React Material UI Datagrid rowMouseEnter 不触发

问题描述 投票:0回答:2

当我将鼠标悬停在 Datagrid 中的任何行上时,我希望“分析”按钮从概述的变体变为包含的变体。当鼠标悬停在该行上时,我无法触发任何事件,也找不到有关当鼠标位于该行内时如何更新/重新呈现该行内的单元格的任何信息。

"@mui/x-data-grid": "^5.17.25", "@mui/x-data-grid-generator": "^6.0.0", "@mui/x-data-grid-pro": "^6.0.0",

import React, { useRef, useState, useEffect } from "react";
import { DataGrid, GridRowsProp, GridColDef } from "@mui/x-data-grid";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import { useTheme } from "@mui/system";
import Link from "next/link";
import TextField from "@mui/material/TextField";
import InputAdornment from "@mui/material/InputAdornment";
import SearchIcon from "@mui/icons-material/Search";
import AddIcon from "@mui/icons-material/Add";
import CircularProgress from "@mui/material/CircularProgress";
import { alpha, styled, lighten } from "@mui/material/styles";

export default function PropertiesList({ newProperties }) {
  const theme = useTheme();
  const boxRef = useRef(null);
  const [searchText, setSearchText] = useState("");
  const columns = getColumns(theme);

  function getColumns(theme) {
    // commented because irrelevant
    return [
      {
        field: "id",
        headerName: "Actions",
        width: 150,
        renderCell: (params) => {
          return (
            <Box
              sx={{
                display: "flex",
                justifyContent: "space-between",
                width: "100%"
              }}
            >
              <Link
                href="/properties/[id]"
                as={`/properties/${params.row.original_doc || params.row.id}`}
              >
                <Button
                  size="small"
                  variant="outlined"
                  startIcon={<CalculateIcon />}
                  sx={{
                    backgroundColor:
                      hoveredRowId === params.id
                        ? theme.palette.success.main
                        : ""
                  }}
                >
                  Analyze
                </Button>
              </Link>
            </Box>
          );
        }
      }
    ];
  }

  useEffect(() => {
    if (!boxRef.current) return;
    const screenHeight = window.innerHeight;
    boxRef.current.style.height = `${screenHeight - 120}px`;
  }, []);

  const handleRowOver = (params) => {
    // change the analyze button from "outlined" to "contained" when hovered.
    // The below console.log does not trigger.
    console.log(`Row ${params.id} is being hovered over`);
  };

  return (
    <Box ref={boxRef}>
      {!newProperties && (
        <Box
          sx={{
            height: "calc(100vh - 160px)",
            display: "flex",
            justifyContent: "center",
            alignItems: "center"
          }}
        >
          <CircularProgress size={32} />
        </Box>
      )}
      {newProperties && (
        <>
          <Box
            sx={{
              display: "flex",
              justifyContent: "space-between",
              alignItems: "center",
              background: theme.palette.background.background2,
              marginTop: 3,
              // marginBottom: 1,
              padding: 2,
              border: "1px solid " + theme.palette.contrast.contrast1,
              borderTopLeftRadius: 8,
              borderTopRightRadius: 8
            }}
          >
            <TextField
              label="Search for property"
              placeholder=""
              sx={{ marginTop: 1, marginBottom: 1 }}
              onChange={(event) => setSearchText(event.target.value)}
              InputProps={{
                startAdornment: (
                  <InputAdornment position="start">
                    <SearchIcon />
                  </InputAdornment>
                )
              }}
            />

            <Link href="/properties/add">
              <Button
                size="medium"
                variant="contained"
                sx={{ height: 50 }}
                startIcon={<AddIcon />}
              >
                Add Property
              </Button>
            </Link>
          </Box>

          <DataGrid
            rowMouseEnter={handleRowOver}
            sx={{
              border: "1px solid " + theme.palette.contrast.contrast1,
              height: "calc(100vh - 280px)",
              background: theme.palette.background.background1,
              "& .MuiDataGrid-virtualScroller::-webkit-scrollbar": {
                height: "0.4em",
                width: "0.4em"
              },
              "& .MuiDataGrid-virtualScroller::-webkit-scrollbar-track": {
                background: theme.palette.contrast.contrast1
              },
              "& .MuiDataGrid-virtualScroller::-webkit-scrollbar-thumb": {
                backgroundColor: theme.palette.contrast.contrast2
              },
              "& .MuiDataGrid-virtualScroller::-webkit-scrollbar-thumb:hover": {
                background: theme.palette.contrast.default
              },
              borderTopLeftRadius: 0,
              borderTopRightRadius: 0,
              borderBottomLeftRadius: 8,
              borderBottomRightRadius: 8
            }}
            rows={newProperties.filter(
              (row) =>
                (row.address &&
                  row.address
                    .toLowerCase()
                    .includes(searchText.toLowerCase())) ||
                (row.city &&
                  row.city.toLowerCase().includes(searchText.toLowerCase())) ||
                (row.state &&
                  row.state.toLowerCase().includes(searchText.toLowerCase())) ||
                (row.zip &&
                  row.zip
                    .toString()
                    .toLowerCase()
                    .includes(searchText.toLowerCase()))
            )}
            columns={columns}
            pageSize={13}
            disableColumnFilter
            disableSelectionOnClick
            disableColumnSelector
          />
        </>
      )}
    </Box>
  );
}
reactjs material-ui datagrid
2个回答
1
投票

正如@VonC 在他的回答中提到的,您可以使用 slotProps 将道具传递给行元素,特别是 onMouseEnter 和 onMouseLeave。使用 here 中描述的技术,我能够以相当简洁的方式重现您试图实现的行为。

主要思想是在 onMouseEnter 和 onMouseLeave 中触发一个事件,我们将在自定义按钮组件中订阅该事件。

为了实现不同行之间的事件隔离,我们会在事件名称中包含行id。

没有上下文很难运行你的组件,所以为了演示原理,我自己构建了一个最小的DataGrid。

你可以在这里看到一个活生生的例子:

Edit MUI Datagrid change cell props on row hover

代码:

import React, { FC, useState, useEffect } from "react";
import Button from "@mui/material/Button";
import { DataGrid, GridColDef } from "@mui/x-data-grid";
import CalculateIcon from "@mui/icons-material/Calculate";

const CustomButtonElement: FC<{ rowId: number | string }> = ({ rowId }) => {
  const [rowHovered, setRowHovered] = useState(false);
  useEffect(() => {
    const handleCustomEvent = (e) => setRowHovered(e.detail.hovered);
    document.addEventListener(`row${rowId}HoverChange`, handleCustomEvent);
    // cleanup listener
    return () =>
      document.removeEventListener(`row${rowId}HoverChange`, handleCustomEvent);
  }, [rowId]);

  return (
    <Button variant={rowHovered ? "outlined" : "contained"}>
      <CalculateIcon />
    </Button>
  );
};

export default function DataGridDemo() {
  const rows = [
    { id: 1, lastName: "Snow", firstName: "Jon", age: 35 },
    { id: 2, lastName: "Lannister", firstName: "Cersei", age: 42 },
    { id: 3, lastName: "Lannister", firstName: "Jaime", age: 45 },
    { id: 4, lastName: "Stark", firstName: "Arya", age: 16 },
    { id: 5, lastName: "Targaryen", firstName: "Daenerys", age: null }
  ];

  const columns: GridColDef[] = [
    { field: "id", headerName: "ID", width: 90 },
    {
      field: "",
      headerName: "Action",
      renderCell: (params) => <CustomButtonElement rowId={params.id} />
    },
    { field: "firstName", headerName: "First Name", width: 90 },
    { field: "lastName", headerName: "Last Name", width: 90 }
  ];

  const handleRowHovered = (event: React.MouseEvent<HTMLElement>) => {
    const rowId = event.currentTarget?.dataset?.id;
    document.dispatchEvent(
      new CustomEvent(`row${rowId}HoverChange`, { detail: { hovered: true } })
    );
  };

  const handleRowLeaved = (event: React.MouseEvent<HTMLElement>) => {
    const rowId = event.currentTarget?.dataset?.id;
    document.dispatchEvent(
      new CustomEvent(`row${rowId}HoverChange`, { detail: { hovered: false } })
    );
  };

  return (
    <DataGrid
      rows={rows}
      columns={columns}
      slotProps={{
        row: {
          onMouseEnter: handleRowHovered,
          onMouseLeave: handleRowLeaved
        }
      }}
    />
  );
}

更新

为了解决组件离开视图时状态丢失的问题(由于 unmount ),我添加了一个 useEffect 将在每个按钮上运行 mount 并检查鼠标是否悬停在按钮行元素上。为此,我使用 matchesapiRef 对象通过其上下文对 DataGrid 行元素进行更多本机访问。

事实证明,由于相同的 apiRef 和

useGridApiEventHandler
钩子,您可以以更原生的方式订阅 events(无需创建自定义事件),因此代码更加简洁和富有表现力。

Updated Code(上面的Codesandbox也更新了):

import React, { FC, useState, useEffect } from "react";
import Button from "@mui/material/Button";
import {
  DataGrid,
  GridColDef,
  GridEventListener,
  useGridApiContext,
  useGridApiEventHandler
} from "@mui/x-data-grid";
import CalculateIcon from "@mui/icons-material/Calculate";

const CustomButtonElement: FC<{ rowId: number | string }> = ({ rowId }) => {
  const [rowHovered, setRowHovered] = useState(false);
  const apiRef = useGridApiContext();

  // runs only "onComponentMount"
  useEffect(() => {
    if (apiRef.current.getRowElement(rowId).matches(":hover"))
      setRowHovered(true);
  }, []);

  const handleRowEnter: GridEventListener<"rowMouseEnter"> = ({ id }) =>
    id === rowId && setRowHovered(true);
  const handleRowLeave: GridEventListener<"rowMouseLeave"> = ({ id }) =>
    id === rowId && setRowHovered(false);

  useGridApiEventHandler(apiRef, "rowMouseEnter", handleRowEnter);
  useGridApiEventHandler(apiRef, "rowMouseLeave", handleRowLeave);

  return (
    <Button variant={rowHovered ? "outlined" : "contained"}>
      <CalculateIcon />
    </Button>
  );
};

export default function DataGridDemo() {
  const rows = [
    { id: 1, lastName: "Snow", firstName: "Jon", age: 35 },
    { id: 2, lastName: "Lannister", firstName: "Cersei", age: 42 },
    { id: 3, lastName: "Lannister", firstName: "Jaime", age: 45 },
    { id: 4, lastName: "Stark", firstName: "Arya", age: 16 },
    { id: 5, lastName: "Targaryen", firstName: "Daenerys", age: null }
  ];

  const columns: GridColDef[] = [
    { field: "id", headerName: "ID", width: 90 },
    {
      field: "",
      headerName: "Action",
      renderCell: (params) => <CustomButtonElement rowId={params.id} />
    },
    { field: "firstName", headerName: "First Name", width: 90 },
    { field: "lastName", headerName: "Last Name", width: 90 },
    { field: "age", headerName: "Age", width: 90 }
  ];

  return <DataGrid rows={rows} columns={columns} />;
}

0
投票

MUI DataGrid 不包含任何关于悬停的事件函数。
(你只有像

onCellClick()
onCellEditStart()
onCellKeyDown()
,......这样的功能)

我考虑过像

onMouseEnter
onMouseLeave
这样的事件监听器,但是,正如在这里提到的那样,它会导致如此多的渲染。

也许使用 sx prop 将有助于使用

:hover
:

设计这些块的样式

类似的东西:

function getColumns(theme) {
  return [
    {
      field: "id",
      headerName: "Actions",
      width: 150,
      renderCell: (params) => {
        return (
          <Box
            sx={{
              display: "flex",
              justifyContent: "space-between",
              width: "100%",
              '&:hover button': {
                backgroundColor: theme.palette.success.main,
                border: '1px solid transparent',
                color: theme.palette.common.white,
              },
            }}
          >
            <Link
              href="/properties/[id]"
              as={`/properties/${params.row.original_doc || params.row.id}`}
            >
              <Button
                size="small"
                variant="outlined"
                startIcon={<CalculateIcon />}
                sx={{
                  borderColor: theme.palette.success.main,
                  '&:hover': {
                    backgroundColor: theme.palette.success.main,
                    borderColor: 'transparent',
                    color: theme.palette.common.white,
                  },
                }}
              >
                Analyze
              </Button>
            </Link>
          </Box>
        );
      },
    },
  ];
}

关联

<DataGrid
  onRowMouseEnter={handleRowOver}
  // Other props
/>

(不是

rowMouseEnter

使用与 DataGrid 行中显示的类似的想法

<DataGrid
  // Other props
  slotProps={{
    row: {
      onMouseEnter: (event) => handleRowOver(Number(event.currentTarget.getAttribute('data-id'))),
      onMouseLeave: () => console.log('Mouse left the row'),
    },
  }}
/>

与:

const handleRowOver = (rowId) => {
  console.log(`Row ${rowId} is being hovered over`);
};

当鼠标悬停在DataGrid中的一行上时,会调用

handleRowOver
函数,可以在浏览器控制台看到日志。
当鼠标离开该行时,匿名函数将打印一条消息。 “分析”按钮的悬停效果仍将由
:hover
属性中的
sx
伪类处理。

© www.soinside.com 2019 - 2024. All rights reserved.