UNPKG

aspire-react-data-table

Version:
1,192 lines (1,134 loc) 104 kB
import * as React from 'react'; import React__default, { useState, useRef } from 'react'; import styled, { css, ThemeProvider } from 'styled-components'; import { IoFilter, IoSearchOutline, IoCloseOutline, IoEllipsisHorizontalSharp, IoCloseSharp } from 'react-icons/io5'; import merge from 'deepmerge'; import { CiViewColumn } from 'react-icons/ci'; import { MdOutlineDragIndicator } from 'react-icons/md'; var SortOrder; (function (SortOrder) { SortOrder["ASC"] = "asc"; SortOrder["DESC"] = "desc"; })(SortOrder || (SortOrder = {})); function prop(obj, key) { return obj[key]; } function isEmpty(field = '') { if (typeof field === 'number') { return false; } return !field || field.length === 0; } function sort(rows, selector, direction, sortFn) { if (!selector) { return rows; } if (sortFn && typeof sortFn === 'function') { return sortFn(rows.slice(0), selector, direction); } return rows.slice(0).sort((a, b) => { const aValue = selector(a); const bValue = selector(b); console.log('test sort '); if (direction === 'asc') { if (aValue < bValue) { return -1; } if (aValue > bValue) { return 1; } } if (direction === 'desc') { if (aValue > bValue) { return -1; } if (aValue < bValue) { return 1; } } return 0; }); } function getProperty(row, selector, format, rowIndex) { if (!selector) { return null; } if (format && typeof format === 'function') { return format(row, rowIndex); } return selector(row, rowIndex); } function insertItem(array = [], item, index = 0) { return [...array.slice(0, index), item, ...array.slice(index)]; } function removeItem(array = [], item, keyField = 'id') { const newArray = array.slice(); const outerField = prop(item, keyField); if (outerField) { newArray.splice(newArray.findIndex((a) => { const innerField = prop(a, keyField); return innerField === outerField; }), 1); } else { newArray.splice(newArray.findIndex(a => a === item), 1); } return newArray; } function decorateColumns(columns) { return columns.map((column, index) => { const decoratedColumn = Object.assign(Object.assign({}, column), { sortable: column.sortable || !!column.sortFunction || undefined }); if (!column.id) { decoratedColumn.id = index + 1; return decoratedColumn; } return decoratedColumn; }); } function getSortDirection(ascDirection = false) { return ascDirection ? SortOrder.ASC : SortOrder.DESC; } function handleFunctionProps(object, ...args) { let newObject; Object.keys(object) .map(o => object[o]) .forEach((value, index) => { const oldObject = object; if (typeof value === 'function') { newObject = Object.assign(Object.assign({}, oldObject), { [Object.keys(object)[index]]: value(...args) }); } }); return newObject || object; } function getNumberOfPages(rowCount, rowsPerPage) { return Math.ceil(rowCount / rowsPerPage); } function recalculatePage(prevPage, nextPage) { return Math.min(prevPage, nextPage); } const noop = () => null; function getConditionalStyle(row, conditionalRowStyles = [], baseClassNames = []) { let rowStyle = {}; let classNames = [...baseClassNames]; if (conditionalRowStyles.length) { conditionalRowStyles.forEach(crs => { if (!crs.when || typeof crs.when !== 'function') { throw new Error('"when" must be defined in the conditional style object and must be function'); } if (crs.when(row)) { rowStyle = crs.style || {}; if (crs.classNames) { classNames = [...classNames, ...crs.classNames]; } if (typeof crs.style === 'function') { rowStyle = crs.style(row) || {}; } } }); } return { conditionalStyle: rowStyle, classNames: classNames.join(' ') }; } function isRowSelected(row, selectedRows = [], keyField = 'id') { const outerField = prop(row, keyField); if (outerField) { return selectedRows.some(r => { const innerField = prop(r, keyField); return innerField === outerField; }); } return selectedRows.some(r => r === row); } function isOdd(num) { return num % 2 === 0; } function findColumnIndexById(columns, id) { if (!id) { return -1; } return columns.findIndex(c => { return equalizeId(c.id, id); }); } function equalizeId(a, b) { return a == b; } function tableReducer(state, action) { const toggleOnSelectedRowsChange = !state.toggleOnSelectedRowsChange; switch (action.type) { case 'SELECT_ALL_ROWS': { const { keyField, rows, rowCount, mergeSelections } = action; const allChecked = !state.allSelected; const toggleOnSelectedRowsChange = !state.toggleOnSelectedRowsChange; if (mergeSelections) { const selections = allChecked ? [...state.selectedRows, ...rows.filter(row => !isRowSelected(row, state.selectedRows, keyField))] : state.selectedRows.filter(row => !isRowSelected(row, rows, keyField)); return Object.assign(Object.assign({}, state), { allSelected: allChecked, selectedCount: selections.length, selectedRows: selections, toggleOnSelectedRowsChange }); } return Object.assign(Object.assign({}, state), { allSelected: allChecked, selectedCount: allChecked ? rowCount : 0, selectedRows: allChecked ? rows : [], toggleOnSelectedRowsChange }); } case 'SELECT_SINGLE_ROW': { const { keyField, row, isSelected, rowCount, singleSelect } = action; if (singleSelect) { if (isSelected) { return Object.assign(Object.assign({}, state), { selectedCount: 0, allSelected: false, selectedRows: [], toggleOnSelectedRowsChange }); } return Object.assign(Object.assign({}, state), { selectedCount: 1, allSelected: false, selectedRows: [row], toggleOnSelectedRowsChange }); } if (isSelected) { return Object.assign(Object.assign({}, state), { selectedCount: state.selectedRows.length > 0 ? state.selectedRows.length - 1 : 0, allSelected: false, selectedRows: removeItem(state.selectedRows, row, keyField), toggleOnSelectedRowsChange }); } return Object.assign(Object.assign({}, state), { selectedCount: state.selectedRows.length + 1, allSelected: state.selectedRows.length + 1 === rowCount, selectedRows: insertItem(state.selectedRows, row), toggleOnSelectedRowsChange }); } case 'SELECT_MULTIPLE_ROWS': { const { keyField, selectedRows, totalRows, mergeSelections } = action; if (mergeSelections) { const selections = [ ...state.selectedRows, ...selectedRows.filter(row => !isRowSelected(row, state.selectedRows, keyField)), ]; return Object.assign(Object.assign({}, state), { selectedCount: selections.length, allSelected: false, selectedRows: selections, toggleOnSelectedRowsChange }); } return Object.assign(Object.assign({}, state), { selectedCount: selectedRows.length, allSelected: selectedRows.length === totalRows, selectedRows, toggleOnSelectedRowsChange }); } case 'CLEAR_SELECTED_ROWS': { const { selectedRowsFlag } = action; return Object.assign(Object.assign({}, state), { allSelected: false, selectedCount: 0, selectedRows: [], selectedRowsFlag }); } case 'SORT_CHANGE': { const { sortDirection, selectedColumn, clearSelectedOnSort } = action; return Object.assign(Object.assign(Object.assign({}, state), { selectedColumn, sortDirection, currentPage: 1 }), (clearSelectedOnSort && { allSelected: false, selectedCount: 0, selectedRows: [], toggleOnSelectedRowsChange, })); } case 'CHANGE_PAGE': { const { page, paginationServer, visibleOnly, persistSelectedOnPageChange } = action; const mergeSelections = paginationServer && persistSelectedOnPageChange; const clearSelectedOnPage = (paginationServer && !persistSelectedOnPageChange) || visibleOnly; return Object.assign(Object.assign(Object.assign(Object.assign({}, state), { currentPage: page }), (mergeSelections && { allSelected: false, })), (clearSelectedOnPage && { allSelected: false, selectedCount: 0, selectedRows: [], toggleOnSelectedRowsChange, })); } case 'CHANGE_ROWS_PER_PAGE': { const { rowsPerPage, page } = action; return Object.assign(Object.assign({}, state), { currentPage: page, rowsPerPage }); } } } const disabledCSS = css ` pointer-events: none; opacity: 0.4; `; const TableStyle = styled.div ` position: relative; box-sizing: border-box; display: flex; flex-direction: column; width: 100%; height: 100%; max-width: 100%; ${({ disabled }) => disabled && disabledCSS}; ${({ theme }) => theme.table.style}; `; const fixedCSS = css ` position: sticky; position: -webkit-sticky; /* Safari */ top: 0; z-index: 1; `; const Head = styled.div ` display: flex; width: 100%; ${({ $fixedHeader }) => $fixedHeader && fixedCSS}; ${({ theme }) => theme.head.style}; `; const HeadRow = styled.div ` display: flex; align-items: stretch; width: 100%; ${({ theme }) => theme.headRow.style}; ${({ $dense, theme }) => $dense && theme.headRow.denseStyle}; `; const SMALL = 599; const MEDIUM = 959; const LARGE = 1280; const media = { sm: (literals, ...args) => css ` @media screen and (max-width: ${SMALL}px) { ${css(literals, ...args)} } `, md: (literals, ...args) => css ` @media screen and (max-width: ${MEDIUM}px) { ${css(literals, ...args)} } `, lg: (literals, ...args) => css ` @media screen and (max-width: ${LARGE}px) { ${css(literals, ...args)} } `, custom: (value) => (literals, ...args) => css ` @media screen and (max-width: ${value}px) { ${css(literals, ...args)} } `, }; const CellBase = styled.div ` position: relative; display: flex; align-items: center; box-sizing: border-box; line-height: normal; ${({ theme, $headCell }) => theme[$headCell ? 'headCells' : 'cells'].style}; ${({ $noPadding }) => $noPadding && 'padding: 0'}; `; const CellExtended = styled(CellBase) ` flex-grow: ${({ button, grow }) => (grow === 0 || button ? 0 : grow || 1)}; flex-shrink: 0; flex-basis: 0; max-width: ${({ maxWidth }) => maxWidth || '100%'}; min-width: ${({ minWidth }) => minWidth || '110px'}; ${({ width }) => width && css ` min-width: ${width}; max-width: ${width}; `}; ${({ right }) => right && 'justify-content: flex-end'}; ${({ button, center }) => (center || button) && 'justify-content: center'}; ${({ compact, button }) => (compact || button) && 'padding: 0'}; /* handle hiding cells */ ${({ hide }) => hide && hide === 'sm' && media.sm ` display: none; `}; ${({ hide }) => hide && hide === 'md' && media.md ` display: none; `}; ${({ hide }) => hide && hide === 'lg' && media.lg ` display: none; `}; ${({ hide }) => hide && Number.isInteger(hide) && media.custom(hide) ` display: none; `}; `; const overflowCSS = css ` div:first-child { white-space: ${({ $wrapCell }) => ($wrapCell ? 'normal' : 'nowrap')}; overflow: ${({ $allowOverflow }) => ($allowOverflow ? 'visible' : 'hidden')}; text-overflow: ellipsis; } `; const CellStyle = styled(CellExtended).attrs(props => ({ style: props.style, })) ` ${({ $renderAsCell }) => !$renderAsCell && overflowCSS}; ${({ theme, $isDragging }) => $isDragging && theme.cells.draggingStyle}; ${({ $cellStyle }) => $cellStyle}; `; function Cell({ id, column, row, rowIndex, dataTag, isDragging, onDragStart, onDragOver, onDragEnd, onDragEnter, onDragLeave, }) { const { conditionalStyle, classNames } = getConditionalStyle(row, column.conditionalCellStyles, ['rdt_TableCell']); const [showTooltip, setShowTooltip] = React.useState(false); const cellContentRef = React.useRef(null); React.useEffect(() => { if (cellContentRef.current) { setShowTooltip(cellContentRef.current.scrollWidth > cellContentRef.current.clientWidth); } }, []); return (React.createElement(CellStyle, { id: id, "data-column-id": column.id, role: "cell", className: classNames, "data-tag": dataTag, "$cellStyle": column.style, "$renderAsCell": !!column.cell, "$allowOverflow": column.allowOverflow, button: column.button, center: column.center, compact: column.compact, grow: column.grow, hide: column.hide, maxWidth: column.maxWidth, minWidth: column.minWidth, right: column.right, width: column.width, "$wrapCell": column.wrap, style: conditionalStyle, "$isDragging": isDragging, onDragStart: onDragStart, onDragOver: onDragOver, onDragEnd: onDragEnd, onDragEnter: onDragEnter, onDragLeave: onDragLeave }, !column.cell && (React.createElement("div", { ref: cellContentRef, "data-tag": dataTag, title: showTooltip ? String(getProperty(row, column.selector, column.format, rowIndex)) : undefined }, getProperty(row, column.selector, column.format, rowIndex))), column.cell && column.cell(row, rowIndex, column, id))); } var TableCell = React.memo(Cell); const defaultComponentName = 'input'; const calculateBaseStyle = (disabled) => (Object.assign(Object.assign({ fontSize: '18px' }, (!disabled && { cursor: 'pointer' })), { padding: 0, marginTop: '1px', verticalAlign: 'middle', position: 'relative' })); function Checkbox({ name, component = defaultComponentName, componentOptions = { style: {} }, indeterminate = false, checked = false, disabled = false, onClick = noop, }) { const setCheckboxRef = (checkbox) => { if (checkbox) { checkbox.indeterminate = indeterminate; } }; const TagName = component; const baseStyle = TagName !== defaultComponentName ? componentOptions.style : calculateBaseStyle(disabled); const resolvedComponentOptions = React.useMemo(() => handleFunctionProps(componentOptions, indeterminate), [componentOptions, indeterminate]); return (React.createElement(TagName, Object.assign({ type: "checkbox", ref: setCheckboxRef, style: baseStyle, onClick: disabled ? noop : onClick, name: name, "aria-label": name, checked: checked, disabled: disabled }, resolvedComponentOptions, { onChange: noop }))); } var Checkbox$1 = React.memo(Checkbox); const TableCellCheckboxStyle = styled(CellBase) ` flex: 0 0 48px; min-width: 48px; justify-content: center; align-items: center; user-select: none; white-space: nowrap; `; function TableCellCheckbox({ name, keyField, row, rowCount, selected, selectableRowsComponent, selectableRowsComponentProps, selectableRowsSingle, selectableRowDisabled, onSelectedRow, }) { const disabled = !!(selectableRowDisabled && selectableRowDisabled(row)); const handleOnRowSelected = () => { onSelectedRow({ type: 'SELECT_SINGLE_ROW', row, isSelected: selected, keyField, rowCount, singleSelect: selectableRowsSingle, }); }; return (React.createElement(TableCellCheckboxStyle, { onClick: (e) => e.stopPropagation(), className: "rdt_TableCell", "$noPadding": true }, React.createElement(Checkbox$1, { name: name, component: selectableRowsComponent, componentOptions: selectableRowsComponentProps, checked: selected, "aria-checked": selected, onClick: handleOnRowSelected, disabled: disabled }))); } const ButtonStyle = styled.button ` display: inline-flex; align-items: center; user-select: none; white-space: nowrap; border: none; background-color: transparent; ${({ theme }) => theme.expanderButton.style}; `; function ExpanderButton({ disabled = false, expanded = false, expandableIcon, id, row, onToggled, }) { const icon = expanded ? expandableIcon.expanded : expandableIcon.collapsed; const handleToggle = () => onToggled && onToggled(row); return (React.createElement(ButtonStyle, { "aria-disabled": disabled, onClick: handleToggle, "data-testid": `expander-button-${id}`, disabled: disabled, "aria-label": expanded ? 'Collapse Row' : 'Expand Row', role: "button", type: "button" }, icon)); } const CellExpanderStyle = styled(CellBase) ` white-space: nowrap; font-weight: 400; min-width: 48px; ${({ theme }) => theme.expanderCell.style}; `; function CellExpander({ row, expanded = false, expandableIcon, id, onToggled, disabled = false, }) { return (React.createElement(CellExpanderStyle, { onClick: (e) => e.stopPropagation(), "$noPadding": true }, React.createElement(ExpanderButton, { id: id, row: row, expanded: expanded, expandableIcon: expandableIcon, disabled: disabled, onToggled: onToggled }))); } const ExpanderRowStyle = styled.div ` width: 100%; box-sizing: border-box; ${({ theme }) => theme.expanderRow.style}; ${({ $extendedRowStyle }) => $extendedRowStyle}; `; function ExpanderRow({ data, ExpanderComponent, expanderComponentProps, extendedRowStyle, extendedClassNames, }) { const classNamesSplit = extendedClassNames.split(' ').filter(c => c !== 'rdt_TableRow'); const classNames = ['rdt_ExpanderRow', ...classNamesSplit].join(' '); return (React.createElement(ExpanderRowStyle, { className: classNames, "$extendedRowStyle": extendedRowStyle }, React.createElement(ExpanderComponent, Object.assign({ data: data }, expanderComponentProps)))); } var ExpanderRow$1 = React.memo(ExpanderRow); const STOP_PROP_TAG = 'allowRowEvents'; var Direction; (function (Direction) { Direction["LTR"] = "ltr"; Direction["RTL"] = "rtl"; Direction["AUTO"] = "auto"; })(Direction || (Direction = {})); var Alignment; (function (Alignment) { Alignment["LEFT"] = "left"; Alignment["RIGHT"] = "right"; Alignment["CENTER"] = "center"; })(Alignment || (Alignment = {})); var Media; (function (Media) { Media["SM"] = "sm"; Media["MD"] = "md"; Media["LG"] = "lg"; })(Media || (Media = {})); const highlightCSS = css ` &:hover { ${({ $highlightOnHover, theme }) => $highlightOnHover && theme.rows.highlightOnHoverStyle}; } `; const pointerCSS = css ` &:hover { cursor: pointer; } `; const TableRowStyle = styled.div.attrs(props => ({ style: props.style, })) ` display: flex; align-items: stretch; align-content: stretch; width: 100%; box-sizing: border-box; ${({ theme }) => theme.rows.style}; ${({ $dense, theme }) => $dense && theme.rows.denseStyle}; ${({ $striped, theme }) => $striped && theme.rows.stripedStyle}; ${({ $highlightOnHover }) => $highlightOnHover && highlightCSS}; ${({ $pointerOnHover }) => $pointerOnHover && pointerCSS}; ${({ $selected, theme }) => $selected && theme.rows.selectedHighlightStyle}; ${({ $conditionalStyle }) => $conditionalStyle}; ${({ $highlighted, theme }) => { var _a; return $highlighted && css ` background-color: ${((_a = theme.rows.highlightStyle) === null || _a === void 0 ? void 0 : _a.backgroundColor) || '#e3f2fd'}; `; }} `; function Row({ columns = [], conditionalRowStyles = [], defaultExpanded = false, defaultExpanderDisabled = false, dense = false, expandableIcon, expandableRows = false, expandableRowsComponent, expandableRowsComponentProps, expandableRowsHideExpander, expandOnRowClicked = false, expandOnRowDoubleClicked = false, highlightOnHover = false, id, expandableInheritConditionalStyles, keyField, onRowClicked = noop, onRowDoubleClicked = noop, onRowMouseEnter = noop, onRowMouseLeave = noop, onRowExpandToggled = noop, onSelectedRow = noop, pointerOnHover = false, row, rowCount, rowIndex, selectableRowDisabled = null, selectableRows = false, selectableRowsComponent, selectableRowsComponentProps, selectableRowsHighlight = false, selectableRowsSingle = false, selected, striped = false, draggingColumnId, onDragStart, onDragOver, onDragEnd, onDragEnter, onDragLeave, selectRowFunction, highlighted = false, }) { const [expanded, setExpanded] = React.useState(defaultExpanded); React.useEffect(() => { setExpanded(defaultExpanded); }, [defaultExpanded]); const handleExpanded = React.useCallback(() => { setExpanded(!expanded); onRowExpandToggled(!expanded, row); }, [expanded, onRowExpandToggled, row]); const showPointer = pointerOnHover || (expandableRows && (expandOnRowClicked || expandOnRowDoubleClicked)); const handleRowClick = React.useCallback((e) => { const target = e.target; if (target.getAttribute('data-tag') === STOP_PROP_TAG) { onRowClicked(row, e); if (!defaultExpanderDisabled && expandableRows && expandOnRowClicked) { handleExpanded(); } if (selectRowFunction) { selectRowFunction(row, e); } } }, [defaultExpanderDisabled, expandOnRowClicked, expandableRows, handleExpanded, onRowClicked, row, selectRowFunction]); const handleRowDoubleClick = React.useCallback((e) => { const target = e.target; if (target.getAttribute('data-tag') === STOP_PROP_TAG) { onRowDoubleClicked(row, e); if (!defaultExpanderDisabled && expandableRows && expandOnRowDoubleClicked) { handleExpanded(); } } }, [defaultExpanderDisabled, expandOnRowDoubleClicked, expandableRows, handleExpanded, onRowDoubleClicked, row]); const handleRowMouseEnter = React.useCallback((e) => { onRowMouseEnter(row, e); }, [onRowMouseEnter, row]); const handleRowMouseLeave = React.useCallback((e) => { onRowMouseLeave(row, e); }, [onRowMouseLeave, row]); const rowKeyField = prop(row, keyField); const { conditionalStyle, classNames } = getConditionalStyle(row, conditionalRowStyles, ['rdt_TableRow']); const highlightSelected = selectableRowsHighlight && selected; const inheritStyles = expandableInheritConditionalStyles ? conditionalStyle : {}; const isStriped = striped && isOdd(rowIndex); React.useEffect(() => { if (highlighted) { console.log('highlighted', highlighted); } }, [highlighted]); return (React.createElement(React.Fragment, null, React.createElement(TableRowStyle, { id: `row-${id}`, role: "row", "$striped": isStriped, "$highlightOnHover": highlightOnHover, "$pointerOnHover": !defaultExpanderDisabled && showPointer, "$dense": dense, onClick: handleRowClick, onDoubleClick: handleRowDoubleClick, onMouseEnter: handleRowMouseEnter, onMouseLeave: handleRowMouseLeave, className: classNames, "$selected": highlightSelected, "$conditionalStyle": conditionalStyle, "$highlighted": highlighted }, selectableRows && (React.createElement(TableCellCheckbox, { name: `select-row-${rowKeyField}`, keyField: keyField, row: row, rowCount: rowCount, selected: selected, selectableRowsComponent: selectableRowsComponent, selectableRowsComponentProps: selectableRowsComponentProps, selectableRowDisabled: selectableRowDisabled, selectableRowsSingle: selectableRowsSingle, onSelectedRow: onSelectedRow })), expandableRows && !expandableRowsHideExpander && (React.createElement(CellExpander, { id: rowKeyField, expandableIcon: expandableIcon, expanded: expanded, row: row, onToggled: handleExpanded, disabled: defaultExpanderDisabled })), columns.map((column, index) => { if (column.omit) { return null; } if (column.isHidden) return null; return (React.createElement(TableCell, { id: `cell-${column.id}-${rowKeyField}`, key: `cell-${column.id}-${rowKeyField}`, dataTag: column.ignoreRowClick || column.button ? null : STOP_PROP_TAG, column: column, row: row, rowIndex: rowIndex, isDragging: equalizeId(draggingColumnId, column.id), onDragStart: onDragStart, onDragOver: onDragOver, onDragEnd: onDragEnd, onDragEnter: onDragEnter, onDragLeave: onDragLeave, "data-last-cell": index === columns.length - 1 })); })), expandableRows && expanded && (React.createElement(ExpanderRow$1, { key: `expander-${rowKeyField}`, data: row, extendedRowStyle: inheritStyles, extendedClassNames: classNames, ExpanderComponent: expandableRowsComponent, expanderComponentProps: expandableRowsComponentProps })))); } const Icon = styled.span ` padding: 2px; color: inherit; flex-grow: 0; flex-shrink: 0; ${({ $sortActive }) => ($sortActive ? 'opacity: 1' : 'opacity: 0')}; ${({ $sortDirection }) => $sortDirection === 'desc' && 'transform: rotate(180deg)'}; `; const NativeSortIcon = ({ sortActive, sortDirection }) => (React__default.createElement(Icon, { "$sortActive": sortActive, "$sortDirection": sortDirection }, "\u25B2")); const ColumnStyled = styled(CellExtended) ` ${({ button }) => button && 'text-align: center'}; ${({ theme, $isDragging }) => $isDragging && theme.headCells.draggingStyle}; `; const sortableCSS = css ` span.__rdt_custom_sort_icon__ { i, svg { transform: 'translate3d(0, 0, 0)'; ${({ $sortActive }) => ($sortActive ? 'opacity: 1' : 'opacity: 0')}; color: inherit; font-size: 10px; height: 10px; width: 10px; backface-visibility: hidden; transform-style: preserve-3d; transition-duration: 95ms; transition-property: transform; } &.asc i, &.asc svg { transform: rotate(180deg); } } ${({ $sortActive }) => !$sortActive && css ` &:hover, &:focus { opacity: 0.7; span, span.__rdt_custom_sort_icon__ * { opacity: 0.7; } } `}; `; const ColumnSortable = styled.div ` display: inline-flex; align-items: center; justify-content: inherit; height: 100%; width: 100%; outline: none; user-select: none; overflow: hidden; ${({ disabled }) => !disabled && sortableCSS}; `; const ColumnText = styled.div ` overflow: hidden; white-space: nowrap; text-overflow: ellipsis; `; const IconBtn = styled.button ` background-color: transparent; border: none; cursor: pointer; margin-left: 5px; margin-right: 5px; `; function TableCol({ column, disabled, draggingColumnId, selectedColumn = { identifier: '', isHidden: undefined }, sortDirection, sortIcon, sortServer, pagination, paginationServer, persistSelectedOnSort, selectableRowsVisibleOnly, onSort, onDragStart, onDragOver, onDragEnd, onDragEnter, onDragLeave, showFilter, showFilterList }) { React.useEffect(() => { if (typeof column.selector === 'string') { console.error(`Warning: ${column.selector} is a string based column selector which has been deprecated as of v7 and will be removed in v8. Instead, use a selector function e.g. row => row[field]...`); } }, []); const [showTooltip, setShowTooltip] = React.useState(false); const columnRef = React.useRef(null); React.useEffect(() => { if (columnRef.current) { setShowTooltip(columnRef.current.scrollWidth > columnRef.current.clientWidth); } }, [showTooltip]); if (column.omit) { return null; } const handleSortChange = () => { if (!column.sortable && !column.selector) { return; } let direction = sortDirection; if (equalizeId(selectedColumn.id, column.id)) { direction = sortDirection === SortOrder.DESC ? SortOrder.ASC : SortOrder.DESC; } onSort({ type: 'SORT_CHANGE', sortDirection: direction, selectedColumn: column, clearSelectedOnSort: (pagination && paginationServer && !persistSelectedOnSort) || sortServer || selectableRowsVisibleOnly, }); }; const handleKeyPress = (event) => { if (event.key === 'Enter') { handleSortChange(); } }; const renderNativeSortIcon = (sortActive, onClick) => (React.createElement(IconBtn, { onClick: onClick }, React.createElement(NativeSortIcon, { sortActive: sortActive, sortDirection: sortDirection }))); const renderCustomSortIcon = (onClick) => (React.createElement(IconBtn, { onClick: onClick, style: { margin: '1px' } }, React.createElement("span", { className: [sortDirection, '__rdt_custom_sort_icon__'].join(' ') }, sortIcon))); const sortActive = !!(column.sortable && equalizeId(selectedColumn.id, column.id)); const disableSort = !column.sortable || disabled; const nativeSortIconLeft = column.sortable && !sortIcon && !column.right; const nativeSortIconRight = column.sortable && !sortIcon && column.right; const customSortIconLeft = column.sortable && sortIcon && !column.right; const customSortIconRight = column.sortable && sortIcon && column.right; return (React.createElement(ColumnStyled, { "data-column-id": column.id, className: "rdt_TableCol", "$headCell": true, allowOverflow: column.allowOverflow, button: column.button, compact: column.compact, grow: column.grow, hide: column.hide, maxWidth: column.maxWidth, minWidth: column.minWidth, right: column.right, center: column.center, width: column.width, draggable: column.reorder, "$isDragging": equalizeId(column.id, draggingColumnId), onDragStart: onDragStart, onDragOver: onDragOver, onDragEnd: onDragEnd, onDragEnter: onDragEnter, onDragLeave: onDragLeave }, column.name && (React.createElement(ColumnSortable, { "data-column-id": column.id, "data-sort-id": column.id, role: "columnheader", tabIndex: 0, className: "rdt_TableCol_Sortable", onKeyPress: !disableSort ? handleKeyPress : undefined, "$sortActive": !disableSort && sortActive, disabled: disableSort }, React.createElement("div", { style: { display: 'flex', justifyContent: "space-between", width: "100%" } }, !disableSort && customSortIconRight && renderCustomSortIcon(!disableSort ? handleSortChange : null), !disableSort && nativeSortIconRight && renderNativeSortIcon(sortActive, !disableSort ? handleSortChange : null), React.createElement("div", { style: { display: 'flex', justifyContent: "space-between" } }, typeof column.name === 'string' ? (React.createElement(ColumnText, { title: showTooltip ? column.name : undefined, ref: columnRef, "data-column-id": column.id }, column.name)) : (column.name), (showFilter && column.identifier && column.identifier != 'actions') && React.createElement(IconBtn, { onClick: (e) => { showFilterList(e, column.identifier); } }, React.createElement(IoFilter, null))), !disableSort && customSortIconLeft && renderCustomSortIcon(!disableSort ? handleSortChange : null), !disableSort && nativeSortIconLeft && renderNativeSortIcon(sortActive, !disableSort ? handleSortChange : null)))))); } var Column = React.memo(TableCol); const ColumnStyle = styled(CellBase) ` flex: 0 0 48px; justify-content: center; align-items: center; user-select: none; white-space: nowrap; font-size: unset; `; function ColumnCheckbox({ headCell = true, rowData, keyField, allSelected, mergeSelections, selectedRows, selectableRowsComponent, selectableRowsComponentProps, selectableRowDisabled, onSelectAllRows, }) { const indeterminate = selectedRows.length > 0 && !allSelected; const rows = selectableRowDisabled ? rowData.filter((row) => !selectableRowDisabled(row)) : rowData; const isDisabled = rows.length === 0; const rowCount = Math.min(rowData.length, rows.length); const handleSelectAll = () => { onSelectAllRows({ type: 'SELECT_ALL_ROWS', rows, rowCount, mergeSelections, keyField, }); }; return (React.createElement(ColumnStyle, { className: "rdt_TableCol", "$headCell": headCell, "$noPadding": true }, React.createElement(Checkbox$1, { name: "select-all-rows", component: selectableRowsComponent, componentOptions: selectableRowsComponentProps, onClick: handleSelectAll, checked: allSelected, indeterminate: indeterminate, disabled: isDisabled }))); } function useRTL(direction = Direction.AUTO) { const isClient = typeof window === 'object'; const [isRTL, setIsRTL] = React.useState(false); React.useEffect(() => { if (!isClient) { return; } if (direction === 'auto') { const canUse = !!(window.document && window.document.createElement); const bodyRTL = document.getElementsByTagName('BODY')[0]; const htmlTRL = document.getElementsByTagName('HTML')[0]; const hasRTL = bodyRTL.dir === 'rtl' || htmlTRL.dir === 'rtl'; setIsRTL(canUse && hasRTL); return; } setIsRTL(direction === 'rtl'); }, [direction, isClient]); return isRTL; } const Title$1 = styled.div ` display: flex; align-items: center; flex: 1 0 auto; height: 100%; color: ${({ theme }) => theme.contextMenu.fontColor}; font-size: ${({ theme }) => theme.contextMenu.fontSize}; font-weight: 400; `; const ContextActions = styled.div ` display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; `; const ContextMenuStyle = styled.div ` position: absolute; top: 0; left: 0; width: 100%; height: 100%; box-sizing: inherit; z-index: 1; align-items: center; justify-content: space-between; display: flex; ${({ $rtl }) => $rtl && 'direction: rtl'}; ${({ theme }) => theme.contextMenu.style}; ${({ theme, $visible }) => $visible && theme.contextMenu.activeStyle}; `; const generateDefaultContextTitle = (contextMessage, selectedCount, rtl) => { if (selectedCount === 0) { return null; } const datumName = selectedCount === 1 ? contextMessage.singular : contextMessage.plural; if (rtl) { return `${selectedCount} ${contextMessage.message || ''} ${datumName}`; } return `${selectedCount} ${datumName} ${contextMessage.message || ''}`; }; function ContextMenu({ contextMessage, contextActions, contextComponent, selectedCount, direction, }) { const isRTL = useRTL(direction); const visible = selectedCount > 0; if (contextComponent) { return (React.createElement(ContextMenuStyle, { "$visible": visible }, React.cloneElement(contextComponent, { selectedCount }))); } return (React.createElement(ContextMenuStyle, { "$visible": visible, "$rtl": isRTL }, React.createElement(Title$1, null, generateDefaultContextTitle(contextMessage, selectedCount, isRTL)), React.createElement(ContextActions, null, contextActions))); } const HeaderStyle = styled.div ` position: relative; box-sizing: border-box; overflow: hidden; display: flex; flex: 1 1 auto; align-items: center; justify-content: space-between; width: 100%; flex-wrap: wrap; ${({ theme }) => theme.header.style} `; const Title = styled.div ` flex: 1 0 auto; color: ${({ theme }) => theme.header.fontColor}; font-size: ${({ theme }) => theme.header.fontSize}; font-weight: 400; `; const Actions = styled.div ` flex: 1 0 auto; display: flex; align-items: center; justify-content: flex-end; > * { margin-left: 5px; } `; const Header = ({ title, actions = null, contextMessage, contextActions, contextComponent, selectedCount, direction, showMenu = true, }) => (React.createElement(HeaderStyle, { className: "rdt_TableHeader", role: "heading", "aria-level": 1 }, React.createElement(Title, null, title), actions && React.createElement(Actions, null, actions), showMenu && (React.createElement(ContextMenu, { contextMessage: contextMessage, contextActions: contextActions, contextComponent: contextComponent, direction: direction, selectedCount: selectedCount })))); /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; const alignMap = { left: 'flex-start', right: 'flex-end', center: 'center', }; const SubheaderWrapper = styled.header ` position: relative; display: flex; flex: 1 1 auto; box-sizing: border-box; align-items: center; padding: 4px 16px 4px 24px; width: 100%; justify-content: ${({ align }) => alignMap[align]}; flex-wrap: ${({ $wrapContent }) => ($wrapContent ? 'wrap' : 'nowrap')}; ${({ theme }) => theme.subHeader.style} `; const Subheader = (_a) => { var { align = 'right', wrapContent = true } = _a, rest = __rest(_a, ["align", "wrapContent"]); return (React.createElement(SubheaderWrapper, Object.assign({ align: align, "$wrapContent": wrapContent }, rest))); }; const Body = styled.div ` display: flex; flex-direction: column; `; const ResponsiveWrapper = styled.div ` position: relative; width: 100%; border-radius: inherit; ${({ $responsive, $fixedHeader }) => $responsive && css ` overflow-x: auto; // hidden prevents vertical scrolling in firefox when fixedHeader is disabled overflow-y: ${$fixedHeader ? 'auto' : 'hidden'}; min-height: 0; `}; ${({ $fixedHeader = false, $fixedHeaderScrollHeight = '100vh' }) => $fixedHeader && css ` max-height: ${$fixedHeaderScrollHeight}; -webkit-overflow-scrolling: touch; `}; ${({ theme }) => theme.responsiveWrapper.style}; `; const ProgressWrapper = styled.div ` position: relative; box-sizing: border-box; width: 100%; height: 100%; ${props => props.theme.progress.style}; `; const Wrapper = styled.div ` position: relative; width: 100%; ${({ theme }) => theme.tableWrapper.style}; `; const ColumnExpander = styled(CellBase) ` white-space: nowrap; ${({ theme }) => theme.expanderCell.style}; `; const NoDataWrapper = styled.div ` box-sizing: border-box; width: 100%; height: 100%; ${({ theme }) => theme.noData.style}; `; const DropdownIcon = () => (React__default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24" }, React__default.createElement("path", { d: "M7 10l5 5 5-5z" }), React__default.createElement("path", { d: "M0 0h24v24H0z", fill: "none" }))); const SelectControl = styled.select ` cursor: pointer; height: 24px; max-width: 100%; user-select: none; padding-left: 8px; padding-right: 24px; box-sizing: content-box; font-size: inherit; color: inherit; border: none; background-color: transparent; appearance: none; direction: ltr; flex-shrink: 0; &::-ms-expand { display: none; } &:disabled::-ms-expand { background: #f60; } option { color: initial; } `; const SelectWrapper = styled.div ` position: relative; flex-shrink: 0; font-size: inherit; color: inherit; margin-top: 1px; svg { top: 0; right: 0; color: inherit; position: absolute; fill: currentColor; width: 24px; height: 24px; display: inline-block; user-select: none; pointer-events: none; } `; const Select = (_a) => { var { defaultValue, onChange } = _a, rest = __rest(_a, ["defaultValue", "onChange"]); return (React.createElement(SelectWrapper, null, React.createElement(SelectControl, Object.assign({ onChange: onChange, defaultValue: defaultValue }, rest)), React.createElement(DropdownIcon, null))); }; const useWindowSize = () => { const isClient = typeof window === 'object'; function getSize() { return { width: isClient ? window.innerWidth : undefined, height: isClient ? window.innerHeight : undefined, }; } const [windowSize, setWindowSize] = React.useState(getSize); React.useEffect(() => { if (!isClient) { return () => null; } function handleResize() { setWindowSize(getSize()); } window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); return windowSize; }; const FirstPage = () => (React__default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", "aria-hidden": "true", role: "presentation" }, React__default.createElement("path", { d: "M18.41 16.59L13.82 12l4.59-4.59L17 6l-6 6 6 6zM6 6h2v12H6z" }), React__default.createElement("path", { fill: "none", d: "M24 24H0V0h24v24z" }))); const LastPage = () => (React__default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", "aria-hidden": "true", role: "presentation" }, React__default.createElement("path", { d: "M5.59 7.41L10.18 12l-4.59 4.59L7 18l6-6-6-6zM16 6h2v12h-2z" }), React__default.createElement("path", { fill: "none", d: "M0 0h24v24H0V0z" }))); const Left = () => (React__default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", "aria-hidden": "true", role: "presentation" }, React__default.createElement("path", { d: "M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z" }), React__default.createElement("path", { d: "M0 0h24v24H0z", fill: "none" }))); const Right = () => (React__default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", "aria-hidden": "true", role: "presentation" }, React__default.createElement("path", { d: "M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" }), React__default.createElement("path", { d: "M0 0h24v24H0z", fill: "none" }))); const ExpanderCollapsedIcon = () => (React__default.createElement("svg", { fill: "currentColor", height: "24", viewBox: "0 0 24 24", width: "24", xmlns: "http://www.w3.org/2000/svg" }, React__default.createElement("path", { d: "M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z" }), React__default.createElement("path", { d: "M0-.25h24v24H0z", fill: "none" }))); const ExpanderExpandedIcon = () => (React__default.createElement("svg", { fill: "currentColor", height: "24", viewBox: "0 0 24 24", width: "24", xmlns: "http://www.w3.org/2000/svg" }, React__default.createElement("path", { d: "M7.41 7.84L12 12.42l4.59-4.58L18 9.25l-6 6-6-6z" }), React__default.createElement("path", { d: "M0-.75h24v24H0z", fill: "none" }))); const defaultProps = { columns: [], data: [], title: '', keyField: 'id', selectableRows: false, selectableRowsHighlight: false, selectableRowsNoSelectAll: false, selectableRowSelected: null, selectableRowDisabled: null, selectableRowsComponent: 'input', selectableRowsComponentProps: {}, selectableRowsVisibleOnly: false, selectableRowsSingle: false, clearSelectedRows: false, expandableRows: false, expandableRowDisabled: null, expandableRowExpanded: null, expandOnRowClicked: false, expandableRowsHideExpander: false, expandOnRowDoubleClicked: false, expandableInheritConditionalStyles: false, expandableRowsComponent: function DefaultExpander() { return (React__default.createElement("div", null, "To add an expander pass in a component instance via ", React__default.createElement("strong", null, "expandableRowsComponent"), ". You can then access props.data from this component.")); }, expandableIcon: { collapsed: React__default.createElement(ExpanderCollapsedIcon, null), expanded: React__default.createElement(ExpanderExpandedIcon, null), }, expandableRowsComponentProps: {}, progressPending: false, progressComponent: React__default.createElement("div", { style: { fontSize: '24px', fontWeight: 700, padding: '24px' } }, "Loading..."), persistTableHead: false, sortIcon: null, sortFunction: null, sortServer: false, striped: false, highlightOnHover: false, pointerOnHover: false, noContextMenu: false, contextMessage: { singular: 'item', plural: 'items', message: 'selected' }, actions: null, contextActions: null, contextComponent: null, defaultSortFieldId: null, defaultSortAsc: true, responsive: true, noDataComponent: React__default.createElement("div", { style: { padding: '24px' } }, "There are no records to display"), disabled: false, noTableHead: false, noHeader: false, subHeader: false, subHeaderAlign: Alignment.RIGHT, subHeaderWrap: true, subHeaderComponent: null, fixedHeader: false, fixedHeaderScrollHeight: '100vh', pagination: false, paginationServer: false, paginationServerOptions: { persistSelectedOnSort: false, persistSelectedOnPageChange: false, }, paginationDefaultPage: 1, paginationResetDefaultPage: false, paginationTotalRows: 0, paginationPerPage: 10, paginationRowsPerPageOptions: [10, 15, 20, 25, 30], paginationComponent: null, paginationComponentOptions: {}, paginationIconFirstPage: React__default.createElement(FirstPage, null), paginationIconLastPage: React__default.createElement(LastPage, null), paginationIconNext: React__default.createElement(Right, null), paginationIconPrevious: React__default.createElement(Left, null), dense: false, conditionalRowStyles: [], theme: 'default', customStyles: {}, direction: Direction.AUTO, onChangePage: noop, onChangeRowsPerPage: noop, onRowClicked: noop, onRowDoubleClicked: noop, onRowMouseEnter: noop, onRowMouseLeave: noop, onRowExpandToggled: noop, onSelectedRowsChange: noop, onSort: noop, onColumnOrderChange: noop, }; const defaultComponentOptions = { rowsPerPageText: 'Rows per page:', rangeSeparatorText: 'of', noRowsPerPage: false, selectAllRowsItem: false, selectAllRowsItemText: 'All', }; const PaginationWrapper = styled.nav ` display: flex; flex: 1 1 auto; justify-content: flex-end; align-items: center; box-sizing: border-box; padding-right: 8px; padding-left: 8px; width: 100%; ${({ theme }) => theme.pagination.style}; `; const Button = styled.button ` position: relative; display: block; user-select: none; border: none; ${({ theme }) => theme.pagination.pageButtonsStyle}; ${({ $isRTL }) => $isRTL && 'transform: scale(-1, -1)'}; `; const PageList = styled.div ` display: flex; align-items: center; border-radius: 4px; white-space: nowrap; ${media.sm ` width: 100%; justify-content: space-around; `}; `; const Span = styled.span ` flex-shrink: 1; user-select: none; `; const Range = styled(Span) ` margin: 0 24px; `; const RowLabel = styled(Span) ` margin: 0 4px; `; function Pagination({ rowsPerPage, rowCount, currentPage, direction = defaultProps.direction, paginationRowsPerPageOptions = defaultProps.paginationRowsPerPageOptions, paginationIconLastPage = defaultProps.paginationIconLastPage, paginationIconFirstPage = defaultProps.paginationIconFirstPage, paginationIconNext = defaultProps.paginationIconNext, paginationIconPrevious = defaultProps.paginationIconPrevious, paginationComponentOptions = defaultProps.paginationComponentOptions, onChangeRowsPerPage = defaultProps.onChangeRowsPerPage, onChangePage = defaultProps.onChangePage, }) { const windowSize = useWindowSize(); const isRTL = useRTL(direction); const shouldShow = windowSize.width && windowSize.width > SMALL; const numPages = getNumberOfPages(rowCount, rowsPerPage); const lastIndex = currentPage * rowsPerPage; const firstIndex = lastIndex - rowsPerPage + 1; const disabledLesser = currentPage === 1; const disabledGreater = currentPage === numPages; const options = Object.assign(Object.assign({}, defaultComponentOptions), paginationComponentOptions); const range = currentPage === numPages ? `${firstIndex}-${rowCount} ${options.rangeSeparatorT