@tensor_1/react 2.9.0 → 2.10.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/chart.cjs +12 -3
- package/dist/components/Alert.js +15 -7
- package/dist/components/AppShell.d.ts +3 -1
- package/dist/components/AppShell.js +3 -1
- package/dist/components/Avatar.js +2 -1
- package/dist/components/Button.js +32 -16
- package/dist/components/Card.d.ts +3 -1
- package/dist/components/Card.js +2 -2
- package/dist/components/Chart.js +12 -3
- package/dist/components/CommandPalette.d.ts +4 -2
- package/dist/components/CommandPalette.js +66 -13
- package/dist/components/Comments.d.ts +5 -1
- package/dist/components/Comments.js +1 -1
- package/dist/components/Composer.d.ts +8 -1
- package/dist/components/Composer.js +7 -2
- package/dist/components/DetailLayout.js +1 -1
- package/dist/components/Drawer.d.ts +3 -4
- package/dist/components/Drawer.js +6 -4
- package/dist/components/Field.js +6 -3
- package/dist/components/Inbox.d.ts +13 -3
- package/dist/components/Inbox.js +61 -3
- package/dist/components/Input.d.ts +13 -2
- package/dist/components/Input.js +20 -5
- package/dist/components/Kanban.d.ts +15 -3
- package/dist/components/Kanban.js +42 -10
- package/dist/components/Meter.d.ts +3 -1
- package/dist/components/Meter.js +9 -1
- package/dist/components/Modal.d.ts +3 -5
- package/dist/components/Modal.js +6 -4
- package/dist/components/NavFavourites.js +11 -0
- package/dist/components/PageHeader.d.ts +3 -1
- package/dist/components/PageHeader.js +2 -2
- package/dist/components/Peek.d.ts +3 -4
- package/dist/components/Peek.js +9 -6
- package/dist/components/Progress.d.ts +3 -1
- package/dist/components/Progress.js +5 -3
- package/dist/components/Radio.d.ts +3 -1
- package/dist/components/Radio.js +6 -2
- package/dist/components/Sidebar.js +4 -2
- package/dist/components/Spinner.d.ts +3 -1
- package/dist/components/Spinner.js +3 -3
- package/dist/components/Stat.js +5 -1
- package/dist/components/States.d.ts +3 -1
- package/dist/components/States.js +8 -2
- package/dist/components/Table.d.ts +9 -2
- package/dist/components/Table.js +10 -6
- package/dist/components/Tabs.d.ts +13 -1
- package/dist/components/Tabs.js +78 -3
- package/dist/components/Timeline.d.ts +3 -3
- package/dist/components/Timeline.js +3 -3
- package/dist/components/Toast.js +42 -5
- package/dist/components/Tooltip.d.ts +3 -1
- package/dist/components/Tooltip.js +44 -5
- package/dist/components/Tree.d.ts +2 -0
- package/dist/components/Tree.js +26 -4
- package/dist/hooks/useDialog.d.ts +1 -1
- package/dist/hooks/useDialog.js +17 -2
- package/dist/hooks/useTable.d.ts +5 -0
- package/dist/hooks/useTable.js +20 -1
- package/dist/index.cjs +616 -162
- package/dist/index.d.cts +127 -37
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -1
- package/dist/tree-model.d.ts +4 -0
- package/dist/tree-model.js +8 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -103,6 +103,7 @@ __export(src_exports, {
|
|
|
103
103
|
SubNavItem: () => SubNavItem,
|
|
104
104
|
Switch: () => Switch,
|
|
105
105
|
Tab: () => Tab,
|
|
106
|
+
TabPanel: () => TabPanel,
|
|
106
107
|
Table: () => Table,
|
|
107
108
|
TableWrap: () => TableWrap,
|
|
108
109
|
Tabs: () => Tabs,
|
|
@@ -181,8 +182,10 @@ function unlockBodyScroll() {
|
|
|
181
182
|
saved = null;
|
|
182
183
|
}
|
|
183
184
|
}
|
|
184
|
-
function useDialog(open) {
|
|
185
|
+
function useDialog(open, onClose) {
|
|
185
186
|
const ref = (0, import_react.useRef)(null);
|
|
187
|
+
const onCloseRef = (0, import_react.useRef)(onClose);
|
|
188
|
+
onCloseRef.current = onClose;
|
|
186
189
|
(0, import_react.useEffect)(() => {
|
|
187
190
|
const el = ref.current;
|
|
188
191
|
if (!el || !open) {
|
|
@@ -191,8 +194,21 @@ function useDialog(open) {
|
|
|
191
194
|
}
|
|
192
195
|
if (!el.open) el.showModal();
|
|
193
196
|
lockBodyScroll();
|
|
197
|
+
let locked = true;
|
|
198
|
+
const handleClose = () => {
|
|
199
|
+
if (locked) {
|
|
200
|
+
locked = false;
|
|
201
|
+
unlockBodyScroll();
|
|
202
|
+
}
|
|
203
|
+
onCloseRef.current?.();
|
|
204
|
+
};
|
|
205
|
+
el.addEventListener("close", handleClose);
|
|
194
206
|
return () => {
|
|
195
|
-
|
|
207
|
+
el.removeEventListener("close", handleClose);
|
|
208
|
+
if (locked) {
|
|
209
|
+
locked = false;
|
|
210
|
+
unlockBodyScroll();
|
|
211
|
+
}
|
|
196
212
|
if (el.open) el.close();
|
|
197
213
|
};
|
|
198
214
|
}, [open]);
|
|
@@ -459,6 +475,24 @@ function useTable(data, options = {}) {
|
|
|
459
475
|
const toggleAllRows = (0, import_react2.useCallback)(() => setSelected((s) => togglePage(s, pageIds)), [pageIds]);
|
|
460
476
|
const clearSelection = (0, import_react2.useCallback)(() => setSelected(/* @__PURE__ */ new Set()), []);
|
|
461
477
|
const togglePin = (0, import_react2.useCallback)((id) => setPinned((s) => toggleId(s, id)), []);
|
|
478
|
+
const [announcement, setAnnouncement] = (0, import_react2.useState)("");
|
|
479
|
+
const mounted = (0, import_react2.useRef)(false);
|
|
480
|
+
(0, import_react2.useEffect)(() => {
|
|
481
|
+
if (!mounted.current) return;
|
|
482
|
+
const key = sort[0];
|
|
483
|
+
setAnnouncement(key ? `Sorted by ${key.field}, ${key.dir}` : "Sort cleared");
|
|
484
|
+
}, [sort]);
|
|
485
|
+
(0, import_react2.useEffect)(() => {
|
|
486
|
+
if (!mounted.current) return;
|
|
487
|
+
setAnnouncement(`${rows.length} result${rows.length === 1 ? "" : "s"}`);
|
|
488
|
+
}, [filters, query]);
|
|
489
|
+
(0, import_react2.useEffect)(() => {
|
|
490
|
+
if (!mounted.current) return;
|
|
491
|
+
setAnnouncement(`${selected.size} selected`);
|
|
492
|
+
}, [selected]);
|
|
493
|
+
(0, import_react2.useEffect)(() => {
|
|
494
|
+
mounted.current = true;
|
|
495
|
+
}, []);
|
|
462
496
|
const view = (0, import_react2.useMemo)(() => ({ sort, filters, q: query }), [sort, filters, query]);
|
|
463
497
|
const viewQueryString = (0, import_react2.useMemo)(() => serializeView(view), [view]);
|
|
464
498
|
const applyView = (0, import_react2.useCallback)((queryString) => {
|
|
@@ -475,6 +509,7 @@ function useTable(data, options = {}) {
|
|
|
475
509
|
selected,
|
|
476
510
|
pinned,
|
|
477
511
|
selectAll,
|
|
512
|
+
announcement,
|
|
478
513
|
toggleSort: doToggleSort,
|
|
479
514
|
setSort,
|
|
480
515
|
setFilters,
|
|
@@ -597,24 +632,40 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
|
|
|
597
632
|
// src/components/Button.tsx
|
|
598
633
|
var import_react4 = require("react");
|
|
599
634
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
635
|
+
var warnedIconButton = false;
|
|
600
636
|
var Button = (0, import_react4.forwardRef)(
|
|
601
|
-
({ variant = "secondary", size = "md", icon = false, loading = false, className, children, disabled, ...props }, ref) =>
|
|
602
|
-
"
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
className: cx(
|
|
606
|
-
"uix-btn",
|
|
607
|
-
`uix-btn--${variant}`,
|
|
608
|
-
size !== "md" && `uix-btn--${size}`,
|
|
609
|
-
icon && "uix-btn--icon",
|
|
610
|
-
className
|
|
611
|
-
),
|
|
612
|
-
disabled: disabled || loading,
|
|
613
|
-
"data-loading": loading || void 0,
|
|
614
|
-
...props,
|
|
615
|
-
children
|
|
637
|
+
({ variant = "secondary", size = "md", icon = false, loading = false, className, children, disabled, onClick, ...props }, ref) => {
|
|
638
|
+
if (!warnedIconButton && icon && children == null && !props["aria-label"] && !props["aria-labelledby"] && typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
|
|
639
|
+
warnedIconButton = true;
|
|
640
|
+
console.warn("uix: icon-only <Button> has no accessible name \u2014 pass aria-label or aria-labelledby.");
|
|
616
641
|
}
|
|
617
|
-
|
|
642
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
643
|
+
"button",
|
|
644
|
+
{
|
|
645
|
+
ref,
|
|
646
|
+
className: cx(
|
|
647
|
+
"uix-btn",
|
|
648
|
+
`uix-btn--${variant}`,
|
|
649
|
+
size !== "md" && `uix-btn--${size}`,
|
|
650
|
+
icon && "uix-btn--icon",
|
|
651
|
+
className
|
|
652
|
+
),
|
|
653
|
+
disabled,
|
|
654
|
+
"aria-disabled": loading || void 0,
|
|
655
|
+
"aria-busy": loading || void 0,
|
|
656
|
+
"data-loading": loading || void 0,
|
|
657
|
+
onClick: loading || onClick ? (e) => {
|
|
658
|
+
if (loading) {
|
|
659
|
+
e.preventDefault();
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
onClick?.(e);
|
|
663
|
+
} : void 0,
|
|
664
|
+
...props,
|
|
665
|
+
children
|
|
666
|
+
}
|
|
667
|
+
);
|
|
668
|
+
}
|
|
618
669
|
);
|
|
619
670
|
Button.displayName = "Button";
|
|
620
671
|
function ButtonGroup({ children, className }) {
|
|
@@ -637,7 +688,22 @@ var Input = (0, import_react5.forwardRef)(
|
|
|
637
688
|
)
|
|
638
689
|
);
|
|
639
690
|
Input.displayName = "Input";
|
|
640
|
-
function InputGroup({
|
|
691
|
+
function InputGroup({
|
|
692
|
+
children,
|
|
693
|
+
leadingIcon,
|
|
694
|
+
trailingIcon,
|
|
695
|
+
className,
|
|
696
|
+
id,
|
|
697
|
+
"aria-describedby": describedBy,
|
|
698
|
+
"aria-invalid": invalid,
|
|
699
|
+
"aria-required": required
|
|
700
|
+
}) {
|
|
701
|
+
const control = (0, import_react5.isValidElement)(children) && (id ?? describedBy ?? invalid ?? required) != null ? (0, import_react5.cloneElement)(children, {
|
|
702
|
+
id: children.props.id ?? id,
|
|
703
|
+
"aria-describedby": children.props["aria-describedby"] ?? describedBy,
|
|
704
|
+
"aria-invalid": children.props["aria-invalid"] ?? invalid,
|
|
705
|
+
"aria-required": children.props["aria-required"] ?? required
|
|
706
|
+
}) : children;
|
|
641
707
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
642
708
|
"div",
|
|
643
709
|
{
|
|
@@ -648,9 +714,9 @@ function InputGroup({ children, leadingIcon, trailingIcon, className }) {
|
|
|
648
714
|
className
|
|
649
715
|
),
|
|
650
716
|
children: [
|
|
651
|
-
leadingIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "uix-input-group__icon", children: leadingIcon }),
|
|
652
|
-
|
|
653
|
-
trailingIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "uix-input-group__icon uix-input-group__icon--trailing", children: trailingIcon })
|
|
717
|
+
leadingIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "uix-input-group__icon", "aria-hidden": "true", children: leadingIcon }),
|
|
718
|
+
control,
|
|
719
|
+
trailingIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "uix-input-group__icon uix-input-group__icon--trailing", "aria-hidden": "true", children: trailingIcon })
|
|
654
720
|
]
|
|
655
721
|
}
|
|
656
722
|
);
|
|
@@ -730,8 +796,12 @@ var Radio = (0, import_react9.forwardRef)(
|
|
|
730
796
|
] })
|
|
731
797
|
);
|
|
732
798
|
Radio.displayName = "Radio";
|
|
733
|
-
function RadioGroup({ children, className }) {
|
|
734
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cx("uix-radio-group", className), children });
|
|
799
|
+
function RadioGroup({ children, className, label }) {
|
|
800
|
+
if (label == null) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cx("uix-radio-group", className), children });
|
|
801
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("fieldset", { className: cx("uix-radio-group", className), children: [
|
|
802
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { className: "uix-radio-group__legend", children: label }),
|
|
803
|
+
children
|
|
804
|
+
] });
|
|
735
805
|
}
|
|
736
806
|
|
|
737
807
|
// src/components/Switch.tsx
|
|
@@ -753,25 +823,30 @@ function Field({ label, hint, error, success, required, children, className, htm
|
|
|
753
823
|
const uid = (0, import_react11.useId)();
|
|
754
824
|
const msgId = `${uid}-msg`;
|
|
755
825
|
const describedBy = error || success || hint ? msgId : void 0;
|
|
826
|
+
const controlId = (0, import_react11.isValidElement)(children) ? children.props.id ?? `${uid}-ctl` : void 0;
|
|
756
827
|
const control = (0, import_react11.isValidElement)(children) ? (0, import_react11.cloneElement)(children, {
|
|
828
|
+
id: controlId,
|
|
757
829
|
"aria-describedby": [children.props["aria-describedby"], describedBy].filter(Boolean).join(" ") || void 0,
|
|
758
|
-
"aria-invalid": error ? true : children.props["aria-invalid"]
|
|
830
|
+
"aria-invalid": error ? true : children.props["aria-invalid"],
|
|
831
|
+
"aria-required": children.props["aria-required"] ?? (required || void 0)
|
|
759
832
|
}) : children;
|
|
760
833
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: cx("uix-field", className), children: [
|
|
761
|
-
label && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { className: "uix-field__label", htmlFor, "data-required": required || void 0, children: label }),
|
|
834
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { className: "uix-field__label", htmlFor: htmlFor ?? controlId, "data-required": required || void 0, children: label }),
|
|
762
835
|
control,
|
|
763
|
-
/* @__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 })
|
|
836
|
+
/* @__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", role: "status", children: success }) : hint ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-field__hint", children: hint }) : null })
|
|
764
837
|
] });
|
|
765
838
|
}
|
|
766
839
|
|
|
767
840
|
// src/components/Modal.tsx
|
|
841
|
+
var import_react12 = require("react");
|
|
768
842
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
769
843
|
var CloseIcon = () => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M4 4l8 8M12 4l-8 8" }) });
|
|
770
|
-
function Modal({ open, onClose, title, children, footer, className,
|
|
771
|
-
const ref = useDialog(open);
|
|
772
|
-
|
|
844
|
+
function Modal({ open, onClose, title, children, footer, className, ...rest }) {
|
|
845
|
+
const ref = useDialog(open, onClose);
|
|
846
|
+
const titleId = (0, import_react12.useId)();
|
|
847
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("dialog", { ref, className: cx("uix-dialog", className), "aria-labelledby": title ? titleId : void 0, ...rest, children: [
|
|
773
848
|
(title != null || onClose) && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "uix-dialog__header", children: [
|
|
774
|
-
title && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("
|
|
849
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { className: "uix-dialog__title", id: titleId, children: title }),
|
|
775
850
|
onClose && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { className: "uix-dialog__close", onClick: onClose, "aria-label": "Close dialog", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CloseIcon, {}) })
|
|
776
851
|
] }),
|
|
777
852
|
children != null && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "uix-dialog__body", children }),
|
|
@@ -780,13 +855,15 @@ function Modal({ open, onClose, title, children, footer, className, style }) {
|
|
|
780
855
|
}
|
|
781
856
|
|
|
782
857
|
// src/components/Drawer.tsx
|
|
858
|
+
var import_react13 = require("react");
|
|
783
859
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
784
860
|
var CloseIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M4 4l8 8M12 4l-8 8" }) });
|
|
785
|
-
function Drawer({ open, onClose, title, children, footer, className }) {
|
|
786
|
-
const ref = useDialog(open);
|
|
787
|
-
|
|
861
|
+
function Drawer({ open, onClose, title, children, footer, className, ...rest }) {
|
|
862
|
+
const ref = useDialog(open, onClose);
|
|
863
|
+
const titleId = (0, import_react13.useId)();
|
|
864
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("dialog", { ref, className: cx("uix-drawer", className), "aria-labelledby": title ? titleId : void 0, ...rest, children: [
|
|
788
865
|
(title != null || onClose) && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "uix-drawer__header", children: [
|
|
789
|
-
title && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("
|
|
866
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { id: titleId, style: { fontWeight: 600, fontSize: "var(--uix-text-h3)", fontFamily: "inherit", letterSpacing: "inherit", lineHeight: "inherit" }, children: title }),
|
|
790
867
|
onClose && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
791
868
|
"button",
|
|
792
869
|
{
|
|
@@ -803,27 +880,30 @@ function Drawer({ open, onClose, title, children, footer, className }) {
|
|
|
803
880
|
}
|
|
804
881
|
|
|
805
882
|
// src/components/Peek.tsx
|
|
883
|
+
var import_react14 = require("react");
|
|
806
884
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
807
885
|
var CloseIcon3 = () => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M4 4l8 8M12 4l-8 8" }) });
|
|
808
886
|
var ChevronUp = () => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M2 8l4-4 4 4" }) });
|
|
809
887
|
var ChevronDown = () => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M2 4l4 4 4-4" }) });
|
|
810
|
-
function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, className }) {
|
|
811
|
-
const ref = useDialog(open);
|
|
888
|
+
function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, className, onClick, ...rest }) {
|
|
889
|
+
const ref = useDialog(open, onClose);
|
|
812
890
|
const hasNav = onNavPrev != null || onNavNext != null;
|
|
891
|
+
const titleId = (0, import_react14.useId)();
|
|
813
892
|
const onBackdropClick = (e) => {
|
|
893
|
+
onClick?.(e);
|
|
814
894
|
if (!onClose || e.target !== e.currentTarget) return;
|
|
815
895
|
const r = e.currentTarget.getBoundingClientRect();
|
|
816
896
|
if (r.width === 0) return;
|
|
817
897
|
const outside = e.clientX < r.left || e.clientX > r.right || e.clientY < r.top || e.clientY > r.bottom;
|
|
818
898
|
if (outside) onClose();
|
|
819
899
|
};
|
|
820
|
-
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("dialog", { ref, className: cx("uix-peek", className),
|
|
900
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("dialog", { ref, className: cx("uix-peek", className), "aria-labelledby": title ? titleId : void 0, ...rest, onClick: onBackdropClick, children: [
|
|
821
901
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "uix-peek__header", children: [
|
|
822
902
|
hasNav && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "uix-peek__nav", children: [
|
|
823
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { className: "uix-peek__navbtn", onClick: onNavPrev, disabled:
|
|
824
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { className: "uix-peek__navbtn", onClick: onNavNext, disabled:
|
|
903
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { className: "uix-peek__navbtn", onClick: () => onNavPrev?.(), "aria-disabled": onNavPrev ? void 0 : true, "aria-label": "Previous record", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChevronUp, {}) }),
|
|
904
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { className: "uix-peek__navbtn", onClick: () => onNavNext?.(), "aria-disabled": onNavNext ? void 0 : true, "aria-label": "Next record", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChevronDown, {}) })
|
|
825
905
|
] }),
|
|
826
|
-
title && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("
|
|
906
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h2", { className: "uix-peek__title", id: titleId, children: title }),
|
|
827
907
|
hint && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "uix-peek__hint", children: hint }),
|
|
828
908
|
onClose && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { className: "uix-peek__close", onClick: onClose, "aria-label": "Close preview", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CloseIcon3, {}) })
|
|
829
909
|
] }),
|
|
@@ -834,12 +914,12 @@ function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hi
|
|
|
834
914
|
|
|
835
915
|
// src/components/Card.tsx
|
|
836
916
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
837
|
-
function Card({ title, subtitle, headerAction, footer, children, className, ...props }) {
|
|
917
|
+
function Card({ title, titleAs: TitleTag = "div", subtitle, headerAction, footer, children, className, ...props }) {
|
|
838
918
|
const hasHeader = title != null || subtitle != null || headerAction != null;
|
|
839
919
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: cx("uix-card", className), ...props, children: [
|
|
840
920
|
hasHeader && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "uix-card__header", children: [
|
|
841
921
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { flex: 1 }, children: [
|
|
842
|
-
title && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
922
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TitleTag, { className: "uix-card__title", children: title }),
|
|
843
923
|
subtitle && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "uix-card__subtitle", children: subtitle })
|
|
844
924
|
] }),
|
|
845
925
|
headerAction
|
|
@@ -851,11 +931,11 @@ function Card({ title, subtitle, headerAction, footer, children, className, ...p
|
|
|
851
931
|
|
|
852
932
|
// src/components/PageHeader.tsx
|
|
853
933
|
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
854
|
-
function PageHeader({ eyebrow, title, subtitle, actions, className, ...props }) {
|
|
934
|
+
function PageHeader({ eyebrow, title, subtitle, actions, as: TitleTag = "h1", className, ...props }) {
|
|
855
935
|
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("header", { className: cx("uix-page-header", className), ...props, children: [
|
|
856
936
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "uix-page-header__titles", children: [
|
|
857
937
|
eyebrow != null && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "uix-page-header__eyebrow", children: eyebrow }),
|
|
858
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
938
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TitleTag, { className: "uix-page-header__title", children: title }),
|
|
859
939
|
subtitle != null && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "uix-page-header__subtitle", children: subtitle })
|
|
860
940
|
] }),
|
|
861
941
|
actions != null && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "uix-page-header__actions", children: actions })
|
|
@@ -867,7 +947,7 @@ var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
|
867
947
|
function DetailLayout({ side, children, className, ...props }) {
|
|
868
948
|
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: cx("uix-detail", className), ...props, children: [
|
|
869
949
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "uix-detail__main", children }),
|
|
870
|
-
side != null && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("
|
|
950
|
+
side != null && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("aside", { className: "uix-detail__side", children: side })
|
|
871
951
|
] });
|
|
872
952
|
}
|
|
873
953
|
|
|
@@ -890,11 +970,11 @@ function ListItem({ title, meta, trail, leading, children, className, ...props }
|
|
|
890
970
|
}
|
|
891
971
|
|
|
892
972
|
// src/components/DescriptionList.tsx
|
|
893
|
-
var
|
|
973
|
+
var import_react15 = require("react");
|
|
894
974
|
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
895
975
|
function DescriptionList({ items, children, className, ...props }) {
|
|
896
976
|
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("dl", { className: cx("uix-dl", className), ...props, children: [
|
|
897
|
-
items?.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
977
|
+
items?.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react15.Fragment, { children: [
|
|
898
978
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dt", { children: item.term }),
|
|
899
979
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dd", { children: item.description })
|
|
900
980
|
] }, item.id ?? i)),
|
|
@@ -909,21 +989,80 @@ function DescriptionItem({ term, children }) {
|
|
|
909
989
|
}
|
|
910
990
|
|
|
911
991
|
// src/components/Tabs.tsx
|
|
912
|
-
var
|
|
992
|
+
var import_react16 = require("react");
|
|
913
993
|
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
914
|
-
var TabsCtx = (0,
|
|
994
|
+
var TabsCtx = (0, import_react16.createContext)({});
|
|
915
995
|
function Tabs({ variant = "line", value, onChange, children, className }) {
|
|
916
|
-
|
|
996
|
+
const baseId = (0, import_react16.useId)();
|
|
997
|
+
const listRef = (0, import_react16.useRef)(null);
|
|
998
|
+
const kids = import_react16.Children.toArray(children);
|
|
999
|
+
const panels = kids.filter((k) => (0, import_react16.isValidElement)(k) && k.type === TabPanel);
|
|
1000
|
+
const tabs = kids.filter((k) => !((0, import_react16.isValidElement)(k) && k.type === TabPanel));
|
|
1001
|
+
(0, import_react16.useEffect)(() => {
|
|
1002
|
+
const list = listRef.current;
|
|
1003
|
+
if (!list) return;
|
|
1004
|
+
const all = Array.from(list.querySelectorAll('[role="tab"]'));
|
|
1005
|
+
const enabled = all.filter((t) => !t.matches(":disabled"));
|
|
1006
|
+
const active = enabled.find((t) => t.getAttribute("aria-selected") === "true") ?? enabled[0];
|
|
1007
|
+
for (const t of all) t.tabIndex = t === active ? 0 : -1;
|
|
1008
|
+
});
|
|
1009
|
+
const onKeyDown = (e) => {
|
|
1010
|
+
const items = Array.from(
|
|
1011
|
+
listRef.current?.querySelectorAll('[role="tab"]:not(:disabled)') ?? []
|
|
1012
|
+
);
|
|
1013
|
+
const cur = e.target.closest('[role="tab"]');
|
|
1014
|
+
const idx = cur ? items.indexOf(cur) : -1;
|
|
1015
|
+
if (idx < 0 || items.length === 0) return;
|
|
1016
|
+
let next;
|
|
1017
|
+
switch (e.key) {
|
|
1018
|
+
case "ArrowRight":
|
|
1019
|
+
next = (idx + 1) % items.length;
|
|
1020
|
+
break;
|
|
1021
|
+
case "ArrowLeft":
|
|
1022
|
+
next = (idx - 1 + items.length) % items.length;
|
|
1023
|
+
break;
|
|
1024
|
+
case "Home":
|
|
1025
|
+
next = 0;
|
|
1026
|
+
break;
|
|
1027
|
+
case "End":
|
|
1028
|
+
next = items.length - 1;
|
|
1029
|
+
break;
|
|
1030
|
+
default:
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
e.preventDefault();
|
|
1034
|
+
const target = items[next];
|
|
1035
|
+
if (target) {
|
|
1036
|
+
target.focus();
|
|
1037
|
+
target.click();
|
|
1038
|
+
}
|
|
1039
|
+
};
|
|
1040
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(TabsCtx.Provider, { value: { value, onChange, baseId }, children: [
|
|
1041
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1042
|
+
"div",
|
|
1043
|
+
{
|
|
1044
|
+
ref: listRef,
|
|
1045
|
+
role: "tablist",
|
|
1046
|
+
className: cx("uix-tabs", `uix-tabs--${variant}`, className),
|
|
1047
|
+
onKeyDown,
|
|
1048
|
+
children: tabs
|
|
1049
|
+
}
|
|
1050
|
+
),
|
|
1051
|
+
panels
|
|
1052
|
+
] });
|
|
917
1053
|
}
|
|
918
1054
|
function Tab({ value, children, disabled, className, onClick, ...props }) {
|
|
919
|
-
const ctx = (0,
|
|
1055
|
+
const ctx = (0, import_react16.useContext)(TabsCtx);
|
|
920
1056
|
const selected = ctx.value === value;
|
|
921
1057
|
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
922
1058
|
"button",
|
|
923
1059
|
{
|
|
924
1060
|
role: "tab",
|
|
1061
|
+
id: ctx.baseId ? `${ctx.baseId}-tab-${value}` : void 0,
|
|
1062
|
+
"aria-controls": ctx.baseId ? `${ctx.baseId}-panel-${value}` : void 0,
|
|
925
1063
|
className: cx("uix-tab", className),
|
|
926
1064
|
"aria-selected": selected,
|
|
1065
|
+
tabIndex: selected ? 0 : -1,
|
|
927
1066
|
disabled,
|
|
928
1067
|
onClick: (e) => {
|
|
929
1068
|
ctx.onChange?.(value);
|
|
@@ -934,9 +1073,24 @@ function Tab({ value, children, disabled, className, onClick, ...props }) {
|
|
|
934
1073
|
}
|
|
935
1074
|
);
|
|
936
1075
|
}
|
|
1076
|
+
function TabPanel({ value, children, ...props }) {
|
|
1077
|
+
const ctx = (0, import_react16.useContext)(TabsCtx);
|
|
1078
|
+
if (ctx.value !== value) return null;
|
|
1079
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1080
|
+
"div",
|
|
1081
|
+
{
|
|
1082
|
+
role: "tabpanel",
|
|
1083
|
+
id: ctx.baseId ? `${ctx.baseId}-panel-${value}` : void 0,
|
|
1084
|
+
"aria-labelledby": ctx.baseId ? `${ctx.baseId}-tab-${value}` : void 0,
|
|
1085
|
+
tabIndex: 0,
|
|
1086
|
+
...props,
|
|
1087
|
+
children
|
|
1088
|
+
}
|
|
1089
|
+
);
|
|
1090
|
+
}
|
|
937
1091
|
|
|
938
1092
|
// src/components/AppShell.tsx
|
|
939
|
-
var
|
|
1093
|
+
var import_react17 = require("react");
|
|
940
1094
|
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
941
1095
|
function AppShell({
|
|
942
1096
|
nav,
|
|
@@ -944,6 +1098,7 @@ function AppShell({
|
|
|
944
1098
|
focus,
|
|
945
1099
|
onExitFocus,
|
|
946
1100
|
mainBleed,
|
|
1101
|
+
mainId = "uix-main",
|
|
947
1102
|
sidebar,
|
|
948
1103
|
topbar,
|
|
949
1104
|
children,
|
|
@@ -951,7 +1106,7 @@ function AppShell({
|
|
|
951
1106
|
...props
|
|
952
1107
|
}) {
|
|
953
1108
|
const navState = nav ?? (collapsed ? "rail" : "full");
|
|
954
|
-
(0,
|
|
1109
|
+
(0, import_react17.useEffect)(() => {
|
|
955
1110
|
if (!focus || !onExitFocus) return;
|
|
956
1111
|
const onKey = (e) => {
|
|
957
1112
|
if (e.key === "Escape") onExitFocus();
|
|
@@ -968,9 +1123,10 @@ function AppShell({
|
|
|
968
1123
|
"data-focus": focus || void 0,
|
|
969
1124
|
...props,
|
|
970
1125
|
children: [
|
|
1126
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("a", { className: "uix-skiplink", href: `#${mainId}`, children: "Skip to content" }),
|
|
971
1127
|
sidebar && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "uix-shell__sidebar", children: sidebar }),
|
|
972
1128
|
topbar && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "uix-shell__topbar", children: topbar }),
|
|
973
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("main", { className: cx("uix-shell__main", mainBleed && "uix-shell__main--bleed"), children }),
|
|
1129
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("main", { id: mainId, tabIndex: -1, className: cx("uix-shell__main", mainBleed && "uix-shell__main--bleed"), children }),
|
|
974
1130
|
focus && onExitFocus && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "uix-shell__focus-exit", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", onClick: onExitFocus, children: "Esc \xB7 Exit focus" }) })
|
|
975
1131
|
]
|
|
976
1132
|
}
|
|
@@ -978,7 +1134,7 @@ function AppShell({
|
|
|
978
1134
|
}
|
|
979
1135
|
|
|
980
1136
|
// src/components/Sidebar.tsx
|
|
981
|
-
var
|
|
1137
|
+
var import_react18 = require("react");
|
|
982
1138
|
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
983
1139
|
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" }) });
|
|
984
1140
|
function Sidebar({ collapsed, onToggle, brand, toggleIcon, children, className, ...props }) {
|
|
@@ -1032,13 +1188,15 @@ function NavItem({ icon, badge, active, as: Tag = "a", children, className, ...p
|
|
|
1032
1188
|
);
|
|
1033
1189
|
}
|
|
1034
1190
|
function NavGroup({ icon, label, children, defaultExpanded = true }) {
|
|
1035
|
-
const [expanded, setExpanded] = (0,
|
|
1191
|
+
const [expanded, setExpanded] = (0, import_react18.useState)(defaultExpanded);
|
|
1192
|
+
const panelId = (0, import_react18.useId)();
|
|
1036
1193
|
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "uix-navgroup", children: [
|
|
1037
1194
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1038
1195
|
"button",
|
|
1039
1196
|
{
|
|
1040
1197
|
className: "uix-navitem uix-navgroup__trigger",
|
|
1041
1198
|
"aria-expanded": expanded,
|
|
1199
|
+
"aria-controls": panelId,
|
|
1042
1200
|
onClick: () => setExpanded((v) => !v),
|
|
1043
1201
|
children: [
|
|
1044
1202
|
icon && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "uix-navitem__icon", "aria-hidden": "true", children: icon }),
|
|
@@ -1047,7 +1205,7 @@ function NavGroup({ icon, label, children, defaultExpanded = true }) {
|
|
|
1047
1205
|
]
|
|
1048
1206
|
}
|
|
1049
1207
|
),
|
|
1050
|
-
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "uix-navgroup__panel", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { children }) })
|
|
1208
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "uix-navgroup__panel", id: panelId, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { children }) })
|
|
1051
1209
|
] });
|
|
1052
1210
|
}
|
|
1053
1211
|
function SubNavItem({ active, as: Tag = "a", children, className, ...props }) {
|
|
@@ -1100,7 +1258,7 @@ function StarButton({
|
|
|
1100
1258
|
}
|
|
1101
1259
|
|
|
1102
1260
|
// src/components/NavFavourites.tsx
|
|
1103
|
-
var
|
|
1261
|
+
var import_react19 = require("react");
|
|
1104
1262
|
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1105
1263
|
var ChevronDownIcon = () => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1106
1264
|
"svg",
|
|
@@ -1164,13 +1322,14 @@ function NavFavourites({
|
|
|
1164
1322
|
emptyReason,
|
|
1165
1323
|
className
|
|
1166
1324
|
}) {
|
|
1167
|
-
const regionId = (0,
|
|
1168
|
-
const headerRef = (0,
|
|
1169
|
-
const regionRef = (0,
|
|
1170
|
-
const
|
|
1171
|
-
const
|
|
1325
|
+
const regionId = (0, import_react19.useId)();
|
|
1326
|
+
const headerRef = (0, import_react19.useRef)(null);
|
|
1327
|
+
const regionRef = (0, import_react19.useRef)(null);
|
|
1328
|
+
const [announcement, setAnnouncement] = (0, import_react19.useState)("");
|
|
1329
|
+
const focusAfterRef = (0, import_react19.useRef)(null);
|
|
1330
|
+
const triggerRefs = (0, import_react19.useRef)(/* @__PURE__ */ new Map());
|
|
1172
1331
|
const order = items.map((i) => i.id);
|
|
1173
|
-
(0,
|
|
1332
|
+
(0, import_react19.useEffect)(() => {
|
|
1174
1333
|
const id = focusAfterRef.current;
|
|
1175
1334
|
if (!id) return;
|
|
1176
1335
|
focusAfterRef.current = null;
|
|
@@ -1196,6 +1355,8 @@ function NavFavourites({
|
|
|
1196
1355
|
if (it === void 0) return;
|
|
1197
1356
|
reordered.splice(next, 0, it);
|
|
1198
1357
|
focusAfterRef.current = id;
|
|
1358
|
+
const moved = items.find((i) => i.id === id);
|
|
1359
|
+
if (moved) setAnnouncement(`${moved.label} moved to position ${next + 1} of ${order.length}`);
|
|
1199
1360
|
onReorder(reordered);
|
|
1200
1361
|
};
|
|
1201
1362
|
const remove = (id) => {
|
|
@@ -1205,6 +1366,7 @@ function NavFavourites({
|
|
|
1205
1366
|
onRemove(id);
|
|
1206
1367
|
};
|
|
1207
1368
|
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: cx("uix-favourites", className), style: { marginBottom: "var(--uix-space-1)" }, children: [
|
|
1369
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { role: "status", className: "uix-visually-hidden", children: announcement }),
|
|
1208
1370
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
1209
1371
|
"button",
|
|
1210
1372
|
{
|
|
@@ -1281,18 +1443,18 @@ function FavouriteRow({
|
|
|
1281
1443
|
onRemove,
|
|
1282
1444
|
registerTrigger
|
|
1283
1445
|
}) {
|
|
1284
|
-
const [open, setOpen] = (0,
|
|
1285
|
-
const [revealed, setRevealed] = (0,
|
|
1286
|
-
const menuId = (0,
|
|
1287
|
-
const triggerRef = (0,
|
|
1288
|
-
const itemRefs = (0,
|
|
1446
|
+
const [open, setOpen] = (0, import_react19.useState)(false);
|
|
1447
|
+
const [revealed, setRevealed] = (0, import_react19.useState)(false);
|
|
1448
|
+
const menuId = (0, import_react19.useId)();
|
|
1449
|
+
const triggerRef = (0, import_react19.useRef)(null);
|
|
1450
|
+
const itemRefs = (0, import_react19.useRef)([]);
|
|
1289
1451
|
const optionsTemplate = labels.options ?? "{label} favourite options";
|
|
1290
1452
|
const actions = [
|
|
1291
1453
|
...isFirst ? [] : [{ label: labels.moveUp, onSelect: onMoveUp }],
|
|
1292
1454
|
...isLast ? [] : [{ label: labels.moveDown, onSelect: onMoveDown }],
|
|
1293
1455
|
{ label: labels.remove.replace("{label}", item.label), onSelect: onRemove }
|
|
1294
1456
|
];
|
|
1295
|
-
(0,
|
|
1457
|
+
(0, import_react19.useEffect)(() => {
|
|
1296
1458
|
if (open) itemRefs.current[0]?.focus();
|
|
1297
1459
|
}, [open]);
|
|
1298
1460
|
const closeAndReturnFocus = () => {
|
|
@@ -1308,6 +1470,12 @@ function FavouriteRow({
|
|
|
1308
1470
|
} else if (e.key === "ArrowUp") {
|
|
1309
1471
|
e.preventDefault();
|
|
1310
1472
|
menuItems[(currentIdx - 1 + menuItems.length) % menuItems.length]?.focus();
|
|
1473
|
+
} else if (e.key === "Home") {
|
|
1474
|
+
e.preventDefault();
|
|
1475
|
+
menuItems[0]?.focus();
|
|
1476
|
+
} else if (e.key === "End") {
|
|
1477
|
+
e.preventDefault();
|
|
1478
|
+
menuItems[menuItems.length - 1]?.focus();
|
|
1311
1479
|
} else if (e.key === "Escape") {
|
|
1312
1480
|
e.preventDefault();
|
|
1313
1481
|
closeAndReturnFocus();
|
|
@@ -1388,6 +1556,7 @@ function FavouriteRow({
|
|
|
1388
1556
|
{
|
|
1389
1557
|
id: menuId,
|
|
1390
1558
|
role: "menu",
|
|
1559
|
+
"aria-label": optionsTemplate.replace("{label}", item.label),
|
|
1391
1560
|
onKeyDown: onMenuKeyDown,
|
|
1392
1561
|
style: {
|
|
1393
1562
|
position: "absolute",
|
|
@@ -1451,60 +1620,107 @@ function FavouriteRow({
|
|
|
1451
1620
|
// src/components/Alert.tsx
|
|
1452
1621
|
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
1453
1622
|
function Alert({ tone, title, icon, children, className, ...props }) {
|
|
1454
|
-
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1623
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
1624
|
+
"div",
|
|
1625
|
+
{
|
|
1626
|
+
className: cx("uix-alert", tone && tone !== "neutral" && `uix-alert--${tone}`, className),
|
|
1627
|
+
role: tone === "danger" || tone === "warning" ? "alert" : "status",
|
|
1628
|
+
...props,
|
|
1629
|
+
children: [
|
|
1630
|
+
icon && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "uix-alert__icon", children: icon }),
|
|
1631
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { children: [
|
|
1632
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "uix-alert__title", children: title }),
|
|
1633
|
+
children && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "uix-alert__body", children })
|
|
1634
|
+
] })
|
|
1635
|
+
]
|
|
1636
|
+
}
|
|
1637
|
+
);
|
|
1461
1638
|
}
|
|
1462
1639
|
|
|
1463
1640
|
// src/components/Spinner.tsx
|
|
1464
1641
|
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1465
|
-
function Spinner({ size = "md", accent, className, ...props }) {
|
|
1642
|
+
function Spinner({ size = "md", accent, label, className, ...props }) {
|
|
1466
1643
|
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1467
1644
|
"span",
|
|
1468
1645
|
{
|
|
1469
1646
|
className: cx("uix-spinner", accent && "uix-spinner--accent", size === "lg" && "uix-spinner--lg", className),
|
|
1470
1647
|
role: "status",
|
|
1471
|
-
|
|
1472
|
-
|
|
1648
|
+
...props,
|
|
1649
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "uix-visually-hidden", children: label ?? "Loading\u2026" })
|
|
1473
1650
|
}
|
|
1474
1651
|
);
|
|
1475
1652
|
}
|
|
1476
1653
|
|
|
1477
1654
|
// src/components/Toast.tsx
|
|
1655
|
+
var import_react20 = require("react");
|
|
1478
1656
|
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1657
|
+
var ToasterContext = (0, import_react20.createContext)(null);
|
|
1479
1658
|
function Toast({ title, message, tone, icon, onClose, leaving, className, ...props }) {
|
|
1480
1659
|
const assertive = tone === "danger";
|
|
1660
|
+
const toaster = (0, import_react20.useContext)(ToasterContext);
|
|
1661
|
+
const bodyRef = (0, import_react20.useRef)(null);
|
|
1662
|
+
(0, import_react20.useEffect)(() => {
|
|
1663
|
+
if (assertive || !toaster) return;
|
|
1664
|
+
const text = bodyRef.current?.textContent?.trim();
|
|
1665
|
+
if (text) toaster.announce(text);
|
|
1666
|
+
}, []);
|
|
1667
|
+
const handleClose = (e) => {
|
|
1668
|
+
const btn = e.currentTarget;
|
|
1669
|
+
if (document.activeElement === btn) {
|
|
1670
|
+
const toasterEl = btn.closest(".uix-toaster");
|
|
1671
|
+
if (toasterEl) {
|
|
1672
|
+
const closes = Array.from(toasterEl.querySelectorAll(".uix-toast__close"));
|
|
1673
|
+
const i = closes.indexOf(btn);
|
|
1674
|
+
(closes[i + 1] ?? closes[i - 1])?.focus();
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
onClose?.();
|
|
1678
|
+
};
|
|
1679
|
+
const ownLiveRegion = assertive || !toaster;
|
|
1481
1680
|
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
1482
1681
|
"div",
|
|
1483
1682
|
{
|
|
1484
1683
|
className: cx("uix-toast", tone && `uix-toast--${tone}`, className),
|
|
1485
1684
|
"data-leaving": leaving || void 0,
|
|
1486
|
-
role: assertive ? "alert" : "status",
|
|
1487
|
-
"aria-live": assertive ? "assertive" : "polite",
|
|
1685
|
+
role: ownLiveRegion ? assertive ? "alert" : "status" : void 0,
|
|
1686
|
+
"aria-live": ownLiveRegion ? assertive ? "assertive" : "polite" : void 0,
|
|
1488
1687
|
...props,
|
|
1489
1688
|
children: [
|
|
1490
1689
|
icon && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "uix-toast__icon", "aria-hidden": "true", children: icon }),
|
|
1491
|
-
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "uix-toast__body", children: [
|
|
1690
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "uix-toast__body", ref: bodyRef, children: [
|
|
1492
1691
|
title && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "uix-toast__title", children: title }),
|
|
1493
1692
|
message && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "uix-toast__msg", children: message })
|
|
1494
1693
|
] }),
|
|
1495
|
-
onClose && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { className: "uix-toast__close", onClick:
|
|
1694
|
+
onClose && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { className: "uix-toast__close", onClick: handleClose, "aria-label": "Dismiss", children: "\xD7" })
|
|
1496
1695
|
]
|
|
1497
1696
|
}
|
|
1498
1697
|
);
|
|
1499
1698
|
}
|
|
1500
1699
|
function Toaster({ children, className, ...props }) {
|
|
1501
|
-
|
|
1700
|
+
const [announced, setAnnounced] = (0, import_react20.useState)("");
|
|
1701
|
+
const clearTimer = (0, import_react20.useRef)(null);
|
|
1702
|
+
const announce = (0, import_react20.useCallback)((text) => {
|
|
1703
|
+
setAnnounced((cur) => cur ? `${cur} ${text}` : text);
|
|
1704
|
+
if (clearTimer.current != null) clearTimeout(clearTimer.current);
|
|
1705
|
+
clearTimer.current = window.setTimeout(() => setAnnounced(""), 3e3);
|
|
1706
|
+
}, []);
|
|
1707
|
+
(0, import_react20.useEffect)(() => () => {
|
|
1708
|
+
if (clearTimer.current != null) clearTimeout(clearTimer.current);
|
|
1709
|
+
}, []);
|
|
1710
|
+
const ctx = (0, import_react20.useMemo)(() => ({ announce }), [announce]);
|
|
1711
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ToasterContext.Provider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: cx("uix-toaster", className), role: "region", "aria-label": "Notifications", ...props, children: [
|
|
1712
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "uix-visually-hidden", role: "status", "aria-live": "polite", children: announced }),
|
|
1713
|
+
children
|
|
1714
|
+
] }) });
|
|
1502
1715
|
}
|
|
1503
1716
|
|
|
1504
1717
|
// src/components/Table.tsx
|
|
1505
1718
|
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1506
|
-
function TableWrap({ children, className, ...props }) {
|
|
1507
|
-
return /* @__PURE__ */ (0, import_jsx_runtime25.
|
|
1719
|
+
function TableWrap({ announcement, children, className, ...props }) {
|
|
1720
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: cx("uix-table-wrap", className), ...props, children: [
|
|
1721
|
+
announcement != null && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { role: "status", className: "uix-visually-hidden", children: announcement }),
|
|
1722
|
+
children
|
|
1723
|
+
] });
|
|
1508
1724
|
}
|
|
1509
1725
|
function Table({
|
|
1510
1726
|
density = "standard",
|
|
@@ -1543,9 +1759,9 @@ function Th({ sortable, sortDirection, sortOrder, onSort, className, children, o
|
|
|
1543
1759
|
"data-sort": sortable || void 0,
|
|
1544
1760
|
"data-sort-order": sortOrder != null && sortOrder > 0 ? sortOrder : void 0,
|
|
1545
1761
|
"aria-sort": sortDirection && sortDirection !== "none" ? sortDirection : void 0,
|
|
1546
|
-
onClick: sortable
|
|
1762
|
+
onClick: sortable && onSort ? void 0 : onClick,
|
|
1547
1763
|
...props,
|
|
1548
|
-
children
|
|
1764
|
+
children: sortable ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { type: "button", className: "uix-th__sortbtn", onClick: onSort, children }) : children
|
|
1549
1765
|
}
|
|
1550
1766
|
);
|
|
1551
1767
|
}
|
|
@@ -1557,7 +1773,7 @@ function Tr({ selected, pinned, className, children, ...props }) {
|
|
|
1557
1773
|
"tr",
|
|
1558
1774
|
{
|
|
1559
1775
|
className,
|
|
1560
|
-
"
|
|
1776
|
+
"data-selected": selected || void 0,
|
|
1561
1777
|
"data-pinned": pinned || void 0,
|
|
1562
1778
|
...props,
|
|
1563
1779
|
children
|
|
@@ -1580,13 +1796,14 @@ function RowActions({ children, className, ...props }) {
|
|
|
1580
1796
|
function RowAction({ children, className, type = "button", ...props }) {
|
|
1581
1797
|
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { type, className: cx("uix-rowact", className), ...props, children });
|
|
1582
1798
|
}
|
|
1583
|
-
function ExpandToggle({ expanded, className, type = "button", ...props }) {
|
|
1799
|
+
function ExpandToggle({ expanded, controls, className, type = "button", ...props }) {
|
|
1584
1800
|
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1585
1801
|
"button",
|
|
1586
1802
|
{
|
|
1587
1803
|
type,
|
|
1588
1804
|
className: cx("uix-table__expand", className),
|
|
1589
1805
|
"aria-expanded": expanded,
|
|
1806
|
+
"aria-controls": controls,
|
|
1590
1807
|
"aria-label": expanded ? "Collapse row" : "Expand row",
|
|
1591
1808
|
...props,
|
|
1592
1809
|
children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("path", { d: "M6 4l4 4-4 4" }) })
|
|
@@ -1677,15 +1894,17 @@ function StatusPill({ tone = "neutral", treatment = "filled", dot, children, cla
|
|
|
1677
1894
|
|
|
1678
1895
|
// src/components/Progress.tsx
|
|
1679
1896
|
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1680
|
-
function Progress({ value = 0, max = 100, indeterminate, className, ...props }) {
|
|
1681
|
-
const
|
|
1897
|
+
function Progress({ value = 0, max = 100, indeterminate, label, className, ...props }) {
|
|
1898
|
+
const clamped = Math.max(0, Math.min(max || 100, value));
|
|
1899
|
+
const pct = clamped / (max || 100) * 100;
|
|
1682
1900
|
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1683
1901
|
"div",
|
|
1684
1902
|
{
|
|
1685
1903
|
role: "progressbar",
|
|
1686
1904
|
"aria-valuemin": 0,
|
|
1687
1905
|
"aria-valuemax": max,
|
|
1688
|
-
"aria-valuenow": indeterminate ? void 0 :
|
|
1906
|
+
"aria-valuenow": indeterminate ? void 0 : clamped,
|
|
1907
|
+
"aria-label": label,
|
|
1689
1908
|
className: cx("uix-progress", indeterminate && "uix-progress--indeterminate", className),
|
|
1690
1909
|
...props,
|
|
1691
1910
|
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "uix-progress__bar", style: indeterminate ? void 0 : { width: `${pct}%` } })
|
|
@@ -1695,7 +1914,13 @@ function Progress({ value = 0, max = 100, indeterminate, className, ...props })
|
|
|
1695
1914
|
|
|
1696
1915
|
// src/components/Meter.tsx
|
|
1697
1916
|
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1698
|
-
|
|
1917
|
+
var toneText = {
|
|
1918
|
+
warning: "warning",
|
|
1919
|
+
danger: "critical",
|
|
1920
|
+
attention: "needs attention",
|
|
1921
|
+
overdue: "overdue"
|
|
1922
|
+
};
|
|
1923
|
+
function Meter({ value = 0, tone, label, className, ...props }) {
|
|
1699
1924
|
const pct = Math.max(0, Math.min(100, value));
|
|
1700
1925
|
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1701
1926
|
"div",
|
|
@@ -1704,6 +1929,8 @@ function Meter({ value = 0, tone, className, ...props }) {
|
|
|
1704
1929
|
"aria-valuenow": pct,
|
|
1705
1930
|
"aria-valuemin": 0,
|
|
1706
1931
|
"aria-valuemax": 100,
|
|
1932
|
+
"aria-valuetext": tone && tone !== "success" ? `${pct}%, ${toneText[tone]}` : void 0,
|
|
1933
|
+
"aria-label": label,
|
|
1707
1934
|
className: cx("uix-meter", className),
|
|
1708
1935
|
...props,
|
|
1709
1936
|
children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
@@ -1728,22 +1955,34 @@ function Stat({ label, value, icon, meta, trend, className, ...props }) {
|
|
|
1728
1955
|
] }),
|
|
1729
1956
|
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "uix-stat__value", children: value }),
|
|
1730
1957
|
(meta != null || trend != null) && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "uix-stat__meta", children: [
|
|
1731
|
-
trend != null && /* @__PURE__ */ (0, import_jsx_runtime30.
|
|
1958
|
+
trend != null && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { className: cx("uix-stat__trend", `uix-stat__trend--${trend.direction}`), children: [
|
|
1959
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: trend.direction === "up" ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { d: "M12 19V5m-6 6 6-6 6 6" }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { d: "M12 5v14m6-6-6 6-6-6" }) }),
|
|
1960
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "uix-visually-hidden", children: trend.direction === "up" ? "up" : "down" }),
|
|
1961
|
+
trend.label
|
|
1962
|
+
] }),
|
|
1732
1963
|
meta
|
|
1733
1964
|
] })
|
|
1734
1965
|
] });
|
|
1735
1966
|
}
|
|
1736
1967
|
|
|
1737
1968
|
// src/components/Tooltip.tsx
|
|
1738
|
-
var
|
|
1969
|
+
var import_react21 = require("react");
|
|
1739
1970
|
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1740
1971
|
function Tooltip({ label, placement = "top", className, children, ...props }) {
|
|
1741
|
-
const triggerRef = (0,
|
|
1742
|
-
const bubbleRef = (0,
|
|
1743
|
-
const
|
|
1744
|
-
const
|
|
1972
|
+
const triggerRef = (0, import_react21.useRef)(null);
|
|
1973
|
+
const bubbleRef = (0, import_react21.useRef)(null);
|
|
1974
|
+
const closeTimer = (0, import_react21.useRef)(null);
|
|
1975
|
+
const [open, setOpen] = (0, import_react21.useState)(false);
|
|
1976
|
+
const id = (0, import_react21.useId)();
|
|
1745
1977
|
useAnchoredPosition(triggerRef, bubbleRef, { open, placement, offset: 6 });
|
|
1978
|
+
const cancelClose = () => {
|
|
1979
|
+
if (closeTimer.current != null) {
|
|
1980
|
+
clearTimeout(closeTimer.current);
|
|
1981
|
+
closeTimer.current = null;
|
|
1982
|
+
}
|
|
1983
|
+
};
|
|
1746
1984
|
const show = () => {
|
|
1985
|
+
cancelClose();
|
|
1747
1986
|
const b = bubbleRef.current;
|
|
1748
1987
|
if (b && !b.matches(":popover-open")) {
|
|
1749
1988
|
try {
|
|
@@ -1754,6 +1993,7 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
|
|
|
1754
1993
|
setOpen(true);
|
|
1755
1994
|
};
|
|
1756
1995
|
const hide = () => {
|
|
1996
|
+
cancelClose();
|
|
1757
1997
|
const b = bubbleRef.current;
|
|
1758
1998
|
if (b && b.matches(":popover-open")) {
|
|
1759
1999
|
try {
|
|
@@ -1763,7 +2003,20 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
|
|
|
1763
2003
|
}
|
|
1764
2004
|
setOpen(false);
|
|
1765
2005
|
};
|
|
1766
|
-
|
|
2006
|
+
const scheduleHide = () => {
|
|
2007
|
+
cancelClose();
|
|
2008
|
+
closeTimer.current = window.setTimeout(hide, 100);
|
|
2009
|
+
};
|
|
2010
|
+
(0, import_react21.useEffect)(() => {
|
|
2011
|
+
if (!open) return;
|
|
2012
|
+
const onKey = (e) => {
|
|
2013
|
+
if (e.key === "Escape") hide();
|
|
2014
|
+
};
|
|
2015
|
+
document.addEventListener("keydown", onKey);
|
|
2016
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
2017
|
+
}, [open]);
|
|
2018
|
+
(0, import_react21.useEffect)(() => () => {
|
|
2019
|
+
if (closeTimer.current != null) clearTimeout(closeTimer.current);
|
|
1767
2020
|
const b = bubbleRef.current;
|
|
1768
2021
|
if (b && b.matches(":popover-open")) {
|
|
1769
2022
|
try {
|
|
@@ -1773,14 +2026,19 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
|
|
|
1773
2026
|
}
|
|
1774
2027
|
}, []);
|
|
1775
2028
|
const bubbleAttr = { popover: "manual" };
|
|
2029
|
+
const single = (0, import_react21.isValidElement)(children);
|
|
2030
|
+
const child = single ? (0, import_react21.cloneElement)(children, {
|
|
2031
|
+
"aria-describedby": [children.props["aria-describedby"], id].filter(Boolean).join(" ")
|
|
2032
|
+
}) : children;
|
|
1776
2033
|
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
1777
2034
|
"span",
|
|
1778
2035
|
{
|
|
1779
2036
|
ref: triggerRef,
|
|
1780
2037
|
className: cx("uix-tooltip", className),
|
|
1781
|
-
"aria-describedby": id,
|
|
2038
|
+
"aria-describedby": single ? void 0 : id,
|
|
2039
|
+
tabIndex: single ? void 0 : 0,
|
|
1782
2040
|
onMouseEnter: show,
|
|
1783
|
-
onMouseLeave:
|
|
2041
|
+
onMouseLeave: scheduleHide,
|
|
1784
2042
|
onFocus: show,
|
|
1785
2043
|
onBlur: hide,
|
|
1786
2044
|
onKeyDown: (e) => {
|
|
@@ -1788,8 +2046,20 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
|
|
|
1788
2046
|
},
|
|
1789
2047
|
...props,
|
|
1790
2048
|
children: [
|
|
1791
|
-
|
|
1792
|
-
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
2049
|
+
child,
|
|
2050
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
2051
|
+
"span",
|
|
2052
|
+
{
|
|
2053
|
+
ref: bubbleRef,
|
|
2054
|
+
id,
|
|
2055
|
+
role: "tooltip",
|
|
2056
|
+
className: "uix-tooltip__bubble",
|
|
2057
|
+
onMouseEnter: cancelClose,
|
|
2058
|
+
onMouseLeave: scheduleHide,
|
|
2059
|
+
...bubbleAttr,
|
|
2060
|
+
children: label
|
|
2061
|
+
}
|
|
2062
|
+
)
|
|
1793
2063
|
]
|
|
1794
2064
|
}
|
|
1795
2065
|
);
|
|
@@ -1829,8 +2099,14 @@ function Skeleton({ variant = "block", width, height, className, style, ...props
|
|
|
1829
2099
|
}
|
|
1830
2100
|
);
|
|
1831
2101
|
}
|
|
1832
|
-
function LoadingState({ rows = 3, className, ...props }) {
|
|
1833
|
-
return
|
|
2102
|
+
function LoadingState({ rows = 3, label, className, ...props }) {
|
|
2103
|
+
return (
|
|
2104
|
+
// no aria-busy: busy=true tells AT the region isn't ready and suppresses the announcement (UIX-A11Y-4)
|
|
2105
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: cx("uix-stack", className), role: "status", ...props, children: [
|
|
2106
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "uix-visually-hidden", children: label ?? "Loading" }),
|
|
2107
|
+
Array.from({ length: rows }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Skeleton, { variant: "text", width: `${92 - i % 3 * 14}%` }, i))
|
|
2108
|
+
] })
|
|
2109
|
+
);
|
|
1834
2110
|
}
|
|
1835
2111
|
|
|
1836
2112
|
// src/components/Label.tsx
|
|
@@ -1848,7 +2124,8 @@ var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
|
1848
2124
|
function Avatar({ size = "md", src, alt, children, status, className, ...props }) {
|
|
1849
2125
|
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: cx("uix-avatar", size !== "md" && `uix-avatar--${size}`, className), ...props, children: [
|
|
1850
2126
|
src ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("img", { src, alt: alt ?? "" }) : children,
|
|
1851
|
-
status && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "uix-avatar__status" })
|
|
2127
|
+
status && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "uix-avatar__status", "aria-hidden": "true" }),
|
|
2128
|
+
status && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "uix-visually-hidden", children: "online" })
|
|
1852
2129
|
] });
|
|
1853
2130
|
}
|
|
1854
2131
|
function AvatarGroup({ children, className, ...props }) {
|
|
@@ -1868,7 +2145,7 @@ function UserChip({ avatar, name, sub, className, children, ...props }) {
|
|
|
1868
2145
|
// src/components/Comments.tsx
|
|
1869
2146
|
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
1870
2147
|
function Comments({ children, className, ...props }) {
|
|
1871
|
-
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: cx("uix-comments", className), ...props, children });
|
|
2148
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { role: "log", className: cx("uix-comments", className), ...props, children });
|
|
1872
2149
|
}
|
|
1873
2150
|
function Comment({ avatar, author, meta, children, className, ...props }) {
|
|
1874
2151
|
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: cx("uix-comment", className), ...props, children: [
|
|
@@ -1885,18 +2162,23 @@ function Comment({ avatar, author, meta, children, className, ...props }) {
|
|
|
1885
2162
|
}
|
|
1886
2163
|
|
|
1887
2164
|
// src/components/Composer.tsx
|
|
2165
|
+
var import_react22 = require("react");
|
|
1888
2166
|
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
1889
|
-
function Composer({ children, className, ...props }) {
|
|
1890
|
-
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: cx("uix-composer", className), ...props, children
|
|
2167
|
+
function Composer({ children, className, label = "Add a comment", ...props }) {
|
|
2168
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: cx("uix-composer", className), ...props, children: import_react22.Children.map(children, (child) => {
|
|
2169
|
+
if (!(0, import_react22.isValidElement)(child) || child.type !== "textarea") return child;
|
|
2170
|
+
const p = child.props;
|
|
2171
|
+
return p["aria-label"] || p["aria-labelledby"] || p.id ? child : (0, import_react22.cloneElement)(child, { "aria-label": label });
|
|
2172
|
+
}) });
|
|
1891
2173
|
}
|
|
1892
2174
|
function ComposerBar({ children, className, ...props }) {
|
|
1893
2175
|
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: cx("uix-composer__bar", className), ...props, children });
|
|
1894
2176
|
}
|
|
1895
2177
|
|
|
1896
2178
|
// src/components/Segmented.tsx
|
|
1897
|
-
var
|
|
2179
|
+
var import_react23 = require("react");
|
|
1898
2180
|
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
1899
|
-
var SegmentedCtx = (0,
|
|
2181
|
+
var SegmentedCtx = (0, import_react23.createContext)({});
|
|
1900
2182
|
function Segmented({ value, onChange, children, className, ...props }) {
|
|
1901
2183
|
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 }) });
|
|
1902
2184
|
}
|
|
@@ -1907,7 +2189,7 @@ function SegmentedOption({
|
|
|
1907
2189
|
onClick,
|
|
1908
2190
|
...props
|
|
1909
2191
|
}) {
|
|
1910
|
-
const ctx = (0,
|
|
2192
|
+
const ctx = (0, import_react23.useContext)(SegmentedCtx);
|
|
1911
2193
|
const selected = ctx.value === value;
|
|
1912
2194
|
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
1913
2195
|
"button",
|
|
@@ -1928,11 +2210,11 @@ function SegmentedOption({
|
|
|
1928
2210
|
// src/components/Timeline.tsx
|
|
1929
2211
|
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
1930
2212
|
function Timeline({ children, className, ...props }) {
|
|
1931
|
-
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("
|
|
2213
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("ol", { className: cx("uix-timeline", className), ...props, children });
|
|
1932
2214
|
}
|
|
1933
2215
|
function TimelineItem({ node, meta, children, className, ...props }) {
|
|
1934
|
-
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("
|
|
1935
|
-
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "uix-timeline__rail", children: [
|
|
2216
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("li", { className: cx("uix-timeline__item", className), ...props, children: [
|
|
2217
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "uix-timeline__rail", "aria-hidden": "true", children: [
|
|
1936
2218
|
node ?? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "uix-timeline__node" }),
|
|
1937
2219
|
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "uix-timeline__line" })
|
|
1938
2220
|
] }),
|
|
@@ -1956,7 +2238,7 @@ function Note({ tone, icon, children, className, ...props }) {
|
|
|
1956
2238
|
}
|
|
1957
2239
|
|
|
1958
2240
|
// src/components/Popover.tsx
|
|
1959
|
-
var
|
|
2241
|
+
var import_react24 = require("react");
|
|
1960
2242
|
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
1961
2243
|
function Popover({
|
|
1962
2244
|
popover = "auto",
|
|
@@ -1967,15 +2249,15 @@ function Popover({
|
|
|
1967
2249
|
children,
|
|
1968
2250
|
...props
|
|
1969
2251
|
}) {
|
|
1970
|
-
const ref = (0,
|
|
1971
|
-
const [open, setOpen] = (0,
|
|
2252
|
+
const ref = (0, import_react24.useRef)(null);
|
|
2253
|
+
const [open, setOpen] = (0, import_react24.useState)(false);
|
|
1972
2254
|
const popoverAttr = { popover };
|
|
1973
2255
|
const reposition = useAnchoredPosition(anchor ?? null, ref, {
|
|
1974
2256
|
open: open && !!anchor,
|
|
1975
2257
|
placement,
|
|
1976
2258
|
offset
|
|
1977
2259
|
});
|
|
1978
|
-
(0,
|
|
2260
|
+
(0, import_react24.useEffect)(() => {
|
|
1979
2261
|
const el = ref.current;
|
|
1980
2262
|
if (!el || !anchor) return;
|
|
1981
2263
|
const sync = () => {
|
|
@@ -1990,34 +2272,138 @@ function Popover({
|
|
|
1990
2272
|
}
|
|
1991
2273
|
|
|
1992
2274
|
// src/components/CommandPalette.tsx
|
|
2275
|
+
var import_react25 = require("react");
|
|
1993
2276
|
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
2277
|
+
var CmdkContext = (0, import_react25.createContext)(null);
|
|
1994
2278
|
function CommandPalette({ inputProps, children, className, ...props }) {
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
]
|
|
2279
|
+
const listId = (0, import_react25.useId)();
|
|
2280
|
+
const [activeId, setActiveId] = (0, import_react25.useState)(null);
|
|
2281
|
+
const [count, setCount] = (0, import_react25.useState)(0);
|
|
2282
|
+
const [announced, setAnnounced] = (0, import_react25.useState)("");
|
|
2283
|
+
const setActive = (0, import_react25.useCallback)((id, active) => {
|
|
2284
|
+
setActiveId((cur) => active ? id : cur === id ? null : cur);
|
|
2285
|
+
}, []);
|
|
2286
|
+
const registerItem = (0, import_react25.useCallback)(() => {
|
|
2287
|
+
setCount((c) => c + 1);
|
|
2288
|
+
return () => setCount((c) => c - 1);
|
|
2289
|
+
}, []);
|
|
2290
|
+
(0, import_react25.useEffect)(() => {
|
|
2291
|
+
const t = window.setTimeout(() => setAnnounced(`${count} result${count === 1 ? "" : "s"}`), 150);
|
|
2292
|
+
return () => clearTimeout(t);
|
|
2293
|
+
}, [count]);
|
|
2294
|
+
const ctx = (0, import_react25.useMemo)(() => ({ listId, setActive, registerItem }), [listId, setActive, registerItem]);
|
|
2295
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(CmdkContext.Provider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: cx("uix-cmdk", className), ...props, children: [
|
|
2296
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2297
|
+
"input",
|
|
2298
|
+
{
|
|
2299
|
+
className: "uix-cmdk__input",
|
|
2300
|
+
role: "combobox",
|
|
2301
|
+
"aria-expanded": "true",
|
|
2302
|
+
"aria-autocomplete": "list",
|
|
2303
|
+
"aria-controls": listId,
|
|
2304
|
+
"aria-activedescendant": activeId ?? void 0,
|
|
2305
|
+
"aria-label": "Command palette",
|
|
2306
|
+
...inputProps
|
|
2307
|
+
}
|
|
2308
|
+
),
|
|
2309
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "uix-cmdk__list", role: "listbox", id: listId, children }),
|
|
2310
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "uix-visually-hidden", role: "status", children: announced })
|
|
2311
|
+
] }) });
|
|
1999
2312
|
}
|
|
2000
2313
|
function CommandGroup({ label, children }) {
|
|
2001
|
-
|
|
2002
|
-
|
|
2314
|
+
const labelId = (0, import_react25.useId)();
|
|
2315
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { role: "group", "aria-labelledby": label != null ? labelId : void 0, children: [
|
|
2316
|
+
label != null && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "uix-cmdk__group", id: labelId, children: label }),
|
|
2003
2317
|
children
|
|
2004
2318
|
] });
|
|
2005
2319
|
}
|
|
2006
|
-
function CommandItem({ icon, shortcut, active, children, className, ...props }) {
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2320
|
+
function CommandItem({ icon, shortcut, active, children, className, id, ...props }) {
|
|
2321
|
+
const ctx = (0, import_react25.useContext)(CmdkContext);
|
|
2322
|
+
const autoId = (0, import_react25.useId)();
|
|
2323
|
+
const itemId = id ?? autoId;
|
|
2324
|
+
(0, import_react25.useEffect)(() => ctx?.registerItem(), [ctx]);
|
|
2325
|
+
(0, import_react25.useEffect)(() => {
|
|
2326
|
+
if (!ctx || !active) return;
|
|
2327
|
+
ctx.setActive(itemId, true);
|
|
2328
|
+
return () => ctx.setActive(itemId, false);
|
|
2329
|
+
}, [ctx, itemId, active]);
|
|
2330
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
|
|
2331
|
+
"div",
|
|
2332
|
+
{
|
|
2333
|
+
className: cx("uix-cmdk__item", className),
|
|
2334
|
+
"data-active": active || void 0,
|
|
2335
|
+
id: itemId,
|
|
2336
|
+
role: "option",
|
|
2337
|
+
"aria-selected": !!active,
|
|
2338
|
+
...props,
|
|
2339
|
+
children: [
|
|
2340
|
+
icon,
|
|
2341
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children }),
|
|
2342
|
+
shortcut
|
|
2343
|
+
]
|
|
2344
|
+
}
|
|
2345
|
+
);
|
|
2012
2346
|
}
|
|
2013
2347
|
|
|
2014
2348
|
// src/components/Inbox.tsx
|
|
2349
|
+
var import_react26 = require("react");
|
|
2015
2350
|
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
2016
|
-
function Inbox({ children, className, ...props }) {
|
|
2017
|
-
return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: cx("uix-inbox", className), ...props, children });
|
|
2351
|
+
function Inbox({ view, children, className, ...props }) {
|
|
2352
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: cx("uix-inbox", className), "data-view": view, ...props, children });
|
|
2018
2353
|
}
|
|
2354
|
+
var InboxListCtx = (0, import_react26.createContext)({});
|
|
2019
2355
|
function InboxList({ children, className, ...props }) {
|
|
2020
|
-
|
|
2356
|
+
const listRef = (0, import_react26.useRef)(null);
|
|
2357
|
+
const [activeId, setActiveId] = (0, import_react26.useState)(void 0);
|
|
2358
|
+
const ctx = (0, import_react26.useMemo)(() => ({ activeId }), [activeId]);
|
|
2359
|
+
const onKeyDown = (e) => {
|
|
2360
|
+
const opts = Array.from(listRef.current?.querySelectorAll('[role="option"]') ?? []);
|
|
2361
|
+
if (opts.length === 0) return;
|
|
2362
|
+
const idx = opts.findIndex((el2) => el2.id === activeId);
|
|
2363
|
+
let next;
|
|
2364
|
+
switch (e.key) {
|
|
2365
|
+
case "ArrowDown":
|
|
2366
|
+
next = Math.min(idx + 1, opts.length - 1);
|
|
2367
|
+
break;
|
|
2368
|
+
case "ArrowUp":
|
|
2369
|
+
next = idx < 0 ? 0 : Math.max(idx - 1, 0);
|
|
2370
|
+
break;
|
|
2371
|
+
case "Home":
|
|
2372
|
+
next = 0;
|
|
2373
|
+
break;
|
|
2374
|
+
case "End":
|
|
2375
|
+
next = opts.length - 1;
|
|
2376
|
+
break;
|
|
2377
|
+
case "Enter":
|
|
2378
|
+
case " ":
|
|
2379
|
+
if (idx >= 0) {
|
|
2380
|
+
e.preventDefault();
|
|
2381
|
+
opts[idx]?.click();
|
|
2382
|
+
}
|
|
2383
|
+
return;
|
|
2384
|
+
default:
|
|
2385
|
+
return;
|
|
2386
|
+
}
|
|
2387
|
+
e.preventDefault();
|
|
2388
|
+
const el = opts[next];
|
|
2389
|
+
if (!el || !el.id) return;
|
|
2390
|
+
setActiveId(el.id);
|
|
2391
|
+
el.scrollIntoView({ block: "nearest" });
|
|
2392
|
+
};
|
|
2393
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(InboxListCtx.Provider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2394
|
+
"div",
|
|
2395
|
+
{
|
|
2396
|
+
ref: listRef,
|
|
2397
|
+
className: cx("uix-inbox__list", className),
|
|
2398
|
+
role: "listbox",
|
|
2399
|
+
tabIndex: 0,
|
|
2400
|
+
"aria-label": "Inbox",
|
|
2401
|
+
"aria-activedescendant": activeId,
|
|
2402
|
+
onKeyDown,
|
|
2403
|
+
...props,
|
|
2404
|
+
children
|
|
2405
|
+
}
|
|
2406
|
+
) });
|
|
2021
2407
|
}
|
|
2022
2408
|
function InboxItem({
|
|
2023
2409
|
subject,
|
|
@@ -2027,13 +2413,19 @@ function InboxItem({
|
|
|
2027
2413
|
selected,
|
|
2028
2414
|
children,
|
|
2029
2415
|
className,
|
|
2416
|
+
id,
|
|
2030
2417
|
...props
|
|
2031
2418
|
}) {
|
|
2419
|
+
const autoId = (0, import_react26.useId)();
|
|
2420
|
+
const itemId = id ?? autoId;
|
|
2421
|
+
const { activeId } = (0, import_react26.useContext)(InboxListCtx);
|
|
2032
2422
|
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
2033
2423
|
"div",
|
|
2034
2424
|
{
|
|
2035
2425
|
role: "option",
|
|
2426
|
+
id: itemId,
|
|
2036
2427
|
"aria-selected": selected ?? false,
|
|
2428
|
+
"data-active": activeId === itemId ? "" : void 0,
|
|
2037
2429
|
className: cx("uix-inbox__item", className),
|
|
2038
2430
|
...unread ? { "data-unread": "" } : {},
|
|
2039
2431
|
...props,
|
|
@@ -2051,29 +2443,60 @@ function InboxDetail({ children, className, ...props }) {
|
|
|
2051
2443
|
}
|
|
2052
2444
|
|
|
2053
2445
|
// src/components/Kanban.tsx
|
|
2446
|
+
var import_react27 = require("react");
|
|
2054
2447
|
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
2055
|
-
function Kanban({ children, className, ...props }) {
|
|
2056
|
-
return /* @__PURE__ */ (0, import_jsx_runtime43.
|
|
2448
|
+
function Kanban({ announcement, children, className, ...props }) {
|
|
2449
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: cx("uix-kanban", className), ...props, children: [
|
|
2450
|
+
announcement != null && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { role: "status", className: "uix-visually-hidden", children: announcement }),
|
|
2451
|
+
children
|
|
2452
|
+
] });
|
|
2057
2453
|
}
|
|
2058
2454
|
function KanbanColumn({ title, count, children, className, ...props }) {
|
|
2455
|
+
const headId = (0, import_react27.useId)();
|
|
2456
|
+
const hasHead = title != null || count != null;
|
|
2059
2457
|
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: cx("uix-kanban__col", className), ...props, children: [
|
|
2060
|
-
|
|
2458
|
+
hasHead && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "uix-kanban__head", id: headId, children: [
|
|
2061
2459
|
title,
|
|
2062
2460
|
count != null && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "uix-kanban__count", children: count })
|
|
2063
2461
|
] }),
|
|
2064
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uix-kanban__body", children })
|
|
2462
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uix-kanban__body", role: "list", "aria-labelledby": hasHead ? headId : void 0, children })
|
|
2065
2463
|
] });
|
|
2066
2464
|
}
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2465
|
+
var MOVE_KEYS = {
|
|
2466
|
+
ArrowUp: "up",
|
|
2467
|
+
ArrowDown: "down",
|
|
2468
|
+
ArrowLeft: "left",
|
|
2469
|
+
ArrowRight: "right"
|
|
2470
|
+
};
|
|
2471
|
+
function KanbanCard({ title, meta, children, className, onMove, onKeyDown, ...props }) {
|
|
2472
|
+
const handleKeyDown = (e) => {
|
|
2473
|
+
onKeyDown?.(e);
|
|
2474
|
+
if (!onMove || !e.altKey || e.defaultPrevented) return;
|
|
2475
|
+
const dir = MOVE_KEYS[e.key];
|
|
2476
|
+
if (dir) {
|
|
2477
|
+
e.preventDefault();
|
|
2478
|
+
onMove(dir);
|
|
2479
|
+
}
|
|
2480
|
+
};
|
|
2481
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
|
|
2482
|
+
"div",
|
|
2483
|
+
{
|
|
2484
|
+
role: "listitem",
|
|
2485
|
+
tabIndex: 0,
|
|
2486
|
+
className: cx("uix-kanban__card", className),
|
|
2487
|
+
onKeyDown: handleKeyDown,
|
|
2488
|
+
...props,
|
|
2489
|
+
children: [
|
|
2490
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uix-kanban__card-title", children: title }),
|
|
2491
|
+
meta && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uix-kanban__card-meta", children: meta }),
|
|
2492
|
+
children
|
|
2493
|
+
]
|
|
2494
|
+
}
|
|
2495
|
+
);
|
|
2073
2496
|
}
|
|
2074
2497
|
|
|
2075
2498
|
// src/components/Tree.tsx
|
|
2076
|
-
var
|
|
2499
|
+
var import_react28 = require("react");
|
|
2077
2500
|
|
|
2078
2501
|
// src/tree-model.ts
|
|
2079
2502
|
function flattenTree(nodes, expanded, level = 1) {
|
|
@@ -2118,6 +2541,14 @@ function treeNav(flat, currentId, key) {
|
|
|
2118
2541
|
case " ":
|
|
2119
2542
|
return cur.hasChildren ? { toggleId: cur.node.id, selectId: cur.node.id } : { selectId: cur.node.id };
|
|
2120
2543
|
default:
|
|
2544
|
+
if (key.length === 1) {
|
|
2545
|
+
const q = key.toLowerCase();
|
|
2546
|
+
for (let step = 1; step <= flat.length; step++) {
|
|
2547
|
+
const f = flat[(idx + step) % flat.length];
|
|
2548
|
+
const label = f.node.typeaheadLabel ?? (typeof f.node.label === "string" ? f.node.label : void 0);
|
|
2549
|
+
if (label != null && label.toLowerCase().startsWith(q)) return { focusId: f.node.id };
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2121
2552
|
return {};
|
|
2122
2553
|
}
|
|
2123
2554
|
}
|
|
@@ -2185,12 +2616,12 @@ function Tree({
|
|
|
2185
2616
|
className,
|
|
2186
2617
|
...props
|
|
2187
2618
|
}) {
|
|
2188
|
-
const [internalExpanded, setInternalExpanded] = (0,
|
|
2619
|
+
const [internalExpanded, setInternalExpanded] = (0, import_react28.useState)(defaultExpanded ?? /* @__PURE__ */ new Set());
|
|
2189
2620
|
const expanded = controlledExpanded ?? internalExpanded;
|
|
2190
|
-
const rootRef = (0,
|
|
2191
|
-
const [focusedId, setFocusedId] = (0,
|
|
2621
|
+
const rootRef = (0, import_react28.useRef)(null);
|
|
2622
|
+
const [focusedId, setFocusedId] = (0, import_react28.useState)(void 0);
|
|
2192
2623
|
const selectable = selected !== void 0 || onSelect != null;
|
|
2193
|
-
const handleToggle = (0,
|
|
2624
|
+
const handleToggle = (0, import_react28.useCallback)((id) => {
|
|
2194
2625
|
if (!controlledExpanded) {
|
|
2195
2626
|
setInternalExpanded((prev) => {
|
|
2196
2627
|
const next = new Set(prev);
|
|
@@ -2201,7 +2632,7 @@ function Tree({
|
|
|
2201
2632
|
}
|
|
2202
2633
|
onToggle?.(id);
|
|
2203
2634
|
}, [controlledExpanded, onToggle]);
|
|
2204
|
-
const flat = (0,
|
|
2635
|
+
const flat = (0, import_react28.useMemo)(() => flattenTree(nodes, expanded), [nodes, expanded]);
|
|
2205
2636
|
const useVirtual = virtualize ?? shouldVirtualize(flat.length);
|
|
2206
2637
|
const tabbableId = focusedId ?? selected ?? nodes[0]?.id;
|
|
2207
2638
|
if (useVirtual) {
|
|
@@ -2272,6 +2703,13 @@ function Tree({
|
|
|
2272
2703
|
setFocusedId(id);
|
|
2273
2704
|
break;
|
|
2274
2705
|
default:
|
|
2706
|
+
if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
2707
|
+
const act = treeNav(flat, id, e.key);
|
|
2708
|
+
if (act.focusId !== void 0) {
|
|
2709
|
+
e.preventDefault();
|
|
2710
|
+
focusItem(rootRef.current?.querySelector(`[role="treeitem"][data-id="${CSS.escape(act.focusId)}"]`));
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2275
2713
|
break;
|
|
2276
2714
|
}
|
|
2277
2715
|
};
|
|
@@ -2305,12 +2743,14 @@ function VirtualTreeView({
|
|
|
2305
2743
|
className,
|
|
2306
2744
|
...props
|
|
2307
2745
|
}) {
|
|
2308
|
-
const scrollRef = (0,
|
|
2309
|
-
const [scrollTop, setScrollTop] = (0,
|
|
2310
|
-
const pendingFocus = (0,
|
|
2746
|
+
const scrollRef = (0, import_react28.useRef)(null);
|
|
2747
|
+
const [scrollTop, setScrollTop] = (0, import_react28.useState)(0);
|
|
2748
|
+
const pendingFocus = (0, import_react28.useRef)(false);
|
|
2749
|
+
const hadFocus = (0, import_react28.useRef)(false);
|
|
2311
2750
|
const win = virtualWindow(scrollTop, maxHeight, rowHeight, flat.length);
|
|
2312
2751
|
const rows = flat.slice(win.start, win.end);
|
|
2313
|
-
|
|
2752
|
+
const windowHasTabbable = tabbableId != null && rows.some((f) => f.node.id === tabbableId);
|
|
2753
|
+
(0, import_react28.useEffect)(() => {
|
|
2314
2754
|
if (!pendingFocus.current || focusedId == null) return;
|
|
2315
2755
|
const el = scrollRef.current?.querySelector(`[role="treeitem"][data-id="${CSS.escape(focusedId)}"]`);
|
|
2316
2756
|
if (el) {
|
|
@@ -2318,6 +2758,11 @@ function VirtualTreeView({
|
|
|
2318
2758
|
pendingFocus.current = false;
|
|
2319
2759
|
}
|
|
2320
2760
|
}, [focusedId, win.start, win.end]);
|
|
2761
|
+
(0, import_react28.useEffect)(() => {
|
|
2762
|
+
if (!hadFocus.current || pendingFocus.current) return;
|
|
2763
|
+
const active = document.activeElement;
|
|
2764
|
+
if (active === document.body || active == null) scrollRef.current?.focus({ preventScroll: true });
|
|
2765
|
+
});
|
|
2321
2766
|
const moveTo = (id) => {
|
|
2322
2767
|
const i = flat.findIndex((f) => f.node.id === id);
|
|
2323
2768
|
if (i < 0) return;
|
|
@@ -2333,6 +2778,7 @@ function VirtualTreeView({
|
|
|
2333
2778
|
setFocusedId(id);
|
|
2334
2779
|
};
|
|
2335
2780
|
const onKeyDown = (e) => {
|
|
2781
|
+
if (e.key.length === 1 && (e.ctrlKey || e.metaKey || e.altKey)) return;
|
|
2336
2782
|
const curId = e.target.closest('[role="treeitem"]')?.dataset.id ?? focusedId;
|
|
2337
2783
|
const act = treeNav(flat, curId, e.key);
|
|
2338
2784
|
if (act.focusId === void 0 && act.toggleId === void 0 && act.selectId === void 0) return;
|
|
@@ -2353,13 +2799,20 @@ function VirtualTreeView({
|
|
|
2353
2799
|
ref: scrollRef,
|
|
2354
2800
|
className: cx("uix-tree", "uix-tree--virtual", className),
|
|
2355
2801
|
role: "tree",
|
|
2802
|
+
tabIndex: -1,
|
|
2356
2803
|
style: { maxHeight, overflow: "auto" },
|
|
2357
2804
|
onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
|
|
2358
2805
|
onKeyDown,
|
|
2806
|
+
onFocus: () => {
|
|
2807
|
+
hadFocus.current = true;
|
|
2808
|
+
},
|
|
2809
|
+
onBlur: (e) => {
|
|
2810
|
+
if (!e.currentTarget.contains(e.relatedTarget)) hadFocus.current = false;
|
|
2811
|
+
},
|
|
2359
2812
|
...props,
|
|
2360
2813
|
children: [
|
|
2361
|
-
win.padTop > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { "aria-hidden": "true", style: { height: win.padTop } }),
|
|
2362
|
-
rows.map((f) => {
|
|
2814
|
+
win.padTop > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { role: "presentation", "aria-hidden": "true", style: { height: win.padTop } }),
|
|
2815
|
+
rows.map((f, i) => {
|
|
2363
2816
|
const rowStyle = { height: rowHeight, paddingLeft: `calc(${f.level - 1} * var(--uix-space-5))` };
|
|
2364
2817
|
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2365
2818
|
"li",
|
|
@@ -2371,7 +2824,7 @@ function VirtualTreeView({
|
|
|
2371
2824
|
"aria-expanded": f.hasChildren ? f.isExpanded : void 0,
|
|
2372
2825
|
"aria-selected": selectable ? selected === f.node.id : void 0,
|
|
2373
2826
|
"data-id": f.node.id,
|
|
2374
|
-
tabIndex: tabbableId === f.node.id ? 0 : -1,
|
|
2827
|
+
tabIndex: tabbableId === f.node.id || !windowHasTabbable && i === 0 ? 0 : -1,
|
|
2375
2828
|
className: "uix-tree__item",
|
|
2376
2829
|
onFocus: (e) => {
|
|
2377
2830
|
if (e.target === e.currentTarget) setFocusedId(f.node.id);
|
|
@@ -2391,7 +2844,7 @@ function VirtualTreeView({
|
|
|
2391
2844
|
f.node.id
|
|
2392
2845
|
);
|
|
2393
2846
|
}),
|
|
2394
|
-
win.padBottom > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { "aria-hidden": "true", style: { height: win.padBottom } })
|
|
2847
|
+
win.padBottom > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { role: "presentation", "aria-hidden": "true", style: { height: win.padBottom } })
|
|
2395
2848
|
]
|
|
2396
2849
|
}
|
|
2397
2850
|
);
|
|
@@ -2650,6 +3103,7 @@ function StatusRow({ indicator, name, meta, className, ...props }) {
|
|
|
2650
3103
|
SubNavItem,
|
|
2651
3104
|
Switch,
|
|
2652
3105
|
Tab,
|
|
3106
|
+
TabPanel,
|
|
2653
3107
|
Table,
|
|
2654
3108
|
TableWrap,
|
|
2655
3109
|
Tabs,
|