UNPKG

@trail-ui/react

Version:
283 lines (280 loc) 12.8 kB
import { _Checkbox } from "./chunk-BVL6GTHH.mjs"; import { _Spinner } from "./chunk-P3AIC7XZ.mjs"; // src/table/table.tsx import React, { useEffect, useCallback, useMemo } from "react"; import { CaretUpDownIcon } from "@trail-ui/icons"; import { ArrowDownIcon, ArrowUpIcon } from "@trail-ui/icons"; import { jsx, jsxs } from "react/jsx-runtime"; var sortOptions = { asc: /* @__PURE__ */ jsx(ArrowUpIcon, { "aria-hidden": true, className: "h-4 w-4 text-purple-600" }), desc: /* @__PURE__ */ jsx(ArrowDownIcon, { "aria-hidden": true, className: "h-4 w-4 text-purple-600" }) }; var sortOrder = [false, "asc", "desc"]; function SelectAllCheckboxComponent({ tableName, data, selectedIds, onToggle, idSelector, selectionCheckFn }) { const allSelected = selectedIds.size > 0 && data.filter((d) => !(selectionCheckFn == null ? void 0 : selectionCheckFn(d))).every((d) => selectedIds.has(idSelector(d))); const someSelected = data.filter((d) => !(selectionCheckFn == null ? void 0 : selectionCheckFn(d))).some((d) => selectedIds.has(idSelector(d))); return /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx( _Checkbox, { "aria-label": `Select All ${tableName}`, isSelected: allSelected, isIndeterminate: !allSelected && someSelected, onChange: () => onToggle(!allSelected) } ) }); } var SelectAllCheckbox = React.memo(SelectAllCheckboxComponent); function CustomTable(props) { var _a, _b, _c; const toggleRowSelection = useCallback( (id) => { var _a2; (_a2 = props.selection) == null ? void 0 : _a2.setSelectedIds((prev) => { const updated = new Set(prev); if (updated.has(id)) { updated.delete(id); } else { updated.add(id); } return updated; }); }, [props.selection] ); const toggleAllSelection = useCallback( (select) => { var _a2, _b2, _c2; if ((_a2 = props.selection) == null ? void 0 : _a2.onSelectAll) { (_b2 = props.selection) == null ? void 0 : _b2.onSelectAll(); } else { (_c2 = props.selection) == null ? void 0 : _c2.setSelectedIds((prev) => { const updated = new Set(prev); props.data.forEach((item) => { var _a3, _b3, _c3, _d, _e; if (!((_b3 = (_a3 = props.selection) == null ? void 0 : _a3.isRowSelectionDisabled) == null ? void 0 : _b3.call(_a3, item)) && ((_c3 = props.selection) == null ? void 0 : _c3.idSelector)) { if (select) { updated.add((_d = props.selection) == null ? void 0 : _d.idSelector(item)); } else { updated.delete((_e = props.selection) == null ? void 0 : _e.idSelector(item)); } } }); return updated; }); } }, [props.data, props.selection] ); const toggleGroupSelection = useCallback( (items, select) => { var _a2; (_a2 = props.selection) == null ? void 0 : _a2.setSelectedIds((prev) => { const updated = new Set(prev); items.forEach((item) => { var _a3, _b2; if ((_a3 = props.selection) == null ? void 0 : _a3.idSelector) { const id = (_b2 = props.selection) == null ? void 0 : _b2.idSelector(item); if (select) { updated.add(id); } else { updated.delete(id); } } }); return updated; }); }, [props.selection] ); const handleSortChange = useCallback( (sortBy) => { if (!props.sort) { return; } const index = props.sort.sortBy === sortBy ? sortOrder.indexOf(props.sort.sortDirection) : 0; const nextDirection = sortOrder[(index + 1) % sortOrder.length]; props.sort.onSortClick(sortBy, nextDirection); }, [props.sort] ); const renderRow = useCallback( (item, index) => { var _a2, _b2, _c2; const isChecked = (_a2 = props.selection) == null ? void 0 : _a2.selectedIds.has(props.selection.idSelector(item)); return /* @__PURE__ */ jsxs( "tr", { className: "border-b border-b-neutral-200 last:border-b-0", children: [ props.selection && /* @__PURE__ */ jsx("td", { className: `${isChecked ? "bg-purple-50" : "bg-neutral-50"} px-2 py-2 text-sm`, children: /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx( _Checkbox, { "aria-label": ((_c2 = props.selection) == null ? void 0 : _c2.checkBoxLabel) ? props.selection.checkBoxLabel(item) : `Select ${props.selection.idSelector(item)}`, isSelected: isChecked, onChange: () => { var _a3; return ((_a3 = props.selection) == null ? void 0 : _a3.idSelector) && toggleRowSelection(props.selection.idSelector(item)); }, ...props.selection.isRowSelectionDisabled && { isDisabled: props.selection.isRowSelectionDisabled(item) } } ) }) }), props.headers.map((column) => { const Element = column.columnType || "td"; return /* @__PURE__ */ jsx( Element, { headers: column.id, className: `${isChecked ? "bg-purple-50" : "bg-neutral-50"} px-2 py-2 text-sm`, style: { minWidth: column.width, maxWidth: column.width, width: column.width }, children: column.cellRenderFn ? column.cellRenderFn(item) : column.accessorKey && item[column.accessorKey] }, column.id ); }) ] }, ((_b2 = props.selection) == null ? void 0 : _b2.idSelector(item)) || index ); }, [props.selection, props.headers, toggleRowSelection] ); const renderGroup = useCallback( (group) => { const allSelected = group.items.every( (item) => { var _a2; return (_a2 = props.selection) == null ? void 0 : _a2.selectedIds.has(props.selection.idSelector(item)); } ); const someSelected = group.items.some( (item) => { var _a2; return (_a2 = props.selection) == null ? void 0 : _a2.selectedIds.has(props.selection.idSelector(item)); } ); return /* @__PURE__ */ jsxs(React.Fragment, { children: [ /* @__PURE__ */ jsxs("tr", { children: [ /* @__PURE__ */ jsx("td", { className: "border-b border-r bg-neutral-50 p-0", children: /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx( _Checkbox, { "aria-label": `Select group ${group.groupName}`, isSelected: allSelected, isIndeterminate: !allSelected && someSelected, onChange: () => toggleGroupSelection(group.items, !allSelected) } ) }) }), /* @__PURE__ */ jsxs("th", { colSpan: props.headers.length, className: "border-b bg-neutral-50 text-left", children: [ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: group.groupName }), " ", /* @__PURE__ */ jsxs("span", { className: "text-sm font-normal text-neutral-600", children: [ "(", group.items.length, " ", group.items.length === 1 ? "issue" : "issues", ")" ] }) ] }) ] }), group.items.map(renderRow) ] }, group.groupId); }, [props.selection, props.headers.length, toggleGroupSelection, renderRow] ); const columnCells = useMemo(() => { return props.headers.map((column) => { var _a2, _b2, _c2; return /* @__PURE__ */ jsx( "th", { id: column.id, scope: "col", className: "h-10 border-r border-neutral-200 bg-neutral-100 p-2 text-left text-sm font-semibold last:border-r-0", style: { minWidth: column.width === 100 ? "max-content" : column.width, maxWidth: column.width === 100 ? "max-content" : column.width, width: column.width }, ...column.isSortable ? { "aria-sort": ((_a2 = props.sort) == null ? void 0 : _a2.sortBy) === column.id ? ((_b2 = props.sort) == null ? void 0 : _b2.sortDirection) === "asc" ? "ascending" : "descending" : "none" } : {}, children: column.isSortable ? /* @__PURE__ */ jsxs( "button", { type: "button", onClick: () => handleSortChange(column.id), className: "flex w-full items-center justify-between gap-4 px-1 outline-purple-600", children: [ column.headerRenderFn ? column.headerRenderFn() : /* @__PURE__ */ jsxs("span", { className: "text-left", children: [ column.label, " ", /* @__PURE__ */ jsx("span", { className: "sr-only", children: " Sortable" }) ] }), ((_c2 = props.sort) == null ? void 0 : _c2.sortBy) === column.id && props.sort.sortDirection !== false ? sortOptions[props.sort.sortDirection] : /* @__PURE__ */ jsx(CaretUpDownIcon, { className: "h-4 w-4 text-neutral-600" }) ] } ) : column.headerRenderFn ? column.headerRenderFn() : column.label }, column.id ); }); }, [props.headers, props.sort, handleSortChange]); useEffect(() => { var _a2, _b2; if (!((_a2 = props.selection) == null ? void 0 : _a2.retainSelectionAccrossPages)) { (_b2 = props.selection) == null ? void 0 : _b2.setSelectedIds(/* @__PURE__ */ new Set()); } }, [props.data]); return /* @__PURE__ */ jsx("div", { className: "rounded", children: /* @__PURE__ */ jsx("div", { className: "h-full overflow-auto rounded border border-neutral-200", children: /* @__PURE__ */ jsx("div", { className: (_a = props.maxHeight) != null ? _a : "max-h-[60vh]", children: /* @__PURE__ */ jsxs( "table", { "aria-label": (_b = props.tableName) != null ? _b : "", className: "w-full table-auto border-collapse bg-white", children: [ props.headers.some((header) => header.isSortable) && /* @__PURE__ */ jsx("caption", { className: "sr-only", children: "column headers with buttons are sortable" }), /* @__PURE__ */ jsx("thead", { className: "sticky top-0 z-[1] before:absolute before:bottom-0 before:w-full before:border-b before:border-b-neutral-200", children: /* @__PURE__ */ jsxs("tr", { className: "bg-gray-200", children: [ props.selection && props.tableType === "normal" && !props.selection.hideSelectAll ? /* @__PURE__ */ jsx("td", { className: "h-10 w-10 border-r border-neutral-200 bg-neutral-100 p-2 text-sm font-semibold", children: /* @__PURE__ */ jsx( SelectAllCheckbox, { tableName: props.tableName, data: props.data, selectedIds: props.selection.selectedIds, onToggle: toggleAllSelection, idSelector: props.selection.idSelector, selectionCheckFn: props.selection.isRowSelectionDisabled } ) }) : props.selection && /* @__PURE__ */ jsx("td", { className: "h-10 w-10 border-r border-neutral-200 bg-neutral-100 p-2" }), columnCells ] }) }), /* @__PURE__ */ jsx("tbody", { children: props.isLoading ? /* @__PURE__ */ jsx("tr", { className: "h-[300px]", children: /* @__PURE__ */ jsx("td", { colSpan: props.headers.length + 1, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center gap-1", children: [ /* @__PURE__ */ jsx(_Spinner, { className: "h-8 w-8 animate-spin" }), /* @__PURE__ */ jsx("p", { className: "pt-1 text-neutral-700", children: "Loading..." }) ] }) }) }) : !((_c = props.data) == null ? void 0 : _c.length) ? /* @__PURE__ */ jsx("tr", { className: "h-[300px]", children: /* @__PURE__ */ jsx("td", { colSpan: props.headers.length + 1, children: /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center justify-center gap-1", children: /* @__PURE__ */ jsx("p", { role: "alert", "aria-live": "polite", className: "text-neutral-700", children: "No data" }) }) }) }) : props.data.map( (item, index) => props.tableType === "normal" ? renderRow(item, index) : renderGroup(item) ) }) ] } ) }) }) }); } export { sortOptions, sortOrder, SelectAllCheckbox, CustomTable };