@multiplatform.one/table 7.8.0 → 7.10.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.
Files changed (28) hide show
  1. package/dist/cjs/components/DataTable/DataTableMobileCards.cjs +100 -33
  2. package/dist/cjs/components/DataTable/DataTableMobileCards.native.js +139 -38
  3. package/dist/cjs/components/DataTable/DataTableMobileCards.native.js.map +1 -1
  4. package/dist/cjs/components/DataTable/index.native.js +59 -93
  5. package/dist/cjs/components/DataTable/index.native.js.map +1 -1
  6. package/dist/esm/components/DataTable/DataTableMobileCards.mjs +99 -34
  7. package/dist/esm/components/DataTable/DataTableMobileCards.mjs.map +1 -1
  8. package/dist/esm/components/DataTable/DataTableMobileCards.native.js +138 -39
  9. package/dist/esm/components/DataTable/DataTableMobileCards.native.js.map +1 -1
  10. package/dist/esm/components/DataTable/index.native.js +60 -94
  11. package/dist/esm/components/DataTable/index.native.js.map +1 -1
  12. package/dist/jsx/components/DataTable/DataTableMobileCards.mjs +99 -34
  13. package/dist/jsx/components/DataTable/DataTableMobileCards.mjs.map +1 -1
  14. package/dist/jsx/components/DataTable/DataTableMobileCards.native.js +139 -38
  15. package/dist/jsx/components/DataTable/DataTableMobileCards.native.js.map +1 -1
  16. package/dist/jsx/components/DataTable/index.native.js +59 -93
  17. package/dist/jsx/components/DataTable/index.native.js.map +1 -1
  18. package/package.json +12 -12
  19. package/src/components/DataTable/DataTable.spec.tsx +5 -3
  20. package/src/components/DataTable/DataTableMobileCards.tsx +162 -39
  21. package/src/components/DataTable/cardLayout.spec.tsx +19 -8
  22. package/src/components/DataTable/dataTableMobileCards.spec.tsx +50 -2
  23. package/src/components/DataTable/index.native.tsx +80 -124
  24. package/src/components/DataTable/nativeCardParity.spec.tsx +95 -0
  25. package/types/components/DataTable/DataTableMobileCards.d.ts +42 -8
  26. package/types/components/DataTable/DataTableMobileCards.d.ts.map +1 -1
  27. package/types/components/DataTable/index.native.d.ts +1 -1
  28. package/types/components/DataTable/index.native.d.ts.map +1 -1
@@ -8,10 +8,14 @@
8
8
  * delete cluster beside the title, the delete floor at `minRows`, and the two
9
9
  * distinct empty-state copies (no data vs no results) — plus the rule that an
10
10
  * in-flight refetch paints NO empty state over a momentarily blank body.
11
+ *
12
+ * It also pins `classifyCardField`, which decides whether a field prints its
13
+ * column name at all. In the grid a header is written once for a whole
14
+ * column; on a card it would be written once per value.
11
15
  */
12
16
 
13
17
  import { renderWithProviders } from "@multiplatform.one/test-utils";
14
- import { fireEvent, screen, waitFor } from "@testing-library/react";
18
+ import { fireEvent, screen, waitFor, within } from "@testing-library/react";
15
19
  import { describe, expect, it, vi } from "vitest";
16
20
  import type { DataTableColumn, TableFilter } from "../../types";
17
21
  import { DataTable } from "./index";
@@ -52,7 +56,7 @@ describe("DataTableMobileCards", () => {
52
56
  expect(cards(container).length).toBe(people.length);
53
57
  });
54
58
 
55
- it("the first visible column is the card title, the rest are label/value rows", () => {
59
+ it("the first visible column is the card title, the rest are classified fields", () => {
56
60
  const { container } = renderCards();
57
61
  const first = cards(container)[0];
58
62
  expect(first.querySelector('[data-card-slot="title"]')?.textContent).toBe("John");
@@ -69,6 +73,50 @@ describe("DataTableMobileCards", () => {
69
73
  expect(first.textContent).not.toContain("Last Name");
70
74
  });
71
75
 
76
+ it("a self-naming string goes bare, into one wrapped flow with its name hidden", () => {
77
+ const { container } = renderCards();
78
+ const first = cards(container)[0];
79
+ const meta = first.querySelector('[data-card-slot="meta"]');
80
+ expect(meta).not.toBeNull();
81
+ expect(meta!.textContent).toContain("Doe");
82
+ expect(within(first).queryByText("Last Name")).toBeNull();
83
+ expect(first.textContent).toContain("Last Name:");
84
+ });
85
+
86
+ it("a figure keeps its column name, inline beside the value", () => {
87
+ const { container } = renderCards();
88
+ const first = cards(container)[0];
89
+ const figure = first.querySelector('[data-card-slot="figure"]');
90
+ expect(figure).not.toBeNull();
91
+ expect(figure!.textContent).toContain("28");
92
+ expect(within(first).getByText("Age")).toBeDefined();
93
+ });
94
+
95
+ it("a field with no value spends no row at all", () => {
96
+ const { container } = renderCards({
97
+ data: [{ id: 1, firstName: "John", lastName: "", age: 28 }],
98
+ });
99
+ const first = cards(container)[0];
100
+ expect(first.querySelector('[data-card-slot="meta"]')).toBeNull();
101
+ expect(first.textContent).not.toContain("Last Name");
102
+ });
103
+
104
+ it("a control keeps a row of its own and prints no label", () => {
105
+ // Explicit id on BOTH columns: DataTable seeds columnOrder from
106
+ // `col.id || ""`, so an id-less accessor column beside an id'd one would
107
+ // put `_actions` first and make the control the card title instead.
108
+ const { container } = renderCards({
109
+ columns: [
110
+ { id: "firstName", accessorKey: "firstName", header: "First Name" },
111
+ { id: "_actions", accessorKey: "lastName", header: "Actions", meta: { role: "actions" } },
112
+ ],
113
+ });
114
+ const first = cards(container)[0];
115
+ expect(first.querySelector('[data-card-slot="control"]')).not.toBeNull();
116
+ expect(within(first).queryByText("Actions")).toBeNull();
117
+ expect(first.textContent).toContain("Actions:");
118
+ });
119
+
72
120
  it("selection adds a select-all bar above the stack and a checkbox per card", () => {
73
121
  const { container } = renderCards({ enableRowSelection: true });
74
122
  expect(screen.getByLabelText("Select all rows")).toBeTruthy();
@@ -17,12 +17,12 @@ import { FlashList, type FlashListRef } from "@shopify/flash-list";
17
17
  import { AsyncBoundary, EmptyState, useDirection } from "@multiplatform.one/components";
18
18
  import { Button, Field, useFormField } from "@multiplatform.one/forms";
19
19
  import { flexRender } from "@tanstack/react-table";
20
- import { hairline, useResolvedKnobs } from "@multiplatform.one/theme";
20
+ import { useResolvedKnobs } from "@multiplatform.one/theme";
21
21
  import React, { useRef, useMemo, useCallback } from "react";
22
22
  import { ScrollView, Text, View, useMedia, withStaticProperties } from "tamagui";
23
23
  import { useTranslation } from "react-i18next";
24
24
  import { useDataTable } from "../../hooks/useDataTable";
25
- import type { DataTableColumn, DataTableField } from "../../types";
25
+ import type { DataTableColumn, DataTableField, SelectAllScope } from "../../types";
26
26
  import { DEFAULT_PAGE_SIZE_OPTIONS } from "../../utils/paginationDefaults";
27
27
  import { withInterp } from "../../utils/withInterp";
28
28
  import { DataTableColumnPinning } from "../DataTableColumnPinning";
@@ -38,6 +38,7 @@ import { DataTableCell } from "../DataTableCell";
38
38
  import { Table } from "@multiplatform.one/table-primitives";
39
39
  import { DataTableContext } from "./DataTableContext";
40
40
  import { DataTableDocumentList } from "./DataTableDocumentList";
41
+ import { DataTableMobileCards } from "./DataTableMobileCards";
41
42
  import { dataTableChrome, resolveDataTableIntent, rowIntentProps } from "./dataTableChrome";
42
43
  import { tableRowStateProps } from "./rowStateChrome";
43
44
  import { DataTableStateContext } from "./DataTableStateContext";
@@ -134,6 +135,8 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
134
135
  virtualizationOverscan = 5,
135
136
  enableRowExpansion = false,
136
137
  renderSubComponent,
138
+ enableRowSelection = false,
139
+ selectAllScope: selectAllScopeProp,
137
140
  enableColumnPinning = false,
138
141
  maxPinnedColumns = 3,
139
142
  enableKeyboardNavigation: _enableKeyboardNavigation = false,
@@ -181,6 +184,9 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
181
184
  compact,
182
185
  });
183
186
  const chrome = dataTableChrome(String(knobProps.sizeToken));
187
+ // House selectAllScope knob (DG-TBL-03): prop overrides knob; default page.
188
+ const selectAllScope: SelectAllScope =
189
+ selectAllScopeProp ?? (knobProps.selectAllScope === "filtered" ? "filtered" : "page");
184
190
  const rowHeightPx = chrome.rowPx;
185
191
  // Collapsed expand-caret points toward inline-end, so it mirrors in RTL via
186
192
  // glyph swap (useDirection, Axiom 15); rotation sign flips so expanded
@@ -216,6 +222,7 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
216
222
  virtualizationOverscan,
217
223
  enableRowExpansion,
218
224
  renderSubComponent,
225
+ enableRowSelection,
219
226
  enableColumnPinning,
220
227
  maxPinnedColumns,
221
228
  enableKeyboardNavigation: false,
@@ -225,6 +232,25 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
225
232
  isFetching: isFetchingProp,
226
233
  });
227
234
 
235
+ const selectAllChecked =
236
+ enableRowSelection &&
237
+ (selectAllScope === "filtered"
238
+ ? table.getIsAllRowsSelected()
239
+ : table.getIsAllPageRowsSelected());
240
+ const selectAllIndeterminate =
241
+ enableRowSelection &&
242
+ (selectAllScope === "filtered"
243
+ ? table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()
244
+ : table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected());
245
+
246
+ const toggleSelectAll = useCallback(() => {
247
+ if (selectAllScope === "filtered") {
248
+ table.toggleAllRowsSelected();
249
+ } else {
250
+ table.toggleAllPageRowsSelected();
251
+ }
252
+ }, [selectAllScope, table]);
253
+
228
254
  const resolvedSize: "sm" | "md" | "lg" =
229
255
  size ?? (knobProps.sizeToken === "$3" ? "sm" : knobProps.sizeToken === "$5" ? "lg" : "md");
230
256
 
@@ -493,8 +519,8 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
493
519
  // (see utils/rowPress.ts). Roving tabindex is a web-only concern.
494
520
  const rowPressActive = Boolean(onRowPress) && !disabled;
495
521
 
496
- const deriveRowLabel = useCallback(
497
- (row: (typeof tableRows)[0], index: number): string => {
522
+ const deriveRowIdentity = useCallback(
523
+ (row: (typeof tableRows)[0]): string | undefined => {
498
524
  const fromAccessor = rowLabel?.(row.original as TData);
499
525
  if (fromAccessor) return fromAccessor;
500
526
  let firstCellValue: unknown;
@@ -509,13 +535,33 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
509
535
  ) {
510
536
  return String(firstCellValue);
511
537
  }
512
- return withInterp(t("Row {{row}}", { row: index + 1 }), { row: index + 1 });
538
+ return undefined;
513
539
  },
514
- [rowLabel, t],
540
+ [rowLabel],
541
+ );
542
+
543
+ const deriveRowLabel = useCallback(
544
+ (row: (typeof tableRows)[0], index: number): string =>
545
+ deriveRowIdentity(row) ??
546
+ withInterp(t("Row {{row}}", { row: index + 1 }), { row: index + 1 }),
547
+ [deriveRowIdentity, t],
548
+ );
549
+
550
+ // Selection-toggle name: row identity beats a bare index ("Select row
551
+ // Bulbasaur", falling back to "Select row 3"), the same source the grid
552
+ // and the card list both read.
553
+ const deriveSelectRowLabel = useCallback(
554
+ (row: (typeof tableRows)[0], index: number): string => {
555
+ const identity = deriveRowIdentity(row) ?? String(index + 1);
556
+ return withInterp(t("Select row {{row}}", { row: identity }), { row: identity });
557
+ },
558
+ [deriveRowIdentity, t],
515
559
  );
516
560
 
517
561
  const getRowPressProps = useCallback(
518
- (row: (typeof tableRows)[0], index: number) => {
562
+ // The card list asks for the "card" variant. Native carries no DOM row
563
+ // role for it to override, so both variants land on role="button".
564
+ (row: (typeof tableRows)[0], index: number, _variant: "row" | "card" = "row") => {
519
565
  if (!rowPressActive) return {};
520
566
  return {
521
567
  onPress: () => onRowPress?.(row.original as TData, index),
@@ -551,101 +597,6 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
551
597
  [renderRowContent, tableZebraOn, t, getRowPressProps],
552
598
  );
553
599
 
554
- const renderMobileCard = useCallback(
555
- (row: (typeof tableRows)[0], index: number, onDelete?: (idx: number) => void) => {
556
- const rowData = row.original as TData;
557
- const visibleCells = row.getVisibleCells();
558
- return (
559
- <View
560
- key={`${row.id}-${index}`}
561
- backgroundColor="$color1"
562
- borderRadius="$4"
563
- borderWidth={1}
564
- borderColor="$borderColor"
565
- padding="$3"
566
- marginBottom="$2"
567
- {...(getRowPressProps(row, index) as any)}
568
- {...(rowIntentProps(rowData) as any)}
569
- >
570
- {showRowNumbers && (
571
- <View
572
- flexDirection="row"
573
- justifyContent="space-between"
574
- paddingBottom="$2"
575
- {...hairline.bottom}
576
- borderBottomColor="$borderColor"
577
- marginBottom="$2"
578
- >
579
- <Text fontSize="$3" color="$color10" fontWeight="600">
580
- #{index + 1}
581
- </Text>
582
- {showDeleteRow && !readOnly && (
583
- <Button
584
- chromeless
585
- circular
586
- size="$2"
587
- icon={TrashIcon}
588
- disabled={disabled || tableRows.length <= minRows}
589
- onPress={() => onDelete?.(index)}
590
- aria-label={withInterp(t("Delete row {{row}}", { row: index + 1 }), {
591
- row: index + 1,
592
- })}
593
- />
594
- )}
595
- </View>
596
- )}
597
- {visibleCells.map((cell: any) => {
598
- const columnDef = cell.column.columnDef as DataTableColumn;
599
- const headerLabel =
600
- typeof columnDef.header === "string"
601
- ? columnDef.header
602
- : columnDef.id || cell.column.id;
603
- return (
604
- <View
605
- key={cell.id}
606
- flexDirection="row"
607
- alignItems="center"
608
- paddingVertical="$1.5"
609
- gap="$2"
610
- >
611
- <Text fontSize="$3" color="$color10" fontWeight="500" width="40%" flexShrink={0}>
612
- {headerLabel}
613
- </Text>
614
- <View flex={1}>{renderCellContent(cell, index, rowData)}</View>
615
- </View>
616
- );
617
- })}
618
- {!showRowNumbers && showDeleteRow && !readOnly && (
619
- <View paddingTop="$2" alignItems="flex-end">
620
- <Button
621
- chromeless
622
- circular
623
- size="$2"
624
- icon={TrashIcon}
625
- disabled={disabled || tableRows.length <= minRows}
626
- onPress={() => onDelete?.(index)}
627
- aria-label={withInterp(t("Delete row {{row}}", { row: index + 1 }), {
628
- row: index + 1,
629
- })}
630
- />
631
- </View>
632
- )}
633
- </View>
634
- );
635
- },
636
- [
637
- showRowNumbers,
638
- showDeleteRow,
639
- readOnly,
640
- disabled,
641
- tableRows.length,
642
- minRows,
643
- renderCellContent,
644
- t,
645
- getRowPressProps,
646
- ],
647
- );
648
-
649
600
  const renderToolbarComponent = () => {
650
601
  if (renderToolbar) {
651
602
  return renderToolbar({
@@ -777,36 +728,37 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
777
728
  {showList ? (
778
729
  <DataTableDocumentList
779
730
  tableRows={displayRows}
731
+ enableRowSelection={enableRowSelection}
780
732
  disabled={disabled}
781
733
  hasActiveFilters={computed.hasActiveFilters}
782
734
  renderCellContent={renderCellContent}
783
735
  compact={isCompact}
736
+ selectRowLabel={deriveSelectRowLabel}
784
737
  queryInFlight={queryInFlight}
785
738
  {...(rowPressActive ? { getRowPressProps } : {})}
786
739
  />
787
740
  ) : showCards ? (
788
741
  <View flexDirection="column" gap="$2">
789
- {displayRows.length === 0 ? (
790
- queryInFlight ? null : (
791
- <EmptyState
792
- compact
793
- backgroundColor="$color1"
794
- borderRadius="$4"
795
- aria-live="polite"
796
- title={
797
- computed.hasActiveFilters ? t("No results found") : t("No data available")
798
- }
799
- />
800
- )
801
- ) : (
802
- displayRows.map((row, index) =>
803
- renderMobileCard(
804
- row,
805
- index,
806
- isFormMode ? effectiveHandleDeleteRow : handleDeleteRow,
807
- ),
808
- )
809
- )}
742
+ <DataTableMobileCards
743
+ tableRows={displayRows}
744
+ enableRowSelection={enableRowSelection}
745
+ showRowNumbers={showRowNumbers}
746
+ showDeleteRow={showDeleteRow}
747
+ readOnly={readOnly}
748
+ disabled={disabled}
749
+ dataLength={tableData.length}
750
+ minRows={minRows}
751
+ hasActiveFilters={computed.hasActiveFilters}
752
+ renderCellContent={renderCellContent}
753
+ onDeleteRow={isFormMode ? effectiveHandleDeleteRow : handleDeleteRow}
754
+ selectAllChecked={selectAllChecked}
755
+ selectAllIndeterminate={selectAllIndeterminate}
756
+ onToggleSelectAll={toggleSelectAll}
757
+ compact={isCompact}
758
+ selectRowLabel={deriveSelectRowLabel}
759
+ queryInFlight={queryInFlight}
760
+ {...(rowPressActive ? { getRowPressProps } : {})}
761
+ />
810
762
  {enablePagination && (
811
763
  <View width="100%" {...(disabled ? disabledState.chromeKnobProps : {})}>
812
764
  {renderPaginationComponent()}
@@ -1099,7 +1051,6 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
1099
1051
  virtualizationHeight,
1100
1052
  virtualizationOverscan,
1101
1053
  renderRow,
1102
- renderMobileCard,
1103
1054
  renderSubComponent,
1104
1055
  footerGroups,
1105
1056
  sm,
@@ -1118,6 +1069,11 @@ function DataTableRoot<TData extends Record<string, any> = Record<string, any>>(
1118
1069
  renderCellContent,
1119
1070
  rowPressActive,
1120
1071
  getRowPressProps,
1072
+ enableRowSelection,
1073
+ selectAllChecked,
1074
+ selectAllIndeterminate,
1075
+ toggleSelectAll,
1076
+ deriveSelectRowLabel,
1121
1077
  displayRows,
1122
1078
  queryInFlight,
1123
1079
  t,
@@ -0,0 +1,95 @@
1
+ /**
2
+ * One card renderer, two platforms.
3
+ *
4
+ * DataTableRoot.tsx (web) and index.native.tsx (native) are separate roots
5
+ * because native virtualizes through FlashList. The CARD LIST is not part
6
+ * of that split — a card has no platform-specific anatomy — so both roots
7
+ * render the same DataTableMobileCards rather than each drawing a card.
8
+ *
9
+ * MPO-285 shared the field RULE (classifyCardField, getColumnLabel) while
10
+ * leaving two copies of the JSX, and the two had already drifted: the
11
+ * native copy hardcoded its surface, and shipped without zebra striping,
12
+ * the selection checkbox, the empty states, and the TableCellContext that
13
+ * makes a field render chromeless. Sharing the rule alone does not stop
14
+ * that, because a new slot still has to be added twice.
15
+ *
16
+ * These assertions read source rather than render: the native root pulls in
17
+ * FlashList and the suite runs under happy-dom, so index.native.tsx is
18
+ * never the module a spec resolves. What the card DOES on native is covered
19
+ * by dataTableMobileCards.spec.tsx, which now describes both platforms.
20
+ */
21
+
22
+ import { readdirSync, readFileSync } from "node:fs";
23
+ import { resolve } from "node:path";
24
+ import { describe, expect, it } from "vitest";
25
+
26
+ const DIR = resolve(__dirname);
27
+
28
+ function read(file: string): string {
29
+ return readFileSync(resolve(DIR, file), "utf-8");
30
+ }
31
+
32
+ /** A `<Component ... />` element at a call site, as text. */
33
+ function callSite(source: string, component: string): string {
34
+ const start = source.indexOf(`<${component}`);
35
+ if (start === -1) return "";
36
+ const end = source.indexOf("/>", start);
37
+ return end === -1 ? "" : source.slice(start, end);
38
+ }
39
+
40
+ /** Prop names that call site passes. */
41
+ function propNames(source: string, component: string): Set<string> {
42
+ const names = new Set<string>();
43
+ for (const match of callSite(source, component).matchAll(/(?:^|\s)([A-Za-z][A-Za-z0-9]*)=\{/g)) {
44
+ names.add(match[1]!);
45
+ }
46
+ return names;
47
+ }
48
+
49
+ const cardCallSite = (source: string) => callSite(source, "DataTableMobileCards");
50
+
51
+ describe("DataTable card renderer parity", () => {
52
+ it("the native root renders DataTableMobileCards and carries no card of its own", () => {
53
+ const native = read("index.native.tsx");
54
+ expect(native).toMatch(
55
+ /import \{[^}]*DataTableMobileCards[^}]*\} from "\.\/DataTableMobileCards"/,
56
+ );
57
+ expect(cardCallSite(native)).not.toBe("");
58
+ expect(native).not.toMatch(/renderMobileCard/);
59
+ });
60
+
61
+ it("the native root reaches the card field rule through the component, not directly", () => {
62
+ // MPO-285 left index.native.tsx importing classifyCardField/getColumnLabel
63
+ // to feed its own JSX. With the JSX shared there is nothing left to feed:
64
+ // an import here again would mean a second card had grown back.
65
+ const native = read("index.native.tsx");
66
+ expect(native).not.toMatch(/\bclassifyCardField\b/);
67
+ expect(native).not.toMatch(/\bgetColumnLabel\b/);
68
+ });
69
+
70
+ it("exactly one file in the component draws a card", () => {
71
+ const drawers = readdirSync(DIR)
72
+ .filter(
73
+ (file) => file.endsWith(".tsx") && !file.includes(".spec.") && !file.includes(".stories."),
74
+ )
75
+ .filter((file) => read(file).includes('testID="data-table-card"'));
76
+ expect(drawers).toEqual(["DataTableMobileCards.tsx"]);
77
+ });
78
+
79
+ // Both roots hand their row views the same contract. A prop web passes and
80
+ // native does not is how the card lost selection and the empty states in
81
+ // the first place, so it is the drift worth failing a build over.
82
+ it.each(["DataTableMobileCards", "DataTableDocumentList"])(
83
+ "native passes every %s prop web passes",
84
+ (component) => {
85
+ const web = propNames(read("DataTableRoot.tsx"), component);
86
+ const native = propNames(read("index.native.tsx"), component);
87
+ expect(web.size).toBeGreaterThan(0);
88
+ expect([...web].filter((prop) => !native.has(prop)).sort()).toEqual([]);
89
+ },
90
+ );
91
+
92
+ it("native hands the card list its press props, so a card is pressable there too", () => {
93
+ expect(cardCallSite(read("index.native.tsx"))).toMatch(/getRowPressProps/);
94
+ });
95
+ });
@@ -5,16 +5,50 @@
5
5
  * resolved layout is "cards" (below the `$sm` breakpoint with
6
6
  * layout="auto", or forced via layout="cards").
7
7
  *
8
- * Card anatomy (knobs system): `surface` + `cardSurface` fragments own
9
- * the card frame; the first visible column renders as the card title in
10
- * the header row (with selection checkbox / row number / delete), and
11
- * the remaining visible columns render as label/value rows. Column
12
- * visibility and order from table state apply unchanged — cells come
13
- * from `row.getVisibleCells()`. Values render through the same cell
14
- * renderers as the grid, inside `TableCellContext` so fields stay
15
- * chromeless (useIsInTableCell).
8
+ * Card anatomy: the first visible column is the card title in the header
9
+ * row (with selection checkbox / row number / delete). The remaining
10
+ * visible columns are NOT a stack of label/value rows any more — see
11
+ * `classifyCardField`. Column visibility and order from table state apply
12
+ * unchanged — cells come from `row.getVisibleCells()`. Values render
13
+ * through the same cell renderers as the grid, inside `TableCellContext`
14
+ * so fields stay chromeless (useIsInTableCell).
15
+ *
16
+ * The card is FLAT: no border, no elevation. A card list is drawn inside
17
+ * a panel that already has its own frame, and an outlined, shadowed card
18
+ * inside it reads as a box inside a box. The `cardSurface` fill and the
19
+ * gap between cards are what separate one row from the next.
16
20
  */
17
21
  import type React from "react";
22
+ /**
23
+ * Resolve the human label for a column: `meta.label` → string header →
24
+ * column id. `meta.label` is the explicit label channel for columns whose
25
+ * header is a render function or a bare glyph (the liked heart) — without
26
+ * it a card field row would print the raw column id (`_liked_by`).
27
+ */
28
+ export declare function getColumnLabel(cell: any): string;
29
+ export type CardFieldKind = "skip" | "control" | "bare" | "figure";
30
+ /**
31
+ * Whether a card field prints the column's name, and where it goes.
32
+ *
33
+ * A card is not the grid with its header repeated down the page. In the
34
+ * grid the header is written ONCE for a whole column of values; on a card
35
+ * it is written once per value, so a 12-column table prints 12 labels per
36
+ * record. That is what a label costs here, and most labels do not earn it.
37
+ *
38
+ * skip the raw value is absent. A field with nothing in it is not a
39
+ * fact — "Rejected —" spends a row saying nothing.
40
+ * control a button, a switch, a rating. The control names itself and
41
+ * needs its own tap target, so it keeps a row and takes no label.
42
+ * bare a string that is neither a number nor a date. "DIVIDEND
43
+ * CREDIT" and "Income:Dividends" say what they are; "Payee" and
44
+ * "Account" in front of them are the header printed twice. The
45
+ * name is kept for assistive tech via VisuallyHidden, which
46
+ * costs no layout.
47
+ * figure a number, a date, a boolean. "1,204" alone cannot be read, so
48
+ * these DO keep their label — inline beside the figure, not in a
49
+ * 40%-wide column of its own.
50
+ */
51
+ export declare function classifyCardField(cell: any): CardFieldKind;
18
52
  export interface DataTableMobileCardsProps<TData> {
19
53
  tableRows: any[];
20
54
  enableRowSelection?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"DataTableMobileCards.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/DataTableMobileCards.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAMH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAsB/B,MAAM,WAAW,yBAAyB,CAAC,KAAK;IAC9C,SAAS,EAAE,GAAG,EAAE,CAAC;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,KAAK,KAAK,CAAC,SAAS,CAAC;IACpF,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IACxD;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,GAAG,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjG,qEAAqE;IACrE,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EACtE,SAAS,EACT,kBAA0B,EAC1B,cAAc,EACd,aAAa,EACb,QAAQ,EACR,QAAgB,EAChB,UAAU,EACV,OAAO,EACP,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,gBAAwB,EACxB,sBAA8B,EAC9B,iBAAiB,EACjB,OAAe,EACf,cAAc,EACd,gBAAgB,EAChB,aAAqB,GACtB,EAAE,yBAAyB,CAAC,KAAK,CAAC,2CAqLlC"}
1
+ {"version":3,"file":"DataTableMobileCards.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/DataTableMobileCards.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,CAKhD;AAsBD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,GAAG,GAAG,aAAa,CAgB1D;AAED,MAAM,WAAW,yBAAyB,CAAC,KAAK;IAC9C,SAAS,EAAE,GAAG,EAAE,CAAC;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,KAAK,KAAK,CAAC,SAAS,CAAC;IACpF,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IACxD;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,GAAG,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjG,qEAAqE;IACrE,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EACtE,SAAS,EACT,kBAA0B,EAC1B,cAAc,EACd,aAAa,EACb,QAAQ,EACR,QAAgB,EAChB,UAAU,EACV,OAAO,EACP,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,gBAAwB,EACxB,sBAA8B,EAC9B,iBAAiB,EACjB,OAAe,EACf,cAAc,EACd,gBAAgB,EAChB,aAAqB,GACtB,EAAE,yBAAyB,CAAC,KAAK,CAAC,2CA+OlC"}
@@ -17,7 +17,7 @@ export { DataTableContext } from "./DataTableContext";
17
17
  export { DataTableStateContext, useDataTableState } from "./DataTableStateContext";
18
18
  export type { DataTableStateContextValue } from "./DataTableStateContext";
19
19
  export type { DataTableRootProps } from "./DataTableRoot";
20
- declare function DataTableRoot<TData extends Record<string, any> = Record<string, any>>({ data, columns, initialState, onStateChange, onRetry, isLoading: isLoadingProp, isFetching: isFetchingProp, syncWithUrl, urlDebounceMs, urlStrategy, manualFiltering, manualPagination, manualSorting, enableFiltering, enableSorting, enablePagination, pageSizeOptions, enableColumnVisibility, enableColumnOrdering, enableColumnResizing, enableGlobalSearch, enableViews, layout, enableVirtualization, virtualizationHeight, virtualizationOverscan, enableRowExpansion, renderSubComponent, enableColumnPinning, maxPinnedColumns, enableKeyboardNavigation: _enableKeyboardNavigation, onRowPress, rowLabel, readOnly, onCellChange, name, form: formProp, showRowNumbers, showAddRow, showDeleteRow, minRows, maxRows, defaultRow, onDataChange, rowCount, pageCount, renderToolbar, renderPagination, renderFilters, className: _className, size, compact, disabled, accent, error, warning, success, children, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, ...props }: DataTableRootProps<TData>): import("react/jsx-runtime").JSX.Element;
20
+ declare function DataTableRoot<TData extends Record<string, any> = Record<string, any>>({ data, columns, initialState, onStateChange, onRetry, isLoading: isLoadingProp, isFetching: isFetchingProp, syncWithUrl, urlDebounceMs, urlStrategy, manualFiltering, manualPagination, manualSorting, enableFiltering, enableSorting, enablePagination, pageSizeOptions, enableColumnVisibility, enableColumnOrdering, enableColumnResizing, enableGlobalSearch, enableViews, layout, enableVirtualization, virtualizationHeight, virtualizationOverscan, enableRowExpansion, renderSubComponent, enableRowSelection, selectAllScope: selectAllScopeProp, enableColumnPinning, maxPinnedColumns, enableKeyboardNavigation: _enableKeyboardNavigation, onRowPress, rowLabel, readOnly, onCellChange, name, form: formProp, showRowNumbers, showAddRow, showDeleteRow, minRows, maxRows, defaultRow, onDataChange, rowCount, pageCount, renderToolbar, renderPagination, renderFilters, className: _className, size, compact, disabled, accent, error, warning, success, children, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, ...props }: DataTableRootProps<TData>): import("react/jsx-runtime").JSX.Element;
21
21
  export declare const DataTable: typeof DataTableRoot & {
22
22
  Toolbar: typeof DataTableToolbar;
23
23
  Filters: typeof DataTableFilters;
@@ -1 +1 @@
1
- {"version":3,"file":"index.native.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/index.native.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuBH,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AASzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACnF,YAAY,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAC1E,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AA0D1D,iBAAS,aAAa,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAC9E,IAAI,EACJ,OAAO,EACP,YAAY,EACZ,aAAa,EACb,OAAO,EACP,SAAS,EAAE,aAAa,EACxB,UAAU,EAAE,cAAc,EAC1B,WAAmB,EACnB,aAAmB,EACnB,WAAW,EACX,eAAuB,EACvB,gBAAwB,EACxB,aAAqB,EACrB,eAAsB,EACtB,aAAoB,EACpB,gBAAuB,EACvB,eAAe,EACf,sBAA6B,EAC7B,oBAA4B,EAC5B,oBAA4B,EAC5B,kBAAyB,EACzB,WAAmB,EACnB,MAAe,EACf,oBAA4B,EAC5B,oBAAoB,EACpB,sBAA0B,EAC1B,kBAA0B,EAC1B,kBAAkB,EAClB,mBAA2B,EAC3B,gBAAoB,EACpB,wBAAwB,EAAE,yBAAiC,EAC3D,UAAU,EACV,QAAQ,EACR,QAAgB,EAChB,YAAY,EACZ,IAAI,EACJ,IAAI,EAAE,QAAQ,EACd,cAAsB,EACtB,UAAkB,EAClB,aAAqB,EACrB,OAAW,EACX,OAAO,EACP,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,SAAS,EAAE,UAAU,EACrB,IAAI,EACJ,OAAe,EACf,QAAgB,EAChB,MAAM,EACN,KAAK,EACL,OAAO,EACP,OAAO,EACP,QAAQ,EACR,YAAY,EAAE,SAAS,EACvB,kBAAkB,EAAE,eAAe,EACnC,GAAG,KAAK,EACT,EAAE,kBAAkB,CAAC,KAAK,CAAC,2CA08B3B;AAED,eAAO,MAAM,SAAS;;;;;;;;;;CAUpB,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,OAAO,SAAS,CAAC"}
1
+ {"version":3,"file":"index.native.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/index.native.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuBH,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAUzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACnF,YAAY,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAC1E,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AA0D1D,iBAAS,aAAa,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAC9E,IAAI,EACJ,OAAO,EACP,YAAY,EACZ,aAAa,EACb,OAAO,EACP,SAAS,EAAE,aAAa,EACxB,UAAU,EAAE,cAAc,EAC1B,WAAmB,EACnB,aAAmB,EACnB,WAAW,EACX,eAAuB,EACvB,gBAAwB,EACxB,aAAqB,EACrB,eAAsB,EACtB,aAAoB,EACpB,gBAAuB,EACvB,eAAe,EACf,sBAA6B,EAC7B,oBAA4B,EAC5B,oBAA4B,EAC5B,kBAAyB,EACzB,WAAmB,EACnB,MAAe,EACf,oBAA4B,EAC5B,oBAAoB,EACpB,sBAA0B,EAC1B,kBAA0B,EAC1B,kBAAkB,EAClB,kBAA0B,EAC1B,cAAc,EAAE,kBAAkB,EAClC,mBAA2B,EAC3B,gBAAoB,EACpB,wBAAwB,EAAE,yBAAiC,EAC3D,UAAU,EACV,QAAQ,EACR,QAAgB,EAChB,YAAY,EACZ,IAAI,EACJ,IAAI,EAAE,QAAQ,EACd,cAAsB,EACtB,UAAkB,EAClB,aAAqB,EACrB,OAAW,EACX,OAAO,EACP,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,SAAS,EAAE,UAAU,EACrB,IAAI,EACJ,OAAe,EACf,QAAgB,EAChB,MAAM,EACN,KAAK,EACL,OAAO,EACP,OAAO,EACP,QAAQ,EACR,YAAY,EAAE,SAAS,EACvB,kBAAkB,EAAE,eAAe,EACnC,GAAG,KAAK,EACT,EAAE,kBAAkB,CAAC,KAAK,CAAC,2CA25B3B;AAED,eAAO,MAAM,SAAS;;;;;;;;;;CAUpB,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,OAAO,SAAS,CAAC"}