UNPKG

@itwin/itwinui-react

Version:

A react component library for iTwinUI

916 lines (915 loc) 27.3 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true, }); function _export(target, all) { for (var name in all) Object.defineProperty(target, name, { enumerable: true, get: all[name], }); } _export(exports, { Table: function () { return Table; }, iuiId: function () { return iuiId; }, tableResizeStartAction: function () { return tableResizeStartAction; }, }); const _interop_require_default = require('@swc/helpers/_/_interop_require_default'); const _interop_require_wildcard = require('@swc/helpers/_/_interop_require_wildcard'); const _react = /*#__PURE__*/ _interop_require_wildcard._(require('react')); const _classnames = /*#__PURE__*/ _interop_require_default._( require('classnames'), ); const _reacttable = require('react-table'); const _ProgressRadial = require('../ProgressIndicators/ProgressRadial.js'); const _index = require('../../utils/index.js'); const _utils = require('./utils.js'); const _TableRowMemoized = require('./TableRowMemoized.js'); const _customFilterFunctions = require('./filters/customFilterFunctions.js'); const _index1 = require('./hooks/index.js'); const _index2 = require('./actionHandlers/index.js'); const _index3 = require('./columns/index.js'); const _ColumnHeader = require('./ColumnHeader.js'); const _TableExpandableContentMemoized = require('./TableExpandableContentMemoized.js'); const _VisuallyHidden = require('../VisuallyHidden/VisuallyHidden.js'); const singleRowSelectedAction = 'singleRowSelected'; const shiftRowSelectedAction = 'shiftRowSelected'; const tableResizeStartAction = 'tableResizeStart'; const tableResizeEndAction = 'tableResizeEnd'; const iuiId = Symbol('iui-id'); const flattenColumns = (columns) => { let flatColumns = []; columns.forEach((column) => { flatColumns.push(column); if ('columns' in column) flatColumns.push(...flattenColumns(column.columns)); }); return flatColumns; }; const Table = (props) => { let { data, columns, isLoading = false, emptyTableContent, className, style, id, isSelectable = false, onSelect, onRowClick, selectionMode = 'multi', isSortable = false, onSort, stateReducer, onBottomReached, onRowInViewport, intersectionMargin = 300, subComponent, onExpand, onFilter, globalFilterValue, emptyFilteredTableContent, filterTypes: filterFunctions, expanderCell, isRowDisabled, rowProps, density = 'default', selectSubRows = true, getSubRows, selectRowOnClick = true, paginatorRenderer, pageSize = 25, isResizable = false, columnResizeMode = 'fit', styleType = 'default', enableVirtualization = false, enableColumnReordering = false, headerWrapperProps, headerProps, bodyProps, tableProps, emptyTableContentProps, getRowId, caption = 'Table', role, scrollToRow, useControlledState, autoResetExpanded, autoResetFilters, autoResetGlobalFilter, autoResetHiddenColumns, autoResetPage, autoResetResize, autoResetSelectedRows, autoResetSortBy, defaultCanFilter, defaultCanSort, defaultColumn: defaultColumnProp, disableFilters, disableGlobalFilter, disableMultiSort, disableSortRemove, disabledMultiRemove, expandSubRows, globalFilter, initialState, isMultiSortEvent, manualExpandedKey, manualFilters, manualGlobalFilter, manualRowSelectedKey, manualSortBy, maxMultiSortColCount, orderByFn, pageCount, sortTypes, manualPagination, paginateExpandedRows, ..._rest } = props; let { ariaRestAttributes, nonAriaRestAttributes } = _react.useMemo( () => Object.entries(_rest).reduce( (result, [key, value]) => { if (key.startsWith('aria-')) result.ariaRestAttributes[key] = value; else result.nonAriaRestAttributes[key] = value; return result; }, { ariaRestAttributes: {}, nonAriaRestAttributes: {}, }, ), [_rest], ); let { outerAriaRestAttributes, innerAriaRestAttributes } = _react.useMemo(() => { if (tableProps || role) return { outerAriaRestAttributes: { ...ariaRestAttributes, }, innerAriaRestAttributes: {}, }; return { outerAriaRestAttributes: {}, innerAriaRestAttributes: { ...ariaRestAttributes, }, }; }, [ariaRestAttributes, role, tableProps]); (0, _index.useGlobals)(); let ownerDocument = _react.useRef(void 0); let defaultColumn = _react.useMemo( () => ({ maxWidth: 0, minWidth: 0, width: 0, ...defaultColumnProp, }), [defaultColumnProp], ); let rowHeight = _react.useMemo(() => { if ('condensed' === density) return 50; if ('extra-condensed' === density) return 38; return 62; }, [density]); let onBottomReachedRef = (0, _index.useLatestRef)(onBottomReached); let onRowInViewportRef = (0, _index.useLatestRef)(onRowInViewport); let hasManualSelectionColumn = _react.useMemo(() => { let flatColumns = flattenColumns(columns); return flatColumns.some( (column) => column.id === _index3.SELECTION_CELL_ID, ); }, [columns]); let disableUserSelect = _react.useCallback((e) => { if ('Shift' === e.key) ownerDocument.current && (ownerDocument.current.documentElement.style.userSelect = 'none'); }, []); let enableUserSelect = _react.useCallback((e) => { if ('Shift' === e.key) ownerDocument.current && (ownerDocument.current.documentElement.style.userSelect = ''); }, []); _react.useEffect(() => { if (!isSelectable || 'multi' !== selectionMode) return; let ownerDoc = ownerDocument.current; ownerDoc?.addEventListener('keydown', disableUserSelect); ownerDoc?.addEventListener('keyup', enableUserSelect); return () => { ownerDoc?.removeEventListener('keydown', disableUserSelect); ownerDoc?.removeEventListener('keyup', enableUserSelect); }; }, [ isSelectable, selectionMode, ownerDocument, disableUserSelect, enableUserSelect, ]); let previousFilter = _react.useRef([]); let currentFilter = _react.useRef(previousFilter.current); let tableStateReducer = _react.useCallback( (newState, action, previousState, instance) => { switch (action.type) { case _reacttable.actions.toggleSortBy: onSort?.(newState); break; case _reacttable.actions.setFilter: currentFilter.current = (0, _index2.onFilterHandler)( newState, action, previousState, currentFilter.current, instance, ); break; case _reacttable.actions.toggleRowExpanded: case _reacttable.actions.toggleAllRowsExpanded: (0, _index2.onExpandHandler)(newState, instance, onExpand); break; case singleRowSelectedAction: newState = (0, _index2.onSingleSelectHandler)( newState, action, instance, onSelect, hasManualSelectionColumn ? void 0 : isRowDisabled, ); break; case shiftRowSelectedAction: newState = (0, _index2.onShiftSelectHandler)( newState, action, instance, onSelect, hasManualSelectionColumn ? void 0 : isRowDisabled, ); break; case _reacttable.actions.toggleRowSelected: case _reacttable.actions.toggleAllRowsSelected: case _reacttable.actions.toggleAllPageRowsSelected: (0, _index2.onToggleHandler)( newState, action, instance, onSelect, hasManualSelectionColumn ? void 0 : isRowDisabled, ); break; case tableResizeStartAction: newState = (0, _index2.onTableResizeStart)(newState); break; case tableResizeEndAction: newState = (0, _index2.onTableResizeEnd)(newState, action); break; default: break; } return stateReducer ? stateReducer(newState, action, previousState, instance) : newState; }, [ hasManualSelectionColumn, isRowDisabled, onExpand, onSelect, onSort, stateReducer, ], ); let filterTypes = _react.useMemo( () => ({ ..._customFilterFunctions.customFilterFunctions, ...filterFunctions, }), [filterFunctions], ); let hasAnySubRows = _react.useMemo( () => data.some((item, index) => getSubRows ? getSubRows(item, index) : item.subRows, ), [data, getSubRows], ); let getSubRowsWithSubComponents = _react.useCallback( (originalRow, relativeIndex) => { if (originalRow[iuiId]) return []; if (originalRow.subRows) return originalRow.subRows; return [ { [iuiId]: `subcomponent-${relativeIndex}`, ...originalRow, }, ]; }, [], ); let getRowIdWithSubComponents = _react.useCallback( (originalRow, relativeIndex, parent) => { let defaultRowId = parent ? `${parent.id}.${relativeIndex}` : `${relativeIndex}`; let rowIdFromUser = getRowId?.(originalRow, relativeIndex, parent); if (void 0 !== rowIdFromUser && originalRow[iuiId]) return `${rowIdFromUser}-${defaultRowId}`; return rowIdFromUser ?? defaultRowId; }, [getRowId], ); let instance = (0, _reacttable.useTable)( { manualPagination: manualPagination ?? !paginatorRenderer, paginateExpandedRows: paginateExpandedRows ?? false, useControlledState, autoResetExpanded, autoResetFilters, autoResetGlobalFilter, autoResetHiddenColumns, autoResetPage, autoResetResize, autoResetSelectedRows, autoResetSortBy, defaultCanFilter, defaultCanSort, disableFilters, disableGlobalFilter, disableMultiSort, disableSortRemove, disabledMultiRemove, expandSubRows, globalFilter, isMultiSortEvent, manualExpandedKey, manualFilters, manualGlobalFilter, manualRowSelectedKey, manualSortBy, maxMultiSortColCount, orderByFn, pageCount: pageCount ?? -1, sortTypes, columns, defaultColumn, disableSortBy: !isSortable, stateReducer: tableStateReducer, filterTypes, selectSubRows, data, getSubRows: subComponent ? getSubRowsWithSubComponents : getSubRows, initialState: { pageSize, ...initialState, }, columnResizeMode, getRowId: subComponent ? getRowIdWithSubComponents : getRowId, }, _reacttable.useFlexLayout, (0, _index1.useResizeColumns)(ownerDocument), _reacttable.useFilters, (0, _index1.useSubRowFiltering)(hasAnySubRows), _reacttable.useGlobalFilter, _reacttable.useSortBy, _reacttable.useExpanded, _reacttable.usePagination, _reacttable.useRowSelect, _index1.useSubRowSelection, (0, _index1.useExpanderCell)(subComponent, expanderCell, isRowDisabled), (0, _index1.useSelectionCell)( isSelectable, selectionMode, isRowDisabled, density, ), _reacttable.useColumnOrder, (0, _index1.useColumnDragAndDrop)(enableColumnReordering), _index1.useStickyColumns, ); let { getTableProps, rows, headerGroups: _headerGroups, getTableBodyProps, prepareRow, state, allColumns, dispatch, page, gotoPage, setPageSize, flatHeaders, setGlobalFilter, } = instance; let headerGroups = _headerGroups; let logWarning = (0, _index.useWarningLogger)(); if (1 === columns.length && 'columns' in columns[0]) { headerGroups = _headerGroups.slice(1); if ('development' === process.env.NODE_ENV) logWarning( "Table's `columns` prop should not have a top-level `Header` or sub-columns. They are only allowed to be passed for backwards compatibility.\n See https://github.com/iTwin/iTwinUI/wiki/iTwinUI-react-v2-migration-guide#breaking-changes", ); } if ( 'development' === process.env.NODE_ENV && subComponent && data.some((item) => !!item.subRows?.length) ) logWarning( 'Passing both `subComponent` and `data` with `subRows` is not supported. There are features designed for `subRows` that are not compatible with `subComponent` and vice versa.', ); let areFiltersSet = allColumns.some( (column) => null != column.filterValue && '' !== column.filterValue, ) || !!globalFilterValue; let onRowClickHandler = _react.useCallback( (event, row) => { let isDisabled = isRowDisabled?.(row.original); let ctrlPressed = event.ctrlKey || event.metaKey; if (!isDisabled) onRowClick?.(event, row); if ( isSelectable && !isDisabled && selectRowOnClick && !event.isDefaultPrevented() ) if ('multi' === selectionMode && event.shiftKey) dispatch({ type: shiftRowSelectedAction, id: row.id, ctrlPressed: ctrlPressed, }); else if (row.isSelected || ('single' !== selectionMode && ctrlPressed)) row.toggleRowSelected(!row.isSelected); else dispatch({ type: singleRowSelectedAction, id: row.id, }); }, [ isRowDisabled, isSelectable, selectRowOnClick, selectionMode, dispatch, onRowClick, ], ); _react.useEffect(() => { setGlobalFilter(globalFilterValue); }, [globalFilterValue, setGlobalFilter]); _react.useEffect(() => { setPageSize(pageSize); }, [pageSize, setPageSize]); _react.useEffect(() => { if (previousFilter.current !== currentFilter.current) { previousFilter.current = currentFilter.current; onFilter?.(currentFilter.current, state, instance.filteredRows); } }, [state, instance.filteredRows, onFilter]); let lastPassedColumns = _react.useRef([]); _react.useEffect(() => { if ( lastPassedColumns.current.length > 0 && JSON.stringify(lastPassedColumns.current) !== JSON.stringify(columns) ) instance.setColumnOrder([]); lastPassedColumns.current = columns; }, [columns, instance]); let paginatorRendererProps = _react.useMemo( () => ({ currentPage: state.pageIndex, pageSize: state.pageSize, totalRowsCount: rows.length, size: 'default' !== density ? 'small' : 'default', isLoading, onPageChange: gotoPage, onPageSizeChange: setPageSize, totalSelectedRowsCount: 'single' === selectionMode ? 0 : instance.selectedFlatRows.length, }), [ density, gotoPage, isLoading, rows.length, setPageSize, state.pageIndex, state.pageSize, instance.selectedFlatRows, selectionMode, ], ); let tableRef = _react.useRef(null); let { scrollToIndex, tableRowRef } = (0, _index1.useScrollToRow)({ ...props, scrollToRow, page, }); let columnRefs = _react.useRef({}); let previousTableWidth = _react.useRef(0); let onTableResize = _react.useCallback( ({ width }) => { if (!isResizable) return; instance.tableWidth = width; if (width === previousTableWidth.current) return; previousTableWidth.current = width; flatHeaders.forEach((header) => { if (columnRefs.current[header.id]) header.resizeWidth = columnRefs.current[header.id].getBoundingClientRect().width; }); if (0 === Object.keys(state.columnResizing.columnWidths).length) return; dispatch({ type: tableResizeStartAction, }); }, [ dispatch, state.columnResizing.columnWidths, flatHeaders, instance, isResizable, ], ); let [resizeRef] = (0, _index.useResizeObserver)(onTableResize); (0, _index.useLayoutEffect)(() => { if (state.isTableResizing) { let newColumnWidths = {}; flatHeaders.forEach((column) => { if (columnRefs.current[column.id]) newColumnWidths[column.id] = columnRefs.current[column.id].getBoundingClientRect().width; }); dispatch({ type: tableResizeEndAction, columnWidths: newColumnWidths, }); } }); let { virtualizer, css: virtualizerCss } = (0, _index.useVirtualScroll)({ enabled: enableVirtualization, count: page.length, getScrollElement: () => tableRef.current, estimateSize: () => rowHeight, getItemKey: (index) => page[index].id, overscan: 1, }); (0, _index.useLayoutEffect)(() => { if (scrollToIndex) virtualizer.scrollToIndex(scrollToIndex, { align: 'start', }); }, [virtualizer, scrollToIndex]); let getPreparedRow = _react.useCallback( (index, virtualItem, virtualizer) => { let row = page[index]; prepareRow(row); let isRowASubComponent = !!row.original[iuiId] && !!subComponent; if (isRowASubComponent) return _react.createElement( _TableExpandableContentMemoized.TableExpandableContentMemoized, { key: row.getRowProps().key, virtualItem: virtualItem, ref: enableVirtualization ? virtualizer?.measureElement : tableRowRef(row), isDisabled: !!isRowDisabled?.(row.original), }, subComponent(row), ); return _react.createElement(_TableRowMemoized.TableRowMemoized, { row: row, rowProps: rowProps, isLast: index === page.length - 1, onRowInViewport: onRowInViewportRef, onBottomReached: onBottomReachedRef, intersectionMargin: intersectionMargin, state: state, key: row.getRowProps().key, onClick: onRowClickHandler, subComponent: subComponent, isDisabled: !!isRowDisabled?.(row.original), tableHasSubRows: hasAnySubRows, tableInstance: instance, expanderCell: expanderCell, scrollContainerRef: tableRef.current, tableRowRef: enableVirtualization ? void 0 : tableRowRef(row), density: density, virtualItem: virtualItem, virtualizer: virtualizer, }); }, [ page, prepareRow, subComponent, rowProps, onRowInViewportRef, onBottomReachedRef, intersectionMargin, state, onRowClickHandler, isRowDisabled, hasAnySubRows, instance, expanderCell, enableVirtualization, tableRowRef, density, ], ); let updateStickyState = () => { if (!tableRef.current || flatHeaders.every((header) => !header.sticky)) return; 0 !== tableRef.current.scrollLeft ? dispatch({ type: _reacttable.actions.setScrolledRight, value: true, }) : dispatch({ type: _reacttable.actions.setScrolledRight, value: false, }); tableRef.current.scrollLeft !== tableRef.current.scrollWidth - tableRef.current.clientWidth ? dispatch({ type: _reacttable.actions.setScrolledLeft, value: true, }) : dispatch({ type: _reacttable.actions.setScrolledLeft, value: false, }); }; _react.useEffect(() => { updateStickyState(); }, []); let captionId = (0, _index.useId)(); return _react.createElement( _utils.TableInstanceContext.Provider, { value: instance, }, _react.createElement( _index.Box, { ref: (0, _index.useMergedRefs)( tableRef, resizeRef, _react.useCallback((element) => { ownerDocument.current = element?.ownerDocument; }, []), ), id: id, ...getTableProps({ className: (0, _classnames.default)('iui-table', className), style: { minWidth: 0, ...style, }, }), role: role, onScroll: () => updateStickyState(), 'data-iui-size': 'default' === density ? void 0 : density, ...outerAriaRestAttributes, ...nonAriaRestAttributes, }, _react.createElement( _index.ShadowRoot, null, _react.createElement( 'div', { role: 'table', ...innerAriaRestAttributes, ...tableProps, 'aria-labelledby': captionId, }, _react.createElement( _VisuallyHidden.VisuallyHidden, { id: captionId, }, caption, ), _react.createElement('slot', { name: 'iui-table-header-wrapper', }), _react.createElement('slot', { name: 'iui-table-body', }), ), _react.createElement('slot', { name: 'iui-table-body-extra', }), _react.createElement('slot', null), ), headerGroups.map((headerGroup) => { headerGroup.headers = headerGroup.headers.filter( (header) => !header.id.includes('iui-table-checkbox-selector_placeholder') && !header.id.includes('iui-table-expander_placeholder'), ); let headerGroupProps = headerGroup.getHeaderGroupProps({ className: 'iui-table-row', }); return _react.createElement( _index.Box, { slot: 'iui-table-header-wrapper', as: 'div', key: headerGroupProps.key, ...headerWrapperProps, className: (0, _classnames.default)( 'iui-table-header-wrapper', headerWrapperProps?.className, ), }, _react.createElement( _index.Box, { as: 'div', ...headerProps, className: (0, _classnames.default)( 'iui-table-header', headerProps?.className, ), }, _react.createElement( _index.Box, { ...headerGroupProps, key: headerGroupProps.key, }, headerGroup.headers.map((column, index) => { let dragAndDropProps = column.getDragAndDropProps(); return _react.createElement(_ColumnHeader.ColumnHeader, { ...dragAndDropProps, key: dragAndDropProps.key || column.id || index, column: column, areFiltersSet: areFiltersSet, columnHasExpanders: hasAnySubRows && index === headerGroup.headers.findIndex( (c) => c.id !== _index3.SELECTION_CELL_ID, ), isLast: index === headerGroup.headers.length - 1, isTableEmpty: 0 === data.length, isResizable: isResizable, columnResizeMode: columnResizeMode, enableColumnReordering: enableColumnReordering, density: density, ref: (el) => { if (el) columnRefs.current[column.id] = el; }, }); }), ), ), ); }), _react.createElement( _index.Box, { slot: 'iui-table-body', as: 'div', ...bodyProps, ...getTableBodyProps({ className: (0, _classnames.default)( 'iui-table-body', { 'iui-zebra-striping': 'zebra-rows' === styleType, }, bodyProps?.className, ), }), role: void 0, }, _react.createElement( _index.ShadowRoot, { css: virtualizerCss, flush: false, }, enableVirtualization && 0 !== data.length ? _react.createElement( 'div', { 'data-iui-virtualizer': 'root', style: { minBlockSize: virtualizer.getTotalSize(), }, }, _react.createElement('slot', null), ) : _react.createElement('slot', null), ), 0 !== data.length && _react.createElement( _react.Fragment, null, enableVirtualization ? virtualizer .getVirtualItems() .map((virtualItem) => getPreparedRow(virtualItem.index, virtualItem, virtualizer), ) : page.map((_, index) => getPreparedRow(index)), ), ), isLoading && 0 === data.length && _react.createElement( TableBodyExtraWrapper, null, _react.createElement( TableEmptyWrapper, emptyTableContentProps, _react.createElement(_ProgressRadial.ProgressRadial, { indeterminate: true, }), ), ), !isLoading && 0 === data.length && !areFiltersSet && _react.createElement( TableBodyExtraWrapper, null, _react.createElement( TableEmptyWrapper, emptyTableContentProps, _react.createElement('div', null, emptyTableContent), ), ), !isLoading && (0 === data.length || 0 === rows.length) && areFiltersSet && _react.createElement( TableBodyExtraWrapper, null, _react.createElement( TableEmptyWrapper, emptyTableContentProps, _react.createElement('div', null, emptyFilteredTableContent), ), ), isLoading && 0 !== data.length && _react.createElement( TableBodyExtraWrapper, { 'data-iui-loading': 'true', }, _react.createElement(_ProgressRadial.ProgressRadial, { indeterminate: true, size: 'small', }), ), paginatorRenderer?.(paginatorRendererProps), ), ); }; if ('development' === process.env.NODE_ENV) Table.displayName = 'Table'; const TableBodyExtraWrapper = _react.forwardRef((props, ref) => { let { children, ...rest } = props; return _react.createElement( _index.Box, { as: 'div', ref: ref, slot: 'iui-table-body-extra', ...rest, className: (0, _classnames.default)( 'iui-table-body-extra', rest.className, ), }, children, ); }); const TableEmptyWrapper = _react.forwardRef((props, ref) => { let { children, ...rest } = props; return _react.createElement( _index.Box, { as: 'div', ref: ref, ...rest, className: (0, _classnames.default)('iui-table-empty', rest.className), }, children, ); });