@tradalab/lyra 3.12.0 → 3.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -2
- package/dist/{account-menu-D9M_kEzN.d.ts → account-menu-q2VMEk3b.d.ts} +10 -2
- package/dist/blocks/index.js +5 -5
- package/dist/{chunk-S5O4AGES.js → chunk-4ILJ3M7K.js} +5 -111
- package/dist/chunk-4ILJ3M7K.js.map +1 -0
- package/dist/{chunk-CLNMWZPV.js → chunk-SQELPQZU.js} +83 -4
- package/dist/chunk-SQELPQZU.js.map +1 -0
- package/dist/{chunk-AM6UJ666.js → chunk-U4HIZXI6.js} +33 -5
- package/dist/chunk-U4HIZXI6.js.map +1 -0
- package/dist/data-table/index.d.ts +43 -5
- package/dist/data-table/index.js +135 -15
- package/dist/data-table/index.js.map +1 -1
- package/dist/shell/index.d.ts +23 -2
- package/dist/shell/index.js +34 -4
- package/dist/shell/index.js.map +1 -1
- package/dist/ui/index.d.ts +4 -12
- package/dist/ui/index.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-AM6UJ666.js.map +0 -1
- package/dist/chunk-CLNMWZPV.js.map +0 -1
- package/dist/chunk-S5O4AGES.js.map +0 -1
package/dist/data-table/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { TableHead, Table, TableRow, TableHeader, TableCell, TableBody, NativeSelect, NativeSelectOption } from '../chunk-
|
|
2
|
+
import { TableHead, Table, TableRow, TableHeader, TableCell, TableBody, Checkbox, NativeSelect, NativeSelectOption } from '../chunk-U4HIZXI6.js';
|
|
3
3
|
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from '../chunk-VEWKPOMS.js';
|
|
4
4
|
import { Input, Button } from '../chunk-VGOGWZCF.js';
|
|
5
5
|
import { cn } from '../chunk-XH55BHUU.js';
|
|
6
6
|
import { flexRender, useReactTable, getSortedRowModel, getCoreRowModel, getPaginationRowModel, getFilteredRowModel } from '@tanstack/react-table';
|
|
7
|
-
import { SearchIcon, RefreshCwIcon, ArrowDownIcon, ArrowUpIcon, ChevronsUpDownIcon, Loader2Icon } from 'lucide-react';
|
|
7
|
+
import { SearchIcon, RefreshCwIcon, XIcon, ArrowDownIcon, ArrowUpIcon, ChevronsUpDownIcon, Loader2Icon } from 'lucide-react';
|
|
8
8
|
import { createContext, memo, useState, useRef, useEffect, useCallback, useContext } from 'react';
|
|
9
9
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
10
10
|
|
|
@@ -26,19 +26,34 @@ function TableFilterBar({
|
|
|
26
26
|
}
|
|
27
27
|
function TableSearch({
|
|
28
28
|
onChange,
|
|
29
|
+
value = "",
|
|
29
30
|
placeholder = "Search\u2026",
|
|
30
31
|
className,
|
|
31
32
|
icon = true
|
|
32
33
|
}) {
|
|
33
|
-
const [text, setText] = useState(
|
|
34
|
+
const [text, setText] = useState(value);
|
|
35
|
+
const [sent, setSent] = useState(value.trim());
|
|
36
|
+
const [held, setHeld] = useState(value);
|
|
37
|
+
if (value !== held) {
|
|
38
|
+
setHeld(value);
|
|
39
|
+
if (value.trim() !== sent) {
|
|
40
|
+
setSent(value.trim());
|
|
41
|
+
setText(value);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
34
44
|
const sink = useRef(onChange);
|
|
35
45
|
useEffect(() => {
|
|
36
46
|
sink.current = onChange;
|
|
37
47
|
}, [onChange]);
|
|
38
48
|
useEffect(() => {
|
|
39
|
-
const id = setTimeout(() =>
|
|
49
|
+
const id = setTimeout(() => {
|
|
50
|
+
const q = text.trim();
|
|
51
|
+
if (q === sent) return;
|
|
52
|
+
setSent(q);
|
|
53
|
+
sink.current(q);
|
|
54
|
+
}, 300);
|
|
40
55
|
return () => clearTimeout(id);
|
|
41
|
-
}, [text]);
|
|
56
|
+
}, [text, sent]);
|
|
42
57
|
return /* @__PURE__ */ jsxs("div", { className: cn("relative w-full max-w-xs", className), children: [
|
|
43
58
|
icon ? /* @__PURE__ */ jsx(SearchIcon, { className: "text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2" }) : null,
|
|
44
59
|
/* @__PURE__ */ jsx(
|
|
@@ -71,18 +86,54 @@ function TableReload({
|
|
|
71
86
|
}
|
|
72
87
|
);
|
|
73
88
|
}
|
|
89
|
+
function TableFilterChips({
|
|
90
|
+
chips,
|
|
91
|
+
clearLabel = "Clear filters",
|
|
92
|
+
onClearAll,
|
|
93
|
+
removeLabel = "Remove filter"
|
|
94
|
+
}) {
|
|
95
|
+
if (chips.length === 0) return null;
|
|
96
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
97
|
+
chips.map((c) => /* @__PURE__ */ jsxs("span", { className: "bg-muted inline-flex h-7 items-center gap-1 border pr-1 pl-2 text-xs", children: [
|
|
98
|
+
c.label,
|
|
99
|
+
/* @__PURE__ */ jsx(
|
|
100
|
+
Button,
|
|
101
|
+
{
|
|
102
|
+
type: "button",
|
|
103
|
+
variant: "ghost",
|
|
104
|
+
size: "icon-xs",
|
|
105
|
+
"aria-label": typeof c.label === "string" ? `${removeLabel}: ${c.label}` : removeLabel,
|
|
106
|
+
title: removeLabel,
|
|
107
|
+
onClick: c.onClear,
|
|
108
|
+
children: /* @__PURE__ */ jsx(XIcon, {})
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
] }, c.key)),
|
|
112
|
+
onClearAll && chips.length > 1 ? /* @__PURE__ */ jsx(Button, { type: "button", variant: "link", size: "sm", className: "h-7 px-1", onClick: onClearAll, children: clearLabel }) : null
|
|
113
|
+
] });
|
|
114
|
+
}
|
|
74
115
|
var PAGE_SIZE_OPTIONS = [25, 50, 100, 200];
|
|
75
116
|
var DEFAULT_PAGE_SIZE = 25;
|
|
76
117
|
function usePager(options = {}) {
|
|
77
118
|
const {
|
|
78
119
|
pageSize: initialSize = DEFAULT_PAGE_SIZE,
|
|
79
120
|
pageSizeOptions = PAGE_SIZE_OPTIONS,
|
|
80
|
-
labels
|
|
121
|
+
labels,
|
|
122
|
+
page: heldPage,
|
|
123
|
+
onPageChange
|
|
81
124
|
} = options;
|
|
82
|
-
const [
|
|
125
|
+
const [ownPage, setOwnPage] = useState(1);
|
|
83
126
|
const [size, setSize] = useState(initialSize);
|
|
127
|
+
const page = heldPage ?? ownPage;
|
|
128
|
+
const setPage = onPageChange ?? setOwnPage;
|
|
129
|
+
const seen = useRef(0);
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
const last = Math.ceil(seen.current / size);
|
|
132
|
+
if (last > 0 && page > last) setPage(last);
|
|
133
|
+
});
|
|
84
134
|
const props = useCallback(
|
|
85
135
|
(total) => {
|
|
136
|
+
seen.current = total;
|
|
86
137
|
const pageCount = Math.max(1, Math.ceil(total / size));
|
|
87
138
|
const at = total > 0 ? Math.min(page, pageCount) : page;
|
|
88
139
|
return {
|
|
@@ -111,14 +162,14 @@ function usePager(options = {}) {
|
|
|
111
162
|
}
|
|
112
163
|
};
|
|
113
164
|
},
|
|
114
|
-
[page, size, pageSizeOptions, labels]
|
|
165
|
+
[page, setPage, size, pageSizeOptions, labels]
|
|
115
166
|
);
|
|
116
167
|
const onFilter = useCallback(
|
|
117
168
|
(apply) => (v) => {
|
|
118
169
|
apply(v);
|
|
119
170
|
setPage(1);
|
|
120
171
|
},
|
|
121
|
-
[]
|
|
172
|
+
[setPage]
|
|
122
173
|
);
|
|
123
174
|
return {
|
|
124
175
|
page,
|
|
@@ -209,6 +260,16 @@ var TableBody2 = ({ children, className }) => {
|
|
|
209
260
|
const rows = table?.getRowModel().rows;
|
|
210
261
|
return /* @__PURE__ */ jsx(TableBody, { className, children: rows?.length ? rows.map((row) => children({ row })) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { className: "h-24 text-center", colSpan: columns.length, children: "No results." }) }) });
|
|
211
262
|
};
|
|
263
|
+
var INTERACTIVE = "a,button,input,select,textarea,label,summary,[contenteditable=true],[role=button],[role=link],[role=checkbox],[role=switch],[role=menuitem],[role=tab]";
|
|
264
|
+
function opensRow(e) {
|
|
265
|
+
const row = e.currentTarget;
|
|
266
|
+
const target = e.target;
|
|
267
|
+
if (!row.contains(target)) return false;
|
|
268
|
+
if (target === row) return true;
|
|
269
|
+
const hit = target.closest(INTERACTIVE);
|
|
270
|
+
if (hit !== null && row.contains(hit)) return false;
|
|
271
|
+
return e.type !== "click" || !window.getSelection()?.toString();
|
|
272
|
+
}
|
|
212
273
|
function pageWindow(index, count, radius = 1) {
|
|
213
274
|
if (count <= 0) return [];
|
|
214
275
|
const want = /* @__PURE__ */ new Set([0, count - 1]);
|
|
@@ -253,15 +314,23 @@ function DataTable({
|
|
|
253
314
|
pageSizeOptions,
|
|
254
315
|
onPageSizeChange,
|
|
255
316
|
rangeLabel,
|
|
256
|
-
pageLabels
|
|
317
|
+
pageLabels,
|
|
318
|
+
onRowClick,
|
|
319
|
+
getRowId,
|
|
320
|
+
selected = [],
|
|
321
|
+
onSelectedChange,
|
|
322
|
+
selectLabels
|
|
257
323
|
}) {
|
|
258
324
|
const [globalFilter, setGlobalFilter] = useState("");
|
|
325
|
+
const selecting = Boolean(onSelectedChange && getRowId);
|
|
326
|
+
const picked = new Set(selected);
|
|
259
327
|
const infinite = Boolean(onLoadMore);
|
|
260
328
|
const served = !infinite && Boolean(onPageChange);
|
|
261
329
|
const sliced = !infinite && !served && !unpaged;
|
|
262
330
|
const table = useReactTable({
|
|
263
331
|
data,
|
|
264
332
|
columns,
|
|
333
|
+
getRowId: getRowId ? (row) => getRowId(row) : void 0,
|
|
265
334
|
state: { globalFilter },
|
|
266
335
|
onGlobalFilterChange: setGlobalFilter,
|
|
267
336
|
getCoreRowModel: getCoreRowModel(),
|
|
@@ -272,6 +341,17 @@ function DataTable({
|
|
|
272
341
|
...sliced ? { getPaginationRowModel: getPaginationRowModel(), initialState: { pagination: { pageSize } } } : {}
|
|
273
342
|
});
|
|
274
343
|
const rows = table.getRowModel().rows;
|
|
344
|
+
const span = columns.length + (selecting ? 1 : 0);
|
|
345
|
+
const shown = rows.map((r) => r.id);
|
|
346
|
+
const shownPicked = shown.filter((id) => picked.has(id)).length;
|
|
347
|
+
const toggle = (ids, on) => {
|
|
348
|
+
const next = new Set(picked);
|
|
349
|
+
for (const id of ids) {
|
|
350
|
+
if (on) next.add(id);
|
|
351
|
+
else next.delete(id);
|
|
352
|
+
}
|
|
353
|
+
onSelectedChange?.([...next]);
|
|
354
|
+
};
|
|
275
355
|
const showBar = Boolean(searchable || onReload || toolbar);
|
|
276
356
|
const sentinelRef = useRef(null);
|
|
277
357
|
const onLoadMoreRef = useRef(onLoadMore);
|
|
@@ -336,12 +416,52 @@ function DataTable({
|
|
|
336
416
|
] }) : null,
|
|
337
417
|
/* @__PURE__ */ jsxs("div", { className: cn("overflow-hidden border", className), children: [
|
|
338
418
|
/* @__PURE__ */ jsxs(Table, { children: [
|
|
339
|
-
/* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((hg) => /* @__PURE__ */
|
|
419
|
+
/* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((hg) => /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
420
|
+
selecting ? /* @__PURE__ */ jsx(TableHead, { className: "w-8", children: /* @__PURE__ */ jsx(
|
|
421
|
+
Checkbox,
|
|
422
|
+
{
|
|
423
|
+
"aria-label": selectLabels?.rows ?? "Select rows on this page",
|
|
424
|
+
checked: shown.length > 0 && shownPicked === shown.length ? true : shownPicked > 0 ? "indeterminate" : false,
|
|
425
|
+
disabled: shown.length === 0,
|
|
426
|
+
onCheckedChange: (v) => toggle(shown, v === true)
|
|
427
|
+
}
|
|
428
|
+
) }) : null,
|
|
429
|
+
hg.headers.map((header) => /* @__PURE__ */ jsx(TableHead, { children: header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext()) }, header.id))
|
|
430
|
+
] }, hg.id)) }),
|
|
340
431
|
/* @__PURE__ */ jsx(TableBody, { children: loading && rows.length === 0 ? (
|
|
341
432
|
// Only replace the body with a loading row on the FIRST load; on a
|
|
342
433
|
// refetch keep the (stale) rows visible while the reload icon spins.
|
|
343
|
-
/* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { className: "h-24 text-center", colSpan:
|
|
344
|
-
) : rows.length ? rows.map((row) => /* @__PURE__ */
|
|
434
|
+
/* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { className: "h-24 text-center", colSpan: span, children: loadingText }) })
|
|
435
|
+
) : rows.length ? rows.map((row) => /* @__PURE__ */ jsxs(
|
|
436
|
+
TableRow,
|
|
437
|
+
{
|
|
438
|
+
"data-state": picked.has(row.id) ? "selected" : void 0,
|
|
439
|
+
className: onRowClick ? "focus-visible:outline-ring cursor-pointer focus-visible:outline-2 focus-visible:-outline-offset-2" : void 0,
|
|
440
|
+
tabIndex: onRowClick ? 0 : void 0,
|
|
441
|
+
onClick: onRowClick ? (e) => opensRow(e) && onRowClick(row.original) : void 0,
|
|
442
|
+
onKeyDown: onRowClick ? (e) => {
|
|
443
|
+
if ((e.key === "Enter" || e.key === " ") && opensRow(e)) {
|
|
444
|
+
e.preventDefault();
|
|
445
|
+
onRowClick(row.original);
|
|
446
|
+
}
|
|
447
|
+
} : void 0,
|
|
448
|
+
children: [
|
|
449
|
+
selecting ? (
|
|
450
|
+
// The cell is a wide target around a small box; a near miss must not open the row.
|
|
451
|
+
/* @__PURE__ */ jsx(TableCell, { className: "w-8", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx(
|
|
452
|
+
Checkbox,
|
|
453
|
+
{
|
|
454
|
+
"aria-label": selectLabels?.row ?? "Select row",
|
|
455
|
+
checked: picked.has(row.id),
|
|
456
|
+
onCheckedChange: (v) => toggle([row.id], v === true)
|
|
457
|
+
}
|
|
458
|
+
) })
|
|
459
|
+
) : null,
|
|
460
|
+
row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(TableCell, { children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))
|
|
461
|
+
]
|
|
462
|
+
},
|
|
463
|
+
row.id
|
|
464
|
+
)) : (
|
|
345
465
|
// An empty state is not a record, so the row drops the `hover:bg-muted/50` every
|
|
346
466
|
// other row carries: lighting up under the pointer offers an interaction that is not
|
|
347
467
|
// there, and it gets louder the taller the cell grows.
|
|
@@ -349,7 +469,7 @@ function DataTable({
|
|
|
349
469
|
TableCell,
|
|
350
470
|
{
|
|
351
471
|
className: empty ? "p-0" : "text-muted-foreground h-24 text-center",
|
|
352
|
-
colSpan:
|
|
472
|
+
colSpan: span,
|
|
353
473
|
children: empty ?? emptyText
|
|
354
474
|
}
|
|
355
475
|
) })
|
|
@@ -442,6 +562,6 @@ function DataTable({
|
|
|
442
562
|
] });
|
|
443
563
|
}
|
|
444
564
|
|
|
445
|
-
export { DEFAULT_PAGE_SIZE, DataTable, PAGE_SIZE_OPTIONS, TableBody2 as TableBody, TableCell2 as TableCell, TableColumnHeader, TableContext, TableFilterBar, TableHead2 as TableHead, TableHeader2 as TableHeader, TableHeaderGroup, TableProvider, TableReload, TableRow2 as TableRow, TableSearch, filterRows, pageWindow, usePager };
|
|
565
|
+
export { DEFAULT_PAGE_SIZE, DataTable, PAGE_SIZE_OPTIONS, TableBody2 as TableBody, TableCell2 as TableCell, TableColumnHeader, TableContext, TableFilterBar, TableFilterChips, TableHead2 as TableHead, TableHeader2 as TableHeader, TableHeaderGroup, TableProvider, TableReload, TableRow2 as TableRow, TableSearch, filterRows, pageWindow, usePager };
|
|
446
566
|
//# sourceMappingURL=index.js.map
|
|
447
567
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/data-table/filter-rows.ts","../../src/data-table/filter-bar.tsx","../../src/data-table/use-pager.ts","../../src/data-table/index.tsx"],"names":["useState","jsx","TableHead","TableHeader","useCallback","jsxs","TableCell","TableRow","TableBody","useRef","useEffect","SearchIcon","RefreshCwIcon"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Case-insensitive search over chosen fields, for a list the server does NOT page.\n *\n * Honest only there: when the fetch is the whole answer, filtering here searches everything\n * the reader could have seen. On a server-paged list the same code searches one page and then\n * reports \"no results\" about rows it never had - which is why those lists send the query to\n * the server instead.\n *\n * Takes a field selector rather than key names so a derived value (a joined label, a formatted\n * code) is searchable too - on the tables this exists for, the derived column is usually the\n * one people type into the box.\n */\nexport function filterRows<T>(\n rows: T[],\n q: string,\n fields: (r: T) => (string | undefined | null)[],\n): T[] {\n const needle = q.trim().toLowerCase()\n if (!needle) return rows\n return rows.filter(r => fields(r).some(v => v?.toLowerCase().includes(needle)))\n}\n","\"use client\"\n\nimport { RefreshCwIcon, SearchIcon } from \"lucide-react\"\nimport type { ReactNode } from \"react\"\nimport { useEffect, useRef, useState } from \"react\"\nimport { Button } from \"~/ui/primitives/button.js\"\nimport { Input } from \"~/ui/primitives/input.js\"\nimport { cn } from \"~/utils/cn.js\"\n\n/**\n * The row above a table: narrowing on the left, actions on the right.\n *\n * This REPLACES `DataTable`'s own bar (`searchable` + `toolbar` + `onReload`) rather than\n * sitting beside it. Two bars on one table is how a screen ends up with two search boxes that\n * search different things, and `searchable` in particular must stay off on a server-paged\n * table: it filters the rows already in the browser, so it answers \"no results\" about a row\n * that is on page three.\n */\nexport function TableFilterBar({\n children,\n actions,\n className,\n}: {\n children?: ReactNode\n actions?: ReactNode\n className?: string\n}) {\n return (\n <div className={cn(\"flex flex-wrap items-center gap-2\", className)}>\n {children}\n {actions ? <div className=\"ml-auto flex items-center gap-2\">{actions}</div> : null}\n </div>\n )\n}\n\n/**\n * A search box that reports the settled value, debounced here so no call site has to remember.\n *\n * 300ms, because on a server-paged list every keystroke is both a round trip and a new query\n * key - and a new query key throws away the page already loaded.\n */\nexport function TableSearch({\n onChange,\n placeholder = \"Search…\",\n className,\n icon = true,\n}: {\n /** Called with the trimmed value once typing settles. */\n onChange: (q: string) => void\n placeholder?: string\n className?: string\n /** Set false where the surrounding bar already says what the box is for. */\n icon?: boolean\n}) {\n const [text, setText] = useState(\"\")\n // Held in a ref, written from an effect: a caller passing an inline arrow would otherwise\n // restart the timer on every render, so the request would only fire once the user stopped\n // typing AND the parent stopped re-rendering.\n const sink = useRef(onChange)\n useEffect(() => {\n sink.current = onChange\n }, [onChange])\n useEffect(() => {\n const id = setTimeout(() => sink.current(text.trim()), 300)\n return () => clearTimeout(id)\n }, [text])\n\n return (\n <div className={cn(\"relative w-full max-w-xs\", className)}>\n {icon ? (\n <SearchIcon className=\"text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2\" />\n ) : null}\n <Input\n value={text}\n onChange={e => setText(e.target.value)}\n placeholder={placeholder}\n className={icon ? \"pl-8\" : undefined}\n />\n </div>\n )\n}\n\n/** Refetch, spinning while it does. Belongs in the filter bar, not in DataTable's own bar. */\nexport function TableReload({\n onClick,\n loading,\n label = \"Reload\",\n}: {\n onClick: () => void\n loading?: boolean\n label?: string\n}) {\n return (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon-sm\"\n onClick={onClick}\n disabled={loading}\n aria-label={label}\n title={label}\n >\n <RefreshCwIcon className={cn(\"size-4\", loading && \"animate-spin\")} />\n </Button>\n )\n}\n","\"use client\";\n\nimport { useCallback, useState } from \"react\";\n\n/**\n * The state behind `DataTable`'s numbered pager, so a screen configures paging instead of\n * owning it.\n *\n * Every console that grew a numbered table had written the same twelve lines - page state,\n * size state, the 0-based/1-based conversion, the range string, the back-to-page-1 wrapper -\n * and the copies had already drifted into pagers that behaved slightly differently on screens\n * next to each other. `DataTable` owned the controls; nothing owned the wiring.\n *\n * Two shapes, because the lists behind these tables are not all the same:\n *\n * `bind(total)` the SERVER paged it. Feed `page`/`size` into the request and hand back the\n * `total` it answered with.\n * `slice(rows)` the fetch IS the whole answer - a list derived from two endpoints, the\n * result of one operation, a parsed file - so the page is cut here.\n *\n * `slice` is honest only in that second case. Over a list the server truncated it reports a\n * page count that does not exist, which tells the reader they have seen everything.\n */\n\n/** Sizes offered in the rows-per-page selector. */\nexport const PAGE_SIZE_OPTIONS = [25, 50, 100, 200];\n\n/** Rows per page before anyone chooses. Smaller than a screenful is churn; larger buries the pager. */\nexport const DEFAULT_PAGE_SIZE = 25;\n\n/**\n * Copy for the pager. lyra ships none of it: `range` also decides number formatting, which is\n * a locale decision the app's i18n already makes.\n */\nexport type PagerLabels = {\n first?: string;\n previous?: string;\n next?: string;\n last?: string;\n size?: string;\n range?: (r: { from: number; to: number; total: number }) => string;\n};\n\n/** The `DataTable` props that make up the numbered pager. Spread them; never spell them out. */\nexport type PagerProps = {\n pageIndex: number;\n pageCount: number;\n onPageChange: (index: number) => void;\n pageSize: number;\n pageSizeOptions: number[];\n onPageSizeChange: (size: number) => void;\n rangeLabel: string;\n pageLabels: {\n first?: string;\n previous?: string;\n next?: string;\n last?: string;\n size?: string;\n };\n};\n\nexport type UsePagerOptions = {\n pageSize?: number;\n pageSizeOptions?: number[];\n labels?: PagerLabels;\n};\n\nexport function usePager(options: UsePagerOptions = {}) {\n const {\n pageSize: initialSize = DEFAULT_PAGE_SIZE,\n pageSizeOptions = PAGE_SIZE_OPTIONS,\n labels,\n } = options;\n const [page, setPage] = useState(1);\n const [size, setSize] = useState(initialSize);\n\n const props = useCallback(\n (total: number): PagerProps => {\n // The REPORTED page is clamped, exactly as `slice` clamps the rows it cuts. A set that\n // shrank under the current page - somebody deleted the last row on page 3, or another\n // operator did - otherwise leaves `pageIndex` past `pageCount`, and then `pageWindow`\n // highlights nothing at all: next and last are disabled, no button is marked current,\n // and the reader has to work out from an empty table that they are off the end.\n //\n // Only when `total > 0`, because a caller whose query has not answered yet passes 0, and\n // clamping then would snap page 3 back to page 1 on every single page change. Pass\n // react-query's `placeholderData: keepPreviousData` and the total stays real across a\n // page change, which is what makes this engage when it should.\n //\n // ⚠️ It clamps what the pager SHOWS, not what the caller requested - the rows for that\n // render are still the (empty) answer for the old page. The reader sees \"page 2 of 2\"\n // and recovers in one click instead of facing a pager that looks broken. Moving the\n // request too needs `total` to reach the hook rather than `bind`, which is an API change\n // and not this fix.\n const pageCount = Math.max(1, Math.ceil(total / size));\n const at = total > 0 ? Math.min(page, pageCount) : page;\n return {\n pageIndex: at - 1,\n pageCount,\n onPageChange: (i) => setPage(i + 1),\n pageSize: size,\n pageSizeOptions,\n // A different size means a different page 1; staying on page 7 of the old size lands\n // past the end of the new one.\n onPageSizeChange: (n) => {\n setSize(n);\n setPage(1);\n },\n rangeLabel:\n total > 0\n ? (labels?.range?.({\n from: (at - 1) * size + 1,\n to: Math.min(at * size, total),\n total,\n }) ?? \"\")\n : \"\",\n pageLabels: {\n first: labels?.first,\n previous: labels?.previous,\n next: labels?.next,\n last: labels?.last,\n size: labels?.size,\n },\n };\n },\n [page, size, pageSizeOptions, labels],\n );\n\n /**\n * Wrap a filter setter so changing it returns to page 1.\n *\n * In the HANDLER, not in an effect watching the filter: an effect renders the stale page\n * once and fires a request for it, so narrowing to a department with two people flashes\n * \"no results\" on every keystroke before correcting itself.\n */\n const onFilter = useCallback(\n <T>(apply: (v: T) => void) =>\n (v: T) => {\n apply(v);\n setPage(1);\n },\n [],\n );\n\n return {\n page,\n size,\n setPage,\n onFilter,\n bind: props,\n /** Cut the current page out of a complete list. */\n slice<T>(rows: T[]): { rows: T[]; props: PagerProps } {\n // A list that shrank under a filter can leave `page` past the end, which renders an\n // empty table that reads as \"nothing matched\". The SLICE is clamped, not the state:\n // writing state while rendering is the loop this replaced.\n const last = Math.max(1, Math.ceil(rows.length / size));\n const at = Math.min(page, last);\n return {\n rows: rows.slice((at - 1) * size, at * size),\n props: { ...props(rows.length), pageIndex: at - 1 },\n };\n },\n };\n}\n","import type { Cell, Column, ColumnDef, Header, HeaderGroup, Row, SortingState, Table } from \"@tanstack/react-table\"\nimport { flexRender, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from \"@tanstack/react-table\"\nimport { ArrowDownIcon, ArrowUpIcon, ChevronsUpDownIcon, Loader2Icon, RefreshCwIcon, SearchIcon } from \"lucide-react\"\nimport type { HTMLAttributes, ReactNode } from \"react\"\nimport { createContext, memo, useCallback, useContext, useEffect, useRef, useState } from \"react\"\nimport { Button } from \"~/ui/primitives/button.js\"\nimport { Input } from \"~/ui/primitives/input.js\"\nimport { NativeSelect, NativeSelectOption } from \"~/ui/primitives/native-select.js\"\nimport { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from \"~/ui/primitives/dropdown-menu.js\"\nimport {\n TableBody as TableBodyRaw,\n TableCell as TableCellRaw,\n TableHeader as TableHeaderRaw,\n TableHead as TableHeadRaw,\n Table as TableRaw,\n TableRow as TableRowRaw,\n} from \"~/ui/primitives/table.js\"\nimport { cn } from \"~/utils/cn.js\"\n\nexport type { ColumnDef } from \"@tanstack/react-table\"\n\nexport { filterRows } from \"./filter-rows.js\"\nexport { TableFilterBar, TableReload, TableSearch } from \"./filter-bar.js\"\nexport {\n DEFAULT_PAGE_SIZE,\n PAGE_SIZE_OPTIONS,\n usePager,\n type PagerLabels,\n type PagerProps,\n type UsePagerOptions,\n} from \"./use-pager.js\"\n\n\nexport const TableContext = createContext<{\n data: unknown[]\n columns: ColumnDef<unknown, unknown>[]\n table: Table<unknown> | null\n}>({\n data: [],\n columns: [],\n table: null,\n})\n\nexport type TableProviderProps<TData, TValue> = {\n columns: ColumnDef<TData, TValue>[]\n data: TData[]\n children: ReactNode\n className?: string\n}\n\nexport function TableProvider<TData, TValue>({ columns, data, children, className }: TableProviderProps<TData, TValue>) {\n const [sorting, setSorting] = useState<SortingState>([])\n const table = useReactTable({\n data,\n columns,\n getCoreRowModel: getCoreRowModel(),\n getSortedRowModel: getSortedRowModel(),\n // useState's setter is itself a valid TanStack OnChangeFn (it accepts a\n // value or an updater fn), so it can be wired directly — type-safe, no cast.\n onSortingChange: setSorting,\n state: {\n sorting,\n },\n })\n\n return (\n <TableContext.Provider\n value={{\n data,\n columns: columns as never,\n table: table as never,\n }}\n >\n <TableRaw className={className}>{children}</TableRaw>\n </TableContext.Provider>\n )\n}\n\nexport type TableHeadProps = {\n header: Header<unknown, unknown>\n className?: string\n}\n\nexport const TableHead = memo(({ header, className }: TableHeadProps) => (\n <TableHeadRaw className={className} key={header.id}>\n {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}\n </TableHeadRaw>\n))\n\nTableHead.displayName = \"TableHead\"\n\nexport type TableHeaderGroupProps = {\n headerGroup: HeaderGroup<unknown>\n children: (props: { header: Header<unknown, unknown> }) => ReactNode\n}\n\nexport const TableHeaderGroup = ({ headerGroup, children }: TableHeaderGroupProps) => (\n <TableRowRaw key={headerGroup.id}>{headerGroup.headers.map(header => children({ header }))}</TableRowRaw>\n)\n\nexport type TableHeaderProps = {\n className?: string\n children: (props: { headerGroup: HeaderGroup<unknown> }) => ReactNode\n}\n\nexport const TableHeader = ({ className, children }: TableHeaderProps) => {\n const { table } = useContext(TableContext)\n\n return <TableHeaderRaw className={className}>{table?.getHeaderGroups().map(headerGroup => children({ headerGroup }))}</TableHeaderRaw>\n}\n\nexport interface TableColumnHeaderProps<TData, TValue> extends HTMLAttributes<HTMLDivElement> {\n column: Column<TData, TValue>\n title: string\n}\n\nexport function TableColumnHeader<TData, TValue>({ column, title, className }: TableColumnHeaderProps<TData, TValue>) {\n // Extract inline event handlers to prevent unnecessary re-renders\n const handleSortAsc = useCallback(() => {\n column.toggleSorting(false)\n }, [column])\n\n const handleSortDesc = useCallback(() => {\n column.toggleSorting(true)\n }, [column])\n\n if (!column.getCanSort()) {\n return <div className={cn(className)}>{title}</div>\n }\n\n return (\n <div className={cn(\"flex items-center space-x-2\", className)}>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button className=\"-ml-3 h-8 data-[state=open]:bg-accent\" size=\"sm\" variant=\"ghost\">\n <span>{title}</span>\n {column.getIsSorted() === \"desc\" ? (\n <ArrowDownIcon className=\"ml-2 h-4 w-4\" />\n ) : column.getIsSorted() === \"asc\" ? (\n <ArrowUpIcon className=\"ml-2 h-4 w-4\" />\n ) : (\n <ChevronsUpDownIcon className=\"ml-2 h-4 w-4\" />\n )}\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\">\n <DropdownMenuItem onClick={handleSortAsc}>\n <ArrowUpIcon className=\"mr-2 h-3.5 w-3.5 text-muted-foreground/70\" />\n Asc\n </DropdownMenuItem>\n <DropdownMenuItem onClick={handleSortDesc}>\n <ArrowDownIcon className=\"mr-2 h-3.5 w-3.5 text-muted-foreground/70\" />\n Desc\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n )\n}\n\nexport type TableCellProps = {\n cell: Cell<unknown, unknown>\n className?: string\n}\n\nexport const TableCell = ({ cell, className }: TableCellProps) => (\n <TableCellRaw className={className}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</TableCellRaw>\n)\n\nexport type TableRowProps = {\n row: Row<unknown>\n children: (props: { cell: Cell<unknown, unknown> }) => ReactNode\n className?: string\n}\n\nexport const TableRow = ({ row, children, className }: TableRowProps) => (\n <TableRowRaw className={className} data-state={row.getIsSelected() && \"selected\"} key={row.id}>\n {row.getVisibleCells().map(cell => children({ cell }))}\n </TableRowRaw>\n)\n\nexport type TableBodyProps = {\n children: (props: { row: Row<unknown> }) => ReactNode\n className?: string\n}\n\nexport const TableBody = ({ children, className }: TableBodyProps) => {\n const { columns, table } = useContext(TableContext)\n const rows = table?.getRowModel().rows\n\n return (\n <TableBodyRaw className={className}>\n {rows?.length ? (\n rows.map(row => children({ row }))\n ) : (\n <TableRowRaw>\n <TableCellRaw className=\"h-24 text-center\" colSpan={columns.length}>\n No results.\n </TableCellRaw>\n </TableRowRaw>\n )}\n </TableBodyRaw>\n )\n}\n\n// ---------------------------------------------------------------------------\n// DataTable — a batteries-included, self-contained paginated table. Unlike the\n// composition primitives above (TableProvider + render-prop parts), this is a\n// drop-in: pass `columns` + `data` and get header, body, empty/loading states\n// and Prev/Next pagination. All user-facing labels are props (English\n// defaults) so apps can feed i18n strings; the component itself ships no copy.\n// ---------------------------------------------------------------------------\n\nexport interface DataTableProps<TData, TValue> {\n columns: ColumnDef<TData, TValue>[]\n data: TData[]\n /** Show a single-row loading state instead of the body. */\n loading?: boolean\n /** Row shown when there are no results. */\n emptyText?: string\n /**\n * What fills the body when there are no results, in place of `emptyText`.\n *\n * A centred grey line reads the same as a request that failed, and on a table it is the only\n * thing a reader has to tell \"nobody matched your filter\" from \"this screen is broken\". Takes\n * `EmptyState` from `@tradalab/lyra/ui`, or anything else. `emptyText` stays because most tables\n * want one sentence and nothing more.\n */\n empty?: ReactNode\n /** Row shown while `loading`. */\n loadingText?: string\n /** Previous-page button label. */\n previousLabel?: string\n /** Next-page button label. */\n nextLabel?: string\n /** Rows per page (default 10). Pagination controls appear only when needed. */\n pageSize?: number\n /**\n * Render every row and no pager at all.\n *\n * For the list whose row count does not grow with tenants or with time - a declared\n * inventory, a fixed catalogue, the handful of providers somebody configured by hand. There\n * the fetch is the whole answer, so a pager is a control nobody needs and a page count is a\n * number nobody asked for. Opt-in rather than the default, because the default (slice by\n * `pageSize`, Prev/Next) is what eleven existing call sites already rely on.\n */\n unpaged?: boolean\n /** Extra classes on the bordered table wrapper. */\n className?: string\n // --- control bar (all optional; the bar renders only when one is set) ---\n /** Show a search box that globally filters rows across all accessor columns. */\n searchable?: boolean\n /** Placeholder for the search box. */\n searchPlaceholder?: string\n /** Show a reload button; called on click. Its icon spins while `loading`. */\n onReload?: () => void\n /** Accessible label / tooltip for the reload button. */\n reloadLabel?: string\n /** Custom controls rendered on the right of the bar (e.g. a \"New\" button). */\n toolbar?: ReactNode\n // --- infinite scroll (opt-in; providing `onLoadMore` switches the table from\n // Prev/Next pagination to rendering every accumulated row and requesting more\n // as a bottom sentinel scrolls into view). Data fetching stays with the caller\n // (e.g. react-query useInfiniteQuery); the table only owns the sentinel. ---\n /** Called when the bottom sentinel becomes visible and more rows can load. */\n onLoadMore?: () => void\n /** Whether another page is available; the sentinel is ignored once false. */\n hasMore?: boolean\n /** True while the next page is loading — shows a spinner row and de-dupes calls. */\n loadingMore?: boolean\n /** Label beside the bottom spinner. */\n loadingMoreText?: string\n // --- server-driven pages (opt-in; providing `onPageChange` switches the table from\n // slicing rows itself to showing exactly the page it was handed, plus a numbered pager).\n //\n // For the screens that get AUDITED rather than skimmed: a timesheet, a punch log at year\n // end. Infinite scroll cannot say \"page 3 of 47\", cannot be sent to a colleague, and cannot\n // reach the end of a long list without dragging through all of it.\n //\n // `onLoadMore` wins if both are given - a table with two pagers is a bug that ships. ---\n /** Zero-based index of the page being shown. */\n pageIndex?: number\n /** How many pages exist. The table cannot work this out: only the server knows the total. */\n pageCount?: number\n /** Called with a zero-based page index. */\n onPageChange?: (index: number) => void\n /** Sizes offered in the selector. Omit to leave the size fixed and hide the control. */\n pageSizeOptions?: number[]\n /** Called with the chosen size. The caller is expected to return to page 1. */\n onPageSizeChange?: (size: number) => void\n /** Free text beside the pager, e.g. \"1-50 of 4,231\". lyra ships no copy and no number\n * formatting, so the caller builds it. */\n rangeLabel?: ReactNode\n /** Accessible labels for the pager buttons. */\n pageLabels?: { first?: string; previous?: string; next?: string; last?: string; size?: string }\n}\n\n/**\n * Which page numbers to show: always the first, the last, and a couple either side of the\n * current one, with `null` where a gap is elided.\n *\n * A pager that renders every page is unusable at forty pages and absurd at four hundred, and\n * one that renders only prev/next hides how long the list is - which is the question the\n * reader opened it with.\n */\nexport function pageWindow(index: number, count: number, radius = 1): (number | null)[] {\n if (count <= 0) return []\n const want = new Set<number>([0, count - 1])\n for (let p = index - radius; p <= index + radius; p++) {\n if (p >= 0 && p < count) want.add(p)\n }\n const sorted = [...want].sort((a, b) => a - b)\n const out: (number | null)[] = []\n let prev = -1\n for (const p of sorted) {\n // A single skipped page is rendered rather than elided: \"1 … 3\" is longer than \"1 2 3\"\n // and tells the reader less.\n if (prev >= 0 && p - prev === 2) out.push(p - 1)\n else if (prev >= 0 && p - prev > 2) out.push(null)\n out.push(p)\n prev = p\n }\n return out\n}\n\nexport function DataTable<TData, TValue>({\n columns,\n data,\n loading,\n emptyText = \"No results.\",\n empty,\n loadingText = \"Loading…\",\n previousLabel = \"Previous\",\n nextLabel = \"Next\",\n pageSize = 10,\n unpaged,\n className,\n searchable,\n searchPlaceholder = \"Search…\",\n onReload,\n reloadLabel = \"Reload\",\n toolbar,\n onLoadMore,\n hasMore,\n loadingMore,\n loadingMoreText = \"Loading more…\",\n pageIndex = 0,\n pageCount = 0,\n onPageChange,\n pageSizeOptions,\n onPageSizeChange,\n rangeLabel,\n pageLabels,\n}: DataTableProps<TData, TValue>) {\n const [globalFilter, setGlobalFilter] = useState(\"\")\n const infinite = Boolean(onLoadMore)\n // Server pages: the rows handed in ARE the page, so the table must not slice them again.\n const served = !infinite && Boolean(onPageChange)\n // Same for `unpaged`, for the opposite reason: there is only ever one page.\n const sliced = !infinite && !served && !unpaged\n const table = useReactTable({\n data,\n columns,\n state: { globalFilter },\n onGlobalFilterChange: setGlobalFilter,\n getCoreRowModel: getCoreRowModel(),\n getFilteredRowModel: getFilteredRowModel(),\n // Only the default finite table slices rows itself. The other three modes each hand the\n // table exactly what to render: infinite every accumulated row, served the one page the\n // server answered with, unpaged the whole list.\n ...(sliced\n ? { getPaginationRowModel: getPaginationRowModel(), initialState: { pagination: { pageSize } } }\n : {}),\n })\n\n const rows = table.getRowModel().rows\n const showBar = Boolean(searchable || onReload || toolbar)\n\n // --- infinite scroll: observe a bottom sentinel and ask the caller for more.\n // Latest callback/flags are kept in refs so the observer subscribes once. ---\n const sentinelRef = useRef<HTMLDivElement | null>(null)\n const onLoadMoreRef = useRef(onLoadMore)\n onLoadMoreRef.current = onLoadMore\n const flags = useRef({ hasMore, loadingMore })\n flags.current = { hasMore, loadingMore }\n const intersecting = useRef(false)\n\n const maybeLoadMore = useCallback(() => {\n if (intersecting.current && flags.current.hasMore && !flags.current.loadingMore) {\n onLoadMoreRef.current?.()\n }\n }, [])\n\n useEffect(() => {\n if (!infinite) return\n const el = sentinelRef.current\n if (!el) return\n const io = new IntersectionObserver(\n entries => {\n intersecting.current = entries[0]?.isIntersecting ?? false\n maybeLoadMore()\n },\n { rootMargin: \"200px\" },\n )\n io.observe(el)\n return () => {\n io.disconnect()\n }\n }, [infinite, maybeLoadMore])\n\n // A freshly loaded page can leave the sentinel still on-screen; the observer\n // won't re-fire on its own, so re-check whenever the rows or flags change.\n useEffect(() => {\n if (infinite) maybeLoadMore()\n }, [infinite, rows.length, loadingMore, hasMore, maybeLoadMore])\n\n return (\n <div>\n {showBar ? (\n <div className=\"flex items-center gap-2 pb-4\">\n {searchable ? (\n <div className=\"relative w-full max-w-xs\">\n <SearchIcon className=\"text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2\" />\n <Input\n value={globalFilter}\n onChange={e => table.setGlobalFilter(e.target.value)}\n placeholder={searchPlaceholder}\n className=\"pl-8\"\n />\n </div>\n ) : null}\n {onReload || toolbar ? (\n <div className=\"ml-auto flex items-center gap-2\">\n {toolbar}\n {onReload ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon-sm\"\n onClick={onReload}\n disabled={loading}\n aria-label={reloadLabel}\n title={reloadLabel}\n >\n <RefreshCwIcon className={cn(\"size-4\", loading && \"animate-spin\")} />\n </Button>\n ) : null}\n </div>\n ) : null}\n </div>\n ) : null}\n <div className={cn(\"overflow-hidden border\", className)}>\n <TableRaw>\n <TableHeaderRaw>\n {table.getHeaderGroups().map(hg => (\n <TableRowRaw key={hg.id}>\n {hg.headers.map(header => (\n <TableHeadRaw key={header.id}>\n {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}\n </TableHeadRaw>\n ))}\n </TableRowRaw>\n ))}\n </TableHeaderRaw>\n <TableBodyRaw>\n {loading && rows.length === 0 ? (\n // Only replace the body with a loading row on the FIRST load; on a\n // refetch keep the (stale) rows visible while the reload icon spins.\n <TableRowRaw>\n <TableCellRaw className=\"h-24 text-center\" colSpan={columns.length}>\n {loadingText}\n </TableCellRaw>\n </TableRowRaw>\n ) : rows.length ? (\n rows.map(row => (\n <TableRowRaw key={row.id} data-state={row.getIsSelected() && \"selected\"}>\n {row.getVisibleCells().map(cell => (\n <TableCellRaw key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</TableCellRaw>\n ))}\n </TableRowRaw>\n ))\n ) : (\n // An empty state is not a record, so the row drops the `hover:bg-muted/50` every\n // other row carries: lighting up under the pointer offers an interaction that is not\n // there, and it gets louder the taller the cell grows.\n <TableRowRaw className=\"hover:bg-transparent\">\n <TableCellRaw\n className={empty ? \"p-0\" : \"text-muted-foreground h-24 text-center\"}\n colSpan={columns.length}\n >\n {empty ?? emptyText}\n </TableCellRaw>\n </TableRowRaw>\n )}\n </TableBodyRaw>\n </TableRaw>\n {infinite && loadingMore ? (\n <div className=\"text-muted-foreground flex items-center justify-center gap-2 py-3 text-sm\">\n <Loader2Icon className=\"size-4 animate-spin\" />\n {loadingMoreText}\n </div>\n ) : null}\n {infinite ? <div ref={sentinelRef} aria-hidden className=\"h-px w-full\" /> : null}\n </div>\n {served ? (\n <div className=\"flex flex-wrap items-center justify-between gap-3 py-4\">\n <div className=\"text-muted-foreground flex items-center gap-3 text-sm\">\n {rangeLabel}\n {pageSizeOptions && pageSizeOptions.length > 0 ? (\n <NativeSelect\n aria-label={pageLabels?.size ?? \"Rows per page\"}\n className=\"h-8 w-auto\"\n value={String(pageSize)}\n onChange={e => onPageSizeChange?.(Number(e.target.value))}\n >\n {pageSizeOptions.map(n => (\n <NativeSelectOption key={n} value={String(n)}>\n {n}\n </NativeSelectOption>\n ))}\n </NativeSelect>\n ) : null}\n </div>\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n aria-label={pageLabels?.first ?? \"First page\"}\n onClick={() => onPageChange?.(0)}\n disabled={pageIndex <= 0}\n >\n «\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n aria-label={pageLabels?.previous ?? previousLabel}\n onClick={() => onPageChange?.(pageIndex - 1)}\n disabled={pageIndex <= 0}\n >\n ‹\n </Button>\n {pageWindow(pageIndex, pageCount).map((p, i) =>\n p === null ? (\n <span key={`gap${i}`} className=\"text-muted-foreground px-1 text-sm\">\n …\n </span>\n ) : (\n <Button\n key={p}\n variant={p === pageIndex ? \"default\" : \"outline\"}\n size=\"sm\"\n aria-current={p === pageIndex ? \"page\" : undefined}\n onClick={() => onPageChange?.(p)}\n >\n {p + 1}\n </Button>\n ),\n )}\n <Button\n variant=\"outline\"\n size=\"sm\"\n aria-label={pageLabels?.next ?? nextLabel}\n onClick={() => onPageChange?.(pageIndex + 1)}\n disabled={pageIndex >= pageCount - 1}\n >\n ›\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n aria-label={pageLabels?.last ?? \"Last page\"}\n onClick={() => onPageChange?.(pageCount - 1)}\n disabled={pageIndex >= pageCount - 1}\n >\n »\n </Button>\n </div>\n </div>\n ) : null}\n {sliced && table.getPageCount() > 1 ? (\n <div className=\"flex items-center justify-end gap-2 py-4\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()}>\n {previousLabel}\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={() => table.nextPage()} disabled={!table.getCanNextPage()}>\n {nextLabel}\n </Button>\n </div>\n ) : null}\n </div>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/data-table/filter-rows.ts","../../src/data-table/filter-bar.tsx","../../src/data-table/use-pager.ts","../../src/data-table/index.tsx"],"names":["useState","useRef","useEffect","jsx","TableHead","TableHeader","useCallback","jsxs","TableCell","TableRow","TableBody","SearchIcon","RefreshCwIcon"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Case-insensitive search over chosen fields, for a list the server does NOT page.\n *\n * Honest only there: when the fetch is the whole answer, filtering here searches everything\n * the reader could have seen. On a server-paged list the same code searches one page and then\n * reports \"no results\" about rows it never had - which is why those lists send the query to\n * the server instead.\n *\n * Takes a field selector rather than key names so a derived value (a joined label, a formatted\n * code) is searchable too - on the tables this exists for, the derived column is usually the\n * one people type into the box.\n */\nexport function filterRows<T>(\n rows: T[],\n q: string,\n fields: (r: T) => (string | undefined | null)[],\n): T[] {\n const needle = q.trim().toLowerCase()\n if (!needle) return rows\n return rows.filter(r => fields(r).some(v => v?.toLowerCase().includes(needle)))\n}\n","\"use client\"\n\nimport { RefreshCwIcon, SearchIcon, XIcon } from \"lucide-react\"\nimport type { ReactNode } from \"react\"\nimport { useEffect, useRef, useState } from \"react\"\nimport { Button } from \"~/ui/primitives/button.js\"\nimport { Input } from \"~/ui/primitives/input.js\"\nimport { cn } from \"~/utils/cn.js\"\n\n/**\n * The row above a table: narrowing on the left, actions on the right.\n *\n * This REPLACES `DataTable`'s own bar (`searchable` + `toolbar` + `onReload`) rather than\n * sitting beside it. Two bars on one table is how a screen ends up with two search boxes that\n * search different things, and `searchable` in particular must stay off on a server-paged\n * table: it filters the rows already in the browser, so it answers \"no results\" about a row\n * that is on page three.\n */\nexport function TableFilterBar({\n children,\n actions,\n className,\n}: {\n children?: ReactNode\n actions?: ReactNode\n className?: string\n}) {\n return (\n <div className={cn(\"flex flex-wrap items-center gap-2\", className)}>\n {children}\n {actions ? <div className=\"ml-auto flex items-center gap-2\">{actions}</div> : null}\n </div>\n )\n}\n\n/**\n * A search box that reports the settled value, debounced here so no call site has to remember.\n *\n * 300ms, because on a server-paged list every keystroke is both a round trip and a new query\n * key - and a new query key throws away the page already loaded.\n */\nexport function TableSearch({\n onChange,\n value = \"\",\n placeholder = \"Search…\",\n className,\n icon = true,\n}: {\n /** Called with the trimmed value once typing settles. */\n onChange: (q: string) => void\n /** The query the screen holds, such as one in the URL. Changing it from outside resets the box. */\n value?: string\n placeholder?: string\n className?: string\n /** Set false where the surrounding bar already says what the box is for. */\n icon?: boolean\n}) {\n const [text, setText] = useState(value)\n // Only a changed query is reported: echoing the opening one would send a URL-backed page back\n // to page 1 on every load.\n const [sent, setSent] = useState(value.trim())\n const [held, setHeld] = useState(value)\n if (value !== held) {\n setHeld(value)\n if (value.trim() !== sent) {\n setSent(value.trim())\n setText(value)\n }\n }\n // Held in a ref, written from an effect: a caller passing an inline arrow would otherwise\n // restart the timer on every render, so the request would only fire once the user stopped\n // typing AND the parent stopped re-rendering.\n const sink = useRef(onChange)\n useEffect(() => {\n sink.current = onChange\n }, [onChange])\n useEffect(() => {\n const id = setTimeout(() => {\n const q = text.trim()\n if (q === sent) return\n setSent(q)\n sink.current(q)\n }, 300)\n return () => clearTimeout(id)\n }, [text, sent])\n\n return (\n <div className={cn(\"relative w-full max-w-xs\", className)}>\n {icon ? (\n <SearchIcon className=\"text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2\" />\n ) : null}\n <Input\n value={text}\n onChange={e => setText(e.target.value)}\n placeholder={placeholder}\n className={icon ? \"pl-8\" : undefined}\n />\n </div>\n )\n}\n\n/** Refetch, spinning while it does. Belongs in the filter bar, not in DataTable's own bar. */\nexport function TableReload({\n onClick,\n loading,\n label = \"Reload\",\n}: {\n onClick: () => void\n loading?: boolean\n label?: string\n}) {\n return (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon-sm\"\n onClick={onClick}\n disabled={loading}\n aria-label={label}\n title={label}\n >\n <RefreshCwIcon className={cn(\"size-4\", loading && \"animate-spin\")} />\n </Button>\n )\n}\n\nexport type TableFilterChip = { key: string; label: ReactNode; onClear: () => void }\n\n/**\n * The filters in force, each with its own clear. A link can open a table already narrowed, and\n * without these \"3 results\" reads as \"there are only 3\".\n */\nexport function TableFilterChips({\n chips,\n clearLabel = \"Clear filters\",\n onClearAll,\n removeLabel = \"Remove filter\",\n}: {\n chips: TableFilterChip[]\n clearLabel?: string\n onClearAll?: () => void\n removeLabel?: string\n}) {\n if (chips.length === 0) return null\n return (\n <div className=\"flex flex-wrap items-center gap-2\">\n {chips.map(c => (\n <span key={c.key} className=\"bg-muted inline-flex h-7 items-center gap-1 border pr-1 pl-2 text-xs\">\n {c.label}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n // Several chips share the button text, so a screen reader needs to hear which one.\n aria-label={typeof c.label === \"string\" ? `${removeLabel}: ${c.label}` : removeLabel}\n title={removeLabel}\n onClick={c.onClear}\n >\n <XIcon />\n </Button>\n </span>\n ))}\n {onClearAll && chips.length > 1 ? (\n <Button type=\"button\" variant=\"link\" size=\"sm\" className=\"h-7 px-1\" onClick={onClearAll}>\n {clearLabel}\n </Button>\n ) : null}\n </div>\n )\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\n/**\n * The state behind `DataTable`'s numbered pager, so a screen configures paging instead of\n * owning it.\n *\n * Every console that grew a numbered table had written the same twelve lines - page state,\n * size state, the 0-based/1-based conversion, the range string, the back-to-page-1 wrapper -\n * and the copies had already drifted into pagers that behaved slightly differently on screens\n * next to each other. `DataTable` owned the controls; nothing owned the wiring.\n *\n * Two shapes, because the lists behind these tables are not all the same:\n *\n * `bind(total)` the SERVER paged it. Feed `page`/`size` into the request and hand back the\n * `total` it answered with.\n * `slice(rows)` the fetch IS the whole answer - a list derived from two endpoints, the\n * result of one operation, a parsed file - so the page is cut here.\n *\n * `slice` is honest only in that second case. Over a list the server truncated it reports a\n * page count that does not exist, which tells the reader they have seen everything.\n */\n\n/** Sizes offered in the rows-per-page selector. */\nexport const PAGE_SIZE_OPTIONS = [25, 50, 100, 200];\n\n/** Rows per page before anyone chooses. Smaller than a screenful is churn; larger buries the pager. */\nexport const DEFAULT_PAGE_SIZE = 25;\n\n/**\n * Copy for the pager. lyra ships none of it: `range` also decides number formatting, which is\n * a locale decision the app's i18n already makes.\n */\nexport type PagerLabels = {\n first?: string;\n previous?: string;\n next?: string;\n last?: string;\n size?: string;\n range?: (r: { from: number; to: number; total: number }) => string;\n};\n\n/** The `DataTable` props that make up the numbered pager. Spread them; never spell them out. */\nexport type PagerProps = {\n pageIndex: number;\n pageCount: number;\n onPageChange: (index: number) => void;\n pageSize: number;\n pageSizeOptions: number[];\n onPageSizeChange: (size: number) => void;\n rangeLabel: string;\n pageLabels: {\n first?: string;\n previous?: string;\n next?: string;\n last?: string;\n size?: string;\n };\n};\n\nexport type UsePagerOptions = {\n pageSize?: number;\n pageSizeOptions?: number[];\n labels?: PagerLabels;\n /** The 1-based page, when the caller keeps it (the URL, via `usePageParam` in `shell`). */\n page?: number;\n onPageChange?: (page: number) => void;\n};\n\nexport function usePager(options: UsePagerOptions = {}) {\n const {\n pageSize: initialSize = DEFAULT_PAGE_SIZE,\n pageSizeOptions = PAGE_SIZE_OPTIONS,\n labels,\n page: heldPage,\n onPageChange,\n } = options;\n const [ownPage, setOwnPage] = useState(1);\n const [size, setSize] = useState(initialSize);\n const page = heldPage ?? ownPage;\n const setPage = onPageChange ?? setOwnPage;\n // The last total `bind` or `slice` saw. A page past its end, such as an old link to `?page=9`,\n // is moved to the last page so the caller requests rows that exist. A ref, not state: `bind`\n // may run while a child renders, where setting this component's state is an error.\n const seen = useRef(0);\n useEffect(() => {\n const last = Math.ceil(seen.current / size);\n if (last > 0 && page > last) setPage(last);\n });\n\n const props = useCallback(\n (total: number): PagerProps => {\n // The REPORTED page is clamped, exactly as `slice` clamps the rows it cuts. A set that\n // shrank under the current page - somebody deleted the last row on page 3, or another\n // operator did - otherwise leaves `pageIndex` past `pageCount`, and then `pageWindow`\n // highlights nothing at all: next and last are disabled, no button is marked current,\n // and the reader has to work out from an empty table that they are off the end.\n //\n // Only when `total > 0`, because a caller whose query has not answered yet passes 0, and\n // clamping then would snap page 3 back to page 1 on every single page change. Pass\n // react-query's `placeholderData: keepPreviousData` and the total stays real across a\n // page change, which is what makes this engage when it should.\n //\n // The rows of this render are still the empty answer for the old page; the effect above\n // moves the page itself, so the clamp only spares one frame of a broken pager.\n seen.current = total;\n const pageCount = Math.max(1, Math.ceil(total / size));\n const at = total > 0 ? Math.min(page, pageCount) : page;\n return {\n pageIndex: at - 1,\n pageCount,\n onPageChange: (i) => setPage(i + 1),\n pageSize: size,\n pageSizeOptions,\n // A different size means a different page 1; staying on page 7 of the old size lands\n // past the end of the new one.\n onPageSizeChange: (n) => {\n setSize(n);\n setPage(1);\n },\n rangeLabel:\n total > 0\n ? (labels?.range?.({\n from: (at - 1) * size + 1,\n to: Math.min(at * size, total),\n total,\n }) ?? \"\")\n : \"\",\n pageLabels: {\n first: labels?.first,\n previous: labels?.previous,\n next: labels?.next,\n last: labels?.last,\n size: labels?.size,\n },\n };\n },\n [page, setPage, size, pageSizeOptions, labels],\n );\n\n /**\n * Wrap a filter setter so changing it returns to page 1.\n *\n * In the HANDLER, not in an effect watching the filter: an effect renders the stale page\n * once and fires a request for it, so narrowing to a department with two people flashes\n * \"no results\" on every keystroke before correcting itself.\n */\n const onFilter = useCallback(\n <T>(apply: (v: T) => void) =>\n (v: T) => {\n apply(v);\n setPage(1);\n },\n [setPage],\n );\n\n return {\n page,\n size,\n setPage,\n onFilter,\n bind: props,\n /** Cut the current page out of a complete list. */\n slice<T>(rows: T[]): { rows: T[]; props: PagerProps } {\n // A list that shrank under a filter can leave `page` past the end, which renders an\n // empty table that reads as \"nothing matched\". The slice is clamped here and the state is\n // moved by the effect above, since writing state while rendering is the loop this replaced.\n const last = Math.max(1, Math.ceil(rows.length / size));\n const at = Math.min(page, last);\n return {\n rows: rows.slice((at - 1) * size, at * size),\n props: { ...props(rows.length), pageIndex: at - 1 },\n };\n },\n };\n}\n","import type { Cell, Column, ColumnDef, Header, HeaderGroup, Row, SortingState, Table } from \"@tanstack/react-table\"\nimport { flexRender, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from \"@tanstack/react-table\"\nimport { ArrowDownIcon, ArrowUpIcon, ChevronsUpDownIcon, Loader2Icon, RefreshCwIcon, SearchIcon } from \"lucide-react\"\nimport type { HTMLAttributes, KeyboardEvent, MouseEvent, ReactNode } from \"react\"\nimport { createContext, memo, useCallback, useContext, useEffect, useRef, useState } from \"react\"\nimport { Button } from \"~/ui/primitives/button.js\"\nimport { Checkbox } from \"~/ui/primitives/checkbox.js\"\nimport { Input } from \"~/ui/primitives/input.js\"\nimport { NativeSelect, NativeSelectOption } from \"~/ui/primitives/native-select.js\"\nimport { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from \"~/ui/primitives/dropdown-menu.js\"\nimport {\n TableBody as TableBodyRaw,\n TableCell as TableCellRaw,\n TableHeader as TableHeaderRaw,\n TableHead as TableHeadRaw,\n Table as TableRaw,\n TableRow as TableRowRaw,\n} from \"~/ui/primitives/table.js\"\nimport { cn } from \"~/utils/cn.js\"\n\nexport type { ColumnDef } from \"@tanstack/react-table\"\n\nexport { filterRows } from \"./filter-rows.js\"\nexport { TableFilterBar, TableFilterChips, TableReload, TableSearch, type TableFilterChip } from \"./filter-bar.js\"\nexport {\n DEFAULT_PAGE_SIZE,\n PAGE_SIZE_OPTIONS,\n usePager,\n type PagerLabels,\n type PagerProps,\n type UsePagerOptions,\n} from \"./use-pager.js\"\n\n\nexport const TableContext = createContext<{\n data: unknown[]\n columns: ColumnDef<unknown, unknown>[]\n table: Table<unknown> | null\n}>({\n data: [],\n columns: [],\n table: null,\n})\n\nexport type TableProviderProps<TData, TValue> = {\n columns: ColumnDef<TData, TValue>[]\n data: TData[]\n children: ReactNode\n className?: string\n}\n\nexport function TableProvider<TData, TValue>({ columns, data, children, className }: TableProviderProps<TData, TValue>) {\n const [sorting, setSorting] = useState<SortingState>([])\n const table = useReactTable({\n data,\n columns,\n getCoreRowModel: getCoreRowModel(),\n getSortedRowModel: getSortedRowModel(),\n // useState's setter is itself a valid TanStack OnChangeFn (it accepts a\n // value or an updater fn), so it can be wired directly — type-safe, no cast.\n onSortingChange: setSorting,\n state: {\n sorting,\n },\n })\n\n return (\n <TableContext.Provider\n value={{\n data,\n columns: columns as never,\n table: table as never,\n }}\n >\n <TableRaw className={className}>{children}</TableRaw>\n </TableContext.Provider>\n )\n}\n\nexport type TableHeadProps = {\n header: Header<unknown, unknown>\n className?: string\n}\n\nexport const TableHead = memo(({ header, className }: TableHeadProps) => (\n <TableHeadRaw className={className} key={header.id}>\n {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}\n </TableHeadRaw>\n))\n\nTableHead.displayName = \"TableHead\"\n\nexport type TableHeaderGroupProps = {\n headerGroup: HeaderGroup<unknown>\n children: (props: { header: Header<unknown, unknown> }) => ReactNode\n}\n\nexport const TableHeaderGroup = ({ headerGroup, children }: TableHeaderGroupProps) => (\n <TableRowRaw key={headerGroup.id}>{headerGroup.headers.map(header => children({ header }))}</TableRowRaw>\n)\n\nexport type TableHeaderProps = {\n className?: string\n children: (props: { headerGroup: HeaderGroup<unknown> }) => ReactNode\n}\n\nexport const TableHeader = ({ className, children }: TableHeaderProps) => {\n const { table } = useContext(TableContext)\n\n return <TableHeaderRaw className={className}>{table?.getHeaderGroups().map(headerGroup => children({ headerGroup }))}</TableHeaderRaw>\n}\n\nexport interface TableColumnHeaderProps<TData, TValue> extends HTMLAttributes<HTMLDivElement> {\n column: Column<TData, TValue>\n title: string\n}\n\nexport function TableColumnHeader<TData, TValue>({ column, title, className }: TableColumnHeaderProps<TData, TValue>) {\n // Extract inline event handlers to prevent unnecessary re-renders\n const handleSortAsc = useCallback(() => {\n column.toggleSorting(false)\n }, [column])\n\n const handleSortDesc = useCallback(() => {\n column.toggleSorting(true)\n }, [column])\n\n if (!column.getCanSort()) {\n return <div className={cn(className)}>{title}</div>\n }\n\n return (\n <div className={cn(\"flex items-center space-x-2\", className)}>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button className=\"-ml-3 h-8 data-[state=open]:bg-accent\" size=\"sm\" variant=\"ghost\">\n <span>{title}</span>\n {column.getIsSorted() === \"desc\" ? (\n <ArrowDownIcon className=\"ml-2 h-4 w-4\" />\n ) : column.getIsSorted() === \"asc\" ? (\n <ArrowUpIcon className=\"ml-2 h-4 w-4\" />\n ) : (\n <ChevronsUpDownIcon className=\"ml-2 h-4 w-4\" />\n )}\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\">\n <DropdownMenuItem onClick={handleSortAsc}>\n <ArrowUpIcon className=\"mr-2 h-3.5 w-3.5 text-muted-foreground/70\" />\n Asc\n </DropdownMenuItem>\n <DropdownMenuItem onClick={handleSortDesc}>\n <ArrowDownIcon className=\"mr-2 h-3.5 w-3.5 text-muted-foreground/70\" />\n Desc\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n )\n}\n\nexport type TableCellProps = {\n cell: Cell<unknown, unknown>\n className?: string\n}\n\nexport const TableCell = ({ cell, className }: TableCellProps) => (\n <TableCellRaw className={className}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</TableCellRaw>\n)\n\nexport type TableRowProps = {\n row: Row<unknown>\n children: (props: { cell: Cell<unknown, unknown> }) => ReactNode\n className?: string\n}\n\nexport const TableRow = ({ row, children, className }: TableRowProps) => (\n <TableRowRaw className={className} data-state={row.getIsSelected() && \"selected\"} key={row.id}>\n {row.getVisibleCells().map(cell => children({ cell }))}\n </TableRowRaw>\n)\n\nexport type TableBodyProps = {\n children: (props: { row: Row<unknown> }) => ReactNode\n className?: string\n}\n\nexport const TableBody = ({ children, className }: TableBodyProps) => {\n const { columns, table } = useContext(TableContext)\n const rows = table?.getRowModel().rows\n\n return (\n <TableBodyRaw className={className}>\n {rows?.length ? (\n rows.map(row => children({ row }))\n ) : (\n <TableRowRaw>\n <TableCellRaw className=\"h-24 text-center\" colSpan={columns.length}>\n No results.\n </TableCellRaw>\n </TableRowRaw>\n )}\n </TableBodyRaw>\n )\n}\n\n// ---------------------------------------------------------------------------\n// DataTable — a batteries-included, self-contained paginated table. Unlike the\n// composition primitives above (TableProvider + render-prop parts), this is a\n// drop-in: pass `columns` + `data` and get header, body, empty/loading states\n// and Prev/Next pagination. All user-facing labels are props (English\n// defaults) so apps can feed i18n strings; the component itself ships no copy.\n// ---------------------------------------------------------------------------\n\ninterface TableProps<TData, TValue> {\n columns: ColumnDef<TData, TValue>[]\n data: TData[]\n /** Show a single-row loading state instead of the body. */\n loading?: boolean\n /** Row shown when there are no results. */\n emptyText?: string\n /**\n * What fills the body when there are no results, in place of `emptyText`.\n *\n * A centred grey line reads the same as a request that failed, and on a table it is the only\n * thing a reader has to tell \"nobody matched your filter\" from \"this screen is broken\". Takes\n * `EmptyState` from `@tradalab/lyra/ui`, or anything else. `emptyText` stays because most tables\n * want one sentence and nothing more.\n */\n empty?: ReactNode\n /** Row shown while `loading`. */\n loadingText?: string\n /** Previous-page button label. */\n previousLabel?: string\n /** Next-page button label. */\n nextLabel?: string\n /** Rows per page (default 10). Pagination controls appear only when needed. */\n pageSize?: number\n /**\n * Render every row and no pager at all.\n *\n * For the list whose row count does not grow with tenants or with time - a declared\n * inventory, a fixed catalogue, the handful of providers somebody configured by hand. There\n * the fetch is the whole answer, so a pager is a control nobody needs and a page count is a\n * number nobody asked for. Opt-in rather than the default, because the default (slice by\n * `pageSize`, Prev/Next) is what eleven existing call sites already rely on.\n */\n unpaged?: boolean\n /** Extra classes on the bordered table wrapper. */\n className?: string\n // --- control bar (all optional; the bar renders only when one is set) ---\n /** Show a search box that globally filters rows across all accessor columns. */\n searchable?: boolean\n /** Placeholder for the search box. */\n searchPlaceholder?: string\n /** Show a reload button; called on click. Its icon spins while `loading`. */\n onReload?: () => void\n /** Accessible label / tooltip for the reload button. */\n reloadLabel?: string\n /** Custom controls rendered on the right of the bar (e.g. a \"New\" button). */\n toolbar?: ReactNode\n // --- infinite scroll (opt-in; providing `onLoadMore` switches the table from\n // Prev/Next pagination to rendering every accumulated row and requesting more\n // as a bottom sentinel scrolls into view). Data fetching stays with the caller\n // (e.g. react-query useInfiniteQuery); the table only owns the sentinel. ---\n /** Called when the bottom sentinel becomes visible and more rows can load. */\n onLoadMore?: () => void\n /** Whether another page is available; the sentinel is ignored once false. */\n hasMore?: boolean\n /** True while the next page is loading — shows a spinner row and de-dupes calls. */\n loadingMore?: boolean\n /** Label beside the bottom spinner. */\n loadingMoreText?: string\n // --- server-driven pages (opt-in; providing `onPageChange` switches the table from\n // slicing rows itself to showing exactly the page it was handed, plus a numbered pager).\n //\n // For the screens that get AUDITED rather than skimmed: a timesheet, a punch log at year\n // end. Infinite scroll cannot say \"page 3 of 47\", cannot be sent to a colleague, and cannot\n // reach the end of a long list without dragging through all of it.\n //\n // `onLoadMore` wins if both are given - a table with two pagers is a bug that ships. ---\n /** Zero-based index of the page being shown. */\n pageIndex?: number\n /** How many pages exist. The table cannot work this out: only the server knows the total. */\n pageCount?: number\n /** Called with a zero-based page index. */\n onPageChange?: (index: number) => void\n /** Sizes offered in the selector. Omit to leave the size fixed and hide the control. */\n pageSizeOptions?: number[]\n /** Called with the chosen size. The caller is expected to return to page 1. */\n onPageSizeChange?: (size: number) => void\n /** Free text beside the pager, e.g. \"1-50 of 4,231\". lyra ships no copy and no number\n * formatting, so the caller builds it. */\n rangeLabel?: ReactNode\n /** Accessible labels for the pager buttons. */\n pageLabels?: { first?: string; previous?: string; next?: string; last?: string; size?: string }\n /** Open a row: the whole row clicks, and Enter or Space opens it from the keyboard. */\n onRowClick?: (row: TData) => void\n /** Accessible labels for the selection checkboxes. */\n selectLabels?: { rows?: string; row?: string }\n}\n\n// Selection is opt-in: `onSelectedChange` adds a checkbox column. The header box covers the rows\n// on screen only: a server-paged table never holds the rest, so \"every row that matches\" is a\n// separate action that sends the filter, not a longer list of ids. Without `getRowId` the ids\n// would be row indexes, which name a different row on every page, so the type demands it.\ntype RowSelection<TData> =\n | {\n /** Stable id of a row; selected ids are kept across pages. */\n getRowId: (row: TData) => string\n selected?: readonly string[]\n onSelectedChange?: (ids: string[]) => void\n }\n | { getRowId?: (row: TData) => string; selected?: undefined; onSelectedChange?: undefined }\n\nexport type DataTableProps<TData, TValue> = TableProps<TData, TValue> & RowSelection<TData>\n\n// A click on a control inside the row is that control's, not a request to open the row.\nconst INTERACTIVE =\n \"a,button,input,select,textarea,label,summary,[contenteditable=true],[role=button],[role=link],[role=checkbox],[role=switch],[role=menuitem],[role=tab]\"\n\nfunction opensRow(e: MouseEvent<HTMLElement> | KeyboardEvent<HTMLElement>) {\n const row = e.currentTarget\n const target = e.target as Element\n // React bubbles events from a portal (a row's menu or confirm dialog) through to the row.\n if (!row.contains(target)) return false\n if (target === row) return true\n const hit = target.closest(INTERACTIVE)\n if (hit !== null && row.contains(hit)) return false\n // A drag that selected text, such as copying a URL out of a cell, is not a click on the row.\n return e.type !== \"click\" || !window.getSelection()?.toString()\n}\n\n/**\n * Which page numbers to show: always the first, the last, and a couple either side of the\n * current one, with `null` where a gap is elided.\n *\n * A pager that renders every page is unusable at forty pages and absurd at four hundred, and\n * one that renders only prev/next hides how long the list is - which is the question the\n * reader opened it with.\n */\nexport function pageWindow(index: number, count: number, radius = 1): (number | null)[] {\n if (count <= 0) return []\n const want = new Set<number>([0, count - 1])\n for (let p = index - radius; p <= index + radius; p++) {\n if (p >= 0 && p < count) want.add(p)\n }\n const sorted = [...want].sort((a, b) => a - b)\n const out: (number | null)[] = []\n let prev = -1\n for (const p of sorted) {\n // A single skipped page is rendered rather than elided: \"1 … 3\" is longer than \"1 2 3\"\n // and tells the reader less.\n if (prev >= 0 && p - prev === 2) out.push(p - 1)\n else if (prev >= 0 && p - prev > 2) out.push(null)\n out.push(p)\n prev = p\n }\n return out\n}\n\nexport function DataTable<TData, TValue>({\n columns,\n data,\n loading,\n emptyText = \"No results.\",\n empty,\n loadingText = \"Loading…\",\n previousLabel = \"Previous\",\n nextLabel = \"Next\",\n pageSize = 10,\n unpaged,\n className,\n searchable,\n searchPlaceholder = \"Search…\",\n onReload,\n reloadLabel = \"Reload\",\n toolbar,\n onLoadMore,\n hasMore,\n loadingMore,\n loadingMoreText = \"Loading more…\",\n pageIndex = 0,\n pageCount = 0,\n onPageChange,\n pageSizeOptions,\n onPageSizeChange,\n rangeLabel,\n pageLabels,\n onRowClick,\n getRowId,\n selected = [],\n onSelectedChange,\n selectLabels,\n}: DataTableProps<TData, TValue>) {\n const [globalFilter, setGlobalFilter] = useState(\"\")\n const selecting = Boolean(onSelectedChange && getRowId)\n const picked = new Set(selected)\n const infinite = Boolean(onLoadMore)\n // Server pages: the rows handed in ARE the page, so the table must not slice them again.\n const served = !infinite && Boolean(onPageChange)\n // Same for `unpaged`, for the opposite reason: there is only ever one page.\n const sliced = !infinite && !served && !unpaged\n const table = useReactTable({\n data,\n columns,\n getRowId: getRowId ? (row: TData) => getRowId(row) : undefined,\n state: { globalFilter },\n onGlobalFilterChange: setGlobalFilter,\n getCoreRowModel: getCoreRowModel(),\n getFilteredRowModel: getFilteredRowModel(),\n // Only the default finite table slices rows itself. The other three modes each hand the\n // table exactly what to render: infinite every accumulated row, served the one page the\n // server answered with, unpaged the whole list.\n ...(sliced\n ? { getPaginationRowModel: getPaginationRowModel(), initialState: { pagination: { pageSize } } }\n : {}),\n })\n\n const rows = table.getRowModel().rows\n const span = columns.length + (selecting ? 1 : 0)\n const shown = rows.map(r => r.id)\n const shownPicked = shown.filter(id => picked.has(id)).length\n const toggle = (ids: string[], on: boolean) => {\n const next = new Set(picked)\n for (const id of ids) {\n if (on) next.add(id)\n else next.delete(id)\n }\n onSelectedChange?.([...next])\n }\n const showBar = Boolean(searchable || onReload || toolbar)\n\n // --- infinite scroll: observe a bottom sentinel and ask the caller for more.\n // Latest callback/flags are kept in refs so the observer subscribes once. ---\n const sentinelRef = useRef<HTMLDivElement | null>(null)\n const onLoadMoreRef = useRef(onLoadMore)\n onLoadMoreRef.current = onLoadMore\n const flags = useRef({ hasMore, loadingMore })\n flags.current = { hasMore, loadingMore }\n const intersecting = useRef(false)\n\n const maybeLoadMore = useCallback(() => {\n if (intersecting.current && flags.current.hasMore && !flags.current.loadingMore) {\n onLoadMoreRef.current?.()\n }\n }, [])\n\n useEffect(() => {\n if (!infinite) return\n const el = sentinelRef.current\n if (!el) return\n const io = new IntersectionObserver(\n entries => {\n intersecting.current = entries[0]?.isIntersecting ?? false\n maybeLoadMore()\n },\n { rootMargin: \"200px\" },\n )\n io.observe(el)\n return () => {\n io.disconnect()\n }\n }, [infinite, maybeLoadMore])\n\n // A freshly loaded page can leave the sentinel still on-screen; the observer\n // won't re-fire on its own, so re-check whenever the rows or flags change.\n useEffect(() => {\n if (infinite) maybeLoadMore()\n }, [infinite, rows.length, loadingMore, hasMore, maybeLoadMore])\n\n return (\n <div>\n {showBar ? (\n <div className=\"flex items-center gap-2 pb-4\">\n {searchable ? (\n <div className=\"relative w-full max-w-xs\">\n <SearchIcon className=\"text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2\" />\n <Input\n value={globalFilter}\n onChange={e => table.setGlobalFilter(e.target.value)}\n placeholder={searchPlaceholder}\n className=\"pl-8\"\n />\n </div>\n ) : null}\n {onReload || toolbar ? (\n <div className=\"ml-auto flex items-center gap-2\">\n {toolbar}\n {onReload ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon-sm\"\n onClick={onReload}\n disabled={loading}\n aria-label={reloadLabel}\n title={reloadLabel}\n >\n <RefreshCwIcon className={cn(\"size-4\", loading && \"animate-spin\")} />\n </Button>\n ) : null}\n </div>\n ) : null}\n </div>\n ) : null}\n <div className={cn(\"overflow-hidden border\", className)}>\n <TableRaw>\n <TableHeaderRaw>\n {table.getHeaderGroups().map(hg => (\n <TableRowRaw key={hg.id}>\n {selecting ? (\n <TableHeadRaw className=\"w-8\">\n <Checkbox\n aria-label={selectLabels?.rows ?? \"Select rows on this page\"}\n checked={shown.length > 0 && shownPicked === shown.length ? true : shownPicked > 0 ? \"indeterminate\" : false}\n disabled={shown.length === 0}\n onCheckedChange={v => toggle(shown, v === true)}\n />\n </TableHeadRaw>\n ) : null}\n {hg.headers.map(header => (\n <TableHeadRaw key={header.id}>\n {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}\n </TableHeadRaw>\n ))}\n </TableRowRaw>\n ))}\n </TableHeaderRaw>\n <TableBodyRaw>\n {loading && rows.length === 0 ? (\n // Only replace the body with a loading row on the FIRST load; on a\n // refetch keep the (stale) rows visible while the reload icon spins.\n <TableRowRaw>\n <TableCellRaw className=\"h-24 text-center\" colSpan={span}>\n {loadingText}\n </TableCellRaw>\n </TableRowRaw>\n ) : rows.length ? (\n rows.map(row => (\n <TableRowRaw\n key={row.id}\n data-state={picked.has(row.id) ? \"selected\" : undefined}\n className={\n onRowClick\n ? \"focus-visible:outline-ring cursor-pointer focus-visible:outline-2 focus-visible:-outline-offset-2\"\n : undefined\n }\n tabIndex={onRowClick ? 0 : undefined}\n onClick={onRowClick ? e => opensRow(e) && onRowClick(row.original) : undefined}\n onKeyDown={\n onRowClick\n ? e => {\n if ((e.key === \"Enter\" || e.key === \" \") && opensRow(e)) {\n e.preventDefault()\n onRowClick(row.original)\n }\n }\n : undefined\n }\n >\n {selecting ? (\n // The cell is a wide target around a small box; a near miss must not open the row.\n <TableCellRaw className=\"w-8\" onClick={e => e.stopPropagation()}>\n <Checkbox\n aria-label={selectLabels?.row ?? \"Select row\"}\n checked={picked.has(row.id)}\n onCheckedChange={v => toggle([row.id], v === true)}\n />\n </TableCellRaw>\n ) : null}\n {row.getVisibleCells().map(cell => (\n <TableCellRaw key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</TableCellRaw>\n ))}\n </TableRowRaw>\n ))\n ) : (\n // An empty state is not a record, so the row drops the `hover:bg-muted/50` every\n // other row carries: lighting up under the pointer offers an interaction that is not\n // there, and it gets louder the taller the cell grows.\n <TableRowRaw className=\"hover:bg-transparent\">\n <TableCellRaw\n className={empty ? \"p-0\" : \"text-muted-foreground h-24 text-center\"}\n colSpan={span}\n >\n {empty ?? emptyText}\n </TableCellRaw>\n </TableRowRaw>\n )}\n </TableBodyRaw>\n </TableRaw>\n {infinite && loadingMore ? (\n <div className=\"text-muted-foreground flex items-center justify-center gap-2 py-3 text-sm\">\n <Loader2Icon className=\"size-4 animate-spin\" />\n {loadingMoreText}\n </div>\n ) : null}\n {infinite ? <div ref={sentinelRef} aria-hidden className=\"h-px w-full\" /> : null}\n </div>\n {served ? (\n <div className=\"flex flex-wrap items-center justify-between gap-3 py-4\">\n <div className=\"text-muted-foreground flex items-center gap-3 text-sm\">\n {rangeLabel}\n {pageSizeOptions && pageSizeOptions.length > 0 ? (\n <NativeSelect\n aria-label={pageLabels?.size ?? \"Rows per page\"}\n className=\"h-8 w-auto\"\n value={String(pageSize)}\n onChange={e => onPageSizeChange?.(Number(e.target.value))}\n >\n {pageSizeOptions.map(n => (\n <NativeSelectOption key={n} value={String(n)}>\n {n}\n </NativeSelectOption>\n ))}\n </NativeSelect>\n ) : null}\n </div>\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n aria-label={pageLabels?.first ?? \"First page\"}\n onClick={() => onPageChange?.(0)}\n disabled={pageIndex <= 0}\n >\n «\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n aria-label={pageLabels?.previous ?? previousLabel}\n onClick={() => onPageChange?.(pageIndex - 1)}\n disabled={pageIndex <= 0}\n >\n ‹\n </Button>\n {pageWindow(pageIndex, pageCount).map((p, i) =>\n p === null ? (\n <span key={`gap${i}`} className=\"text-muted-foreground px-1 text-sm\">\n …\n </span>\n ) : (\n <Button\n key={p}\n variant={p === pageIndex ? \"default\" : \"outline\"}\n size=\"sm\"\n aria-current={p === pageIndex ? \"page\" : undefined}\n onClick={() => onPageChange?.(p)}\n >\n {p + 1}\n </Button>\n ),\n )}\n <Button\n variant=\"outline\"\n size=\"sm\"\n aria-label={pageLabels?.next ?? nextLabel}\n onClick={() => onPageChange?.(pageIndex + 1)}\n disabled={pageIndex >= pageCount - 1}\n >\n ›\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n aria-label={pageLabels?.last ?? \"Last page\"}\n onClick={() => onPageChange?.(pageCount - 1)}\n disabled={pageIndex >= pageCount - 1}\n >\n »\n </Button>\n </div>\n </div>\n ) : null}\n {sliced && table.getPageCount() > 1 ? (\n <div className=\"flex items-center justify-end gap-2 py-4\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()}>\n {previousLabel}\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={() => table.nextPage()} disabled={!table.getCanNextPage()}>\n {nextLabel}\n </Button>\n </div>\n ) : null}\n </div>\n )\n}\n"]}
|
package/dist/shell/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { ComponentProps, ReactNode } from 'react';
|
|
3
|
-
import { S as SidebarProvider, a as SidebarInset, b as Sidebar, A as AccountMenuUser, c as AccountMenuLabels, B as Breadcrumb } from '../account-menu-
|
|
3
|
+
import { S as SidebarProvider, a as SidebarInset, b as Sidebar, A as AccountMenuUser, c as AccountMenuLabels, B as Breadcrumb, T as Tabs } from '../account-menu-q2VMEk3b.js';
|
|
4
4
|
import { a as PlatePickerProps } from '../plate-picker-CXxUGyNz.js';
|
|
5
5
|
import 'class-variance-authority/types';
|
|
6
6
|
import 'class-variance-authority';
|
|
@@ -627,4 +627,25 @@ interface AppGlyphProps {
|
|
|
627
627
|
/** An app's mark: its image when `icon` is a URL that loads, else a name monogram. */
|
|
628
628
|
declare function AppGlyph({ icon, name, className, ownOrigin }: AppGlyphProps): React.JSX.Element;
|
|
629
629
|
|
|
630
|
-
|
|
630
|
+
/**
|
|
631
|
+
* One query parameter as state, so a reload or a shared link keeps it. The fallback leaves the
|
|
632
|
+
* parameter out of the URL. Like any `useSearchParams` caller, a statically rendered page must
|
|
633
|
+
* wrap it in `<Suspense>` or `next build` fails.
|
|
634
|
+
*/
|
|
635
|
+
declare function useSearchParam(key: string, fallback?: string): readonly [string, (next: string) => void];
|
|
636
|
+
/** The open tab of one tab group at `?<group>=<tab>`; a tab the group lacks falls back. */
|
|
637
|
+
declare function useTabParam<T extends string>(group: string, tabs: readonly T[], fallback: T): readonly [T, (next: string) => void];
|
|
638
|
+
/** A 1-based page at `?<key>=`, shaped to spread into `usePager` from `data-table`. */
|
|
639
|
+
declare function usePageParam(key?: string): {
|
|
640
|
+
page: number;
|
|
641
|
+
onPageChange: (p: number) => void;
|
|
642
|
+
};
|
|
643
|
+
type UrlTabsProps = Omit<ComponentProps<typeof Tabs>, "value" | "defaultValue" | "onValueChange"> & {
|
|
644
|
+
group: string;
|
|
645
|
+
tabs: readonly string[];
|
|
646
|
+
defaultValue: string;
|
|
647
|
+
};
|
|
648
|
+
/** `Tabs` with the open tab in the URL, for a server page that cannot hold the state itself. */
|
|
649
|
+
declare function UrlTabs({ group, tabs, defaultValue, ...props }: UrlTabsProps): React.JSX.Element;
|
|
650
|
+
|
|
651
|
+
export { AppGlyph, type AppGlyphProps, AppGrid, type AppGridGroup, type AppGridItem, type AppGridProps, AppShell, AppShellBody, AppShellHeader, AppShellMain, type AppShellProps, AppSwitcher, type AppSwitcherApp, type AppSwitcherProps, type AppSwitcherSection, AppTile, BottomNav, type BottomNavItem, type BottomNavProps, Breadcrumbs, type BreadcrumbsProps, type LeadingCrumb, type MatchMode, NavBrand, type NavBrandApp, type NavBrandGroup, type NavBrandProps, NavGroup, type NavGroupProps, type NavIcon, NavItem, type NavItemProps, NavSection, type NavSectionProps, NavUser, type NavUserProps, type NavUserThemeLabels, PageHeader, type PageHeaderProps, type PageMeasure, PageShell, type PageShellProps, SidebarDock, SidebarPanel, StackedSidebar, TabBar, type TabBarLabels, type TabBarProps, type TabItem, UrlTabs, type UrlTabsProps, type UseActiveRoute, defaultAppGridLabels, defaultNavBrandAppsLabel, defaultNavUserThemeLabels, defaultTabBarLabels, isIconUrl, useActiveRoute, usePageParam, useSearchParam, useTabParam };
|