UNPKG

@equinor/eds-data-grid-react

Version:

A feature-rich data-grid written in React, implementing the Equinor Design System

1,000 lines (968 loc) 34.4 kB
'use strict'; var reactTable = require('@tanstack/react-table'); var edsCoreReact = require('@equinor/eds-core-react'); var reactVirtual = require('@tanstack/react-virtual'); var react = require('react'); var styled = require('styled-components'); var jsxRuntime = require('react/jsx-runtime'); var edsIcons = require('@equinor/eds-icons'); var edsTokens = require('@equinor/eds-tokens'); var edsUtils = require('@equinor/eds-utils'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var styled__default = /*#__PURE__*/_interopDefault(styled); // eslint-disable-next-line @typescript-eslint/no-explicit-any const EdsDataGridContext = /*#__PURE__*/react.createContext({ enableSorting: false, stickyHeader: false, stickyFooter: false, enableColumnFiltering: false, table: null }); function TableProvider({ children, ...props }) { return /*#__PURE__*/jsxRuntime.jsx(EdsDataGridContext.Provider, { value: props, children: children }); } const useTableContext = () => react.useContext(EdsDataGridContext); /* istanbul ignore file */ // File ignored, as relevant actions are covered via Filter.test.tsx function DebouncedInput({ value: initialValue, values, onChange, debounce = 500, label, ...props }) { const [value, setValue] = react.useState(initialValue); react.useEffect(() => { setValue(initialValue); }, [initialValue]); react.useEffect(() => { const timeout = setTimeout(() => { onChange(value); }, debounce); return () => clearTimeout(timeout); // eslint-disable-next-line react-hooks/exhaustive-deps }, [value]); return /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, { children: props.type === 'number' ? /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.InputWrapper, { label: props.placeholder, children: /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Input, { type: 'number', placeholder: '0', value: value, onChange: e => setValue(e.target.valueAsNumber) }) }) : /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [/*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Autocomplete, { options: values, autoWidth: true, multiple: true, optionComponent: opt => opt === 'NULL_OR_UNDEFINED' ? '<Blank>' : opt, "data-testid": 'autocomplete' /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */, label: `Select ${label ?? ''}`, placeholder: props.placeholder ?? 'Search', selectedOptions: value, onOptionsChange: c => setValue(c.selectedItems), multiline: true }), /*#__PURE__*/jsxRuntime.jsx("div", { style: { display: 'flex', flexWrap: 'wrap', marginTop: '8px' }, children: value.map(v => /*#__PURE__*/jsxRuntime.jsxs(edsCoreReact.Chip, { title: v, onKeyDownCapture: event => { if (['Backspace', 'Delete'].includes(event.key)) { onChange(value.filter(item => item !== v)); } }, style: { margin: '4px' }, onDelete: () => onChange(value.filter(item => item !== v)), children: [v.slice(0, 20), v.length > 20 ? '...' : ''] }, v)) })] }) }); } /* istanbul ignore file */ const NumberContainer = styled__default.default.div.withConfig({ displayName: "Filter__NumberContainer", componentId: "sc-ytpdpw-0" })(["display:grid;grid-template-columns:80px 80px;grid-column-gap:32px;"]); function Filter({ column, table }) { const firstValue = table.getPreFilteredRowModel().flatRows[0]?.getValue(column.id); const columnText = react.useMemo(() => { let header; try { if (typeof column.columnDef.header === 'function') { const obj = column.columnDef.header( // eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-argument {}); header = obj.props.children; } else { header = column.columnDef.header; } } catch { /*em all*/ } return header; }, [column.columnDef]); const columnFilterValue = column.getFilterValue(); const sortedUniqueValues = react.useMemo(() => typeof firstValue === 'number' ? [] : Array.from(column.getFacetedUniqueValues().keys()).sort().map(v => v ?? 'NULL_OR_UNDEFINED'), // eslint-disable-next-line react-hooks/exhaustive-deps [column.getFacetedUniqueValues()]); return typeof firstValue === 'number' ? /*#__PURE__*/jsxRuntime.jsxs(NumberContainer, { children: [/*#__PURE__*/jsxRuntime.jsx(DebouncedInput, { type: "number", values: sortedUniqueValues, min: Number(column.getFacetedMinMaxValues()?.[0] ?? ''), max: Number(column.getFacetedMinMaxValues()?.[1] ?? ''), value: columnFilterValue?.[0] ?? '', onChange: value => column.setFilterValue(old => [value, old?.[1]]), placeholder: `Min ${column.getFacetedMinMaxValues()?.[0] ? `(${column.getFacetedMinMaxValues()?.[0]})` : ''}` }), /*#__PURE__*/jsxRuntime.jsx(DebouncedInput, { type: "number", values: sortedUniqueValues, min: Number(column.getFacetedMinMaxValues()?.[0] ?? ''), max: Number(column.getFacetedMinMaxValues()?.[1] ?? ''), value: columnFilterValue?.[1] ?? '', onChange: value => column.setFilterValue(old => [old?.[0], value]), placeholder: `Max ${column.getFacetedMinMaxValues()?.[1] ? `(${column.getFacetedMinMaxValues()?.[1]})` : ''}` })] }) : /*#__PURE__*/jsxRuntime.jsx(DebouncedInput, { type: "text", label: columnText, values: sortedUniqueValues, debounce: 100, value: columnFilterValue ?? [], onChange: value => column.setFilterValue(value), placeholder: `${(columnFilterValue ?? []).length} / ${column.getFacetedUniqueValues().size} selected`, list: column.id + 'list' }); } /* istanbul ignore file */ function FilterWrapper({ column, CustomComponent }) { const { table } = useTableContext(); const firstValue = table.getPreFilteredRowModel().flatRows[0]?.getValue(column.id); const [open, setOpen] = react.useState(false); const filterIconRef = react.useRef(); const togglePopover = event => { event.stopPropagation(); setOpen(!open); }; const columnFilterValue = column.getFilterValue(); const hasActiveFilters = value => { if (Array.isArray(value)) { if (typeof firstValue === 'number') { return value.some(v => !isNaN(v) && !!v); } else { return value.filter(v => !!v).length > 0; } } return value; }; const onChange = react.useCallback(value => column.setFilterValue(value), [column]); return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [/*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Button, { "aria-haspopup": true, "aria-expanded": open, "data-testid": 'open-filters', ref: filterIconRef, onClick: togglePopover, variant: 'ghost_icon', "aria-label": 'Show column filters', children: /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Icon, { color: edsTokens.tokens.colors.text.static_icons__default.hex, data: hasActiveFilters(columnFilterValue) ? edsIcons.filter_alt_active : edsIcons.filter_alt }) }), /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Popover, { style: { width: typeof firstValue === 'number' ? '220px' : '340px' }, anchorEl: filterIconRef.current, open: open, onClose: () => setOpen(false), children: /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Popover.Content, { style: { width: typeof firstValue === 'number' ? '180px' : '310px' }, children: CustomComponent ? /*#__PURE__*/jsxRuntime.jsx(CustomComponent, { onChange: onChange, value: columnFilterValue }) : /*#__PURE__*/jsxRuntime.jsx(Filter, { column: column, table: table }) }) })] }); } const SortIndicator = ({ column }) => { return { asc: /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Icon, { data: edsIcons.arrow_up }), desc: /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Icon, { data: edsIcons.arrow_down }) }[column.getIsSorted()] ?? null; }; const ResizeInner = styled__default.default.div.withConfig({ displayName: "Resizer__ResizeInner", componentId: "sc-plcbjs-0" })(["width:2px;opacity:0;height:100%;"]); const Resizer = styled__default.default.div.withConfig({ displayName: "Resizer", componentId: "sc-plcbjs-1" })(["transform:", ";", "{opacity:", ";}position:absolute;right:0;top:0;height:100%;width:5px;cursor:col-resize;user-select:none;touch-action:none;display:flex;justify-content:flex-end;"], props => props.$columnResizeMode === 'onEnd' ? 'translateX(0px)' : 'none', ResizeInner, props => props.$isResizing ? 1 : 0); const TableCell = styled__default.default(edsCoreReact.Table.Cell).withConfig({ displayName: "TableCell", componentId: "sc-1g0k23m-0" })(["font-weight:bold;position:", ";top:0;", " ", ";&:hover ", "{background:", ";opacity:1;}"], p => p.$sticky || p.$pinned ? 'sticky' : 'relative', p => { if (p.$pinned) { return `${p.$pinned}: ${p.$offset}px;`; } return ''; }, p => { if (p.$sticky && p.$pinned) return 'z-index: 13'; if (p.$sticky || p.$pinned) return 'z-index: 12'; }, ResizeInner, edsTokens.tokens.colors.interactive.primary__hover.rgba); const getSortLabel = sorted => { if (sorted) { return `${sorted}ending`; } return 'none'; }; function TableHeaderCell({ header, columnResizeMode }) { const ctx = useTableContext(); const table = ctx.table; const pinned = header.column.getIsPinned(); const offset = react.useMemo(() => { if (!pinned) { return null; } return pinned === 'left' ? header.getStart() : table.getTotalSize() - header.getStart() - header.getSize(); }, [pinned, header, table]); return header.isPlaceholder ? /*#__PURE__*/jsxRuntime.jsx(TableCell, { $sticky: ctx.stickyHeader, $offset: offset, $pinned: pinned, className: ctx.headerClass ? ctx.headerClass(header.column) : '', style: { ...(ctx.headerStyle ? ctx.headerStyle(header.column) : {}) }, "aria-hidden": true }) : /*#__PURE__*/jsxRuntime.jsxs(TableCell, { $sticky: ctx.stickyHeader, $offset: offset, $pinned: pinned, className: ctx.headerClass ? ctx.headerClass(header.column) : '', "aria-sort": getSortLabel(header.column.getIsSorted()), onClick: header.column.getToggleSortingHandler(), colSpan: header.colSpan, style: { width: header.getSize(), verticalAlign: ctx.enableColumnFiltering ? 'top' : 'middle', ...(ctx.headerStyle ? ctx.headerStyle(header.column) : {}) }, children: [/*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [/*#__PURE__*/jsxRuntime.jsx("div", { style: { display: 'flex', flexDirection: 'column' }, children: /*#__PURE__*/jsxRuntime.jsx("span", { className: "table-header-cell-label", children: reactTable.flexRender(header.column.columnDef.header, header.getContext()) }) }), !header.column.columnDef.meta?.customFilterInput && /*#__PURE__*/jsxRuntime.jsx(SortIndicator, { column: header.column }), header.column.getCanFilter() && !header.column.columnDef.meta?.customFilterInput ? /*#__PURE__*/ // Supressing this warning - div is not interactive, but prevents propagation of events to avoid unintended sorting // eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions jsxRuntime.jsx("div", { onClick: e => e.stopPropagation(), children: /*#__PURE__*/jsxRuntime.jsx(FilterWrapper, { column: header.column }) }) : null] }), columnResizeMode && /*#__PURE__*/jsxRuntime.jsx(Resizer, { onClick: e => e.stopPropagation(), onMouseDown: header.getResizeHandler(), onTouchStart: header.getResizeHandler(), $isResizing: header.column.getIsResizing(), $columnResizeMode: columnResizeMode, className: 'resize-handle', "data-testid": 'resize-handle', children: /*#__PURE__*/jsxRuntime.jsx(ResizeInner, {}) })] }, header.id); } function TableHeaderRow({ headerGroup, columnResizeMode, deltaOffset, table }) { return /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Table.Row, { children: headerGroup.headers.map(header => /*#__PURE__*/jsxRuntime.jsx(TableHeaderCell, { header: header, table: table, columnResizeMode: columnResizeMode, deltaOffset: deltaOffset }, header.id)) }); } function TableFooterCell({ footer, columnResizeMode }) { const ctx = useTableContext(); const table = ctx.table; const pinned = footer.column.getIsPinned(); const offset = react.useMemo(() => { if (!pinned) { return null; } return pinned === 'left' ? footer.getStart() : table.getTotalSize() - footer.getStart() - footer.getSize(); }, [pinned, footer, table]); return footer.isPlaceholder ? /*#__PURE__*/jsxRuntime.jsx(TableCell, { $sticky: ctx.stickyFooter, $offset: offset, $pinned: pinned, className: ctx.footerClass ? ctx.footerClass(footer.column) : '', style: { ...(ctx.footerStyle ? ctx.footerStyle(footer.column) : {}) }, "aria-hidden": true }) : /*#__PURE__*/jsxRuntime.jsxs(TableCell, { $sticky: ctx.stickyFooter, $offset: offset, $pinned: pinned, className: ctx.footerClass ? ctx.footerClass(footer.column) : '', onClick: footer.column.getToggleSortingHandler(), colSpan: footer.colSpan, style: { width: footer.getSize(), verticalAlign: ctx.enableColumnFiltering ? 'top' : 'middle', ...(ctx.footerStyle ? ctx.footerStyle(footer.column) : {}) }, children: [/*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [/*#__PURE__*/jsxRuntime.jsx("div", { style: { display: 'flex', flexDirection: 'column' }, children: /*#__PURE__*/jsxRuntime.jsx("span", { className: "table-footer-cell-label", children: reactTable.flexRender(footer.column.columnDef.footer, footer.getContext()) }) }), !footer.column.columnDef.meta?.customFilterInput && /*#__PURE__*/jsxRuntime.jsx(SortIndicator, { column: footer.column })] }), columnResizeMode && /*#__PURE__*/jsxRuntime.jsx(Resizer, { onClick: e => e.stopPropagation(), onMouseDown: footer.getResizeHandler(), onTouchStart: footer.getResizeHandler(), $isResizing: footer.column.getIsResizing(), $columnResizeMode: columnResizeMode, className: 'resize-handle', "data-testid": 'resize-handle', children: /*#__PURE__*/jsxRuntime.jsx(ResizeInner, {}) })] }, footer.id); } function TableFooterRow({ footerGroup, columnResizeMode, deltaOffset, table }) { return /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Table.Row, { children: footerGroup.headers.map(footer => /*#__PURE__*/jsxRuntime.jsx(TableFooterCell, { footer: footer, table: table, columnResizeMode: columnResizeMode, deltaOffset: deltaOffset }, footer.id)) }); } const StyledCell = styled__default.default(edsCoreReact.Table.Cell).withConfig({ displayName: "TableBodyCell__StyledCell", componentId: "sc-1gsd2k4-0" })(["position:", ";", " z-index:", ";background-color:inherit;"], p => p.$pinned ? 'sticky' : 'relative', p => { if (p.$pinned) { return `${p.$pinned}: ${p.$offset}px;`; } return ''; }, p => p.$pinned ? 11 : 'auto'); function TableBodyCell({ cell }) { const { cellClass, cellStyle, table } = useTableContext(); const pinned = cell.column.getIsPinned(); const pinnedOffset = react.useMemo(() => { if (!pinned) { return 0; } const header = table.getFlatHeaders().find(h => h.id === cell.column.id); return pinned === 'left' ? header.getStart() : table.getTotalSize() - header.getStart() - cell.column.getSize(); }, [pinned, cell.column, table]); return /*#__PURE__*/jsxRuntime.jsx(StyledCell, { $pinned: pinned, $offset: pinnedOffset, className: cellClass ? cellClass(cell.row, cell.column.id) : '', style: { width: cell.column.getSize(), maxWidth: cell.column.getSize(), ...(cellStyle?.(cell.row, cell.column.id) ?? {}) }, children: reactTable.flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id); } function TableRow({ row, onCellClick, onClick, onDoubleClick, onContextMenu }) { const { rowClass, rowStyle } = useTableContext(); return /*#__PURE__*/jsxRuntime.jsx(StyledTableRow, { style: { ...(rowStyle?.(row) ?? {}) }, className: `${row.getIsSelected() ? 'selected' : ''} ${rowClass?.(row)}`, onClick: onClick, onDoubleClick: onDoubleClick, onContextMenu: onContextMenu, children: row.getVisibleCells().map(cell => /*#__PURE__*/jsxRuntime.jsx(TableBodyCell, { cell: cell, onClick: onCellClick ? event => onCellClick(cell, event) : undefined }, cell.id)) }); } // Neccessary to have this attribute as class to prevent overriding hover color const StyledTableRow = styled__default.default(edsCoreReact.Table.Row).withConfig({ displayName: "TableRow__StyledTableRow", componentId: "sc-1vjfq5p-0" })(["background-color:inherit;"]); /** * Function returning wether a string only contains number. Allows leading or trailing spaces. * * Examples: * * ``` * isNumberOnlyString("10") // true * isNumberOnlyString("10.10") // true * isNumberOnlyString("10px") // false * isNumberOnlyString("10%") // false * isNumberOnlyString("10 ") // true * ``` * * @param number * @returns */ function isNumberOnlyString(number) { return !isNaN(Number(number)) && !isNaN(parseFloat(number)); } function addPxSuffixIfInputHasNoPrefix(size) { if (typeof size === 'number' || isNumberOnlyString(size)) { return `${size}px`; } return size; } function logDevelopmentWarningOfPropUse(deprecatedProps) { if (process.env.NODE_ENV !== 'development') { return; } for (const [key, { value, mitigationInfo }] of Object.entries(deprecatedProps)) { if (typeof value !== 'undefined') { console.warn(`The prop '${key}' is deprecated and will be removed in a future release. ${mitigationInfo}`); } } } /* eslint-disable @typescript-eslint/no-non-null-assertion */ function EdsDataGridInner({ rows, columns, columnResizeMode, pageSize, rowSelection, enableRowSelection, enableMultiRowSelection, enableSubRowSelection, selectedRows, rowSelectionState, enableColumnFiltering, debug, enablePagination, enableSorting, stickyHeader, stickyFooter, onSelectRow, onRowSelectionChange, caption, enableVirtual, virtualHeight, columnVisibility, columnVisibilityChange, emptyMessage, columnOrder, cellClass, cellStyle, rowClass, rowStyle, headerClass, headerStyle, footerClass, footerStyle, externalPaginator, onSortingChange, manualSorting, sortingState, columnPinState, scrollbarHorizontal, width, minWidth, height, getRowId, rowVirtualizerInstanceRef, tableInstanceRef, columnSizing, onColumnResize, expansionState, setExpansionState, getSubRows, defaultColumn, onRowContextMenu, onRowClick, onRowDoubleClick, onCellClick, enableFooter, enableSortingRemoval, ...rest }, ref) { logDevelopmentWarningOfPropUse({ virtualHeight: { value: virtualHeight, mitigationInfo: "Use 'height' instead." }, rowSelection: { value: rowSelection, mitigationInfo: "Use 'enableRowSelection' instead." }, onSelectRow: { value: onSelectRow, mitigationInfo: "Use 'onRowSelectionChange' instead." }, selectedRows: { value: selectedRows, mitigationInfo: "Use 'rowSelectionState' instead." } }); const [sorting, setSorting] = react.useState(sortingState ?? []); const [internalRowSelectionState, setInternalRowSelectionState] = react.useState(rowSelectionState ?? selectedRows ?? {}); const [columnPin, setColumnPin] = react.useState(columnPinState ?? {}); const [columnFilters, setColumnFilters] = react.useState([]); const [internalColumnSize, setInternalColumnSize] = react.useState(columnSizing ?? {}); const [visible, setVisible] = react.useState(columnVisibility ?? {}); const [globalFilter, setGlobalFilter] = react.useState(''); const [columnOrderState, setColumnOrderState] = react.useState([]); const [page, setPage] = react.useState({ pageIndex: 0, pageSize: pageSize ?? 25 }); react.useEffect(() => { if (virtualHeight) { console.warn(`virtualHeight prop is deprecated and will be removed in a later version. Please update your code to use height instead.`); } }, [virtualHeight]); react.useEffect(() => { setVisible(columnVisibility ?? {}); }, [columnVisibility, setVisible]); react.useEffect(() => { setColumnPin(s => columnPinState ?? s); }, [columnPinState]); react.useEffect(() => { setSorting(sortingState); }, [sortingState]); react.useEffect(() => { setInternalRowSelectionState(rowSelectionState ?? selectedRows ?? {}); }, [rowSelectionState, selectedRows]); /** * By default, the filter-function accepts single-value filters. This adds multi-filter functionality out of the box. */ const _columns = react.useMemo(() => { return columns.map(column => { if (column.filterFn || column.enableColumnFilter === false) { return column; } /* istanbul ignore next */ return { ...column, filterFn: (row, columnId, filterValue) => { if (debug) { console.log('filterFn', row, columnId, filterValue); } if (!filterValue || (Array.isArray(filterValue) || typeof filterValue === 'string') && filterValue.length === 0) { return true; } const value = row.getValue(columnId) ?? 'NULL_OR_UNDEFINED'; if (Array.isArray(filterValue)) { const numeric = filterValue.some(v => typeof v === 'number'); if (numeric) { const [start, end] = filterValue; return Number(value) >= (isNaN(start) ? 0 : start) && Number(value) <= (!end || isNaN(end) ? Infinity : end); } else { const validFilterValue = filterValue.filter(v => !!v); if (validFilterValue.length === 0) return true; return filterValue.includes(value); } } return value === filterValue; } }; }); }, [debug, columns]); /** * Set up default table options */ const options = { data: rows, columns: _columns, defaultColumn: defaultColumn ?? { size: 150, cell: context => { return /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Typography, { style: { overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis' }, group: "table", variant: "cell_text", children: String(context.getValue() ?? '') }); } }, columnResizeMode: columnResizeMode, onColumnSizingChange: change => { if (typeof change === 'function') { setInternalColumnSize(change(internalColumnSize)); } else { setInternalColumnSize(change); } if (onColumnResize) { onColumnResize(internalColumnSize); } }, state: { sorting, columnPinning: columnPin, rowSelection: internalRowSelectionState, columnOrder: columnOrderState, columnSizing: columnSizing ?? internalColumnSize, expanded: expansionState }, getSubRows: getSubRows, onExpandedChange: setExpansionState, getExpandedRowModel: reactTable.getExpandedRowModel(), onSortingChange: changes => { if (onSortingChange) { onSortingChange(changes); } setSorting(changes); }, enableColumnFilters: !!enableColumnFiltering, enableFilters: !!enableColumnFiltering, enableSorting: enableSorting ?? false, manualSorting: manualSorting ?? false, enableColumnResizing: !!columnResizeMode, getCoreRowModel: reactTable.getCoreRowModel(), getSortedRowModel: reactTable.getSortedRowModel(), debugTable: debug, debugHeaders: debug, debugColumns: debug, enableRowSelection: enableRowSelection ?? rowSelection ?? false, enableMultiRowSelection: enableMultiRowSelection ?? false, enableSubRowSelection: enableSubRowSelection ?? false, enableColumnPinning: true, enablePinning: true, getRowId, enableSortingRemoval: enableSortingRemoval ?? true }; react.useEffect(() => { if (columnOrder && columnOrder.length > 0) { setColumnOrderState(columnOrder ?? []); } }, [columnOrder]); /** * Set up handlers for rowSelection */ if (enableRowSelection ?? rowSelection ?? false) { options.onRowSelectionChange = updaterOrValue => { onSelectRow?.(updaterOrValue); onRowSelectionChange?.(updaterOrValue); setInternalRowSelectionState(updaterOrValue); }; } /** * Set up config for column filtering */ if (enableColumnFiltering) { options.state.columnFilters = columnFilters; options.state.globalFilter = globalFilter; options.onColumnFiltersChange = setColumnFilters; options.onGlobalFilterChange = setGlobalFilter; options.getFacetedRowModel = reactTable.getFacetedRowModel(); options.getFacetedUniqueValues = reactTable.getFacetedUniqueValues(); options.getFacetedMinMaxValues = reactTable.getFacetedMinMaxValues(); options.getFilteredRowModel = reactTable.getFilteredRowModel(); } /** * Set up config for pagination */ if (enablePagination ?? false) { options.state.pagination = page; options.getPaginationRowModel = reactTable.getPaginationRowModel(); } /** * Set up config to handle column visibility controls */ if (columnVisibility) { options.state.columnVisibility = visible; options.onColumnVisibilityChange = vis => { let updated; if (typeof vis === 'function') { updated = vis(visible); } else { updated = vis; } if (columnVisibilityChange) columnVisibilityChange(updated); setVisible(updated); }; } react.useEffect(() => { setPage(prev => ({ ...prev, pageSize: pageSize ?? 25 })); }, [pageSize]); const table = reactTable.useReactTable(options); if (tableInstanceRef) { tableInstanceRef.current = table; } let tableWrapperStyle = {}; /** * Style the parent container to enable virtualization. * By not setting this, the virtual-scroll will always render every row, reducing computational overhead if turned off. */ if (enableVirtual) { tableWrapperStyle = { height: height ?? virtualHeight ?? 500, overflow: 'auto', position: 'relative' }; } const parentRef = react.useRef(null); const combinedRef = react.useMemo(() => edsUtils.mergeRefs(parentRef, ref), [parentRef, ref]); /** * Virtualization setup */ const { density } = edsCoreReact.useEds(); const estimateSize = react.useCallback(() => { return density === 'compact' ? 32 : 48; }, [density]); const virtualizer = reactVirtual.useVirtualizer({ count: table.getRowModel().rows.length, getScrollElement: () => parentRef.current, estimateSize }); if (rowVirtualizerInstanceRef) rowVirtualizerInstanceRef.current = virtualizer; const virtualRows = virtualizer.getVirtualItems(); const paddingTop = virtualRows.length ? virtualRows[0].start : 0; const paddingBottom = virtualRows.length ? virtualizer.getTotalSize() - virtualRows[virtualRows.length - 1].end : 0; // These classes are primarily used to allow for feature-detection in the test-suite const classList = { 'sticky-header': !!stickyHeader, 'sticky-footer': !!stickyFooter, virtual: !!enableVirtual, paging: !!enablePagination }; return /*#__PURE__*/jsxRuntime.jsxs(TableProvider, { cellClass: cellClass, cellStyle: cellStyle, rowClass: rowClass, rowStyle: rowStyle, headerClass: headerClass, headerStyle: headerStyle, footerClass: footerClass, footerStyle: footerStyle, table: table, enableSorting: !!enableSorting, enableColumnFiltering: !!enableColumnFiltering, stickyHeader: !!stickyHeader, stickyFooter: !!stickyFooter, children: [/*#__PURE__*/jsxRuntime.jsxs(TableWrapper, { ...rest, className: `table-wrapper ${rest.className ?? ''}`, style: { ...rest.style, ...tableWrapperStyle }, ref: combinedRef, $height: height, $width: width, $scrollbarHorizontal: scrollbarHorizontal, children: [/*#__PURE__*/jsxRuntime.jsxs(edsCoreReact.Table, { className: Object.entries(classList).filter(([, k]) => k).map(([k]) => k).join(' '), style: { tableLayout: scrollbarHorizontal ? 'fixed' : 'auto', width: table.getTotalSize(), minWidth: scrollbarHorizontal ? minWidth : 'auto' }, children: [caption && /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Table.Caption, { children: caption }), /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Table.Head, { sticky: stickyHeader, children: table.getHeaderGroups().map(headerGroup => /*#__PURE__*/jsxRuntime.jsx(TableHeaderRow, { table: table, headerGroup: headerGroup, columnResizeMode: columnResizeMode, deltaOffset: table.getState().columnSizingInfo.deltaOffset }, headerGroup.id)) }), /*#__PURE__*/jsxRuntime.jsxs(edsCoreReact.Table.Body, { style: { backgroundColor: 'inherit' }, children: [table.getRowModel().rows.length === 0 && emptyMessage && /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Table.Row, { children: /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Table.Cell, { colSpan: table.getFlatHeaders().length, children: emptyMessage }) }), enableVirtual && /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [paddingTop > 0 && /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Table.Row, { "data-testid": "virtual-padding-top", className: 'virtual-padding-top', style: { pointerEvents: 'none' }, children: /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Table.Cell, { style: { height: `${paddingTop}px` } }) }), virtualRows.map(virtualItem => { const row = table.getRowModel().rows[virtualItem.index]; return /*#__PURE__*/jsxRuntime.jsx(TableRow, { row: row, onContextMenu: onRowContextMenu ? event => onRowContextMenu(row, event) : undefined, onClick: onRowClick ? event => onRowClick(row, event) : undefined, onDoubleClick: onRowDoubleClick ? event => onRowDoubleClick(row, event) : undefined, onCellClick: onCellClick }, virtualItem.index); }), paddingBottom > 0 && /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Table.Row, { "data-testid": "virtual-padding-bottom", className: 'virtual-padding-bottom', style: { pointerEvents: 'none' }, children: /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Table.Cell, { style: { height: `${paddingBottom}px` } }) })] }), !enableVirtual && table.getRowModel().rows.map(row => /*#__PURE__*/jsxRuntime.jsx(TableRow, { row: row, onContextMenu: onRowContextMenu ? event => onRowContextMenu(row, event) : undefined, onClick: onRowClick ? event => onRowClick(row, event) : undefined, onCellClick: onCellClick }, row.id))] }), enableFooter && /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Table.Foot, { sticky: stickyFooter, "data-testid": "eds-grid-footer", children: table.getFooterGroups().map(footerGroup => /*#__PURE__*/jsxRuntime.jsx(TableFooterRow, { table: table, footerGroup: footerGroup, columnResizeMode: columnResizeMode, deltaOffset: table.getState().columnSizingInfo.deltaOffset }, footerGroup.id)) })] }), externalPaginator ? externalPaginator : enablePagination && /*#__PURE__*/jsxRuntime.jsx("div", { style: { maxWidth: `${table.getTotalSize()}px` }, children: /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Pagination, { totalItems: table.getFilteredRowModel().rows.length, withItemIndicator: true, itemsPerPage: page.pageSize, onChange: (e, p) => setPage(s => ({ ...s, pageIndex: p - 1 })), defaultPage: 1 }) })] }), debug && enableVirtual && /*#__PURE__*/jsxRuntime.jsxs("span", { children: ["Visible items: ", virtualizer.range.startIndex, " -", ' ', virtualizer.range.endIndex, " / ", rows.length] })] }); } const TableWrapper = styled__default.default.div.withConfig({ displayName: "EdsDataGrid__TableWrapper", componentId: "sc-82fj3f-0" })(["height:", ";width:", ";overflow:auto;contain:", ";"], ({ $height }) => addPxSuffixIfInputHasNoPrefix($height) ?? 'auto', ({ $scrollbarHorizontal, $width }) => $scrollbarHorizontal ? addPxSuffixIfInputHasNoPrefix($width) ?? '100%' : 'auto', ({ $height, $width }) => Boolean($height) && Boolean($width) ? 'strict' : 'unset'); const EdsDataGrid = /*#__PURE__*/react.forwardRef(EdsDataGridInner); Object.defineProperty(exports, "createColumnHelper", { enumerable: true, get: function () { return reactTable.createColumnHelper; } }); exports.EdsDataGrid = EdsDataGrid; exports.FilterWrapper = FilterWrapper; exports.SortIndicator = SortIndicator;