@colixsystems/widget-sdk 0.247.0 → 0.248.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/README.md +53 -5
- package/dist/board-auto-scroll.js +59 -0
- package/dist/board-view.js +327 -0
- package/dist/contract.cjs +70 -2
- package/dist/contract.js +70 -2
- package/dist/data-table-logic.js +179 -0
- package/dist/data-table-view.js +388 -0
- package/dist/data-table.js +22 -0
- package/dist/data-table.native.js +14 -0
- package/dist/drag-kit-view.js +310 -0
- package/dist/file-upload-view.js +13 -2
- package/dist/filter-bar-logic.js +143 -0
- package/dist/filter-bar-view.js +381 -0
- package/dist/filter-bar.js +15 -0
- package/dist/filter-bar.native.js +13 -0
- package/dist/form-control-logic.js +20 -3
- package/dist/form-control-view.js +18 -0
- package/dist/form-layout-logic.js +92 -0
- package/dist/form-view.js +268 -0
- package/dist/form.js +15 -0
- package/dist/form.native.js +9 -0
- package/dist/index.d.ts +372 -5
- package/dist/index.js +8 -0
- package/dist/index.native.js +8 -0
- package/dist/menu-view.js +5 -2
- package/dist/people-picker-view.js +3 -0
- package/dist/radio-group-view.js +7 -3
- package/dist/select-view.js +7 -2
- package/dist/sortable-list-view.js +228 -0
- package/dist/sortable-logic.js +273 -0
- package/dist/sortable-tokens.js +120 -0
- package/dist/sortable-view.js +24 -0
- package/dist/sortable.js +39 -0
- package/dist/sortable.native.js +30 -0
- package/dist/text-field-view.js +7 -2
- package/package.json +1 -1
- package/src/contract.js +70 -2
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
// The ONE implementation behind `<DataTable>`. The bindings (`data-table.js`
|
|
2
|
+
// / `data-table.native.js`) differ only in where the primitives and the list,
|
|
3
|
+
// loading, menu and button parts come from, so the hosts cannot drift.
|
|
4
|
+
//
|
|
5
|
+
// A record list in three tiers from its own measured width: stacked
|
|
6
|
+
// label/value cards, one card per row, then aligned columns under a sticky
|
|
7
|
+
// header. Every tier is drawn by `<List>` / `<ListRow>` / `<ListHeader>`, so
|
|
8
|
+
// the theme's `list` scope (and `card` scope for the cards) styles it.
|
|
9
|
+
|
|
10
|
+
import React from "react";
|
|
11
|
+
import { useContainerWidth, useHostTheme, useHostSharedText } from "./hooks.js";
|
|
12
|
+
import { DEFAULT_THEME_TOKENS } from "./_theme-tokens.js";
|
|
13
|
+
import { CONTRACT } from "./contract.js";
|
|
14
|
+
import { Icon } from "./icon.js";
|
|
15
|
+
import { LIST_TOKEN_NAMES, resolveListTokens } from "./list-tokens.js";
|
|
16
|
+
import { textRoleStyle } from "./text-roles.js";
|
|
17
|
+
import {
|
|
18
|
+
DATA_TABLE_HEADER_TARGET,
|
|
19
|
+
DATA_TABLE_SKELETON_ROWS,
|
|
20
|
+
DEFAULT_SORT_LABELS,
|
|
21
|
+
ariaSortFor,
|
|
22
|
+
cellText,
|
|
23
|
+
dataTableLayout,
|
|
24
|
+
justifyFor,
|
|
25
|
+
nextSort,
|
|
26
|
+
normaliseColumns,
|
|
27
|
+
normaliseSort,
|
|
28
|
+
resolveMaxHeight,
|
|
29
|
+
rowKey,
|
|
30
|
+
sortRows,
|
|
31
|
+
sortSpokenLabel,
|
|
32
|
+
sortStateWords,
|
|
33
|
+
sortsClientSide,
|
|
34
|
+
} from "./data-table-logic.js";
|
|
35
|
+
|
|
36
|
+
const h = React.createElement;
|
|
37
|
+
const EMPTY_FALLBACK = CONTRACT.sharedTranslationKeys.no_results.en;
|
|
38
|
+
const SORT_ICON_SIZE = 14;
|
|
39
|
+
const SORT_GLYPHS = Object.freeze({
|
|
40
|
+
none: "chevrons-up-down",
|
|
41
|
+
ascending: "arrow-up",
|
|
42
|
+
descending: "arrow-down",
|
|
43
|
+
});
|
|
44
|
+
const VISUALLY_HIDDEN = {
|
|
45
|
+
position: "absolute",
|
|
46
|
+
width: 1,
|
|
47
|
+
height: 1,
|
|
48
|
+
overflow: "hidden",
|
|
49
|
+
opacity: 0,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
function useTableTheme() {
|
|
53
|
+
return useHostTheme() || DEFAULT_THEME_TOKENS;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function listOverrides(props) {
|
|
57
|
+
const out = {};
|
|
58
|
+
for (const name of LIST_TOKEN_NAMES) {
|
|
59
|
+
if (props[name] !== undefined && props[name] !== null) out[name] = props[name];
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function testId(base, part) {
|
|
65
|
+
return base ? `${base}-${part}` : undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function cellBox(column) {
|
|
69
|
+
return { flex: column.flex, minWidth: column.minWidth, justifyContent: "center" };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Bind the shared table to one platform's primitives and parts.
|
|
74
|
+
*
|
|
75
|
+
* @param {{View: any, Text: any, Pressable: any, ScrollView: any, Platform: any,
|
|
76
|
+
* AccessibilityInfo: any}} rn
|
|
77
|
+
* @param {{List: any, ListRow: any, ListHeader: any, EmptyState: any,
|
|
78
|
+
* Skeleton: any, Menu: any, Button: any}} parts — the same host's components.
|
|
79
|
+
*/
|
|
80
|
+
export function makeDataTable(rn, parts) {
|
|
81
|
+
const { View, Text, Pressable, ScrollView, Platform, AccessibilityInfo } = rn;
|
|
82
|
+
const { List, ListRow, ListHeader, EmptyState, Skeleton, Menu, Button } = parts;
|
|
83
|
+
const web = Platform.OS === "web";
|
|
84
|
+
|
|
85
|
+
// A string or number becomes text in the slot's style; a node is drawn as given.
|
|
86
|
+
function cellNode(column, row, renderCell, style, lines) {
|
|
87
|
+
const own = column.render
|
|
88
|
+
? column.render(row, column)
|
|
89
|
+
: typeof renderCell === "function"
|
|
90
|
+
? renderCell(row, column)
|
|
91
|
+
: undefined;
|
|
92
|
+
const value = own !== undefined ? own : cellText(row && row[column.key]) || "—";
|
|
93
|
+
if (typeof value !== "string" && typeof value !== "number") return value;
|
|
94
|
+
return h(Text, { style: [style, { textAlign: column.align }], numberOfLines: lines }, value);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// `change` is null when nobody can act on a sort: a controlled one without
|
|
98
|
+
// `onSortChange` would be announced but never happen.
|
|
99
|
+
function useSort({ sort, defaultSort, onSortChange, labels, columns }) {
|
|
100
|
+
const [own, setOwn] = React.useState(() => normaliseSort(defaultSort));
|
|
101
|
+
const [spoken, setSpoken] = React.useState("");
|
|
102
|
+
const controlled = sort !== undefined;
|
|
103
|
+
const current = controlled ? normaliseSort(sort) : own;
|
|
104
|
+
const reported = typeof onSortChange === "function";
|
|
105
|
+
// The web refocuses the menu's trigger, whose name then says the sort.
|
|
106
|
+
const change = (key, { announce = true } = {}) => {
|
|
107
|
+
const next = nextSort(current, key);
|
|
108
|
+
if (!controlled) setOwn(next);
|
|
109
|
+
if (reported) onSortChange(next);
|
|
110
|
+
if (!announce) return;
|
|
111
|
+
const column = columns.find((c) => c.key === key);
|
|
112
|
+
const text = sortSpokenLabel(String(column ? column.label : key), { key }, next, labels);
|
|
113
|
+
if (web) setSpoken(text);
|
|
114
|
+
else if (AccessibilityInfo && typeof AccessibilityInfo.announceForAccessibility === "function") {
|
|
115
|
+
AccessibilityInfo.announceForAccessibility(text);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
return [current, controlled && !reported ? null : change, spoken];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// The sort state is the button's description, so the tap's announcement
|
|
122
|
+
// is the one thing a reader says when it changes.
|
|
123
|
+
function HeaderCell({ column, sort, onSort, labels, testID }) {
|
|
124
|
+
const { colors, typography } = useTableTheme();
|
|
125
|
+
const stateId = React.useId();
|
|
126
|
+
const label = h(
|
|
127
|
+
Text,
|
|
128
|
+
{
|
|
129
|
+
style: [textRoleStyle("overline", typography, colors), { textAlign: column.align, flexShrink: 1 }],
|
|
130
|
+
numberOfLines: 1,
|
|
131
|
+
},
|
|
132
|
+
column.label,
|
|
133
|
+
);
|
|
134
|
+
if (!column.sortable || !onSort) return h(View, { style: cellBox(column) }, label);
|
|
135
|
+
const state = ariaSortFor(column, sort);
|
|
136
|
+
const words = sortStateWords(column, sort, labels);
|
|
137
|
+
const described = words ? (web ? { "aria-describedby": stateId } : { accessibilityHint: words }) : null;
|
|
138
|
+
return h(
|
|
139
|
+
View,
|
|
140
|
+
{ style: cellBox(column) },
|
|
141
|
+
web && words ? h(Text, { nativeID: stateId, "aria-hidden": true, style: VISUALLY_HIDDEN }, words) : null,
|
|
142
|
+
h(
|
|
143
|
+
Pressable,
|
|
144
|
+
{
|
|
145
|
+
onPress: () => onSort(column.key),
|
|
146
|
+
accessibilityRole: "button",
|
|
147
|
+
accessibilityLabel: String(column.label),
|
|
148
|
+
...described,
|
|
149
|
+
testID: testId(testID, `sort-${column.key}`),
|
|
150
|
+
style: {
|
|
151
|
+
minHeight: DATA_TABLE_HEADER_TARGET,
|
|
152
|
+
flexDirection: "row",
|
|
153
|
+
alignItems: "center",
|
|
154
|
+
justifyContent: justifyFor(column.align),
|
|
155
|
+
gap: 4,
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
label,
|
|
159
|
+
h(Icon, {
|
|
160
|
+
name: SORT_GLYPHS[state],
|
|
161
|
+
size: SORT_ICON_SIZE,
|
|
162
|
+
color: state === "none" ? colors.onSurfaceMuted : colors.onSurface,
|
|
163
|
+
}),
|
|
164
|
+
),
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Inside a card's scope, so the text takes that card's own inks.
|
|
169
|
+
function StackedBody({ columns, row, renderCell }) {
|
|
170
|
+
const { colors, typography, spacing } = useTableTheme();
|
|
171
|
+
const caption = textRoleStyle("caption", typography, colors);
|
|
172
|
+
const body = textRoleStyle("body", typography, colors);
|
|
173
|
+
return h(
|
|
174
|
+
View,
|
|
175
|
+
{ style: { flex: 1, minWidth: 0, gap: spacing.xs } },
|
|
176
|
+
...columns.map((column) =>
|
|
177
|
+
h(
|
|
178
|
+
View,
|
|
179
|
+
{ key: column.key, style: { gap: 2 } },
|
|
180
|
+
h(Text, { style: caption }, column.label),
|
|
181
|
+
cellNode({ ...column, align: "left" }, row, renderCell, body, undefined),
|
|
182
|
+
),
|
|
183
|
+
),
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function CardBody({ columns, row, renderCell }) {
|
|
188
|
+
const { colors, typography, spacing } = useTableTheme();
|
|
189
|
+
const [first, ...details] = columns;
|
|
190
|
+
const caption = textRoleStyle("caption", typography, colors);
|
|
191
|
+
const body = textRoleStyle("body", typography, colors);
|
|
192
|
+
return h(
|
|
193
|
+
View,
|
|
194
|
+
{ style: { flex: 1, minWidth: 0, gap: 2 } },
|
|
195
|
+
cellNode({ ...first, align: "left" }, row, renderCell, textRoleStyle("subtitle", typography, colors), 1),
|
|
196
|
+
details.length > 0
|
|
197
|
+
? h(
|
|
198
|
+
View,
|
|
199
|
+
{ style: { flexDirection: "row", alignItems: "center", gap: spacing.md, minWidth: 0 } },
|
|
200
|
+
...details.map((column) =>
|
|
201
|
+
h(
|
|
202
|
+
View,
|
|
203
|
+
{
|
|
204
|
+
key: column.key,
|
|
205
|
+
style: { flexDirection: "row", alignItems: "center", gap: spacing.xs, flexShrink: 1, minWidth: 0 },
|
|
206
|
+
},
|
|
207
|
+
h(Text, { style: caption, numberOfLines: 1 }, column.label),
|
|
208
|
+
cellNode({ ...column, align: "left" }, row, renderCell, body, 1),
|
|
209
|
+
),
|
|
210
|
+
),
|
|
211
|
+
)
|
|
212
|
+
: null,
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function ColumnCells({ columns, row, renderCell }) {
|
|
217
|
+
const { colors, typography } = useTableTheme();
|
|
218
|
+
const body = textRoleStyle("body", typography, colors);
|
|
219
|
+
return columns.map((column) =>
|
|
220
|
+
h(View, { key: column.key, style: cellBox(column) }, cellNode(column, row, renderCell, body, 1)),
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function rowsOf(table, layout) {
|
|
225
|
+
const { columns, rows, keyField, renderCell, onRowPress, overrides, testID } = table;
|
|
226
|
+
const Body = layout === "stacked" ? StackedBody : layout === "cards" ? CardBody : ColumnCells;
|
|
227
|
+
return h(
|
|
228
|
+
List,
|
|
229
|
+
{ ...overrides, variant: layout === "columns" ? "plain" : "card" },
|
|
230
|
+
...rows.map((row, index) => {
|
|
231
|
+
const key = rowKey(row, keyField, index);
|
|
232
|
+
return h(
|
|
233
|
+
ListRow,
|
|
234
|
+
{
|
|
235
|
+
key,
|
|
236
|
+
onPress: typeof onRowPress === "function" ? () => onRowPress(row) : undefined,
|
|
237
|
+
testID: testId(testID, `row-${key}`),
|
|
238
|
+
},
|
|
239
|
+
h(Body, { columns, row, renderCell }),
|
|
240
|
+
);
|
|
241
|
+
}),
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function bodyOf(table, layout) {
|
|
246
|
+
if (table.loading && table.rows.length === 0) {
|
|
247
|
+
return h(Skeleton, { shape: "listRow", count: DATA_TABLE_SKELETON_ROWS, leading: false });
|
|
248
|
+
}
|
|
249
|
+
if (table.rows.length === 0) {
|
|
250
|
+
return h(EmptyState, {
|
|
251
|
+
title: table.emptyTitle,
|
|
252
|
+
message: table.emptyMessage,
|
|
253
|
+
testID: testId(table.testID, "empty"),
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
return rowsOf(table, layout);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function SortMenu({ table }) {
|
|
260
|
+
const { columns, sort, onSort, labels, testID } = table;
|
|
261
|
+
const sortable = columns.filter((c) => c.sortable);
|
|
262
|
+
if (sortable.length === 0 || !onSort) return null;
|
|
263
|
+
const active = sort ? sortable.find((c) => c.key === sort.key) : undefined;
|
|
264
|
+
const state = active ? ariaSortFor(active, sort) : "none";
|
|
265
|
+
const spoken = active ? sortSpokenLabel(String(active.label), active, sort, labels) : "";
|
|
266
|
+
return h(
|
|
267
|
+
View,
|
|
268
|
+
{ style: { flexDirection: "row", justifyContent: "flex-end" } },
|
|
269
|
+
h(Menu, {
|
|
270
|
+
accessibilityLabel: labels.sort,
|
|
271
|
+
testID: testId(testID, "sort-menu"),
|
|
272
|
+
trigger: h(Button, {
|
|
273
|
+
variant: "ghost",
|
|
274
|
+
title: active ? `${labels.sort}: ${active.label}` : labels.sort,
|
|
275
|
+
icon: SORT_GLYPHS[state],
|
|
276
|
+
iconPosition: "trailing",
|
|
277
|
+
accessibilityLabel: active ? `${labels.sort}: ${spoken}` : labels.sort,
|
|
278
|
+
testID: testId(testID, "sort"),
|
|
279
|
+
}),
|
|
280
|
+
items: sortable.map((column) => ({
|
|
281
|
+
key: column.key,
|
|
282
|
+
label: String(column.label),
|
|
283
|
+
icon: active && active.key === column.key ? SORT_GLYPHS[state] : undefined,
|
|
284
|
+
accessibilityLabel: sortSpokenLabel(String(column.label), column, sort, labels),
|
|
285
|
+
onPress: () => onSort(column.key, { announce: !web }),
|
|
286
|
+
testID: testId(testID, `sort-${column.key}`),
|
|
287
|
+
})),
|
|
288
|
+
}),
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Opaque, or scrolled rows show through: the rows' own Lists fill first.
|
|
293
|
+
function headerFill(theme, overrides) {
|
|
294
|
+
return resolveListTokens(theme, overrides).background || theme.colors.surface;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Inside the page's scroller; Android scrolls only the outer one without it.
|
|
298
|
+
function ColumnsLayout({ table }) {
|
|
299
|
+
const theme = useTableTheme();
|
|
300
|
+
return h(
|
|
301
|
+
ScrollView,
|
|
302
|
+
{
|
|
303
|
+
style: { maxHeight: table.maxHeight },
|
|
304
|
+
nestedScrollEnabled: true,
|
|
305
|
+
stickyHeaderIndices: [0],
|
|
306
|
+
testID: testId(table.testID, "scroll"),
|
|
307
|
+
},
|
|
308
|
+
h(
|
|
309
|
+
ListHeader,
|
|
310
|
+
{
|
|
311
|
+
...table.overrides,
|
|
312
|
+
style: { backgroundColor: headerFill(theme, table.overrides) },
|
|
313
|
+
testID: testId(table.testID, "header"),
|
|
314
|
+
},
|
|
315
|
+
...table.columns.map((column) =>
|
|
316
|
+
h(HeaderCell, {
|
|
317
|
+
key: column.key,
|
|
318
|
+
column,
|
|
319
|
+
sort: table.sort,
|
|
320
|
+
onSort: table.onSort,
|
|
321
|
+
labels: table.labels,
|
|
322
|
+
testID: table.testID,
|
|
323
|
+
}),
|
|
324
|
+
),
|
|
325
|
+
),
|
|
326
|
+
bodyOf(table, "columns"),
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function CardsLayout({ table, layout }) {
|
|
331
|
+
const { spacing } = useTableTheme();
|
|
332
|
+
return h(
|
|
333
|
+
View,
|
|
334
|
+
{ style: { gap: spacing.sm } },
|
|
335
|
+
h(SortMenu, { table }),
|
|
336
|
+
h(
|
|
337
|
+
ScrollView,
|
|
338
|
+
{ style: { maxHeight: table.maxHeight }, nestedScrollEnabled: true, testID: testId(table.testID, "scroll") },
|
|
339
|
+
bodyOf(table, layout),
|
|
340
|
+
),
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* @param {object} props — see `DataTable` in index.d.ts.
|
|
346
|
+
*/
|
|
347
|
+
function DataTable(props) {
|
|
348
|
+
const { rows, keyField = "id", sort, defaultSort, onSortChange, sortLabels, style, testID } = props;
|
|
349
|
+
const [width, onLayout] = useContainerWidth();
|
|
350
|
+
const layout = dataTableLayout(width);
|
|
351
|
+
const columns = normaliseColumns(props.columns);
|
|
352
|
+
const labels = { ...DEFAULT_SORT_LABELS, ...sortLabels };
|
|
353
|
+
const [current, onSort, spoken] = useSort({ sort, defaultSort, onSortChange, labels, columns });
|
|
354
|
+
const given = Array.isArray(rows) ? rows : [];
|
|
355
|
+
const emptyFallback = useHostSharedText("no_results", EMPTY_FALLBACK);
|
|
356
|
+
const table = {
|
|
357
|
+
columns,
|
|
358
|
+
rows: sortsClientSide(onSortChange) ? sortRows(given, current) : given,
|
|
359
|
+
keyField,
|
|
360
|
+
renderCell: props.renderCell,
|
|
361
|
+
onRowPress: props.onRowPress,
|
|
362
|
+
sort: current,
|
|
363
|
+
onSort,
|
|
364
|
+
labels,
|
|
365
|
+
loading: props.loading === true,
|
|
366
|
+
emptyTitle: props.emptyTitle,
|
|
367
|
+
emptyMessage: props.emptyMessage ?? (props.emptyTitle ? undefined : emptyFallback),
|
|
368
|
+
maxHeight: resolveMaxHeight(props.maxHeight),
|
|
369
|
+
overrides: listOverrides(props),
|
|
370
|
+
testID,
|
|
371
|
+
};
|
|
372
|
+
const content =
|
|
373
|
+
columns.length === 0
|
|
374
|
+
? null
|
|
375
|
+
: layout === "columns"
|
|
376
|
+
? h(ColumnsLayout, { table })
|
|
377
|
+
: h(CardsLayout, { table, layout });
|
|
378
|
+
return h(
|
|
379
|
+
View,
|
|
380
|
+
{ testID, accessibilityLabel: props.accessibilityLabel, style: [{ alignSelf: "stretch" }, style] },
|
|
381
|
+
h(View, { onLayout }, content),
|
|
382
|
+
web ? h(Text, { "aria-live": "polite", style: VISUALLY_HIDDEN }, spoken) : null,
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
DataTable.displayName = "DataTable";
|
|
386
|
+
|
|
387
|
+
return DataTable;
|
|
388
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// `<DataTable>` (web). Binds ./data-table-view.js to react-native-web and the
|
|
2
|
+
// web list, loading, menu and button parts; see ./primitives.js for why not
|
|
3
|
+
// the bare specifier.
|
|
4
|
+
|
|
5
|
+
import * as ReactNative from "react-native-web";
|
|
6
|
+
import { List, ListRow, ListHeader, EmptyState } from "./list.js";
|
|
7
|
+
import { Skeleton } from "./loading.js";
|
|
8
|
+
import { Menu } from "./popover.js";
|
|
9
|
+
import { Button } from "./button.js";
|
|
10
|
+
import { makeDataTable } from "./data-table-view.js";
|
|
11
|
+
|
|
12
|
+
export const DataTable = makeDataTable(
|
|
13
|
+
{
|
|
14
|
+
View: ReactNative.View,
|
|
15
|
+
Text: ReactNative.Text,
|
|
16
|
+
Pressable: ReactNative.Pressable,
|
|
17
|
+
ScrollView: ReactNative.ScrollView,
|
|
18
|
+
Platform: ReactNative.Platform,
|
|
19
|
+
AccessibilityInfo: ReactNative.AccessibilityInfo,
|
|
20
|
+
},
|
|
21
|
+
{ List, ListRow, ListHeader, EmptyState, Skeleton, Menu, Button },
|
|
22
|
+
);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// `<DataTable>` (native). Binds ./data-table-view.js to React Native's own
|
|
2
|
+
// primitives and the native parts; only the imports differ from ./data-table.js.
|
|
3
|
+
|
|
4
|
+
import { View, Text, Pressable, ScrollView, Platform, AccessibilityInfo } from "react-native";
|
|
5
|
+
import { List, ListRow, ListHeader, EmptyState } from "./list.native.js";
|
|
6
|
+
import { Skeleton } from "./loading.native.js";
|
|
7
|
+
import { Menu } from "./popover.native.js";
|
|
8
|
+
import { Button } from "./button.native.js";
|
|
9
|
+
import { makeDataTable } from "./data-table-view.js";
|
|
10
|
+
|
|
11
|
+
export const DataTable = makeDataTable(
|
|
12
|
+
{ View, Text, Pressable, ScrollView, Platform, AccessibilityInfo },
|
|
13
|
+
{ List, ListRow, ListHeader, EmptyState, Skeleton, Menu, Button },
|
|
14
|
+
);
|