如何使用 react-wondow 和 display:sticky 将一列贴在表格的右边?

How to stick a column to the right in a table using react-wondow and display:sticky?

提问人:Vlad Em 提问时间:5/22/2023 更新时间:5/22/2023 访问量:135

问:

我目前正在创建一个包含大量数据的复杂表。 正因为如此,我决定选择虚拟化(react-window)。 一切都会好起来的,但我需要冻结表中的第一行、左列和右列。 我找到了一个很好的例子: https://codesandbox.io/s/strange-feistel-kwsed?file=/index.js 但是表格中只有第一行和左列是固定的。还需要修复右侧的最后一列。 我遇到了这个问题,想请你帮忙。

索引.js

import React from "react";
import ReactDOM from "react-dom";
import { GridWithStickyCells } from "./grid-with-sticky-cells";

import "./styles.css";

// These cell sizes are arbitrary.
// Yours should be based on the content of the cell.
const columnWidths = new Array(1000)
  .fill(true)
  .map(() => 75 + Math.round(Math.random() * 50));
const rowHeights = new Array(1000)
  .fill(true)
  .map(() => 25 + Math.round(Math.random() * 50));

const Cell = ({ columnIndex, rowIndex, style }) => (
  <div
    className={
      columnIndex % 2
        ? rowIndex % 2 === 0
          ? "GridItemOdd"
          : "GridItemEven"
        : rowIndex % 2
        ? "GridItemOdd"
        : "GridItemEven"
    }
    style={style}
  >
    r{rowIndex}, c{columnIndex}
  </div>
);

const Example = () => (
  <GridWithStickyCells
    className="Grid"
    columnCount={1000}
    columnWidth={(index) => columnWidths[index]}
    height={600}
    rowCount={1000}
    rowHeight={(index) => rowHeights[index]}
    width={600}
  >
    {Cell}
  </GridWithStickyCells>
);

ReactDOM.render(<Example />, document.getElementById("root"));

网格与粘性单元格.jsx

import React from "react";
import { VariableSizeGrid as Grid } from "react-window";

function getCellIndicies(child) {
  return { row: child.props.rowIndex, column: child.props.columnIndex };
}

function getShownIndicies(children) {
  let minRow = Infinity;
  let maxRow = -Infinity;
  let minColumn = Infinity;
  let maxColumn = -Infinity;

  React.Children.forEach(children, (child) => {
    const { row, column } = getCellIndicies(child);
    minRow = Math.min(minRow, row);
    maxRow = Math.max(maxRow, row);
    minColumn = Math.min(minColumn, column);
    maxColumn = Math.max(maxColumn, column);
  });

  return {
    from: {
      row: minRow,
      column: minColumn
    },
    to: {
      row: maxRow,
      column: maxColumn
    }
  };
}

function useInnerElementType(Cell, columnWidth, rowHeight) {
  return React.useMemo(
    () =>
      React.forwardRef((props, ref) => {
        function sumRowsHeights(index) {
          let sum = 0;

          while (index > 1) {
            sum += rowHeight(index - 1);
            index -= 1;
          }

          return sum;
        }

        function sumColumnWidths(index) {
          let sum = 0;

          while (index > 1) {
            sum += columnWidth(index - 1);
            index -= 1;
          }

          return sum;
        }

        const shownIndecies = getShownIndicies(props.children);

        const children = React.Children.map(props.children, (child) => {
          const { column, row } = getCellIndicies(child);

          // do not show non-sticky cell
          if (column === 0 || row === 0) {
            return null;
          }

          return child;
        });

        children.push(
          React.createElement(Cell, {
            key: "0:0",
            rowIndex: 0,
            columnIndex: 0,
            style: {
              display: "inline-flex",
              width: columnWidth(0),
              height: rowHeight(0),
              position: "sticky",
              top: 0,
              left: 0,
              zIndex: 4
            }
          })
        );

        const shownColumnsCount =
          shownIndecies.to.column - shownIndecies.from.column;

        for (let i = 1; i <= shownColumnsCount; i += 1) {
          const columnIndex = i + shownIndecies.from.column;
          const rowIndex = 0;
          const width = columnWidth(columnIndex);
          const height = rowHeight(rowIndex);

          const marginLeft = i === 1 ? sumColumnWidths(columnIndex) : undefined;

          children.push(
            React.createElement(Cell, {
              key: `${rowIndex}:${columnIndex}`,
              rowIndex,
              columnIndex,
              style: {
                marginLeft,
                display: "inline-flex",
                width,
                height,
                position: "sticky",
                top: 0,
                zIndex: 3
              }
            })
          );
        }

        const shownRowsCount = shownIndecies.to.row - shownIndecies.from.row;

        for (let i = 1; i <= shownRowsCount; i += 1) {
          const columnIndex = 0;
          const rowIndex = i + shownIndecies.from.row;
          const width = columnWidth(columnIndex);
          const height = rowHeight(rowIndex);

          const marginTop = i === 1 ? sumRowsHeights(rowIndex) : undefined;

          children.push(
            React.createElement(Cell, {
              key: `${rowIndex}:${columnIndex}`,
              rowIndex,
              columnIndex,
              style: {
                marginTop,
                width,
                height,
                position: "sticky",
                left: 0,
                zIndex: 2
              }
            })
          );
        }

        return (
          <div ref={ref} {...props}>
            {children}
          </div>
        );
      }),
    [Cell, columnWidth, rowHeight]
  );
}

export function GridWithStickyCells(props) {
  return (
    <Grid
      {...props}
      innerElementType={useInnerElementType(
        props.children,
        props.columnWidth,
        props.rowHeight
      )}
    />
  );
}

我试图改变代码的样子,但它没有使最后一列粘在右边缘。我花了一天半的时间,但无法解决问题。 我很乐意提供任何帮助。先谢谢你!

html 反应js html表 反应窗口

评论

0赞 tgf 5/24/2023
这写得很好。通过将代码放入 codesandbox 或类似站点,您可能会获得更多答案。

答: 暂无答案