@tensor_1/react 2.6.0 → 2.7.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/Field.js +10 -4
- package/dist/components/Popover.d.ts +18 -4
- package/dist/components/Popover.js +31 -2
- package/dist/components/Toast.js +5 -4
- package/dist/components/Tooltip.d.ts +9 -5
- package/dist/components/Tooltip.js +61 -3
- package/dist/components/Tree.d.ts +15 -1
- package/dist/components/Tree.js +247 -35
- package/dist/hooks/useAnchoredPosition.d.ts +22 -0
- package/dist/hooks/useAnchoredPosition.js +44 -0
- package/dist/hooks/useDialog.js +36 -5
- package/dist/index.cjs +657 -100
- package/dist/index.d.cts +164 -11
- package/dist/index.d.ts +3 -0
- package/dist/index.js +7 -0
- package/dist/overlay-position.d.ts +59 -0
- package/dist/overlay-position.js +62 -0
- package/dist/table-engine.js +72 -6
- package/dist/tree-model.d.ts +48 -0
- package/dist/tree-model.js +49 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -106,7 +106,9 @@ __export(src_exports, {
|
|
|
106
106
|
UserChip: () => UserChip,
|
|
107
107
|
applyFilters: () => applyFilters,
|
|
108
108
|
clampWidth: () => clampWidth,
|
|
109
|
+
computePosition: () => computePosition,
|
|
109
110
|
cx: () => cx,
|
|
111
|
+
flattenTree: () => flattenTree,
|
|
110
112
|
highlightSegments: () => highlightSegments,
|
|
111
113
|
matchFilter: () => matchFilter,
|
|
112
114
|
mergePinned: () => mergePinned,
|
|
@@ -121,6 +123,8 @@ __export(src_exports, {
|
|
|
121
123
|
toggleId: () => toggleId,
|
|
122
124
|
togglePage: () => togglePage,
|
|
123
125
|
toggleSort: () => toggleSort,
|
|
126
|
+
treeNav: () => treeNav,
|
|
127
|
+
useAnchoredPosition: () => useAnchoredPosition,
|
|
124
128
|
useDialog: () => useDialog,
|
|
125
129
|
useTable: () => useTable,
|
|
126
130
|
virtualWindow: () => virtualWindow
|
|
@@ -134,17 +138,48 @@ function cx(...args) {
|
|
|
134
138
|
|
|
135
139
|
// src/hooks/useDialog.ts
|
|
136
140
|
var import_react = require("react");
|
|
141
|
+
var lockCount = 0;
|
|
142
|
+
var saved = null;
|
|
143
|
+
function lockBodyScroll() {
|
|
144
|
+
if (typeof document === "undefined") return;
|
|
145
|
+
if (++lockCount > 1) return;
|
|
146
|
+
const el = document.scrollingElement ?? document.documentElement;
|
|
147
|
+
const scrollbarW = window.innerWidth - document.documentElement.clientWidth;
|
|
148
|
+
saved = {
|
|
149
|
+
el,
|
|
150
|
+
overflow: el.style.overflow,
|
|
151
|
+
paddingRight: el.style.paddingRight,
|
|
152
|
+
overscroll: el.style.overscrollBehavior
|
|
153
|
+
};
|
|
154
|
+
el.style.overflow = "hidden";
|
|
155
|
+
if (scrollbarW > 0) {
|
|
156
|
+
const current = parseFloat(getComputedStyle(el).paddingRight) || 0;
|
|
157
|
+
el.style.paddingRight = `${current + scrollbarW}px`;
|
|
158
|
+
}
|
|
159
|
+
el.style.overscrollBehavior = "contain";
|
|
160
|
+
}
|
|
161
|
+
function unlockBodyScroll() {
|
|
162
|
+
if (typeof document === "undefined" || lockCount === 0) return;
|
|
163
|
+
if (--lockCount > 0) return;
|
|
164
|
+
if (saved) {
|
|
165
|
+
saved.el.style.overflow = saved.overflow;
|
|
166
|
+
saved.el.style.paddingRight = saved.paddingRight;
|
|
167
|
+
saved.el.style.overscrollBehavior = saved.overscroll;
|
|
168
|
+
saved = null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
137
171
|
function useDialog(open) {
|
|
138
172
|
const ref = (0, import_react.useRef)(null);
|
|
139
173
|
(0, import_react.useEffect)(() => {
|
|
140
174
|
const el = ref.current;
|
|
141
|
-
if (!el)
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
} else {
|
|
145
|
-
if (el.open) el.close();
|
|
175
|
+
if (!el || !open) {
|
|
176
|
+
if (el && el.open) el.close();
|
|
177
|
+
return;
|
|
146
178
|
}
|
|
179
|
+
if (!el.open) el.showModal();
|
|
180
|
+
lockBodyScroll();
|
|
147
181
|
return () => {
|
|
182
|
+
unlockBodyScroll();
|
|
148
183
|
if (el.open) el.close();
|
|
149
184
|
};
|
|
150
185
|
}, [open]);
|
|
@@ -187,8 +222,36 @@ function toggleSort(keys, field, additive = false) {
|
|
|
187
222
|
function asNum(v) {
|
|
188
223
|
return typeof v === "number" ? v : Number(v);
|
|
189
224
|
}
|
|
225
|
+
function asTime(v) {
|
|
226
|
+
if (v == null) return NaN;
|
|
227
|
+
if (v instanceof Date) return v.getTime();
|
|
228
|
+
if (typeof v === "number") return v;
|
|
229
|
+
const s = String(v).trim();
|
|
230
|
+
if (s === "") return NaN;
|
|
231
|
+
const parsed = Date.parse(s);
|
|
232
|
+
return Number.isNaN(parsed) ? Number(s) : parsed;
|
|
233
|
+
}
|
|
234
|
+
function matchDate(cell, f) {
|
|
235
|
+
const c = asTime(cell);
|
|
236
|
+
if (Number.isNaN(c)) return false;
|
|
237
|
+
switch (f.op) {
|
|
238
|
+
case "eq":
|
|
239
|
+
return c === asTime(f.value);
|
|
240
|
+
case "lt":
|
|
241
|
+
return c < asTime(f.value);
|
|
242
|
+
case "gt":
|
|
243
|
+
return c > asTime(f.value);
|
|
244
|
+
case "between": {
|
|
245
|
+
const [lo, hi] = f.value.map(asTime);
|
|
246
|
+
return c >= lo && c <= hi;
|
|
247
|
+
}
|
|
248
|
+
default:
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
190
252
|
function matchFilter(row, f) {
|
|
191
253
|
const cell = row[f.field];
|
|
254
|
+
if (f.kind === "date") return matchDate(cell, f);
|
|
192
255
|
switch (f.op) {
|
|
193
256
|
case "isAnyOf":
|
|
194
257
|
return Array.isArray(f.value) && f.value.includes(cell);
|
|
@@ -247,13 +310,53 @@ function highlightSegments(text, query) {
|
|
|
247
310
|
}
|
|
248
311
|
return out;
|
|
249
312
|
}
|
|
313
|
+
var ARRAY_OPS = /* @__PURE__ */ new Set(["isAnyOf", "isNoneOf", "between"]);
|
|
314
|
+
var escapePart = (s) => encodeURIComponent(s).replace(/~/g, "%7E");
|
|
315
|
+
var unescapePart = (s) => decodeURIComponent(s);
|
|
316
|
+
var scalarToStr = (v) => v == null ? "" : String(v);
|
|
317
|
+
function parseScalar(s, kind) {
|
|
318
|
+
switch (kind) {
|
|
319
|
+
case "number":
|
|
320
|
+
return Number(s);
|
|
321
|
+
case "boolean":
|
|
322
|
+
return s === "true";
|
|
323
|
+
case "date":
|
|
324
|
+
return /^-?\d+$/.test(s) ? Number(s) : s;
|
|
325
|
+
// epoch-ms number, else the ISO/date string
|
|
326
|
+
default:
|
|
327
|
+
return s;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
function kindFromOp(op) {
|
|
331
|
+
if (op === "isAnyOf" || op === "isNoneOf") return "enum";
|
|
332
|
+
if (op === "is") return "boolean";
|
|
333
|
+
if (op === "contains" || op === "equals" || op === "startsWith") return "text";
|
|
334
|
+
return "number";
|
|
335
|
+
}
|
|
336
|
+
function serializeFilter(f) {
|
|
337
|
+
const value = Array.isArray(f.value) ? f.value.map((x) => escapePart(scalarToStr(x))).join("|") : escapePart(scalarToStr(f.value));
|
|
338
|
+
return `${escapePart(f.field)}~${f.kind}~${f.op}~${value}`;
|
|
339
|
+
}
|
|
340
|
+
function parseFilter(tok) {
|
|
341
|
+
const parts = tok.split("~");
|
|
342
|
+
let field, kind, op, raw;
|
|
343
|
+
if (parts.length >= 4) {
|
|
344
|
+
[field, kind, op, raw] = [unescapePart(parts[0]), parts[1], parts[2], parts[3] ?? ""];
|
|
345
|
+
} else {
|
|
346
|
+
[field, op, raw] = [unescapePart(parts[0]), parts[1], parts[2] ?? ""];
|
|
347
|
+
kind = kindFromOp(op);
|
|
348
|
+
}
|
|
349
|
+
if (!field || !op) return null;
|
|
350
|
+
const value = ARRAY_OPS.has(op) ? raw.split("|").filter((s) => s !== "").map((s) => parseScalar(unescapePart(s), kind)) : parseScalar(unescapePart(raw), kind);
|
|
351
|
+
return { field, kind, op, value };
|
|
352
|
+
}
|
|
250
353
|
function serializeView(v) {
|
|
251
354
|
const p = new URLSearchParams();
|
|
252
355
|
if (v.sort?.length) p.set("sort", v.sort.map((s) => `${s.field}:${s.dir === "descending" ? "d" : "a"}`).join(","));
|
|
253
356
|
if (v.columns?.length) p.set("cols", v.columns.join(","));
|
|
254
357
|
if (v.density && v.density !== "standard") p.set("density", v.density);
|
|
255
358
|
if (v.q) p.set("q", v.q);
|
|
256
|
-
if (v.filters?.length) p.set("filter", v.filters.map(
|
|
359
|
+
if (v.filters?.length) p.set("filter", v.filters.map(serializeFilter).join(","));
|
|
257
360
|
return p.toString();
|
|
258
361
|
}
|
|
259
362
|
function parseView(qs) {
|
|
@@ -271,11 +374,9 @@ function parseView(qs) {
|
|
|
271
374
|
const q = p.get("q");
|
|
272
375
|
if (q) v.q = q;
|
|
273
376
|
const filter = p.get("filter");
|
|
274
|
-
if (filter)
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
return { field, kind: "text", op, value };
|
|
278
|
-
});
|
|
377
|
+
if (filter) {
|
|
378
|
+
v.filters = filter.split(",").filter(Boolean).map(parseFilter).filter((f) => f !== null);
|
|
379
|
+
}
|
|
279
380
|
return v;
|
|
280
381
|
}
|
|
281
382
|
function virtualWindow(scrollTop, viewportH, rowH, count, overscan = 6) {
|
|
@@ -376,10 +477,114 @@ function useTable(data, options = {}) {
|
|
|
376
477
|
};
|
|
377
478
|
}
|
|
378
479
|
|
|
379
|
-
// src/
|
|
480
|
+
// src/hooks/useAnchoredPosition.ts
|
|
380
481
|
var import_react3 = require("react");
|
|
482
|
+
|
|
483
|
+
// src/overlay-position.ts
|
|
484
|
+
var OPPOSITE = { top: "bottom", bottom: "top", left: "right", right: "left" };
|
|
485
|
+
function parsePlacement(p) {
|
|
486
|
+
const [side, align] = p.split("-");
|
|
487
|
+
return { side, align: align ?? "center" };
|
|
488
|
+
}
|
|
489
|
+
var clamp = (v, min, max) => Math.max(min, Math.min(max, v));
|
|
490
|
+
function spaceOn(side, a, vp, offset) {
|
|
491
|
+
switch (side) {
|
|
492
|
+
case "top":
|
|
493
|
+
return a.y - offset;
|
|
494
|
+
case "bottom":
|
|
495
|
+
return vp.height - (a.y + a.height) - offset;
|
|
496
|
+
case "left":
|
|
497
|
+
return a.x - offset;
|
|
498
|
+
case "right":
|
|
499
|
+
return vp.width - (a.x + a.width) - offset;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
function fitsOn(side, a, f, vp, offset, padding) {
|
|
503
|
+
const need = side === "top" || side === "bottom" ? f.height : f.width;
|
|
504
|
+
return spaceOn(side, a, vp, offset) >= need + padding;
|
|
505
|
+
}
|
|
506
|
+
function computePosition(anchor, floating, viewport, options = {}) {
|
|
507
|
+
const { placement = "bottom-start", offset = 6, padding = 8, flip = true, shift = true } = options;
|
|
508
|
+
const { side: preferred, align } = parsePlacement(placement);
|
|
509
|
+
let side = preferred;
|
|
510
|
+
if (flip && !fitsOn(side, anchor, floating, viewport, offset, padding)) {
|
|
511
|
+
const opp = OPPOSITE[side];
|
|
512
|
+
if (fitsOn(opp, anchor, floating, viewport, offset, padding) || spaceOn(opp, anchor, viewport, offset) > spaceOn(side, anchor, viewport, offset)) {
|
|
513
|
+
side = opp;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
let x, y;
|
|
517
|
+
const horizontal = side === "top" || side === "bottom";
|
|
518
|
+
if (side === "top") y = anchor.y - floating.height - offset;
|
|
519
|
+
else if (side === "bottom") y = anchor.y + anchor.height + offset;
|
|
520
|
+
else if (side === "left") x = anchor.x - floating.width - offset;
|
|
521
|
+
else x = anchor.x + anchor.width + offset;
|
|
522
|
+
if (horizontal) {
|
|
523
|
+
const start = anchor.x;
|
|
524
|
+
const end = anchor.x + anchor.width - floating.width;
|
|
525
|
+
const center = anchor.x + (anchor.width - floating.width) / 2;
|
|
526
|
+
x = align === "start" ? start : align === "end" ? end : center;
|
|
527
|
+
if (shift) x = clamp(x, padding, Math.max(padding, viewport.width - floating.width - padding));
|
|
528
|
+
} else {
|
|
529
|
+
const start = anchor.y;
|
|
530
|
+
const end = anchor.y + anchor.height - floating.height;
|
|
531
|
+
const center = anchor.y + (anchor.height - floating.height) / 2;
|
|
532
|
+
y = align === "start" ? start : align === "end" ? end : center;
|
|
533
|
+
if (shift) y = clamp(y, padding, Math.max(padding, viewport.height - floating.height - padding));
|
|
534
|
+
}
|
|
535
|
+
return {
|
|
536
|
+
x,
|
|
537
|
+
y,
|
|
538
|
+
placement: align === "center" ? side : `${side}-${align}`,
|
|
539
|
+
side,
|
|
540
|
+
align
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// src/hooks/useAnchoredPosition.ts
|
|
545
|
+
var useIsomorphicLayoutEffect = typeof document !== "undefined" ? import_react3.useLayoutEffect : import_react3.useEffect;
|
|
546
|
+
var resolve = (a) => a && typeof a === "object" && "current" in a ? a.current : a ?? null;
|
|
547
|
+
function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-start", offset = 6, padding = 8 }) {
|
|
548
|
+
const opts = (0, import_react3.useRef)({ placement, offset, padding });
|
|
549
|
+
opts.current = { placement, offset, padding };
|
|
550
|
+
const reposition = (0, import_react3.useCallback)(() => {
|
|
551
|
+
const anchorEl = resolve(anchor);
|
|
552
|
+
const floating = floatingRef.current;
|
|
553
|
+
if (!anchorEl || !floating) return;
|
|
554
|
+
const s = floating.style;
|
|
555
|
+
s.position = "fixed";
|
|
556
|
+
s.inset = "auto";
|
|
557
|
+
s.margin = "0";
|
|
558
|
+
const a = anchorEl.getBoundingClientRect();
|
|
559
|
+
const { placement: p, offset: o, padding: pad } = opts.current;
|
|
560
|
+
const pos = computePosition(
|
|
561
|
+
{ x: a.x, y: a.y, width: a.width, height: a.height },
|
|
562
|
+
{ width: floating.offsetWidth, height: floating.offsetHeight },
|
|
563
|
+
{ width: window.innerWidth, height: window.innerHeight },
|
|
564
|
+
{ placement: p, offset: o, padding: pad }
|
|
565
|
+
);
|
|
566
|
+
s.left = `${Math.round(pos.x)}px`;
|
|
567
|
+
s.top = `${Math.round(pos.y)}px`;
|
|
568
|
+
floating.dataset.placement = pos.placement;
|
|
569
|
+
}, [anchor, floatingRef]);
|
|
570
|
+
useIsomorphicLayoutEffect(() => {
|
|
571
|
+
if (!open) return;
|
|
572
|
+
reposition();
|
|
573
|
+
const onScroll = () => reposition();
|
|
574
|
+
window.addEventListener("scroll", onScroll, { passive: true, capture: true });
|
|
575
|
+
window.addEventListener("resize", onScroll);
|
|
576
|
+
return () => {
|
|
577
|
+
window.removeEventListener("scroll", onScroll, true);
|
|
578
|
+
window.removeEventListener("resize", onScroll);
|
|
579
|
+
};
|
|
580
|
+
}, [open, reposition]);
|
|
581
|
+
return reposition;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// src/components/Button.tsx
|
|
585
|
+
var import_react4 = require("react");
|
|
381
586
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
382
|
-
var Button = (0,
|
|
587
|
+
var Button = (0, import_react4.forwardRef)(
|
|
383
588
|
({ variant = "secondary", size = "md", icon = false, loading = false, className, children, disabled, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
384
589
|
"button",
|
|
385
590
|
{
|
|
@@ -404,9 +609,9 @@ function ButtonGroup({ children, className }) {
|
|
|
404
609
|
}
|
|
405
610
|
|
|
406
611
|
// src/components/Input.tsx
|
|
407
|
-
var
|
|
612
|
+
var import_react5 = require("react");
|
|
408
613
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
409
|
-
var Input = (0,
|
|
614
|
+
var Input = (0, import_react5.forwardRef)(
|
|
410
615
|
({ size = "md", invalid, valid, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
411
616
|
"input",
|
|
412
617
|
{
|
|
@@ -439,9 +644,9 @@ function InputGroup({ children, leadingIcon, trailingIcon, className }) {
|
|
|
439
644
|
}
|
|
440
645
|
|
|
441
646
|
// src/components/Textarea.tsx
|
|
442
|
-
var
|
|
647
|
+
var import_react6 = require("react");
|
|
443
648
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
444
|
-
var Textarea = (0,
|
|
649
|
+
var Textarea = (0, import_react6.forwardRef)(
|
|
445
650
|
({ invalid, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
446
651
|
"textarea",
|
|
447
652
|
{
|
|
@@ -455,9 +660,9 @@ var Textarea = (0, import_react5.forwardRef)(
|
|
|
455
660
|
Textarea.displayName = "Textarea";
|
|
456
661
|
|
|
457
662
|
// src/components/Select.tsx
|
|
458
|
-
var
|
|
663
|
+
var import_react7 = require("react");
|
|
459
664
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
460
|
-
var Select = (0,
|
|
665
|
+
var Select = (0, import_react7.forwardRef)(
|
|
461
666
|
({ size = "md", className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
462
667
|
"select",
|
|
463
668
|
{
|
|
@@ -470,14 +675,14 @@ var Select = (0, import_react6.forwardRef)(
|
|
|
470
675
|
Select.displayName = "Select";
|
|
471
676
|
|
|
472
677
|
// src/components/Checkbox.tsx
|
|
473
|
-
var
|
|
678
|
+
var import_react8 = require("react");
|
|
474
679
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
475
680
|
var CheckIcon = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "1.5,6 4.5,9 10.5,3" }) });
|
|
476
681
|
var IndeterminateIcon = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { viewBox: "0 0 12 12", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: "2", y: "5", width: "8", height: "2", rx: "1", fill: "currentColor" }) });
|
|
477
|
-
var Checkbox = (0,
|
|
682
|
+
var Checkbox = (0, import_react8.forwardRef)(
|
|
478
683
|
({ label, indeterminate, className, ...props }, forwardedRef) => {
|
|
479
|
-
const innerRef = (0,
|
|
480
|
-
(0,
|
|
684
|
+
const innerRef = (0, import_react8.useRef)(null);
|
|
685
|
+
(0, import_react8.useEffect)(() => {
|
|
481
686
|
const el = innerRef.current;
|
|
482
687
|
if (el) el.indeterminate = !!indeterminate;
|
|
483
688
|
}, [indeterminate]);
|
|
@@ -502,9 +707,9 @@ var Checkbox = (0, import_react7.forwardRef)(
|
|
|
502
707
|
Checkbox.displayName = "Checkbox";
|
|
503
708
|
|
|
504
709
|
// src/components/Radio.tsx
|
|
505
|
-
var
|
|
710
|
+
var import_react9 = require("react");
|
|
506
711
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
507
|
-
var Radio = (0,
|
|
712
|
+
var Radio = (0, import_react9.forwardRef)(
|
|
508
713
|
({ label, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: cx("uix-radio", className), children: [
|
|
509
714
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "radio", ref, ...props }),
|
|
510
715
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "uix-radio__dot" }),
|
|
@@ -517,9 +722,9 @@ function RadioGroup({ children, className }) {
|
|
|
517
722
|
}
|
|
518
723
|
|
|
519
724
|
// src/components/Switch.tsx
|
|
520
|
-
var
|
|
725
|
+
var import_react10 = require("react");
|
|
521
726
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
522
|
-
var Switch = (0,
|
|
727
|
+
var Switch = (0, import_react10.forwardRef)(
|
|
523
728
|
({ label, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: cx("uix-switch", className), children: [
|
|
524
729
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { type: "checkbox", role: "switch", ref, ...props }),
|
|
525
730
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "uix-switch__track" }),
|
|
@@ -529,14 +734,20 @@ var Switch = (0, import_react9.forwardRef)(
|
|
|
529
734
|
Switch.displayName = "Switch";
|
|
530
735
|
|
|
531
736
|
// src/components/Field.tsx
|
|
737
|
+
var import_react11 = require("react");
|
|
532
738
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
533
739
|
function Field({ label, hint, error, success, required, children, className, htmlFor }) {
|
|
740
|
+
const uid = (0, import_react11.useId)();
|
|
741
|
+
const msgId = `${uid}-msg`;
|
|
742
|
+
const describedBy = error || success || hint ? msgId : void 0;
|
|
743
|
+
const control = (0, import_react11.isValidElement)(children) ? (0, import_react11.cloneElement)(children, {
|
|
744
|
+
"aria-describedby": [children.props["aria-describedby"], describedBy].filter(Boolean).join(" ") || void 0,
|
|
745
|
+
"aria-invalid": error ? true : children.props["aria-invalid"]
|
|
746
|
+
}) : children;
|
|
534
747
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: cx("uix-field", className), children: [
|
|
535
748
|
label && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { className: "uix-field__label", htmlFor, "data-required": required || void 0, children: label }),
|
|
536
|
-
|
|
537
|
-
error
|
|
538
|
-
success && !error && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-field__success", children: success }),
|
|
539
|
-
hint && !error && !success && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-field__hint", children: hint })
|
|
749
|
+
control,
|
|
750
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "uix-field__msg", id: msgId, children: error ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-field__error", role: "alert", children: error }) : success ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-field__success", children: success }) : hint ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-field__hint", children: hint }) : null })
|
|
540
751
|
] });
|
|
541
752
|
}
|
|
542
753
|
|
|
@@ -666,11 +877,11 @@ function ListItem({ title, meta, trail, leading, children, className, ...props }
|
|
|
666
877
|
}
|
|
667
878
|
|
|
668
879
|
// src/components/DescriptionList.tsx
|
|
669
|
-
var
|
|
880
|
+
var import_react12 = require("react");
|
|
670
881
|
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
671
882
|
function DescriptionList({ items, children, className, ...props }) {
|
|
672
883
|
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("dl", { className: cx("uix-dl", className), ...props, children: [
|
|
673
|
-
items?.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
884
|
+
items?.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react12.Fragment, { children: [
|
|
674
885
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dt", { children: item.term }),
|
|
675
886
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dd", { children: item.description })
|
|
676
887
|
] }, item.id ?? i)),
|
|
@@ -685,14 +896,14 @@ function DescriptionItem({ term, children }) {
|
|
|
685
896
|
}
|
|
686
897
|
|
|
687
898
|
// src/components/Tabs.tsx
|
|
688
|
-
var
|
|
899
|
+
var import_react13 = require("react");
|
|
689
900
|
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
690
|
-
var TabsCtx = (0,
|
|
901
|
+
var TabsCtx = (0, import_react13.createContext)({});
|
|
691
902
|
function Tabs({ variant = "line", value, onChange, children, className }) {
|
|
692
903
|
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(TabsCtx.Provider, { value: { value, onChange }, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { role: "tablist", className: cx("uix-tabs", `uix-tabs--${variant}`, className), children }) });
|
|
693
904
|
}
|
|
694
905
|
function Tab({ value, children, disabled, className, onClick, ...props }) {
|
|
695
|
-
const ctx = (0,
|
|
906
|
+
const ctx = (0, import_react13.useContext)(TabsCtx);
|
|
696
907
|
const selected = ctx.value === value;
|
|
697
908
|
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
698
909
|
"button",
|
|
@@ -712,7 +923,7 @@ function Tab({ value, children, disabled, className, onClick, ...props }) {
|
|
|
712
923
|
}
|
|
713
924
|
|
|
714
925
|
// src/components/AppShell.tsx
|
|
715
|
-
var
|
|
926
|
+
var import_react14 = require("react");
|
|
716
927
|
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
717
928
|
function AppShell({
|
|
718
929
|
nav,
|
|
@@ -727,7 +938,7 @@ function AppShell({
|
|
|
727
938
|
...props
|
|
728
939
|
}) {
|
|
729
940
|
const navState = nav ?? (collapsed ? "rail" : "full");
|
|
730
|
-
(0,
|
|
941
|
+
(0, import_react14.useEffect)(() => {
|
|
731
942
|
if (!focus || !onExitFocus) return;
|
|
732
943
|
const onKey = (e) => {
|
|
733
944
|
if (e.key === "Escape") onExitFocus();
|
|
@@ -754,7 +965,7 @@ function AppShell({
|
|
|
754
965
|
}
|
|
755
966
|
|
|
756
967
|
// src/components/Sidebar.tsx
|
|
757
|
-
var
|
|
968
|
+
var import_react15 = require("react");
|
|
758
969
|
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
759
970
|
var ChevronIcon = () => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("svg", { className: "uix-navgroup__chevron", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("path", { d: "M2 4l4 4 4-4" }) });
|
|
760
971
|
function Sidebar({ collapsed, onToggle, brand, toggleIcon, children, className, ...props }) {
|
|
@@ -808,7 +1019,7 @@ function NavItem({ icon, badge, active, as: Tag = "a", children, className, ...p
|
|
|
808
1019
|
);
|
|
809
1020
|
}
|
|
810
1021
|
function NavGroup({ icon, label, children, defaultExpanded = true }) {
|
|
811
|
-
const [expanded, setExpanded] = (0,
|
|
1022
|
+
const [expanded, setExpanded] = (0, import_react15.useState)(defaultExpanded);
|
|
812
1023
|
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "uix-navgroup", children: [
|
|
813
1024
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
814
1025
|
"button",
|
|
@@ -876,7 +1087,7 @@ function StarButton({
|
|
|
876
1087
|
}
|
|
877
1088
|
|
|
878
1089
|
// src/components/NavFavourites.tsx
|
|
879
|
-
var
|
|
1090
|
+
var import_react16 = require("react");
|
|
880
1091
|
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
881
1092
|
var ChevronDownIcon = () => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
882
1093
|
"svg",
|
|
@@ -940,13 +1151,13 @@ function NavFavourites({
|
|
|
940
1151
|
emptyReason,
|
|
941
1152
|
className
|
|
942
1153
|
}) {
|
|
943
|
-
const regionId = (0,
|
|
944
|
-
const headerRef = (0,
|
|
945
|
-
const regionRef = (0,
|
|
946
|
-
const focusAfterRef = (0,
|
|
947
|
-
const triggerRefs = (0,
|
|
1154
|
+
const regionId = (0, import_react16.useId)();
|
|
1155
|
+
const headerRef = (0, import_react16.useRef)(null);
|
|
1156
|
+
const regionRef = (0, import_react16.useRef)(null);
|
|
1157
|
+
const focusAfterRef = (0, import_react16.useRef)(null);
|
|
1158
|
+
const triggerRefs = (0, import_react16.useRef)(/* @__PURE__ */ new Map());
|
|
948
1159
|
const order = items.map((i) => i.id);
|
|
949
|
-
(0,
|
|
1160
|
+
(0, import_react16.useEffect)(() => {
|
|
950
1161
|
const id = focusAfterRef.current;
|
|
951
1162
|
if (!id) return;
|
|
952
1163
|
focusAfterRef.current = null;
|
|
@@ -1057,18 +1268,18 @@ function FavouriteRow({
|
|
|
1057
1268
|
onRemove,
|
|
1058
1269
|
registerTrigger
|
|
1059
1270
|
}) {
|
|
1060
|
-
const [open, setOpen] = (0,
|
|
1061
|
-
const [revealed, setRevealed] = (0,
|
|
1062
|
-
const menuId = (0,
|
|
1063
|
-
const triggerRef = (0,
|
|
1064
|
-
const itemRefs = (0,
|
|
1271
|
+
const [open, setOpen] = (0, import_react16.useState)(false);
|
|
1272
|
+
const [revealed, setRevealed] = (0, import_react16.useState)(false);
|
|
1273
|
+
const menuId = (0, import_react16.useId)();
|
|
1274
|
+
const triggerRef = (0, import_react16.useRef)(null);
|
|
1275
|
+
const itemRefs = (0, import_react16.useRef)([]);
|
|
1065
1276
|
const optionsTemplate = labels.options ?? "{label} favourite options";
|
|
1066
1277
|
const actions = [
|
|
1067
1278
|
...isFirst ? [] : [{ label: labels.moveUp, onSelect: onMoveUp }],
|
|
1068
1279
|
...isLast ? [] : [{ label: labels.moveDown, onSelect: onMoveDown }],
|
|
1069
1280
|
{ label: labels.remove.replace("{label}", item.label), onSelect: onRemove }
|
|
1070
1281
|
];
|
|
1071
|
-
(0,
|
|
1282
|
+
(0, import_react16.useEffect)(() => {
|
|
1072
1283
|
if (open) itemRefs.current[0]?.focus();
|
|
1073
1284
|
}, [open]);
|
|
1074
1285
|
const closeAndReturnFocus = () => {
|
|
@@ -1253,16 +1464,17 @@ function Spinner({ size = "md", accent, className, ...props }) {
|
|
|
1253
1464
|
// src/components/Toast.tsx
|
|
1254
1465
|
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1255
1466
|
function Toast({ title, message, tone, icon, onClose, leaving, className, ...props }) {
|
|
1467
|
+
const assertive = tone === "danger";
|
|
1256
1468
|
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
1257
1469
|
"div",
|
|
1258
1470
|
{
|
|
1259
1471
|
className: cx("uix-toast", tone && `uix-toast--${tone}`, className),
|
|
1260
1472
|
"data-leaving": leaving || void 0,
|
|
1261
|
-
role: "status",
|
|
1262
|
-
"aria-live": "polite",
|
|
1473
|
+
role: assertive ? "alert" : "status",
|
|
1474
|
+
"aria-live": assertive ? "assertive" : "polite",
|
|
1263
1475
|
...props,
|
|
1264
1476
|
children: [
|
|
1265
|
-
icon && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "uix-toast__icon", children: icon }),
|
|
1477
|
+
icon && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "uix-toast__icon", "aria-hidden": "true", children: icon }),
|
|
1266
1478
|
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "uix-toast__body", children: [
|
|
1267
1479
|
title && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "uix-toast__title", children: title }),
|
|
1268
1480
|
message && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "uix-toast__msg", children: message })
|
|
@@ -1273,7 +1485,7 @@ function Toast({ title, message, tone, icon, onClose, leaving, className, ...pro
|
|
|
1273
1485
|
);
|
|
1274
1486
|
}
|
|
1275
1487
|
function Toaster({ children, className, ...props }) {
|
|
1276
|
-
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cx("uix-toaster", className),
|
|
1488
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cx("uix-toaster", className), ...props, children });
|
|
1277
1489
|
}
|
|
1278
1490
|
|
|
1279
1491
|
// src/components/Table.tsx
|
|
@@ -1510,9 +1722,64 @@ function Stat({ label, value, icon, meta, trend, className, ...props }) {
|
|
|
1510
1722
|
}
|
|
1511
1723
|
|
|
1512
1724
|
// src/components/Tooltip.tsx
|
|
1725
|
+
var import_react17 = require("react");
|
|
1513
1726
|
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1514
|
-
function Tooltip({ label, children, ...props }) {
|
|
1515
|
-
|
|
1727
|
+
function Tooltip({ label, placement = "top", className, children, ...props }) {
|
|
1728
|
+
const triggerRef = (0, import_react17.useRef)(null);
|
|
1729
|
+
const bubbleRef = (0, import_react17.useRef)(null);
|
|
1730
|
+
const [open, setOpen] = (0, import_react17.useState)(false);
|
|
1731
|
+
const id = (0, import_react17.useId)();
|
|
1732
|
+
useAnchoredPosition(triggerRef, bubbleRef, { open, placement, offset: 6 });
|
|
1733
|
+
const show = () => {
|
|
1734
|
+
const b = bubbleRef.current;
|
|
1735
|
+
if (b && !b.matches(":popover-open")) {
|
|
1736
|
+
try {
|
|
1737
|
+
b.showPopover();
|
|
1738
|
+
} catch {
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
setOpen(true);
|
|
1742
|
+
};
|
|
1743
|
+
const hide = () => {
|
|
1744
|
+
const b = bubbleRef.current;
|
|
1745
|
+
if (b && b.matches(":popover-open")) {
|
|
1746
|
+
try {
|
|
1747
|
+
b.hidePopover();
|
|
1748
|
+
} catch {
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
setOpen(false);
|
|
1752
|
+
};
|
|
1753
|
+
(0, import_react17.useEffect)(() => () => {
|
|
1754
|
+
const b = bubbleRef.current;
|
|
1755
|
+
if (b && b.matches(":popover-open")) {
|
|
1756
|
+
try {
|
|
1757
|
+
b.hidePopover();
|
|
1758
|
+
} catch {
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
}, []);
|
|
1762
|
+
const bubbleAttr = { popover: "manual" };
|
|
1763
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
1764
|
+
"span",
|
|
1765
|
+
{
|
|
1766
|
+
ref: triggerRef,
|
|
1767
|
+
className: cx("uix-tooltip", className),
|
|
1768
|
+
"aria-describedby": id,
|
|
1769
|
+
onMouseEnter: show,
|
|
1770
|
+
onMouseLeave: hide,
|
|
1771
|
+
onFocus: show,
|
|
1772
|
+
onBlur: hide,
|
|
1773
|
+
onKeyDown: (e) => {
|
|
1774
|
+
if (e.key === "Escape") hide();
|
|
1775
|
+
},
|
|
1776
|
+
...props,
|
|
1777
|
+
children: [
|
|
1778
|
+
children,
|
|
1779
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { ref: bubbleRef, id, role: "tooltip", className: "uix-tooltip__bubble", ...bubbleAttr, children: label })
|
|
1780
|
+
]
|
|
1781
|
+
}
|
|
1782
|
+
);
|
|
1516
1783
|
}
|
|
1517
1784
|
|
|
1518
1785
|
// src/components/States.tsx
|
|
@@ -1614,9 +1881,9 @@ function ComposerBar({ children, className, ...props }) {
|
|
|
1614
1881
|
}
|
|
1615
1882
|
|
|
1616
1883
|
// src/components/Segmented.tsx
|
|
1617
|
-
var
|
|
1884
|
+
var import_react18 = require("react");
|
|
1618
1885
|
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
1619
|
-
var SegmentedCtx = (0,
|
|
1886
|
+
var SegmentedCtx = (0, import_react18.createContext)({});
|
|
1620
1887
|
function Segmented({ value, onChange, children, className, ...props }) {
|
|
1621
1888
|
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(SegmentedCtx.Provider, { value: { value, onChange }, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { role: "group", className: cx("uix-segmented", className), ...props, children }) });
|
|
1622
1889
|
}
|
|
@@ -1627,7 +1894,7 @@ function SegmentedOption({
|
|
|
1627
1894
|
onClick,
|
|
1628
1895
|
...props
|
|
1629
1896
|
}) {
|
|
1630
|
-
const ctx = (0,
|
|
1897
|
+
const ctx = (0, import_react18.useContext)(SegmentedCtx);
|
|
1631
1898
|
const selected = ctx.value === value;
|
|
1632
1899
|
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
1633
1900
|
"button",
|
|
@@ -1676,10 +1943,37 @@ function Note({ tone, icon, children, className, ...props }) {
|
|
|
1676
1943
|
}
|
|
1677
1944
|
|
|
1678
1945
|
// src/components/Popover.tsx
|
|
1946
|
+
var import_react19 = require("react");
|
|
1679
1947
|
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
1680
|
-
function Popover({
|
|
1948
|
+
function Popover({
|
|
1949
|
+
popover = "auto",
|
|
1950
|
+
anchor,
|
|
1951
|
+
placement = "bottom-start",
|
|
1952
|
+
offset = 6,
|
|
1953
|
+
className,
|
|
1954
|
+
children,
|
|
1955
|
+
...props
|
|
1956
|
+
}) {
|
|
1957
|
+
const ref = (0, import_react19.useRef)(null);
|
|
1958
|
+
const [open, setOpen] = (0, import_react19.useState)(false);
|
|
1681
1959
|
const popoverAttr = { popover };
|
|
1682
|
-
|
|
1960
|
+
const reposition = useAnchoredPosition(anchor ?? null, ref, {
|
|
1961
|
+
open: open && !!anchor,
|
|
1962
|
+
placement,
|
|
1963
|
+
offset
|
|
1964
|
+
});
|
|
1965
|
+
(0, import_react19.useEffect)(() => {
|
|
1966
|
+
const el = ref.current;
|
|
1967
|
+
if (!el || !anchor) return;
|
|
1968
|
+
const sync = () => {
|
|
1969
|
+
const isOpen = el.matches(":popover-open");
|
|
1970
|
+
if (isOpen) reposition();
|
|
1971
|
+
setOpen(isOpen);
|
|
1972
|
+
};
|
|
1973
|
+
el.addEventListener("toggle", sync);
|
|
1974
|
+
return () => el.removeEventListener("toggle", sync);
|
|
1975
|
+
}, [anchor, reposition]);
|
|
1976
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { ref, className: cx("uix-popover", className), ...popoverAttr, ...props, children });
|
|
1683
1977
|
}
|
|
1684
1978
|
|
|
1685
1979
|
// src/components/CommandPalette.tsx
|
|
@@ -1766,48 +2060,124 @@ function KanbanCard({ title, meta, children, className, ...props }) {
|
|
|
1766
2060
|
}
|
|
1767
2061
|
|
|
1768
2062
|
// src/components/Tree.tsx
|
|
1769
|
-
var
|
|
2063
|
+
var import_react20 = require("react");
|
|
2064
|
+
|
|
2065
|
+
// src/tree-model.ts
|
|
2066
|
+
function flattenTree(nodes, expanded, level = 1) {
|
|
2067
|
+
const out = [];
|
|
2068
|
+
const setsize = nodes.length;
|
|
2069
|
+
nodes.forEach((node, i) => {
|
|
2070
|
+
const hasChildren = Array.isArray(node.children) && node.children.length > 0;
|
|
2071
|
+
const isExpanded = hasChildren && expanded.has(node.id);
|
|
2072
|
+
out.push({ node, level, setsize, posinset: i + 1, hasChildren, isExpanded });
|
|
2073
|
+
if (isExpanded) out.push(...flattenTree(node.children, expanded, level + 1));
|
|
2074
|
+
});
|
|
2075
|
+
return out;
|
|
2076
|
+
}
|
|
2077
|
+
function treeNav(flat, currentId, key) {
|
|
2078
|
+
const idx = flat.findIndex((f) => f.node.id === currentId);
|
|
2079
|
+
if (idx < 0) return {};
|
|
2080
|
+
const cur = flat[idx];
|
|
2081
|
+
const focusAt = (i) => {
|
|
2082
|
+
const clamped = Math.max(0, Math.min(flat.length - 1, i));
|
|
2083
|
+
return { focusId: flat[clamped]?.node.id };
|
|
2084
|
+
};
|
|
2085
|
+
switch (key) {
|
|
2086
|
+
case "ArrowDown":
|
|
2087
|
+
return focusAt(idx + 1);
|
|
2088
|
+
case "ArrowUp":
|
|
2089
|
+
return focusAt(idx - 1);
|
|
2090
|
+
case "Home":
|
|
2091
|
+
return focusAt(0);
|
|
2092
|
+
case "End":
|
|
2093
|
+
return focusAt(flat.length - 1);
|
|
2094
|
+
case "ArrowRight":
|
|
2095
|
+
if (cur.hasChildren && !cur.isExpanded) return { toggleId: cur.node.id };
|
|
2096
|
+
if (cur.hasChildren && cur.isExpanded) return focusAt(idx + 1);
|
|
2097
|
+
return {};
|
|
2098
|
+
case "ArrowLeft":
|
|
2099
|
+
if (cur.hasChildren && cur.isExpanded) return { toggleId: cur.node.id };
|
|
2100
|
+
for (let i = idx - 1; i >= 0; i--) {
|
|
2101
|
+
if (flat[i].level === cur.level - 1) return { focusId: flat[i].node.id };
|
|
2102
|
+
}
|
|
2103
|
+
return {};
|
|
2104
|
+
case "Enter":
|
|
2105
|
+
case " ":
|
|
2106
|
+
return cur.hasChildren ? { toggleId: cur.node.id, selectId: cur.node.id } : { selectId: cur.node.id };
|
|
2107
|
+
default:
|
|
2108
|
+
return {};
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
// src/components/Tree.tsx
|
|
1770
2113
|
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
1771
2114
|
var ChevronIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("svg", { viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("path", { d: "M2 4l4 4 4-4" }) });
|
|
1772
|
-
function TreeNode({ node, expanded, selected, onToggle, onSelect, level =
|
|
2115
|
+
function TreeNode({ node, expanded, selected, selectable, tabbableId, onToggle, onSelect, onFocusNode, level = 1 }) {
|
|
1773
2116
|
const hasChildren = Array.isArray(node.children) && node.children.length > 0;
|
|
1774
2117
|
const isExpanded = expanded.has(node.id);
|
|
1775
|
-
return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
2118
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
|
|
2119
|
+
"li",
|
|
2120
|
+
{
|
|
2121
|
+
role: "treeitem",
|
|
2122
|
+
"aria-level": level,
|
|
2123
|
+
"aria-expanded": hasChildren ? isExpanded : void 0,
|
|
2124
|
+
"aria-selected": selectable ? selected === node.id : void 0,
|
|
2125
|
+
"data-id": node.id,
|
|
2126
|
+
tabIndex: tabbableId === node.id ? 0 : -1,
|
|
2127
|
+
className: "uix-tree__item",
|
|
2128
|
+
onFocus: (e) => {
|
|
2129
|
+
if (e.target === e.currentTarget) onFocusNode(node.id);
|
|
2130
|
+
},
|
|
2131
|
+
onClick: (e) => {
|
|
2132
|
+
e.stopPropagation();
|
|
2133
|
+
if (hasChildren) onToggle(node.id);
|
|
2134
|
+
onSelect?.(node.id);
|
|
2135
|
+
onFocusNode(node.id);
|
|
2136
|
+
},
|
|
2137
|
+
children: [
|
|
2138
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "uix-tree__row", children: [
|
|
1787
2139
|
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "uix-tree__toggle", children: hasChildren && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ChevronIcon2, {}) }),
|
|
1788
2140
|
node.icon && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { "aria-hidden": "true", children: node.icon }),
|
|
1789
|
-
node.label
|
|
1790
|
-
]
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
2141
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "uix-tree__label", children: node.label })
|
|
2142
|
+
] }),
|
|
2143
|
+
hasChildren && isExpanded && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { role: "group", className: "uix-tree__group", children: node.children.map((child) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2144
|
+
TreeNode,
|
|
2145
|
+
{
|
|
2146
|
+
node: child,
|
|
2147
|
+
expanded,
|
|
2148
|
+
selected,
|
|
2149
|
+
selectable,
|
|
2150
|
+
tabbableId,
|
|
2151
|
+
onToggle,
|
|
2152
|
+
onSelect,
|
|
2153
|
+
onFocusNode,
|
|
2154
|
+
level: level + 1
|
|
2155
|
+
},
|
|
2156
|
+
child.id
|
|
2157
|
+
)) })
|
|
2158
|
+
]
|
|
2159
|
+
}
|
|
2160
|
+
);
|
|
1806
2161
|
}
|
|
1807
|
-
function Tree({
|
|
1808
|
-
|
|
2162
|
+
function Tree({
|
|
2163
|
+
nodes,
|
|
2164
|
+
expanded: controlledExpanded,
|
|
2165
|
+
defaultExpanded,
|
|
2166
|
+
selected,
|
|
2167
|
+
onToggle,
|
|
2168
|
+
onSelect,
|
|
2169
|
+
virtualize,
|
|
2170
|
+
rowHeight = 32,
|
|
2171
|
+
maxHeight = 384,
|
|
2172
|
+
className,
|
|
2173
|
+
...props
|
|
2174
|
+
}) {
|
|
2175
|
+
const [internalExpanded, setInternalExpanded] = (0, import_react20.useState)(defaultExpanded ?? /* @__PURE__ */ new Set());
|
|
1809
2176
|
const expanded = controlledExpanded ?? internalExpanded;
|
|
1810
|
-
|
|
2177
|
+
const rootRef = (0, import_react20.useRef)(null);
|
|
2178
|
+
const [focusedId, setFocusedId] = (0, import_react20.useState)(void 0);
|
|
2179
|
+
const selectable = selected !== void 0 || onSelect != null;
|
|
2180
|
+
const handleToggle = (0, import_react20.useCallback)((id) => {
|
|
1811
2181
|
if (!controlledExpanded) {
|
|
1812
2182
|
setInternalExpanded((prev) => {
|
|
1813
2183
|
const next = new Set(prev);
|
|
@@ -1817,19 +2187,202 @@ function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected,
|
|
|
1817
2187
|
});
|
|
1818
2188
|
}
|
|
1819
2189
|
onToggle?.(id);
|
|
2190
|
+
}, [controlledExpanded, onToggle]);
|
|
2191
|
+
const flat = (0, import_react20.useMemo)(() => flattenTree(nodes, expanded), [nodes, expanded]);
|
|
2192
|
+
const useVirtual = virtualize ?? shouldVirtualize(flat.length);
|
|
2193
|
+
const tabbableId = focusedId ?? selected ?? nodes[0]?.id;
|
|
2194
|
+
if (useVirtual) {
|
|
2195
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2196
|
+
VirtualTreeView,
|
|
2197
|
+
{
|
|
2198
|
+
flat,
|
|
2199
|
+
rowHeight,
|
|
2200
|
+
maxHeight,
|
|
2201
|
+
selectable,
|
|
2202
|
+
selected,
|
|
2203
|
+
tabbableId,
|
|
2204
|
+
onToggle: handleToggle,
|
|
2205
|
+
onSelect,
|
|
2206
|
+
focusedId,
|
|
2207
|
+
setFocusedId,
|
|
2208
|
+
className,
|
|
2209
|
+
...props
|
|
2210
|
+
}
|
|
2211
|
+
);
|
|
1820
2212
|
}
|
|
1821
|
-
|
|
2213
|
+
const treeItems = () => Array.from(rootRef.current?.querySelectorAll('[role="treeitem"]') ?? []);
|
|
2214
|
+
const focusItem = (el) => {
|
|
2215
|
+
if (el) {
|
|
2216
|
+
el.focus();
|
|
2217
|
+
setFocusedId(el.dataset.id);
|
|
2218
|
+
}
|
|
2219
|
+
};
|
|
2220
|
+
const onKeyDown = (e) => {
|
|
2221
|
+
const cur = e.target.closest('[role="treeitem"]');
|
|
2222
|
+
if (!cur || !rootRef.current?.contains(cur)) return;
|
|
2223
|
+
const list = treeItems();
|
|
2224
|
+
const idx = list.indexOf(cur);
|
|
2225
|
+
const id = cur.dataset.id;
|
|
2226
|
+
const exp = cur.getAttribute("aria-expanded");
|
|
2227
|
+
switch (e.key) {
|
|
2228
|
+
case "ArrowDown":
|
|
2229
|
+
e.preventDefault();
|
|
2230
|
+
focusItem(list[Math.min(idx + 1, list.length - 1)]);
|
|
2231
|
+
break;
|
|
2232
|
+
case "ArrowUp":
|
|
2233
|
+
e.preventDefault();
|
|
2234
|
+
focusItem(list[Math.max(idx - 1, 0)]);
|
|
2235
|
+
break;
|
|
2236
|
+
case "Home":
|
|
2237
|
+
e.preventDefault();
|
|
2238
|
+
focusItem(list[0]);
|
|
2239
|
+
break;
|
|
2240
|
+
case "End":
|
|
2241
|
+
e.preventDefault();
|
|
2242
|
+
focusItem(list[list.length - 1]);
|
|
2243
|
+
break;
|
|
2244
|
+
case "ArrowRight":
|
|
2245
|
+
e.preventDefault();
|
|
2246
|
+
if (exp === "false") handleToggle(id);
|
|
2247
|
+
else if (exp === "true") focusItem(cur.querySelector(':scope > [role="group"] > [role="treeitem"]'));
|
|
2248
|
+
break;
|
|
2249
|
+
case "ArrowLeft":
|
|
2250
|
+
e.preventDefault();
|
|
2251
|
+
if (exp === "true") handleToggle(id);
|
|
2252
|
+
else focusItem(cur.parentElement?.closest('[role="treeitem"]'));
|
|
2253
|
+
break;
|
|
2254
|
+
case "Enter":
|
|
2255
|
+
case " ":
|
|
2256
|
+
e.preventDefault();
|
|
2257
|
+
if (exp !== null) handleToggle(id);
|
|
2258
|
+
onSelect?.(id);
|
|
2259
|
+
setFocusedId(id);
|
|
2260
|
+
break;
|
|
2261
|
+
default:
|
|
2262
|
+
break;
|
|
2263
|
+
}
|
|
2264
|
+
};
|
|
2265
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { ref: rootRef, className: cx("uix-tree", className), role: "tree", onKeyDown, ...props, children: nodes.map((node) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
1822
2266
|
TreeNode,
|
|
1823
2267
|
{
|
|
1824
2268
|
node,
|
|
1825
2269
|
expanded,
|
|
1826
2270
|
selected,
|
|
2271
|
+
selectable,
|
|
2272
|
+
tabbableId,
|
|
1827
2273
|
onToggle: handleToggle,
|
|
1828
|
-
onSelect
|
|
2274
|
+
onSelect,
|
|
2275
|
+
onFocusNode: setFocusedId,
|
|
2276
|
+
level: 1
|
|
1829
2277
|
},
|
|
1830
2278
|
node.id
|
|
1831
2279
|
)) });
|
|
1832
2280
|
}
|
|
2281
|
+
function VirtualTreeView({
|
|
2282
|
+
flat,
|
|
2283
|
+
rowHeight,
|
|
2284
|
+
maxHeight,
|
|
2285
|
+
selectable,
|
|
2286
|
+
selected,
|
|
2287
|
+
tabbableId,
|
|
2288
|
+
onToggle,
|
|
2289
|
+
onSelect,
|
|
2290
|
+
focusedId,
|
|
2291
|
+
setFocusedId,
|
|
2292
|
+
className,
|
|
2293
|
+
...props
|
|
2294
|
+
}) {
|
|
2295
|
+
const scrollRef = (0, import_react20.useRef)(null);
|
|
2296
|
+
const [scrollTop, setScrollTop] = (0, import_react20.useState)(0);
|
|
2297
|
+
const pendingFocus = (0, import_react20.useRef)(false);
|
|
2298
|
+
const win = virtualWindow(scrollTop, maxHeight, rowHeight, flat.length);
|
|
2299
|
+
const rows = flat.slice(win.start, win.end);
|
|
2300
|
+
(0, import_react20.useEffect)(() => {
|
|
2301
|
+
if (!pendingFocus.current || focusedId == null) return;
|
|
2302
|
+
const el = scrollRef.current?.querySelector(`[role="treeitem"][data-id="${CSS.escape(focusedId)}"]`);
|
|
2303
|
+
if (el) {
|
|
2304
|
+
el.focus();
|
|
2305
|
+
pendingFocus.current = false;
|
|
2306
|
+
}
|
|
2307
|
+
}, [focusedId, win.start, win.end]);
|
|
2308
|
+
const moveTo = (id) => {
|
|
2309
|
+
const i = flat.findIndex((f) => f.node.id === id);
|
|
2310
|
+
if (i < 0) return;
|
|
2311
|
+
const top = i * rowHeight;
|
|
2312
|
+
let next = scrollTop;
|
|
2313
|
+
if (top < scrollTop) next = top;
|
|
2314
|
+
else if (top + rowHeight > scrollTop + maxHeight) next = top + rowHeight - maxHeight;
|
|
2315
|
+
if (next !== scrollTop) {
|
|
2316
|
+
if (scrollRef.current) scrollRef.current.scrollTop = next;
|
|
2317
|
+
setScrollTop(next);
|
|
2318
|
+
}
|
|
2319
|
+
pendingFocus.current = true;
|
|
2320
|
+
setFocusedId(id);
|
|
2321
|
+
};
|
|
2322
|
+
const onKeyDown = (e) => {
|
|
2323
|
+
const curId = e.target.closest('[role="treeitem"]')?.dataset.id ?? focusedId;
|
|
2324
|
+
const act = treeNav(flat, curId, e.key);
|
|
2325
|
+
if (act.focusId === void 0 && act.toggleId === void 0 && act.selectId === void 0) return;
|
|
2326
|
+
e.preventDefault();
|
|
2327
|
+
if (act.toggleId !== void 0) onToggle(act.toggleId);
|
|
2328
|
+
if (act.selectId !== void 0) {
|
|
2329
|
+
onSelect?.(act.selectId);
|
|
2330
|
+
if (act.focusId === void 0) {
|
|
2331
|
+
pendingFocus.current = true;
|
|
2332
|
+
setFocusedId(act.selectId);
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
if (act.focusId !== void 0) moveTo(act.focusId);
|
|
2336
|
+
};
|
|
2337
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
|
|
2338
|
+
"ul",
|
|
2339
|
+
{
|
|
2340
|
+
ref: scrollRef,
|
|
2341
|
+
className: cx("uix-tree", "uix-tree--virtual", className),
|
|
2342
|
+
role: "tree",
|
|
2343
|
+
style: { maxHeight, overflow: "auto" },
|
|
2344
|
+
onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
|
|
2345
|
+
onKeyDown,
|
|
2346
|
+
...props,
|
|
2347
|
+
children: [
|
|
2348
|
+
win.padTop > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { "aria-hidden": "true", style: { height: win.padTop } }),
|
|
2349
|
+
rows.map((f) => {
|
|
2350
|
+
const rowStyle = { height: rowHeight, paddingLeft: `calc(${f.level - 1} * var(--uix-space-5))` };
|
|
2351
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2352
|
+
"li",
|
|
2353
|
+
{
|
|
2354
|
+
role: "treeitem",
|
|
2355
|
+
"aria-level": f.level,
|
|
2356
|
+
"aria-setsize": f.setsize,
|
|
2357
|
+
"aria-posinset": f.posinset,
|
|
2358
|
+
"aria-expanded": f.hasChildren ? f.isExpanded : void 0,
|
|
2359
|
+
"aria-selected": selectable ? selected === f.node.id : void 0,
|
|
2360
|
+
"data-id": f.node.id,
|
|
2361
|
+
tabIndex: tabbableId === f.node.id ? 0 : -1,
|
|
2362
|
+
className: "uix-tree__item",
|
|
2363
|
+
onFocus: (e) => {
|
|
2364
|
+
if (e.target === e.currentTarget) setFocusedId(f.node.id);
|
|
2365
|
+
},
|
|
2366
|
+
onClick: (e) => {
|
|
2367
|
+
e.stopPropagation();
|
|
2368
|
+
if (f.hasChildren) onToggle(f.node.id);
|
|
2369
|
+
onSelect?.(f.node.id);
|
|
2370
|
+
setFocusedId(f.node.id);
|
|
2371
|
+
},
|
|
2372
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "uix-tree__row", style: rowStyle, children: [
|
|
2373
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "uix-tree__toggle", children: f.hasChildren && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ChevronIcon2, {}) }),
|
|
2374
|
+
f.node.icon && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { "aria-hidden": "true", children: f.node.icon }),
|
|
2375
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "uix-tree__label", children: f.node.label })
|
|
2376
|
+
] })
|
|
2377
|
+
},
|
|
2378
|
+
f.node.id
|
|
2379
|
+
);
|
|
2380
|
+
}),
|
|
2381
|
+
win.padBottom > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { "aria-hidden": "true", style: { height: win.padBottom } })
|
|
2382
|
+
]
|
|
2383
|
+
}
|
|
2384
|
+
);
|
|
2385
|
+
}
|
|
1833
2386
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1834
2387
|
0 && (module.exports = {
|
|
1835
2388
|
Alert,
|
|
@@ -1918,7 +2471,9 @@ function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected,
|
|
|
1918
2471
|
UserChip,
|
|
1919
2472
|
applyFilters,
|
|
1920
2473
|
clampWidth,
|
|
2474
|
+
computePosition,
|
|
1921
2475
|
cx,
|
|
2476
|
+
flattenTree,
|
|
1922
2477
|
highlightSegments,
|
|
1923
2478
|
matchFilter,
|
|
1924
2479
|
mergePinned,
|
|
@@ -1933,6 +2488,8 @@ function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected,
|
|
|
1933
2488
|
toggleId,
|
|
1934
2489
|
togglePage,
|
|
1935
2490
|
toggleSort,
|
|
2491
|
+
treeNav,
|
|
2492
|
+
useAnchoredPosition,
|
|
1936
2493
|
useDialog,
|
|
1937
2494
|
useTable,
|
|
1938
2495
|
virtualWindow
|