@multiplatform.one/table 7.28.0 → 7.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/DataTable/CellContent.cjs +77 -0
- package/dist/cjs/components/DataTable/CellContent.native.js +88 -0
- package/dist/cjs/components/DataTable/CellContent.native.js.map +1 -0
- package/dist/cjs/components/DataTable/DataTableInfiniteTail.cjs +90 -0
- package/dist/cjs/components/DataTable/DataTableInfiniteTail.native.js +99 -0
- package/dist/cjs/components/DataTable/DataTableInfiniteTail.native.js.map +1 -0
- package/dist/cjs/components/DataTable/DataTableRoot.cjs +17 -47
- package/dist/cjs/components/DataTable/DataTableRoot.native.js +17 -56
- package/dist/cjs/components/DataTable/DataTableRoot.native.js.map +1 -1
- package/dist/cjs/components/DataTable/NativeInfiniteSentinel.cjs +62 -0
- package/dist/cjs/components/DataTable/NativeInfiniteSentinel.native.js +67 -0
- package/dist/cjs/components/DataTable/NativeInfiniteSentinel.native.js.map +1 -0
- package/dist/cjs/components/DataTable/index.native.js +75 -15
- package/dist/cjs/components/DataTable/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableBulkActions/index.cjs +70 -51
- package/dist/cjs/components/DataTableBulkActions/index.native.js +84 -63
- package/dist/cjs/components/DataTableBulkActions/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableCell/index.cjs +3 -1
- package/dist/cjs/components/DataTableCell/index.native.js +3 -1
- package/dist/cjs/components/DataTableCell/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableDateFilter/index.cjs +2 -1
- package/dist/cjs/components/DataTableDateFilter/index.native.js +2 -1
- package/dist/cjs/components/DataTableDateFilter/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableExport/index.cjs +2 -1
- package/dist/cjs/components/DataTableExport/index.native.js +2 -1
- package/dist/cjs/components/DataTableExport/index.native.js.map +1 -1
- package/dist/cjs/components/DataTableGrouping/index.cjs +2 -2
- package/dist/cjs/components/DataTableGrouping/index.native.js +2 -2
- package/dist/cjs/components/DataTableViewOptions/index.cjs +1 -1
- package/dist/cjs/components/DataTableViewOptions/index.native.js +1 -1
- package/dist/cjs/components/DataTableViewOptions/index.native.js.map +1 -1
- package/dist/cjs/components/KnobDialog.cjs +2 -1
- package/dist/cjs/components/KnobDialog.native.js +4 -2
- package/dist/cjs/components/KnobDialog.native.js.map +1 -1
- package/dist/cjs/components/TableInput/RowDetailEditor.cjs +1 -1
- package/dist/cjs/components/TableInput/index.native.js +1 -0
- package/dist/cjs/components/TableInput/index.native.js.map +1 -1
- package/dist/esm/components/DataTable/CellContent.mjs +52 -0
- package/dist/esm/components/DataTable/CellContent.mjs.map +1 -0
- package/dist/esm/components/DataTable/CellContent.native.js +60 -0
- package/dist/esm/components/DataTable/CellContent.native.js.map +1 -0
- package/dist/esm/components/DataTable/DataTableInfiniteTail.mjs +65 -0
- package/dist/esm/components/DataTable/DataTableInfiniteTail.mjs.map +1 -0
- package/dist/esm/components/DataTable/DataTableInfiniteTail.native.js +71 -0
- package/dist/esm/components/DataTable/DataTableInfiniteTail.native.js.map +1 -0
- package/dist/esm/components/DataTable/DataTableRoot.mjs +18 -48
- package/dist/esm/components/DataTable/DataTableRoot.mjs.map +1 -1
- package/dist/esm/components/DataTable/DataTableRoot.native.js +18 -57
- package/dist/esm/components/DataTable/DataTableRoot.native.js.map +1 -1
- package/dist/esm/components/DataTable/NativeInfiniteSentinel.mjs +36 -0
- package/dist/esm/components/DataTable/NativeInfiniteSentinel.mjs.map +1 -0
- package/dist/esm/components/DataTable/NativeInfiniteSentinel.native.js +38 -0
- package/dist/esm/components/DataTable/NativeInfiniteSentinel.native.js.map +1 -0
- package/dist/esm/components/DataTable/index.native.js +77 -17
- package/dist/esm/components/DataTable/index.native.js.map +1 -1
- package/dist/esm/components/DataTableBulkActions/index.mjs +71 -52
- package/dist/esm/components/DataTableBulkActions/index.mjs.map +1 -1
- package/dist/esm/components/DataTableBulkActions/index.native.js +85 -64
- package/dist/esm/components/DataTableBulkActions/index.native.js.map +1 -1
- package/dist/esm/components/DataTableCell/index.mjs +3 -1
- package/dist/esm/components/DataTableCell/index.mjs.map +1 -1
- package/dist/esm/components/DataTableCell/index.native.js +3 -1
- package/dist/esm/components/DataTableCell/index.native.js.map +1 -1
- package/dist/esm/components/DataTableDateFilter/index.mjs +2 -1
- package/dist/esm/components/DataTableDateFilter/index.mjs.map +1 -1
- package/dist/esm/components/DataTableDateFilter/index.native.js +2 -1
- package/dist/esm/components/DataTableDateFilter/index.native.js.map +1 -1
- package/dist/esm/components/DataTableExport/index.mjs +2 -1
- package/dist/esm/components/DataTableExport/index.mjs.map +1 -1
- package/dist/esm/components/DataTableExport/index.native.js +2 -1
- package/dist/esm/components/DataTableExport/index.native.js.map +1 -1
- package/dist/esm/components/DataTableGrouping/index.mjs +2 -2
- package/dist/esm/components/DataTableGrouping/index.native.js +2 -2
- package/dist/esm/components/DataTableViewOptions/index.mjs +2 -2
- package/dist/esm/components/DataTableViewOptions/index.mjs.map +1 -1
- package/dist/esm/components/DataTableViewOptions/index.native.js +2 -2
- package/dist/esm/components/DataTableViewOptions/index.native.js.map +1 -1
- package/dist/esm/components/KnobDialog.mjs +2 -1
- package/dist/esm/components/KnobDialog.mjs.map +1 -1
- package/dist/esm/components/KnobDialog.native.js +4 -2
- package/dist/esm/components/KnobDialog.native.js.map +1 -1
- package/dist/esm/components/TableInput/RowDetailEditor.mjs +2 -2
- package/dist/esm/components/TableInput/RowDetailEditor.mjs.map +1 -1
- package/dist/esm/components/TableInput/index.native.js +1 -0
- package/dist/esm/components/TableInput/index.native.js.map +1 -1
- package/dist/jsx/components/DataTable/CellContent.mjs +52 -0
- package/dist/jsx/components/DataTable/CellContent.mjs.map +1 -0
- package/dist/jsx/components/DataTable/CellContent.native.js +88 -0
- package/dist/jsx/components/DataTable/CellContent.native.js.map +1 -0
- package/dist/jsx/components/DataTable/DataTableInfiniteTail.mjs +65 -0
- package/dist/jsx/components/DataTable/DataTableInfiniteTail.mjs.map +1 -0
- package/dist/jsx/components/DataTable/DataTableInfiniteTail.native.js +99 -0
- package/dist/jsx/components/DataTable/DataTableInfiniteTail.native.js.map +1 -0
- package/dist/jsx/components/DataTable/DataTableRoot.mjs +18 -48
- package/dist/jsx/components/DataTable/DataTableRoot.mjs.map +1 -1
- package/dist/jsx/components/DataTable/DataTableRoot.native.js +17 -56
- package/dist/jsx/components/DataTable/DataTableRoot.native.js.map +1 -1
- package/dist/jsx/components/DataTable/NativeInfiniteSentinel.mjs +36 -0
- package/dist/jsx/components/DataTable/NativeInfiniteSentinel.mjs.map +1 -0
- package/dist/jsx/components/DataTable/NativeInfiniteSentinel.native.js +67 -0
- package/dist/jsx/components/DataTable/NativeInfiniteSentinel.native.js.map +1 -0
- package/dist/jsx/components/DataTable/index.native.js +75 -15
- package/dist/jsx/components/DataTable/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableBulkActions/index.mjs +71 -52
- package/dist/jsx/components/DataTableBulkActions/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableBulkActions/index.native.js +84 -63
- package/dist/jsx/components/DataTableBulkActions/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableCell/index.mjs +3 -1
- package/dist/jsx/components/DataTableCell/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableCell/index.native.js +3 -1
- package/dist/jsx/components/DataTableCell/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableDateFilter/index.mjs +2 -1
- package/dist/jsx/components/DataTableDateFilter/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableDateFilter/index.native.js +2 -1
- package/dist/jsx/components/DataTableDateFilter/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableExport/index.mjs +2 -1
- package/dist/jsx/components/DataTableExport/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableExport/index.native.js +2 -1
- package/dist/jsx/components/DataTableExport/index.native.js.map +1 -1
- package/dist/jsx/components/DataTableGrouping/index.mjs +2 -2
- package/dist/jsx/components/DataTableGrouping/index.native.js +2 -2
- package/dist/jsx/components/DataTableViewOptions/index.mjs +2 -2
- package/dist/jsx/components/DataTableViewOptions/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableViewOptions/index.native.js +1 -1
- package/dist/jsx/components/DataTableViewOptions/index.native.js.map +1 -1
- package/dist/jsx/components/KnobDialog.mjs +2 -1
- package/dist/jsx/components/KnobDialog.mjs.map +1 -1
- package/dist/jsx/components/KnobDialog.native.js +4 -2
- package/dist/jsx/components/KnobDialog.native.js.map +1 -1
- package/dist/jsx/components/TableInput/RowDetailEditor.mjs +2 -2
- package/dist/jsx/components/TableInput/RowDetailEditor.mjs.map +1 -1
- package/dist/jsx/components/TableInput/index.native.js +1 -0
- package/dist/jsx/components/TableInput/index.native.js.map +1 -1
- package/package.json +12 -12
- package/src/components/DataTable/CellContent.tsx +73 -0
- package/src/components/DataTable/DataTableInfiniteTail.tsx +94 -0
- package/src/components/DataTable/DataTableRoot.tsx +37 -81
- package/src/components/DataTable/NativeInfiniteSentinel.tsx +45 -0
- package/src/components/DataTable/cellWeight.native.spec.tsx +82 -0
- package/src/components/DataTable/cellWeight.spec.tsx +147 -0
- package/src/components/DataTable/index.native.tsx +108 -19
- package/src/components/DataTable/nativeRoot.spec.tsx +180 -0
- package/src/components/DataTableBulkActions/index.tsx +100 -71
- package/src/components/DataTableCell/index.tsx +3 -2
- package/src/components/DataTableDateFilter/index.tsx +2 -10
- package/src/components/DataTableExport/index.tsx +2 -1
- package/src/components/DataTableFilters/DataTableFilters.stories.tsx +7 -19
- package/src/components/DataTableGrouping/index.tsx +2 -2
- package/src/components/DataTablePagination/DataTablePagination.stories.tsx +12 -46
- package/src/components/DataTableViewOptions/index.tsx +2 -2
- package/src/components/KnobDialog.tsx +2 -0
- package/src/components/TableInput/RowDetailEditor.tsx +2 -2
- package/src/components/TableInput/index.native.tsx +1 -0
- package/types/components/DataTable/CellContent.d.ts +19 -0
- package/types/components/DataTable/CellContent.d.ts.map +1 -0
- package/types/components/DataTable/DataTableInfiniteTail.d.ts +22 -0
- package/types/components/DataTable/DataTableInfiniteTail.d.ts.map +1 -0
- package/types/components/DataTable/DataTableRoot.d.ts.map +1 -1
- package/types/components/DataTable/NativeInfiniteSentinel.d.ts +17 -0
- package/types/components/DataTable/NativeInfiniteSentinel.d.ts.map +1 -0
- package/types/components/DataTable/index.native.d.ts +1 -1
- package/types/components/DataTable/index.native.d.ts.map +1 -1
- package/types/components/DataTableBulkActions/index.d.ts.map +1 -1
- package/types/components/DataTableCell/index.d.ts.map +1 -1
- package/types/components/DataTableDateFilter/index.d.ts.map +1 -1
- package/types/components/DataTableExport/index.d.ts.map +1 -1
- package/types/components/KnobDialog.d.ts.map +1 -1
- package/types/components/TableInput/index.native.d.ts.map +1 -1
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infinite mode tail, shared by the web and native roots: the load-more
|
|
3
|
+
* trigger each platform supplies, an append skeleton bounded by the actual
|
|
4
|
+
* load (no infinite spinner), and an honest, announced end-of-data state
|
|
5
|
+
* (DG-ST-01/04).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Skeleton } from "@multiplatform.one/components";
|
|
9
|
+
import type { ReactNode } from "react";
|
|
10
|
+
import { useTranslation } from "react-i18next";
|
|
11
|
+
import { Text, View } from "tamagui";
|
|
12
|
+
import type { DataTableInfiniteState } from "../../types";
|
|
13
|
+
import { bidiIsolate, withInterp } from "../../utils/withInterp";
|
|
14
|
+
|
|
15
|
+
export interface DataTableInfiniteTailProps {
|
|
16
|
+
infinite: DataTableInfiniteState;
|
|
17
|
+
/** Rows on screen now, for "Showing N of M". */
|
|
18
|
+
visibleCount: number;
|
|
19
|
+
/** False when the virtualizer draws its own skeleton row. */
|
|
20
|
+
showAppendSkeleton: boolean;
|
|
21
|
+
rowHeightPx: number;
|
|
22
|
+
fontSize: any;
|
|
23
|
+
gapProps?: Record<string, unknown>;
|
|
24
|
+
/** The platform's load-more trigger; absent once the data is exhausted. */
|
|
25
|
+
sentinel?: ReactNode;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function DataTableInfiniteTail({
|
|
29
|
+
infinite,
|
|
30
|
+
visibleCount,
|
|
31
|
+
showAppendSkeleton,
|
|
32
|
+
rowHeightPx,
|
|
33
|
+
fontSize,
|
|
34
|
+
gapProps,
|
|
35
|
+
sentinel,
|
|
36
|
+
}: DataTableInfiniteTailProps) {
|
|
37
|
+
const { t } = useTranslation();
|
|
38
|
+
return (
|
|
39
|
+
<View flexDirection="column" width="100%">
|
|
40
|
+
{sentinel}
|
|
41
|
+
{showAppendSkeleton && infinite.isAppending ? (
|
|
42
|
+
<View
|
|
43
|
+
flexDirection="column"
|
|
44
|
+
width="100%"
|
|
45
|
+
{...gapProps}
|
|
46
|
+
paddingVertical="$2"
|
|
47
|
+
aria-hidden
|
|
48
|
+
{...({ "data-testid": "infinite-append-skeleton" } as any)}
|
|
49
|
+
>
|
|
50
|
+
{Array.from({ length: 3 }).map((_, i) => (
|
|
51
|
+
<Skeleton key={i} width="100%" height={rowHeightPx - 8} />
|
|
52
|
+
))}
|
|
53
|
+
</View>
|
|
54
|
+
) : null}
|
|
55
|
+
<Text
|
|
56
|
+
role="status"
|
|
57
|
+
aria-live="polite"
|
|
58
|
+
textAlign="center"
|
|
59
|
+
paddingVertical="$2"
|
|
60
|
+
fontSize={fontSize}
|
|
61
|
+
color="$color10"
|
|
62
|
+
{...({ "data-testid": "infinite-status" } as any)}
|
|
63
|
+
>
|
|
64
|
+
{/* Numeric labels are bidi-isolated so RTL contexts keep
|
|
65
|
+
the interpolated runs in order (FSI…PDI). */}
|
|
66
|
+
{infinite.isAppending
|
|
67
|
+
? t("Loading more rows…")
|
|
68
|
+
: infinite.exhausted
|
|
69
|
+
? typeof infinite.totalCount === "number" && infinite.totalCount > 0
|
|
70
|
+
? bidiIsolate(
|
|
71
|
+
withInterp(t("All {{count}} rows loaded", { count: infinite.totalCount }), {
|
|
72
|
+
count: infinite.totalCount,
|
|
73
|
+
}),
|
|
74
|
+
)
|
|
75
|
+
: t("End of results")
|
|
76
|
+
: typeof infinite.totalCount === "number"
|
|
77
|
+
? bidiIsolate(
|
|
78
|
+
withInterp(
|
|
79
|
+
t("Showing {{visible}} of {{total}} rows", {
|
|
80
|
+
visible: visibleCount,
|
|
81
|
+
total: infinite.totalCount,
|
|
82
|
+
}),
|
|
83
|
+
{ visible: visibleCount, total: infinite.totalCount },
|
|
84
|
+
),
|
|
85
|
+
)
|
|
86
|
+
: bidiIsolate(
|
|
87
|
+
withInterp(t("Showing {{visible}} rows", { visible: visibleCount }), {
|
|
88
|
+
visible: visibleCount,
|
|
89
|
+
}),
|
|
90
|
+
)}
|
|
91
|
+
</Text>
|
|
92
|
+
</View>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
@@ -22,13 +22,8 @@ import { flexRender } from "@tanstack/react-table";
|
|
|
22
22
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
23
23
|
import { ensureFocusVisibleRing, useResolvedKnobs } from "@multiplatform.one/theme";
|
|
24
24
|
import React, { useEffect, useRef, useMemo, useCallback, useState } from "react";
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
EmptyState,
|
|
28
|
-
Skeleton,
|
|
29
|
-
Tooltip,
|
|
30
|
-
useDirection,
|
|
31
|
-
} from "@multiplatform.one/components";
|
|
25
|
+
import { AsyncBoundary, EmptyState, Tooltip, useDirection } from "@multiplatform.one/components";
|
|
26
|
+
import type { FontSizeTokens } from "tamagui";
|
|
32
27
|
import { Text, View, isWeb, useMedia } from "tamagui";
|
|
33
28
|
import { useTranslation } from "react-i18next";
|
|
34
29
|
import { useDataTable } from "../../hooks/useDataTable";
|
|
@@ -59,6 +54,8 @@ import type {
|
|
|
59
54
|
import { DataTableCardControls, DataTableCardSortControl } from "./DataTableCardControls";
|
|
60
55
|
import { DataTableMobileCards } from "./DataTableMobileCards";
|
|
61
56
|
import { DataTableDocumentList } from "./DataTableDocumentList";
|
|
57
|
+
import { DataTableInfiniteTail } from "./DataTableInfiniteTail";
|
|
58
|
+
import { CellContent } from "./CellContent";
|
|
62
59
|
import { DataTableVirtualized } from "./DataTableVirtualized";
|
|
63
60
|
import { dataTableChrome, resolveDataTableIntent, rowIntentProps } from "./dataTableChrome";
|
|
64
61
|
import { tableCellKeyboardProps, tableRowStateProps } from "./rowStateChrome";
|
|
@@ -749,7 +746,11 @@ export function DataTableRoot<TData extends Record<string, any> = Record<string,
|
|
|
749
746
|
justifyContent={alignToFlex(customAlign)}
|
|
750
747
|
{...({ "data-cell-align": customAlign } as any)}
|
|
751
748
|
>
|
|
752
|
-
|
|
749
|
+
<CellContent
|
|
750
|
+
cellDef={columnDef.cell}
|
|
751
|
+
ctx={cell.getContext()}
|
|
752
|
+
fontSize={knobProps.label.fontSize as FontSizeTokens}
|
|
753
|
+
/>
|
|
753
754
|
</View>
|
|
754
755
|
);
|
|
755
756
|
}
|
|
@@ -785,7 +786,14 @@ export function DataTableRoot<TData extends Record<string, any> = Record<string,
|
|
|
785
786
|
/>
|
|
786
787
|
);
|
|
787
788
|
},
|
|
788
|
-
[
|
|
789
|
+
[
|
|
790
|
+
handleCellChange,
|
|
791
|
+
readOnly,
|
|
792
|
+
disabled,
|
|
793
|
+
knobProps.sizeToken,
|
|
794
|
+
knobProps.label.fontSize,
|
|
795
|
+
isCompact,
|
|
796
|
+
],
|
|
789
797
|
);
|
|
790
798
|
|
|
791
799
|
const extraColumnsBefore =
|
|
@@ -2224,79 +2232,27 @@ export function DataTableRoot<TData extends Record<string, any> = Record<string,
|
|
|
2224
2232
|
</View>
|
|
2225
2233
|
)}
|
|
2226
2234
|
|
|
2227
|
-
{/* Infinite mode tail: sentinel (non-virtualized trigger), append
|
|
2228
|
-
skeleton (bounded by the actual load — no infinite spinner),
|
|
2229
|
-
and an honest, announced end-of-data state (DG-ST-01/04). */}
|
|
2230
2235
|
{infinite ? (
|
|
2231
|
-
<
|
|
2232
|
-
{
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
{Array.from({ length: 3 }).map((_, i) => (
|
|
2252
|
-
<Skeleton key={i} width="100%" height={rowHeightPx - 8} />
|
|
2253
|
-
))}
|
|
2254
|
-
</View>
|
|
2255
|
-
) : null}
|
|
2256
|
-
<Text
|
|
2257
|
-
role="status"
|
|
2258
|
-
aria-live="polite"
|
|
2259
|
-
textAlign="center"
|
|
2260
|
-
paddingVertical="$2"
|
|
2261
|
-
fontSize={knobProps.sizeToken}
|
|
2262
|
-
color="$color10"
|
|
2263
|
-
{...({ "data-testid": "infinite-status" } as any)}
|
|
2264
|
-
>
|
|
2265
|
-
{/* Numeric labels are bidi-isolated so RTL contexts keep
|
|
2266
|
-
the interpolated runs in order (FSI…PDI). */}
|
|
2267
|
-
{infinite.isAppending
|
|
2268
|
-
? t("Loading more rows…")
|
|
2269
|
-
: infinite.exhausted
|
|
2270
|
-
? typeof infinite.totalCount === "number" && infinite.totalCount > 0
|
|
2271
|
-
? bidiIsolate(
|
|
2272
|
-
withInterp(
|
|
2273
|
-
t("All {{count}} rows loaded", { count: infinite.totalCount }),
|
|
2274
|
-
{
|
|
2275
|
-
count: infinite.totalCount,
|
|
2276
|
-
},
|
|
2277
|
-
),
|
|
2278
|
-
)
|
|
2279
|
-
: t("End of results")
|
|
2280
|
-
: typeof infinite.totalCount === "number"
|
|
2281
|
-
? bidiIsolate(
|
|
2282
|
-
withInterp(
|
|
2283
|
-
t("Showing {{visible}} of {{total}} rows", {
|
|
2284
|
-
visible: tableRows.length,
|
|
2285
|
-
total: infinite.totalCount,
|
|
2286
|
-
}),
|
|
2287
|
-
{ visible: tableRows.length, total: infinite.totalCount },
|
|
2288
|
-
),
|
|
2289
|
-
)
|
|
2290
|
-
: bidiIsolate(
|
|
2291
|
-
withInterp(
|
|
2292
|
-
t("Showing {{visible}} rows", { visible: tableRows.length }),
|
|
2293
|
-
{
|
|
2294
|
-
visible: tableRows.length,
|
|
2295
|
-
},
|
|
2296
|
-
),
|
|
2297
|
-
)}
|
|
2298
|
-
</Text>
|
|
2299
|
-
</View>
|
|
2236
|
+
<DataTableInfiniteTail
|
|
2237
|
+
infinite={infinite}
|
|
2238
|
+
visibleCount={tableRows.length}
|
|
2239
|
+
showAppendSkeleton={!isVirtualized}
|
|
2240
|
+
rowHeightPx={rowHeightPx}
|
|
2241
|
+
fontSize={knobProps.sizeToken}
|
|
2242
|
+
gapProps={knobProps.gap}
|
|
2243
|
+
sentinel={
|
|
2244
|
+
!isVirtualized && !infinite.exhausted && isWeb ? (
|
|
2245
|
+
<View
|
|
2246
|
+
// @ts-ignore web ref for IntersectionObserver
|
|
2247
|
+
ref={sentinelRef as any}
|
|
2248
|
+
height={1}
|
|
2249
|
+
width="100%"
|
|
2250
|
+
aria-hidden
|
|
2251
|
+
{...({ "data-testid": "infinite-sentinel" } as any)}
|
|
2252
|
+
/>
|
|
2253
|
+
) : null
|
|
2254
|
+
}
|
|
2255
|
+
/>
|
|
2300
2256
|
) : null}
|
|
2301
2257
|
|
|
2302
2258
|
{enableRowSelection && bulkBarPlacement === "bottom" ? (
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native load-more trigger for DataTable's infinite mode.
|
|
3
|
+
*
|
|
4
|
+
* Web watches its sentinel with an IntersectionObserver. React Native has
|
|
5
|
+
* none, and the table never sees its host's scroll events: the backoffice
|
|
6
|
+
* list scrolls the whole screen in a ScrollView the table sits inside. So
|
|
7
|
+
* the sentinel asks where it is on screen, on a short interval while more
|
|
8
|
+
* pages exist, and fires once it comes within `leadPx` of the window's
|
|
9
|
+
* bottom edge, the same lead web gives its observer.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { type ComponentRef, useEffect, useRef } from "react";
|
|
13
|
+
import { Dimensions, View } from "react-native";
|
|
14
|
+
|
|
15
|
+
export const NATIVE_SENTINEL_POLL_MS = 250;
|
|
16
|
+
|
|
17
|
+
export interface NativeInfiniteSentinelProps {
|
|
18
|
+
onVisible: () => void;
|
|
19
|
+
leadPx: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function NativeInfiniteSentinel({ onVisible, leadPx }: NativeInfiniteSentinelProps) {
|
|
23
|
+
const ref = useRef<ComponentRef<typeof View>>(null);
|
|
24
|
+
const onVisibleRef = useRef(onVisible);
|
|
25
|
+
onVisibleRef.current = onVisible;
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
let active = true;
|
|
29
|
+
const timer = setInterval(() => {
|
|
30
|
+
ref.current?.measureInWindow((_x, y, width) => {
|
|
31
|
+
// An unmeasured view reports all zeros; width 0 means not laid out.
|
|
32
|
+
if (!active || width <= 0) return;
|
|
33
|
+
if (y <= Dimensions.get("window").height + leadPx) onVisibleRef.current();
|
|
34
|
+
});
|
|
35
|
+
}, NATIVE_SENTINEL_POLL_MS);
|
|
36
|
+
return () => {
|
|
37
|
+
active = false;
|
|
38
|
+
clearInterval(timer);
|
|
39
|
+
};
|
|
40
|
+
}, [leadPx]);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<View ref={ref} style={{ height: 1, width: "100%" }} aria-hidden testID="infinite-sentinel" />
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MPO-446 table arm (native): the native root renders body cells through the
|
|
3
|
+
* same CellContent as web, so a primitive value rides house Text and takes
|
|
4
|
+
* the fontWeight and bodyFont knobs. index.native.tsx pulls in FlashList and
|
|
5
|
+
* the suite runs under happy-dom, so the root is read as source (the
|
|
6
|
+
* ./nativeCardParity.spec.tsx precedent) and CellContent renders under the
|
|
7
|
+
* native platform flags.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFileSync } from "node:fs";
|
|
11
|
+
import { resolve } from "node:path";
|
|
12
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
13
|
+
import { Preset } from "@multiplatform.one/theme";
|
|
14
|
+
import { cleanup, screen } from "@testing-library/react";
|
|
15
|
+
import { Text as RawText } from "tamagui";
|
|
16
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
17
|
+
import { CellContent } from "./CellContent";
|
|
18
|
+
|
|
19
|
+
vi.mock("@multiplatform.one/platform", async (importOriginal) => ({
|
|
20
|
+
...(await importOriginal<typeof import("@multiplatform.one/platform")>()),
|
|
21
|
+
isWeb: false,
|
|
22
|
+
isNative: true,
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
afterEach(cleanup);
|
|
26
|
+
|
|
27
|
+
function classes(el: Element, prefix: string): string[] {
|
|
28
|
+
return String(el.className || "")
|
|
29
|
+
.split(" ")
|
|
30
|
+
.filter((c) => c.startsWith(prefix))
|
|
31
|
+
.sort();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function explicit(prefix: string, props: Record<string, string>): string[] {
|
|
35
|
+
renderWithProviders(<RawText {...props}>probe</RawText>);
|
|
36
|
+
const got = classes(screen.getByText("probe"), prefix);
|
|
37
|
+
cleanup();
|
|
38
|
+
return got;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe("DataTable custom cell values follow the text knobs (native, MPO-446)", () => {
|
|
42
|
+
it("really is running with the native platform flags", async () => {
|
|
43
|
+
const platform = await import("@multiplatform.one/platform");
|
|
44
|
+
expect(platform.isWeb).toBe(false);
|
|
45
|
+
expect(platform.isNative).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("the native root renders body cells through CellContent with the value voice size", () => {
|
|
49
|
+
const native = readFileSync(resolve(__dirname, "index.native.tsx"), "utf-8");
|
|
50
|
+
const start = native.indexOf("<CellContent");
|
|
51
|
+
expect(start).toBeGreaterThan(-1);
|
|
52
|
+
const site = native.slice(start, native.indexOf("/>", start));
|
|
53
|
+
expect(site).toMatch(/cellDef=\{columnDef\.cell\}/);
|
|
54
|
+
expect(site).toMatch(/fontSize=\{knobProps\.label\.fontSize/);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it.each([
|
|
58
|
+
["regular", "400"],
|
|
59
|
+
["bold", "700"],
|
|
60
|
+
] as const)("string and number values paint the knob weight at %s", (fontWeight, want) => {
|
|
61
|
+
const expected = explicit("_fow-", { fontWeight: want });
|
|
62
|
+
for (const value of ["Bulbasaur", 4461]) {
|
|
63
|
+
renderWithProviders(
|
|
64
|
+
<Preset overrides={{ fontWeight }}>
|
|
65
|
+
<CellContent cellDef={() => value} ctx={{}} fontSize="$3" />
|
|
66
|
+
</Preset>,
|
|
67
|
+
);
|
|
68
|
+
expect(classes(screen.getByText(String(value)), "_fow-"), String(value)).toEqual(expected);
|
|
69
|
+
cleanup();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("string values take the bodyFont family", () => {
|
|
74
|
+
const expected = explicit("_ff-", { fontFamily: "$mono" });
|
|
75
|
+
renderWithProviders(
|
|
76
|
+
<Preset overrides={{ bodyFont: "mono" }}>
|
|
77
|
+
<CellContent cellDef={() => "Bulbasaur"} ctx={{}} fontSize="$3" />
|
|
78
|
+
</Preset>,
|
|
79
|
+
);
|
|
80
|
+
expect(classes(screen.getByText("Bulbasaur"), "_ff-")).toEqual(expected);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MPO-446 table arm (web): a DataTable custom cell's value takes the
|
|
3
|
+
* fontWeight and bodyFont knobs.
|
|
4
|
+
*
|
|
5
|
+
* `cell: (info) => info.getValue()` returns a primitive, and flexRender
|
|
6
|
+
* dropped it as a bare text node into the cell's alignment View: a div with
|
|
7
|
+
* no text voice, so the value painted the page's 400 under Weight bold. The
|
|
8
|
+
* value now rides house Text (./CellContent.tsx). ./cellWeight.native.spec.tsx
|
|
9
|
+
* is the native arm.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
13
|
+
import { Preset } from "@multiplatform.one/theme";
|
|
14
|
+
import { act, cleanup, screen } from "@testing-library/react";
|
|
15
|
+
import { useState, type ReactNode } from "react";
|
|
16
|
+
import { Text as RawText } from "tamagui";
|
|
17
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
18
|
+
import type { DataTableColumn } from "../../types";
|
|
19
|
+
import { CellContent } from "./CellContent";
|
|
20
|
+
import { DataTable } from "./index";
|
|
21
|
+
|
|
22
|
+
type Row = { id: number; name: string; status: string; amount: string };
|
|
23
|
+
|
|
24
|
+
const rows: Row[] = [{ id: 4461, name: "Bulbasaur", status: "sleeping", amount: "12.50" }];
|
|
25
|
+
|
|
26
|
+
const columns: DataTableColumn<Row>[] = [
|
|
27
|
+
{ id: "id", accessorKey: "id", header: "ID", cell: (info) => info.getValue() },
|
|
28
|
+
{ id: "name", accessorKey: "name", header: "Name", cell: (info) => info.getValue() },
|
|
29
|
+
{
|
|
30
|
+
id: "status",
|
|
31
|
+
accessorKey: "status",
|
|
32
|
+
header: "Status",
|
|
33
|
+
cell: (info) => <RawText fontWeight="300">{String(info.getValue())}</RawText>,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: "amount",
|
|
37
|
+
accessorKey: "amount",
|
|
38
|
+
header: "Amount",
|
|
39
|
+
field: "money",
|
|
40
|
+
fieldProps: { currency: "USD", locale: "en-US" },
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
const weights = [
|
|
45
|
+
["regular", "400"],
|
|
46
|
+
["bold", "700"],
|
|
47
|
+
] as const;
|
|
48
|
+
|
|
49
|
+
afterEach(cleanup);
|
|
50
|
+
|
|
51
|
+
function classes(el: Element, prefix: string): string[] {
|
|
52
|
+
return String(el.className || "")
|
|
53
|
+
.split(" ")
|
|
54
|
+
.filter((c) => c.startsWith(prefix))
|
|
55
|
+
.sort();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The class a prop paints, read off a raw node so no house wiring is involved. */
|
|
59
|
+
function explicit(prefix: string, props: Record<string, string>): string[] {
|
|
60
|
+
renderWithProviders(<RawText {...props}>probe</RawText>);
|
|
61
|
+
const got = classes(screen.getByText("probe"), prefix);
|
|
62
|
+
cleanup();
|
|
63
|
+
return got;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function renderTable(overrides: Record<string, string>) {
|
|
67
|
+
return renderWithProviders(
|
|
68
|
+
<Preset overrides={overrides as never}>
|
|
69
|
+
<DataTable
|
|
70
|
+
data={rows}
|
|
71
|
+
columns={columns as DataTableColumn<Record<string, any>>[]}
|
|
72
|
+
layout="table"
|
|
73
|
+
enablePagination={false}
|
|
74
|
+
readOnly
|
|
75
|
+
/>
|
|
76
|
+
</Preset>,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
describe("DataTable custom cell values follow the text knobs (web, MPO-446)", () => {
|
|
81
|
+
it.each(weights)("string and number values paint the knob weight at %s", (fontWeight, want) => {
|
|
82
|
+
const expected = explicit("_fow-", { fontWeight: want });
|
|
83
|
+
renderTable({ fontWeight });
|
|
84
|
+
for (const text of ["Bulbasaur", "4461"]) {
|
|
85
|
+
const node = screen.getByText(text);
|
|
86
|
+
expect(node.closest("[data-cell-align]")).not.toBe(node);
|
|
87
|
+
expect(classes(node, "_fow-"), text).toEqual(expected);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("string values take the bodyFont family", () => {
|
|
92
|
+
const expected = explicit("_ff-", { fontFamily: "$mono" });
|
|
93
|
+
renderTable({ bodyFont: "mono" });
|
|
94
|
+
expect(classes(screen.getByText("Bulbasaur"), "_ff-")).toEqual(expected);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it.each(weights)("a money value paints the knob weight at %s", (fontWeight, want) => {
|
|
98
|
+
const expected = explicit("_fow-", { fontWeight: want });
|
|
99
|
+
renderTable({ fontWeight });
|
|
100
|
+
expect(classes(screen.getByText(/12\.50/), "_fow-")).toEqual(expected);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("a renderer that returns an element keeps its own weight", () => {
|
|
104
|
+
const expected = explicit("_fow-", { fontWeight: "300" });
|
|
105
|
+
renderTable({ fontWeight: "bold" });
|
|
106
|
+
expect(classes(screen.getByText("sleeping"), "_fow-")).toEqual(expected);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe("CellContent keeps flexRender's component identity per renderer", () => {
|
|
111
|
+
function Counter({ start }: { start: number }): ReactNode {
|
|
112
|
+
const [n, setN] = useState(start);
|
|
113
|
+
return (
|
|
114
|
+
<RawText onPress={() => setN((v) => v + 1)} data-testid="counter">
|
|
115
|
+
{n}
|
|
116
|
+
</RawText>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
it("a renderer's hook state survives a re-render with the same renderer", () => {
|
|
121
|
+
const render = (ctx: unknown) => <Counter start={(ctx as { start: number }).start} />;
|
|
122
|
+
const { rerender } = renderWithProviders(
|
|
123
|
+
<CellContent cellDef={render} ctx={{ start: 1 }} fontSize="$3" />,
|
|
124
|
+
);
|
|
125
|
+
act(() => screen.getByTestId("counter").click());
|
|
126
|
+
expect(screen.getByTestId("counter").textContent).toBe("2");
|
|
127
|
+
rerender(<CellContent cellDef={render} ctx={{ start: 1 }} fontSize="$3" />);
|
|
128
|
+
expect(screen.getByTestId("counter").textContent).toBe("2");
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("a new renderer mounts its own hooks instead of reading the old renderer's slots", () => {
|
|
132
|
+
const first = () => {
|
|
133
|
+
const [label] = useState("first renderer");
|
|
134
|
+
return label;
|
|
135
|
+
};
|
|
136
|
+
const second = () => {
|
|
137
|
+
const [label] = useState("second renderer");
|
|
138
|
+
return label;
|
|
139
|
+
};
|
|
140
|
+
const { rerender } = renderWithProviders(
|
|
141
|
+
<CellContent cellDef={first} ctx={{}} fontSize="$3" />,
|
|
142
|
+
);
|
|
143
|
+
expect(screen.getByText("first renderer")).toBeInTheDocument();
|
|
144
|
+
rerender(<CellContent cellDef={second} ctx={{}} fontSize="$3" />);
|
|
145
|
+
expect(screen.getByText("second renderer")).toBeInTheDocument();
|
|
146
|
+
});
|
|
147
|
+
});
|