@ngrok/mantle 0.70.2 → 0.71.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.
Files changed (77) hide show
  1. package/README.md +4 -0
  2. package/dist/agent.json +76 -0
  3. package/dist/alert-dialog.d.ts +337 -90
  4. package/dist/alert-dialog.js.map +1 -1
  5. package/dist/alert.js.map +1 -1
  6. package/dist/anchor-2stEauOz.js.map +1 -1
  7. package/dist/anchor.d.ts +45 -4
  8. package/dist/badge.d.ts +32 -3
  9. package/dist/badge.js.map +1 -1
  10. package/dist/{button-BMgAxAwM.d.ts → button-Bq0x5Pv4.d.ts} +4 -4
  11. package/dist/button.d.ts +1 -1
  12. package/dist/checkbox.d.ts +1 -1
  13. package/dist/code-block.d.ts +1 -1
  14. package/dist/code-block_highlight-utils.d.ts +1 -1
  15. package/dist/code.d.ts +22 -1
  16. package/dist/code.js.map +1 -1
  17. package/dist/combobox.d.ts +10 -0
  18. package/dist/combobox.js.map +1 -1
  19. package/dist/command.d.ts +7 -7
  20. package/dist/command.js.map +1 -1
  21. package/dist/copy-to-clipboard-DjOD_Mwb.js.map +1 -1
  22. package/dist/data-table.d.ts +303 -22
  23. package/dist/data-table.js.map +1 -1
  24. package/dist/dialog-BHzl9eye.js.map +1 -1
  25. package/dist/dialog.d.ts +6 -1
  26. package/dist/flag.d.ts +33 -4
  27. package/dist/flag.js.map +1 -1
  28. package/dist/hooks.d.ts +297 -72
  29. package/dist/hooks.js.map +1 -1
  30. package/dist/hover-card.d.ts +15 -10
  31. package/dist/hover-card.js.map +1 -1
  32. package/dist/icons.js.map +1 -1
  33. package/dist/{index-DMAkXvFI.d.ts → index-C91lxoX9.d.ts} +55 -12
  34. package/dist/input.d.ts +1 -1
  35. package/dist/input.js.map +1 -1
  36. package/dist/kbd-CAVUiqBT.js.map +1 -1
  37. package/dist/kbd.d.ts +37 -8
  38. package/dist/label.d.ts +40 -9
  39. package/dist/label.js.map +1 -1
  40. package/dist/llms.txt +77 -0
  41. package/dist/media-object.d.ts +26 -10
  42. package/dist/media-object.js.map +1 -1
  43. package/dist/multi-select.d.ts +185 -34
  44. package/dist/multi-select.js.map +1 -1
  45. package/dist/otp-input.d.ts +167 -0
  46. package/dist/otp-input.js +2 -0
  47. package/dist/otp-input.js.map +1 -0
  48. package/dist/pagination.d.ts +1 -1
  49. package/dist/pagination.js.map +1 -1
  50. package/dist/popover.d.ts +7 -5
  51. package/dist/popover.js.map +1 -1
  52. package/dist/primitive-tXm_8n_t.js.map +1 -1
  53. package/dist/progress.js.map +1 -1
  54. package/dist/resolve-pre-rendered-props-C-kiaLHj.js.map +1 -1
  55. package/dist/{resolve-pre-rendered-props-x-52gvQ1.d.ts → resolve-pre-rendered-props-CNUnH1fU.d.ts} +2 -2
  56. package/dist/select-DOgdZO0Q.js.map +1 -1
  57. package/dist/{select-BjpP51vO.d.ts → select-DZutJxyr.d.ts} +9 -1
  58. package/dist/select.d.ts +1 -1
  59. package/dist/separator-DSOIrnhj.js.map +1 -1
  60. package/dist/sheet.d.ts +5 -1
  61. package/dist/sheet.js.map +1 -1
  62. package/dist/skeleton.d.ts +32 -5
  63. package/dist/skeleton.js.map +1 -1
  64. package/dist/sort-DzCsa6Qj.js.map +1 -1
  65. package/dist/split-button.d.ts +1 -1
  66. package/dist/{table--DsTqaWO.d.ts → table-BsNJBKiq.d.ts} +7 -3
  67. package/dist/table-Cl4nlRMR.js.map +1 -1
  68. package/dist/table.d.ts +1 -1
  69. package/dist/theme-provider-BFcnjeME.js.map +1 -1
  70. package/dist/theme.d.ts +1 -1
  71. package/dist/theme.js.map +1 -1
  72. package/dist/tooltip.d.ts +31 -14
  73. package/dist/tooltip.js.map +1 -1
  74. package/dist/use-copy-to-clipboard-C7vsjJe-.js.map +1 -1
  75. package/dist/use-matches-media-query-CojcYxlA.js.map +1 -1
  76. package/dist/use-prefers-reduced-motion-aXfsyo-k.js.map +1 -1
  77. package/package.json +14 -7
@@ -1,6 +1,6 @@
1
- import { t as Button } from "./button-BMgAxAwM.js";
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--DsTqaWO.js";
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 via the `table` instance
21
- * returned from `useReactTable`. Built on top of TanStack Table.
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
- * const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });
28
- * const rows = table.getRowModel().rows;
32
+ * import {
33
+ * DataTable,
34
+ * createColumnHelper,
35
+ * getCoreRowModel,
36
+ * useReactTable,
37
+ * } from "@ngrok/mantle/data-table";
29
38
  *
30
- * <DataTable.Root table={table}>
31
- * <DataTable.Head />
32
- * <DataTable.Body>
33
- * {rows.length > 0
34
- * ? rows.map((row) => <DataTable.Row key={row.id} row={row} />)
35
- * : <DataTable.EmptyRow>No results.</DataTable.EmptyRow>}
36
- * </DataTable.Body>
37
- * </DataTable.Root>
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
- * A data table for dynamic, application data — sortable, filterable, paginatable,
257
- * and selectable. Built on top of TanStack Table; every TanStack utility
258
- * (`createColumnHelper`, `getCoreRowModel`, `getSortedRowModel`,
259
- * `getPaginationRowModel`, `getFilteredRowModel`, `useReactTable`, …) is
260
- * re-exported from `@ngrok/mantle/data-table`.
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
- * Prefer the plain `Table` when content is static and none of those behaviors
263
- * apply.
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
  *
@@ -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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"}