@tensor_1/react 2.49.1 → 2.50.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/components/DataWorkspace.d.ts +178 -7
- package/dist/components/DataWorkspace.js +769 -89
- package/dist/components/SearchBar.d.ts +3 -1
- package/dist/components/SearchBar.js +2 -0
- package/dist/components/Table.d.ts +6 -1
- package/dist/components/Table.js +10 -2
- package/dist/index.cjs +793 -104
- package/dist/index.d.cts +192 -12
- package/dist/index.d.ts +1 -1
- package/dist/table-engine.d.ts +4 -2
- package/dist/table-engine.js +3 -2
- package/package.json +1 -1
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useId, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { memo, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
|
+
import { toggleSort } from "../table-engine.js";
|
|
5
6
|
import { fillLabel } from "../fill-label.js";
|
|
6
7
|
import { useUixLabels } from "../labels-context.js";
|
|
7
8
|
import { renderUixLink } from "../link.js";
|
|
8
9
|
import { isFilterEmpty, summarizeFilter } from "../filter-model.js";
|
|
9
10
|
import { useResolvedFilterLabels } from "../hooks/useResolvedFilterLabels.js";
|
|
11
|
+
import { useVirtualRows } from "../hooks/useVirtualRows.js";
|
|
10
12
|
import { BusyRegion } from "./BusyRegion.js";
|
|
11
13
|
import { Button } from "./Button.js";
|
|
12
14
|
import { Checkbox } from "./Checkbox.js";
|
|
@@ -45,11 +47,40 @@ const DEFAULT_DATA_WORKSPACE_LABELS = {
|
|
|
45
47
|
filteredEmpty: "Nothing matches",
|
|
46
48
|
filteredEmptyDetail: "Change the search or clear a filter.",
|
|
47
49
|
error: "The list could not be loaded",
|
|
48
|
-
retry: "Try again"
|
|
50
|
+
retry: "Try again",
|
|
51
|
+
sortPriority: "Sort priority {n}",
|
|
52
|
+
groupCount: "{count} rows",
|
|
53
|
+
groupCountOne: "{count} row",
|
|
54
|
+
collapseGroup: "Collapse group {name}",
|
|
55
|
+
expandGroup: "Expand group {name}",
|
|
56
|
+
selectGroup: "Select group {name}"
|
|
49
57
|
};
|
|
58
|
+
const useIsomorphicLayoutEffect = typeof document !== "undefined" ? useLayoutEffect : useEffect;
|
|
50
59
|
const ArrowUpRight = () => /* @__PURE__ */ jsx("svg", { className: "uix-id-cell__arrow", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx("path", { d: "M7 17 17 7M8 7h9v9" }) });
|
|
51
60
|
const PlusIcon = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx("path", { d: "M12 5v14M5 12h14" }) });
|
|
52
61
|
const MaximizeIcon = ({ restore }) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx("path", { d: restore ? "M4 14h6v6M20 10h-6V4M14 10l7-7M3 21l7-7" : "M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7" }) });
|
|
62
|
+
const ChevronIcon = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx("path", { d: "M6 4l4 4-4 4" }) });
|
|
63
|
+
const LADDER_CARDS = 768;
|
|
64
|
+
const LADDER_LOW = 900;
|
|
65
|
+
const LADDER_MID = 600;
|
|
66
|
+
const ladderFor = (mobile, width) => {
|
|
67
|
+
if (!mobile || !(width > 0)) return null;
|
|
68
|
+
if (mobile === "cards" && width < LADDER_CARDS) return "cards";
|
|
69
|
+
if (width < LADDER_MID) return "priority-mid";
|
|
70
|
+
if (width < LADDER_LOW) return "priority";
|
|
71
|
+
return null;
|
|
72
|
+
};
|
|
73
|
+
const hiddenBy = (ladder, priority) => ladder === "priority" && priority === "low" || ladder === "priority-mid" && (priority === "low" || priority === "mid");
|
|
74
|
+
const ROW_HEIGHT = { compact: 44, standard: 56, comfortable: 68 };
|
|
75
|
+
const CONTROLS = 'a, button, input, select, textarea, label, summary, dialog, [role="button"], [role="menuitem"], [contenteditable="true"], [popover], td.uix-table__selcell, td.uix-table__actions';
|
|
76
|
+
const RESERVED_COLS = /* @__PURE__ */ new Set(["select", "id", "actions", "expand"]);
|
|
77
|
+
const textOf = (node) => typeof node === "string" ? node : void 0;
|
|
78
|
+
const columnLabel = (column) => column.label ?? textOf(column.header);
|
|
79
|
+
const ladderAttrs = (on, column, body) => on ? {
|
|
80
|
+
"data-col": RESERVED_COLS.has(column.id) ? void 0 : column.id,
|
|
81
|
+
"data-priority": column.priority && column.priority !== "high" ? column.priority : void 0,
|
|
82
|
+
"data-label": body ? columnLabel(column) : void 0
|
|
83
|
+
} : {};
|
|
53
84
|
function DataWorkspace({
|
|
54
85
|
label,
|
|
55
86
|
rows,
|
|
@@ -60,10 +91,15 @@ function DataWorkspace({
|
|
|
60
91
|
onSortChange,
|
|
61
92
|
selection,
|
|
62
93
|
onSelectionChange,
|
|
94
|
+
maxSelection,
|
|
63
95
|
bulkActions,
|
|
64
96
|
rowActions,
|
|
65
97
|
getRowProps,
|
|
66
98
|
density,
|
|
99
|
+
zebra,
|
|
100
|
+
freezeFirstColumn = false,
|
|
101
|
+
fixed,
|
|
102
|
+
mobile,
|
|
67
103
|
search,
|
|
68
104
|
filters,
|
|
69
105
|
savedViews,
|
|
@@ -71,13 +107,32 @@ function DataWorkspace({
|
|
|
71
107
|
viewChanged = false,
|
|
72
108
|
onSaveView,
|
|
73
109
|
onSaveViewAs,
|
|
110
|
+
saveViewAs = "changed",
|
|
74
111
|
maximized = false,
|
|
75
112
|
onMaximizedChange,
|
|
76
113
|
toolbarEnd,
|
|
114
|
+
beforeBody,
|
|
77
115
|
pagination,
|
|
78
116
|
status = "ready",
|
|
79
117
|
refreshing = false,
|
|
80
118
|
onRetry,
|
|
119
|
+
loadingRows = 6,
|
|
120
|
+
empty,
|
|
121
|
+
emptyAction,
|
|
122
|
+
filteredEmpty,
|
|
123
|
+
filteredEmptyAction,
|
|
124
|
+
error,
|
|
125
|
+
stateOverride,
|
|
126
|
+
groups,
|
|
127
|
+
collapsedGroups,
|
|
128
|
+
onCollapsedGroupsChange,
|
|
129
|
+
virtualize,
|
|
130
|
+
rowNavigation,
|
|
131
|
+
onRowActivate,
|
|
132
|
+
activateOnClick = true,
|
|
133
|
+
activeRowId,
|
|
134
|
+
onActiveRowChange,
|
|
135
|
+
multiSort = false,
|
|
81
136
|
labels: labelOverrides,
|
|
82
137
|
className,
|
|
83
138
|
...props
|
|
@@ -111,68 +166,123 @@ function DataWorkspace({
|
|
|
111
166
|
const root = rootRef.current;
|
|
112
167
|
const target = event.target;
|
|
113
168
|
const owner = target?.closest?.('[popover], [role="dialog"], [role="menu"], [role="listbox"], [aria-expanded="true"]');
|
|
114
|
-
if (owner && root?.contains(owner)) return;
|
|
169
|
+
if (owner && root?.contains(owner) && (owner.getAttribute("aria-expanded") !== "true" || stillExpanded(owner))) return;
|
|
115
170
|
if (root && openInside(root)) return;
|
|
116
171
|
onMaximizedChange(false);
|
|
117
172
|
};
|
|
118
173
|
window.addEventListener("keydown", onKey);
|
|
119
174
|
return () => window.removeEventListener("keydown", onKey);
|
|
120
175
|
}, [maximized, onMaximizedChange]);
|
|
176
|
+
const [ladder, setLadder] = useState(null);
|
|
177
|
+
useIsomorphicLayoutEffect(() => {
|
|
178
|
+
const root = rootRef.current;
|
|
179
|
+
if (!mobile || !root) {
|
|
180
|
+
setLadder(null);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const read = () => {
|
|
184
|
+
const style = getComputedStyle(root);
|
|
185
|
+
const padding = (parseFloat(style.paddingLeft) || 0) + (parseFloat(style.paddingRight) || 0);
|
|
186
|
+
setLadder(ladderFor(mobile, root.getBoundingClientRect().width - padding));
|
|
187
|
+
};
|
|
188
|
+
read();
|
|
189
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
190
|
+
const observer = new ResizeObserver(read);
|
|
191
|
+
observer.observe(root);
|
|
192
|
+
return () => observer.disconnect();
|
|
193
|
+
}, [mobile]);
|
|
194
|
+
const cards = ladder === "cards";
|
|
121
195
|
const filtered = (filters ? filters.fields.some((field) => !isFilterEmpty(filters.values[field.id])) : false) || (search?.value.trim() ?? "") !== "";
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
const
|
|
196
|
+
const source = groups ?? rows;
|
|
197
|
+
const flat = useMemo(() => groups ? groups.flatMap((group) => group.rows) : rows, [groups, rows]);
|
|
198
|
+
const current = { source, flat, groups, filtered };
|
|
199
|
+
const [settled, setSettled] = useState(current);
|
|
200
|
+
if (!refreshing && source !== settled.source) setSettled(current);
|
|
201
|
+
const view = flat.length > 0 ? current : refreshing || source === settled.source ? settled : current;
|
|
202
|
+
const shownRows = view.flat;
|
|
203
|
+
const shownGroups = view.groups;
|
|
204
|
+
const [ownCollapsed, setOwnCollapsed] = useState(() => /* @__PURE__ */ new Set());
|
|
205
|
+
const collapsed = collapsedGroups ?? ownCollapsed;
|
|
206
|
+
const toggleGroupOpen = (groupId) => {
|
|
207
|
+
const next = new Set(collapsed);
|
|
208
|
+
if (next.has(groupId)) next.delete(groupId);
|
|
209
|
+
else next.add(groupId);
|
|
210
|
+
if (!collapsedGroups) setOwnCollapsed(next);
|
|
211
|
+
onCollapsedGroupsChange?.(next);
|
|
212
|
+
};
|
|
126
213
|
const ids = useMemo(() => shownRows.map(getRowId), [shownRows, getRowId]);
|
|
127
|
-
const
|
|
214
|
+
const visibleIds = useMemo(() => {
|
|
215
|
+
if (!shownGroups) return ids;
|
|
216
|
+
const out = [];
|
|
217
|
+
for (const group of shownGroups) if (!collapsed.has(group.id)) for (const row of group.rows) out.push(getRowId(row));
|
|
218
|
+
return out;
|
|
219
|
+
}, [shownGroups, collapsed, ids, getRowId]);
|
|
220
|
+
const selected = selection ?? EMPTY_SET;
|
|
128
221
|
const selectable = onSelectionChange != null;
|
|
222
|
+
const cap = maxSelection ?? Infinity;
|
|
129
223
|
const onPage = ids.filter((rowId) => selected.has(rowId)).length;
|
|
224
|
+
const apply = (next, rowIds, on) => {
|
|
225
|
+
for (const rowId of rowIds) {
|
|
226
|
+
if (!on) next.delete(rowId);
|
|
227
|
+
else if (next.has(rowId) || next.size < cap) next.add(rowId);
|
|
228
|
+
}
|
|
229
|
+
return next;
|
|
230
|
+
};
|
|
130
231
|
const toggleRow = (rowId, on, event) => {
|
|
131
232
|
if (!onSelectionChange) return;
|
|
132
233
|
const next = new Set(selected);
|
|
133
|
-
const from = anchor.current ?
|
|
134
|
-
const to =
|
|
234
|
+
const from = anchor.current ? visibleIds.indexOf(anchor.current) : -1;
|
|
235
|
+
const to = visibleIds.indexOf(rowId);
|
|
135
236
|
if (event?.shiftKey && from >= 0 && to >= 0) {
|
|
136
|
-
|
|
137
|
-
if (on) next.add(ids[i]);
|
|
138
|
-
else next.delete(ids[i]);
|
|
139
|
-
}
|
|
237
|
+
apply(next, visibleIds.slice(Math.min(from, to), Math.max(from, to) + 1), on);
|
|
140
238
|
} else {
|
|
141
|
-
|
|
142
|
-
else next.delete(rowId);
|
|
239
|
+
apply(next, [rowId], on);
|
|
143
240
|
anchor.current = rowId;
|
|
144
241
|
}
|
|
145
242
|
onSelectionChange(next);
|
|
146
243
|
};
|
|
147
|
-
const
|
|
244
|
+
const toggleMany = (rowIds, on) => {
|
|
148
245
|
if (!onSelectionChange) return;
|
|
149
|
-
|
|
150
|
-
for (const rowId of ids) {
|
|
151
|
-
if (on) next.add(rowId);
|
|
152
|
-
else next.delete(rowId);
|
|
153
|
-
}
|
|
154
|
-
onSelectionChange(next);
|
|
246
|
+
onSelectionChange(apply(new Set(selected), rowIds, on));
|
|
155
247
|
};
|
|
248
|
+
const atCap = selected.size >= cap;
|
|
156
249
|
const actionsByRow = useMemo(() => rowActions ? shownRows.map((row) => rowActions(row)) : [], [shownRows, rowActions]);
|
|
157
250
|
const hasActions = actionsByRow.some((node) => node != null && node !== false);
|
|
158
251
|
const activeFilters = filters ? filters.fields.filter((field) => !isFilterEmpty(filters.values[field.id])) : [];
|
|
159
252
|
const searching = (search?.value.trim() ?? "") !== "";
|
|
160
253
|
const clearAll = () => {
|
|
254
|
+
if (filters?.onClearAll) {
|
|
255
|
+
filters.onClearAll({ search: searching });
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
161
258
|
for (const field of activeFilters) filters?.onChange(field.id, void 0);
|
|
162
259
|
if (searching) search?.onChange("");
|
|
163
260
|
};
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
261
|
+
const multi = multiSort || Array.isArray(sort);
|
|
262
|
+
const keys = Array.isArray(sort) ? sort : sort ? [sort] : [];
|
|
263
|
+
const sortFor = (column) => keys.find((key) => key.columnId === column.id)?.direction ?? "none";
|
|
264
|
+
const cycleSort = (column, additive) => {
|
|
265
|
+
if (!onSortChange) return;
|
|
266
|
+
const current2 = sortFor(column);
|
|
267
|
+
const turned = current2 === "none" ? "ascending" : current2 === "ascending" ? "descending" : null;
|
|
268
|
+
if (!multi) {
|
|
269
|
+
onSortChange(turned ? { columnId: column.id, direction: turned } : null);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const next = toggleSort(keys.map((key) => ({ field: key.columnId, dir: key.direction })), column.id, additive).map((key) => ({ columnId: key.field, direction: key.dir === "descending" ? "descending" : "ascending" }));
|
|
273
|
+
onSortChange(next);
|
|
168
274
|
};
|
|
275
|
+
const showSaveAs = onSaveViewAs != null && (viewChanged || saveViewAs === "always");
|
|
169
276
|
const toolbar = /* @__PURE__ */ jsxs("div", { className: "uix-toolbar uix-data-workspace__toolbar", role: "toolbar", "aria-label": label, children: [
|
|
170
|
-
savedViews && /* @__PURE__ */ jsx(PopoverButton, { id: `${id}-views`, label: savedViews.current, title: typeof savedViews.current === "string" ? fillLabel(labels.savedView, { name: savedViews.current }) : void 0, variant: "secondary", children: (close) => /* @__PURE__ */
|
|
171
|
-
savedViews.
|
|
172
|
-
|
|
173
|
-
|
|
277
|
+
savedViews && /* @__PURE__ */ jsx(PopoverButton, { id: `${id}-views`, label: savedViews.current, title: typeof savedViews.current === "string" ? fillLabel(labels.savedView, { name: savedViews.current }) : void 0, variant: "secondary", children: (close) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
278
|
+
savedViews.note != null && /* @__PURE__ */ jsx("div", { className: "uix-data-workspace__views-note", children: savedViews.note }),
|
|
279
|
+
/* @__PURE__ */ jsx(SavedViewMenu, { ...savedViews.menu, onSelect: (viewId) => {
|
|
280
|
+
savedViews.menu.onSelect(viewId);
|
|
281
|
+
close();
|
|
282
|
+
} })
|
|
283
|
+
] }) }),
|
|
174
284
|
viewChanged && onSaveView && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", onClick: onSaveView, children: labels.saveView }),
|
|
175
|
-
|
|
285
|
+
showSaveAs && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: onSaveViewAs, children: labels.saveViewAs }),
|
|
176
286
|
filters && /* @__PURE__ */ jsx(FilterChips, { filters, labels, baseId: id }),
|
|
177
287
|
/* @__PURE__ */ jsx("span", { className: "uix-toolbar__spacer" }),
|
|
178
288
|
search && /* @__PURE__ */ jsx(
|
|
@@ -184,67 +294,77 @@ function DataWorkspace({
|
|
|
184
294
|
placeholder: search.placeholder ?? labels.searchPlaceholder,
|
|
185
295
|
value: search.value,
|
|
186
296
|
onValueChange: search.onChange,
|
|
187
|
-
inputRef: searchRef
|
|
297
|
+
inputRef: searchRef,
|
|
298
|
+
onSubmit: search.onSubmit,
|
|
299
|
+
onClear: search.onClear,
|
|
300
|
+
maxLength: search.maxLength
|
|
188
301
|
}
|
|
189
302
|
),
|
|
190
303
|
viewMenu && /* @__PURE__ */ jsx(PopoverButton, { id: `${id}-view`, label: labels.view, variant: "ghost", children: () => /* @__PURE__ */ jsx(ViewMenu, { ...viewMenu }) }),
|
|
191
304
|
onMaximizedChange && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", icon: true, "aria-label": maximized ? labels.restore : labels.maximize, "aria-pressed": maximized, onClick: () => onMaximizedChange(!maximized), children: /* @__PURE__ */ jsx(MaximizeIcon, { restore: maximized }) }),
|
|
192
305
|
toolbarEnd
|
|
193
306
|
] });
|
|
307
|
+
const busy = (node) => /* @__PURE__ */ jsx(BusyRegion, { busy: refreshing, className: "uix-data-workspace__state", children: node });
|
|
194
308
|
let body;
|
|
195
|
-
if (
|
|
196
|
-
else if (status === "
|
|
309
|
+
if (stateOverride != null) body = busy(stateOverride);
|
|
310
|
+
else if (status === "loading") body = /* @__PURE__ */ jsx(LoadingState, { rows: loadingRows, label: labels.loading, className: "uix-data-workspace__loading" });
|
|
311
|
+
else if (status === "error") body = error != null ? busy(error) : /* @__PURE__ */ jsx(ErrorState, { title: labels.error, action: onRetry && /* @__PURE__ */ jsx(Button, { size: "sm", onClick: onRetry, children: labels.retry }) });
|
|
197
312
|
else if (shownRows.length === 0) {
|
|
198
|
-
|
|
313
|
+
const clear = /* @__PURE__ */ jsx(Button, { size: "sm", onClick: clearAll, children: labels.clearFilters });
|
|
314
|
+
body = busy(view.filtered ? filteredEmpty ?? /* @__PURE__ */ jsx(
|
|
315
|
+
FilteredEmptyState,
|
|
316
|
+
{
|
|
317
|
+
title: labels.filteredEmpty,
|
|
318
|
+
action: filteredEmptyAction != null ? /* @__PURE__ */ jsxs("div", { className: "uix-data-workspace__state-actions", children: [
|
|
319
|
+
clear,
|
|
320
|
+
filteredEmptyAction
|
|
321
|
+
] }) : clear,
|
|
322
|
+
children: labels.filteredEmptyDetail
|
|
323
|
+
}
|
|
324
|
+
) : empty ?? /* @__PURE__ */ jsx(EmptyState, { title: labels.empty, action: emptyAction, children: labels.emptyDetail }));
|
|
199
325
|
} else {
|
|
200
|
-
body = /* @__PURE__ */ jsx(
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
)),
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
] }) }),
|
|
243
|
-
columns.map((column) => /* @__PURE__ */ jsx(Td, { className: column.align === "end" ? "uix-col--num" : void 0, children: column.cell(row) }, column.id)),
|
|
244
|
-
hasActions && /* @__PURE__ */ jsx(RowActions, { children: actionsByRow[index] })
|
|
245
|
-
] }, rowId);
|
|
246
|
-
}) })
|
|
247
|
-
] }) });
|
|
326
|
+
body = /* @__PURE__ */ jsx(
|
|
327
|
+
WorkspaceGrid,
|
|
328
|
+
{
|
|
329
|
+
label,
|
|
330
|
+
rows: shownRows,
|
|
331
|
+
ids,
|
|
332
|
+
groups: shownGroups,
|
|
333
|
+
collapsed,
|
|
334
|
+
onToggleGroup: toggleGroupOpen,
|
|
335
|
+
getRowId,
|
|
336
|
+
columns,
|
|
337
|
+
idColumn,
|
|
338
|
+
getRowProps,
|
|
339
|
+
density,
|
|
340
|
+
zebra,
|
|
341
|
+
freezeFirstColumn,
|
|
342
|
+
fixed: fixed ?? columns.some((column) => column.className != null || column.minWidth != null),
|
|
343
|
+
ladderOn: mobile != null,
|
|
344
|
+
ladder,
|
|
345
|
+
refreshing,
|
|
346
|
+
selectable,
|
|
347
|
+
selected,
|
|
348
|
+
onPage,
|
|
349
|
+
atCap,
|
|
350
|
+
toggleRow,
|
|
351
|
+
toggleMany,
|
|
352
|
+
actionsByRow,
|
|
353
|
+
hasActions,
|
|
354
|
+
keys,
|
|
355
|
+
sortable: onSortChange != null,
|
|
356
|
+
multi,
|
|
357
|
+
sortFor,
|
|
358
|
+
cycleSort,
|
|
359
|
+
virtualize: pagination || shownGroups ? void 0 : virtualize,
|
|
360
|
+
rowNavigation: rowNavigation ?? onRowActivate != null,
|
|
361
|
+
onRowActivate,
|
|
362
|
+
activateOnClick,
|
|
363
|
+
activeRowId,
|
|
364
|
+
onActiveRowChange,
|
|
365
|
+
labels
|
|
366
|
+
}
|
|
367
|
+
);
|
|
248
368
|
}
|
|
249
369
|
const selectedIds = [...selected];
|
|
250
370
|
return /* @__PURE__ */ jsxs(
|
|
@@ -257,12 +377,559 @@ function DataWorkspace({
|
|
|
257
377
|
children: [
|
|
258
378
|
toolbar,
|
|
259
379
|
bulkActions && selectedIds.length > 0 && /* @__PURE__ */ jsx(BulkBar, { count: selectedIds.length, label: labels.bulkActions, selectedLabel: (n) => fillLabel(labels.selected, { count: n }), children: bulkActions(selectedIds) }),
|
|
380
|
+
beforeBody != null && /* @__PURE__ */ jsx("div", { className: "uix-data-workspace__before", children: beforeBody }),
|
|
260
381
|
body,
|
|
261
|
-
pagination && status === "ready" && shownRows.length > 0 && /* @__PURE__ */ jsx(Footer, { pagination, labels, rowCount: shownRows.length })
|
|
382
|
+
pagination && status === "ready" && stateOverride == null && shownRows.length > 0 && /* @__PURE__ */ jsx(Footer, { pagination, labels, rowCount: shownRows.length })
|
|
262
383
|
]
|
|
263
384
|
}
|
|
264
385
|
);
|
|
265
386
|
}
|
|
387
|
+
const EMPTY_SET = /* @__PURE__ */ new Set();
|
|
388
|
+
function WorkspaceGrid(props) {
|
|
389
|
+
return props.virtualize ? /* @__PURE__ */ jsx(WindowedGrid, { ...props }) : /* @__PURE__ */ jsx(WorkspaceTable, { ...props, windowing: null, virtual: null });
|
|
390
|
+
}
|
|
391
|
+
function WindowedGrid(props) {
|
|
392
|
+
const windowing = props.virtualize === true || !props.virtualize ? {} : props.virtualize;
|
|
393
|
+
const threshold = windowing.threshold ?? 100;
|
|
394
|
+
const virtual = useVirtualRows(props.rows, {
|
|
395
|
+
rowHeight: windowing.estimatedRowHeight ?? ROW_HEIGHT[props.density ?? "standard"],
|
|
396
|
+
overscan: windowing.overscan ?? 8,
|
|
397
|
+
// windowed at `threshold` rows or more (the hook windows above its own threshold)
|
|
398
|
+
threshold: Math.max(0, threshold - 1),
|
|
399
|
+
estimatedViewportHeight: windowing.maxHeight ?? 640,
|
|
400
|
+
measureRows: "tbody > tr[data-uix-row-id]",
|
|
401
|
+
// cards are not windowed: a card's height is its content's
|
|
402
|
+
enabled: props.rows.length >= threshold && props.ladder !== "cards"
|
|
403
|
+
});
|
|
404
|
+
return /* @__PURE__ */ jsx(WorkspaceTable, { ...props, windowing, virtual });
|
|
405
|
+
}
|
|
406
|
+
const columnFloor = (width, minWidth, className) => {
|
|
407
|
+
if (minWidth) return minWidth;
|
|
408
|
+
if (width && !width.trim().endsWith("%")) return width;
|
|
409
|
+
const classes = className?.split(/\s+/) ?? [];
|
|
410
|
+
for (const cls of classes) {
|
|
411
|
+
const tier = /^uix-col--w-(xs|sm|md|lg|xl)$/.exec(cls)?.[1];
|
|
412
|
+
if (tier) return `var(--uix-col-w-${tier})`;
|
|
413
|
+
}
|
|
414
|
+
if (classes.includes("uix-col--flex") || classes.includes("uix-col--primary")) return "var(--uix-col-title-min)";
|
|
415
|
+
return "var(--uix-col-w-sm)";
|
|
416
|
+
};
|
|
417
|
+
const isFlex = (className) => /(^|\s)uix-col--(flex|primary)(\s|$)/.test(className ?? "");
|
|
418
|
+
const colClasses = (className) => className?.split(/\s+/).filter((cls) => /^uix-col--(w-(xs|sm|md|lg|xl)|flex|primary)$/.test(cls)).join(" ") || void 0;
|
|
419
|
+
function WorkspaceTable({
|
|
420
|
+
label,
|
|
421
|
+
rows,
|
|
422
|
+
ids,
|
|
423
|
+
groups,
|
|
424
|
+
collapsed,
|
|
425
|
+
onToggleGroup,
|
|
426
|
+
getRowId,
|
|
427
|
+
columns,
|
|
428
|
+
idColumn,
|
|
429
|
+
getRowProps,
|
|
430
|
+
density,
|
|
431
|
+
zebra,
|
|
432
|
+
freezeFirstColumn,
|
|
433
|
+
fixed,
|
|
434
|
+
ladderOn,
|
|
435
|
+
ladder,
|
|
436
|
+
refreshing,
|
|
437
|
+
selectable,
|
|
438
|
+
selected,
|
|
439
|
+
onPage,
|
|
440
|
+
atCap,
|
|
441
|
+
toggleRow,
|
|
442
|
+
toggleMany,
|
|
443
|
+
actionsByRow,
|
|
444
|
+
hasActions,
|
|
445
|
+
keys,
|
|
446
|
+
sortable,
|
|
447
|
+
multi,
|
|
448
|
+
sortFor,
|
|
449
|
+
cycleSort,
|
|
450
|
+
rowNavigation,
|
|
451
|
+
onRowActivate,
|
|
452
|
+
activateOnClick,
|
|
453
|
+
activeRowId,
|
|
454
|
+
onActiveRowChange,
|
|
455
|
+
labels,
|
|
456
|
+
windowing,
|
|
457
|
+
virtual
|
|
458
|
+
}) {
|
|
459
|
+
const cards = ladder === "cards";
|
|
460
|
+
const wrapRef = useRef(null);
|
|
461
|
+
const baseId = useId();
|
|
462
|
+
const maxHeight = windowing?.maxHeight ?? 640;
|
|
463
|
+
const long = windowing != null && !cards && rows.length >= (windowing.threshold ?? 100);
|
|
464
|
+
const containerRef = virtual?.containerRef;
|
|
465
|
+
const setWrap = useCallback((node) => {
|
|
466
|
+
wrapRef.current = node;
|
|
467
|
+
if (containerRef) containerRef.current = node;
|
|
468
|
+
}, [containerRef]);
|
|
469
|
+
const windowed = virtual?.virtualized ?? false;
|
|
470
|
+
const start = virtual && windowed ? virtual.startIndex : 0;
|
|
471
|
+
const mountedRows = virtual && windowed ? virtual.rows : rows;
|
|
472
|
+
const nav = rowNavigation;
|
|
473
|
+
const clickActivates = onRowActivate != null && activateOnClick;
|
|
474
|
+
const [ownActive, setOwnActive] = useState(null);
|
|
475
|
+
const active = activeRowId !== void 0 ? activeRowId : ownActive;
|
|
476
|
+
const navIds = useMemo(() => {
|
|
477
|
+
if (!groups) return ids;
|
|
478
|
+
const out = [];
|
|
479
|
+
for (const group of groups) if (!collapsed.has(group.id)) for (const row of group.rows) out.push(getRowId(row));
|
|
480
|
+
return out;
|
|
481
|
+
}, [groups, collapsed, ids, getRowId]);
|
|
482
|
+
const mountedIds = useMemo(() => new Set(windowed ? ids.slice(start, start + mountedRows.length) : navIds), [windowed, ids, start, mountedRows.length, navIds]);
|
|
483
|
+
const tabStop = active != null && mountedIds.has(active) ? active : navIds.find((rowId) => mountedIds.has(rowId)) ?? null;
|
|
484
|
+
const pendingFocus = useRef(null);
|
|
485
|
+
const focusedRow = useRef(null);
|
|
486
|
+
const rowElement = (rowId) => {
|
|
487
|
+
for (const el of Array.from(wrapRef.current?.querySelectorAll("tbody > tr[data-uix-row-id]") ?? [])) {
|
|
488
|
+
if (el.getAttribute("data-uix-row-id") === rowId) return el;
|
|
489
|
+
}
|
|
490
|
+
return null;
|
|
491
|
+
};
|
|
492
|
+
const reveal = (el) => {
|
|
493
|
+
el.scrollIntoView?.({ block: "nearest", inline: "nearest" });
|
|
494
|
+
const wrap = wrapRef.current;
|
|
495
|
+
const head = wrap?.querySelector("thead");
|
|
496
|
+
if (wrap && head && windowed) {
|
|
497
|
+
const under = head.getBoundingClientRect().bottom - el.getBoundingClientRect().top;
|
|
498
|
+
if (under > 0) wrap.scrollTop -= under;
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
const setActive = (rowId) => {
|
|
502
|
+
if (rowId === active) return;
|
|
503
|
+
if (activeRowId === void 0) setOwnActive(rowId);
|
|
504
|
+
onActiveRowChange?.(rowId);
|
|
505
|
+
};
|
|
506
|
+
const focusRow = (rowId, { take = true } = {}) => {
|
|
507
|
+
if (!navIds.includes(rowId)) return;
|
|
508
|
+
setActive(rowId);
|
|
509
|
+
if (!take) return;
|
|
510
|
+
const el = rowElement(rowId);
|
|
511
|
+
if (el) {
|
|
512
|
+
el.focus({ preventScroll: true });
|
|
513
|
+
reveal(el);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
const index = ids.indexOf(rowId);
|
|
517
|
+
if (index >= 0 && wrapRef.current && virtual && windowed) {
|
|
518
|
+
pendingFocus.current = rowId;
|
|
519
|
+
wrapRef.current.scrollTop = index * virtual.rowHeight;
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
useIsomorphicLayoutEffect(() => {
|
|
523
|
+
if (!nav) return;
|
|
524
|
+
const pending = pendingFocus.current;
|
|
525
|
+
if (pending != null) {
|
|
526
|
+
if (!ids.includes(pending)) {
|
|
527
|
+
pendingFocus.current = null;
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
const el2 = rowElement(pending);
|
|
531
|
+
if (el2) {
|
|
532
|
+
pendingFocus.current = null;
|
|
533
|
+
el2.focus({ preventScroll: true });
|
|
534
|
+
reveal(el2);
|
|
535
|
+
}
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
const had = focusedRow.current;
|
|
539
|
+
if (had == null) return;
|
|
540
|
+
const wrap = wrapRef.current;
|
|
541
|
+
const focused = typeof document !== "undefined" ? document.activeElement : null;
|
|
542
|
+
const el = rowElement(had);
|
|
543
|
+
if (el && wrap && focused === wrap) {
|
|
544
|
+
el.focus({ preventScroll: true });
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
if (el || !(focused == null || focused === document.body)) return;
|
|
548
|
+
if (windowed && ids.includes(had) && wrap) {
|
|
549
|
+
wrap.focus({ preventScroll: true });
|
|
550
|
+
} else if (tabStop != null) {
|
|
551
|
+
rowElement(tabStop)?.focus({ preventScroll: true });
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
const lastActiveProp = useRef(activeRowId);
|
|
555
|
+
useEffect(() => {
|
|
556
|
+
if (!nav || activeRowId === lastActiveProp.current) return;
|
|
557
|
+
lastActiveProp.current = activeRowId;
|
|
558
|
+
if (activeRowId == null) return;
|
|
559
|
+
const focused = document.activeElement;
|
|
560
|
+
const el = rowElement(activeRowId);
|
|
561
|
+
if (el && el.contains(focused)) return;
|
|
562
|
+
const mayTake = focused == null || focused === document.body || (wrapRef.current?.contains(focused) ?? false);
|
|
563
|
+
focusRow(activeRowId, { take: mayTake });
|
|
564
|
+
});
|
|
565
|
+
const latest = {
|
|
566
|
+
toggle: (rowId, on, event) => toggleRow(rowId, on, event),
|
|
567
|
+
key: (rowId, event) => {
|
|
568
|
+
if (event.target !== event.currentTarget || event.altKey || event.ctrlKey || event.metaKey) return;
|
|
569
|
+
const at = navIds.indexOf(rowId);
|
|
570
|
+
const to = event.key === "ArrowDown" ? at + 1 : event.key === "ArrowUp" ? at - 1 : event.key === "Home" ? 0 : event.key === "End" ? navIds.length - 1 : null;
|
|
571
|
+
if (to != null) {
|
|
572
|
+
event.preventDefault();
|
|
573
|
+
const target = navIds[Math.max(0, Math.min(navIds.length - 1, to))];
|
|
574
|
+
if (target != null) focusRow(target);
|
|
575
|
+
} else if (event.key === "Enter") {
|
|
576
|
+
event.preventDefault();
|
|
577
|
+
const row = rows[ids.indexOf(rowId)];
|
|
578
|
+
if (onRowActivate) {
|
|
579
|
+
if (row !== void 0) onRowActivate(row);
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
const el = event.currentTarget;
|
|
583
|
+
(el.querySelector(".uix-id-cell__btn") ?? el.querySelector("a[href]"))?.click();
|
|
584
|
+
} else if (event.key === " ") {
|
|
585
|
+
event.preventDefault();
|
|
586
|
+
}
|
|
587
|
+
},
|
|
588
|
+
click: (rowId, event) => {
|
|
589
|
+
if (!clickActivates) return;
|
|
590
|
+
if (!event.currentTarget.contains(event.target)) return;
|
|
591
|
+
const control = event.target.closest?.(CONTROLS);
|
|
592
|
+
if (control && control !== event.currentTarget && event.currentTarget.contains(control)) return;
|
|
593
|
+
if (typeof window !== "undefined" && (window.getSelection?.()?.toString() ?? "") !== "") return;
|
|
594
|
+
const row = rows[ids.indexOf(rowId)];
|
|
595
|
+
if (row !== void 0) onRowActivate?.(row);
|
|
596
|
+
},
|
|
597
|
+
focus: (rowId) => {
|
|
598
|
+
focusedRow.current = rowId;
|
|
599
|
+
setActive(rowId);
|
|
600
|
+
},
|
|
601
|
+
blur: (rowId, event) => {
|
|
602
|
+
const next = event.relatedTarget;
|
|
603
|
+
if (next && wrapRef.current?.contains(next)) return;
|
|
604
|
+
if (next) {
|
|
605
|
+
focusedRow.current = null;
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
const el = event.currentTarget;
|
|
609
|
+
setTimeout(() => {
|
|
610
|
+
if (el.isConnected && focusedRow.current === rowId) focusedRow.current = null;
|
|
611
|
+
}, 0);
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
const latestRef = useRef(latest);
|
|
615
|
+
latestRef.current = latest;
|
|
616
|
+
const handlers = useMemo(() => ({
|
|
617
|
+
toggle: (...args) => latestRef.current.toggle(...args),
|
|
618
|
+
key: (...args) => latestRef.current.key(...args),
|
|
619
|
+
click: (...args) => latestRef.current.click(...args),
|
|
620
|
+
focus: (...args) => latestRef.current.focus(...args),
|
|
621
|
+
blur: (...args) => latestRef.current.blur(...args)
|
|
622
|
+
}), []);
|
|
623
|
+
const groupAnchor = useRef(null);
|
|
624
|
+
const toggleGroup = (groupId, header) => {
|
|
625
|
+
groupAnchor.current = header ? { id: groupId, top: header.getBoundingClientRect().top } : null;
|
|
626
|
+
onToggleGroup(groupId);
|
|
627
|
+
};
|
|
628
|
+
useIsomorphicLayoutEffect(() => {
|
|
629
|
+
const was = groupAnchor.current;
|
|
630
|
+
if (!was) return;
|
|
631
|
+
groupAnchor.current = null;
|
|
632
|
+
const header = wrapRef.current?.querySelector(`tr[data-group-id="${cssEscape(was.id)}"]`);
|
|
633
|
+
if (!header) return;
|
|
634
|
+
const moved = header.getBoundingClientRect().top - was.top;
|
|
635
|
+
if (Math.abs(moved) < 1) return;
|
|
636
|
+
const wrap = wrapRef.current;
|
|
637
|
+
if (wrap && wrap.scrollHeight > wrap.clientHeight + 1 && getComputedStyle(wrap).overflowY !== "visible") wrap.scrollTop += moved;
|
|
638
|
+
else window.scrollBy?.(0, moved);
|
|
639
|
+
});
|
|
640
|
+
const shiftKey = useRef(false);
|
|
641
|
+
const additive = useRef(false);
|
|
642
|
+
const frozen = !cards && ladder !== "priority-mid" && (idColumn != null || freezeFirstColumn);
|
|
643
|
+
const shown = columns.filter((column) => !hiddenBy(ladder, column.priority));
|
|
644
|
+
const colCount = (selectable ? 1 : 0) + (idColumn ? 1 : 0) + shown.length + (hasActions ? 1 : 0);
|
|
645
|
+
const selectRowLabel = labels.selectRow;
|
|
646
|
+
const openLabel = labels.open;
|
|
647
|
+
const fixedOn = fixed && !cards;
|
|
648
|
+
const [actionsWidth, setActionsWidth] = useState(null);
|
|
649
|
+
useIsomorphicLayoutEffect(() => {
|
|
650
|
+
if (!fixedOn || !hasActions) return;
|
|
651
|
+
let widest = 0;
|
|
652
|
+
for (const td of Array.from(wrapRef.current?.querySelectorAll("tbody td.uix-table__actions") ?? [])) {
|
|
653
|
+
const range = document.createRange();
|
|
654
|
+
range.selectNodeContents(td);
|
|
655
|
+
const content = range.getBoundingClientRect().width;
|
|
656
|
+
const style = getComputedStyle(td);
|
|
657
|
+
widest = Math.max(widest, content + parseFloat(style.paddingLeft || "0") + parseFloat(style.paddingRight || "0"));
|
|
658
|
+
}
|
|
659
|
+
const next = widest > 0 ? Math.ceil(widest) : null;
|
|
660
|
+
if (next !== actionsWidth) setActionsWidth(next);
|
|
661
|
+
});
|
|
662
|
+
const actionsCol = actionsWidth != null ? `${actionsWidth}px` : "5.25rem";
|
|
663
|
+
const floors = fixedOn ? [
|
|
664
|
+
...selectable ? ["var(--uix-selcol-w)"] : [],
|
|
665
|
+
...idColumn ? [columnFloor(idColumn.width, idColumn.minWidth, void 0)] : [],
|
|
666
|
+
...shown.map((column) => columnFloor(column.width, column.minWidth, column.className)),
|
|
667
|
+
...hasActions ? [actionsCol] : []
|
|
668
|
+
] : [];
|
|
669
|
+
const tableStyle = fixedOn && floors.length ? { minWidth: floors.length === 1 ? floors[0] : `calc(${floors.join(" + ")})` } : void 0;
|
|
670
|
+
const renderRow = (row, index) => {
|
|
671
|
+
const rowId = ids[index];
|
|
672
|
+
const isSelected = selected.has(rowId);
|
|
673
|
+
return /* @__PURE__ */ jsx(
|
|
674
|
+
WorkspaceRow,
|
|
675
|
+
{
|
|
676
|
+
row,
|
|
677
|
+
rowId,
|
|
678
|
+
columns,
|
|
679
|
+
idColumn,
|
|
680
|
+
getRowProps,
|
|
681
|
+
selectable,
|
|
682
|
+
selected: isSelected,
|
|
683
|
+
selectDisabled: !isSelected && atCap,
|
|
684
|
+
hasActions,
|
|
685
|
+
actions: actionsByRow[index],
|
|
686
|
+
ladderOn,
|
|
687
|
+
selectRowLabel,
|
|
688
|
+
openLabel,
|
|
689
|
+
marked: nav || windowing != null,
|
|
690
|
+
ariaRowIndex: windowed ? index + 2 : void 0,
|
|
691
|
+
nav,
|
|
692
|
+
tabStop: tabStop === rowId,
|
|
693
|
+
handlers
|
|
694
|
+
},
|
|
695
|
+
rowId
|
|
696
|
+
);
|
|
697
|
+
};
|
|
698
|
+
let tbody;
|
|
699
|
+
if (groups) {
|
|
700
|
+
let offset = 0;
|
|
701
|
+
tbody = groups.map((group, groupIndex) => {
|
|
702
|
+
const first = offset;
|
|
703
|
+
offset += group.rows.length;
|
|
704
|
+
const open = !collapsed.has(group.id);
|
|
705
|
+
const name = group.name ?? textOf(group.label) ?? group.id;
|
|
706
|
+
const groupIds = ids.slice(first, first + group.rows.length);
|
|
707
|
+
const on = groupIds.filter((rowId) => selected.has(rowId)).length;
|
|
708
|
+
const count = group.count ?? group.rows.length;
|
|
709
|
+
const nameId = `${baseId}-g${groupIndex}-name`;
|
|
710
|
+
const countId = `${baseId}-g${groupIndex}-count`;
|
|
711
|
+
const firstAgg = shown.findIndex((column) => group.aggregates?.[column.id] != null);
|
|
712
|
+
const lead = (idColumn ? 1 : 0) + (firstAgg < 0 ? shown.length + (hasActions ? 1 : 0) : firstAgg);
|
|
713
|
+
const leadAgg = lead === 0 && firstAgg === 0 ? group.aggregates?.[shown[0].id] : null;
|
|
714
|
+
return /* @__PURE__ */ jsxs("tbody", { className: "uix-data-workspace__group", "data-collapsed": open ? void 0 : "", children: [
|
|
715
|
+
/* @__PURE__ */ jsxs("tr", { className: "uix-data-workspace__group-head", "data-group-id": group.id, children: [
|
|
716
|
+
selectable && /* @__PURE__ */ jsx("td", { className: "uix-table__selcell", "data-col": ladderOn ? "select" : void 0, children: /* @__PURE__ */ jsx(
|
|
717
|
+
Checkbox,
|
|
718
|
+
{
|
|
719
|
+
"aria-label": fillLabel(labels.selectGroup, { name }),
|
|
720
|
+
disabled: group.rows.length === 0 || atCap && on === 0,
|
|
721
|
+
checked: on > 0 && on === groupIds.length,
|
|
722
|
+
indeterminate: on > 0 && on < groupIds.length,
|
|
723
|
+
onChange: () => toggleMany(groupIds, on < groupIds.length && !atCap)
|
|
724
|
+
}
|
|
725
|
+
) }),
|
|
726
|
+
/* @__PURE__ */ jsx("th", { scope: "rowgroup", colSpan: Math.max(1, lead), className: "uix-data-workspace__group-label", "aria-labelledby": `${nameId} ${countId}`, children: /* @__PURE__ */ jsxs("span", { className: "uix-data-workspace__group-title", children: [
|
|
727
|
+
/* @__PURE__ */ jsx(
|
|
728
|
+
"button",
|
|
729
|
+
{
|
|
730
|
+
type: "button",
|
|
731
|
+
className: "uix-data-workspace__group-toggle",
|
|
732
|
+
"aria-expanded": open,
|
|
733
|
+
"aria-label": fillLabel(open ? labels.collapseGroup : labels.expandGroup, { name }),
|
|
734
|
+
onClick: (event) => toggleGroup(group.id, event.currentTarget.closest("tr")),
|
|
735
|
+
children: /* @__PURE__ */ jsx(ChevronIcon, {})
|
|
736
|
+
}
|
|
737
|
+
),
|
|
738
|
+
/* @__PURE__ */ jsx("span", { className: "uix-data-workspace__group-name", id: nameId, children: group.label }),
|
|
739
|
+
/* @__PURE__ */ jsx("span", { className: "uix-data-workspace__group-count", id: countId, children: fillLabel(count === 1 ? labels.groupCountOne : labels.groupCount, { count }) }),
|
|
740
|
+
leadAgg
|
|
741
|
+
] }) }),
|
|
742
|
+
firstAgg >= 0 && shown.slice(lead === 0 ? 1 : firstAgg).map((column) => /* @__PURE__ */ jsx("td", { className: cx("uix-data-workspace__group-agg", column.align === "end" && "uix-col--num", column.className), children: group.aggregates?.[column.id] }, column.id)),
|
|
743
|
+
firstAgg >= 0 && hasActions && /* @__PURE__ */ jsx("td", { className: "uix-table__actions" })
|
|
744
|
+
] }),
|
|
745
|
+
open && group.rows.map((row, at) => renderRow(row, first + at))
|
|
746
|
+
] }, group.id);
|
|
747
|
+
});
|
|
748
|
+
} else {
|
|
749
|
+
const spacer = (height, key) => /* @__PURE__ */ jsx("tr", { "aria-hidden": "true", className: "uix-data-workspace__spacer", children: /* @__PURE__ */ jsx("td", { colSpan: colCount, style: { height } }) }, key);
|
|
750
|
+
tbody = /* @__PURE__ */ jsxs("tbody", { children: [
|
|
751
|
+
virtual && windowed && start > 0 && spacer(virtual.padTop, "pad-top"),
|
|
752
|
+
windowed && start > 0 && start % 2 === 0 && spacer(0, "pad-zebra"),
|
|
753
|
+
mountedRows.map((row, at) => renderRow(row, start + at)),
|
|
754
|
+
virtual && windowed && virtual.padBottom > 0 && spacer(virtual.padBottom, "pad-bottom")
|
|
755
|
+
] });
|
|
756
|
+
}
|
|
757
|
+
const wrapStyle = long ? { "--uix-table-max-height": `${maxHeight}px` } : void 0;
|
|
758
|
+
return /* @__PURE__ */ jsx(
|
|
759
|
+
TableWrap,
|
|
760
|
+
{
|
|
761
|
+
ref: setWrap,
|
|
762
|
+
refreshing,
|
|
763
|
+
style: wrapStyle,
|
|
764
|
+
tabIndex: windowing != null && nav ? -1 : void 0,
|
|
765
|
+
className: cx("uix-data-workspace__table", long && "uix-table-wrap--scroll", cards && "uix-data-workspace__table--cards"),
|
|
766
|
+
children: /* @__PURE__ */ jsxs(
|
|
767
|
+
Table,
|
|
768
|
+
{
|
|
769
|
+
density,
|
|
770
|
+
zebra,
|
|
771
|
+
pinnedCol: frozen,
|
|
772
|
+
pinnedActions: hasActions && !cards,
|
|
773
|
+
fixed: fixedOn || void 0,
|
|
774
|
+
style: tableStyle,
|
|
775
|
+
className: cx(
|
|
776
|
+
selectable && frozen && "uix-data-workspace__grid--pin-id",
|
|
777
|
+
ladder && `uix-table--${ladder}`,
|
|
778
|
+
groups && zebra !== false && "uix-data-workspace__grid--grouped",
|
|
779
|
+
nav && "uix-data-workspace__grid--rows",
|
|
780
|
+
clickActivates && "uix-data-workspace__grid--click"
|
|
781
|
+
),
|
|
782
|
+
"aria-label": label,
|
|
783
|
+
"aria-rowcount": windowed ? ids.length + 1 : void 0,
|
|
784
|
+
children: [
|
|
785
|
+
fixedOn && /* @__PURE__ */ jsxs("colgroup", { children: [
|
|
786
|
+
selectable && /* @__PURE__ */ jsx("col", { style: { width: "var(--uix-selcol-w)" } }),
|
|
787
|
+
idColumn && /* @__PURE__ */ jsx("col", { style: idColumn.width ? { width: idColumn.width } : void 0 }),
|
|
788
|
+
shown.map((column) => {
|
|
789
|
+
const width = column.width ?? (isFlex(column.className) ? void 0 : columnFloor(void 0, column.minWidth, column.className));
|
|
790
|
+
return /* @__PURE__ */ jsx("col", { className: colClasses(column.className), style: width ? { width } : void 0 }, column.id);
|
|
791
|
+
}),
|
|
792
|
+
hasActions && /* @__PURE__ */ jsx("col", { style: { width: actionsCol } })
|
|
793
|
+
] }),
|
|
794
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { "aria-rowindex": windowed ? 1 : void 0, children: [
|
|
795
|
+
selectable && /* @__PURE__ */ jsx("th", { className: "uix-table__selcol", scope: "col", "data-col": ladderOn ? "select" : void 0, children: /* @__PURE__ */ jsx(
|
|
796
|
+
Checkbox,
|
|
797
|
+
{
|
|
798
|
+
"aria-label": labels.selectAll,
|
|
799
|
+
checked: onPage > 0 && onPage === ids.length,
|
|
800
|
+
indeterminate: onPage > 0 && onPage < ids.length,
|
|
801
|
+
disabled: atCap && onPage === 0,
|
|
802
|
+
onChange: () => toggleMany(ids, onPage < ids.length && !atCap)
|
|
803
|
+
}
|
|
804
|
+
) }),
|
|
805
|
+
idColumn && /* @__PURE__ */ jsx(Th, { scope: "col", "data-col": ladderOn ? "id" : void 0, children: idColumn.header }),
|
|
806
|
+
columns.map((column) => {
|
|
807
|
+
const order = keys.findIndex((key) => key.columnId === column.id);
|
|
808
|
+
const canSort = column.sortable && sortable;
|
|
809
|
+
const style = !fixedOn && (column.width || column.minWidth) ? { width: column.width, minWidth: column.minWidth } : void 0;
|
|
810
|
+
const ordinal = keys.length > 1 && order >= 0 ? order + 1 : void 0;
|
|
811
|
+
return /* @__PURE__ */ jsx(
|
|
812
|
+
Th,
|
|
813
|
+
{
|
|
814
|
+
scope: "col",
|
|
815
|
+
className: cx(column.align === "end" && "uix-col--num", column.className) || void 0,
|
|
816
|
+
style,
|
|
817
|
+
sortable: canSort,
|
|
818
|
+
sortDirection: sortFor(column),
|
|
819
|
+
sortOrder: ordinal,
|
|
820
|
+
sortOrderLabel: ordinal != null ? fillLabel(labels.sortPriority, { n: ordinal }) : void 0,
|
|
821
|
+
onSort: () => {
|
|
822
|
+
cycleSort(column, multi && additive.current);
|
|
823
|
+
additive.current = false;
|
|
824
|
+
},
|
|
825
|
+
...canSort && multi ? {
|
|
826
|
+
onKeyDownCapture: (event) => {
|
|
827
|
+
if (event.key === "Enter" || event.key === " ") shiftKey.current = event.shiftKey;
|
|
828
|
+
},
|
|
829
|
+
// A click made by Enter or Space has no pointer (detail 0) and may not carry Shift itself.
|
|
830
|
+
onClickCapture: (event) => {
|
|
831
|
+
additive.current = event.shiftKey || event.detail === 0 && shiftKey.current;
|
|
832
|
+
shiftKey.current = false;
|
|
833
|
+
}
|
|
834
|
+
} : {},
|
|
835
|
+
...ladderAttrs(ladderOn, column, false),
|
|
836
|
+
children: column.header
|
|
837
|
+
},
|
|
838
|
+
column.id
|
|
839
|
+
);
|
|
840
|
+
}),
|
|
841
|
+
hasActions && /* @__PURE__ */ jsx("th", { scope: "col", className: "uix-table__actions", "data-col": ladderOn ? "actions" : void 0, children: /* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: labels.rowActions }) })
|
|
842
|
+
] }) }),
|
|
843
|
+
tbody
|
|
844
|
+
]
|
|
845
|
+
}
|
|
846
|
+
)
|
|
847
|
+
}
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
function WorkspaceRowInner({
|
|
851
|
+
row,
|
|
852
|
+
rowId,
|
|
853
|
+
columns,
|
|
854
|
+
idColumn,
|
|
855
|
+
getRowProps,
|
|
856
|
+
selectable,
|
|
857
|
+
selected,
|
|
858
|
+
selectDisabled,
|
|
859
|
+
hasActions,
|
|
860
|
+
actions,
|
|
861
|
+
ladderOn,
|
|
862
|
+
selectRowLabel,
|
|
863
|
+
openLabel,
|
|
864
|
+
marked,
|
|
865
|
+
ariaRowIndex,
|
|
866
|
+
nav,
|
|
867
|
+
tabStop,
|
|
868
|
+
handlers
|
|
869
|
+
}) {
|
|
870
|
+
const text = idColumn?.text(row) ?? rowId;
|
|
871
|
+
const extra = getRowProps?.(row);
|
|
872
|
+
const navProps = nav ? {
|
|
873
|
+
tabIndex: tabStop ? 0 : -1,
|
|
874
|
+
onKeyDown: (event) => {
|
|
875
|
+
extra?.onKeyDown?.(event);
|
|
876
|
+
if (!event.defaultPrevented) handlers.key(rowId, event);
|
|
877
|
+
},
|
|
878
|
+
onClick: (event) => {
|
|
879
|
+
extra?.onClick?.(event);
|
|
880
|
+
if (!event.defaultPrevented) handlers.click(rowId, event);
|
|
881
|
+
},
|
|
882
|
+
onFocus: (event) => {
|
|
883
|
+
extra?.onFocus?.(event);
|
|
884
|
+
handlers.focus(rowId);
|
|
885
|
+
},
|
|
886
|
+
onBlur: (event) => {
|
|
887
|
+
extra?.onBlur?.(event);
|
|
888
|
+
handlers.blur(rowId, event);
|
|
889
|
+
}
|
|
890
|
+
} : {};
|
|
891
|
+
return /* @__PURE__ */ jsxs(Tr, { selected, "data-uix-row-id": marked ? rowId : void 0, "aria-rowindex": ariaRowIndex, ...extra, ...navProps, children: [
|
|
892
|
+
selectable && /* @__PURE__ */ jsx("td", { className: "uix-table__selcell", "data-col": ladderOn ? "select" : void 0, children: /* @__PURE__ */ jsx(
|
|
893
|
+
Checkbox,
|
|
894
|
+
{
|
|
895
|
+
"aria-label": fillLabel(selectRowLabel, { label: text }),
|
|
896
|
+
checked: selected,
|
|
897
|
+
disabled: selectDisabled,
|
|
898
|
+
onClick: (event) => handlers.toggle(rowId, event.currentTarget.checked, event),
|
|
899
|
+
onChange: () => {
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
) }),
|
|
903
|
+
idColumn && /* @__PURE__ */ jsx(Td, { className: "uix-id-cell", "data-col": ladderOn ? "id" : void 0, children: /* @__PURE__ */ jsx(IdCell, { row, text, idColumn, openLabel }) }),
|
|
904
|
+
columns.map((column) => /* @__PURE__ */ jsx(Td, { className: cx(column.align === "end" && "uix-col--num", column.className) || void 0, ...ladderAttrs(ladderOn, column, true), children: column.cell(row) }, column.id)),
|
|
905
|
+
hasActions && /* @__PURE__ */ jsx(RowActions, { "data-col": ladderOn ? "actions" : void 0, children: actions })
|
|
906
|
+
] });
|
|
907
|
+
}
|
|
908
|
+
const WorkspaceRow = memo(WorkspaceRowInner);
|
|
909
|
+
function IdCell({ row, text, idColumn, openLabel }) {
|
|
910
|
+
const rich = idColumn.render ? /* @__PURE__ */ jsx("span", { className: "uix-data-workspace__id-text", children: idColumn.render(row) }) : null;
|
|
911
|
+
const arrow = idColumn.href && renderUixLink(idColumn.renderLink, {
|
|
912
|
+
href: idColumn.href(row),
|
|
913
|
+
className: "uix-data-workspace__open",
|
|
914
|
+
"aria-label": fillLabel(openLabel, { label: text }),
|
|
915
|
+
children: /* @__PURE__ */ jsx(ArrowUpRight, {})
|
|
916
|
+
});
|
|
917
|
+
if (!idColumn.onPeek && idColumn.href && idColumn.linkId) {
|
|
918
|
+
return /* @__PURE__ */ jsx("span", { className: "uix-data-workspace__id", children: renderUixLink(idColumn.renderLink, {
|
|
919
|
+
href: idColumn.href(row),
|
|
920
|
+
className: "uix-id-cell__btn uix-data-workspace__id-link",
|
|
921
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
922
|
+
rich ?? text,
|
|
923
|
+
/* @__PURE__ */ jsx(ArrowUpRight, {})
|
|
924
|
+
] })
|
|
925
|
+
}) });
|
|
926
|
+
}
|
|
927
|
+
return /* @__PURE__ */ jsxs("span", { className: "uix-data-workspace__id", children: [
|
|
928
|
+
idColumn.onPeek ? /* @__PURE__ */ jsx("button", { type: "button", className: "uix-id-cell__btn", "aria-haspopup": "dialog", onClick: () => idColumn.onPeek?.(row), children: rich ?? text }) : rich ?? /* @__PURE__ */ jsx("span", { className: "uix-cell-strong", children: text }),
|
|
929
|
+
arrow
|
|
930
|
+
] });
|
|
931
|
+
}
|
|
932
|
+
const cssEscape = (value) => typeof CSS !== "undefined" && CSS.escape ? CSS.escape(value) : value.replace(/["\\]/g, "\\$&");
|
|
266
933
|
function Footer({ pagination, labels, rowCount }) {
|
|
267
934
|
const sizes = pagination.pageSizes;
|
|
268
935
|
const size = pagination.mode === "offset" ? pagination.pageSize : pagination.pageSize;
|
|
@@ -310,7 +977,18 @@ function openInside(root) {
|
|
|
310
977
|
if (root.querySelector(":popover-open")) return true;
|
|
311
978
|
} catch {
|
|
312
979
|
}
|
|
313
|
-
|
|
980
|
+
if (root.querySelector("dialog[open]")) return true;
|
|
981
|
+
return Array.from(root.querySelectorAll('[aria-expanded="true"]')).some(stillExpanded);
|
|
982
|
+
}
|
|
983
|
+
function stillExpanded(el) {
|
|
984
|
+
const controls = el.getAttribute("aria-controls");
|
|
985
|
+
const target = controls ? document.getElementById(controls) : null;
|
|
986
|
+
if (!target?.hasAttribute("popover")) return true;
|
|
987
|
+
try {
|
|
988
|
+
return target.matches(":popover-open");
|
|
989
|
+
} catch {
|
|
990
|
+
return true;
|
|
991
|
+
}
|
|
314
992
|
}
|
|
315
993
|
function FilterChips({ filters, labels, baseId }) {
|
|
316
994
|
const [editing, setEditing] = useState(null);
|
|
@@ -353,6 +1031,10 @@ function FilterChips({ filters, labels, baseId }) {
|
|
|
353
1031
|
setFocusChip(null);
|
|
354
1032
|
}
|
|
355
1033
|
});
|
|
1034
|
+
const clearSet = () => {
|
|
1035
|
+
if (filters.onClearAll) filters.onClearAll({ search: false });
|
|
1036
|
+
else for (const f of set) filters.onChange(f.id, void 0);
|
|
1037
|
+
};
|
|
356
1038
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
357
1039
|
set.map((field) => /* @__PURE__ */ jsx("span", { ref: (el) => {
|
|
358
1040
|
if (el) anchors.current.set(field.id, el);
|
|
@@ -368,9 +1050,7 @@ function FilterChips({ filters, labels, baseId }) {
|
|
|
368
1050
|
}
|
|
369
1051
|
) }, field.id)),
|
|
370
1052
|
unset.length > 0 && /* @__PURE__ */ jsx("span", { ref: addRef, children: /* @__PURE__ */ jsx(Menu, { trigger: /* @__PURE__ */ jsx(Chip, { variant: "add", icon: /* @__PURE__ */ jsx(PlusIcon, {}), children: labels.addFilter }), children: unset.map((field) => /* @__PURE__ */ jsx(MenuItem, { onSelect: () => edit(field, addRef.current), children: field.label }, field.id)) }) }),
|
|
371
|
-
set.length > 0 && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "link", onClick:
|
|
372
|
-
for (const f of set) filters.onChange(f.id, void 0);
|
|
373
|
-
}, children: labels.clearFilters }),
|
|
1053
|
+
set.length > 0 && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "link", onClick: clearSet, children: labels.clearFilters }),
|
|
374
1054
|
/* @__PURE__ */ jsx(Popover, { id: popId, anchor: editing?.anchor ?? null, role: "dialog", "aria-label": editing?.field.label, children: editing && /* @__PURE__ */ jsx(FilterEditor, { field: editing.field, value: draft, onValueChange: setDraft, onApply: (v) => finish(v), onClear: () => finish(void 0) }) })
|
|
375
1055
|
] });
|
|
376
1056
|
}
|