@godxjp/ui 31.4.0 → 31.6.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/agent/START-HERE.md +6 -6
- package/agent/components/BranchScopePicker.json +5 -0
- package/agent/components/Cascader.json +1 -1
- package/agent/components/CodeBlock.json +7 -1
- package/agent/components/DataTable.json +2 -2
- package/agent/components/InfiniteQueryState.json +6 -1
- package/agent/components/OrgChart.json +59 -0
- package/agent/components/RangeTimeline.json +6 -0
- package/agent/components/RecordPicker.json +10 -0
- package/agent/components/Transfer.json +5 -0
- package/agent/components/Tree.json +21 -4
- package/agent/components/TreeSelect.json +1 -1
- package/agent/components-index.json +5 -0
- package/agent/components.json +123 -10
- package/agent/index.json +7 -7
- package/agent/llms.txt +7 -7
- package/agent/tokens.json +114 -0
- package/dist/components/data-display/code-block.js +90 -4
- package/dist/components/data-display/data-table.d.ts +3 -0
- package/dist/components/data-display/data-table.js +415 -370
- package/dist/components/data-display/index.d.ts +2 -0
- package/dist/components/data-display/index.js +2 -0
- package/dist/components/data-display/org-chart.d.ts +5 -0
- package/dist/components/data-display/org-chart.js +169 -0
- package/dist/components/data-display/range-timeline.d.ts +20 -0
- package/dist/components/data-display/range-timeline.js +217 -197
- package/dist/components/data-display/tree.js +179 -23
- package/dist/components/data-entry/branch-scope-picker.d.ts +1 -0
- package/dist/components/data-entry/branch-scope-picker.js +2 -1
- package/dist/components/data-entry/cascader.js +4 -5
- package/dist/components/data-entry/record-picker.d.ts +2 -0
- package/dist/components/data-entry/record-picker.js +22 -3
- package/dist/components/data-entry/transfer.d.ts +1 -1
- package/dist/components/data-entry/transfer.js +5 -2
- package/dist/components/data-entry/tree-select.js +14 -5
- package/dist/components/general/typography.d.ts +32 -0
- package/dist/components/general/typography.js +11 -2
- package/dist/components/query/infinite-query-state.d.ts +1 -1
- package/dist/components/query/infinite-query-state.js +2 -1
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +26 -2
- package/dist/i18n/messages/ja.json +17 -2
- package/dist/i18n/messages/vi.json +17 -2
- package/dist/lib/hooks.d.ts +8 -4
- package/dist/lib/hooks.js +5 -2
- package/dist/lib/tree.d.ts +23 -0
- package/dist/lib/tree.js +29 -0
- package/dist/props/components/data-display.prop.d.ts +72 -3
- package/dist/props/components/data-entry.prop.d.ts +31 -4
- package/dist/props/components/query.prop.d.ts +8 -1
- package/dist/props/registry.d.ts +63 -6
- package/dist/props/registry.js +90 -4
- package/dist/props/vocabulary/data.prop.d.ts +32 -2
- package/dist/props/vocabulary/index.d.ts +1 -1
- package/dist/styles/data-display-layout.css +225 -0
- package/dist/styles/layers.json +14 -1
- package/dist/styles/table-layout.css +16 -1
- package/dist/tokens/base.css +1 -0
- package/dist/tokens/components/org-chart.css +27 -0
- package/dist/tokens/components/tree.css +2 -0
- package/docs/FRAME-COVERAGE-REPORT.md +3 -2
- package/docs/data-display/code-block.tsx +27 -0
- package/docs/data-display/data-table/examples/server-paged.tsx +23 -0
- package/docs/data-display/data-table/index.md +2 -0
- package/docs/data-display/org-chart.tsx +165 -0
- package/docs/data-display/timeline.tsx +33 -0
- package/docs/data-display/tree.tsx +154 -0
- package/docs/data-entry/branch-scope-picker.tsx +6 -1
- package/docs/data-entry/record-picker.tsx +1 -0
- package/docs/data-entry/transfer.tsx +1 -0
- package/docs/i18n/messages/en.json +19 -0
- package/docs/i18n/messages/ja.json +18 -0
- package/docs/i18n/messages/vi.json +18 -0
- package/docs/navigation/tabs.tsx +17 -15
- package/docs/query/data-state.tsx +2 -4
- package/docs/query/infinite-query-state.tsx +5 -1
- package/docs/roadmap/tree-components.md +2 -1
- package/docs/showcase/table-view-tabs.tsx +1 -1
- package/package.json +2 -2
- package/scripts/ui-audit.mjs +15 -5
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import { mergeRefs } from "@react-aria/utils";
|
|
4
5
|
import { ChevronDown, ChevronRight, File as FileIcon, Folder, FolderOpen } from "lucide-react";
|
|
5
6
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
6
7
|
import { cn } from "../../lib/utils.js";
|
|
@@ -8,6 +9,7 @@ import { CheckboxVisual } from "../data-entry/checkbox.js";
|
|
|
8
9
|
import { Skeleton } from "../feedback/skeleton.js";
|
|
9
10
|
import {
|
|
10
11
|
collectAllExpandableKeys,
|
|
12
|
+
createLazyLoadLedger,
|
|
11
13
|
flattenVisibleTree,
|
|
12
14
|
getDescendantValues,
|
|
13
15
|
normalizeTreeOptions,
|
|
@@ -44,6 +46,7 @@ function indexTree(nodes, into = /* @__PURE__ */ new Map()) {
|
|
|
44
46
|
function isRtl(element) {
|
|
45
47
|
return element.closest("[dir]")?.getAttribute("dir")?.toLowerCase() === "rtl";
|
|
46
48
|
}
|
|
49
|
+
const VIRTUAL_OVERSCAN = 6;
|
|
47
50
|
function TreeRoot({
|
|
48
51
|
treeData,
|
|
49
52
|
fieldNames,
|
|
@@ -62,6 +65,9 @@ function TreeRoot({
|
|
|
62
65
|
defaultExpandAll = false,
|
|
63
66
|
loadData,
|
|
64
67
|
titleRender,
|
|
68
|
+
filterTreeNode,
|
|
69
|
+
height,
|
|
70
|
+
virtual = true,
|
|
65
71
|
showLine = false,
|
|
66
72
|
showIcon = false,
|
|
67
73
|
divided = false,
|
|
@@ -114,21 +120,29 @@ function TreeRoot({
|
|
|
114
120
|
if (!isCheckedControlled) setInternalChecked(settled);
|
|
115
121
|
onCheckedValuesChange?.(settled);
|
|
116
122
|
};
|
|
117
|
-
const
|
|
123
|
+
const [lazyLoads] = React.useState(createLazyLoadLedger);
|
|
124
|
+
const loadedFor = React.useRef(/* @__PURE__ */ new Set());
|
|
118
125
|
const [loadingValues, setLoadingValues] = React.useState(() => /* @__PURE__ */ new Set());
|
|
119
126
|
const requestLoad = (node) => {
|
|
120
127
|
if (!loadData) return;
|
|
121
128
|
if ((node.children?.length ?? 0) > 0 || node.isLeaf === true) return;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
const started = lazyLoads.run(
|
|
130
|
+
node.value,
|
|
131
|
+
() => loadData(node),
|
|
132
|
+
(ok) => {
|
|
133
|
+
setLoadingValues((prev) => {
|
|
134
|
+
const next = new Set(prev);
|
|
135
|
+
next.delete(node.value);
|
|
136
|
+
return next;
|
|
137
|
+
});
|
|
138
|
+
if (ok) return;
|
|
139
|
+
loadedFor.current.delete(node.value);
|
|
140
|
+
if (!isExpandedControlled) {
|
|
141
|
+
setInternalExpanded((prev) => prev.filter((entry) => entry !== node.value));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
if (started) setLoadingValues((prev) => new Set(prev).add(node.value));
|
|
132
146
|
};
|
|
133
147
|
const requestLoadRef = React.useRef(requestLoad);
|
|
134
148
|
requestLoadRef.current = requestLoad;
|
|
@@ -143,7 +157,6 @@ function TreeRoot({
|
|
|
143
157
|
walk(options);
|
|
144
158
|
return map;
|
|
145
159
|
}, [options]);
|
|
146
|
-
const loadedFor = React.useRef(/* @__PURE__ */ new Set());
|
|
147
160
|
React.useEffect(() => {
|
|
148
161
|
if (!loadData) return;
|
|
149
162
|
for (const value2 of expandedSet) {
|
|
@@ -157,6 +170,17 @@ function TreeRoot({
|
|
|
157
170
|
const visibleIndex = new Map(visible.map((entry, index) => [entry.node.value, index]));
|
|
158
171
|
const [activeValue, setActiveValue] = React.useState(null);
|
|
159
172
|
const rovingValue = (activeValue && visibleIndex.has(activeValue) ? activeValue : null) ?? visible.find((entry) => selected.includes(entry.node.value))?.node.value ?? visible[0]?.node.value ?? null;
|
|
173
|
+
const isWindowed = height !== void 0 && virtual;
|
|
174
|
+
const treeRef = React.useRef(null);
|
|
175
|
+
const [scrollTop, setScrollTop] = React.useState(0);
|
|
176
|
+
const [rowHeight, setRowHeight] = React.useState(0);
|
|
177
|
+
const [hasFocusWithin, setHasFocusWithin] = React.useState(false);
|
|
178
|
+
React.useLayoutEffect(() => {
|
|
179
|
+
if (!isWindowed) return;
|
|
180
|
+
const firstRow = treeRef.current?.querySelector('[role="treeitem"]');
|
|
181
|
+
const measured = firstRow?.offsetHeight ?? 0;
|
|
182
|
+
if (measured > 0 && measured !== rowHeight) setRowHeight(measured);
|
|
183
|
+
}, [isWindowed, rowHeight, size, visible.length]);
|
|
160
184
|
const nodeRefs = React.useRef(/* @__PURE__ */ new Map());
|
|
161
185
|
const pendingFocus = React.useRef(null);
|
|
162
186
|
React.useEffect(() => {
|
|
@@ -165,9 +189,23 @@ function TreeRoot({
|
|
|
165
189
|
pendingFocus.current = null;
|
|
166
190
|
nodeRefs.current.get(target)?.focus();
|
|
167
191
|
});
|
|
192
|
+
const revealRow = (value2) => {
|
|
193
|
+
const element = treeRef.current;
|
|
194
|
+
const rowIndex = rowIndexOf.get(value2);
|
|
195
|
+
if (!isWindowed || !element || rowHeight <= 0 || rowIndex === void 0) return;
|
|
196
|
+
const viewport = element.clientHeight || height || 0;
|
|
197
|
+
const top = rowIndex * rowHeight;
|
|
198
|
+
let next = element.scrollTop;
|
|
199
|
+
if (top < next) next = top;
|
|
200
|
+
else if (top + rowHeight > next + viewport) next = top + rowHeight - viewport;
|
|
201
|
+
if (next === element.scrollTop) return;
|
|
202
|
+
element.scrollTop = next;
|
|
203
|
+
setScrollTop(next);
|
|
204
|
+
};
|
|
168
205
|
const moveTo = (index) => {
|
|
169
206
|
const entry = visible[index];
|
|
170
207
|
if (!entry) return;
|
|
208
|
+
revealRow(entry.node.value);
|
|
171
209
|
setActiveValue(entry.node.value);
|
|
172
210
|
pendingFocus.current = entry.node.value;
|
|
173
211
|
};
|
|
@@ -318,19 +356,18 @@ function TreeRoot({
|
|
|
318
356
|
jumpByLabel(event.key, index);
|
|
319
357
|
}
|
|
320
358
|
};
|
|
321
|
-
const
|
|
359
|
+
const renderItem = (node, depth, setSize, posInSet, groupId) => {
|
|
322
360
|
const hasChildren = (node.children?.length ?? 0) > 0 && node.isLeaf !== true;
|
|
323
361
|
const expandable = expandableOf(node);
|
|
324
362
|
const isOpen = expandable && expandedSet.has(node.value);
|
|
325
363
|
const isLoading = loadingValues.has(node.value);
|
|
326
364
|
const isSelected = selected.includes(node.value);
|
|
365
|
+
const isFilterMatch = filterTreeNode ? filterTreeNode(node) : false;
|
|
327
366
|
const checkState = checkableProp ? checkStateOf(node) : "unchecked";
|
|
328
367
|
const nodeDisabled = disabled || Boolean(node.disabled);
|
|
329
368
|
const labelId = `${treeId}-${node.value}-label`;
|
|
330
|
-
const groupId = `${treeId}-${node.value}-group`;
|
|
331
|
-
const showGroup = isOpen && (hasChildren || isLoading);
|
|
332
369
|
const glyph = node.icon ?? (variant === "directory" ? hasChildren ? isOpen ? /* @__PURE__ */ jsx(FolderOpen, {}) : /* @__PURE__ */ jsx(Folder, {}) : /* @__PURE__ */ jsx(FileIcon, {}) : null);
|
|
333
|
-
|
|
370
|
+
return /* @__PURE__ */ jsxs(
|
|
334
371
|
"div",
|
|
335
372
|
{
|
|
336
373
|
ref: (element) => {
|
|
@@ -339,11 +376,11 @@ function TreeRoot({
|
|
|
339
376
|
role: "treeitem",
|
|
340
377
|
"aria-labelledby": labelId,
|
|
341
378
|
"aria-level": depth + 1,
|
|
342
|
-
"aria-setsize":
|
|
343
|
-
"aria-posinset":
|
|
379
|
+
"aria-setsize": setSize,
|
|
380
|
+
"aria-posinset": posInSet,
|
|
344
381
|
"aria-selected": isSelected,
|
|
345
382
|
"aria-expanded": expandable ? isOpen : void 0,
|
|
346
|
-
"aria-owns":
|
|
383
|
+
"aria-owns": groupId,
|
|
347
384
|
"aria-checked": checkableProp ? checkState === "indeterminate" ? "mixed" : checkState === "checked" : void 0,
|
|
348
385
|
"aria-disabled": nodeDisabled || void 0,
|
|
349
386
|
"aria-busy": isLoading || void 0,
|
|
@@ -357,9 +394,19 @@ function TreeRoot({
|
|
|
357
394
|
"data-value": node.value,
|
|
358
395
|
"data-selected": isSelected ? "true" : void 0,
|
|
359
396
|
"data-disabled": nodeDisabled ? "" : void 0,
|
|
397
|
+
"data-filter-node": isFilterMatch ? "true" : void 0,
|
|
360
398
|
className: "ui-tree-node ui-focus-ring",
|
|
361
399
|
style: { "--tree-node-level": depth },
|
|
362
400
|
children: [
|
|
401
|
+
isWindowed && showLine ? Array.from({ length: depth }, (_, level) => /* @__PURE__ */ jsx(
|
|
402
|
+
"span",
|
|
403
|
+
{
|
|
404
|
+
"aria-hidden": "true",
|
|
405
|
+
className: "ui-tree-rail",
|
|
406
|
+
style: { "--tree-rail-level": level + 1 }
|
|
407
|
+
},
|
|
408
|
+
`rail-${level}`
|
|
409
|
+
)) : null,
|
|
363
410
|
/* @__PURE__ */ jsx(
|
|
364
411
|
"span",
|
|
365
412
|
{
|
|
@@ -395,11 +442,26 @@ function TreeRoot({
|
|
|
395
442
|
) : null,
|
|
396
443
|
showIcon && glyph ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ui-tree-icon", children: glyph }) : null,
|
|
397
444
|
/* @__PURE__ */ jsx("span", { id: labelId, className: "ui-tree-label", children: titleRender ? titleRender(node) : node.label }),
|
|
398
|
-
isSelected ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: t("dataDisplay.tree.selected") }) : null
|
|
445
|
+
isSelected ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: t("dataDisplay.tree.selected") }) : null,
|
|
446
|
+
isFilterMatch ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: t("dataDisplay.tree.filterMatch") }) : null
|
|
399
447
|
]
|
|
400
448
|
},
|
|
401
449
|
node.value
|
|
402
450
|
);
|
|
451
|
+
};
|
|
452
|
+
const renderNodes = (nodes, depth) => nodes.flatMap((node, position) => {
|
|
453
|
+
const hasChildren = (node.children?.length ?? 0) > 0 && node.isLeaf !== true;
|
|
454
|
+
const isOpen = expandableOf(node) && expandedSet.has(node.value);
|
|
455
|
+
const isLoading = loadingValues.has(node.value);
|
|
456
|
+
const groupId = `${treeId}-${node.value}-group`;
|
|
457
|
+
const showGroup = isOpen && (hasChildren || isLoading);
|
|
458
|
+
const item = renderItem(
|
|
459
|
+
node,
|
|
460
|
+
depth,
|
|
461
|
+
nodes.length,
|
|
462
|
+
position + 1,
|
|
463
|
+
showGroup ? groupId : void 0
|
|
464
|
+
);
|
|
403
465
|
if (!showGroup) return [item];
|
|
404
466
|
return [
|
|
405
467
|
item,
|
|
@@ -426,13 +488,68 @@ function TreeRoot({
|
|
|
426
488
|
)
|
|
427
489
|
];
|
|
428
490
|
});
|
|
491
|
+
const flatRows = [];
|
|
492
|
+
if (isWindowed) {
|
|
493
|
+
const walk = (nodes, depth) => {
|
|
494
|
+
nodes.forEach((node, position) => {
|
|
495
|
+
flatRows.push({
|
|
496
|
+
kind: "node",
|
|
497
|
+
node,
|
|
498
|
+
depth,
|
|
499
|
+
setSize: nodes.length,
|
|
500
|
+
posInSet: position + 1
|
|
501
|
+
});
|
|
502
|
+
if (!expandableOf(node) || !expandedSet.has(node.value)) return;
|
|
503
|
+
if ((node.children?.length ?? 0) > 0 && node.isLeaf !== true)
|
|
504
|
+
walk(node.children, depth + 1);
|
|
505
|
+
else if (loadingValues.has(node.value)) {
|
|
506
|
+
flatRows.push({ kind: "loading", key: `${node.value}::loading`, depth: depth + 1 });
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
};
|
|
510
|
+
walk(options, 0);
|
|
511
|
+
}
|
|
512
|
+
const rowIndexOf = /* @__PURE__ */ new Map();
|
|
513
|
+
flatRows.forEach((row, index) => {
|
|
514
|
+
if (row.kind === "node") rowIndexOf.set(row.node.value, index);
|
|
515
|
+
});
|
|
516
|
+
let windowStart = 0;
|
|
517
|
+
let windowEnd = flatRows.length;
|
|
518
|
+
if (isWindowed && rowHeight > 0 && height !== void 0) {
|
|
519
|
+
const first = Math.floor(scrollTop / rowHeight);
|
|
520
|
+
windowStart = Math.max(0, first - VIRTUAL_OVERSCAN);
|
|
521
|
+
windowEnd = Math.min(flatRows.length, first + Math.ceil(height / rowHeight) + VIRTUAL_OVERSCAN);
|
|
522
|
+
const focusedRow = rovingValue === null ? void 0 : rowIndexOf.get(rovingValue);
|
|
523
|
+
if (hasFocusWithin && focusedRow !== void 0) {
|
|
524
|
+
windowStart = Math.min(windowStart, focusedRow);
|
|
525
|
+
windowEnd = Math.max(windowEnd, focusedRow + 1);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
const rovingRow = rovingValue === null ? void 0 : rowIndexOf.get(rovingValue);
|
|
529
|
+
const rovingOffscreen = isWindowed && rovingRow !== void 0 && (rovingRow < windowStart || rovingRow >= windowEnd);
|
|
429
530
|
const isEmpty = visible.length === 0;
|
|
531
|
+
const body = isEmpty ? null : isWindowed ? flatRows.slice(windowStart, windowEnd).map(
|
|
532
|
+
(row) => row.kind === "node" ? renderItem(row.node, row.depth, row.setSize, row.posInSet, void 0) : (
|
|
533
|
+
// A windowed tree has no `group` to hold the placeholder, and `role="tree"` may own
|
|
534
|
+
// only treeitems — so the skeleton is decoration and `aria-busy` on the row speaks.
|
|
535
|
+
/* @__PURE__ */ jsx(
|
|
536
|
+
"div",
|
|
537
|
+
{
|
|
538
|
+
"aria-hidden": "true",
|
|
539
|
+
className: "ui-tree-loading",
|
|
540
|
+
style: { "--tree-node-level": row.depth },
|
|
541
|
+
children: /* @__PURE__ */ jsx(Skeleton, { className: "ui-tree-loading-bar" })
|
|
542
|
+
},
|
|
543
|
+
row.key
|
|
544
|
+
)
|
|
545
|
+
)
|
|
546
|
+
) : renderNodes(options, 0);
|
|
430
547
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
431
|
-
/* @__PURE__ */
|
|
548
|
+
/* @__PURE__ */ jsxs(
|
|
432
549
|
"div",
|
|
433
550
|
{
|
|
434
551
|
...ariaProps,
|
|
435
|
-
ref: forwardedRef,
|
|
552
|
+
ref: mergeRefs(forwardedRef, treeRef),
|
|
436
553
|
id: treeId,
|
|
437
554
|
role: "tree",
|
|
438
555
|
"aria-multiselectable": multiple || checkableProp,
|
|
@@ -442,8 +559,47 @@ function TreeRoot({
|
|
|
442
559
|
"data-show-line": showLine ? "true" : void 0,
|
|
443
560
|
"data-divided": divided ? "true" : void 0,
|
|
444
561
|
"data-empty": isEmpty ? "true" : void 0,
|
|
562
|
+
"data-virtual": isWindowed ? "true" : void 0,
|
|
445
563
|
className: cn("ui-tree", className),
|
|
446
|
-
|
|
564
|
+
tabIndex: rovingOffscreen ? 0 : void 0,
|
|
565
|
+
style: height === void 0 ? void 0 : {
|
|
566
|
+
maxBlockSize: height,
|
|
567
|
+
overflowY: "auto"
|
|
568
|
+
},
|
|
569
|
+
onScroll: isWindowed ? (event) => setScrollTop(event.currentTarget.scrollTop) : void 0,
|
|
570
|
+
onFocus: (event) => {
|
|
571
|
+
if (!isWindowed) return;
|
|
572
|
+
setHasFocusWithin(true);
|
|
573
|
+
if (event.target === event.currentTarget && rovingValue !== null) {
|
|
574
|
+
revealRow(rovingValue);
|
|
575
|
+
pendingFocus.current = rovingValue;
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
onBlur: (event) => {
|
|
579
|
+
if (!isWindowed) return;
|
|
580
|
+
if (!event.currentTarget.contains(event.relatedTarget)) {
|
|
581
|
+
setHasFocusWithin(false);
|
|
582
|
+
}
|
|
583
|
+
},
|
|
584
|
+
children: [
|
|
585
|
+
isWindowed ? /* @__PURE__ */ jsx(
|
|
586
|
+
"div",
|
|
587
|
+
{
|
|
588
|
+
"aria-hidden": "true",
|
|
589
|
+
className: "ui-tree-spacer",
|
|
590
|
+
style: { blockSize: windowStart * rowHeight }
|
|
591
|
+
}
|
|
592
|
+
) : null,
|
|
593
|
+
body,
|
|
594
|
+
isWindowed ? /* @__PURE__ */ jsx(
|
|
595
|
+
"div",
|
|
596
|
+
{
|
|
597
|
+
"aria-hidden": "true",
|
|
598
|
+
className: "ui-tree-spacer",
|
|
599
|
+
style: { blockSize: (flatRows.length - windowEnd) * rowHeight }
|
|
600
|
+
}
|
|
601
|
+
) : null
|
|
602
|
+
]
|
|
447
603
|
}
|
|
448
604
|
),
|
|
449
605
|
isEmpty ? /* @__PURE__ */ jsx("p", { role: "status", className: "ui-tree-empty", children: t("dataDisplay.tree.empty") }) : null
|
|
@@ -16,6 +16,7 @@ export declare const BranchScopePicker: React.ForwardRefExoticComponent<import("
|
|
|
16
16
|
error?: import("../../props/index.js").ErrorProp;
|
|
17
17
|
loading?: boolean;
|
|
18
18
|
empty?: React.ReactNode;
|
|
19
|
+
notFoundContent?: import("../../props/index.js").NotFoundContentProp;
|
|
19
20
|
listError?: React.ReactNode;
|
|
20
21
|
denied?: React.ReactNode;
|
|
21
22
|
allLabel?: React.ReactNode;
|
|
@@ -25,6 +25,7 @@ const BranchScopePicker = React.forwardRef(
|
|
|
25
25
|
error,
|
|
26
26
|
loading = false,
|
|
27
27
|
empty,
|
|
28
|
+
notFoundContent,
|
|
28
29
|
listError,
|
|
29
30
|
denied,
|
|
30
31
|
allLabel,
|
|
@@ -156,7 +157,7 @@ const BranchScopePicker = React.forwardRef(
|
|
|
156
157
|
ariaLabel: t("dataEntry.branchScope.searchPlaceholder")
|
|
157
158
|
}
|
|
158
159
|
),
|
|
159
|
-
filtered.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "xs", tone: "muted", "aria-live": "polite", children: t("dataEntry.branchScope.noMatches") }) : (
|
|
160
|
+
filtered.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "xs", tone: "muted", "aria-live": "polite", children: notFoundContent ?? t("dataEntry.branchScope.noMatches") }) : (
|
|
160
161
|
// A bounded list is a scroll container, so it MUST be reachable and scrollable
|
|
161
162
|
// with the keyboard alone even when every row is unfocusable (all checkboxes
|
|
162
163
|
// `disabled`) — WCAG 2.1.1 / axe `scrollable-region-focusable`; same contract as
|
|
@@ -16,6 +16,7 @@ import { Popover, PopoverContent, PopoverTrigger } from "../data-display/popover
|
|
|
16
16
|
import { ScrollArea } from "../data-display/scroll-area.js";
|
|
17
17
|
import { Command, CommandInput } from "./command.js";
|
|
18
18
|
import {
|
|
19
|
+
createLazyLoadLedger,
|
|
19
20
|
filterTreeOptions,
|
|
20
21
|
formatPathLabels,
|
|
21
22
|
getNodeByPath,
|
|
@@ -175,18 +176,16 @@ function Cascader({
|
|
|
175
176
|
},
|
|
176
177
|
[isSearchControlled, onSearchChange]
|
|
177
178
|
);
|
|
178
|
-
const
|
|
179
|
+
const [lazyLoads] = React.useState(createLazyLoadLedger);
|
|
179
180
|
const requestLoad = React.useCallback(
|
|
180
181
|
(path, chain) => {
|
|
181
182
|
if (!loadData) return;
|
|
182
183
|
const node = chain.at(-1);
|
|
183
184
|
if (!node || node.isLeaf === true || (node.children?.length ?? 0) > 0) return;
|
|
184
185
|
const key = pathKey(path);
|
|
185
|
-
|
|
186
|
-
requestedLoads.current.add(key);
|
|
187
|
-
void loadData(chain);
|
|
186
|
+
lazyLoads.run(key, () => loadData(chain));
|
|
188
187
|
},
|
|
189
|
-
[loadData]
|
|
188
|
+
[loadData, lazyLoads]
|
|
190
189
|
);
|
|
191
190
|
const isControlledSingle = !multiple && value !== void 0;
|
|
192
191
|
const isControlledMulti = multiple && value !== void 0;
|
|
@@ -85,6 +85,8 @@ export declare const RecordPicker: React.ForwardRefExoticComponent<Omit<React.Bu
|
|
|
85
85
|
};
|
|
86
86
|
placeholder?: string;
|
|
87
87
|
dialogTitle?: string;
|
|
88
|
+
notFoundContent?: import("../../props/index.js").NotFoundContentProp;
|
|
89
|
+
loadMoreLabel?: import("../../props/index.js").LabelProp;
|
|
88
90
|
size?: "xs" | "sm" | "md" | "lg";
|
|
89
91
|
"data-field"?: string;
|
|
90
92
|
} & React.RefAttributes<HTMLButtonElement | HTMLInputElement>>;
|
|
@@ -50,6 +50,8 @@ const RecordPicker = React.forwardRef(
|
|
|
50
50
|
emptyOption,
|
|
51
51
|
placeholder,
|
|
52
52
|
dialogTitle,
|
|
53
|
+
notFoundContent,
|
|
54
|
+
loadMoreLabel,
|
|
53
55
|
disabled = false,
|
|
54
56
|
size,
|
|
55
57
|
className,
|
|
@@ -90,6 +92,8 @@ const RecordPicker = React.forwardRef(
|
|
|
90
92
|
emptyOption,
|
|
91
93
|
placeholder,
|
|
92
94
|
dialogTitle,
|
|
95
|
+
notFoundContent,
|
|
96
|
+
loadMoreLabel,
|
|
93
97
|
disabled,
|
|
94
98
|
size,
|
|
95
99
|
className,
|
|
@@ -115,6 +119,7 @@ const RecordPicker = React.forwardRef(
|
|
|
115
119
|
value: isMultiple ? selected : selected[0] ?? void 0,
|
|
116
120
|
onValueChange: ((next) => commit(toArray(next))),
|
|
117
121
|
placeholder: placeholder ?? t("dataEntry.recordPicker.placeholder"),
|
|
122
|
+
notFoundContent,
|
|
118
123
|
disabled,
|
|
119
124
|
size,
|
|
120
125
|
className
|
|
@@ -136,6 +141,8 @@ const RecordPicker = React.forwardRef(
|
|
|
136
141
|
emptyOption,
|
|
137
142
|
placeholder,
|
|
138
143
|
dialogTitle,
|
|
144
|
+
notFoundContent,
|
|
145
|
+
loadMoreLabel,
|
|
139
146
|
disabled,
|
|
140
147
|
size,
|
|
141
148
|
className,
|
|
@@ -164,6 +171,8 @@ const InlinePicker = React.forwardRef(function InlinePicker2({
|
|
|
164
171
|
emptyOption,
|
|
165
172
|
placeholder,
|
|
166
173
|
dialogTitle,
|
|
174
|
+
notFoundContent,
|
|
175
|
+
loadMoreLabel,
|
|
167
176
|
disabled,
|
|
168
177
|
size,
|
|
169
178
|
className,
|
|
@@ -295,6 +304,8 @@ const InlinePicker = React.forwardRef(function InlinePicker2({
|
|
|
295
304
|
selectedOptions,
|
|
296
305
|
emptyOption,
|
|
297
306
|
dialogTitle,
|
|
307
|
+
notFoundContent,
|
|
308
|
+
loadMoreLabel,
|
|
298
309
|
initialQuery: dialogQuery,
|
|
299
310
|
autoOpen: true,
|
|
300
311
|
onDismiss: () => setDialogQuery(null)
|
|
@@ -313,6 +324,8 @@ const DialogPicker = React.forwardRef(function DialogPicker2({
|
|
|
313
324
|
emptyOption,
|
|
314
325
|
placeholder,
|
|
315
326
|
dialogTitle,
|
|
327
|
+
notFoundContent,
|
|
328
|
+
loadMoreLabel,
|
|
316
329
|
disabled,
|
|
317
330
|
size,
|
|
318
331
|
className,
|
|
@@ -479,11 +492,17 @@ const DialogPicker = React.forwardRef(function DialogPicker2({
|
|
|
479
492
|
variant: "outline",
|
|
480
493
|
size: "sm",
|
|
481
494
|
onClick: () => void load(query, filterValues),
|
|
482
|
-
children: t("
|
|
495
|
+
children: t("common.retry")
|
|
483
496
|
}
|
|
484
497
|
)
|
|
485
498
|
}
|
|
486
|
-
) : status === "loading" ? /* @__PURE__ */ jsx("div", { role: "status", className: "ui-record-picker-status", children: t("dataEntry.recordPicker.loading") }) : visible.length === 0 ? /* @__PURE__ */ jsx(
|
|
499
|
+
) : status === "loading" ? /* @__PURE__ */ jsx("div", { role: "status", className: "ui-record-picker-status", children: t("dataEntry.recordPicker.loading") }) : visible.length === 0 ? /* @__PURE__ */ jsx(
|
|
500
|
+
EmptyState,
|
|
501
|
+
{
|
|
502
|
+
variant: "compact",
|
|
503
|
+
title: notFoundContent ?? t("dataEntry.recordPicker.empty")
|
|
504
|
+
}
|
|
505
|
+
) : groups.map(([heading, items]) => {
|
|
487
506
|
const rendered = items.map((o) => {
|
|
488
507
|
const picked = (isMultiple ? draft : selected).includes(o.value);
|
|
489
508
|
return /* @__PURE__ */ jsxs(
|
|
@@ -516,7 +535,7 @@ const DialogPicker = React.forwardRef(function DialogPicker2({
|
|
|
516
535
|
variant: "outline",
|
|
517
536
|
size: "sm",
|
|
518
537
|
onClick: () => void load(query, filterValues, cursor),
|
|
519
|
-
children: t("dataEntry.recordPicker.more")
|
|
538
|
+
children: loadMoreLabel ?? t("dataEntry.recordPicker.more")
|
|
520
539
|
}
|
|
521
540
|
) : null
|
|
522
541
|
] }) }),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import type { TransferProp } from "../../props/components/data-entry.prop.js";
|
|
3
3
|
export type { TransferProp, TransferProp as TransferProps, TransferItemProp, } from "../../props/components/data-entry.prop.js";
|
|
4
|
-
export declare function Transfer({ dataSource, targetKeys: legacyTargetKeys, value, defaultValue, name, readOnly, pagination, defaultTargetKeys, onValueChange, titles, showSearch, showSelectAll, filterOption, render, oneWay, disabled, id, className, selectedKeys: selectedKeysProp, onSelectChange, ...ariaProps }: TransferProp): React.JSX.Element;
|
|
4
|
+
export declare function Transfer({ dataSource, targetKeys: legacyTargetKeys, value, defaultValue, name, readOnly, pagination, defaultTargetKeys, onValueChange, titles, showSearch, showSelectAll, filterOption, render, oneWay, disabled, id, className, selectedKeys: selectedKeysProp, onSelectChange, locale, ...ariaProps }: TransferProp): React.JSX.Element;
|
|
@@ -158,6 +158,7 @@ function Transfer({
|
|
|
158
158
|
className,
|
|
159
159
|
selectedKeys: selectedKeysProp,
|
|
160
160
|
onSelectChange,
|
|
161
|
+
locale,
|
|
161
162
|
...ariaProps
|
|
162
163
|
}) {
|
|
163
164
|
const { t } = useTranslation();
|
|
@@ -192,6 +193,8 @@ function Transfer({
|
|
|
192
193
|
if (!selectedKeysProp) setInternalSelected(cleared);
|
|
193
194
|
onSelectChange?.(cleared[0], cleared[1]);
|
|
194
195
|
};
|
|
196
|
+
const notFound = locale?.notFoundContent;
|
|
197
|
+
const [leftNotFound, rightNotFound] = Array.isArray(notFound) ? notFound : [notFound, notFound];
|
|
195
198
|
const leftTitle = titles?.[0] ?? t("dataEntry.transfer.source");
|
|
196
199
|
const rightTitle = titles?.[1] ?? t("dataEntry.transfer.target");
|
|
197
200
|
return /* @__PURE__ */ jsxs(
|
|
@@ -219,7 +222,7 @@ function Transfer({
|
|
|
219
222
|
disabled: disabled || readOnly,
|
|
220
223
|
pagination,
|
|
221
224
|
searchPlaceholder: t("dataEntry.transfer.searchPlaceholder"),
|
|
222
|
-
emptyText: t("dataEntry.transfer.empty")
|
|
225
|
+
emptyText: leftNotFound ?? t("dataEntry.transfer.empty")
|
|
223
226
|
}
|
|
224
227
|
),
|
|
225
228
|
/* @__PURE__ */ jsxs("div", { className: "ui-transfer-actions", children: [
|
|
@@ -263,7 +266,7 @@ function Transfer({
|
|
|
263
266
|
disabled: disabled || readOnly,
|
|
264
267
|
pagination,
|
|
265
268
|
searchPlaceholder: t("dataEntry.transfer.searchPlaceholder"),
|
|
266
|
-
emptyText: t("dataEntry.transfer.empty")
|
|
269
|
+
emptyText: rightNotFound ?? t("dataEntry.transfer.empty")
|
|
267
270
|
}
|
|
268
271
|
)
|
|
269
272
|
]
|
|
@@ -19,6 +19,7 @@ import { SearchInput } from "./search-input.js";
|
|
|
19
19
|
import { Separator } from "../layout/separator.js";
|
|
20
20
|
import {
|
|
21
21
|
collectAllExpandableKeys,
|
|
22
|
+
createLazyLoadLedger,
|
|
22
23
|
filterVisibleTree,
|
|
23
24
|
findNodeByValue,
|
|
24
25
|
flattenVisibleTree,
|
|
@@ -134,15 +135,23 @@ function TreeSelectRoot({
|
|
|
134
135
|
if (!isControlled) setInternal(next);
|
|
135
136
|
onValueChange?.(checkable || multiple ? next : next[0] ?? void 0);
|
|
136
137
|
};
|
|
137
|
-
const
|
|
138
|
+
const [lazyLoads] = React.useState(createLazyLoadLedger);
|
|
138
139
|
const toggleExpand = (node) => {
|
|
139
140
|
const key = node.value;
|
|
140
141
|
const willExpand = !expandedKeys.has(key);
|
|
141
142
|
if (willExpand && loadData && !(node.children?.length ?? 0) && node.isLeaf !== true) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
lazyLoads.run(
|
|
144
|
+
key,
|
|
145
|
+
() => loadData(node),
|
|
146
|
+
(ok) => {
|
|
147
|
+
if (ok) return;
|
|
148
|
+
setExpandedKeys((prev) => {
|
|
149
|
+
const next = new Set(prev);
|
|
150
|
+
next.delete(key);
|
|
151
|
+
return next;
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
);
|
|
146
155
|
}
|
|
147
156
|
setExpandedKeys((prev) => {
|
|
148
157
|
const next = new Set(prev);
|
|
@@ -2,6 +2,38 @@ import * as React from "react";
|
|
|
2
2
|
import type { TextProp, TypographyBlockProp } from "../../props/components/general.prop.js";
|
|
3
3
|
import type { TypographyEllipsisConfigProp } from "../../props/vocabulary/index.js";
|
|
4
4
|
export type { TextProp, TextProp as TextProps, HeadingProp, HeadingProp as HeadingProps, TypographyProp, TypographyProp as TypographyProps, TypographyBlockProp, TypographyTitleProp, TypographyTitleProp as TitleProps, ParagraphProp, ParagraphProp as ParagraphProps, LinkProp, LinkProp as LinkProps, } from "../../props/components/general.prop.js";
|
|
5
|
+
/** How long the copy button shows its confirmed state. antd's value (`useCopyClick`). */
|
|
6
|
+
export declare const COPIED_RESET_MS = 3000;
|
|
7
|
+
/** antd `getNode` — `true`/`undefined` take the default; `false` suppresses unless a DOM node is required. */
|
|
8
|
+
export declare function getNode(node: React.ReactNode | boolean | undefined, fallback: React.ReactNode, needDom?: boolean): React.ReactNode;
|
|
9
|
+
/** antd `toCopyConfigList` — `false` suppresses BOTH states, otherwise index 0 = idle, 1 = copied. */
|
|
10
|
+
export declare function toCopyConfigList(value: React.ReactNode | false | undefined): (string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | Iterable<React.ReactNode> | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | React.ReactPortal | null | undefined> | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | React.ReactPortal | null | undefined)[] | readonly [false, false];
|
|
11
|
+
/**
|
|
12
|
+
* antd `useMergedConfig` — `true` means "on, all defaults"; an object means "on, these overrides".
|
|
13
|
+
* The boolean is the SUPPORT flag, so `copyable={false}` and `copyable={undefined}` are both off.
|
|
14
|
+
*/
|
|
15
|
+
export declare function useMergedConfig<T extends object>(propConfig: boolean | T | undefined, template?: Partial<T>): [boolean, T];
|
|
16
|
+
/**
|
|
17
|
+
* The clipboard write, antd's `_util/copy` reduced to the one path a modern browser takes.
|
|
18
|
+
*
|
|
19
|
+
* antd keeps a `document.execCommand("copy")` fallback for browsers without the async Clipboard
|
|
20
|
+
* API. It is NOT ported: `execCommand` is deprecated, every browser in this package's support
|
|
21
|
+
* matrix ships `navigator.clipboard`, and the fallback's failure mode is worse than none — it
|
|
22
|
+
* reports success from a synchronous flag that a permission prompt can leave stale, so the UI
|
|
23
|
+
* claims a copy that never happened. Here a refusal throws, `onCopy` never fires, and the button
|
|
24
|
+
* never shows its confirmed state.
|
|
25
|
+
*/
|
|
26
|
+
export declare function writeClipboard(text: string, format: "text/plain" | "text/html" | undefined): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* A tooltip that disappears entirely when its title is suppressed.
|
|
29
|
+
*
|
|
30
|
+
* antd wraps every action in `<Tooltip>`; this library's Tooltip is the house primitive and is
|
|
31
|
+
* composed (`Tooltip > TooltipTrigger > TooltipContent`), so the wrapper lives here.
|
|
32
|
+
*/
|
|
33
|
+
export declare function ActionTooltip({ title, children, }: {
|
|
34
|
+
title: React.ReactNode;
|
|
35
|
+
children: React.ReactNode;
|
|
36
|
+
}): string | number | bigint | boolean | React.JSX.Element | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | Iterable<React.ReactNode> | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | React.ReactPortal | null | undefined> | null | undefined;
|
|
5
37
|
/**
|
|
6
38
|
* Text — the typographic primitive, and antd's `Typography.Text`.
|
|
7
39
|
*
|
|
@@ -62,7 +62,10 @@ async function writeClipboard(text, format) {
|
|
|
62
62
|
function childrenToText(children) {
|
|
63
63
|
return React.Children.toArray(children).map((node) => typeof node === "string" || typeof node === "number" ? String(node) : "").join("");
|
|
64
64
|
}
|
|
65
|
-
function ActionTooltip({
|
|
65
|
+
function ActionTooltip({
|
|
66
|
+
title,
|
|
67
|
+
children
|
|
68
|
+
}) {
|
|
66
69
|
if (title === null || title === void 0 || title === false || title === "") return children;
|
|
67
70
|
return /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
68
71
|
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children }),
|
|
@@ -654,10 +657,16 @@ const Typography = Object.assign(TypographyRoot, {
|
|
|
654
657
|
Link
|
|
655
658
|
});
|
|
656
659
|
export {
|
|
660
|
+
ActionTooltip,
|
|
661
|
+
COPIED_RESET_MS,
|
|
657
662
|
Heading,
|
|
658
663
|
Link,
|
|
659
664
|
Paragraph,
|
|
660
665
|
Text,
|
|
661
666
|
Title,
|
|
662
|
-
Typography
|
|
667
|
+
Typography,
|
|
668
|
+
getNode,
|
|
669
|
+
toCopyConfigList,
|
|
670
|
+
useMergedConfig,
|
|
671
|
+
writeClipboard
|
|
663
672
|
};
|
|
@@ -27,4 +27,4 @@ export declare function flattenItemPages<TItem>(data: {
|
|
|
27
27
|
*/
|
|
28
28
|
export declare function InfiniteQueryState<TPage, TFlat = TPage extends {
|
|
29
29
|
items: (infer TItem)[];
|
|
30
|
-
} ? TItem[] : unknown>({ query, skeleton, empty, flatten, isEmpty, errorRenderer, showRetry, onRetry, onAuthError, loadingMore, loadMore, showLoadMore, children, }: InfiniteQueryStateProp<TPage, TFlat>): React.JSX.Element;
|
|
30
|
+
} ? TItem[] : unknown>({ query, skeleton, empty, flatten, isEmpty, errorRenderer, showRetry, onRetry, onAuthError, loadingMore, loadMore, loadMoreLabel, showLoadMore, children, }: InfiniteQueryStateProp<TPage, TFlat>): React.JSX.Element;
|
|
@@ -26,6 +26,7 @@ function InfiniteQueryState({
|
|
|
26
26
|
onAuthError,
|
|
27
27
|
loadingMore,
|
|
28
28
|
loadMore,
|
|
29
|
+
loadMoreLabel,
|
|
29
30
|
showLoadMore = true,
|
|
30
31
|
children
|
|
31
32
|
}) {
|
|
@@ -73,7 +74,7 @@ function InfiniteQueryState({
|
|
|
73
74
|
size: "sm",
|
|
74
75
|
disabled: query.isFetchingNextPage,
|
|
75
76
|
onClick: () => void query.fetchNextPage(),
|
|
76
|
-
children: query.isFetchingNextPage ? t("common.working") : t("query.loadMore")
|
|
77
|
+
children: query.isFetchingNextPage ? t("common.working") : loadMoreLabel ?? t("query.loadMore")
|
|
77
78
|
}
|
|
78
79
|
) }) : null;
|
|
79
80
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "AUTO-GENERATED by scripts/gen-measurement-contract.mjs — do not edit. Read this instead of guessing: docs/MEASUREMENT-CONTRACT.md.",
|
|
3
|
-
"version": "31.
|
|
3
|
+
"version": "31.6.0",
|
|
4
4
|
"targetSize": {
|
|
5
5
|
"standard": "WCAG 2.2 SC 2.5.8 Target Size (Minimum), level AA — 24×24 CSS px",
|
|
6
6
|
"min": 24,
|