How to detect hover status of group of Expandable Table Row in React Material UI

Viewed 356

I want to show/hide an Edit button based on hover status of table row. So I use useHover hooks from this website which the implementation is like this :

function useHover() {
  const [value, setValue] = useState(false);
  const ref = useRef(null);
  const handleMouseOver = () => setValue(true);
  const handleMouseOut = () => setValue(false);
  useEffect(
    () => {
      const node = ref.current;
      if (node) {
        node.addEventListener("mouseover", handleMouseOver);
        node.addEventListener("mouseout", handleMouseOut);
        return () => {
          node.removeEventListener("mouseover", handleMouseOver);
          node.removeEventListener("mouseout", handleMouseOut);
        };
      }
    },
    [ref.current] // Recall only if ref changes
  );
  return [ref, value];
}

So currently my component structure roughly like this (just following the example from their docs):

function Row(props) {
  const [hoverRef, isHovered] = useHover();
  return (
    <>
      {/* attach the hoverRef in this TableRow */}
      <TableRow ref={hoverRef}>
        <TableCell>Simplified Info here</TableCell>

        {/* I want this Edit button only displayed when someone hover the row and its 'child' group*/}
        <div className={clsx(!isHovered && { visibility: "hidden" })}>
          <IconButton>
            <EditIcon />
          </IconButton>
        </div>
      </TableRow>

      {props.details.map((detail) => (
        <TableRow>
          <TableCell>
            <Collapse>
              <Text>Detailed information Here</Text>
              
              {/* I also want to display this if the parent is hovered or this cell is hovered */}
              <div className={clsx(!isHovered && { visibility: "hidden" })}>
                <IconButton>
                  <EditIcon />
                </IconButton>
              </div>
            </Collapse>
          </TableCell>
        </TableRow>
      ))}
    </>
  );
}


It is not fully work as I expect (Show Edit button on hover) since it only display edit button if only if the user hover their mouse over the <TableRow>Simplified Info</TableRow> Row. Want I want to do is to make both edit buttons also displayed for 2 cases:

  1. the user mouse is hovering the 'parent' row
  2. the user mouse is hovering one of its 'child' row

So I change the structure by wrapping the <TableRow> inside a div and attach the hover ref into that div like this:

function Row() {
  const [hoverRef, isHovered] = useHover();
  return (
    <>
      {/* Now adding div and attach the hoverRef into this div */}
      <div ref={hoverRef}>
        <TableRow ref={hoverRef}>
          <TableCell>Simplified Info here</TableCell>

          {/* Now this edit button also displayed even if the mouse is on top of its children*/}
          <div className={clsx(!isHovered && { visibility: "hidden" })}>
            <IconButton>
              <EditIcon />
            </IconButton>
          </div>
        </TableRow>

        {props.details.map((detail) => (
          <TableRow>
            <TableCell>
              <Collapse>
                <Text>Detailed information Here</Text>

                {/* This button also displayed even if the mouse is hovering its parent */}
                <div className={clsx(!isHovered && { visibility: "hidden" })}>
                  <IconButton>
                    <EditIcon />
                  </IconButton>
                </div>
              </Collapse>
            </TableCell>
          </TableRow>
        ))}
      </div>
    </>
  );
}

Yes it works but it breaks the style.

Here is some illustration that might help you to understand what I want to achieve (the group of row should be highlighted & edit buttons should be displayed) : Group of row

So The question is: Is there any advice on how to 'listen' to hover state of the parent and its children / siblings without breaking the style ?

1 Answers

With this informations I can't reproduce exactly to check the styles, but to answer your question, you can try use onMouseEnter and onMouseLeave from Table (or others elements as div) to listen when the elements is hovered.

One example to do this:

import { Table, TableCell, TableRow, Typography } from '@mui/material';
import React, { useState } from 'react';

const details: number[] = [1, 2, 3, 4];

export default function TableRowComponent() {
    const [isHovered, setIsHovered] = useState<boolean>(false);

    const openHover = () => setIsHovered(true);
    const closeHover = () => setIsHovered(false);

    return (
        <Table onMouseEnter={openHover} onMouseLeave={closeHover}>
            <TableRow>
                <TableCell>Simplified Info here</TableCell>
                <span className="">{isHovered && <button>{`I'm a button to edit`}</button>}</span>
            </TableRow>

            {details.map((detail) => (
                <TableRow key={detail}>
                    <TableCell>
                        <Typography>Detailed information Here</Typography>
                    </TableCell>
                    <span className="">{isHovered && <button>{`I'm a button to edit`}</button>}</span>
                </TableRow>
            ))}
        </Table>
    );
}

Notes: I'm using @mui v5+, and I'm using Typescript.

In this example the button is showed when the mouse is hovering in any row/element.

Related