UNPKG

@wonderflow/react-components

Version:

UI components from Wonderflow's Wanda design system

162 lines (161 loc) 11.6 kB
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) }))] })); };