UNPKG

@yamada-ui/react

Version:

React UI components of the Yamada, by the Yamada, for the Yamada built with React and Emotion

560 lines (556 loc) • 24.1 kB
"use client"; const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.cjs'); const require_dom = require('../../utils/dom.cjs'); const require_effect = require('../../utils/effect.cjs'); const require_utils_index = require('../../utils/index.cjs'); const require_factory = require('../../core/system/factory.cjs'); const require_focus_ring = require('../../core/css/focus-ring.cjs'); const require_props = require('../../core/components/props.cjs'); const require_create_component = require('../../core/components/create-component.cjs'); const require_chevron_up_icon = require('../icon/icons/chevron-up-icon.cjs'); const require_chevrons_up_down_icon = require('../icon/icons/chevrons-up-down-icon.cjs'); const require_hooks_use_controllable_state_index = require('../../hooks/use-controllable-state/index.cjs'); const require_i18n_provider = require('../../providers/i18n-provider/i18n-provider.cjs'); const require_checkbox = require('../checkbox/checkbox.cjs'); const require_native_table = require('../native-table/native-table.cjs'); let react = require("react"); react = require_rolldown_runtime.__toESM(react); let react_jsx_runtime = require("react/jsx-runtime"); react_jsx_runtime = require_rolldown_runtime.__toESM(react_jsx_runtime); let __tanstack_react_table = require("@tanstack/react-table"); __tanstack_react_table = require_rolldown_runtime.__toESM(__tanstack_react_table); //#region src/components/table/table.tsx function getMergeHeaderGroups(headerGroups) { if (headerGroups.length <= 1) return headerGroups; const columnsIds = /* @__PURE__ */ new Set(); return headerGroups.map((headerGroup, depth, { length: fullDepth }) => { return { ...headerGroup, headers: headerGroup.headers.filter((header) => !columnsIds.has(header.column.id)).map((header) => { columnsIds.add(header.column.id); return header.isPlaceholder ? { ...header, isPlaceholder: false, rowSpan: fullDepth - depth } : { ...header, rowSpan: 1 }; }) }; }); } function getMergeFooterGroups(headerGroups) { if (headerGroups.length <= 1) return headerGroups; return headerGroups.map((headerGroup, depth) => { const nextHeaderGroups = headerGroups.slice(depth + 1); return { ...headerGroup, headers: headerGroup.headers.filter((header) => !header.isPlaceholder).map((header) => { if (nextHeaderGroups.length === 0) return header; const rowSpan = nextHeaderGroups.reduce((acc, nextHeaderGroup) => { return acc + (nextHeaderGroup.headers.some((nextHeader) => nextHeader.column.id === header.column.id) ? 1 : 0); }, 1); return { ...header, rowSpan }; }) }; }); } const { ComponentContext, PropsContext: TablePropsContext, useComponentContext, usePropsContext: useTablePropsContext, withContext } = require_create_component.createComponent("table"); /** * `Table` is a table component equipped with column sorting, row selection, and click event features. * * @see https://yamada-ui.com/docs/components/table */ const Table = withContext(({ colorScheme, size, variant, columnFilters: columnFiltersProp, columnResizeMode = "onChange", columns: columnsProp, data, defaultColumnFilters, defaultPagination = { pageIndex: 0, pageSize: 20 }, defaultRowSelection = {}, defaultSorting, enableAutoResizeTableWidth = false, enableColumnResizing = false, enableKeyboardNavigation = true, enablePagination = false, enableRowSelection = false, footer, header, highlightOnHover = !!enableRowSelection, highlightOnSelected = !!enableRowSelection, initialFocusableCell = { colIndex: 0, rowIndex: 0 }, layout, lineClamp, manualPagination, pagination: paginationProp, rowCount: totalRowCount, rowSelection: rowSelectionProp, selectOnClickRow = false, sortDescFirst = false, sorting: sortingProp, sortingIcon, state, stickyFooter, stickyHeader, striped, truncated, withBorder, withCheckbox = true, withColumnBorders, withFooterCheckbox = false, withFooterGroups = false, withScrollArea, cellProps, checkboxProps, footerGroupProps, footerProps, headerCheckboxProps, headerGroupProps, headerProps, resizableTriggerProps, rowCheckboxProps, rowProps, scrollAreaProps, sortingIconProps, tableProps, tbodyProps, tfootProps, theadProps, onColumnFiltersChange: onColumnFiltersChangeProp, onPaginationChange: onPaginationChangeProp, onRowClick, onRowDoubleClick, onRowSelectionChange: onRowSelectionChangeProp, onSortingChange: onSortingChangeProp,...rest }) => { const { t } = require_i18n_provider.useI18n("table"); const initialFocus = (0, react.useRef)(false); const ref = (0, react.useRef)(null); const focusedCell = (0, react.useRef)(null); const [rowSelection, onRowSelectionChange] = require_hooks_use_controllable_state_index.useControllableState({ defaultValue: defaultRowSelection, value: rowSelectionProp, onChange: onRowSelectionChangeProp }); const [sorting, onSortingChange] = require_hooks_use_controllable_state_index.useControllableState({ defaultValue: defaultSorting, value: sortingProp, onChange: onSortingChangeProp }); const [pagination, onPaginationChange] = require_hooks_use_controllable_state_index.useControllableState({ defaultValue: defaultPagination, value: paginationProp, onChange: onPaginationChangeProp }); const [columnFilters, onColumnFiltersChange] = require_hooks_use_controllable_state_index.useControllableState({ defaultValue: defaultColumnFilters, value: columnFiltersProp, onChange: onColumnFiltersChangeProp }); const table = (0, __tanstack_react_table.useReactTable)({ columnResizeMode, columns: (0, react.useMemo)(() => { if (!enableRowSelection || !withCheckbox) return columnsProp; const clonedColumns = [...columnsProp]; const header$1 = ({ header: header$2, table: table$1 }) => { return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_checkbox.Checkbox, { ...require_props.mergeProps({ checked: table$1.getIsAllRowsSelected(), indeterminate: table$1.getIsSomeRowsSelected(), indicatorProps: { outline: "none" }, inputProps: { "aria-label": t("Select all rows"), "data-focusable": "", tabIndex: -1 }, onChange: table$1.getToggleAllRowsSelectedHandler() }, checkboxProps ?? {}, (0, require_utils_index.utils_exports.runIfFn)(headerCheckboxProps, header$2) ?? {})() }); }; const cell = ({ row }) => { return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_checkbox.Checkbox, { ...require_props.mergeProps({ checked: row.getIsSelected(), disabled: !row.getCanSelect(), indeterminate: row.getIsSomeSelected(), indicatorProps: { outline: "none" }, inputProps: { "aria-label": t("Select row"), "data-focusable": "", tabIndex: -1 }, onChange: row.getToggleSelectedHandler() }, checkboxProps ?? {}, (0, require_utils_index.utils_exports.runIfFn)(rowCheckboxProps, row) ?? {})() }); }; clonedColumns.unshift({ id: "select", cell, header: header$1, ...withFooterCheckbox ? { footer: header$1 } : { footerProps: { "aria-hidden": "true" } }, cellProps: { verticalAlign: "middle" }, headerProps: { w: "calc({spaces.4} + {space-x} * 2)" } }); return clonedColumns; }, [ checkboxProps, columnsProp, enableRowSelection, headerCheckboxProps, rowCheckboxProps, t, withCheckbox, withFooterCheckbox ]), data, enableColumnResizing, enableRowSelection, getCoreRowModel: (0, __tanstack_react_table.getCoreRowModel)(), getFilteredRowModel: (0, __tanstack_react_table.getFilteredRowModel)(), getSortedRowModel: (0, __tanstack_react_table.getSortedRowModel)(), manualPagination, rowCount: totalRowCount, sortDescFirst, state: { columnFilters, pagination: enablePagination ? pagination : void 0, rowSelection, sorting, ...state }, onColumnFiltersChange, onPaginationChange, onRowSelectionChange, onSortingChange, ...enablePagination ? { getPaginationRowModel: (0, __tanstack_react_table.getPaginationRowModel)() } : {}, ...rest }); const headerGroups = table.getHeaderGroups(); const mergedHeaderGroups = getMergeHeaderGroups(headerGroups); const rows = table.getRowModel().rows; const footerGroups = table.getFooterGroups(); const mergedFooterGroups = getMergeFooterGroups(withFooterGroups ? footerGroups : []); const headerGroupCount = headerGroups.length; const rowCount = rows.length; const colCount = table.getAllLeafColumns().length; const maxColIndex = colCount - 1; const pageIndex = enablePagination ? table.getState().pagination.pageIndex : 0; const cellMap = (0, react.useMemo)(() => { const cellMap$1 = /* @__PURE__ */ new Map(); if (!enableKeyboardNavigation) return cellMap$1; const insertCellMap = (id, colSpan, rowSpan, colIndex, rowIndex) => { for (let i = 0; i < colSpan; i++) cellMap$1.set(`${colIndex + i}-${rowIndex}`, `${colIndex}-${rowIndex}`); for (let i = 1; i < rowSpan; i++) cellMap$1.set(`${colIndex}-${rowIndex + i}`, `${colIndex}-${rowIndex}`); }; const insertCellMapByHeaderGroup = (headerGroup, rowIndex) => { let placeholderCount = 0; headerGroup.headers.forEach((header$1) => { const colSpan = header$1.colSpan || 1; const rowSpan = header$1.rowSpan || 1; const colIndex = header$1.index + placeholderCount; placeholderCount += colSpan - 1; insertCellMap(header$1.id, colSpan, rowSpan, colIndex, rowIndex); }); }; mergedHeaderGroups.forEach((headerGroup, rowIndex) => { insertCellMapByHeaderGroup(headerGroup, rowIndex); }); rows.forEach((row, rowIndex) => { rowIndex += headerGroupCount; row.getVisibleCells().forEach((cell) => { const colIndex = cell.column.getIndex(); insertCellMap(cell.id, 1, 1, colIndex, rowIndex); }); }); mergedFooterGroups.forEach((footerGroup, rowIndex) => { rowIndex += headerGroupCount + rowCount; insertCellMapByHeaderGroup(footerGroup, rowIndex); }); return cellMap$1; }, [ enableKeyboardNavigation, headerGroupCount, mergedFooterGroups, mergedHeaderGroups, rows, rowCount ]); const context = (0, react.useMemo)(() => ({ columnResizeMode, sortingIcon, table, resizableTriggerProps, sortingIconProps }), [ columnResizeMode, sortingIcon, sortingIconProps, resizableTriggerProps, table ]); const getCell = (evOrEl) => { if (!evOrEl) return; const el = evOrEl instanceof HTMLElement ? evOrEl.closest("th, td") : "target" in evOrEl && evOrEl.target instanceof HTMLElement ? evOrEl.target.closest("th, td") : null; if (!(el instanceof HTMLTableCellElement)) return; const { colindex, rowindex } = el.dataset; const { colSpan, rowSpan } = el; if (!colindex || !rowindex) return; return { colIndex: parseInt(colindex), colSpan, el, rowIndex: parseInt(rowindex), rowSpan }; }; const getShouldFocusCell = (colIndex, rowIndex) => { const [trulyColIndex, trulyRowIndex] = cellMap.get(`${colIndex}-${rowIndex}`)?.split("-") ?? []; if (!trulyColIndex || !trulyRowIndex) return; const targetEl = ref.current?.querySelector(`[data-colindex="${trulyColIndex}"][data-rowindex="${trulyRowIndex}"]`); if (!targetEl || !(targetEl instanceof HTMLTableCellElement)) return; return targetEl; }; const removeTabIndex = (el) => { if (!el || !(el instanceof HTMLElement)) return; el.tabIndex = -1; el.querySelectorAll("[data-focusable]").forEach((el$1) => { if (el$1 instanceof HTMLElement) el$1.tabIndex = -1; }); }; const onCellFocus = (el, colIndex, rowIndex) => { const targetEl = getShouldFocusCell(colIndex, rowIndex); if (!targetEl) return; focusedCell.current = { colIndex, rowIndex }; removeTabIndex(el); const shouldFocusEl = targetEl.querySelector("[data-focusable]") ?? targetEl; if (shouldFocusEl instanceof HTMLElement) { shouldFocusEl.tabIndex = 0; shouldFocusEl.focus(); } }; const onFocus = (ev) => { if (initialFocus.current) return; initialFocus.current = true; const cell = getCell(ev); if (!cell) return; onCellFocus(cell.el, cell.colIndex, cell.rowIndex); }; const onKeyDown = (ev) => { if (!enableKeyboardNavigation) return; const cell = getCell(ev); if (!cell) return; require_dom.runKeyAction(ev, { ArrowDown: () => onCellFocus(cell.el, cell.colIndex, cell.rowIndex + cell.rowSpan), ArrowLeft: () => onCellFocus(cell.el, cell.colIndex - 1, cell.rowIndex), ArrowRight: () => onCellFocus(cell.el, cell.colIndex + cell.colSpan, cell.rowIndex), ArrowUp: () => onCellFocus(cell.el, cell.colIndex, cell.rowIndex - 1), End: () => onCellFocus(cell.el, maxColIndex, cell.rowIndex), Home: () => onCellFocus(cell.el, 0, cell.rowIndex), ...enablePagination ? { PageDown: () => { if (!table.getCanNextPage()) return; table.setPageIndex(pageIndex + 1); }, PageUp: () => { if (!table.getCanPreviousPage()) return; table.setPageIndex(pageIndex - 1); } } : {} }); }; const getTabIndex = (colIndex, rowIndex) => { if (!enableKeyboardNavigation) return void 0; return colIndex === initialFocusableCell.colIndex && rowIndex === initialFocusableCell.rowIndex ? 0 : void 0; }; require_effect.useUpdateEffect(() => { if (!enableKeyboardNavigation) return; const { colIndex, rowIndex } = focusedCell.current ?? initialFocusableCell; const targetEl = getShouldFocusCell(colIndex, rowIndex); if (targetEl) targetEl.tabIndex = 0; }, [pageIndex, enableKeyboardNavigation]); return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ComponentContext, { value: context, children: [ header ? (0, require_utils_index.utils_exports.runIfFn)(header, table) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_native_table.NativeTableRoot, { ...require_props.mergeProps({ ref, style: enableColumnResizing && enableAutoResizeTableWidth ? { width: table.getCenterTotalSize() } : {}, colorScheme, size, variant, "aria-colcount": colCount, "aria-multiselectable": enableRowSelection ? "true" : void 0, "aria-rowcount": totalRowCount || data.length, highlightOnHover, highlightOnSelected, layout, role: "grid", stickyFooter, stickyHeader, striped, withBorder, withColumnBorders, withScrollArea, scrollAreaProps, onFocus, onKeyDown }, tableProps ?? {})(), children: [ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_native_table.Thead, { role: "rowgroup", ...theadProps, children: mergedHeaderGroups.map((headerGroup, rowIndex) => { let placeholderCount = 0; return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_native_table.Tr, { "aria-rowindex": rowIndex + 1, role: "row", ...(0, require_utils_index.utils_exports.runIfFn)(headerGroupProps, headerGroup), children: headerGroup.headers.map((header$1) => { const { columnDef } = header$1.column; const colIndex = header$1.index + placeholderCount; const tabIndex = getTabIndex(colIndex, rowIndex); const canSort = header$1.column.getCanSort(); const sorted = header$1.column.getIsSorted(); const canResize = header$1.column.getCanResize(); const resizing = header$1.column.getIsResizing(); const children = header$1.isPlaceholder ? null : (0, __tanstack_react_table.flexRender)(header$1.column.columnDef.header, header$1.getContext()); placeholderCount += (header$1.colSpan || 1) - 1; return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_native_table.Th, { "aria-colindex": colIndex + 1, "aria-rowindex": rowIndex + 1, "aria-sort": sorted ? sorted === "asc" ? "ascending" : "descending" : "none", "data-colindex": colIndex, "data-rowindex": rowIndex, colSpan: header$1.colSpan || void 0, numeric: columnDef.numeric, pe: canSort ? "calc((1rem * {lineHeights.moderate}) + {space-x})" : void 0, position: "relative", role: "columnheader", rowSpan: header$1.rowSpan || void 0, tabIndex, ...require_props.mergeProps({ css: { "&:has([data-focusable]:focus-visible)": require_focus_ring.focusRingStyle.outline }, style: enableColumnResizing ? { width: header$1.getSize() } : {} }, (0, require_utils_index.utils_exports.runIfFn)(headerProps, header$1) ?? {}, columnDef.headerProps ?? {})(), children: [ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TruncatedText, { lineClamp: columnDef.lineClamp ?? lineClamp, truncated: columnDef.truncated ?? truncated, children }), canSort ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SortingIcon, { sorted, onClick: header$1.column.getToggleSortingHandler() }) : null, canResize ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResizableTrigger, { resizing, onDoubleClick: header$1.column.resetSize, onMouseDown: header$1.getResizeHandler(), onTouchStart: header$1.getResizeHandler() }) : null ] }, header$1.id); }) }, headerGroup.id); }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_native_table.Tbody, { role: "rowgroup", ...tbodyProps, children: rows.map((row, rowIndex) => { rowIndex += headerGroupCount; const selected = !!rowSelection[row.id]; const disabled = (0, require_utils_index.utils_exports.isFunction)(enableRowSelection) && !enableRowSelection(row); return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_native_table.Tr, { id: row.id, "aria-disabled": (0, require_utils_index.utils_exports.ariaAttr)(disabled), "aria-rowindex": rowIndex + 1, "aria-selected": (0, require_utils_index.utils_exports.ariaAttr)(selected), "data-disabled": (0, require_utils_index.utils_exports.dataAttr)(disabled), "data-selected": (0, require_utils_index.utils_exports.dataAttr)(selected), role: "row", ...require_props.mergeProps({ onClick: !disabled && selectOnClickRow ? () => row.toggleSelected(!selected) : void 0 }, { onClick: !disabled ? () => onRowClick?.(row) : void 0, onDoubleClick: !disabled ? () => onRowDoubleClick?.(row) : void 0 }, (0, require_utils_index.utils_exports.runIfFn)(rowProps, row) ?? {})(), children: row.getVisibleCells().map((cell) => { const { columnDef } = cell.column; const colIndex = cell.column.getIndex(); const tabIndex = getTabIndex(colIndex, rowIndex); const children = (0, __tanstack_react_table.flexRender)(cell.column.columnDef.cell, cell.getContext()); return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_native_table.Td, { "aria-colindex": colIndex + 1, "aria-rowindex": rowIndex + 1, "data-colindex": colIndex, "data-rowindex": rowIndex, numeric: columnDef.numeric, role: "gridcell", tabIndex, ...require_props.mergeProps({ css: { "&:has([data-focusable]:focus-visible)": require_focus_ring.focusRingStyle.outline } }, (0, require_utils_index.utils_exports.runIfFn)(cellProps, cell) ?? {}, columnDef.cellProps ?? {})(), children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TruncatedText, { lineClamp: columnDef.lineClamp ?? lineClamp, truncated: columnDef.truncated ?? truncated, children }) }, cell.id); }) }, row.id); }) }), withFooterGroups ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_native_table.Tfoot, { role: "rowgroup", ...tfootProps, children: mergedFooterGroups.map((footerGroup, rowIndex) => { rowIndex += headerGroupCount + rowCount; let placeholderCount = 0; return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_native_table.Tr, { "aria-rowindex": rowIndex + 1, role: "row", ...(0, require_utils_index.utils_exports.runIfFn)(footerGroupProps, footerGroup), children: footerGroup.headers.map((header$1) => { const { columnDef } = header$1.column; const colIndex = header$1.index + placeholderCount; const tabIndex = getTabIndex(colIndex, rowIndex); const children = header$1.isPlaceholder ? null : (0, __tanstack_react_table.flexRender)(header$1.column.columnDef.footer, header$1.getContext()); placeholderCount += (header$1.colSpan || 1) - 1; return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_native_table.Td, { "aria-colindex": colIndex + 1, "aria-rowindex": rowIndex + 1, "data-colindex": colIndex, "data-rowindex": rowIndex, colSpan: header$1.colSpan || void 0, numeric: columnDef.numeric, role: "gridcell", rowSpan: header$1.rowSpan || void 0, tabIndex, ...require_props.mergeProps({ css: { "&:has([data-focusable]:focus-visible)": require_focus_ring.focusRingStyle.outline } }, (0, require_utils_index.utils_exports.runIfFn)(footerProps, header$1) ?? {}, columnDef.footerProps ?? {})(), children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TruncatedText, { lineClamp: columnDef.lineClamp ?? lineClamp, truncated: columnDef.truncated ?? truncated, children }) }, header$1.id); }) }, footerGroup.id); }) }) : null ] }), footer ? (0, require_utils_index.utils_exports.runIfFn)(footer, table) : null ] }); })(); const SortingIcon = ({ sorted,...rest }) => { const { t } = require_i18n_provider.useI18n("table"); const { sortingIcon, sortingIconProps = {} } = useComponentContext(); const Icon = sorted ? require_chevron_up_icon.ChevronUpIcon : require_chevrons_up_down_icon.ChevronsUpDownIcon; return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_factory.styled.button, { type: "button", layerStyle: "ghost", colorScheme: "mono", "aria-label": t(sorted ? sorted === "desc" ? "Sort descending" : "Sort ascending" : "Clear sorting"), "data-focusable": true, aspectRatio: "1", cursor: "pointer", display: "center", focusVisibleRing: "none", h: "calc(1em * {lineHeights.moderate})", position: "absolute", right: "{space-x}", rounded: "l1", tabIndex: -1, top: "50%", transform: "translateY(-50%)", transitionDuration: "moderate", transitionProperty: "common", _hover: { layerStyle: "ghost.hover" }, ...require_props.mergeProps(rest, sortingIconProps)(), children: (0, require_utils_index.utils_exports.runIfFn)(sortingIcon, sorted) ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { transform: `rotate(${sorted === "desc" ? 180 : 0}deg)` }) }); }; const ResizableTrigger = ({ resizing,...rest }) => { const { columnResizeMode, table, resizableTriggerProps = {} } = useComponentContext(); const offset = table.getState().columnSizingInfo.deltaOffset; return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_factory.styled.div, { "data-active": (0, require_utils_index.utils_exports.dataAttr)(resizing), bg: "colorScheme.solid", cursor: "col-resize", insetY: "0", opacity: { base: "0", _hover: "1", _active: "1" }, position: "absolute", right: "0", touchAction: "none", transform: `translateX(${columnResizeMode === "onEnd" && resizing && offset ? `${offset}px` : "50%"})`, userSelect: "none", w: "1", ...require_props.mergeProps(rest, resizableTriggerProps)() }); }; const TruncatedText = ({ children, lineClamp, truncated,...rest }) => { if (lineClamp || truncated) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_factory.styled.span, { lineClamp, truncated, wordBreak: "break-all", ...rest, children }); else return children; }; //#endregion exports.Table = Table; exports.TablePropsContext = TablePropsContext; exports.useTablePropsContext = useTablePropsContext; //# sourceMappingURL=table.cjs.map