@godxjp/ui 14.0.1 → 15.0.1

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.
@@ -1,11 +1,45 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
- import { ArrowDown, ArrowUp, ChevronsUpDown, Layers, Layers2, MoreHorizontal } from "lucide-react";
3
+ import {
4
+ flexRender,
5
+ getCoreRowModel,
6
+ getFilteredRowModel,
7
+ getPaginationRowModel,
8
+ getSortedRowModel,
9
+ useReactTable
10
+ } from "@tanstack/react-table";
11
+ import {
12
+ ArrowDown,
13
+ ArrowUp,
14
+ ChevronLeft,
15
+ ChevronRight,
16
+ ChevronsUpDown,
17
+ Layers,
18
+ Layers2,
19
+ MoreHorizontal,
20
+ SlidersHorizontal
21
+ } from "lucide-react";
4
22
  import { useTranslation } from "../../i18n/use-translation";
5
23
  import { Flex } from "../layout/flex";
6
24
  import { Button } from "../general/button";
7
25
  import { EmptyState } from "./empty-state";
8
26
  import { Checkbox } from "../data-entry/checkbox";
27
+ import { SearchInput } from "../data-entry/search-input";
28
+ import {
29
+ Select,
30
+ SelectContent,
31
+ SelectItem,
32
+ SelectTrigger,
33
+ SelectValue
34
+ } from "../data-entry/select";
35
+ import {
36
+ DropdownMenu,
37
+ DropdownMenuCheckboxItem,
38
+ DropdownMenuContent,
39
+ DropdownMenuLabel,
40
+ DropdownMenuSeparator,
41
+ DropdownMenuTrigger
42
+ } from "../navigation/dropdown-menu";
9
43
  import {
10
44
  Table,
11
45
  TableBody,
@@ -21,6 +55,17 @@ import {
21
55
  tableCellPaddingClass,
22
56
  tableRowHeightClass
23
57
  } from "../../lib/control-styles";
58
+ function toTanstackColumns(columns) {
59
+ return columns.map((col) => ({
60
+ id: col.key,
61
+ accessorFn: (row) => row[col.key],
62
+ header: () => col.header,
63
+ enableSorting: !!col.sortable,
64
+ enableHiding: col.enableHiding ?? true,
65
+ enableGlobalFilter: true,
66
+ meta: { lean: col }
67
+ }));
68
+ }
24
69
  const DataTableContext = React.createContext(null);
25
70
  function useDataTableContext() {
26
71
  const ctx = React.useContext(DataTableContext);
@@ -36,6 +81,13 @@ const noopGetRowId = (row) => {
36
81
  if (typeof id === "number") return String(id);
37
82
  return "";
38
83
  };
84
+ function sortToSortingState(sort) {
85
+ return sort ? [{ id: sort.key, desc: sort.direction === "desc" }] : [];
86
+ }
87
+ function sortingStateToSort(state) {
88
+ const first = state[0];
89
+ return first ? { key: first.id, direction: first.desc ? "desc" : "asc" } : void 0;
90
+ }
39
91
  function DataTable({
40
92
  data,
41
93
  columns,
@@ -48,6 +100,16 @@ function DataTable({
48
100
  onDensityChange,
49
101
  sort,
50
102
  onSortChange,
103
+ globalFilter: controlledGlobalFilter,
104
+ onGlobalFilterChange,
105
+ pagination: controlledPagination,
106
+ onPaginationChange,
107
+ rowCount,
108
+ columnVisibility: controlledVisibility,
109
+ onColumnVisibilityChange,
110
+ manualSorting = false,
111
+ manualFiltering = false,
112
+ manualPagination = false,
51
113
  loading = false,
52
114
  empty,
53
115
  striped = false,
@@ -63,43 +125,87 @@ function DataTable({
63
125
  setInternalDensity(d);
64
126
  onDensityChange?.(d);
65
127
  };
66
- const [internalSelected, setInternalSelected] = React.useState(/* @__PURE__ */ new Set());
67
- const selected = controlledSelected ?? internalSelected;
68
- const setSelected = (next) => {
69
- setInternalSelected(next);
70
- onSelectChange?.(next);
128
+ const [internalSorting, setInternalSorting] = React.useState([]);
129
+ const [internalFilters] = React.useState([]);
130
+ const [internalGlobal, setInternalGlobal] = React.useState("");
131
+ const [internalPagination, setInternalPagination] = React.useState({
132
+ pageIndex: 0,
133
+ pageSize: 10
134
+ });
135
+ const [internalSelection, setInternalSelection] = React.useState({});
136
+ const [internalVisibility, setInternalVisibility] = React.useState({});
137
+ const selectionFromSet = React.useCallback(
138
+ (set) => Object.fromEntries([...set].map((id) => [id, true])),
139
+ []
140
+ );
141
+ const sortingState = sort !== void 0 ? sortToSortingState(sort) : internalSorting;
142
+ const rowSelection = controlledSelected !== void 0 ? selectionFromSet(controlledSelected) : internalSelection;
143
+ const onSortingChange = (updater) => {
144
+ const next = typeof updater === "function" ? updater(sortingState) : updater;
145
+ if (sort !== void 0 || onSortChange) {
146
+ onSortChange?.(sortingStateToSort(next));
147
+ } else {
148
+ setInternalSorting(next);
149
+ }
71
150
  };
72
- const toggleSelect = (id) => {
73
- const next = new Set(selected);
74
- if (next.has(id)) next.delete(id);
75
- else next.add(id);
76
- setSelected(next);
151
+ const onRowSelectionChange = (updater) => {
152
+ const next = typeof updater === "function" ? updater(rowSelection) : updater;
153
+ if (controlledSelected !== void 0 || onSelectChange) {
154
+ const set = new Set(Object.keys(next).filter((id) => next[id]));
155
+ onSelectChange?.(set);
156
+ if (controlledSelected === void 0) setInternalSelection(next);
157
+ } else {
158
+ setInternalSelection(next);
159
+ }
77
160
  };
78
- const emptyColSpan = columns.length + (selectable ? 1 : 0);
79
- const allSelected = data.length > 0 && data.every((r) => selected.has(getRowId(r)));
80
- const someSelected = !allSelected && data.some((r) => selected.has(getRowId(r)));
81
- const toggleSelectAll = () => {
82
- if (allSelected) setSelected(/* @__PURE__ */ new Set());
83
- else setSelected(new Set(data.map(getRowId)));
161
+ const onGlobalFilterChangeFn = (updater) => {
162
+ const current = controlledGlobalFilter ?? internalGlobal;
163
+ const next = typeof updater === "function" ? updater(current) : updater;
164
+ if (controlledGlobalFilter !== void 0 || onGlobalFilterChange) {
165
+ onGlobalFilterChange?.(next);
166
+ if (controlledGlobalFilter === void 0) setInternalGlobal(next);
167
+ } else {
168
+ setInternalGlobal(next);
169
+ }
84
170
  };
85
- const ctx = {
171
+ const tanstackColumns = React.useMemo(() => toTanstackColumns(columns), [columns]);
172
+ const table = useReactTable({
86
173
  data,
87
- columns,
174
+ columns: tanstackColumns,
175
+ getRowId,
176
+ getCoreRowModel: getCoreRowModel(),
177
+ ...manualSorting ? {} : { getSortedRowModel: getSortedRowModel() },
178
+ ...manualFiltering ? {} : { getFilteredRowModel: getFilteredRowModel() },
179
+ ...manualPagination ? {} : { getPaginationRowModel: getPaginationRowModel() },
180
+ manualSorting,
181
+ manualFiltering,
182
+ manualPagination,
183
+ rowCount,
184
+ enableRowSelection: selectable,
185
+ state: {
186
+ sorting: sortingState,
187
+ columnFilters: internalFilters,
188
+ globalFilter: controlledGlobalFilter ?? internalGlobal,
189
+ pagination: controlledPagination ?? internalPagination,
190
+ rowSelection,
191
+ columnVisibility: controlledVisibility ?? internalVisibility
192
+ },
193
+ onSortingChange,
194
+ onGlobalFilterChange: onGlobalFilterChangeFn,
195
+ onPaginationChange: onPaginationChange ?? setInternalPagination,
196
+ onRowSelectionChange,
197
+ onColumnVisibilityChange: onColumnVisibilityChange ?? setInternalVisibility
198
+ });
199
+ const ctx = {
200
+ table,
88
201
  density,
89
202
  setDensity,
90
- selected,
91
- toggleSelect,
92
- toggleSelectAll,
93
- allSelected,
94
- someSelected,
95
203
  selectable,
96
- getRowId,
97
- onRowClick,
98
204
  sort,
99
205
  onSortChange,
206
+ onRowClick,
100
207
  loading,
101
208
  empty,
102
- emptyColSpan,
103
209
  striped,
104
210
  hoverable,
105
211
  stickyHeader,
@@ -117,18 +223,82 @@ DataTable.Toolbar = function DataTableToolbar({
117
223
  children,
118
224
  className
119
225
  }) {
120
- return /* @__PURE__ */ jsx("div", { className: cn("ui-data-table-toolbar", className), children });
226
+ return /* @__PURE__ */ jsx(
227
+ Flex,
228
+ {
229
+ direction: "row",
230
+ align: "center",
231
+ justify: "between",
232
+ gap: "sm",
233
+ wrap: true,
234
+ className: cn("ui-data-table-toolbar", className),
235
+ children
236
+ }
237
+ );
121
238
  };
122
239
  DataTable.Toolbar.displayName = "DataTable.Toolbar";
240
+ DataTable.Search = function DataTableSearch({
241
+ placeholder,
242
+ className
243
+ }) {
244
+ const { table } = useDataTableContext();
245
+ const { t } = useTranslation();
246
+ const value = table.getState().globalFilter ?? "";
247
+ return /* @__PURE__ */ jsx(
248
+ SearchInput,
249
+ {
250
+ value,
251
+ onValueChange: (q) => table.setGlobalFilter(q),
252
+ onSearch: (q) => table.setGlobalFilter(q),
253
+ placeholder: placeholder ?? t("dataGrid.searchPlaceholder"),
254
+ ariaLabel: t("dataGrid.search"),
255
+ className
256
+ }
257
+ );
258
+ };
259
+ DataTable.Search.displayName = "DataTable.Search";
260
+ DataTable.ViewOptions = function DataTableViewOptions({ className }) {
261
+ const { table } = useDataTableContext();
262
+ const { t } = useTranslation();
263
+ const hideable = table.getAllLeafColumns().filter((c) => c.getCanHide());
264
+ if (hideable.length === 0) return null;
265
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
266
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className, children: [
267
+ /* @__PURE__ */ jsx(SlidersHorizontal, { className: "size-4 shrink-0", "aria-hidden": "true" }),
268
+ t("dataGrid.view")
269
+ ] }) }),
270
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", children: [
271
+ /* @__PURE__ */ jsx(DropdownMenuLabel, { children: t("dataGrid.toggleColumns") }),
272
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
273
+ hideable.map((column) => /* @__PURE__ */ jsx(
274
+ DropdownMenuCheckboxItem,
275
+ {
276
+ checked: column.getIsVisible(),
277
+ onCheckedChange: (v) => column.toggleVisibility(!!v),
278
+ children: columnLabel(column)
279
+ },
280
+ column.id
281
+ ))
282
+ ] })
283
+ ] });
284
+ };
285
+ DataTable.ViewOptions.displayName = "DataTable.ViewOptions";
286
+ function columnLabel(column) {
287
+ const header = column.columnDef.meta?.lean?.header;
288
+ if (typeof header === "string" && header.length > 0) return header;
289
+ return column.id;
290
+ }
123
291
  DataTable.SelectAll = function DataTableSelectAll() {
124
- const { allSelected, someSelected, toggleSelectAll, selectable } = useDataTableContext();
292
+ const { table, selectable } = useDataTableContext();
125
293
  const { t } = useTranslation();
126
294
  if (!selectable) return null;
295
+ const allSelected = table.getIsAllPageRowsSelected();
296
+ const someSelected = table.getIsSomePageRowsSelected();
127
297
  return /* @__PURE__ */ jsx(
128
298
  Checkbox,
129
299
  {
130
300
  checked: allSelected ? true : someSelected ? "indeterminate" : false,
131
- onCheckedChange: toggleSelectAll,
301
+ onCheckedChange: (v) => table.toggleAllPageRowsSelected(!!v),
132
302
  "aria-label": t("dataTable.selectAll")
133
303
  }
134
304
  );
@@ -141,8 +311,23 @@ DataTable.BulkActions = function DataTableBulkActions({
141
311
  }) {
142
312
  const ctx = useOptionalDataTableContext();
143
313
  const { t } = useTranslation();
144
- const c = count ?? ctx?.selected.size ?? 0;
314
+ const selectedCount = ctx?.table.getSelectedRowModel().rows.length ?? 0;
315
+ const c = count ?? selectedCount;
145
316
  if (c === 0) return null;
317
+ if (typeof children === "function") {
318
+ return /* @__PURE__ */ jsx(
319
+ Flex,
320
+ {
321
+ direction: "row",
322
+ align: "center",
323
+ gap: "sm",
324
+ role: "region",
325
+ "aria-label": t("dataTable.bulkActions"),
326
+ className,
327
+ children: children(c)
328
+ }
329
+ );
330
+ }
146
331
  return /* @__PURE__ */ jsxs(
147
332
  "div",
148
333
  {
@@ -182,19 +367,13 @@ DataTable.DensityToggle = function DataTableDensityToggle() {
182
367
  DataTable.DensityToggle.displayName = "DataTable.DensityToggle";
183
368
  DataTable.Content = function DataTableContent() {
184
369
  const {
185
- data,
186
- columns,
187
- density: _density,
370
+ table,
188
371
  selectable,
189
- selected,
190
- toggleSelect,
191
- getRowId,
192
- onRowClick,
193
372
  sort,
194
373
  onSortChange,
374
+ onRowClick,
195
375
  loading,
196
376
  empty,
197
- emptyColSpan,
198
377
  striped,
199
378
  hoverable,
200
379
  stickyHeader,
@@ -203,17 +382,27 @@ DataTable.Content = function DataTableContent() {
203
382
  const { t } = useTranslation();
204
383
  const rowPadding = tableRowHeightClass;
205
384
  const cellPadding = tableCellPaddingClass;
206
- const hasPinEnd = columns.some((col) => col.pin === "end");
385
+ const visibleColumns = table.getVisibleLeafColumns().map((c) => c.columnDef.meta?.lean).filter((c) => !!c);
386
+ const emptyColSpan = visibleColumns.length + (selectable ? 1 : 0);
387
+ const hasPinEnd = visibleColumns.some((col) => col.pin === "end");
388
+ const activeSort = sort ?? sortingStateToSort(table.getState().sorting);
389
+ const isControlledSort = sort !== void 0 || !!onSortChange;
207
390
  const onHeaderClick = (col) => {
208
- if (!col.sortable || !onSortChange) return;
209
- if (sort?.key !== col.key) {
210
- onSortChange({ key: col.key, direction: "asc" });
211
- } else if (sort.direction === "asc") {
212
- onSortChange({ key: col.key, direction: "desc" });
213
- } else {
214
- onSortChange(void 0);
391
+ if (!col.sortable) return;
392
+ if (isControlledSort) {
393
+ if (activeSort?.key !== col.key) {
394
+ onSortChange?.({ key: col.key, direction: "asc" });
395
+ } else if (activeSort.direction === "asc") {
396
+ onSortChange?.({ key: col.key, direction: "desc" });
397
+ } else {
398
+ onSortChange?.(void 0);
399
+ }
400
+ return;
215
401
  }
402
+ table.getColumn(col.key)?.toggleSorting();
216
403
  };
404
+ const dataRows = table.getRowModel().rows;
405
+ const rowCount = dataRows.length;
217
406
  return /* @__PURE__ */ jsx(
218
407
  "div",
219
408
  {
@@ -228,10 +417,10 @@ DataTable.Content = function DataTableContent() {
228
417
  children: /* @__PURE__ */ jsxs(Table, { children: [
229
418
  /* @__PURE__ */ jsx(TableHeader, { className: cn("bg-secondary", stickyHeader && "sticky top-0 z-10"), children: /* @__PURE__ */ jsxs(TableRow, { children: [
230
419
  selectable && /* @__PURE__ */ jsx(TableHead, { className: "w-10", children: /* @__PURE__ */ jsx(DataTable.SelectAll, {}) }),
231
- columns.map((col) => {
232
- const isSortable = !!col.sortable && !!onSortChange;
233
- const isActiveSort = isSortable && sort?.key === col.key;
234
- const sortIndicator = isSortable ? isActiveSort ? sort?.direction === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "size-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ArrowDown, { className: "size-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronsUpDown, { className: "text-muted-foreground size-3", "aria-hidden": "true" }) : null;
420
+ visibleColumns.map((col) => {
421
+ const isSortable = !!col.sortable;
422
+ const isActiveSort = isSortable && activeSort?.key === col.key;
423
+ const sortIndicator = isSortable ? isActiveSort ? activeSort?.direction === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "size-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ArrowDown, { className: "size-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronsUpDown, { className: "text-muted-foreground size-3", "aria-hidden": "true" }) : null;
235
424
  const label = /* @__PURE__ */ jsxs("span", { className: "ui-data-table-sort-label", children: [
236
425
  col.header,
237
426
  sortIndicator
@@ -240,7 +429,7 @@ DataTable.Content = function DataTableContent() {
240
429
  TableHead,
241
430
  {
242
431
  "data-empty": !col.header || void 0,
243
- "aria-sort": isSortable ? isActiveSort ? sort?.direction === "asc" ? "ascending" : "descending" : "none" : void 0,
432
+ "aria-sort": isSortable ? isActiveSort ? activeSort?.direction === "asc" ? "ascending" : "descending" : "none" : void 0,
244
433
  className: cn(
245
434
  col.width,
246
435
  col.align === "right" && "text-end",
@@ -270,9 +459,9 @@ DataTable.Content = function DataTableContent() {
270
459
  // share its borders + column widths — no second framed container
271
460
  // (which double-borders when the table sits in a Card). Count is
272
461
  // bounded to the previous page so the height barely shifts.
273
- Array.from({ length: Math.min(Math.max(data.length, 6), 10) }).map((_, i) => /* @__PURE__ */ jsxs(TableRow, { className: cn(rowPadding, "hover:bg-transparent"), children: [
462
+ Array.from({ length: Math.min(Math.max(rowCount, 6), 10) }).map((_, i) => /* @__PURE__ */ jsxs(TableRow, { className: cn(rowPadding, "hover:bg-transparent"), children: [
274
463
  selectable && /* @__PURE__ */ jsx(TableCell, { className: cellPadding, children: /* @__PURE__ */ jsx("div", { className: "ui-skeleton-block size-4 rounded-sm" }) }),
275
- columns.map((col, j) => /* @__PURE__ */ jsx(
464
+ visibleColumns.map((col, j) => /* @__PURE__ */ jsx(
276
465
  TableCell,
277
466
  {
278
467
  className: cn(
@@ -298,7 +487,7 @@ DataTable.Content = function DataTableContent() {
298
487
  col.key
299
488
  ))
300
489
  ] }, `skeleton-${i}`))
301
- ) : data.length === 0 ? /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx(
490
+ ) : rowCount === 0 ? /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx(
302
491
  TableCell,
303
492
  {
304
493
  colSpan: emptyColSpan,
@@ -306,10 +495,12 @@ DataTable.Content = function DataTableContent() {
306
495
  "aria-live": "polite",
307
496
  children: empty ?? /* @__PURE__ */ jsx(EmptyState, { title: t("dataTable.empty") })
308
497
  }
309
- ) }) : data.map((row) => {
310
- const id = getRowId(row);
311
- const isSelected = selected.has(id);
312
- const isInteractiveTarget = (target) => !!target.closest("button, a, input, select, textarea, [role=menuitem]");
498
+ ) }) : dataRows.map((row) => {
499
+ const original = row.original;
500
+ const isSelected = row.getIsSelected();
501
+ const isInteractiveTarget = (target) => !!target.closest(
502
+ "button, a, input, select, textarea, [role=menuitem], [role=checkbox]"
503
+ );
313
504
  return /* @__PURE__ */ jsxs(
314
505
  TableRow,
315
506
  {
@@ -318,13 +509,13 @@ DataTable.Content = function DataTableContent() {
318
509
  onClick: (e) => {
319
510
  const target = e.target;
320
511
  if (isInteractiveTarget(target)) return;
321
- onRowClick?.(row);
512
+ onRowClick?.(original);
322
513
  },
323
514
  onKeyDown: onRowClick ? (e) => {
324
515
  if (e.key !== "Enter" && e.key !== " ") return;
325
516
  if (e.target !== e.currentTarget) return;
326
517
  e.preventDefault();
327
- onRowClick?.(row);
518
+ onRowClick?.(original);
328
519
  } : void 0,
329
520
  className: cn(
330
521
  rowPadding,
@@ -333,23 +524,23 @@ DataTable.Content = function DataTableContent() {
333
524
  // …but the affordance (cursor + focus ring) only when clickable.
334
525
  onRowClick && "focus-visible:ring-ring cursor-pointer focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-inset",
335
526
  isSelected && "bg-muted/30",
336
- rowClassName?.(row)
527
+ rowClassName?.(original)
337
528
  ),
338
529
  children: [
339
530
  selectable && /* @__PURE__ */ jsx(TableCell, { className: cellPadding, children: /* @__PURE__ */ jsx(
340
531
  Checkbox,
341
532
  {
342
533
  checked: isSelected,
343
- onCheckedChange: () => {
344
- toggleSelect(id);
534
+ onCheckedChange: (v) => {
535
+ row.toggleSelected(!!v);
345
536
  },
346
- "aria-label": t("dataTable.selectRow", { id }),
537
+ "aria-label": t("dataTable.selectRow", { id: row.id }),
347
538
  onClick: (e) => {
348
539
  e.stopPropagation();
349
540
  }
350
541
  }
351
542
  ) }),
352
- columns.map((col) => /* @__PURE__ */ jsx(
543
+ visibleColumns.map((col) => /* @__PURE__ */ jsx(
353
544
  TableCell,
354
545
  {
355
546
  className: cn(
@@ -360,8 +551,8 @@ DataTable.Content = function DataTableContent() {
360
551
  col.hiddenOnMobile && "hidden md:table-cell",
361
552
  col.pin === "end" && "ui-data-table-pin-end"
362
553
  ),
363
- children: col.render ? col.render(row) : (() => {
364
- const v = row[col.key];
554
+ children: col.render ? col.render(original) : (() => {
555
+ const v = original[col.key];
365
556
  if (v == null) return "\u2014";
366
557
  if (typeof v === "string" || typeof v === "number") return String(v);
367
558
  return "\u2014";
@@ -371,7 +562,7 @@ DataTable.Content = function DataTableContent() {
371
562
  ))
372
563
  ]
373
564
  },
374
- id
565
+ row.id
375
566
  );
376
567
  }) })
377
568
  ] })
@@ -381,12 +572,14 @@ DataTable.Content = function DataTableContent() {
381
572
  );
382
573
  };
383
574
  DataTable.Content.displayName = "DataTable.Content";
384
- DataTable.Pagination = function DataTablePagination({
385
- cursor,
386
- hasMore,
387
- onChange,
388
- className
389
- }) {
575
+ DataTable.Pagination = function DataTablePagination(props) {
576
+ if ("onChange" in props && typeof props.onChange === "function") {
577
+ return /* @__PURE__ */ jsx(CursorPagination, { ...props });
578
+ }
579
+ return /* @__PURE__ */ jsx(NumberedPagination, { ...props });
580
+ };
581
+ DataTable.Pagination.displayName = "DataTable.Pagination";
582
+ function CursorPagination({ cursor, hasMore, onChange, className }) {
390
583
  const { t } = useTranslation();
391
584
  return /* @__PURE__ */ jsxs("div", { className: cn("ui-data-table-pagination", className), children: [
392
585
  /* @__PURE__ */ jsx(
@@ -414,8 +607,68 @@ DataTable.Pagination = function DataTablePagination({
414
607
  }
415
608
  )
416
609
  ] });
417
- };
418
- DataTable.Pagination.displayName = "DataTable.Pagination";
610
+ }
611
+ function NumberedPagination({
612
+ pageSizeOptions = [10, 20, 50, 100],
613
+ className
614
+ }) {
615
+ const { table } = useDataTableContext();
616
+ const { t } = useTranslation();
617
+ const { pageIndex, pageSize } = table.getState().pagination;
618
+ const pageCount = table.getPageCount();
619
+ return /* @__PURE__ */ jsxs(
620
+ Flex,
621
+ {
622
+ direction: "row",
623
+ align: "center",
624
+ justify: "between",
625
+ gap: "md",
626
+ wrap: true,
627
+ className: cn("ui-data-table-pagination", className),
628
+ children: [
629
+ /* @__PURE__ */ jsxs(Flex, { direction: "row", align: "center", gap: "sm", children: [
630
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm", children: t("dataGrid.rowsPerPage") }),
631
+ /* @__PURE__ */ jsxs(
632
+ Select,
633
+ {
634
+ value: String(pageSize),
635
+ onValueChange: (v) => table.setPageSize(Number(v)),
636
+ children: [
637
+ /* @__PURE__ */ jsx(SelectTrigger, { size: "sm", "aria-label": t("dataGrid.rowsPerPage"), className: "tabular-nums", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
638
+ /* @__PURE__ */ jsx(SelectContent, { children: pageSizeOptions.map((n) => /* @__PURE__ */ jsx(SelectItem, { value: String(n), className: "tabular-nums", children: n }, n)) })
639
+ ]
640
+ }
641
+ )
642
+ ] }),
643
+ /* @__PURE__ */ jsxs(Flex, { direction: "row", align: "center", gap: "sm", children: [
644
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm tabular-nums", children: t("dataGrid.pageOf", { page: pageIndex + 1, total: Math.max(1, pageCount) }) }),
645
+ /* @__PURE__ */ jsx(
646
+ Button,
647
+ {
648
+ variant: "outline",
649
+ size: "icon",
650
+ disabled: !table.getCanPreviousPage(),
651
+ onClick: () => table.previousPage(),
652
+ "aria-label": t("common.previous") ?? "Previous",
653
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4", "aria-hidden": "true" })
654
+ }
655
+ ),
656
+ /* @__PURE__ */ jsx(
657
+ Button,
658
+ {
659
+ variant: "outline",
660
+ size: "icon",
661
+ disabled: !table.getCanNextPage(),
662
+ onClick: () => table.nextPage(),
663
+ "aria-label": t("common.next") ?? "Next",
664
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4", "aria-hidden": "true" })
665
+ }
666
+ )
667
+ ] })
668
+ ]
669
+ }
670
+ );
671
+ }
419
672
  DataTable.RowActions = function DataTableRowActions({ ariaLabel, children }) {
420
673
  const { t } = useTranslation();
421
674
  return /* @__PURE__ */ jsxs(
@@ -434,5 +687,6 @@ DataTable.RowActions = function DataTableRowActions({ ariaLabel, children }) {
434
687
  };
435
688
  DataTable.RowActions.displayName = "DataTable.RowActions";
436
689
  export {
437
- DataTable
690
+ DataTable,
691
+ flexRender
438
692
  };
@@ -7,7 +7,7 @@ export { Card, CardBar, CardContent, CardCover, CardDescription, CardFooter, Car
7
7
  export type { StatCardProps, CardBarProps } from "./card";
8
8
  export { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table";
9
9
  export { Descriptions } from "./descriptions";
10
- export { DataTable } from "./data-table";
10
+ export { DataTable, flexRender } from "./data-table";
11
11
  export type { ColumnDef, Density as TableDensity } from "./data-table";
12
12
  export type { SortStateProp } from "../../props/vocabulary";
13
13
  export { EmptyState } from "./empty-state";
@@ -15,7 +15,7 @@ import {
15
15
  } from "./card";
16
16
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table";
17
17
  import { Descriptions } from "./descriptions";
18
- import { DataTable } from "./data-table";
18
+ import { DataTable, flexRender } from "./data-table";
19
19
  import { EmptyState } from "./empty-state";
20
20
  import { Progress } from "./progress";
21
21
  import { TreeList } from "./tree-list";
@@ -95,5 +95,6 @@ export {
95
95
  TableRow,
96
96
  Timeline,
97
97
  TreeList,
98
+ flexRender,
98
99
  useCarousel
99
100
  };
@@ -6,7 +6,8 @@ interface SearchInputProps {
6
6
  debounce?: number;
7
7
  /** Fires on EVERY keystroke (immediate) — required to keep a controlled `value` responsive. */
8
8
  onValueChange?: (q: string) => void;
9
- onSearch: (q: string) => void;
9
+ /** Fires with the DEBOUNCED term. Optional — omit it when you drive filtering off `onValueChange`. */
10
+ onSearch?: (q: string) => void;
10
11
  label?: React.ReactNode;
11
12
  ariaLabel?: string;
12
13
  className?: string;
@@ -34,7 +34,7 @@ function SearchInput({
34
34
  onSearchRef.current = onSearch;
35
35
  });
36
36
  React.useEffect(() => {
37
- onSearchRef.current(debounced);
37
+ onSearchRef.current?.(debounced);
38
38
  }, [debounced]);
39
39
  const setValue = (v) => {
40
40
  if (!isControlled) setInternal(v);
@@ -2,5 +2,3 @@ export { Button, buttonVariants } from "./button";
2
2
  export type { ButtonProps } from "./button";
3
3
  export { Text, Heading } from "./typography";
4
4
  export type { TextProps, HeadingProps, TextProp, HeadingProp } from "./typography";
5
- export { Logo, logoVariants } from "./logo";
6
- export type { LogoProps } from "./logo";
@@ -1,11 +1,8 @@
1
1
  import { Button, buttonVariants } from "./button";
2
2
  import { Text, Heading } from "./typography";
3
- import { Logo, logoVariants } from "./logo";
4
3
  export {
5
4
  Button,
6
5
  Heading,
7
- Logo,
8
6
  Text,
9
- buttonVariants,
10
- logoVariants
7
+ buttonVariants
11
8
  };