内部表中的最后一行不会在“固定数据表”中展开

时间:2018-06-04 14:30:39

标签: javascript reactjs fixed-data-table

我在fixed-data-table-2组件中使用expanded rows。我在内表上有3个级别:

enter image description here

如果单击内部表(第二个嵌套级别)中的折叠单元格,则行不会展开,并且不会渲染最后一个嵌套表格。它在首次单击父行后发生,但在第二次单击后,表格将呈现。

更奇怪的是,如果这种情况不会发生 a)我点击第二行的前三行或 b)如果我扩展主(第一)表中的第一行

如果扩展了主表的第一行以外的第二行,则会发生这种情况。

您可以在thisthis录制中看到此行为。

codesandbox

CollapseCell.jsx

import React from 'react';
import { Cell } from 'fixed-data-table-2';

const { StyleSheet, css } = require('aphrodite');

export default class CollapseCell extends React.PureComponent {
  render() {
    const {
      data, rowIndex, columnKey, collapsedRows, callback, ...props
    } = this.props;
    return (
      <Cell {...props} className={css(styles.collapseCell)}>
        <a onClick={evt => callback(rowIndex)}>{collapsedRows.has(rowIndex) ? '\u25BC' : '\u25BA'}</a>
      </Cell>
    );
  }
}

const styles = StyleSheet.create({
  collapseCell: {
    cursor: 'pointer',
  },
});

TestMeet.jsx

import React, { Component } from 'react';
import debounce from 'lodash/debounce';
import { Table, Column, Cell } from 'fixed-data-table-2';
import isEmpty from 'lodash/isEmpty';
import 'fixed-data-table-2/dist/fixed-data-table.min.css';
import CollapseCell from './CollapseCell.jsx';
import SecondInnerTable from './SecondInnerTable';

const { StyleSheet, css } = require('aphrodite');

export default class TestMeetView extends Component {
  static propTypes = {};

  state = {
    tableData: [
      {
        start: '5/19',
        end: '5/20',
        host: 'DACA',
      },
      {
        start: '6/15',
        end: '6/15',
        host: 'DACA',
      },
      {
        start: '6/16',
        end: '6/17',
        host: 'DACA',
      },
      {
        start: '7/15',
        end: '7/16',
        host: 'DACA',
      },
      {
        start: '7/30',
        end: '7/31',
        host: 'DACA',
      },
    ],
    columnWidths: {
      start: 200,
      end: 200,
      host: 200,
      action: 100,
    },
    tableContainerWidth: 0,
    numOfExpRows: 0,
    expChildRows: {},
    collapsedRows: new Set(),
    scrollToRow: null,
  };

  componentDidMount() {
    this.updateWidth();
    this.updateWidth = debounce(this.updateWidth, 200);
    window.addEventListener('resize', this.updateWidth);
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.updateWidth);
  }

  onTableColumnResizeEndCallback = (newColumnWidth, columnKey) => {
    this.setState(({ columnWidths }) => ({
      columnWidths: {
        ...columnWidths,
        [columnKey]: newColumnWidth,
      },
    }));
  };

  updateWidth = () => {
    if (this.tableContainer.offsetWidth === this.state.tableContainerWidth) {
      return;
    }

    if (
      this.tableContainer &&
      this.tableContainer.offsetWidth !== this.state.tableContainerWidth
    ) {
      const newTableContainerWidth = this.tableContainer.offsetWidth;
      this.setState({
        tableContainerWidth: newTableContainerWidth,
        columnWidths: {
          start: newTableContainerWidth / 3,
          end: newTableContainerWidth / 3,
          host: newTableContainerWidth / 3,
        },
      });
    }
  };

  handleCollapseClick = (rowIndex) => {
    const { collapsedRows } = this.state;
    const shallowCopyOfCollapsedRows = new Set([...collapsedRows]);
    let scrollToRow = rowIndex;
    if (shallowCopyOfCollapsedRows.has(rowIndex)) {
      shallowCopyOfCollapsedRows.delete(rowIndex);
      scrollToRow = null;
    } else {
      shallowCopyOfCollapsedRows.add(rowIndex);
    }

    let numOfExpRows = 0;
    if (collapsedRows.size > 0) {
      numOfExpRows = collapsedRows.size;
    }

    let resetExpChildRow = -1;
    if (collapsedRows.has(rowIndex)) {
      numOfExpRows -= 1;
      resetExpChildRow = rowIndex;
    } else {
      numOfExpRows += 1;
    }

    if (resetExpChildRow === -1) {
      this.setState({
        numOfExpRows,
        scrollToRow,
        collapsedRows: shallowCopyOfCollapsedRows,
      });
    } else {
      this.setState({
        numOfExpRows,
        scrollToRow,
        collapsedRows: shallowCopyOfCollapsedRows,
        expChildRows: {
          ...this.state.expChildRows,
          [rowIndex]: 0,
        },
      });
    }
  };

  subRowHeightGetter = (index) => {
    const numExpChildRows = this.state.expChildRows[index] ? this.state.expChildRows[index] : 0;
    return this.state.collapsedRows.has(index) ? 242 * (numExpChildRows + 1) + 50 : 0;
  };

  rowExpandedGetter = ({ rowIndex, width, height }) => {
    if (!this.state.collapsedRows.has(rowIndex)) {
      return null;
    }

    const style = {
      height,
      width: width - 10,
    };

    return (
      <div style={style}>
        <div className={css(styles.expandStyles)}>
          <SecondInnerTable
            changeNumOfExpandedRows={this.setNumOfInnerExpandedRows}
            parentRowIndex={rowIndex}
          />
        </div>
      </div>
    );
  };

  setNumOfInnerExpandedRows = (numOfExpandedRows, rowIndex) => {
    this.setState({
      expChildRows: {
        ...this.state.expChildRows,
        [rowIndex]: numOfExpandedRows,
      },
    });
  };

  render() {
    let sumOfExpChildRows = 0;
    if (!isEmpty(this.state.expChildRows)) {
      sumOfExpChildRows = Object.values(this.state.expChildRows).reduce((a, b) => a + b);
    }
    return (
      <div className="test-view">
        <div className="container-fluid">
          <div className="mb-5" ref={el => (this.tableContainer = el)}>
            <Table
              scrollToRow={this.state.scrollToRow}
              rowsCount={this.state.tableData.length}
              rowHeight={40}
              headerHeight={40}
              width={this.state.tableContainerWidth}
              height={(this.state.numOfExpRows + sumOfExpChildRows + 1) * 242}
              subRowHeightGetter={this.subRowHeightGetter}
              rowExpanded={this.rowExpandedGetter}
              touchScrollEnabled
              onColumnResizeEndCallback={this.onTableColumnResizeEndCallback}
              isColumnResizing={false}
            >
              <Column
                cell={<CollapseCell callback={this.handleCollapseClick} collapsedRows={this.state.collapsedRows} />}
                fixed
                width={30}
              />
              <Column
                columnKey="start"
                header={<Cell>Start</Cell>}
                cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex].start}</Cell>}
                width={this.state.columnWidths.start}
                isResizable
              />
              <Column
                columnKey="end"
                header={<Cell>End</Cell>}
                cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex].end}</Cell>}
                width={this.state.columnWidths.end}
                isResizable
              />
              <Column
                columnKey="host"
                header={<Cell>Host</Cell>}
                cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex].host}</Cell>}
                width={this.state.columnWidths.host}
                isResizable
              />
            </Table>
          </div>
        </div>
      </div>
    );
  }
}

const styles = StyleSheet.create({
  expandStyles: {
    height: '242px',
    margin: '10px',
  },
});

SecondInnerTable.jsx

import React, { Component } from 'react';
import { Table, Column, Cell } from 'fixed-data-table-2';
import debounce from 'lodash/debounce';
import 'fixed-data-table-2/dist/fixed-data-table.min.css';
import CollapseCell from './CollapseCell';
import ThirdInnerTable from './ThirdInnerTable';

const { StyleSheet, css } = require('aphrodite');

export default class SecondInnerTable extends Component {
  state = {
    tableData: [
      {
        dateOfSession: '5/19/18',
        timeline: '4h00m/4h30m',
        entries: '400/900',
      },
      {
        dateOfSession: '5/19/18',
        timeline: '4h00m/4h30m',
        entries: '400/900',
      },
      {
        dateOfSession: '5/19/18',
        timeline: '4h00m/4h30m',
        entries: '400/900',
      },
      {
        dateOfSession: '5/19/18',
        timeline: '4h00m/4h30m',
        entries: '400/900',
      },
      {
        dateOfSession: '5/19/18',
        timeline: '4h00m/4h30m',
        entries: '400/900',
      },
    ],
    tableColumns: {
      dateOfSession: { label: 'Date of Session', width: 150 },
      timeline: { label: 'Timeline', width: 150 },
      entries: { label: 'Entries', width: 150 },
    },
    tableContainerWidth: 0,
    tableContainerHeight: 252,
    collapsedRows: new Set(),
    scrollToRow: null,
  };

  componentDidMount() {
    this.updateWidth();
    this.updateWidth = debounce(this.updateWidth, 200);
    window.addEventListener('resize', this.updateWidth);
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.updateWidth);
  }

  onSessionsTableColumnResizeEndCallback = (newColumnWidth, columnKey) => {
    this.setState(({ tableColumns }) => ({
      tableColumns: {
        ...tableColumns,
        [columnKey]: { label: tableColumns[columnKey].label, width: newColumnWidth },
      },
    }));
  };

  updateWidth = () => {
    if (this.tableContainer.offsetWidth === this.state.tableContainerWidth) {
      return;
    }
    if (
      this.tableContainer &&
      this.tableContainer.offsetWidth !== this.state.tableContainerWidth
    ) {
      const newTableContainerWidth = this.tableContainer.offsetWidth - 20;
      const newColumnsWidth = newTableContainerWidth / 3;
      this.setState(({ tableColumns }) => ({
        tableContainerWidth: newTableContainerWidth,
        tableColumns: {
          dateOfSession: { label: tableColumns.dateOfSession.label, width: newColumnsWidth },
          timeline: { label: tableColumns.timeline.label, width: newColumnsWidth },
          entries: { label: tableColumns.entries.label, width: newColumnsWidth },
        },
      }));
    }
  };

  handleCollapseClick = (rowIndex) => {
    const { collapsedRows } = this.state;
    const shallowCopyOfCollapsedRows = new Set([...collapsedRows]);
    let scrollToRow = rowIndex;
    if (shallowCopyOfCollapsedRows.has(rowIndex)) {
      shallowCopyOfCollapsedRows.delete(rowIndex);
      scrollToRow = null;
    } else {
      shallowCopyOfCollapsedRows.add(rowIndex);
    }

    let numOfExpRows = 0;
    if (collapsedRows.size > 0) {
      numOfExpRows = collapsedRows.size;
    }

    if (collapsedRows.has(rowIndex)) {
      numOfExpRows -= 1;
    } else {
      numOfExpRows += 1;
    }

    this.setState(
      {
        tableContainerHeight: 252 * (numOfExpRows + 1),
        scrollToRow,
        collapsedRows: shallowCopyOfCollapsedRows,
      },
      () => {
        this.props.changeNumOfExpandedRows(numOfExpRows, this.props.parentRowIndex);
      },
    );
  };

  subRowHeightGetter = index => (this.state.collapsedRows.has(index) ? 272 : 0);

  rowExpandedGetter = ({ rowIndex, width, height }) => {
    if (!this.state.collapsedRows.has(rowIndex)) {
      return null;
    }

    const style = {
      height,
      width: width - 10,
    };
    return (
      <div style={style}>
        <div className={css(styles.expandStyles)}>
          <ThirdInnerTable parentRowIndex={rowIndex} />
        </div>
      </div>
    );
  };

  render() {
    return (
      <div className="mb-2" ref={el => (this.tableContainer = el)}>
        <Table
          scrollToRow={this.state.scrollToRow}
          rowsCount={this.state.tableData.length}
          rowHeight={40}
          headerHeight={50}
          width={this.state.tableContainerWidth}
          height={this.state.tableContainerHeight}
          subRowHeightGetter={this.subRowHeightGetter}
          rowExpanded={this.rowExpandedGetter}
          touchScrollEnabled
          onColumnResizeEndCallback={this.onSessionsTableColumnResizeEndCallback}
          isColumnResizing={false}
        >
          <Column
            cell={<CollapseCell callback={this.handleCollapseClick} collapsedRows={this.state.collapsedRows} />}
            fixed
            width={30}
          />
          {Object.keys(this.state.tableColumns).map(key => (
            <Column
              key={key}
              columnKey={key}
              header={<Cell>{this.state.tableColumns[key].label}</Cell>}
              cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex][key]}</Cell>}
              width={this.state.tableColumns[key].width}
              isResizable
            />
          ))}
        </Table>
      </div>
    );
  }
}

const styles = StyleSheet.create({
  expandStyles: {
    height: '252px',
    margin: '10px',
  },
});

ThirdInnerTable.jsx

import React, { Component } from 'react';
import debounce from 'lodash/debounce';
import { Table, Column, Cell } from 'fixed-data-table-2';
import 'fixed-data-table-2/dist/fixed-data-table.min.css';

export default class ThirdInnerTable extends Component {
  state = {
    tableData: [
      {
        eventNumber: '1',
        qualifyingTime: 'N/A',
        selected: 'N/A',
      },
      {
        eventNumber: '1',
        qualifyingTime: 'N/A',
        selected: 'N/A',
      },
      {
        eventNumber: '1',
        qualifyingTime: 'N/A',
        selected: 'N/A',
      },
      {
        eventNumber: '1',
        qualifyingTime: 'N/A',
        selected: 'N/A',
      },
      {
        eventNumber: '1',
        qualifyingTime: 'N/A',
        selected: 'N/A',
      },
    ],
    tableColumns: {
      eventNumber: { label: 'Event number', width: 150 },
      qualifyingTime: { label: 'Qualifying time', width: 150 },
      selected: { label: 'Selected?', width: 150 },
    },
    tableContainerWidth: 0,
    numOfColumns: 3,
  };

  componentDidMount() {
    this.updateWidth();
    this.updateWidth = debounce(this.updateWidth, 200);
    window.addEventListener('resize', this.updateWidth);
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.updateWidth);
  }

  onEventsTableColumnResizeEndCallback = (newColumnWidth, columnKey) => {
    this.setState(({ tableColumns }) => ({
      tableColumns: {
        ...tableColumns,
        [columnKey]: { label: tableColumns[columnKey].label, width: newColumnWidth },
      },
    }));
  };

  updateWidth = () => {
    if (this.tableContainer.offsetWidth === this.state.tableContainerWidth) {
      return;
    }
    if (
      this.tableContainer &&
      this.tableContainer.offsetWidth !== this.state.tableContainerWidth
    ) {
      const newTableContainerWidth = this.tableContainer.offsetWidth;
      const columnWidth = newTableContainerWidth / 3;

      this.setState(({ tableColumns }) => ({
        tableContainerWidth: newTableContainerWidth,
        tableColumns: {
          eventNumber: { label: tableColumns.eventNumber.label, width: columnWidth },
          qualifyingTime: { label: tableColumns.qualifyingTime.label, width: columnWidth },
          selected: { label: tableColumns.selected.label, width: columnWidth },
        },
      }));
    }
  };

  render() {
    return (
      <div className="mb-5" ref={el => (this.tableContainer = el)}>
        <Table
          rowsCount={this.state.tableData.length}
          rowHeight={40}
          headerHeight={50}
          width={this.state.tableContainerWidth}
          height={252}
          touchScrollEnabled
          onColumnResizeEndCallback={this.onEventsTableColumnResizeEndCallback}
          isColumnResizing={false}
        >
          {Object.keys(this.state.tableColumns).slice(0, this.state.numOfColumns).map(key => (
            <Column
              key={key}
              columnKey={key}
              header={<Cell>{this.state.tableColumns[key].label}</Cell>}
              cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex][key]}</Cell>}
              width={this.state.tableColumns[key].width}
              isResizable
            />
            ))}
        </Table>
      </div>
    );
  }
}

2 个答案:

答案 0 :(得分:2)

我已更新Sandbox代码,请查看以下链接,我认为它对您有用

Code Sandbox

或者

    handleCollapseClick = rowIndex => {
    const { collapsedRows } = this.state;

    const shallowCopyOfCollapsedRows = new Set([...collapsedRows]);
    let scrollToRow = rowIndex;
    if (shallowCopyOfCollapsedRows.has(rowIndex)) {
      shallowCopyOfCollapsedRows.delete(rowIndex);
      scrollToRow = null;
    } else {
      shallowCopyOfCollapsedRows.add(rowIndex);
    }

    let numOfExpRows = 0;
    if (collapsedRows.size > 0) {
      numOfExpRows = collapsedRows.size;
    }

    let resetExpChildRow = -1;
    if (collapsedRows.has(rowIndex)) {
      numOfExpRows -= 1;
      resetExpChildRow = rowIndex;
    } else {
      numOfExpRows += 1;
    }

    if (resetExpChildRow === -1) {
      this.setState({
        tableContainerHeight: 250 * numOfExpRows,
        scrollToRow,
        collapsedRows: shallowCopyOfCollapsedRows
      });
    } else {
      this.setState({
        numOfExpRows,
        scrollToRow,
        collapsedRows: shallowCopyOfCollapsedRows,
        expChildRows: {
          ...this.state.expChildRows,
          [rowIndex]: 0
        }
      });
    }
  };

答案 1 :(得分:0)

我应该评论,但我没有要求的号码。对此的观点。你修复了这个问题吗?因为我刚刚打开你附加的链接并且工作正常

<强>更新 你的代码运行正常。您的方法的问题是您的点击事件是在箭头上触发而不是在整个div(或单元格)上触发。因此,您需要处于点状态,只需单击箭头即可展开行。如果您不想要这种行为,我建议您将click事件放在div(或单元格)

这是一个工作视频,证明它工作正常: https://www.useloom.com/share/2c725f3fede942b09c661a765c72634e

我已从您的视频中截取屏幕截图,其中显示因为您没有点击箭头,这就是您的行未展开的原因