@wonderflow/react-components
Version:
UI components from Wonderflow's Wanda design system
162 lines (161 loc) • 11.6 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/*
* Copyright 2022-2023 Wonderflow Design Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useUpdateEffect } from 'ahooks';
import clsx from 'clsx';
import { AnimatePresence, domMax, LazyMotion, m, } from 'framer-motion';
import { Fragment, useCallback, useEffect, useMemo, } from 'react';
import { useExpanded, usePagination, useRowSelect, useSortBy, useTable, } from 'react-table';
import { useUIDSeed } from 'react-uid';
import { Skeleton, Stack, Text, ToggleButton, } from '../..';
import * as styles from './table.module.css';
import { TableCell } from './table-cell';
import { TableCheckbox } from './table-checkbox';
import { ToggleColumnsControl } from './table-controls';
import { TableExpand } from './table-expand';
import { TableHeader } from './table-header';
import { TablePagination } from './table-pagination';
import { TableRow } from './table-row';
export const Table = ({ className, style, columns, data = [], selectableRows, selectedRowIds = [], onSelectedRowsChange, stripes, showSeparators = true, title, actions, selectedActions, selectedLabel = selectedRowIds => `Selected items: ${selectedRowIds.length}`, showHeader = false, showTableHead = true, columnsControl = false, defaultHiddenColumns, height, loading, background, expandableRowComponent, emptyComponent, showPagination, isManualSorted, itemsPerPage = 10, totalRows, initialPageIndex = 0, onPaginationChange, onSortChange, pageClusters, initialSortBy = [], onRowExpandChange, selectSubRows = true, ...otherProps }) => {
const uid = useUIDSeed();
const hasSomeExpandableRows = useMemo(() => data.some(d => d.subRows), [data]);
const isManualPaginated = useMemo(() => Boolean(showPagination && onPaginationChange && totalRows), [showPagination, totalRows, onPaginationChange]);
const manualPaginationPageCount = useMemo(() => ((isManualPaginated && totalRows) ? Math.ceil(totalRows / itemsPerPage) : -1), [isManualPaginated, totalRows, itemsPerPage]);
const getHiddenColumns = useCallback(() => {
const hiddenColumns = defaultHiddenColumns ? [...defaultHiddenColumns] : [];
if (!selectableRows)
hiddenColumns.push('selection');
if (!hasSomeExpandableRows)
hiddenColumns.push('expander');
return hiddenColumns;
}, [defaultHiddenColumns, selectableRows, hasSomeExpandableRows]);
const getRowId = useCallback((originalRow, relativeIndex, parent) => originalRow?._id || (parent && [parent.id, relativeIndex].join('.')) || relativeIndex.toString(), []);
const computePageSize = useCallback(() => ((data.length > 0) ? data.length : 9999), [data]);
const { getTableProps, getTableBodyProps, headerGroups, rows, page, pageCount, gotoPage, allColumns, prepareRow, visibleColumns, setPageSize, setHiddenColumns, state: { pageSize, pageIndex, sortBy, selectedRowIds: selectedRowIdsState, }, } = useTable({
columns,
data,
getRowId,
expandSubRows: Boolean(!expandableRowComponent),
manualPagination: isManualPaginated,
pageCount: manualPaginationPageCount,
manualSortBy: isManualSorted,
disableMultiSort: true,
autoResetHiddenColumns: false,
autoResetPage: false,
autoResetSortBy: false,
selectSubRows,
/**
* This `paginateExpandedRows` prop prevent expanded rows to
* be placed in the next page. But it breaks row selection
* paginateExpandedRows: !showPagination,
*/
initialState: {
sortBy: initialSortBy,
pageIndex: initialPageIndex,
pageSize: showPagination ? itemsPerPage : computePageSize(),
hiddenColumns: getHiddenColumns(),
selectedRowIds: selectedRowIds.reduce((acc, curr) => ({
...acc,
[curr]: true,
}), {}),
},
}, useSortBy, useExpanded, usePagination, useRowSelect, (hooks) => {
const checkboxColumn = [{
id: 'selection',
isCollapsed: true,
isToggable: true,
Header: ({ getToggleAllPageRowsSelectedProps }) => (_jsx(TableCheckbox, { ...getToggleAllPageRowsSelectedProps() })),
Cell: ({ row }) => _jsx(TableCheckbox, { ...row.getToggleRowSelectedProps() }),
}];
const expanderColumn = [{
id: 'expander',
isToggable: true,
expander: true,
minWidth: 40,
align: 'center',
Cell: ({ row }) => (row.canExpand
? (_jsx(ToggleButton, { kind: "flat", dimension: "small", restingIcon: "chevron-right", pressedIcon: "chevron-down", ...row.getToggleRowExpandedProps(), pressed: row.isExpanded, onClick: () => {
const subRowsExpanded = row.subRows.filter(r => r.isExpanded);
subRowsExpanded.forEach(r => r.toggleRowExpanded(!r.isExpanded));
row.toggleRowExpanded(!row.isExpanded);
onRowExpandChange?.(row);
} }))
: null),
}];
hooks.visibleColumns.push(columns => [
...checkboxColumn,
...expanderColumn,
...columns,
]);
});
useUpdateEffect(() => {
const hiddenColumns = getHiddenColumns();
setHiddenColumns(hiddenColumns);
}, [
setHiddenColumns,
getHiddenColumns,
]);
useUpdateEffect(() => {
onSelectedRowsChange?.(Object.keys(selectedRowIdsState));
}, [
selectedRowIdsState,
onSelectedRowsChange,
]);
useUpdateEffect(() => {
onSortChange?.(sortBy);
}, [onSortChange, sortBy]);
useUpdateEffect(() => {
void onPaginationChange?.({ pageIndex, pageSize });
}, [onPaginationChange, pageIndex, pageSize]);
useEffect(() => {
if (pageIndex >= pageCount) {
gotoPage(0);
}
}, [pageCount, pageIndex, gotoPage]);
const rowEntries = useMemo(() => (showPagination ? page : rows), [page, rows, showPagination]);
const filteredVisibleColumns = useMemo(() => (visibleColumns.filter((col) => !col.isToggable)), [visibleColumns]);
const expandedRows = useMemo(() => rows.filter(row => row.canExpand && row.isExpanded).map(r => r.id), [rows]);
const dynamicStyle = {
'--table-height': height,
'--table-background': background,
};
return (_jsxs("div", { className: clsx(styles.Table, className), style: { ...dynamicStyle, ...style }, children: [_jsx(AnimatePresence, { children: _jsxs(LazyMotion, { features: domMax, children: [!!Object.keys(selectedRowIdsState).length && selectableRows && (_jsxs(Stack, { as: m.div, className: styles.Toast, direction: "row", hAlign: "space-between", vAlign: "center", hPadding: 16, vPadding: 8, fill: false, columnGap: 16, initial: { y: '-16px', opacity: 0 }, animate: {
y: 0,
opacity: 1,
transition: {
type: 'spring',
stiffness: 700,
damping: 30,
},
}, exit: { y: '-16px', opacity: 0 }, children: [_jsx(Text, { as: "span", variant: "body-1", children: _jsx("b", { children: selectedLabel(Object.keys(selectedRowIdsState)) }) }), selectedActions?.(Object.keys(selectedRowIdsState))] })), (showHeader || selectableRows) && (_jsx(m.div, { animate: {
y: Object.keys(selectedRowIdsState).length ? 20 : 0,
opacity: Object.keys(selectedRowIdsState).length ? 0 : 1,
transition: {
type: 'spring',
stiffness: 700,
damping: 30,
},
}, children: _jsxs(TableHeader, { title: title, children: [(columnsControl && data.length)
? (_jsx(ToggleColumnsControl, { columns: allColumns, visibleColumns: filteredVisibleColumns }))
: null, actions] }) }))] }) }), ((data.length || loading) && filteredVisibleColumns.length)
? (_jsx("div", { className: styles.TableWrapper, "data-table-scrolling": Boolean(height), children: _jsxs("table", { className: styles.TableElement, "data-table-stripes": stripes, "data-table-separators": showSeparators, "data-table-loading": loading, "aria-labelledby": uid('table-title'), ...getTableProps(), ...otherProps, children: [showTableHead && (_jsx("thead", { className: styles.THead, children: headerGroups.map(headerGroup => (_jsx(TableRow, { ...headerGroup.getHeaderGroupProps(), children: headerGroup.headers.map((column) => (_jsx(TableCell, { as: "th", width: column.minWidth === 0 ? undefined : column.minWidth, collapsed: column.isCollapsed, isSorted: column.isSorted, isSortedDesc: column.isSorted && column.isSortedDesc, align: column.align, ...column.getHeaderProps(column.getSortByToggleProps()), children: column.render('Header') }))) }))) })), _jsx("tbody", { ...getTableBodyProps(), children: loading
? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 100, children: _jsx(Skeleton, { gap: 16, height: 24, count: 10 }) }) }))
: rowEntries.map((row) => {
prepareRow(row);
return (_jsxs(Fragment, { children: [_jsx(TableRow, { ...row.getRowProps(), expanded: (row.isExpanded && !row.subRows.some(subrow => subrow.isExpanded && subrow.canExpand)), rowData: row, expandedRows: expandedRows, children: row.cells.map((cell) => (_jsx(TableCell, { collapsed: cell.column.isCollapsed, width: cell.column.minWidth === 0 ? undefined : cell.column.minWidth, align: cell.column.align, ...cell.getCellProps(), children: cell.render('Cell') }))) }), (row.subRows && row.isExpanded && expandableRowComponent) && row.subRows.map(subRow => (_jsx(TableRow, { "data-table-row-expander": true, children: _jsx(TableCell, { padding: false, colSpan: 100, children: _jsx(TableExpand, { data: subRow.original, component: expandableRowComponent }) }) }, subRow.id)))] }, row.id));
}) })] }) }))
: (_jsx(Stack, { vAlign: "center", hAlign: "center", children: emptyComponent ?? 'No data' })), (showPagination && filteredVisibleColumns.length > 0 && !!rows.length) && (_jsx(TablePagination, { clusters: pageClusters, pageSize: pageSize, totalItems: totalRows ?? rows.length, currentPage: pageIndex, totalPages: pageCount, isManual: Boolean(isManualPaginated && totalRows), onPageSizeChange: setPageSize, onPageClick: selected => gotoPage(selected) }))] }));
};