@ngrok/mantle 0.70.2 → 0.71.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/dist/alert-dialog.d.ts +337 -90
- package/dist/alert-dialog.js.map +1 -1
- package/dist/alert.js.map +1 -1
- package/dist/anchor-2stEauOz.js.map +1 -1
- package/dist/anchor.d.ts +45 -4
- package/dist/badge.d.ts +32 -3
- package/dist/badge.js.map +1 -1
- package/dist/{button-BMgAxAwM.d.ts → button-Bq0x5Pv4.d.ts} +4 -4
- package/dist/button.d.ts +1 -1
- package/dist/checkbox.d.ts +1 -1
- package/dist/code-block.d.ts +1 -1
- package/dist/code-block_highlight-utils.d.ts +1 -1
- package/dist/code.d.ts +22 -1
- package/dist/code.js.map +1 -1
- package/dist/combobox.d.ts +10 -0
- package/dist/combobox.js.map +1 -1
- package/dist/command.js.map +1 -1
- package/dist/copy-to-clipboard-DjOD_Mwb.js.map +1 -1
- package/dist/data-table.d.ts +303 -22
- package/dist/data-table.js.map +1 -1
- package/dist/dialog-BHzl9eye.js.map +1 -1
- package/dist/dialog.d.ts +6 -1
- package/dist/flag.d.ts +33 -4
- package/dist/flag.js.map +1 -1
- package/dist/hooks.d.ts +299 -74
- package/dist/hooks.js.map +1 -1
- package/dist/hover-card.d.ts +15 -10
- package/dist/hover-card.js.map +1 -1
- package/dist/icons.js.map +1 -1
- package/dist/{index-DMAkXvFI.d.ts → index-C91lxoX9.d.ts} +55 -12
- package/dist/input.d.ts +1 -1
- package/dist/input.js.map +1 -1
- package/dist/kbd-CAVUiqBT.js.map +1 -1
- package/dist/kbd.d.ts +37 -8
- package/dist/label.d.ts +40 -9
- package/dist/label.js.map +1 -1
- package/dist/media-object.d.ts +26 -10
- package/dist/media-object.js.map +1 -1
- package/dist/multi-select.d.ts +185 -34
- package/dist/multi-select.js.map +1 -1
- package/dist/otp-input.d.ts +167 -0
- package/dist/otp-input.js +2 -0
- package/dist/otp-input.js.map +1 -0
- package/dist/pagination.d.ts +1 -1
- package/dist/pagination.js.map +1 -1
- package/dist/popover.d.ts +7 -5
- package/dist/popover.js.map +1 -1
- package/dist/primitive-tXm_8n_t.js.map +1 -1
- package/dist/progress.js.map +1 -1
- package/dist/resolve-pre-rendered-props-C-kiaLHj.js.map +1 -1
- package/dist/{resolve-pre-rendered-props-x-52gvQ1.d.ts → resolve-pre-rendered-props-CNUnH1fU.d.ts} +2 -2
- package/dist/select-DOgdZO0Q.js.map +1 -1
- package/dist/{select-BjpP51vO.d.ts → select-DZutJxyr.d.ts} +9 -1
- package/dist/select.d.ts +1 -1
- package/dist/separator-DSOIrnhj.js.map +1 -1
- package/dist/sheet.d.ts +5 -1
- package/dist/sheet.js.map +1 -1
- package/dist/skeleton.d.ts +32 -5
- package/dist/skeleton.js.map +1 -1
- package/dist/sort-DzCsa6Qj.js.map +1 -1
- package/dist/split-button.d.ts +1 -1
- package/dist/{table--DsTqaWO.d.ts → table-BsNJBKiq.d.ts} +7 -3
- package/dist/table-Cl4nlRMR.js.map +1 -1
- package/dist/table.d.ts +1 -1
- package/dist/theme-provider-BFcnjeME.js.map +1 -1
- package/dist/theme.d.ts +1 -1
- package/dist/theme.js.map +1 -1
- package/dist/tooltip.d.ts +31 -14
- package/dist/tooltip.js.map +1 -1
- package/dist/use-copy-to-clipboard-C7vsjJe-.js.map +1 -1
- package/dist/use-matches-media-query-CojcYxlA.js.map +1 -1
- package/dist/use-prefers-reduced-motion-aXfsyo-k.js.map +1 -1
- package/package.json +12 -7
package/dist/data-table.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as Button } from "./button-
|
|
1
|
+
import { t as Button } from "./button-Bq0x5Pv4.js";
|
|
2
2
|
import { s as SortingMode } from "./direction-DtBAQn7p.js";
|
|
3
|
-
import { t as Table$1 } from "./table
|
|
3
|
+
import { t as Table$1 } from "./table-BsNJBKiq.js";
|
|
4
4
|
import * as _$react from "react";
|
|
5
5
|
import { ComponentProps, ReactNode } from "react";
|
|
6
6
|
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
@@ -17,24 +17,50 @@ type DataTableProps<TData> = ComponentProps<typeof Table$1.Root> & {
|
|
|
17
17
|
};
|
|
18
18
|
/**
|
|
19
19
|
* The root container for a data table. Wraps all other `DataTable`
|
|
20
|
-
* sub-components and provides the table context
|
|
21
|
-
*
|
|
20
|
+
* sub-components and provides the table context to its descendants.
|
|
21
|
+
*
|
|
22
|
+
* REQUIRED: Construct a TanStack Table instance via `useReactTable` (from
|
|
23
|
+
* `@tanstack/react-table`, also re-exported from `@ngrok/mantle/data-table`)
|
|
24
|
+
* and pass it through the `table` prop. The instance owns columns, data, and
|
|
25
|
+
* any sorting / filtering / pagination state — the wrapper components read
|
|
26
|
+
* from it.
|
|
22
27
|
*
|
|
23
28
|
* @see https://mantle.ngrok.com/components/data-table#datatableroot
|
|
24
29
|
*
|
|
25
30
|
* @example
|
|
26
31
|
* ```tsx
|
|
27
|
-
*
|
|
28
|
-
*
|
|
32
|
+
* import {
|
|
33
|
+
* DataTable,
|
|
34
|
+
* createColumnHelper,
|
|
35
|
+
* getCoreRowModel,
|
|
36
|
+
* useReactTable,
|
|
37
|
+
* } from "@ngrok/mantle/data-table";
|
|
29
38
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
39
|
+
* type Row = { id: string; name: string };
|
|
40
|
+
* const columnHelper = createColumnHelper<Row>();
|
|
41
|
+
* const columns = [
|
|
42
|
+
* columnHelper.accessor("name", {
|
|
43
|
+
* id: "name",
|
|
44
|
+
* header: () => <DataTable.Header>Name</DataTable.Header>,
|
|
45
|
+
* cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,
|
|
46
|
+
* }),
|
|
47
|
+
* ];
|
|
48
|
+
*
|
|
49
|
+
* function MyTable({ data }: { data: Row[] }) {
|
|
50
|
+
* const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });
|
|
51
|
+
* const rows = table.getRowModel().rows;
|
|
52
|
+
*
|
|
53
|
+
* return (
|
|
54
|
+
* <DataTable.Root table={table}>
|
|
55
|
+
* <DataTable.Head />
|
|
56
|
+
* <DataTable.Body>
|
|
57
|
+
* {rows.length > 0
|
|
58
|
+
* ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)
|
|
59
|
+
* : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}
|
|
60
|
+
* </DataTable.Body>
|
|
61
|
+
* </DataTable.Root>
|
|
62
|
+
* );
|
|
63
|
+
* }
|
|
38
64
|
* ```
|
|
39
65
|
*/
|
|
40
66
|
declare function Root<TData>({
|
|
@@ -148,6 +174,28 @@ declare namespace Header {
|
|
|
148
174
|
var displayName: string;
|
|
149
175
|
}
|
|
150
176
|
type DataTableHeadProps = Omit<ComponentProps<typeof Table$1.Head>, "children">;
|
|
177
|
+
/**
|
|
178
|
+
* The `<thead>` container that renders column headers automatically from
|
|
179
|
+
* `table.getHeaderGroups()`. Does not accept children — headers come from each
|
|
180
|
+
* column's `header` definition on the TanStack Table column config.
|
|
181
|
+
*
|
|
182
|
+
* @see https://mantle.ngrok.com/components/data-table#datatablehead
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```tsx
|
|
186
|
+
* const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });
|
|
187
|
+
* const rows = table.getRowModel().rows;
|
|
188
|
+
*
|
|
189
|
+
* <DataTable.Root table={table}>
|
|
190
|
+
* <DataTable.Head />
|
|
191
|
+
* <DataTable.Body>
|
|
192
|
+
* {rows.length > 0
|
|
193
|
+
* ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)
|
|
194
|
+
* : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}
|
|
195
|
+
* </DataTable.Body>
|
|
196
|
+
* </DataTable.Root>
|
|
197
|
+
* ```
|
|
198
|
+
*/
|
|
151
199
|
declare function Head<TData>(props: DataTableHeadProps): _$react_jsx_runtime0.JSX.Element;
|
|
152
200
|
declare namespace Head {
|
|
153
201
|
var displayName: string;
|
|
@@ -188,6 +236,29 @@ declare namespace Row$1 {
|
|
|
188
236
|
var displayName: string;
|
|
189
237
|
}
|
|
190
238
|
type DataTableEmptyRowProps = ComponentProps<typeof Table$1.Row>;
|
|
239
|
+
/**
|
|
240
|
+
* An empty-state row that spans every column. Render this as the `else` branch
|
|
241
|
+
* when `rows.length === 0` to keep the table's frame intact instead of
|
|
242
|
+
* collapsing to an empty `<tbody>`. The cell `colSpan` is computed from the
|
|
243
|
+
* TanStack Table instance via context, so no manual column count is needed.
|
|
244
|
+
*
|
|
245
|
+
* @see https://mantle.ngrok.com/components/data-table#datatableemptyrow
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```tsx
|
|
249
|
+
* const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });
|
|
250
|
+
* const rows = table.getRowModel().rows;
|
|
251
|
+
*
|
|
252
|
+
* <DataTable.Root table={table}>
|
|
253
|
+
* <DataTable.Head />
|
|
254
|
+
* <DataTable.Body>
|
|
255
|
+
* {rows.length > 0
|
|
256
|
+
* ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)
|
|
257
|
+
* : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}
|
|
258
|
+
* </DataTable.Body>
|
|
259
|
+
* </DataTable.Root>
|
|
260
|
+
* ```
|
|
261
|
+
*/
|
|
191
262
|
declare function EmptyRow<TData>({
|
|
192
263
|
children,
|
|
193
264
|
...props
|
|
@@ -253,14 +324,17 @@ declare namespace ActionHeader {
|
|
|
253
324
|
var displayName: string;
|
|
254
325
|
}
|
|
255
326
|
/**
|
|
256
|
-
*
|
|
257
|
-
* and
|
|
258
|
-
*
|
|
259
|
-
* `
|
|
260
|
-
*
|
|
327
|
+
* Use `DataTable` for INTERACTIVE tabular data — sorting, filtering, pagination,
|
|
328
|
+
* row selection, and server-side or client-side data. Built on TanStack Table;
|
|
329
|
+
* the consumer MUST construct a `useReactTable` instance from
|
|
330
|
+
* `@tanstack/react-table` and pass it to `DataTable.Root` via the `table` prop.
|
|
331
|
+
* Every TanStack utility (`createColumnHelper`, `getCoreRowModel`,
|
|
332
|
+
* `getSortedRowModel`, `getPaginationRowModel`, `getFilteredRowModel`,
|
|
333
|
+
* `useReactTable`, …) is re-exported from `@ngrok/mantle/data-table` so a single
|
|
334
|
+
* import covers both the wrapper components and the TanStack helpers.
|
|
261
335
|
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
336
|
+
* For STATIC, layout-driven tables (read-only data dumps, simple key/value
|
|
337
|
+
* displays, plain markup tables with no interactivity), use `Table` instead.
|
|
264
338
|
*
|
|
265
339
|
* @see https://mantle.ngrok.com/components/data-table
|
|
266
340
|
*
|
|
@@ -281,6 +355,7 @@ declare namespace ActionHeader {
|
|
|
281
355
|
* ```
|
|
282
356
|
*
|
|
283
357
|
* @example
|
|
358
|
+
* Minimal — read-only table with a single sortable column:
|
|
284
359
|
* ```tsx
|
|
285
360
|
* import {
|
|
286
361
|
* DataTable,
|
|
@@ -322,10 +397,216 @@ declare namespace ActionHeader {
|
|
|
322
397
|
* );
|
|
323
398
|
* }
|
|
324
399
|
* ```
|
|
400
|
+
*
|
|
401
|
+
* @example
|
|
402
|
+
* Sortable + filterable + paginated — with a global text filter and page controls:
|
|
403
|
+
* ```tsx
|
|
404
|
+
* import {
|
|
405
|
+
* DataTable,
|
|
406
|
+
* createColumnHelper,
|
|
407
|
+
* getCoreRowModel,
|
|
408
|
+
* getFilteredRowModel,
|
|
409
|
+
* getPaginationRowModel,
|
|
410
|
+
* getSortedRowModel,
|
|
411
|
+
* useReactTable,
|
|
412
|
+
* } from "@ngrok/mantle/data-table";
|
|
413
|
+
* import { Button } from "@ngrok/mantle/button";
|
|
414
|
+
* import { Input } from "@ngrok/mantle/input";
|
|
415
|
+
* import { useState } from "react";
|
|
416
|
+
*
|
|
417
|
+
* type Payment = { id: string; amount: number; status: "pending" | "succeeded" | "failed"; email: string };
|
|
418
|
+
*
|
|
419
|
+
* const columnHelper = createColumnHelper<Payment>();
|
|
420
|
+
* const columns = [
|
|
421
|
+
* columnHelper.accessor("status", {
|
|
422
|
+
* id: "status",
|
|
423
|
+
* header: (props) => (
|
|
424
|
+
* <DataTable.Header>
|
|
425
|
+
* <DataTable.HeaderSortButton column={props.column} sortingMode="alphanumeric">
|
|
426
|
+
* Status
|
|
427
|
+
* </DataTable.HeaderSortButton>
|
|
428
|
+
* </DataTable.Header>
|
|
429
|
+
* ),
|
|
430
|
+
* cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,
|
|
431
|
+
* }),
|
|
432
|
+
* columnHelper.accessor("email", {
|
|
433
|
+
* id: "email",
|
|
434
|
+
* header: (props) => (
|
|
435
|
+
* <DataTable.Header>
|
|
436
|
+
* <DataTable.HeaderSortButton column={props.column} sortingMode="alphanumeric">
|
|
437
|
+
* Email
|
|
438
|
+
* </DataTable.HeaderSortButton>
|
|
439
|
+
* </DataTable.Header>
|
|
440
|
+
* ),
|
|
441
|
+
* cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,
|
|
442
|
+
* }),
|
|
443
|
+
* columnHelper.accessor("amount", {
|
|
444
|
+
* id: "amount",
|
|
445
|
+
* header: (props) => (
|
|
446
|
+
* <DataTable.Header className="text-right">
|
|
447
|
+
* <DataTable.HeaderSortButton
|
|
448
|
+
* column={props.column}
|
|
449
|
+
* sortingMode="alphanumeric"
|
|
450
|
+
* className="justify-end"
|
|
451
|
+
* iconPlacement="start"
|
|
452
|
+
* >
|
|
453
|
+
* Amount
|
|
454
|
+
* </DataTable.HeaderSortButton>
|
|
455
|
+
* </DataTable.Header>
|
|
456
|
+
* ),
|
|
457
|
+
* cell: (props) => (
|
|
458
|
+
* <DataTable.Cell className="text-right tabular-nums">
|
|
459
|
+
* {new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(props.getValue())}
|
|
460
|
+
* </DataTable.Cell>
|
|
461
|
+
* ),
|
|
462
|
+
* }),
|
|
463
|
+
* ];
|
|
464
|
+
*
|
|
465
|
+
* function PaymentsTable({ data }: { data: Payment[] }) {
|
|
466
|
+
* const [globalFilter, setGlobalFilter] = useState("");
|
|
467
|
+
*
|
|
468
|
+
* const table = useReactTable({
|
|
469
|
+
* data,
|
|
470
|
+
* columns,
|
|
471
|
+
* state: { globalFilter },
|
|
472
|
+
* onGlobalFilterChange: setGlobalFilter,
|
|
473
|
+
* getCoreRowModel: getCoreRowModel(),
|
|
474
|
+
* getSortedRowModel: getSortedRowModel(),
|
|
475
|
+
* getFilteredRowModel: getFilteredRowModel(),
|
|
476
|
+
* getPaginationRowModel: getPaginationRowModel(),
|
|
477
|
+
* });
|
|
478
|
+
* const rows = table.getRowModel().rows;
|
|
479
|
+
*
|
|
480
|
+
* return (
|
|
481
|
+
* <div className="space-y-4">
|
|
482
|
+
* <Input
|
|
483
|
+
* placeholder="Filter payments…"
|
|
484
|
+
* value={globalFilter}
|
|
485
|
+
* onChange={(event) => setGlobalFilter(event.target.value)}
|
|
486
|
+
* />
|
|
487
|
+
* <DataTable.Root table={table}>
|
|
488
|
+
* <DataTable.Head />
|
|
489
|
+
* <DataTable.Body>
|
|
490
|
+
* {rows.length > 0
|
|
491
|
+
* ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)
|
|
492
|
+
* : <DataTable.EmptyRow>No payments match.</DataTable.EmptyRow>}
|
|
493
|
+
* </DataTable.Body>
|
|
494
|
+
* </DataTable.Root>
|
|
495
|
+
* <div className="flex items-center justify-between gap-2">
|
|
496
|
+
* <span className="text-sm text-muted">
|
|
497
|
+
* Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount()}
|
|
498
|
+
* </span>
|
|
499
|
+
* <div className="flex gap-2">
|
|
500
|
+
* <Button
|
|
501
|
+
* type="button"
|
|
502
|
+
* priority="neutral"
|
|
503
|
+
* onClick={() => table.previousPage()}
|
|
504
|
+
* disabled={!table.getCanPreviousPage()}
|
|
505
|
+
* >
|
|
506
|
+
* Previous
|
|
507
|
+
* </Button>
|
|
508
|
+
* <Button
|
|
509
|
+
* type="button"
|
|
510
|
+
* priority="neutral"
|
|
511
|
+
* onClick={() => table.nextPage()}
|
|
512
|
+
* disabled={!table.getCanNextPage()}
|
|
513
|
+
* >
|
|
514
|
+
* Next
|
|
515
|
+
* </Button>
|
|
516
|
+
* </div>
|
|
517
|
+
* </div>
|
|
518
|
+
* </div>
|
|
519
|
+
* );
|
|
520
|
+
* }
|
|
521
|
+
* ```
|
|
522
|
+
*
|
|
523
|
+
* @example
|
|
524
|
+
* Row action column — a sticky right-edge cell with a dropdown menu of actions.
|
|
525
|
+
* If the row also has `onClick`, stop propagation on the action cell so clicks
|
|
526
|
+
* don't bubble up and fire the row handler:
|
|
527
|
+
* ```tsx
|
|
528
|
+
* import { DataTable, createColumnHelper } from "@ngrok/mantle/data-table";
|
|
529
|
+
* import { DropdownMenu } from "@ngrok/mantle/dropdown-menu";
|
|
530
|
+
* import { IconButton } from "@ngrok/mantle/icon-button";
|
|
531
|
+
* import { DotsThreeVerticalIcon } from "@phosphor-icons/react/DotsThreeVertical";
|
|
532
|
+
*
|
|
533
|
+
* const columnHelper = createColumnHelper<Payment>();
|
|
534
|
+
*
|
|
535
|
+
* const columns = [
|
|
536
|
+
* // …other columns…
|
|
537
|
+
* columnHelper.display({
|
|
538
|
+
* id: "actions",
|
|
539
|
+
* header: () => <DataTable.ActionHeader />,
|
|
540
|
+
* cell: (props) => (
|
|
541
|
+
* <DataTable.ActionCell onClick={(event) => event.stopPropagation()}>
|
|
542
|
+
* <DropdownMenu.Root>
|
|
543
|
+
* <DropdownMenu.Trigger asChild>
|
|
544
|
+
* <IconButton type="button" label="Actions" icon={<DotsThreeVerticalIcon />} />
|
|
545
|
+
* </DropdownMenu.Trigger>
|
|
546
|
+
* <DropdownMenu.Content align="end">
|
|
547
|
+
* <DropdownMenu.Item onSelect={() => copy(props.row.original.id)}>
|
|
548
|
+
* Copy ID
|
|
549
|
+
* </DropdownMenu.Item>
|
|
550
|
+
* <DropdownMenu.Item onSelect={() => refund(props.row.original.id)}>
|
|
551
|
+
* Refund
|
|
552
|
+
* </DropdownMenu.Item>
|
|
553
|
+
* </DropdownMenu.Content>
|
|
554
|
+
* </DropdownMenu.Root>
|
|
555
|
+
* </DataTable.ActionCell>
|
|
556
|
+
* ),
|
|
557
|
+
* }),
|
|
558
|
+
* ];
|
|
559
|
+
* ```
|
|
560
|
+
*
|
|
561
|
+
* @example
|
|
562
|
+
* Clickable row navigating to a detail page — also render a `<Link>` inside the
|
|
563
|
+
* primary cell so the row is reachable by keyboard and screen readers (a `<tr>`
|
|
564
|
+
* is not focusable):
|
|
565
|
+
* ```tsx
|
|
566
|
+
* import { DataTable } from "@ngrok/mantle/data-table";
|
|
567
|
+
* import { Link, href, useNavigate } from "react-router";
|
|
568
|
+
*
|
|
569
|
+
* function PaymentsTable({ data }: { data: Payment[] }) {
|
|
570
|
+
* const navigate = useNavigate();
|
|
571
|
+
* const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });
|
|
572
|
+
* const rows = table.getRowModel().rows;
|
|
573
|
+
*
|
|
574
|
+
* return (
|
|
575
|
+
* <DataTable.Root table={table}>
|
|
576
|
+
* <DataTable.Head />
|
|
577
|
+
* <DataTable.Body>
|
|
578
|
+
* {rows.map((row) => (
|
|
579
|
+
* <DataTable.Row
|
|
580
|
+
* key={row.id}
|
|
581
|
+
* row={row}
|
|
582
|
+
* onClick={() => navigate(href("/payments/:id", { id: row.original.id }))}
|
|
583
|
+
* />
|
|
584
|
+
* ))}
|
|
585
|
+
* </DataTable.Body>
|
|
586
|
+
* </DataTable.Root>
|
|
587
|
+
* );
|
|
588
|
+
* }
|
|
589
|
+
*
|
|
590
|
+
* // The primary column's cell renders a <Link> for keyboard / a11y reachability.
|
|
591
|
+
* columnHelper.accessor("email", {
|
|
592
|
+
* id: "email",
|
|
593
|
+
* header: (props) => <DataTable.Header>Email</DataTable.Header>,
|
|
594
|
+
* cell: (props) => (
|
|
595
|
+
* <DataTable.Cell>
|
|
596
|
+
* <Link to={href("/payments/:id", { id: props.row.original.id })}>
|
|
597
|
+
* {props.getValue()}
|
|
598
|
+
* </Link>
|
|
599
|
+
* </DataTable.Cell>
|
|
600
|
+
* ),
|
|
601
|
+
* });
|
|
602
|
+
* ```
|
|
325
603
|
*/
|
|
326
604
|
declare const DataTable: {
|
|
327
605
|
/**
|
|
328
|
-
* The root container of the data table component.
|
|
606
|
+
* The root container of the data table component. REQUIRED: pass a
|
|
607
|
+
* `useReactTable` instance (from `@tanstack/react-table`, also re-exported
|
|
608
|
+
* from `@ngrok/mantle/data-table`) via the `table` prop — every other
|
|
609
|
+
* `DataTable.*` part reads from it through context.
|
|
329
610
|
*
|
|
330
611
|
* @see https://mantle.ngrok.com/components/data-table#datatableroot
|
|
331
612
|
*
|
package/dist/data-table.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-table.js","names":["Table","Row"],"sources":["../src/components/data-table/helpers.ts","../src/components/data-table/data-table.tsx"],"sourcesContent":["import type { SortingMode } from \"../../utils/sorting/direction.js\";\nimport type { SortDirection } from \"./types.js\";\n\nconst alphanumericSortingOrder = [\"unsorted\", \"asc\", \"desc\"] as const satisfies SortDirection[];\n\nconst timeSortingOrder = [\"unsorted\", \"desc\", \"asc\"] as const satisfies SortDirection[];\n\n/**\n * Get the next sort direction based on the current sort direction and sorting mode.\n */\nfunction getNextSortDirection(currentSortDirection: SortDirection, sortingMode: SortingMode) {\n\tconst sortOrder = sortingMode === \"alphanumeric\" ? alphanumericSortingOrder : timeSortingOrder;\n\n\treturn getNextInCircularList(sortOrder, currentSortDirection) ?? \"unsorted\";\n}\n\n/**\n * Get the next item in a circular list.\n * If the current item is not found in the list (or it's empty), return the fallback value.\n */\nfunction getNextInCircularList<T>(list: T[], currentItem: T, fallback?: T | undefined) {\n\tif (list.length === 0) {\n\t\treturn fallback;\n\t}\n\n\tconst currentItemIndex = list.findIndex((item) => item === currentItem);\n\tif (currentItemIndex === -1) {\n\t\treturn fallback;\n\t}\n\n\tconst nextIndex = (currentItemIndex + 1) % list.length;\n\treturn list.at(nextIndex) ?? fallback;\n}\n\nexport {\n\t//,\n\tgetNextSortDirection,\n\tgetNextInCircularList,\n};\n","import {\n\ttype Column,\n\ttype HeaderContext,\n\ttype Table as TableInstance,\n\ttype Row as TableRow,\n\tflexRender,\n} from \"@tanstack/react-table\";\nimport {\n\ttype ComponentProps,\n\ttype ComponentPropsWithoutRef,\n\ttype ComponentRef,\n\tFragment,\n\ttype ReactNode,\n\tcreateContext,\n\tforwardRef,\n\tuseContext,\n\tuseMemo,\n} from \"react\";\nimport invariant from \"tiny-invariant\";\nimport { cx } from \"../../utils/cx/cx.js\";\nimport { $timeSortingDirection, type SortingMode } from \"../../utils/sorting/direction.js\";\nimport { Button } from \"../button/button.js\";\nimport type { SvgAttributes } from \"../icon/types.js\";\nimport { SortIcon } from \"../icons/sort.js\";\nimport { Table } from \"../table/table.js\";\nimport { getNextSortDirection } from \"./helpers.js\";\nimport type { SortDirection } from \"./types.js\";\n\ntype DataTableContextShape<TData = unknown> = {\n\ttable: TableInstance<TData>;\n};\n\n// oxlint-disable-next-line typescript/no-explicit-any - known limitation of react context when using generics 😭\nconst DataTableContext = createContext<DataTableContextShape<any> | null>(null);\n\n/**\n * @private\n */\nfunction useDataTableContext<TData>() {\n\tconst context = useContext(DataTableContext);\n\n\tinvariant(context, \"useDataTableContext should only be used within a DataTable child component\");\n\n\treturn context as DataTableContextShape<TData>;\n}\n\ntype DataTableProps<TData> = ComponentProps<typeof Table.Root> & {\n\ttable: TableInstance<TData>;\n};\n\n/**\n * The root container for a data table. Wraps all other `DataTable`\n * sub-components and provides the table context via the `table` instance\n * returned from `useReactTable`. Built on top of TanStack Table.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableroot\n *\n * @example\n * ```tsx\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0\n * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n * </DataTable.Body>\n * </DataTable.Root>\n * ```\n */\nfunction Root<TData>({ children, table, ...props }: DataTableProps<TData>) {\n\tconst context: DataTableContextShape<TData> = useMemo(() => ({ table }), [table]);\n\n\treturn (\n\t\t<DataTableContext.Provider value={context}>\n\t\t\t<Table.Root data-slot=\"data-table\" {...props}>\n\t\t\t\t<Table.Element>{children}</Table.Element>\n\t\t\t</Table.Root>\n\t\t</DataTableContext.Provider>\n\t);\n}\n\ntype DataTableHeaderSortButtonProps<TData, TValue> = Omit<ComponentProps<typeof Button>, \"icon\"> &\n\tPick<HeaderContext<TData, TValue>, \"column\"> &\n\t(\n\t\t| {\n\t\t\t\t/**\n\t\t\t\t * Disable sorting for this column.\n\t\t\t\t * It will prevent the sorting direction from being toggled and any icon\n\t\t\t\t * from being shown.\n\t\t\t\t */\n\t\t\t\tdisableSorting: true;\n\t\t\t\t/**\n\t\t\t\t * Use this to render a custom sort icon for the column if it is sortable\n\t\t\t\t * and you want to override the default sort icon\n\t\t\t\t */\n\t\t\t\tsortIcon?: undefined;\n\t\t\t\t/**\n\t\t\t\t * The sorting mode of the column, whether it is alphanumeric or time based.\n\t\t\t\t */\n\t\t\t\tsortingMode?: undefined;\n\t\t }\n\t\t| {\n\t\t\t\tdisableSorting?: false;\n\t\t\t\t/**\n\t\t\t\t * Use this to render a custom sort icon for the column if it is sortable\n\t\t\t\t * and you want to override the default sort icon\n\t\t\t\t */\n\t\t\t\tsortIcon?: (sortDirection: SortDirection) => ReactNode;\n\t\t\t\t/**\n\t\t\t\t * The sorting mode of the column, whether it is alphanumeric or time based.\n\t\t\t\t */\n\t\t\t\tsortingMode: SortingMode;\n\t\t }\n\t);\n\n/**\n * A sortable button toggle for a column header in a data table. Renders a sort\n * icon that reflects the current direction, handles ARIA announcements, and\n * cycles through sort states on click.\n *\n * Each click cycles through:\n * - For `\"alphanumeric\"` sorting: `unsorted → ascending → descending → unsorted`\n * - For `\"time\"` sorting: `unsorted → newest-first → oldest-first → unsorted`\n *\n * For right-aligned numeric columns, pass `className=\"justify-end\"` and\n * `iconPlacement=\"start\"` so the sort icon stays paired with the label.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableheadersortbutton\n *\n * @example\n * ```tsx\n * columnHelper.accessor(\"email\", {\n * id: \"email\",\n * header: (props) => (\n * <DataTable.Header>\n * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n * Email\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * });\n * ```\n */\nfunction HeaderSortButton<TData, TValue>({\n\tchildren,\n\tclassName,\n\tcolumn,\n\tdisableSorting = false,\n\ticonPlacement = \"end\",\n\tsortingMode,\n\tsortIcon: propSortIcon,\n\tonClick,\n\t...props\n}: DataTableHeaderSortButtonProps<TData, TValue>) {\n\tconst _sortDirection = column.getIsSorted();\n\tconst canSort = !disableSorting && column.getCanSort();\n\n\tconst sortDirection: SortDirection =\n\t\tcanSort && typeof _sortDirection === \"string\" ? _sortDirection : \"unsorted\";\n\n\tconst sortIcon = propSortIcon?.(sortDirection) ?? (\n\t\t<DefaultSortIcon mode={sortingMode} direction={sortDirection} />\n\t);\n\n\treturn (\n\t\t<Button\n\t\t\tappearance=\"ghost\"\n\t\t\tdata-slot=\"data-table-header-sort-button\"\n\t\t\tclassName={cx(\n\t\t\t\t\"flex justify-start w-full h-full rounded-none not-disabled:active:scale-none text-muted\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tdata-sort-direction={sortDirection}\n\t\t\tdata-table-header-action\n\t\t\ticon={sortIcon}\n\t\t\ticonPlacement={iconPlacement}\n\t\t\tonClick={(event) => {\n\t\t\t\tonClick?.(event);\n\t\t\t\tif (event.defaultPrevented) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (!canSort || disableSorting || typeof sortingMode === \"undefined\") {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ttoggleNextSortingDirection(column, sortingMode);\n\t\t\t}}\n\t\t\tpriority=\"neutral\"\n\t\t\ttype=\"button\"\n\t\t\t{...props}\n\t\t>\n\t\t\t{canSort && sortDirection !== \"unsorted\" && (\n\t\t\t\t<span className=\"sr-only\">\n\t\t\t\t\tColumn sorted in{\" \"}\n\t\t\t\t\t{sortingMode === \"alphanumeric\"\n\t\t\t\t\t\t? sortDirection === \"asc\"\n\t\t\t\t\t\t\t? \"ascending\"\n\t\t\t\t\t\t\t: \"descending\"\n\t\t\t\t\t\t: $timeSortingDirection(sortDirection)}{\" \"}\n\t\t\t\t\torder\n\t\t\t\t</span>\n\t\t\t)}\n\t\t\t{children}\n\t\t</Button>\n\t);\n}\n\ntype DataTableHeaderProps = ComponentProps<typeof Table.Header>;\n\n/**\n * A `<th>` optimized for header actions. Wrap each column's header content in\n * this; for sortable columns, nest a `DataTable.HeaderSortButton` inside.\n * Non-sortable columns can render plain text.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableheader\n *\n * @example\n * ```tsx\n * columnHelper.accessor(\"name\", {\n * id: \"name\",\n * header: (props) => (\n * <DataTable.Header>\n * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n * Name\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * });\n * ```\n */\nfunction Header({ children, className, ...props }: DataTableHeaderProps) {\n\treturn (\n\t\t<Table.Header\n\t\t\tdata-slot=\"data-table-header\"\n\t\t\tclassName={cx(\"has-data-table-header-action:px-0\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children}\n\t\t</Table.Header>\n\t);\n}\n\nconst Body = forwardRef<\n\tComponentRef<typeof Table.Body>,\n\tComponentPropsWithoutRef<typeof Table.Body>\n>((props, ref) => <Table.Body ref={ref} data-slot=\"data-table-body\" {...props} />);\nBody.displayName = \"DataTableBody\";\n\ntype DataTableHeadProps = Omit<ComponentProps<typeof Table.Head>, \"children\">;\n\nfunction Head<TData>(props: DataTableHeadProps) {\n\tconst { table } = useDataTableContext<TData>();\n\n\treturn (\n\t\t<Table.Head data-slot=\"data-table-head\" {...props}>\n\t\t\t{table.getHeaderGroups().map((headerGroup) => (\n\t\t\t\t<Table.Row key={headerGroup.id}>\n\t\t\t\t\t{headerGroup.headers.map((header) => (\n\t\t\t\t\t\t<Fragment key={header.id}>\n\t\t\t\t\t\t\t{header.isPlaceholder ? (\n\t\t\t\t\t\t\t\t<Table.Header key={header.id} />\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\tflexRender(header.column.columnDef.header, header.getContext())\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</Fragment>\n\t\t\t\t\t))}\n\t\t\t\t</Table.Row>\n\t\t\t))}\n\t\t</Table.Head>\n\t);\n}\n\ntype DataTableRowProps<TData> = Omit<ComponentProps<typeof Table.Row>, \"children\"> & {\n\trow: TableRow<TData>;\n};\n\n/**\n * A single data table body row rendered from a TanStack Table row instance.\n * Does not accept children — cells come from each column's `cell` definition.\n *\n * When `onClick` is provided, the row automatically receives `cursor-pointer`.\n * Pass a different `cursor-*` class via `className` (e.g. `cursor-default`,\n * `cursor-wait`) to override. For keyboard and screen-reader access, also\n * render a `<Link>` inside the primary cell — a `<tr>` is not focusable.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatablerow\n *\n * @example\n * ```tsx\n * const navigate = useNavigate();\n *\n * {rows.map((row) => (\n * <DataTable.Row\n * key={row.id}\n * row={row}\n * onClick={() => navigate(href(\"/payments/:id\", { id: row.original.id }))}\n * />\n * ))}\n * ```\n */\nfunction Row<TData>({ className, row, ...props }: DataTableRowProps<TData>) {\n\treturn (\n\t\t<Table.Row\n\t\t\tdata-slot=\"data-table-row\"\n\t\t\tclassName={cx(props.onClick && \"cursor-pointer\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{row.getVisibleCells().map((cell) => (\n\t\t\t\t<Fragment key={cell.id}>\n\t\t\t\t\t{flexRender(cell.column.columnDef.cell, cell.getContext())}\n\t\t\t\t</Fragment>\n\t\t\t))}\n\t\t</Table.Row>\n\t);\n}\n\ntype DataTableEmptyRowProps = ComponentProps<typeof Table.Row>;\n\nfunction EmptyRow<TData>({ children, ...props }: DataTableEmptyRowProps) {\n\tconst { table } = useDataTableContext<TData>();\n\tconst numberOfColumns = table.getAllColumns().length;\n\n\treturn (\n\t\t<Table.Row data-slot=\"data-table-empty-row\" {...props}>\n\t\t\t<Table.Cell colSpan={numberOfColumns}>{children}</Table.Cell>\n\t\t</Table.Row>\n\t);\n}\n\n/**\n * Internal: renders the visual indicator on the left edge of the sticky action\n * column — a 1px divider plus a soft shadow gradient that reads as content\n * sliding under the pinned column. Positioned as a 6px strip sitting\n * immediately to the left of its sticky parent cell; `-inset-y-px` lets\n * adjacent rows' strips overlap at row dividers so the effect reads as one\n * continuous column instead of per-row blobs.\n *\n * Rendered as a child `<span>` because box-shadow on `<td>`/`<th>` is\n * unreliable across table layout modes.\n */\nfunction StickyColIndicator() {\n\treturn (\n\t\t<span\n\t\t\taria-hidden\n\t\t\tclassName={cx(\n\t\t\t\t\"pointer-events-none absolute -inset-y-px -left-1.5 w-1.5\",\n\t\t\t\t\"opacity-0 transition-opacity group-data-sticky-active/table:opacity-100\",\n\t\t\t\t// 1px divider painted at the strip's right edge (= the pinned\n\t\t\t\t// cell's left edge).\n\t\t\t\t\"shadow-[1px_0_0_0_var(--border-color-card-muted)]\",\n\t\t\t\t// Soft shadow gradient fading leftward. Uses mantle's shadow\n\t\t\t\t// tokens so the alpha adapts to light/dark themes.\n\t\t\t\t\"bg-linear-to-l to-transparent\",\n\t\t\t\t\"from-[color-mix(in_oklab,var(--shadow-color)_var(--shadow-second-opacity),transparent)]\",\n\t\t\t)}\n\t\t/>\n\t);\n}\n\ntype DataTableActionCellProps = ComponentProps<typeof Table.Cell>;\n\n/**\n * A sticky-right `<td>` for per-row action buttons (typically an `IconButton`\n * that opens a `DropdownMenu`). Pair with `DataTable.ActionHeader`.\n *\n * If the row has `onClick`, pass `onClick={(event) => event.stopPropagation()}`\n * on this cell so clicks on action controls don't bubble and fire the row\n * handler.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableactioncell\n *\n * @example\n * ```tsx\n * columnHelper.display({\n * id: \"actions\",\n * header: () => <DataTable.ActionHeader />,\n * cell: () => (\n * <DataTable.ActionCell onClick={(event) => event.stopPropagation()}>\n * <DropdownMenu.Root>...</DropdownMenu.Root>\n * </DataTable.ActionCell>\n * ),\n * });\n * ```\n */\nfunction ActionCell({ children, className, ...props }: DataTableActionCellProps) {\n\treturn (\n\t\t<Table.Cell\n\t\t\t// Marks this cell as a sticky right-edge column so Table.Root can suppress\n\t\t\t// its container-level right-side scroll fade (keeping this cell opaque).\n\t\t\tdata-mantle-table-sticky-right\n\t\t\tdata-slot=\"data-table-action-cell\"\n\t\t\tclassName={cx(\n\t\t\t\t// `bg-inherit` keeps the sticky cell opaque with the row's current bg\n\t\t\t\t// (including hover state) so scrolling cells don't show through.\n\t\t\t\t// Avoid `display: flex` here — it overrides `display: table-cell`,\n\t\t\t\t// preventing the cell from stretching to the full row height in\n\t\t\t\t// `border-separate` mode.\n\t\t\t\t\"sticky z-10 right-0 text-end align-middle bg-inherit p-2\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<StickyColIndicator />\n\t\t\t{children}\n\t\t</Table.Cell>\n\t);\n}\n\ntype DataTableActionHeaderProps = ComponentProps<typeof Table.Header>;\n\n/**\n * A sticky header cell that pairs with `DataTable.ActionCell`. Use this as the\n * header for the action column so the pinned column visually aligns across the\n * header and every body row when the table scrolls horizontally.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableactionheader\n *\n * @example\n * ```tsx\n * columnHelper.display({\n * id: \"actions\",\n * header: () => <DataTable.ActionHeader />,\n * cell: () => <DataTable.ActionCell>{...}</DataTable.ActionCell>,\n * })\n * ```\n */\nfunction ActionHeader({ children, className, ...props }: DataTableActionHeaderProps) {\n\tconst { table } = useDataTableContext();\n\tconst hasRows = table.getRowModel().rows.length > 0;\n\n\treturn (\n\t\t<Table.Header\n\t\t\t// Only mark as sticky-right when body rows exist so the empty state\n\t\t\t// doesn't suppress the container's right-side scroll fade.\n\t\t\t{...(hasRows ? { \"data-mantle-table-sticky-right\": true } : {})}\n\t\t\tdata-slot=\"data-table-action-header\"\n\t\t\tclassName={cx(\n\t\t\t\t// `bg-inherit` keeps the sticky header opaque with the thead's current bg.\n\t\t\t\thasRows && \"sticky z-10 right-0 bg-inherit\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{hasRows && <StickyColIndicator />}\n\t\t\t{children}\n\t\t</Table.Header>\n\t);\n}\n\n// Set display names to preserve original component names for debugging\nRoot.displayName = \"DataTable\";\nActionCell.displayName = \"DataTableActionCell\";\nActionHeader.displayName = \"DataTableActionHeader\";\nBody.displayName = \"DataTableBody\";\nEmptyRow.displayName = \"DataTableEmptyRow\";\nHead.displayName = \"DataTableHead\";\nHeader.displayName = \"DataTableHeader\";\nHeaderSortButton.displayName = \"DataTableHeaderSortButton\";\nRow.displayName = \"DataTableRow\";\n\n/**\n * A data table for dynamic, application data — sortable, filterable, paginatable,\n * and selectable. Built on top of TanStack Table; every TanStack utility\n * (`createColumnHelper`, `getCoreRowModel`, `getSortedRowModel`,\n * `getPaginationRowModel`, `getFilteredRowModel`, `useReactTable`, …) is\n * re-exported from `@ngrok/mantle/data-table`.\n *\n * Prefer the plain `Table` when content is static and none of those behaviors\n * apply.\n *\n * @see https://mantle.ngrok.com/components/data-table\n *\n * @example\n * Composition:\n * ```\n * DataTable.Root\n * ├── DataTable.Head\n * │ └── DataTable.Row\n * │ ├── DataTable.Header\n * │ │ └── DataTable.HeaderSortButton\n * │ └── DataTable.ActionHeader\n * └── DataTable.Body\n * ├── DataTable.Row\n * │ ├── DataTable.Cell\n * │ └── DataTable.ActionCell\n * └── DataTable.EmptyRow\n * ```\n *\n * @example\n * ```tsx\n * import {\n * DataTable,\n * createColumnHelper,\n * getCoreRowModel,\n * useReactTable,\n * } from \"@ngrok/mantle/data-table\";\n *\n * type Row = { id: string; name: string };\n *\n * const columnHelper = createColumnHelper<Row>();\n * const columns = [\n * columnHelper.accessor(\"name\", {\n * id: \"name\",\n * header: (props) => (\n * <DataTable.Header>\n * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n * Name\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * }),\n * ];\n *\n * function MyTable({ data }: { data: Row[] }) {\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * return (\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0\n * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n * </DataTable.Body>\n * </DataTable.Root>\n * );\n * }\n * ```\n */\nconst DataTable = {\n\t/**\n\t * The root container of the data table component.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableroot\n\t *\n\t * @example\n\t * ```tsx\n\t * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n\t * const rows = table.getRowModel().rows;\n\t *\n\t * <DataTable.Root table={table}>\n\t * <DataTable.Head />\n\t * <DataTable.Body>\n\t * {rows.length > 0\n\t * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n\t * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n\t * </DataTable.Body>\n\t * </DataTable.Root>\n\t * ```\n\t */\n\tRoot,\n\t/**\n\t * A sticky action cell positioned at the end of each row, typically holding\n\t * an `IconButton` that opens a `DropdownMenu`. Pair with `DataTable.ActionHeader`.\n\t *\n\t * If the row has `onClick`, stop propagation on this cell so clicks on action\n\t * controls don't bubble up and fire the row handler.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableactioncell\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.display({\n\t * id: \"actions\",\n\t * header: () => <DataTable.ActionHeader />,\n\t * cell: () => (\n\t * <DataTable.ActionCell onClick={(event) => event.stopPropagation()}>\n\t * <DropdownMenu.Root>...</DropdownMenu.Root>\n\t * </DataTable.ActionCell>\n\t * ),\n\t * });\n\t * ```\n\t */\n\tActionCell,\n\t/**\n\t * A sticky header cell that pairs with `DataTable.ActionCell`, keeping the\n\t * action column aligned across the header and body when scrolling horizontally.\n\t * Use as the `header` for a `columnHelper.display` action column.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableactionheader\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.display({\n\t * id: \"actions\",\n\t * header: () => <DataTable.ActionHeader />,\n\t * cell: () => (\n\t * <DataTable.ActionCell onClick={(event) => event.stopPropagation()}>\n\t * <DropdownMenu.Root>...</DropdownMenu.Root>\n\t * </DataTable.ActionCell>\n\t * ),\n\t * });\n\t * ```\n\t */\n\tActionHeader,\n\t/**\n\t * A `<td>` for rendering an individual data cell. Re-exported from\n\t * `Table.Cell`. Every cell rendered by a column's `cell` function should\n\t * be wrapped in this — a raw `<td>` skips mantle typography and padding.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatablecell\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.accessor(\"name\", {\n\t * id: \"name\",\n\t * header: (props) => <DataTable.Header>Name</DataTable.Header>,\n\t * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n\t * });\n\t * ```\n\t */\n\tCell: Table.Cell,\n\t/**\n\t * The `<tbody>` container for rows of data. Typically wraps a map of\n\t * `DataTable.Row`, with a `DataTable.EmptyRow` fallback when there is\n\t * no data.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatablebody\n\t *\n\t * @example\n\t * ```tsx\n\t * <DataTable.Body>\n\t * {rows.length > 0\n\t * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n\t * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n\t * </DataTable.Body>\n\t * ```\n\t */\n\tBody,\n\t/**\n\t * An empty-state row that spans every column. Render this as the `else`\n\t * branch when `rows.length === 0` to keep the table's frame intact instead\n\t * of collapsing to an empty `<tbody>`.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableemptyrow\n\t *\n\t * @example\n\t * ```tsx\n\t * <DataTable.Body>\n\t * {rows.length > 0\n\t * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n\t * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n\t * </DataTable.Body>\n\t * ```\n\t */\n\tEmptyRow,\n\t/**\n\t * The `<thead>` container that renders column headers automatically from\n\t * `table.getHeaderGroups()`. Does not accept children — headers come from\n\t * each column's `header` definition.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatablehead\n\t *\n\t * @example\n\t * ```tsx\n\t * <DataTable.Root table={table}>\n\t * <DataTable.Head />\n\t * <DataTable.Body>...</DataTable.Body>\n\t * </DataTable.Root>\n\t * ```\n\t */\n\tHead,\n\t/**\n\t * A `<th>` optimized for header actions. Wrap each column's header content\n\t * in this; for sortable columns, nest a `DataTable.HeaderSortButton` inside.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableheader\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.accessor(\"name\", {\n\t * id: \"name\",\n\t * header: (props) => (\n\t * <DataTable.Header>\n\t * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n\t * Name\n\t * </DataTable.HeaderSortButton>\n\t * </DataTable.Header>\n\t * ),\n\t * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n\t * });\n\t * ```\n\t */\n\tHeader,\n\t/**\n\t * A sortable button toggle for a column header. Clicks cycle through\n\t * sort directions: for `\"alphanumeric\"`, `unsorted → asc → desc → unsorted`;\n\t * for `\"time\"`, `unsorted → desc (newest-first) → asc → unsorted`.\n\t *\n\t * Pass `className=\"justify-end\"` and `iconPlacement=\"start\"` for\n\t * right-aligned numeric columns so the sort icon stays paired with the label.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableheadersortbutton\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.accessor(\"email\", {\n\t * id: \"email\",\n\t * header: (props) => (\n\t * <DataTable.Header>\n\t * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n\t * Email\n\t * </DataTable.HeaderSortButton>\n\t * </DataTable.Header>\n\t * ),\n\t * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n\t * });\n\t * ```\n\t */\n\tHeaderSortButton,\n\t/**\n\t * A single data table body row rendered from a TanStack Table row instance.\n\t * Does not accept children — cells come from each column's `cell` definition.\n\t *\n\t * When `onClick` is provided, the row automatically receives `cursor-pointer`.\n\t * Pass a different `cursor-*` class via `className` to override. For keyboard\n\t * and screen-reader access, also render a `<Link>` inside the primary cell.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatablerow\n\t *\n\t * @example\n\t * ```tsx\n\t * const navigate = useNavigate();\n\t *\n\t * {rows.map((row) => (\n\t * <DataTable.Row\n\t * key={row.id}\n\t * row={row}\n\t * onClick={() => navigate(href(\"/payments/:id\", { id: row.original.id }))}\n\t * />\n\t * ))}\n\t * ```\n\t */\n\tRow,\n} as const;\n\nexport {\n\t//,\n\tDataTable,\n};\n\ntype DefaultSortIconProps = SvgAttributes & {\n\tdirection: SortDirection | undefined;\n\tmode: SortingMode | undefined;\n};\n\nfunction DefaultSortIcon({ direction, mode, ...props }: DefaultSortIconProps) {\n\tif (direction === \"unsorted\" || !mode || !direction) {\n\t\treturn <svg aria-hidden {...props} />;\n\t}\n\n\treturn <SortIcon mode={mode} direction={direction} {...props} />;\n}\n\n/**\n * Toggle the sorting direction of a column.\n * This ordering is typically toggled by clicking the column header.\n *\n * @example\n * ```md\n * Each click cycles through...\n *\n * For alphanumeric sorting:\n * unsorted ➡️ ascending ➡️ descending ➡️ unsorted ➡️ ...\n *\n * For time sorting:\n * unsorted ➡️ newest-to-oldest ➡️ oldest-to-newest ➡️ unsorted ➡️ ...\n *\n * this is equivalent to the inverse of alphanumeric sorting, or\n * unsorted ➡️ descending ➡️ ascending ➡️ unsorted ➡️ ...\n * ```\n */\nfunction toggleNextSortingDirection<TData, TValue>(\n\tcolumn: Column<TData, TValue>,\n\tsortingMode: SortingMode,\n) {\n\tif (!column.getCanSort()) {\n\t\treturn;\n\t}\n\n\tconst sortDirection = column.getIsSorted();\n\tconst currentSortDirection: SortDirection =\n\t\ttypeof sortDirection === \"string\" ? sortDirection : \"unsorted\";\n\n\tconst nextSortDirection = getNextSortDirection(currentSortDirection, sortingMode);\n\n\tswitch (nextSortDirection) {\n\t\tcase \"unsorted\":\n\t\t\tcolumn.clearSorting();\n\t\t\treturn;\n\t\tcase \"asc\":\n\t\t\tcolumn.toggleSorting(false);\n\t\t\treturn;\n\t\tcase \"desc\":\n\t\t\tcolumn.toggleSorting(true);\n\t\t\treturn;\n\t\tdefault:\n\t\t\treturn;\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"data-table.js","names":["Table","Row"],"sources":["../src/components/data-table/helpers.ts","../src/components/data-table/data-table.tsx"],"sourcesContent":["import type { SortingMode } from \"../../utils/sorting/direction.js\";\nimport type { SortDirection } from \"./types.js\";\n\nconst alphanumericSortingOrder = [\"unsorted\", \"asc\", \"desc\"] as const satisfies SortDirection[];\n\nconst timeSortingOrder = [\"unsorted\", \"desc\", \"asc\"] as const satisfies SortDirection[];\n\n/**\n * Get the next sort direction based on the current sort direction and sorting mode.\n */\nfunction getNextSortDirection(currentSortDirection: SortDirection, sortingMode: SortingMode) {\n\tconst sortOrder = sortingMode === \"alphanumeric\" ? alphanumericSortingOrder : timeSortingOrder;\n\n\treturn getNextInCircularList(sortOrder, currentSortDirection) ?? \"unsorted\";\n}\n\n/**\n * Get the next item in a circular list.\n * If the current item is not found in the list (or it's empty), return the fallback value.\n */\nfunction getNextInCircularList<T>(list: T[], currentItem: T, fallback?: T | undefined) {\n\tif (list.length === 0) {\n\t\treturn fallback;\n\t}\n\n\tconst currentItemIndex = list.findIndex((item) => item === currentItem);\n\tif (currentItemIndex === -1) {\n\t\treturn fallback;\n\t}\n\n\tconst nextIndex = (currentItemIndex + 1) % list.length;\n\treturn list.at(nextIndex) ?? fallback;\n}\n\nexport {\n\t//,\n\tgetNextSortDirection,\n\tgetNextInCircularList,\n};\n","import {\n\ttype Column,\n\ttype HeaderContext,\n\ttype Table as TableInstance,\n\ttype Row as TableRow,\n\tflexRender,\n} from \"@tanstack/react-table\";\nimport {\n\ttype ComponentProps,\n\ttype ComponentPropsWithoutRef,\n\ttype ComponentRef,\n\tFragment,\n\ttype ReactNode,\n\tcreateContext,\n\tforwardRef,\n\tuseContext,\n\tuseMemo,\n} from \"react\";\nimport invariant from \"tiny-invariant\";\nimport { cx } from \"../../utils/cx/cx.js\";\nimport { $timeSortingDirection, type SortingMode } from \"../../utils/sorting/direction.js\";\nimport { Button } from \"../button/button.js\";\nimport type { SvgAttributes } from \"../icon/types.js\";\nimport { SortIcon } from \"../icons/sort.js\";\nimport { Table } from \"../table/table.js\";\nimport { getNextSortDirection } from \"./helpers.js\";\nimport type { SortDirection } from \"./types.js\";\n\ntype DataTableContextShape<TData = unknown> = {\n\ttable: TableInstance<TData>;\n};\n\n// oxlint-disable-next-line typescript/no-explicit-any - known limitation of react context when using generics 😭\nconst DataTableContext = createContext<DataTableContextShape<any> | null>(null);\n\n/**\n * @private\n */\nfunction useDataTableContext<TData>() {\n\tconst context = useContext(DataTableContext);\n\n\tinvariant(context, \"useDataTableContext should only be used within a DataTable child component\");\n\n\treturn context as DataTableContextShape<TData>;\n}\n\ntype DataTableProps<TData> = ComponentProps<typeof Table.Root> & {\n\ttable: TableInstance<TData>;\n};\n\n/**\n * The root container for a data table. Wraps all other `DataTable`\n * sub-components and provides the table context to its descendants.\n *\n * REQUIRED: Construct a TanStack Table instance via `useReactTable` (from\n * `@tanstack/react-table`, also re-exported from `@ngrok/mantle/data-table`)\n * and pass it through the `table` prop. The instance owns columns, data, and\n * any sorting / filtering / pagination state — the wrapper components read\n * from it.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableroot\n *\n * @example\n * ```tsx\n * import {\n * DataTable,\n * createColumnHelper,\n * getCoreRowModel,\n * useReactTable,\n * } from \"@ngrok/mantle/data-table\";\n *\n * type Row = { id: string; name: string };\n * const columnHelper = createColumnHelper<Row>();\n * const columns = [\n * columnHelper.accessor(\"name\", {\n * id: \"name\",\n * header: () => <DataTable.Header>Name</DataTable.Header>,\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * }),\n * ];\n *\n * function MyTable({ data }: { data: Row[] }) {\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * return (\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0\n * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n * </DataTable.Body>\n * </DataTable.Root>\n * );\n * }\n * ```\n */\nfunction Root<TData>({ children, table, ...props }: DataTableProps<TData>) {\n\tconst context: DataTableContextShape<TData> = useMemo(() => ({ table }), [table]);\n\n\treturn (\n\t\t<DataTableContext.Provider value={context}>\n\t\t\t<Table.Root data-slot=\"data-table\" {...props}>\n\t\t\t\t<Table.Element>{children}</Table.Element>\n\t\t\t</Table.Root>\n\t\t</DataTableContext.Provider>\n\t);\n}\n\ntype DataTableHeaderSortButtonProps<TData, TValue> = Omit<ComponentProps<typeof Button>, \"icon\"> &\n\tPick<HeaderContext<TData, TValue>, \"column\"> &\n\t(\n\t\t| {\n\t\t\t\t/**\n\t\t\t\t * Disable sorting for this column.\n\t\t\t\t * It will prevent the sorting direction from being toggled and any icon\n\t\t\t\t * from being shown.\n\t\t\t\t */\n\t\t\t\tdisableSorting: true;\n\t\t\t\t/**\n\t\t\t\t * Use this to render a custom sort icon for the column if it is sortable\n\t\t\t\t * and you want to override the default sort icon\n\t\t\t\t */\n\t\t\t\tsortIcon?: undefined;\n\t\t\t\t/**\n\t\t\t\t * The sorting mode of the column, whether it is alphanumeric or time based.\n\t\t\t\t */\n\t\t\t\tsortingMode?: undefined;\n\t\t }\n\t\t| {\n\t\t\t\tdisableSorting?: false;\n\t\t\t\t/**\n\t\t\t\t * Use this to render a custom sort icon for the column if it is sortable\n\t\t\t\t * and you want to override the default sort icon\n\t\t\t\t */\n\t\t\t\tsortIcon?: (sortDirection: SortDirection) => ReactNode;\n\t\t\t\t/**\n\t\t\t\t * The sorting mode of the column, whether it is alphanumeric or time based.\n\t\t\t\t */\n\t\t\t\tsortingMode: SortingMode;\n\t\t }\n\t);\n\n/**\n * A sortable button toggle for a column header in a data table. Renders a sort\n * icon that reflects the current direction, handles ARIA announcements, and\n * cycles through sort states on click.\n *\n * Each click cycles through:\n * - For `\"alphanumeric\"` sorting: `unsorted → ascending → descending → unsorted`\n * - For `\"time\"` sorting: `unsorted → newest-first → oldest-first → unsorted`\n *\n * For right-aligned numeric columns, pass `className=\"justify-end\"` and\n * `iconPlacement=\"start\"` so the sort icon stays paired with the label.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableheadersortbutton\n *\n * @example\n * ```tsx\n * columnHelper.accessor(\"email\", {\n * id: \"email\",\n * header: (props) => (\n * <DataTable.Header>\n * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n * Email\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * });\n * ```\n */\nfunction HeaderSortButton<TData, TValue>({\n\tchildren,\n\tclassName,\n\tcolumn,\n\tdisableSorting = false,\n\ticonPlacement = \"end\",\n\tsortingMode,\n\tsortIcon: propSortIcon,\n\tonClick,\n\t...props\n}: DataTableHeaderSortButtonProps<TData, TValue>) {\n\tconst rawSortDirection = column.getIsSorted();\n\tconst canSort = !disableSorting && column.getCanSort();\n\n\tconst sortDirection: SortDirection =\n\t\tcanSort && typeof rawSortDirection === \"string\" ? rawSortDirection : \"unsorted\";\n\n\tconst sortIcon = propSortIcon?.(sortDirection) ?? (\n\t\t<DefaultSortIcon mode={sortingMode} direction={sortDirection} />\n\t);\n\n\treturn (\n\t\t<Button\n\t\t\tappearance=\"ghost\"\n\t\t\tdata-slot=\"data-table-header-sort-button\"\n\t\t\tclassName={cx(\n\t\t\t\t\"flex justify-start w-full h-full rounded-none not-disabled:active:scale-none text-muted\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tdata-sort-direction={sortDirection}\n\t\t\tdata-table-header-action\n\t\t\ticon={sortIcon}\n\t\t\ticonPlacement={iconPlacement}\n\t\t\tonClick={(event) => {\n\t\t\t\tonClick?.(event);\n\t\t\t\tif (event.defaultPrevented) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (!canSort || disableSorting || typeof sortingMode === \"undefined\") {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ttoggleNextSortingDirection(column, sortingMode);\n\t\t\t}}\n\t\t\tpriority=\"neutral\"\n\t\t\ttype=\"button\"\n\t\t\t{...props}\n\t\t>\n\t\t\t{canSort && sortDirection !== \"unsorted\" && (\n\t\t\t\t<span className=\"sr-only\">\n\t\t\t\t\tColumn sorted in{\" \"}\n\t\t\t\t\t{sortingMode === \"alphanumeric\"\n\t\t\t\t\t\t? sortDirection === \"asc\"\n\t\t\t\t\t\t\t? \"ascending\"\n\t\t\t\t\t\t\t: \"descending\"\n\t\t\t\t\t\t: $timeSortingDirection(sortDirection)}{\" \"}\n\t\t\t\t\torder\n\t\t\t\t</span>\n\t\t\t)}\n\t\t\t{children}\n\t\t</Button>\n\t);\n}\n\ntype DataTableHeaderProps = ComponentProps<typeof Table.Header>;\n\n/**\n * A `<th>` optimized for header actions. Wrap each column's header content in\n * this; for sortable columns, nest a `DataTable.HeaderSortButton` inside.\n * Non-sortable columns can render plain text.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableheader\n *\n * @example\n * ```tsx\n * columnHelper.accessor(\"name\", {\n * id: \"name\",\n * header: (props) => (\n * <DataTable.Header>\n * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n * Name\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * });\n * ```\n */\nfunction Header({ children, className, ...props }: DataTableHeaderProps) {\n\treturn (\n\t\t<Table.Header\n\t\t\tdata-slot=\"data-table-header\"\n\t\t\tclassName={cx(\"has-data-table-header-action:px-0\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children}\n\t\t</Table.Header>\n\t);\n}\n\n/**\n * The `<tbody>` container for rows of data. Typically wraps a map of\n * `DataTable.Row`, with a `DataTable.EmptyRow` fallback when there is no data.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatablebody\n *\n * @example\n * ```tsx\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0\n * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n * </DataTable.Body>\n * </DataTable.Root>\n * ```\n */\nconst Body = forwardRef<\n\tComponentRef<typeof Table.Body>,\n\tComponentPropsWithoutRef<typeof Table.Body>\n>((props, ref) => <Table.Body ref={ref} data-slot=\"data-table-body\" {...props} />);\nBody.displayName = \"DataTableBody\";\n\ntype DataTableHeadProps = Omit<ComponentProps<typeof Table.Head>, \"children\">;\n\n/**\n * The `<thead>` container that renders column headers automatically from\n * `table.getHeaderGroups()`. Does not accept children — headers come from each\n * column's `header` definition on the TanStack Table column config.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatablehead\n *\n * @example\n * ```tsx\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0\n * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n * </DataTable.Body>\n * </DataTable.Root>\n * ```\n */\nfunction Head<TData>(props: DataTableHeadProps) {\n\tconst { table } = useDataTableContext<TData>();\n\n\treturn (\n\t\t<Table.Head data-slot=\"data-table-head\" {...props}>\n\t\t\t{table.getHeaderGroups().map((headerGroup) => (\n\t\t\t\t<Table.Row key={headerGroup.id}>\n\t\t\t\t\t{headerGroup.headers.map((header) => (\n\t\t\t\t\t\t<Fragment key={header.id}>\n\t\t\t\t\t\t\t{header.isPlaceholder ? (\n\t\t\t\t\t\t\t\t<Table.Header key={header.id} />\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\tflexRender(header.column.columnDef.header, header.getContext())\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</Fragment>\n\t\t\t\t\t))}\n\t\t\t\t</Table.Row>\n\t\t\t))}\n\t\t</Table.Head>\n\t);\n}\n\ntype DataTableRowProps<TData> = Omit<ComponentProps<typeof Table.Row>, \"children\"> & {\n\trow: TableRow<TData>;\n};\n\n/**\n * A single data table body row rendered from a TanStack Table row instance.\n * Does not accept children — cells come from each column's `cell` definition.\n *\n * When `onClick` is provided, the row automatically receives `cursor-pointer`.\n * Pass a different `cursor-*` class via `className` (e.g. `cursor-default`,\n * `cursor-wait`) to override. For keyboard and screen-reader access, also\n * render a `<Link>` inside the primary cell — a `<tr>` is not focusable.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatablerow\n *\n * @example\n * ```tsx\n * const navigate = useNavigate();\n *\n * {rows.map((row) => (\n * <DataTable.Row\n * key={row.id}\n * row={row}\n * onClick={() => navigate(href(\"/payments/:id\", { id: row.original.id }))}\n * />\n * ))}\n * ```\n */\nfunction Row<TData>({ className, row, ...props }: DataTableRowProps<TData>) {\n\treturn (\n\t\t<Table.Row\n\t\t\tdata-slot=\"data-table-row\"\n\t\t\tclassName={cx(props.onClick && \"cursor-pointer\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{row.getVisibleCells().map((cell) => (\n\t\t\t\t<Fragment key={cell.id}>\n\t\t\t\t\t{flexRender(cell.column.columnDef.cell, cell.getContext())}\n\t\t\t\t</Fragment>\n\t\t\t))}\n\t\t</Table.Row>\n\t);\n}\n\ntype DataTableEmptyRowProps = ComponentProps<typeof Table.Row>;\n\n/**\n * An empty-state row that spans every column. Render this as the `else` branch\n * when `rows.length === 0` to keep the table's frame intact instead of\n * collapsing to an empty `<tbody>`. The cell `colSpan` is computed from the\n * TanStack Table instance via context, so no manual column count is needed.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableemptyrow\n *\n * @example\n * ```tsx\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0\n * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n * </DataTable.Body>\n * </DataTable.Root>\n * ```\n */\nfunction EmptyRow<TData>({ children, ...props }: DataTableEmptyRowProps) {\n\tconst { table } = useDataTableContext<TData>();\n\tconst numberOfColumns = table.getAllColumns().length;\n\n\treturn (\n\t\t<Table.Row data-slot=\"data-table-empty-row\" {...props}>\n\t\t\t<Table.Cell colSpan={numberOfColumns}>{children}</Table.Cell>\n\t\t</Table.Row>\n\t);\n}\n\n/**\n * Internal: renders the visual indicator on the left edge of the sticky action\n * column — a 1px divider plus a soft shadow gradient that reads as content\n * sliding under the pinned column. Positioned as a 6px strip sitting\n * immediately to the left of its sticky parent cell; `-inset-y-px` lets\n * adjacent rows' strips overlap at row dividers so the effect reads as one\n * continuous column instead of per-row blobs.\n *\n * Rendered as a child `<span>` because box-shadow on `<td>`/`<th>` is\n * unreliable across table layout modes.\n */\nfunction StickyColIndicator() {\n\treturn (\n\t\t<span\n\t\t\taria-hidden\n\t\t\tclassName={cx(\n\t\t\t\t\"pointer-events-none absolute -inset-y-px -left-1.5 w-1.5\",\n\t\t\t\t\"opacity-0 transition-opacity group-data-sticky-active/table:opacity-100\",\n\t\t\t\t// 1px divider painted at the strip's right edge (= the pinned\n\t\t\t\t// cell's left edge).\n\t\t\t\t\"shadow-[1px_0_0_0_var(--border-color-card-muted)]\",\n\t\t\t\t// Soft shadow gradient fading leftward. Uses mantle's shadow\n\t\t\t\t// tokens so the alpha adapts to light/dark themes.\n\t\t\t\t\"bg-linear-to-l to-transparent\",\n\t\t\t\t\"from-[color-mix(in_oklab,var(--shadow-color)_var(--shadow-second-opacity),transparent)]\",\n\t\t\t)}\n\t\t/>\n\t);\n}\n\ntype DataTableActionCellProps = ComponentProps<typeof Table.Cell>;\n\n/**\n * A sticky-right `<td>` for per-row action buttons (typically an `IconButton`\n * that opens a `DropdownMenu`). Pair with `DataTable.ActionHeader`.\n *\n * If the row has `onClick`, pass `onClick={(event) => event.stopPropagation()}`\n * on this cell so clicks on action controls don't bubble and fire the row\n * handler.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableactioncell\n *\n * @example\n * ```tsx\n * columnHelper.display({\n * id: \"actions\",\n * header: () => <DataTable.ActionHeader />,\n * cell: () => (\n * <DataTable.ActionCell onClick={(event) => event.stopPropagation()}>\n * <DropdownMenu.Root>...</DropdownMenu.Root>\n * </DataTable.ActionCell>\n * ),\n * });\n * ```\n */\nfunction ActionCell({ children, className, ...props }: DataTableActionCellProps) {\n\treturn (\n\t\t<Table.Cell\n\t\t\t// Marks this cell as a sticky right-edge column so Table.Root can suppress\n\t\t\t// its container-level right-side scroll fade (keeping this cell opaque).\n\t\t\tdata-mantle-table-sticky-right\n\t\t\tdata-slot=\"data-table-action-cell\"\n\t\t\tclassName={cx(\n\t\t\t\t// `bg-inherit` keeps the sticky cell opaque with the row's current bg\n\t\t\t\t// (including hover state) so scrolling cells don't show through.\n\t\t\t\t// Avoid `display: flex` here — it overrides `display: table-cell`,\n\t\t\t\t// preventing the cell from stretching to the full row height in\n\t\t\t\t// `border-separate` mode.\n\t\t\t\t\"sticky z-10 right-0 text-end align-middle bg-inherit p-2\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<StickyColIndicator />\n\t\t\t{children}\n\t\t</Table.Cell>\n\t);\n}\n\ntype DataTableActionHeaderProps = ComponentProps<typeof Table.Header>;\n\n/**\n * A sticky header cell that pairs with `DataTable.ActionCell`. Use this as the\n * header for the action column so the pinned column visually aligns across the\n * header and every body row when the table scrolls horizontally.\n *\n * @see https://mantle.ngrok.com/components/data-table#datatableactionheader\n *\n * @example\n * ```tsx\n * columnHelper.display({\n * id: \"actions\",\n * header: () => <DataTable.ActionHeader />,\n * cell: () => <DataTable.ActionCell>{...}</DataTable.ActionCell>,\n * })\n * ```\n */\nfunction ActionHeader({ children, className, ...props }: DataTableActionHeaderProps) {\n\tconst { table } = useDataTableContext();\n\tconst hasRows = table.getRowModel().rows.length > 0;\n\n\treturn (\n\t\t<Table.Header\n\t\t\t// Only mark as sticky-right when body rows exist so the empty state\n\t\t\t// doesn't suppress the container's right-side scroll fade.\n\t\t\t{...(hasRows ? { \"data-mantle-table-sticky-right\": true } : {})}\n\t\t\tdata-slot=\"data-table-action-header\"\n\t\t\tclassName={cx(\n\t\t\t\t// `bg-inherit` keeps the sticky header opaque with the thead's current bg.\n\t\t\t\thasRows && \"sticky z-10 right-0 bg-inherit\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{hasRows && <StickyColIndicator />}\n\t\t\t{children}\n\t\t</Table.Header>\n\t);\n}\n\n// Set display names to preserve original component names for debugging\nRoot.displayName = \"DataTable\";\nActionCell.displayName = \"DataTableActionCell\";\nActionHeader.displayName = \"DataTableActionHeader\";\nBody.displayName = \"DataTableBody\";\nEmptyRow.displayName = \"DataTableEmptyRow\";\nHead.displayName = \"DataTableHead\";\nHeader.displayName = \"DataTableHeader\";\nHeaderSortButton.displayName = \"DataTableHeaderSortButton\";\nRow.displayName = \"DataTableRow\";\n\n/**\n * Use `DataTable` for INTERACTIVE tabular data — sorting, filtering, pagination,\n * row selection, and server-side or client-side data. Built on TanStack Table;\n * the consumer MUST construct a `useReactTable` instance from\n * `@tanstack/react-table` and pass it to `DataTable.Root` via the `table` prop.\n * Every TanStack utility (`createColumnHelper`, `getCoreRowModel`,\n * `getSortedRowModel`, `getPaginationRowModel`, `getFilteredRowModel`,\n * `useReactTable`, …) is re-exported from `@ngrok/mantle/data-table` so a single\n * import covers both the wrapper components and the TanStack helpers.\n *\n * For STATIC, layout-driven tables (read-only data dumps, simple key/value\n * displays, plain markup tables with no interactivity), use `Table` instead.\n *\n * @see https://mantle.ngrok.com/components/data-table\n *\n * @example\n * Composition:\n * ```\n * DataTable.Root\n * ├── DataTable.Head\n * │ └── DataTable.Row\n * │ ├── DataTable.Header\n * │ │ └── DataTable.HeaderSortButton\n * │ └── DataTable.ActionHeader\n * └── DataTable.Body\n * ├── DataTable.Row\n * │ ├── DataTable.Cell\n * │ └── DataTable.ActionCell\n * └── DataTable.EmptyRow\n * ```\n *\n * @example\n * Minimal — read-only table with a single sortable column:\n * ```tsx\n * import {\n * DataTable,\n * createColumnHelper,\n * getCoreRowModel,\n * useReactTable,\n * } from \"@ngrok/mantle/data-table\";\n *\n * type Row = { id: string; name: string };\n *\n * const columnHelper = createColumnHelper<Row>();\n * const columns = [\n * columnHelper.accessor(\"name\", {\n * id: \"name\",\n * header: (props) => (\n * <DataTable.Header>\n * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n * Name\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * }),\n * ];\n *\n * function MyTable({ data }: { data: Row[] }) {\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * return (\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0\n * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n * </DataTable.Body>\n * </DataTable.Root>\n * );\n * }\n * ```\n *\n * @example\n * Sortable + filterable + paginated — with a global text filter and page controls:\n * ```tsx\n * import {\n * DataTable,\n * createColumnHelper,\n * getCoreRowModel,\n * getFilteredRowModel,\n * getPaginationRowModel,\n * getSortedRowModel,\n * useReactTable,\n * } from \"@ngrok/mantle/data-table\";\n * import { Button } from \"@ngrok/mantle/button\";\n * import { Input } from \"@ngrok/mantle/input\";\n * import { useState } from \"react\";\n *\n * type Payment = { id: string; amount: number; status: \"pending\" | \"succeeded\" | \"failed\"; email: string };\n *\n * const columnHelper = createColumnHelper<Payment>();\n * const columns = [\n * columnHelper.accessor(\"status\", {\n * id: \"status\",\n * header: (props) => (\n * <DataTable.Header>\n * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n * Status\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * }),\n * columnHelper.accessor(\"email\", {\n * id: \"email\",\n * header: (props) => (\n * <DataTable.Header>\n * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n * Email\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n * }),\n * columnHelper.accessor(\"amount\", {\n * id: \"amount\",\n * header: (props) => (\n * <DataTable.Header className=\"text-right\">\n * <DataTable.HeaderSortButton\n * column={props.column}\n * sortingMode=\"alphanumeric\"\n * className=\"justify-end\"\n * iconPlacement=\"start\"\n * >\n * Amount\n * </DataTable.HeaderSortButton>\n * </DataTable.Header>\n * ),\n * cell: (props) => (\n * <DataTable.Cell className=\"text-right tabular-nums\">\n * {new Intl.NumberFormat(\"en-US\", { style: \"currency\", currency: \"USD\" }).format(props.getValue())}\n * </DataTable.Cell>\n * ),\n * }),\n * ];\n *\n * function PaymentsTable({ data }: { data: Payment[] }) {\n * const [globalFilter, setGlobalFilter] = useState(\"\");\n *\n * const table = useReactTable({\n * data,\n * columns,\n * state: { globalFilter },\n * onGlobalFilterChange: setGlobalFilter,\n * getCoreRowModel: getCoreRowModel(),\n * getSortedRowModel: getSortedRowModel(),\n * getFilteredRowModel: getFilteredRowModel(),\n * getPaginationRowModel: getPaginationRowModel(),\n * });\n * const rows = table.getRowModel().rows;\n *\n * return (\n * <div className=\"space-y-4\">\n * <Input\n * placeholder=\"Filter payments…\"\n * value={globalFilter}\n * onChange={(event) => setGlobalFilter(event.target.value)}\n * />\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.length > 0\n * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n * : <DataTable.EmptyRow>No payments match.</DataTable.EmptyRow>}\n * </DataTable.Body>\n * </DataTable.Root>\n * <div className=\"flex items-center justify-between gap-2\">\n * <span className=\"text-sm text-muted\">\n * Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount()}\n * </span>\n * <div className=\"flex gap-2\">\n * <Button\n * type=\"button\"\n * priority=\"neutral\"\n * onClick={() => table.previousPage()}\n * disabled={!table.getCanPreviousPage()}\n * >\n * Previous\n * </Button>\n * <Button\n * type=\"button\"\n * priority=\"neutral\"\n * onClick={() => table.nextPage()}\n * disabled={!table.getCanNextPage()}\n * >\n * Next\n * </Button>\n * </div>\n * </div>\n * </div>\n * );\n * }\n * ```\n *\n * @example\n * Row action column — a sticky right-edge cell with a dropdown menu of actions.\n * If the row also has `onClick`, stop propagation on the action cell so clicks\n * don't bubble up and fire the row handler:\n * ```tsx\n * import { DataTable, createColumnHelper } from \"@ngrok/mantle/data-table\";\n * import { DropdownMenu } from \"@ngrok/mantle/dropdown-menu\";\n * import { IconButton } from \"@ngrok/mantle/icon-button\";\n * import { DotsThreeVerticalIcon } from \"@phosphor-icons/react/DotsThreeVertical\";\n *\n * const columnHelper = createColumnHelper<Payment>();\n *\n * const columns = [\n * // …other columns…\n * columnHelper.display({\n * id: \"actions\",\n * header: () => <DataTable.ActionHeader />,\n * cell: (props) => (\n * <DataTable.ActionCell onClick={(event) => event.stopPropagation()}>\n * <DropdownMenu.Root>\n * <DropdownMenu.Trigger asChild>\n * <IconButton type=\"button\" label=\"Actions\" icon={<DotsThreeVerticalIcon />} />\n * </DropdownMenu.Trigger>\n * <DropdownMenu.Content align=\"end\">\n * <DropdownMenu.Item onSelect={() => copy(props.row.original.id)}>\n * Copy ID\n * </DropdownMenu.Item>\n * <DropdownMenu.Item onSelect={() => refund(props.row.original.id)}>\n * Refund\n * </DropdownMenu.Item>\n * </DropdownMenu.Content>\n * </DropdownMenu.Root>\n * </DataTable.ActionCell>\n * ),\n * }),\n * ];\n * ```\n *\n * @example\n * Clickable row navigating to a detail page — also render a `<Link>` inside the\n * primary cell so the row is reachable by keyboard and screen readers (a `<tr>`\n * is not focusable):\n * ```tsx\n * import { DataTable } from \"@ngrok/mantle/data-table\";\n * import { Link, href, useNavigate } from \"react-router\";\n *\n * function PaymentsTable({ data }: { data: Payment[] }) {\n * const navigate = useNavigate();\n * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n * const rows = table.getRowModel().rows;\n *\n * return (\n * <DataTable.Root table={table}>\n * <DataTable.Head />\n * <DataTable.Body>\n * {rows.map((row) => (\n * <DataTable.Row\n * key={row.id}\n * row={row}\n * onClick={() => navigate(href(\"/payments/:id\", { id: row.original.id }))}\n * />\n * ))}\n * </DataTable.Body>\n * </DataTable.Root>\n * );\n * }\n *\n * // The primary column's cell renders a <Link> for keyboard / a11y reachability.\n * columnHelper.accessor(\"email\", {\n * id: \"email\",\n * header: (props) => <DataTable.Header>Email</DataTable.Header>,\n * cell: (props) => (\n * <DataTable.Cell>\n * <Link to={href(\"/payments/:id\", { id: props.row.original.id })}>\n * {props.getValue()}\n * </Link>\n * </DataTable.Cell>\n * ),\n * });\n * ```\n */\nconst DataTable = {\n\t/**\n\t * The root container of the data table component. REQUIRED: pass a\n\t * `useReactTable` instance (from `@tanstack/react-table`, also re-exported\n\t * from `@ngrok/mantle/data-table`) via the `table` prop — every other\n\t * `DataTable.*` part reads from it through context.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableroot\n\t *\n\t * @example\n\t * ```tsx\n\t * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });\n\t * const rows = table.getRowModel().rows;\n\t *\n\t * <DataTable.Root table={table}>\n\t * <DataTable.Head />\n\t * <DataTable.Body>\n\t * {rows.length > 0\n\t * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n\t * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n\t * </DataTable.Body>\n\t * </DataTable.Root>\n\t * ```\n\t */\n\tRoot,\n\t/**\n\t * A sticky action cell positioned at the end of each row, typically holding\n\t * an `IconButton` that opens a `DropdownMenu`. Pair with `DataTable.ActionHeader`.\n\t *\n\t * If the row has `onClick`, stop propagation on this cell so clicks on action\n\t * controls don't bubble up and fire the row handler.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableactioncell\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.display({\n\t * id: \"actions\",\n\t * header: () => <DataTable.ActionHeader />,\n\t * cell: () => (\n\t * <DataTable.ActionCell onClick={(event) => event.stopPropagation()}>\n\t * <DropdownMenu.Root>...</DropdownMenu.Root>\n\t * </DataTable.ActionCell>\n\t * ),\n\t * });\n\t * ```\n\t */\n\tActionCell,\n\t/**\n\t * A sticky header cell that pairs with `DataTable.ActionCell`, keeping the\n\t * action column aligned across the header and body when scrolling horizontally.\n\t * Use as the `header` for a `columnHelper.display` action column.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableactionheader\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.display({\n\t * id: \"actions\",\n\t * header: () => <DataTable.ActionHeader />,\n\t * cell: () => (\n\t * <DataTable.ActionCell onClick={(event) => event.stopPropagation()}>\n\t * <DropdownMenu.Root>...</DropdownMenu.Root>\n\t * </DataTable.ActionCell>\n\t * ),\n\t * });\n\t * ```\n\t */\n\tActionHeader,\n\t/**\n\t * A `<td>` for rendering an individual data cell. Re-exported from\n\t * `Table.Cell`. Every cell rendered by a column's `cell` function should\n\t * be wrapped in this — a raw `<td>` skips mantle typography and padding.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatablecell\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.accessor(\"name\", {\n\t * id: \"name\",\n\t * header: (props) => <DataTable.Header>Name</DataTable.Header>,\n\t * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n\t * });\n\t * ```\n\t */\n\tCell: Table.Cell,\n\t/**\n\t * The `<tbody>` container for rows of data. Typically wraps a map of\n\t * `DataTable.Row`, with a `DataTable.EmptyRow` fallback when there is\n\t * no data.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatablebody\n\t *\n\t * @example\n\t * ```tsx\n\t * <DataTable.Body>\n\t * {rows.length > 0\n\t * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n\t * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n\t * </DataTable.Body>\n\t * ```\n\t */\n\tBody,\n\t/**\n\t * An empty-state row that spans every column. Render this as the `else`\n\t * branch when `rows.length === 0` to keep the table's frame intact instead\n\t * of collapsing to an empty `<tbody>`.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableemptyrow\n\t *\n\t * @example\n\t * ```tsx\n\t * <DataTable.Body>\n\t * {rows.length > 0\n\t * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)\n\t * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}\n\t * </DataTable.Body>\n\t * ```\n\t */\n\tEmptyRow,\n\t/**\n\t * The `<thead>` container that renders column headers automatically from\n\t * `table.getHeaderGroups()`. Does not accept children — headers come from\n\t * each column's `header` definition.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatablehead\n\t *\n\t * @example\n\t * ```tsx\n\t * <DataTable.Root table={table}>\n\t * <DataTable.Head />\n\t * <DataTable.Body>...</DataTable.Body>\n\t * </DataTable.Root>\n\t * ```\n\t */\n\tHead,\n\t/**\n\t * A `<th>` optimized for header actions. Wrap each column's header content\n\t * in this; for sortable columns, nest a `DataTable.HeaderSortButton` inside.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableheader\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.accessor(\"name\", {\n\t * id: \"name\",\n\t * header: (props) => (\n\t * <DataTable.Header>\n\t * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n\t * Name\n\t * </DataTable.HeaderSortButton>\n\t * </DataTable.Header>\n\t * ),\n\t * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n\t * });\n\t * ```\n\t */\n\tHeader,\n\t/**\n\t * A sortable button toggle for a column header. Clicks cycle through\n\t * sort directions: for `\"alphanumeric\"`, `unsorted → asc → desc → unsorted`;\n\t * for `\"time\"`, `unsorted → desc (newest-first) → asc → unsorted`.\n\t *\n\t * Pass `className=\"justify-end\"` and `iconPlacement=\"start\"` for\n\t * right-aligned numeric columns so the sort icon stays paired with the label.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatableheadersortbutton\n\t *\n\t * @example\n\t * ```tsx\n\t * columnHelper.accessor(\"email\", {\n\t * id: \"email\",\n\t * header: (props) => (\n\t * <DataTable.Header>\n\t * <DataTable.HeaderSortButton column={props.column} sortingMode=\"alphanumeric\">\n\t * Email\n\t * </DataTable.HeaderSortButton>\n\t * </DataTable.Header>\n\t * ),\n\t * cell: (props) => <DataTable.Cell>{props.getValue()}</DataTable.Cell>,\n\t * });\n\t * ```\n\t */\n\tHeaderSortButton,\n\t/**\n\t * A single data table body row rendered from a TanStack Table row instance.\n\t * Does not accept children — cells come from each column's `cell` definition.\n\t *\n\t * When `onClick` is provided, the row automatically receives `cursor-pointer`.\n\t * Pass a different `cursor-*` class via `className` to override. For keyboard\n\t * and screen-reader access, also render a `<Link>` inside the primary cell.\n\t *\n\t * @see https://mantle.ngrok.com/components/data-table#datatablerow\n\t *\n\t * @example\n\t * ```tsx\n\t * const navigate = useNavigate();\n\t *\n\t * {rows.map((row) => (\n\t * <DataTable.Row\n\t * key={row.id}\n\t * row={row}\n\t * onClick={() => navigate(href(\"/payments/:id\", { id: row.original.id }))}\n\t * />\n\t * ))}\n\t * ```\n\t */\n\tRow,\n} as const;\n\nexport {\n\t//,\n\tDataTable,\n};\n\ntype DefaultSortIconProps = SvgAttributes & {\n\tdirection: SortDirection | undefined;\n\tmode: SortingMode | undefined;\n};\n\nfunction DefaultSortIcon({ direction, mode, ...props }: DefaultSortIconProps) {\n\tif (direction === \"unsorted\" || !mode || !direction) {\n\t\treturn <svg aria-hidden {...props} />;\n\t}\n\n\treturn <SortIcon mode={mode} direction={direction} {...props} />;\n}\n\n/**\n * Toggle the sorting direction of a column.\n * This ordering is typically toggled by clicking the column header.\n *\n * @example\n * ```md\n * Each click cycles through...\n *\n * For alphanumeric sorting:\n * unsorted ➡️ ascending ➡️ descending ➡️ unsorted ➡️ ...\n *\n * For time sorting:\n * unsorted ➡️ newest-to-oldest ➡️ oldest-to-newest ➡️ unsorted ➡️ ...\n *\n * this is equivalent to the inverse of alphanumeric sorting, or\n * unsorted ➡️ descending ➡️ ascending ➡️ unsorted ➡️ ...\n * ```\n */\nfunction toggleNextSortingDirection<TData, TValue>(\n\tcolumn: Column<TData, TValue>,\n\tsortingMode: SortingMode,\n) {\n\tif (!column.getCanSort()) {\n\t\treturn;\n\t}\n\n\tconst sortDirection = column.getIsSorted();\n\tconst currentSortDirection: SortDirection =\n\t\ttypeof sortDirection === \"string\" ? sortDirection : \"unsorted\";\n\n\tconst nextSortDirection = getNextSortDirection(currentSortDirection, sortingMode);\n\n\tswitch (nextSortDirection) {\n\t\tcase \"unsorted\":\n\t\t\tcolumn.clearSorting();\n\t\t\treturn;\n\t\tcase \"asc\":\n\t\t\tcolumn.toggleSorting(false);\n\t\t\treturn;\n\t\tcase \"desc\":\n\t\t\tcolumn.toggleSorting(true);\n\t\t\treturn;\n\t\tdefault:\n\t\t\treturn;\n\t}\n}\n"],"mappings":"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"}
|