@trail-ui/react
Version:
283 lines (280 loc) • 12.8 kB
JavaScript
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
};