@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
JavaScript
'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;