From f7d40a91f7b524d9936a02e483c181cb3ea67a22 Mon Sep 17 00:00:00 2001 From: Dave Falke Date: Mon, 9 Dec 2024 15:18:51 -0500 Subject: [PATCH] Simplify table markup and remove unnecessary events and layout updates --- .../Mesa/Components/AnchoredTooltip.jsx | 3 - .../src/components/Mesa/Ui/DataTable.jsx | 131 +++++------------- .../src/components/Mesa/Ui/ExpansionCell.tsx | 20 +-- .../src/components/Mesa/Ui/HeadingCell.jsx | 3 - .../src/components/Mesa/style/Cells.scss | 3 + .../components/Mesa/style/Ui/DataTable.scss | 90 ++++++------ .../components/Mesa/style/Ui/HeadingCell.scss | 1 - 7 files changed, 96 insertions(+), 155 deletions(-) diff --git a/packages/libs/coreui/src/components/Mesa/Components/AnchoredTooltip.jsx b/packages/libs/coreui/src/components/Mesa/Components/AnchoredTooltip.jsx index 6110521eb3..91d58d1103 100644 --- a/packages/libs/coreui/src/components/Mesa/Components/AnchoredTooltip.jsx +++ b/packages/libs/coreui/src/components/Mesa/Components/AnchoredTooltip.jsx @@ -3,7 +3,6 @@ import { debounce } from 'lodash'; import MesaTooltip from './MesaTooltip'; import Events from '../Utils/Events'; -import { MESA_SCROLL_EVENT, MESA_REFLOW_EVENT } from '../Ui/MesaContants'; class AnchoredTooltip extends React.Component { constructor(props) { @@ -19,8 +18,6 @@ class AnchoredTooltip extends React.Component { this.listeners = { scroll: Events.add('scroll', this.updatePosition), resize: Events.add('resize', this.updatePosition), - MesaScroll: Events.add(MESA_SCROLL_EVENT, this.updatePosition), - MesaReflow: Events.add(MESA_REFLOW_EVENT, this.updatePosition), }; } diff --git a/packages/libs/coreui/src/components/Mesa/Ui/DataTable.jsx b/packages/libs/coreui/src/components/Mesa/Ui/DataTable.jsx index 799682ad59..919c0e2378 100644 --- a/packages/libs/coreui/src/components/Mesa/Ui/DataTable.jsx +++ b/packages/libs/coreui/src/components/Mesa/Ui/DataTable.jsx @@ -7,22 +7,19 @@ import { defaultMemoize } from 'reselect'; import HeadingRow from './HeadingRow'; import DataRowList from './DataRowList'; import { makeClassifier, combineWidths } from '../Utils/Utils'; -import { MESA_SCROLL_EVENT, MESA_REFLOW_EVENT } from './MesaContants'; const dataTableClass = makeClassifier('DataTable'); class DataTable extends React.Component { constructor(props) { super(props); - this.widthCache = {}; - this.state = { dynamicWidths: null, tableWrapperWidth: null }; + this.widthCache = []; this.renderStickyTable = this.renderStickyTable.bind(this); this.componentDidMount = this.componentDidMount.bind(this); - this.getInnerCellWidth = this.getInnerCellWidth.bind(this); this.hasSelectionColumn = this.hasSelectionColumn.bind(this); + this.hasExpansionColumn = this.hasExpansionColumn.bind(this); this.shouldUseStickyHeader = this.shouldUseStickyHeader.bind(this); this.makeFirstNColumnsSticky = this.makeFirstNColumnsSticky.bind(this); - this.handleTableBodyScroll = this.handleTableBodyScroll.bind(this); this.setDynamicWidths = this.setDynamicWidths.bind(this); this.resizeId = -1; this.mainRef = null; @@ -40,7 +37,7 @@ class DataTable extends React.Component { } makeFirstNColumnsSticky(columns, n) { - const { dynamicWidths } = this.state; + const dynamicWidths = this.widthCache; if (n <= columns.length) { const stickyColumns = columns.slice(0, n).map((column, index) => { @@ -95,14 +92,16 @@ class DataTable extends React.Component { } attachLoadEventHandlers() { - if (this.bodyNode == null) return; - this.bodyNode.querySelectorAll('img, iframe, object').forEach((node) => { - if (node.complete) return; - node.addEventListener('load', (event) => { - const el = event.target.offsetParent || event.target; - if (el.scrollWidth > el.clientWidth) this.setDynamicWidths(); + if (this.contentTable == null) return; + this.contentTable + .querySelectorAll('img, iframe, object') + .forEach((node) => { + if (node.complete) return; + node.addEventListener('load', (event) => { + const el = event.target.offsetParent || event.target; + if (el.scrollWidth > el.clientWidth) this.setDynamicWidths(); + }); }); - }); } attachResizeHandler() { @@ -123,11 +122,9 @@ class DataTable extends React.Component { if (this.props.rows.length === 0 || this.props.filteredRows.length === 0) return; - this.setState({ dynamicWidths: null, tableWrapperWidth: null }, () => { - this.widthCache = {}; - const { columns } = this.props; - const hasSelectionColumn = this.hasSelectionColumn(); - const { contentTable, getInnerCellWidth } = this; + this.setState({ dynamicWidths: null }, () => { + this.widthCache = []; + const { contentTable } = this; if (!contentTable) return; const contentCells = Array.from( contentTable.querySelectorAll('tbody > tr:first-child > td') @@ -137,26 +134,18 @@ class DataTable extends React.Component { return; } - if (hasSelectionColumn) { - contentCells.shift(); - } - - const dynamicWidths = columns.map( - (c, i) => - getInnerCellWidth(contentCells[i], c) - - (hasSelectionColumn && !i ? 1 : 0) + this.widthCache = contentCells.map( + (cell) => cell.getBoundingClientRect().width ); - this.setState({ dynamicWidths }, () => { - window.dispatchEvent(new CustomEvent(MESA_REFLOW_EVENT)); - const tableWrapperWidth = this.bodyNode && this.bodyNode.clientWidth; - this.setState({ tableWrapperWidth }); - }); }); } - getInnerCellWidth(cell, { key }) { - if (key && key in this.widthCache) return this.widthCache[key]; - return (this.widthCache[key] = cell.clientWidth); + hasExpansionColumn() { + const { options, eventHandlers } = this.props; + return ( + typeof options.childRow === 'function' && + typeof eventHandlers.onExpandedRowsChange === 'function' + ); } hasSelectionColumn() { @@ -168,12 +157,6 @@ class DataTable extends React.Component { ); } - handleTableBodyScroll() { - const offset = this.bodyNode.scrollLeft; - this.headerNode.scrollLeft = offset; - window.dispatchEvent(new CustomEvent(MESA_SCROLL_EVENT)); - } - // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= renderStickyTable() { @@ -187,30 +170,15 @@ class DataTable extends React.Component { uiState, headerWrapperStyle, } = this.props; - const { dynamicWidths } = this.state; - const stickyColumns = options.useStickyFirstNColumns + const newColumns = options.useStickyFirstNColumns ? this.makeFirstNColumnsSticky(columns, options.useStickyFirstNColumns) : columns; - const newColumns = - stickyColumns.every(({ width }) => width) || - !dynamicWidths || - dynamicWidths.length == 0 - ? stickyColumns - : makeColumnsWithDynamicWidths({ - columns: stickyColumns, - dynamicWidths, - }); const wrapperStyle = { maxHeight: options ? options.tableBodyMaxHeight : null, - minWidth: dynamicWidths - ? combineWidths(columns.map(({ width }) => width)) - : null, }; - const headerWrapperStyleMerged = { - display: dynamicWidths == null ? 'none' : 'block', - ...headerWrapperStyle, + const tableStyle = { + tableLayout: 'auto', }; - const tableLayout = { tableLayout: dynamicWidths ? 'fixed' : 'auto' }; const tableProps = { options, rows, @@ -223,36 +191,21 @@ class DataTable extends React.Component { return (
(this.mainRef = node)} className="MesaComponent">
-
(this.headerNode = node)} - className={dataTableClass('Header')} - > - - -
-
-
(this.bodyNode = node)} - className={dataTableClass('Body')} - onScroll={this.handleTableBodyScroll} + (this.contentTable = node)} > -
(this.contentTable = node)} - > - {dynamicWidths == null ? : null} - -
-
+ + + {this.props.options.marginContent && (
{this.props.options.marginContent} @@ -289,12 +242,4 @@ DataTable.propTypes = { eventHandlers: PropTypes.objectOf(PropTypes.func), }; -const makeColumnsWithDynamicWidths = defaultMemoize( - ({ columns, dynamicWidths }) => - columns.map((column, index) => - Object.assign({}, column, { width: dynamicWidths[index] }) - ), - (a, b) => a.columns === b.columns && isEqual(a.dynamicWidths, b.dynamicWidths) -); - export default DataTable; diff --git a/packages/libs/coreui/src/components/Mesa/Ui/ExpansionCell.tsx b/packages/libs/coreui/src/components/Mesa/Ui/ExpansionCell.tsx index f17451497c..b95b90336e 100644 --- a/packages/libs/coreui/src/components/Mesa/Ui/ExpansionCell.tsx +++ b/packages/libs/coreui/src/components/Mesa/Ui/ExpansionCell.tsx @@ -42,21 +42,13 @@ export default function ExpansionCell({ const title = 'Show or hide all row details'; return ( - + {inert ? null : areAllRowsExpanded ? ( - ) : ( - )} @@ -82,13 +74,13 @@ export default function ExpansionCell({ }; return ( - + {inert ? null : isExpanded ? ( - ) : ( - )} diff --git a/packages/libs/coreui/src/components/Mesa/Ui/HeadingCell.jsx b/packages/libs/coreui/src/components/Mesa/Ui/HeadingCell.jsx index 4a8c46b822..0bbc62c6c4 100644 --- a/packages/libs/coreui/src/components/Mesa/Ui/HeadingCell.jsx +++ b/packages/libs/coreui/src/components/Mesa/Ui/HeadingCell.jsx @@ -6,7 +6,6 @@ import Icon from '../Components/Icon'; import HelpTrigger from '../Components/HelpTrigger'; import { makeClassifier } from '../Utils/Utils'; import Events, { EventsFactory } from '../Utils/Events'; -import { MESA_SCROLL_EVENT, MESA_REFLOW_EVENT } from './MesaContants'; const headingCellClass = makeClassifier('HeadingCell'); @@ -42,8 +41,6 @@ class HeadingCell extends React.PureComponent { this.listeners = { scroll: Events.add('scroll', this.updateOffset), resize: Events.add('resize', this.updateOffset), - MesaScroll: Events.add(MESA_SCROLL_EVENT, this.updateOffset), - MesaReflow: Events.add(MESA_REFLOW_EVENT, this.updateOffset), }; } diff --git a/packages/libs/coreui/src/components/Mesa/style/Cells.scss b/packages/libs/coreui/src/components/Mesa/style/Cells.scss index dcafc81e55..9de4fb18d6 100644 --- a/packages/libs/coreui/src/components/Mesa/style/Cells.scss +++ b/packages/libs/coreui/src/components/Mesa/style/Cells.scss @@ -1,6 +1,9 @@ $StickyColumnBorderColor: rgb(204, 204, 204); .MesaComponent { + tr { + white-space: break-spaces; + } th { background-color: #e2e2e2; transition: background 0.25s; diff --git a/packages/libs/coreui/src/components/Mesa/style/Ui/DataTable.scss b/packages/libs/coreui/src/components/Mesa/style/Ui/DataTable.scss index bb4d37c72d..3cead3cd12 100644 --- a/packages/libs/coreui/src/components/Mesa/style/Ui/DataTable.scss +++ b/packages/libs/coreui/src/components/Mesa/style/Ui/DataTable.scss @@ -1,63 +1,71 @@ .MesaComponent { .DataTable { font-size: $fontSize; - width: 100%; + display: inline-block; margin-bottom: 1.5em; - display: grid; - grid-template-columns: auto 1fr; - grid-template-areas: - '. header' - 'margin body'; - z-index: 0; + width: 100%; + + &--HasMargin { + display: grid; + grid-template-columns: auto 1fr; + grid-template-areas: 'margin table'; + + .DataTable-Margin { + grid-area: margin; + } + align-items: end; + + table { + grid-area: table; + } + } &--Sticky { + z-index: 0; overflow: auto; - .DataTable-Header { + position: relative; + max-height: 70vh; + padding-bottom: 1.5em; /* prevent macos scrollbar from occluding last row */ + thead { position: sticky; top: 0; - z-index: 1; + z-index: 2; + overflow: visible; } } - .DataTable-Header { - grid-area: header; - } - - .DataTable-Body { - grid-area: body; - } - - .DataTable-Margin { - grid-area: margin; - } - - table { + > table { width: 100%; border-collapse: separate; - } - .DataTable-Sticky { - display: block; - overflow: auto; - position: relative; - z-index: 0; - max-height: 70vh; + td, + tr { + .ChildRowToggle { + width: 4em; + vertical-align: middle; + text-align: center; + padding: 0; - .DataTable-Header, - .DataTable-Body { - display: block; - } + > button { + width: 100%; + padding: 0; + border: none; + line-height: 2; + background: transparent !important; + + > svg { + fill: #777; + } - .DataTable-Body { - position: relative; - padding-bottom: 1.5em; /* prevent macos scrollbar from occluding last row */ + &:hover > svg { + fill: black; + } + } + } } - .DataTable-Header { - overflow: visible; - top: 0; - position: sticky; - z-index: 1; + tr._childIsExpanded > .ChildRowToggle > button > svg { + fill: black; } } } diff --git a/packages/libs/coreui/src/components/Mesa/style/Ui/HeadingCell.scss b/packages/libs/coreui/src/components/Mesa/style/Ui/HeadingCell.scss index c28f238064..2ffdcacaa8 100644 --- a/packages/libs/coreui/src/components/Mesa/style/Ui/HeadingCell.scss +++ b/packages/libs/coreui/src/components/Mesa/style/Ui/HeadingCell.scss @@ -10,7 +10,6 @@ z-index: 1; .HeadingCell-Content-Aside { - flex-basis: 0 1 30px; &:last-of-type { text-align: right; }