@tensor_1/react 2.16.0 → 2.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -0
- package/dist/chart-preset.cjs +15 -3
- package/dist/chart.cjs +15 -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/ChartCore.js +15 -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 +62 -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 +4 -5
- package/dist/components/Modal.js +4 -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/RelationshipGraph.d.ts +81 -3
- package/dist/components/RelationshipGraph.js +81 -57
- package/dist/components/RelationshipGraphLayered.d.ts +8 -0
- package/dist/components/RelationshipGraphLayered.js +420 -0
- package/dist/components/RelationshipGraphList.d.ts +16 -0
- package/dist/components/RelationshipGraphList.js +33 -0
- 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 +1 -0
- package/dist/components/States.js +7 -1
- package/dist/components/Table.d.ts +8 -1
- package/dist/components/Table.js +10 -6
- package/dist/components/Tabs.d.ts +13 -1
- package/dist/components/Tabs.js +79 -4
- 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 +1791 -446
- package/dist/index.d.cts +311 -36
- package/dist/index.d.ts +4 -4
- package/dist/index.js +10 -3
- package/dist/relationship-graph-model.d.ts +112 -1
- package/dist/relationship-graph-model.js +420 -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
|
@@ -50,6 +50,7 @@ __export(src_exports, {
|
|
|
50
50
|
ConfirmDialog: () => ConfirmDialog,
|
|
51
51
|
ContentList: () => ContentList,
|
|
52
52
|
ContentListItem: () => ContentListItem,
|
|
53
|
+
DEFAULT_RELATIONSHIP_GRAPH_LABELS: () => DEFAULT_RELATIONSHIP_GRAPH_LABELS,
|
|
53
54
|
DateRangePicker: () => DateRangePicker,
|
|
54
55
|
DescriptionItem: () => DescriptionItem,
|
|
55
56
|
DescriptionList: () => DescriptionList,
|
|
@@ -134,6 +135,7 @@ __export(src_exports, {
|
|
|
134
135
|
SubNavItem: () => SubNavItem,
|
|
135
136
|
Switch: () => Switch,
|
|
136
137
|
Tab: () => Tab,
|
|
138
|
+
TabPanel: () => TabPanel,
|
|
137
139
|
Table: () => Table,
|
|
138
140
|
TableWrap: () => TableWrap,
|
|
139
141
|
Tabs: () => Tabs,
|
|
@@ -160,6 +162,8 @@ __export(src_exports, {
|
|
|
160
162
|
buildThreeWayDiff: () => buildThreeWayDiff,
|
|
161
163
|
clampMetricValue: () => clampMetricValue,
|
|
162
164
|
clampWidth: () => clampWidth,
|
|
165
|
+
classifyLayeredEdges: () => classifyLayeredEdges,
|
|
166
|
+
clusterIdFor: () => clusterIdFor,
|
|
163
167
|
computePosition: () => computePosition,
|
|
164
168
|
contrastRatio: () => contrastRatio,
|
|
165
169
|
cx: () => cx,
|
|
@@ -174,6 +178,8 @@ __export(src_exports, {
|
|
|
174
178
|
isDateInRange: () => isDateInRange,
|
|
175
179
|
isDateUnavailable: () => isDateUnavailable,
|
|
176
180
|
isRuleGroup: () => isRuleGroup,
|
|
181
|
+
layeredNeighbor: () => layeredNeighbor,
|
|
182
|
+
layoutLayeredGraph: () => layoutLayeredGraph,
|
|
177
183
|
layoutRelationshipGraph: () => layoutRelationshipGraph,
|
|
178
184
|
mapRuleGroup: () => mapRuleGroup,
|
|
179
185
|
matchFilter: () => matchFilter,
|
|
@@ -215,6 +221,7 @@ __export(src_exports, {
|
|
|
215
221
|
useVirtualRows: () => useVirtualRows,
|
|
216
222
|
validateRuleDefinition: () => validateRuleDefinition,
|
|
217
223
|
virtualWindow: () => virtualWindow,
|
|
224
|
+
wrapEdgeLabel: () => wrapEdgeLabel,
|
|
218
225
|
zonedDateKey: () => zonedDateKey,
|
|
219
226
|
zonedDateSpan: () => zonedDateSpan
|
|
220
227
|
});
|
|
@@ -257,8 +264,10 @@ function unlockBodyScroll() {
|
|
|
257
264
|
saved = null;
|
|
258
265
|
}
|
|
259
266
|
}
|
|
260
|
-
function useDialog(open) {
|
|
267
|
+
function useDialog(open, onClose) {
|
|
261
268
|
const ref = (0, import_react.useRef)(null);
|
|
269
|
+
const onCloseRef = (0, import_react.useRef)(onClose);
|
|
270
|
+
onCloseRef.current = onClose;
|
|
262
271
|
(0, import_react.useEffect)(() => {
|
|
263
272
|
const el = ref.current;
|
|
264
273
|
if (!el || !open) {
|
|
@@ -267,8 +276,21 @@ function useDialog(open) {
|
|
|
267
276
|
}
|
|
268
277
|
if (!el.open) el.showModal();
|
|
269
278
|
lockBodyScroll();
|
|
279
|
+
let locked = true;
|
|
280
|
+
const handleClose = () => {
|
|
281
|
+
if (locked) {
|
|
282
|
+
locked = false;
|
|
283
|
+
unlockBodyScroll();
|
|
284
|
+
}
|
|
285
|
+
onCloseRef.current?.();
|
|
286
|
+
};
|
|
287
|
+
el.addEventListener("close", handleClose);
|
|
270
288
|
return () => {
|
|
271
|
-
|
|
289
|
+
el.removeEventListener("close", handleClose);
|
|
290
|
+
if (locked) {
|
|
291
|
+
locked = false;
|
|
292
|
+
unlockBodyScroll();
|
|
293
|
+
}
|
|
272
294
|
if (el.open) el.close();
|
|
273
295
|
};
|
|
274
296
|
}, [open]);
|
|
@@ -541,6 +563,24 @@ function useTable(data, options = {}) {
|
|
|
541
563
|
const toggleAllRows = (0, import_react2.useCallback)(() => setSelected((s) => togglePage(s, pageIds)), [pageIds]);
|
|
542
564
|
const clearSelection = (0, import_react2.useCallback)(() => setSelected(/* @__PURE__ */ new Set()), []);
|
|
543
565
|
const togglePin = (0, import_react2.useCallback)((id) => setPinned((s) => toggleId(s, id)), []);
|
|
566
|
+
const [announcement, setAnnouncement] = (0, import_react2.useState)("");
|
|
567
|
+
const mounted = (0, import_react2.useRef)(false);
|
|
568
|
+
(0, import_react2.useEffect)(() => {
|
|
569
|
+
if (!mounted.current) return;
|
|
570
|
+
const key = sort[0];
|
|
571
|
+
setAnnouncement(key ? `Sorted by ${key.field}, ${key.dir}` : "Sort cleared");
|
|
572
|
+
}, [sort]);
|
|
573
|
+
(0, import_react2.useEffect)(() => {
|
|
574
|
+
if (!mounted.current) return;
|
|
575
|
+
setAnnouncement(`${rows.length} result${rows.length === 1 ? "" : "s"}`);
|
|
576
|
+
}, [filters, query]);
|
|
577
|
+
(0, import_react2.useEffect)(() => {
|
|
578
|
+
if (!mounted.current) return;
|
|
579
|
+
setAnnouncement(`${selected.size} selected`);
|
|
580
|
+
}, [selected]);
|
|
581
|
+
(0, import_react2.useEffect)(() => {
|
|
582
|
+
mounted.current = true;
|
|
583
|
+
}, []);
|
|
544
584
|
const view = (0, import_react2.useMemo)(() => ({ sort, filters, q: query }), [sort, filters, query]);
|
|
545
585
|
const viewQueryString = (0, import_react2.useMemo)(() => serializeView(view), [view]);
|
|
546
586
|
const applyView = (0, import_react2.useCallback)((queryString) => {
|
|
@@ -557,6 +597,7 @@ function useTable(data, options = {}) {
|
|
|
557
597
|
selected,
|
|
558
598
|
pinned,
|
|
559
599
|
selectAll,
|
|
600
|
+
announcement,
|
|
560
601
|
toggleSort: doToggleSort,
|
|
561
602
|
setSort,
|
|
562
603
|
setFilters,
|
|
@@ -726,24 +767,40 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
|
|
|
726
767
|
// src/components/Button.tsx
|
|
727
768
|
var import_react5 = require("react");
|
|
728
769
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
770
|
+
var warnedIconButton = false;
|
|
729
771
|
var Button = (0, import_react5.forwardRef)(
|
|
730
|
-
({ variant = "secondary", size = "md", icon = false, loading = false, className, children, disabled, ...props }, ref) =>
|
|
731
|
-
"
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
className: cx(
|
|
735
|
-
"uix-btn",
|
|
736
|
-
`uix-btn--${variant}`,
|
|
737
|
-
size !== "md" && `uix-btn--${size}`,
|
|
738
|
-
icon && "uix-btn--icon",
|
|
739
|
-
className
|
|
740
|
-
),
|
|
741
|
-
disabled: disabled || loading,
|
|
742
|
-
"data-loading": loading || void 0,
|
|
743
|
-
...props,
|
|
744
|
-
children
|
|
772
|
+
({ variant = "secondary", size = "md", icon = false, loading = false, className, children, disabled, onClick, ...props }, ref) => {
|
|
773
|
+
if (!warnedIconButton && icon && children == null && !props["aria-label"] && !props["aria-labelledby"] && typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
|
|
774
|
+
warnedIconButton = true;
|
|
775
|
+
console.warn("uix: icon-only <Button> has no accessible name \u2014 pass aria-label or aria-labelledby.");
|
|
745
776
|
}
|
|
746
|
-
|
|
777
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
778
|
+
"button",
|
|
779
|
+
{
|
|
780
|
+
ref,
|
|
781
|
+
className: cx(
|
|
782
|
+
"uix-btn",
|
|
783
|
+
`uix-btn--${variant}`,
|
|
784
|
+
size !== "md" && `uix-btn--${size}`,
|
|
785
|
+
icon && "uix-btn--icon",
|
|
786
|
+
className
|
|
787
|
+
),
|
|
788
|
+
disabled,
|
|
789
|
+
"aria-disabled": loading || void 0,
|
|
790
|
+
"aria-busy": loading || void 0,
|
|
791
|
+
"data-loading": loading || void 0,
|
|
792
|
+
onClick: loading || onClick ? (e) => {
|
|
793
|
+
if (loading) {
|
|
794
|
+
e.preventDefault();
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
onClick?.(e);
|
|
798
|
+
} : void 0,
|
|
799
|
+
...props,
|
|
800
|
+
children
|
|
801
|
+
}
|
|
802
|
+
);
|
|
803
|
+
}
|
|
747
804
|
);
|
|
748
805
|
Button.displayName = "Button";
|
|
749
806
|
function ButtonGroup({ children, className }) {
|
|
@@ -766,7 +823,22 @@ var Input = (0, import_react6.forwardRef)(
|
|
|
766
823
|
)
|
|
767
824
|
);
|
|
768
825
|
Input.displayName = "Input";
|
|
769
|
-
function InputGroup({
|
|
826
|
+
function InputGroup({
|
|
827
|
+
children,
|
|
828
|
+
leadingIcon,
|
|
829
|
+
trailingIcon,
|
|
830
|
+
className,
|
|
831
|
+
id,
|
|
832
|
+
"aria-describedby": describedBy,
|
|
833
|
+
"aria-invalid": invalid,
|
|
834
|
+
"aria-required": required
|
|
835
|
+
}) {
|
|
836
|
+
const control = (0, import_react6.isValidElement)(children) && (id ?? describedBy ?? invalid ?? required) != null ? (0, import_react6.cloneElement)(children, {
|
|
837
|
+
id: children.props.id ?? id,
|
|
838
|
+
"aria-describedby": children.props["aria-describedby"] ?? describedBy,
|
|
839
|
+
"aria-invalid": children.props["aria-invalid"] ?? invalid,
|
|
840
|
+
"aria-required": children.props["aria-required"] ?? required
|
|
841
|
+
}) : children;
|
|
770
842
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
771
843
|
"div",
|
|
772
844
|
{
|
|
@@ -777,9 +849,9 @@ function InputGroup({ children, leadingIcon, trailingIcon, className }) {
|
|
|
777
849
|
className
|
|
778
850
|
),
|
|
779
851
|
children: [
|
|
780
|
-
leadingIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "uix-input-group__icon", children: leadingIcon }),
|
|
781
|
-
|
|
782
|
-
trailingIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "uix-input-group__icon uix-input-group__icon--trailing", children: trailingIcon })
|
|
852
|
+
leadingIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "uix-input-group__icon", "aria-hidden": "true", children: leadingIcon }),
|
|
853
|
+
control,
|
|
854
|
+
trailingIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "uix-input-group__icon uix-input-group__icon--trailing", "aria-hidden": "true", children: trailingIcon })
|
|
783
855
|
]
|
|
784
856
|
}
|
|
785
857
|
);
|
|
@@ -986,8 +1058,12 @@ var Radio = (0, import_react11.forwardRef)(
|
|
|
986
1058
|
] })
|
|
987
1059
|
);
|
|
988
1060
|
Radio.displayName = "Radio";
|
|
989
|
-
function RadioGroup({ children, className }) {
|
|
990
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: cx("uix-radio-group", className), children });
|
|
1061
|
+
function RadioGroup({ children, className, label }) {
|
|
1062
|
+
if (label == null) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: cx("uix-radio-group", className), children });
|
|
1063
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("fieldset", { className: cx("uix-radio-group", className), children: [
|
|
1064
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("legend", { className: "uix-radio-group__legend", children: label }),
|
|
1065
|
+
children
|
|
1066
|
+
] });
|
|
991
1067
|
}
|
|
992
1068
|
|
|
993
1069
|
// src/components/Switch.tsx
|
|
@@ -1009,14 +1085,17 @@ function Field({ label, hint, error, success, required, children, className, htm
|
|
|
1009
1085
|
const uid = (0, import_react13.useId)();
|
|
1010
1086
|
const msgId = `${uid}-msg`;
|
|
1011
1087
|
const describedBy = error || success || hint ? msgId : void 0;
|
|
1088
|
+
const controlId = (0, import_react13.isValidElement)(children) ? children.props.id ?? `${uid}-ctl` : void 0;
|
|
1012
1089
|
const control = (0, import_react13.isValidElement)(children) ? (0, import_react13.cloneElement)(children, {
|
|
1090
|
+
id: controlId,
|
|
1013
1091
|
"aria-describedby": [children.props["aria-describedby"], describedBy].filter(Boolean).join(" ") || void 0,
|
|
1014
|
-
"aria-invalid": error ? true : children.props["aria-invalid"]
|
|
1092
|
+
"aria-invalid": error ? true : children.props["aria-invalid"],
|
|
1093
|
+
"aria-required": children.props["aria-required"] ?? (required || void 0)
|
|
1015
1094
|
}) : children;
|
|
1016
1095
|
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: cx("uix-field", className), children: [
|
|
1017
|
-
label && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "uix-field__label", htmlFor, "data-required": required || void 0, children: label }),
|
|
1096
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "uix-field__label", htmlFor: htmlFor ?? controlId, "data-required": required || void 0, children: label }),
|
|
1018
1097
|
control,
|
|
1019
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "uix-field__msg", id: msgId, children: error ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "uix-field__error", role: "alert", children: error }) : success ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "uix-field__success", children: success }) : hint ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "uix-field__hint", children: hint }) : null })
|
|
1098
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "uix-field__msg", id: msgId, children: error ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "uix-field__error", role: "alert", children: error }) : success ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "uix-field__success", role: "status", children: success }) : hint ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "uix-field__hint", children: hint }) : null })
|
|
1020
1099
|
] });
|
|
1021
1100
|
}
|
|
1022
1101
|
|
|
@@ -1024,12 +1103,12 @@ function Field({ label, hint, error, success, required, children, className, htm
|
|
|
1024
1103
|
var import_react14 = require("react");
|
|
1025
1104
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1026
1105
|
var CloseIcon = () => /* @__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" }) });
|
|
1027
|
-
function Modal({ open, onClose, title, children, footer, className,
|
|
1028
|
-
const ref = useDialog(open);
|
|
1106
|
+
function Modal({ open, onClose, title, children, footer, className, closeLabel = "Close dialog", role = "dialog", ...rest }) {
|
|
1107
|
+
const ref = useDialog(open, onClose);
|
|
1029
1108
|
const titleId = (0, import_react14.useId)();
|
|
1030
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("dialog", { ref, className: cx("uix-dialog", className),
|
|
1109
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("dialog", { ref, className: cx("uix-dialog", className), role, "aria-labelledby": title != null ? titleId : void 0, ...rest, children: [
|
|
1031
1110
|
(title != null || onClose) && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "uix-dialog__header", children: [
|
|
1032
|
-
title && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("
|
|
1111
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "uix-dialog__title", id: titleId, children: title }),
|
|
1033
1112
|
onClose && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "uix-dialog__close", onClick: onClose, "aria-label": closeLabel, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CloseIcon, {}) })
|
|
1034
1113
|
] }),
|
|
1035
1114
|
children != null && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "uix-dialog__body", children }),
|
|
@@ -1087,13 +1166,15 @@ function PromptDialog({ open, title, description, inputLabel, defaultValue = "",
|
|
|
1087
1166
|
}
|
|
1088
1167
|
|
|
1089
1168
|
// src/components/Drawer.tsx
|
|
1169
|
+
var import_react16 = require("react");
|
|
1090
1170
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1091
1171
|
var CloseIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime12.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_runtime12.jsx)("path", { d: "M4 4l8 8M12 4l-8 8" }) });
|
|
1092
|
-
function Drawer({ open, onClose, title, children, footer, className }) {
|
|
1093
|
-
const ref = useDialog(open);
|
|
1094
|
-
|
|
1172
|
+
function Drawer({ open, onClose, title, children, footer, className, ...rest }) {
|
|
1173
|
+
const ref = useDialog(open, onClose);
|
|
1174
|
+
const titleId = (0, import_react16.useId)();
|
|
1175
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("dialog", { ref, className: cx("uix-drawer", className), "aria-labelledby": title ? titleId : void 0, ...rest, children: [
|
|
1095
1176
|
(title != null || onClose) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "uix-drawer__header", children: [
|
|
1096
|
-
title && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("
|
|
1177
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { id: titleId, style: { fontWeight: 600, fontSize: "var(--uix-text-h3)", fontFamily: "inherit", letterSpacing: "inherit", lineHeight: "inherit" }, children: title }),
|
|
1097
1178
|
onClose && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1098
1179
|
"button",
|
|
1099
1180
|
{
|
|
@@ -1110,27 +1191,30 @@ function Drawer({ open, onClose, title, children, footer, className }) {
|
|
|
1110
1191
|
}
|
|
1111
1192
|
|
|
1112
1193
|
// src/components/Peek.tsx
|
|
1194
|
+
var import_react17 = require("react");
|
|
1113
1195
|
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1114
1196
|
var CloseIcon3 = () => /* @__PURE__ */ (0, import_jsx_runtime13.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_runtime13.jsx)("path", { d: "M4 4l8 8M12 4l-8 8" }) });
|
|
1115
1197
|
var ChevronUp = () => /* @__PURE__ */ (0, import_jsx_runtime13.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_runtime13.jsx)("path", { d: "M2 8l4-4 4 4" }) });
|
|
1116
1198
|
var ChevronDown = () => /* @__PURE__ */ (0, import_jsx_runtime13.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_runtime13.jsx)("path", { d: "M2 4l4 4 4-4" }) });
|
|
1117
|
-
function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, className }) {
|
|
1118
|
-
const ref = useDialog(open);
|
|
1199
|
+
function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, className, onClick, ...rest }) {
|
|
1200
|
+
const ref = useDialog(open, onClose);
|
|
1119
1201
|
const hasNav = onNavPrev != null || onNavNext != null;
|
|
1202
|
+
const titleId = (0, import_react17.useId)();
|
|
1120
1203
|
const onBackdropClick = (e) => {
|
|
1204
|
+
onClick?.(e);
|
|
1121
1205
|
if (!onClose || e.target !== e.currentTarget) return;
|
|
1122
1206
|
const r = e.currentTarget.getBoundingClientRect();
|
|
1123
1207
|
if (r.width === 0) return;
|
|
1124
1208
|
const outside = e.clientX < r.left || e.clientX > r.right || e.clientY < r.top || e.clientY > r.bottom;
|
|
1125
1209
|
if (outside) onClose();
|
|
1126
1210
|
};
|
|
1127
|
-
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("dialog", { ref, className: cx("uix-peek", className),
|
|
1211
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("dialog", { ref, className: cx("uix-peek", className), "aria-labelledby": title ? titleId : void 0, ...rest, onClick: onBackdropClick, children: [
|
|
1128
1212
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "uix-peek__header", children: [
|
|
1129
1213
|
hasNav && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "uix-peek__nav", children: [
|
|
1130
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { className: "uix-peek__navbtn", onClick: onNavPrev, disabled:
|
|
1131
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { className: "uix-peek__navbtn", onClick: onNavNext, disabled:
|
|
1214
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { className: "uix-peek__navbtn", onClick: () => onNavPrev?.(), "aria-disabled": onNavPrev ? void 0 : true, "aria-label": "Previous record", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ChevronUp, {}) }),
|
|
1215
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { className: "uix-peek__navbtn", onClick: () => onNavNext?.(), "aria-disabled": onNavNext ? void 0 : true, "aria-label": "Next record", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ChevronDown, {}) })
|
|
1132
1216
|
] }),
|
|
1133
|
-
title && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("
|
|
1217
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h2", { className: "uix-peek__title", id: titleId, children: title }),
|
|
1134
1218
|
hint && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "uix-peek__hint", children: hint }),
|
|
1135
1219
|
onClose && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { className: "uix-peek__close", onClick: onClose, "aria-label": "Close preview", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CloseIcon3, {}) })
|
|
1136
1220
|
] }),
|
|
@@ -1141,12 +1225,12 @@ function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hi
|
|
|
1141
1225
|
|
|
1142
1226
|
// src/components/Card.tsx
|
|
1143
1227
|
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1144
|
-
function Card({ title, subtitle, headerAction, footer, children, className, ...props }) {
|
|
1228
|
+
function Card({ title, titleAs: TitleTag = "div", subtitle, headerAction, footer, children, className, ...props }) {
|
|
1145
1229
|
const hasHeader = title != null || subtitle != null || headerAction != null;
|
|
1146
1230
|
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: cx("uix-card", className), ...props, children: [
|
|
1147
1231
|
hasHeader && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "uix-card__header", children: [
|
|
1148
1232
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { flex: 1 }, children: [
|
|
1149
|
-
title && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1233
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TitleTag, { className: "uix-card__title", children: title }),
|
|
1150
1234
|
subtitle && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "uix-card__subtitle", children: subtitle })
|
|
1151
1235
|
] }),
|
|
1152
1236
|
headerAction
|
|
@@ -1171,12 +1255,12 @@ function CardLink({ title, subtitle, headerAction, footer, children, className,
|
|
|
1171
1255
|
}
|
|
1172
1256
|
|
|
1173
1257
|
// src/components/Breadcrumbs.tsx
|
|
1174
|
-
var
|
|
1258
|
+
var import_react18 = require("react");
|
|
1175
1259
|
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
1176
1260
|
function Breadcrumbs({ items, label, separator = "/", className, ...props }) {
|
|
1177
1261
|
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("nav", { "aria-label": label, className, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("ol", { className: "uix-breadcrumbs", children: items.map((item, index) => {
|
|
1178
1262
|
const current = item.current ?? index === items.length - 1;
|
|
1179
|
-
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
1263
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react18.Fragment, { children: [
|
|
1180
1264
|
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("li", { className: "uix-breadcrumbs__sep", "aria-hidden": "true", children: separator }),
|
|
1181
1265
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("li", { children: item.href && !current ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("a", { href: item.href, children: item.label }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { "aria-current": current ? "page" : void 0, children: item.label }) })
|
|
1182
1266
|
] }, item.id ?? `${item.href ?? "current"}-${index}`);
|
|
@@ -1185,11 +1269,11 @@ function Breadcrumbs({ items, label, separator = "/", className, ...props }) {
|
|
|
1185
1269
|
|
|
1186
1270
|
// src/components/PageHeader.tsx
|
|
1187
1271
|
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1188
|
-
function PageHeader({ eyebrow, title, subtitle, actions, className, ...props }) {
|
|
1272
|
+
function PageHeader({ eyebrow, title, subtitle, actions, as: TitleTag = "h1", className, ...props }) {
|
|
1189
1273
|
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("header", { className: cx("uix-page-header", className), ...props, children: [
|
|
1190
1274
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "uix-page-header__titles", children: [
|
|
1191
1275
|
eyebrow != null && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "uix-page-header__eyebrow", children: eyebrow }),
|
|
1192
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1276
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(TitleTag, { className: "uix-page-header__title", children: title }),
|
|
1193
1277
|
subtitle != null && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "uix-page-header__subtitle", children: subtitle })
|
|
1194
1278
|
] }),
|
|
1195
1279
|
actions != null && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "uix-page-header__actions", children: actions })
|
|
@@ -1201,7 +1285,7 @@ var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
|
1201
1285
|
function DetailLayout({ side, children, className, ...props }) {
|
|
1202
1286
|
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: cx("uix-detail", className), ...props, children: [
|
|
1203
1287
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "uix-detail__main", children }),
|
|
1204
|
-
side != null && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("
|
|
1288
|
+
side != null && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("aside", { className: "uix-detail__side", children: side })
|
|
1205
1289
|
] });
|
|
1206
1290
|
}
|
|
1207
1291
|
|
|
@@ -1278,11 +1362,11 @@ function ListItem({ title, meta, trail, leading, children, className, ...props }
|
|
|
1278
1362
|
}
|
|
1279
1363
|
|
|
1280
1364
|
// src/components/DescriptionList.tsx
|
|
1281
|
-
var
|
|
1365
|
+
var import_react19 = require("react");
|
|
1282
1366
|
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1283
1367
|
function DescriptionList({ items, children, className, ...props }) {
|
|
1284
1368
|
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("dl", { className: cx("uix-dl", className), ...props, children: [
|
|
1285
|
-
items?.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
1369
|
+
items?.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react19.Fragment, { children: [
|
|
1286
1370
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("dt", { children: item.term }),
|
|
1287
1371
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("dd", { children: item.description })
|
|
1288
1372
|
] }, item.id ?? i)),
|
|
@@ -1297,22 +1381,81 @@ function DescriptionItem({ term, children }) {
|
|
|
1297
1381
|
}
|
|
1298
1382
|
|
|
1299
1383
|
// src/components/Tabs.tsx
|
|
1300
|
-
var
|
|
1384
|
+
var import_react20 = require("react");
|
|
1301
1385
|
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1302
|
-
var TabsCtx = (0,
|
|
1386
|
+
var TabsCtx = (0, import_react20.createContext)({});
|
|
1303
1387
|
function Tabs({ variant = "line", value, onChange, children, className }) {
|
|
1304
|
-
const
|
|
1305
|
-
|
|
1388
|
+
const baseId = (0, import_react20.useId)();
|
|
1389
|
+
const listRef = (0, import_react20.useRef)(null);
|
|
1390
|
+
const contextValue = (0, import_react20.useMemo)(() => ({ value, onChange, baseId }), [value, onChange, baseId]);
|
|
1391
|
+
const kids = import_react20.Children.toArray(children);
|
|
1392
|
+
const panels = kids.filter((k) => (0, import_react20.isValidElement)(k) && k.type === TabPanel);
|
|
1393
|
+
const tabs = kids.filter((k) => !((0, import_react20.isValidElement)(k) && k.type === TabPanel));
|
|
1394
|
+
(0, import_react20.useEffect)(() => {
|
|
1395
|
+
const list = listRef.current;
|
|
1396
|
+
if (!list) return;
|
|
1397
|
+
const all = Array.from(list.querySelectorAll('[role="tab"]'));
|
|
1398
|
+
const enabled = all.filter((t) => !t.matches(":disabled"));
|
|
1399
|
+
const active = enabled.find((t) => t.getAttribute("aria-selected") === "true") ?? enabled[0];
|
|
1400
|
+
for (const t of all) t.tabIndex = t === active ? 0 : -1;
|
|
1401
|
+
});
|
|
1402
|
+
const onKeyDown = (e) => {
|
|
1403
|
+
const items = Array.from(
|
|
1404
|
+
listRef.current?.querySelectorAll('[role="tab"]:not(:disabled)') ?? []
|
|
1405
|
+
);
|
|
1406
|
+
const cur = e.target.closest('[role="tab"]');
|
|
1407
|
+
const idx = cur ? items.indexOf(cur) : -1;
|
|
1408
|
+
if (idx < 0 || items.length === 0) return;
|
|
1409
|
+
let next;
|
|
1410
|
+
switch (e.key) {
|
|
1411
|
+
case "ArrowRight":
|
|
1412
|
+
next = (idx + 1) % items.length;
|
|
1413
|
+
break;
|
|
1414
|
+
case "ArrowLeft":
|
|
1415
|
+
next = (idx - 1 + items.length) % items.length;
|
|
1416
|
+
break;
|
|
1417
|
+
case "Home":
|
|
1418
|
+
next = 0;
|
|
1419
|
+
break;
|
|
1420
|
+
case "End":
|
|
1421
|
+
next = items.length - 1;
|
|
1422
|
+
break;
|
|
1423
|
+
default:
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
1426
|
+
e.preventDefault();
|
|
1427
|
+
const target = items[next];
|
|
1428
|
+
if (target) {
|
|
1429
|
+
target.focus();
|
|
1430
|
+
target.click();
|
|
1431
|
+
}
|
|
1432
|
+
};
|
|
1433
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(TabsCtx.Provider, { value: contextValue, children: [
|
|
1434
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1435
|
+
"div",
|
|
1436
|
+
{
|
|
1437
|
+
ref: listRef,
|
|
1438
|
+
role: "tablist",
|
|
1439
|
+
className: cx("uix-tabs", `uix-tabs--${variant}`, className),
|
|
1440
|
+
onKeyDown,
|
|
1441
|
+
children: tabs
|
|
1442
|
+
}
|
|
1443
|
+
),
|
|
1444
|
+
panels
|
|
1445
|
+
] });
|
|
1306
1446
|
}
|
|
1307
1447
|
function Tab({ value, children, disabled, className, onClick, ...props }) {
|
|
1308
|
-
const ctx = (0,
|
|
1448
|
+
const ctx = (0, import_react20.useContext)(TabsCtx);
|
|
1309
1449
|
const selected = ctx.value === value;
|
|
1310
1450
|
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1311
1451
|
"button",
|
|
1312
1452
|
{
|
|
1313
1453
|
role: "tab",
|
|
1454
|
+
id: ctx.baseId ? `${ctx.baseId}-tab-${value}` : void 0,
|
|
1455
|
+
"aria-controls": ctx.baseId ? `${ctx.baseId}-panel-${value}` : void 0,
|
|
1314
1456
|
className: cx("uix-tab", className),
|
|
1315
1457
|
"aria-selected": selected,
|
|
1458
|
+
tabIndex: selected ? 0 : -1,
|
|
1316
1459
|
disabled,
|
|
1317
1460
|
onClick: (e) => {
|
|
1318
1461
|
ctx.onChange?.(value);
|
|
@@ -1323,9 +1466,24 @@ function Tab({ value, children, disabled, className, onClick, ...props }) {
|
|
|
1323
1466
|
}
|
|
1324
1467
|
);
|
|
1325
1468
|
}
|
|
1469
|
+
function TabPanel({ value, children, ...props }) {
|
|
1470
|
+
const ctx = (0, import_react20.useContext)(TabsCtx);
|
|
1471
|
+
if (ctx.value !== value) return null;
|
|
1472
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1473
|
+
"div",
|
|
1474
|
+
{
|
|
1475
|
+
role: "tabpanel",
|
|
1476
|
+
id: ctx.baseId ? `${ctx.baseId}-panel-${value}` : void 0,
|
|
1477
|
+
"aria-labelledby": ctx.baseId ? `${ctx.baseId}-tab-${value}` : void 0,
|
|
1478
|
+
tabIndex: 0,
|
|
1479
|
+
...props,
|
|
1480
|
+
children
|
|
1481
|
+
}
|
|
1482
|
+
);
|
|
1483
|
+
}
|
|
1326
1484
|
|
|
1327
1485
|
// src/components/AppShell.tsx
|
|
1328
|
-
var
|
|
1486
|
+
var import_react21 = require("react");
|
|
1329
1487
|
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1330
1488
|
function AppShell({
|
|
1331
1489
|
nav,
|
|
@@ -1333,6 +1491,7 @@ function AppShell({
|
|
|
1333
1491
|
focus,
|
|
1334
1492
|
onExitFocus,
|
|
1335
1493
|
mainBleed,
|
|
1494
|
+
mainId = "uix-main",
|
|
1336
1495
|
sidebar,
|
|
1337
1496
|
topbar,
|
|
1338
1497
|
children,
|
|
@@ -1340,7 +1499,7 @@ function AppShell({
|
|
|
1340
1499
|
...props
|
|
1341
1500
|
}) {
|
|
1342
1501
|
const navState = nav ?? (collapsed ? "rail" : "full");
|
|
1343
|
-
(0,
|
|
1502
|
+
(0, import_react21.useEffect)(() => {
|
|
1344
1503
|
if (!focus || !onExitFocus) return;
|
|
1345
1504
|
const onKey = (e) => {
|
|
1346
1505
|
if (e.key === "Escape") onExitFocus();
|
|
@@ -1357,9 +1516,10 @@ function AppShell({
|
|
|
1357
1516
|
"data-focus": focus || void 0,
|
|
1358
1517
|
...props,
|
|
1359
1518
|
children: [
|
|
1519
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("a", { className: "uix-skiplink", href: `#${mainId}`, children: "Skip to content" }),
|
|
1360
1520
|
sidebar && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "uix-shell__sidebar", children: sidebar }),
|
|
1361
1521
|
topbar && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "uix-shell__topbar", children: topbar }),
|
|
1362
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("main", { className: cx("uix-shell__main", mainBleed && "uix-shell__main--bleed"), children }),
|
|
1522
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("main", { id: mainId, tabIndex: -1, className: cx("uix-shell__main", mainBleed && "uix-shell__main--bleed"), children }),
|
|
1363
1523
|
focus && onExitFocus && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "uix-shell__focus-exit", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", onClick: onExitFocus, children: "Esc \xB7 Exit focus" }) })
|
|
1364
1524
|
]
|
|
1365
1525
|
}
|
|
@@ -1367,7 +1527,7 @@ function AppShell({
|
|
|
1367
1527
|
}
|
|
1368
1528
|
|
|
1369
1529
|
// src/components/Sidebar.tsx
|
|
1370
|
-
var
|
|
1530
|
+
var import_react22 = require("react");
|
|
1371
1531
|
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1372
1532
|
var ChevronIcon = () => /* @__PURE__ */ (0, import_jsx_runtime26.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_runtime26.jsx)("path", { d: "M2 4l4 4 4-4" }) });
|
|
1373
1533
|
function Sidebar({ collapsed, nav, onToggle, brand, toggleIcon, children, expandLabel = "Expand sidebar", collapseLabel = "Collapse sidebar", className, ...props }) {
|
|
@@ -1423,12 +1583,12 @@ function NavItem({ icon, badge, active, as: Tag = "a", children, className, ...p
|
|
|
1423
1583
|
);
|
|
1424
1584
|
}
|
|
1425
1585
|
function NavGroup({ icon, label, children, defaultExpanded = true, expanded: controlledExpanded, onExpandedChange }) {
|
|
1426
|
-
const [internalExpanded, setInternalExpanded] = (0,
|
|
1586
|
+
const [internalExpanded, setInternalExpanded] = (0, import_react22.useState)(defaultExpanded);
|
|
1427
1587
|
const expanded = controlledExpanded ?? internalExpanded;
|
|
1428
|
-
const panelId = (0,
|
|
1429
|
-
const triggerRef = (0,
|
|
1430
|
-
const panelRef = (0,
|
|
1431
|
-
(0,
|
|
1588
|
+
const panelId = (0, import_react22.useId)();
|
|
1589
|
+
const triggerRef = (0, import_react22.useRef)(null);
|
|
1590
|
+
const panelRef = (0, import_react22.useRef)(null);
|
|
1591
|
+
(0, import_react22.useEffect)(() => {
|
|
1432
1592
|
panelRef.current?.toggleAttribute("inert", !expanded);
|
|
1433
1593
|
if (!expanded && panelRef.current?.contains(document.activeElement)) triggerRef.current?.focus();
|
|
1434
1594
|
}, [expanded]);
|
|
@@ -1507,7 +1667,7 @@ function StarButton({
|
|
|
1507
1667
|
}
|
|
1508
1668
|
|
|
1509
1669
|
// src/components/NavFavourites.tsx
|
|
1510
|
-
var
|
|
1670
|
+
var import_react23 = require("react");
|
|
1511
1671
|
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1512
1672
|
var ChevronDownIcon = () => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1513
1673
|
"svg",
|
|
@@ -1570,13 +1730,14 @@ function NavFavourites({
|
|
|
1570
1730
|
emptyReason,
|
|
1571
1731
|
className
|
|
1572
1732
|
}) {
|
|
1573
|
-
const regionId = (0,
|
|
1574
|
-
const headerRef = (0,
|
|
1575
|
-
const regionRef = (0,
|
|
1576
|
-
const
|
|
1577
|
-
const
|
|
1578
|
-
const
|
|
1579
|
-
(0,
|
|
1733
|
+
const regionId = (0, import_react23.useId)();
|
|
1734
|
+
const headerRef = (0, import_react23.useRef)(null);
|
|
1735
|
+
const regionRef = (0, import_react23.useRef)(null);
|
|
1736
|
+
const [announcement, setAnnouncement] = (0, import_react23.useState)("");
|
|
1737
|
+
const focusAfterRef = (0, import_react23.useRef)(null);
|
|
1738
|
+
const triggerRefs = (0, import_react23.useRef)(/* @__PURE__ */ new Map());
|
|
1739
|
+
const order = (0, import_react23.useMemo)(() => items.map((item) => item.id), [items]);
|
|
1740
|
+
(0, import_react23.useEffect)(() => {
|
|
1580
1741
|
const id = focusAfterRef.current;
|
|
1581
1742
|
if (!id) return;
|
|
1582
1743
|
focusAfterRef.current = null;
|
|
@@ -1593,7 +1754,7 @@ function NavFavourites({
|
|
|
1593
1754
|
}
|
|
1594
1755
|
onToggleCollapsed?.();
|
|
1595
1756
|
};
|
|
1596
|
-
const move = (0,
|
|
1757
|
+
const move = (0, import_react23.useCallback)((id, dir) => {
|
|
1597
1758
|
const idx = order.indexOf(id);
|
|
1598
1759
|
const next = idx + dir;
|
|
1599
1760
|
if (idx < 0 || next < 0 || next >= order.length) return;
|
|
@@ -1602,19 +1763,22 @@ function NavFavourites({
|
|
|
1602
1763
|
if (it === void 0) return;
|
|
1603
1764
|
reordered.splice(next, 0, it);
|
|
1604
1765
|
focusAfterRef.current = id;
|
|
1766
|
+
const moved = items.find((i) => i.id === id);
|
|
1767
|
+
if (moved) setAnnouncement(`${moved.label} moved to position ${next + 1} of ${order.length}`);
|
|
1605
1768
|
onReorder(reordered);
|
|
1606
1769
|
}, [onReorder, order]);
|
|
1607
|
-
const remove = (0,
|
|
1770
|
+
const remove = (0, import_react23.useCallback)((id) => {
|
|
1608
1771
|
const idx = order.indexOf(id);
|
|
1609
1772
|
const neighbour = order[idx + 1] ?? order[idx - 1] ?? null;
|
|
1610
1773
|
focusAfterRef.current = neighbour;
|
|
1611
1774
|
onRemove(id);
|
|
1612
1775
|
}, [onRemove, order]);
|
|
1613
|
-
const registerTrigger = (0,
|
|
1776
|
+
const registerTrigger = (0, import_react23.useCallback)((id, element) => {
|
|
1614
1777
|
if (element) triggerRefs.current.set(id, element);
|
|
1615
1778
|
else triggerRefs.current.delete(id);
|
|
1616
1779
|
}, []);
|
|
1617
1780
|
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: cx("uix-favourites", className), children: [
|
|
1781
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { role: "status", className: "uix-visually-hidden", children: announcement }),
|
|
1618
1782
|
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
1619
1783
|
"button",
|
|
1620
1784
|
{
|
|
@@ -1661,7 +1825,7 @@ function NavFavourites({
|
|
|
1661
1825
|
)
|
|
1662
1826
|
] });
|
|
1663
1827
|
}
|
|
1664
|
-
var FavouriteRow = (0,
|
|
1828
|
+
var FavouriteRow = (0, import_react23.memo)(function FavouriteRow2({
|
|
1665
1829
|
item,
|
|
1666
1830
|
isFirst,
|
|
1667
1831
|
isLast,
|
|
@@ -1670,17 +1834,17 @@ var FavouriteRow = (0, import_react21.memo)(function FavouriteRow2({
|
|
|
1670
1834
|
onRemove,
|
|
1671
1835
|
registerTrigger
|
|
1672
1836
|
}) {
|
|
1673
|
-
const [open, setOpen] = (0,
|
|
1674
|
-
const menuId = (0,
|
|
1675
|
-
const triggerRef = (0,
|
|
1676
|
-
const itemRefs = (0,
|
|
1837
|
+
const [open, setOpen] = (0, import_react23.useState)(false);
|
|
1838
|
+
const menuId = (0, import_react23.useId)();
|
|
1839
|
+
const triggerRef = (0, import_react23.useRef)(null);
|
|
1840
|
+
const itemRefs = (0, import_react23.useRef)([]);
|
|
1677
1841
|
const optionsTemplate = labels.options ?? "{label} favourite options";
|
|
1678
1842
|
const actions = [
|
|
1679
1843
|
...isFirst ? [] : [{ label: labels.moveUp, onSelect: () => onMove(item.id, -1) }],
|
|
1680
1844
|
...isLast ? [] : [{ label: labels.moveDown, onSelect: () => onMove(item.id, 1) }],
|
|
1681
1845
|
{ label: labels.remove.replace("{label}", item.label), onSelect: () => onRemove(item.id) }
|
|
1682
1846
|
];
|
|
1683
|
-
(0,
|
|
1847
|
+
(0, import_react23.useEffect)(() => {
|
|
1684
1848
|
if (open) itemRefs.current[0]?.focus();
|
|
1685
1849
|
}, [open]);
|
|
1686
1850
|
const closeAndReturnFocus = () => {
|
|
@@ -1696,6 +1860,12 @@ var FavouriteRow = (0, import_react21.memo)(function FavouriteRow2({
|
|
|
1696
1860
|
} else if (e.key === "ArrowUp") {
|
|
1697
1861
|
e.preventDefault();
|
|
1698
1862
|
menuItems[(currentIdx - 1 + menuItems.length) % menuItems.length]?.focus();
|
|
1863
|
+
} else if (e.key === "Home") {
|
|
1864
|
+
e.preventDefault();
|
|
1865
|
+
menuItems[0]?.focus();
|
|
1866
|
+
} else if (e.key === "End") {
|
|
1867
|
+
e.preventDefault();
|
|
1868
|
+
menuItems[menuItems.length - 1]?.focus();
|
|
1699
1869
|
} else if (e.key === "Escape") {
|
|
1700
1870
|
e.preventDefault();
|
|
1701
1871
|
closeAndReturnFocus();
|
|
@@ -1754,6 +1924,7 @@ var FavouriteRow = (0, import_react21.memo)(function FavouriteRow2({
|
|
|
1754
1924
|
{
|
|
1755
1925
|
id: menuId,
|
|
1756
1926
|
role: "menu",
|
|
1927
|
+
"aria-label": optionsTemplate.replace("{label}", item.label),
|
|
1757
1928
|
onKeyDown: onMenuKeyDown,
|
|
1758
1929
|
className: "uix-favourites__menu",
|
|
1759
1930
|
children: actions.map((action, i) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("li", { role: "presentation", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
@@ -1785,61 +1956,108 @@ var FavouriteRow = (0, import_react21.memo)(function FavouriteRow2({
|
|
|
1785
1956
|
// src/components/Alert.tsx
|
|
1786
1957
|
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1787
1958
|
function Alert({ tone, title, icon, children, className, ...props }) {
|
|
1788
|
-
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1959
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
1960
|
+
"div",
|
|
1961
|
+
{
|
|
1962
|
+
className: cx("uix-alert", tone && tone !== "neutral" && `uix-alert--${tone}`, className),
|
|
1963
|
+
role: tone === "danger" || tone === "warning" ? "alert" : "status",
|
|
1964
|
+
...props,
|
|
1965
|
+
children: [
|
|
1966
|
+
icon && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "uix-alert__icon", children: icon }),
|
|
1967
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { children: [
|
|
1968
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "uix-alert__title", children: title }),
|
|
1969
|
+
children && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "uix-alert__body", children })
|
|
1970
|
+
] })
|
|
1971
|
+
]
|
|
1972
|
+
}
|
|
1973
|
+
);
|
|
1795
1974
|
}
|
|
1796
1975
|
|
|
1797
1976
|
// src/components/Spinner.tsx
|
|
1798
1977
|
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1799
|
-
function Spinner({ size = "md", accent, className, ...props }) {
|
|
1978
|
+
function Spinner({ size = "md", accent, label, className, ...props }) {
|
|
1800
1979
|
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
1801
1980
|
"span",
|
|
1802
1981
|
{
|
|
1803
1982
|
className: cx("uix-spinner", accent && "uix-spinner--accent", size === "lg" && "uix-spinner--lg", className),
|
|
1804
1983
|
role: "status",
|
|
1805
|
-
|
|
1806
|
-
|
|
1984
|
+
...props,
|
|
1985
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "uix-visually-hidden", children: label ?? "Loading\u2026" })
|
|
1807
1986
|
}
|
|
1808
1987
|
);
|
|
1809
1988
|
}
|
|
1810
1989
|
|
|
1811
1990
|
// src/components/Toast.tsx
|
|
1991
|
+
var import_react24 = require("react");
|
|
1812
1992
|
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1993
|
+
var ToasterContext = (0, import_react24.createContext)(null);
|
|
1813
1994
|
function Toast({ title, message, tone, icon, onClose, leaving, className, ...props }) {
|
|
1814
1995
|
const assertive = tone === "danger";
|
|
1996
|
+
const toaster = (0, import_react24.useContext)(ToasterContext);
|
|
1997
|
+
const bodyRef = (0, import_react24.useRef)(null);
|
|
1998
|
+
(0, import_react24.useEffect)(() => {
|
|
1999
|
+
if (assertive || !toaster) return;
|
|
2000
|
+
const text = bodyRef.current?.textContent?.trim();
|
|
2001
|
+
if (text) toaster.announce(text);
|
|
2002
|
+
}, []);
|
|
2003
|
+
const handleClose = (e) => {
|
|
2004
|
+
const btn = e.currentTarget;
|
|
2005
|
+
if (document.activeElement === btn) {
|
|
2006
|
+
const toasterEl = btn.closest(".uix-toaster");
|
|
2007
|
+
if (toasterEl) {
|
|
2008
|
+
const closes = Array.from(toasterEl.querySelectorAll(".uix-toast__close"));
|
|
2009
|
+
const i = closes.indexOf(btn);
|
|
2010
|
+
(closes[i + 1] ?? closes[i - 1])?.focus();
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
onClose?.();
|
|
2014
|
+
};
|
|
2015
|
+
const ownLiveRegion = assertive || !toaster;
|
|
1815
2016
|
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
1816
2017
|
"div",
|
|
1817
2018
|
{
|
|
1818
2019
|
className: cx("uix-toast", tone && `uix-toast--${tone}`, className),
|
|
1819
2020
|
"data-leaving": leaving || void 0,
|
|
1820
|
-
role: assertive ? "alert" : "status",
|
|
1821
|
-
"aria-live": assertive ? "assertive" : "polite",
|
|
2021
|
+
role: ownLiveRegion ? assertive ? "alert" : "status" : void 0,
|
|
2022
|
+
"aria-live": ownLiveRegion ? assertive ? "assertive" : "polite" : void 0,
|
|
1822
2023
|
...props,
|
|
1823
2024
|
children: [
|
|
1824
2025
|
icon && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "uix-toast__icon", "aria-hidden": "true", children: icon }),
|
|
1825
|
-
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "uix-toast__body", children: [
|
|
2026
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "uix-toast__body", ref: bodyRef, children: [
|
|
1826
2027
|
title && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "uix-toast__title", children: title }),
|
|
1827
2028
|
message && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "uix-toast__msg", children: message })
|
|
1828
2029
|
] }),
|
|
1829
|
-
onClose && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("button", { className: "uix-toast__close", onClick:
|
|
2030
|
+
onClose && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("button", { className: "uix-toast__close", onClick: handleClose, "aria-label": "Dismiss", children: "\xD7" })
|
|
1830
2031
|
]
|
|
1831
2032
|
}
|
|
1832
2033
|
);
|
|
1833
2034
|
}
|
|
1834
2035
|
function Toaster({ children, className, ...props }) {
|
|
1835
|
-
|
|
2036
|
+
const [announced, setAnnounced] = (0, import_react24.useState)("");
|
|
2037
|
+
const clearTimer = (0, import_react24.useRef)(null);
|
|
2038
|
+
const announce = (0, import_react24.useCallback)((text) => {
|
|
2039
|
+
setAnnounced((cur) => cur ? `${cur} ${text}` : text);
|
|
2040
|
+
if (clearTimer.current != null) clearTimeout(clearTimer.current);
|
|
2041
|
+
clearTimer.current = window.setTimeout(() => setAnnounced(""), 3e3);
|
|
2042
|
+
}, []);
|
|
2043
|
+
(0, import_react24.useEffect)(() => () => {
|
|
2044
|
+
if (clearTimer.current != null) clearTimeout(clearTimer.current);
|
|
2045
|
+
}, []);
|
|
2046
|
+
const ctx = (0, import_react24.useMemo)(() => ({ announce }), [announce]);
|
|
2047
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ToasterContext.Provider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: cx("uix-toaster", className), role: "region", "aria-label": "Notifications", ...props, children: [
|
|
2048
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "uix-visually-hidden", role: "status", "aria-live": "polite", children: announced }),
|
|
2049
|
+
children
|
|
2050
|
+
] }) });
|
|
1836
2051
|
}
|
|
1837
2052
|
|
|
1838
2053
|
// src/components/Table.tsx
|
|
1839
|
-
var
|
|
2054
|
+
var import_react25 = require("react");
|
|
1840
2055
|
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1841
|
-
var TableWrap = (0,
|
|
1842
|
-
return /* @__PURE__ */ (0, import_jsx_runtime32.
|
|
2056
|
+
var TableWrap = (0, import_react25.forwardRef)(function TableWrap2({ announcement, children, className, ...props }, ref) {
|
|
2057
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { ref, className: cx("uix-table-wrap", className), ...props, children: [
|
|
2058
|
+
announcement != null && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { role: "status", className: "uix-visually-hidden", children: announcement }),
|
|
2059
|
+
children
|
|
2060
|
+
] });
|
|
1843
2061
|
});
|
|
1844
2062
|
function Table({
|
|
1845
2063
|
density = "standard",
|
|
@@ -1878,9 +2096,9 @@ function Th({ sortable, sortDirection, sortOrder, onSort, className, children, o
|
|
|
1878
2096
|
"data-sort": sortable || void 0,
|
|
1879
2097
|
"data-sort-order": sortOrder != null && sortOrder > 0 ? sortOrder : void 0,
|
|
1880
2098
|
"aria-sort": sortDirection && sortDirection !== "none" ? sortDirection : void 0,
|
|
1881
|
-
onClick: sortable
|
|
2099
|
+
onClick: sortable && onSort ? void 0 : onClick,
|
|
1882
2100
|
...props,
|
|
1883
|
-
children
|
|
2101
|
+
children: sortable ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("button", { type: "button", className: "uix-th__sortbtn", onClick: onSort, children }) : children
|
|
1884
2102
|
}
|
|
1885
2103
|
);
|
|
1886
2104
|
}
|
|
@@ -1892,7 +2110,7 @@ function Tr({ selected, pinned, className, children, ...props }) {
|
|
|
1892
2110
|
"tr",
|
|
1893
2111
|
{
|
|
1894
2112
|
className,
|
|
1895
|
-
"
|
|
2113
|
+
"data-selected": selected || void 0,
|
|
1896
2114
|
"data-pinned": pinned || void 0,
|
|
1897
2115
|
...props,
|
|
1898
2116
|
children
|
|
@@ -1915,13 +2133,14 @@ function RowActions({ children, className, ...props }) {
|
|
|
1915
2133
|
function RowAction({ children, className, type = "button", ...props }) {
|
|
1916
2134
|
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("button", { type, className: cx("uix-rowact", className), ...props, children });
|
|
1917
2135
|
}
|
|
1918
|
-
function ExpandToggle({ expanded, className, type = "button", ...props }) {
|
|
2136
|
+
function ExpandToggle({ expanded, controls, className, type = "button", ...props }) {
|
|
1919
2137
|
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
1920
2138
|
"button",
|
|
1921
2139
|
{
|
|
1922
2140
|
type,
|
|
1923
2141
|
className: cx("uix-table__expand", className),
|
|
1924
2142
|
"aria-expanded": expanded,
|
|
2143
|
+
"aria-controls": controls,
|
|
1925
2144
|
"aria-label": expanded ? "Collapse row" : "Expand row",
|
|
1926
2145
|
...props,
|
|
1927
2146
|
children: /* @__PURE__ */ (0, import_jsx_runtime32.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_runtime32.jsx)("path", { d: "M6 4l4 4-4 4" }) })
|
|
@@ -2012,15 +2231,17 @@ function StatusPill({ tone = "neutral", treatment = "filled", dot, children, cla
|
|
|
2012
2231
|
|
|
2013
2232
|
// src/components/Progress.tsx
|
|
2014
2233
|
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
2015
|
-
function Progress({ value = 0, max = 100, indeterminate, className, ...props }) {
|
|
2016
|
-
const
|
|
2234
|
+
function Progress({ value = 0, max = 100, indeterminate, label, className, ...props }) {
|
|
2235
|
+
const clamped = Math.max(0, Math.min(max || 100, value));
|
|
2236
|
+
const pct = clamped / (max || 100) * 100;
|
|
2017
2237
|
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
2018
2238
|
"div",
|
|
2019
2239
|
{
|
|
2020
2240
|
role: "progressbar",
|
|
2021
2241
|
"aria-valuemin": 0,
|
|
2022
2242
|
"aria-valuemax": max,
|
|
2023
|
-
"aria-valuenow": indeterminate ? void 0 :
|
|
2243
|
+
"aria-valuenow": indeterminate ? void 0 : clamped,
|
|
2244
|
+
"aria-label": label,
|
|
2024
2245
|
className: cx("uix-progress", indeterminate && "uix-progress--indeterminate", className),
|
|
2025
2246
|
...props,
|
|
2026
2247
|
children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "uix-progress__bar", style: indeterminate ? void 0 : { width: `${pct}%` } })
|
|
@@ -2030,7 +2251,13 @@ function Progress({ value = 0, max = 100, indeterminate, className, ...props })
|
|
|
2030
2251
|
|
|
2031
2252
|
// src/components/Meter.tsx
|
|
2032
2253
|
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
2033
|
-
|
|
2254
|
+
var toneText = {
|
|
2255
|
+
warning: "warning",
|
|
2256
|
+
danger: "critical",
|
|
2257
|
+
attention: "needs attention",
|
|
2258
|
+
overdue: "overdue"
|
|
2259
|
+
};
|
|
2260
|
+
function Meter({ value = 0, tone, label, className, ...props }) {
|
|
2034
2261
|
const pct = Math.max(0, Math.min(100, value));
|
|
2035
2262
|
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
2036
2263
|
"div",
|
|
@@ -2039,6 +2266,8 @@ function Meter({ value = 0, tone, className, ...props }) {
|
|
|
2039
2266
|
"aria-valuenow": pct,
|
|
2040
2267
|
"aria-valuemin": 0,
|
|
2041
2268
|
"aria-valuemax": 100,
|
|
2269
|
+
"aria-valuetext": tone && tone !== "success" ? `${pct}%, ${toneText[tone]}` : void 0,
|
|
2270
|
+
"aria-label": label,
|
|
2042
2271
|
className: cx("uix-meter", className),
|
|
2043
2272
|
...props,
|
|
2044
2273
|
children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
@@ -2063,22 +2292,34 @@ function Stat({ label, value, icon, meta, trend, className, ...props }) {
|
|
|
2063
2292
|
] }),
|
|
2064
2293
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "uix-stat__value", children: value }),
|
|
2065
2294
|
(meta != null || trend != null) && /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "uix-stat__meta", children: [
|
|
2066
|
-
trend != null && /* @__PURE__ */ (0, import_jsx_runtime37.
|
|
2295
|
+
trend != null && /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: cx("uix-stat__trend", `uix-stat__trend--${trend.direction}`), children: [
|
|
2296
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.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_runtime37.jsx)("path", { d: "M12 19V5m-6 6 6-6 6 6" }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("path", { d: "M12 5v14m6-6-6 6-6-6" }) }),
|
|
2297
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "uix-visually-hidden", children: trend.direction === "up" ? "up" : "down" }),
|
|
2298
|
+
trend.label
|
|
2299
|
+
] }),
|
|
2067
2300
|
meta
|
|
2068
2301
|
] })
|
|
2069
2302
|
] });
|
|
2070
2303
|
}
|
|
2071
2304
|
|
|
2072
2305
|
// src/components/Tooltip.tsx
|
|
2073
|
-
var
|
|
2306
|
+
var import_react26 = require("react");
|
|
2074
2307
|
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
2075
2308
|
function Tooltip({ label, placement = "top", className, children, ...props }) {
|
|
2076
|
-
const triggerRef = (0,
|
|
2077
|
-
const bubbleRef = (0,
|
|
2078
|
-
const
|
|
2079
|
-
const
|
|
2309
|
+
const triggerRef = (0, import_react26.useRef)(null);
|
|
2310
|
+
const bubbleRef = (0, import_react26.useRef)(null);
|
|
2311
|
+
const closeTimer = (0, import_react26.useRef)(null);
|
|
2312
|
+
const [open, setOpen] = (0, import_react26.useState)(false);
|
|
2313
|
+
const id = (0, import_react26.useId)();
|
|
2080
2314
|
useAnchoredPosition(triggerRef, bubbleRef, { open, placement, offset: 6 });
|
|
2315
|
+
const cancelClose = () => {
|
|
2316
|
+
if (closeTimer.current != null) {
|
|
2317
|
+
clearTimeout(closeTimer.current);
|
|
2318
|
+
closeTimer.current = null;
|
|
2319
|
+
}
|
|
2320
|
+
};
|
|
2081
2321
|
const show = () => {
|
|
2322
|
+
cancelClose();
|
|
2082
2323
|
const b = bubbleRef.current;
|
|
2083
2324
|
if (b && !b.matches(":popover-open")) {
|
|
2084
2325
|
try {
|
|
@@ -2089,6 +2330,7 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
|
|
|
2089
2330
|
setOpen(true);
|
|
2090
2331
|
};
|
|
2091
2332
|
const hide = () => {
|
|
2333
|
+
cancelClose();
|
|
2092
2334
|
const b = bubbleRef.current;
|
|
2093
2335
|
if (b && b.matches(":popover-open")) {
|
|
2094
2336
|
try {
|
|
@@ -2098,7 +2340,20 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
|
|
|
2098
2340
|
}
|
|
2099
2341
|
setOpen(false);
|
|
2100
2342
|
};
|
|
2101
|
-
|
|
2343
|
+
const scheduleHide = () => {
|
|
2344
|
+
cancelClose();
|
|
2345
|
+
closeTimer.current = window.setTimeout(hide, 100);
|
|
2346
|
+
};
|
|
2347
|
+
(0, import_react26.useEffect)(() => {
|
|
2348
|
+
if (!open) return;
|
|
2349
|
+
const onKey = (e) => {
|
|
2350
|
+
if (e.key === "Escape") hide();
|
|
2351
|
+
};
|
|
2352
|
+
document.addEventListener("keydown", onKey);
|
|
2353
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
2354
|
+
}, [open]);
|
|
2355
|
+
(0, import_react26.useEffect)(() => () => {
|
|
2356
|
+
if (closeTimer.current != null) clearTimeout(closeTimer.current);
|
|
2102
2357
|
const b = bubbleRef.current;
|
|
2103
2358
|
if (b && b.matches(":popover-open")) {
|
|
2104
2359
|
try {
|
|
@@ -2108,14 +2363,19 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
|
|
|
2108
2363
|
}
|
|
2109
2364
|
}, []);
|
|
2110
2365
|
const bubbleAttr = { popover: "manual" };
|
|
2366
|
+
const single = (0, import_react26.isValidElement)(children);
|
|
2367
|
+
const child = single ? (0, import_react26.cloneElement)(children, {
|
|
2368
|
+
"aria-describedby": [children.props["aria-describedby"], id].filter(Boolean).join(" ")
|
|
2369
|
+
}) : children;
|
|
2111
2370
|
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
|
|
2112
2371
|
"span",
|
|
2113
2372
|
{
|
|
2114
2373
|
ref: triggerRef,
|
|
2115
2374
|
className: cx("uix-tooltip", className),
|
|
2116
|
-
"aria-describedby": id,
|
|
2375
|
+
"aria-describedby": single ? void 0 : id,
|
|
2376
|
+
tabIndex: single ? void 0 : 0,
|
|
2117
2377
|
onMouseEnter: show,
|
|
2118
|
-
onMouseLeave:
|
|
2378
|
+
onMouseLeave: scheduleHide,
|
|
2119
2379
|
onFocus: show,
|
|
2120
2380
|
onBlur: hide,
|
|
2121
2381
|
onKeyDown: (e) => {
|
|
@@ -2123,8 +2383,20 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
|
|
|
2123
2383
|
},
|
|
2124
2384
|
...props,
|
|
2125
2385
|
children: [
|
|
2126
|
-
|
|
2127
|
-
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2386
|
+
child,
|
|
2387
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2388
|
+
"span",
|
|
2389
|
+
{
|
|
2390
|
+
ref: bubbleRef,
|
|
2391
|
+
id,
|
|
2392
|
+
role: "tooltip",
|
|
2393
|
+
className: "uix-tooltip__bubble",
|
|
2394
|
+
onMouseEnter: cancelClose,
|
|
2395
|
+
onMouseLeave: scheduleHide,
|
|
2396
|
+
...bubbleAttr,
|
|
2397
|
+
children: label
|
|
2398
|
+
}
|
|
2399
|
+
)
|
|
2128
2400
|
]
|
|
2129
2401
|
}
|
|
2130
2402
|
);
|
|
@@ -2184,11 +2456,17 @@ function Skeleton({ variant = "block", width, height, className, style, ...props
|
|
|
2184
2456
|
);
|
|
2185
2457
|
}
|
|
2186
2458
|
function LoadingState({ rows = 3, density = "comfortable", label, className, ...props }) {
|
|
2187
|
-
return
|
|
2459
|
+
return (
|
|
2460
|
+
// no aria-busy: busy=true tells AT the region isn't ready and suppresses the announcement (UIX-A11Y-4)
|
|
2461
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: cx("uix-stack", density === "compact" && "uix-stack--compact", className), role: "status", ...props, children: [
|
|
2462
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "uix-visually-hidden", children: label ?? "Loading" }),
|
|
2463
|
+
Array.from({ length: rows }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Skeleton, { variant: "text", width: `${92 - i % 3 * 14}%` }, i))
|
|
2464
|
+
] })
|
|
2465
|
+
);
|
|
2188
2466
|
}
|
|
2189
2467
|
|
|
2190
2468
|
// src/components/RelativeTime.tsx
|
|
2191
|
-
var
|
|
2469
|
+
var import_react27 = require("react");
|
|
2192
2470
|
|
|
2193
2471
|
// src/relative-time-model.ts
|
|
2194
2472
|
function parseDateValue(value) {
|
|
@@ -2251,12 +2529,12 @@ function RelativeTime({
|
|
|
2251
2529
|
}) {
|
|
2252
2530
|
const interval = refreshInterval === false ? 0 : Math.max(0, refreshInterval);
|
|
2253
2531
|
const fixedNow = now == null ? void 0 : new Date(now).getTime();
|
|
2254
|
-
const subscribe = (0,
|
|
2532
|
+
const subscribe = (0, import_react27.useCallback)((listener) => fixedNow == null ? subscribeRelativeClock(interval, listener) : () => {
|
|
2255
2533
|
}, [fixedNow, interval]);
|
|
2256
|
-
const getSnapshot = (0,
|
|
2257
|
-
const getServerSnapshot = (0,
|
|
2258
|
-
const clock = (0,
|
|
2259
|
-
const date = (0,
|
|
2534
|
+
const getSnapshot = (0, import_react27.useCallback)(() => fixedNow ?? readRelativeClock(interval), [fixedNow, interval]);
|
|
2535
|
+
const getServerSnapshot = (0, import_react27.useCallback)(() => fixedNow ?? Date.now(), [fixedNow]);
|
|
2536
|
+
const clock = (0, import_react27.useSyncExternalStore)(subscribe, getSnapshot, getServerSnapshot);
|
|
2537
|
+
const date = (0, import_react27.useMemo)(() => parseDateValue(value), [value]);
|
|
2260
2538
|
if (!date) return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("time", { "data-invalid": "true", suppressHydrationWarning: true, ...props, children: invalidLabel });
|
|
2261
2539
|
const current = new Date(clock);
|
|
2262
2540
|
const content = formatter ? formatter({ date, now: current, locale, timeZone }) : (() => {
|
|
@@ -2305,7 +2583,8 @@ var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
|
2305
2583
|
function Avatar({ size = "md", src, alt, children, status, className, ...props }) {
|
|
2306
2584
|
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: cx("uix-avatar", size !== "md" && `uix-avatar--${size}`, className), ...props, children: [
|
|
2307
2585
|
src ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("img", { src, alt: alt ?? "" }) : children,
|
|
2308
|
-
status && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "uix-avatar__status" })
|
|
2586
|
+
status && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "uix-avatar__status", "aria-hidden": "true" }),
|
|
2587
|
+
status && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "uix-visually-hidden", children: "online" })
|
|
2309
2588
|
] });
|
|
2310
2589
|
}
|
|
2311
2590
|
function AvatarGroup({ children, className, ...props }) {
|
|
@@ -2325,7 +2604,7 @@ function UserChip({ avatar, name, sub, className, children, ...props }) {
|
|
|
2325
2604
|
// src/components/Comments.tsx
|
|
2326
2605
|
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
2327
2606
|
function Comments({ children, className, ...props }) {
|
|
2328
|
-
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: cx("uix-comments", className), ...props, children });
|
|
2607
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { role: "log", className: cx("uix-comments", className), ...props, children });
|
|
2329
2608
|
}
|
|
2330
2609
|
function Comment({ avatar, author, meta, children, className, ...props }) {
|
|
2331
2610
|
return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: cx("uix-comment", className), ...props, children: [
|
|
@@ -2342,20 +2621,25 @@ function Comment({ avatar, author, meta, children, className, ...props }) {
|
|
|
2342
2621
|
}
|
|
2343
2622
|
|
|
2344
2623
|
// src/components/Composer.tsx
|
|
2624
|
+
var import_react28 = require("react");
|
|
2345
2625
|
var import_jsx_runtime45 = require("react/jsx-runtime");
|
|
2346
|
-
function Composer({ children, className, ...props }) {
|
|
2347
|
-
return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: cx("uix-composer", className), ...props, children
|
|
2626
|
+
function Composer({ children, className, label = "Add a comment", ...props }) {
|
|
2627
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: cx("uix-composer", className), ...props, children: import_react28.Children.map(children, (child) => {
|
|
2628
|
+
if (!(0, import_react28.isValidElement)(child) || child.type !== "textarea") return child;
|
|
2629
|
+
const p = child.props;
|
|
2630
|
+
return p["aria-label"] || p["aria-labelledby"] || p.id ? child : (0, import_react28.cloneElement)(child, { "aria-label": label });
|
|
2631
|
+
}) });
|
|
2348
2632
|
}
|
|
2349
2633
|
function ComposerBar({ children, className, ...props }) {
|
|
2350
2634
|
return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: cx("uix-composer__bar", className), ...props, children });
|
|
2351
2635
|
}
|
|
2352
2636
|
|
|
2353
2637
|
// src/components/Segmented.tsx
|
|
2354
|
-
var
|
|
2638
|
+
var import_react29 = require("react");
|
|
2355
2639
|
var import_jsx_runtime46 = require("react/jsx-runtime");
|
|
2356
|
-
var SegmentedCtx = (0,
|
|
2640
|
+
var SegmentedCtx = (0, import_react29.createContext)({});
|
|
2357
2641
|
function Segmented({ value, onChange, children, className, ...props }) {
|
|
2358
|
-
const contextValue = (0,
|
|
2642
|
+
const contextValue = (0, import_react29.useMemo)(() => ({ value, onChange }), [value, onChange]);
|
|
2359
2643
|
return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(SegmentedCtx.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { role: "group", className: cx("uix-segmented", className), ...props, children }) });
|
|
2360
2644
|
}
|
|
2361
2645
|
function SegmentedOption({
|
|
@@ -2365,7 +2649,7 @@ function SegmentedOption({
|
|
|
2365
2649
|
onClick,
|
|
2366
2650
|
...props
|
|
2367
2651
|
}) {
|
|
2368
|
-
const ctx = (0,
|
|
2652
|
+
const ctx = (0, import_react29.useContext)(SegmentedCtx);
|
|
2369
2653
|
const selected = ctx.value === value;
|
|
2370
2654
|
return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
2371
2655
|
"button",
|
|
@@ -2384,12 +2668,12 @@ function SegmentedOption({
|
|
|
2384
2668
|
}
|
|
2385
2669
|
|
|
2386
2670
|
// src/components/TableControls.tsx
|
|
2387
|
-
var
|
|
2671
|
+
var import_react30 = require("react");
|
|
2388
2672
|
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
2389
2673
|
function ViewMenu({ density, densityLabel, densityOptions, onDensityChange, zebra, freeze, columns, columnsLabel, onColumnVisibilityChange }) {
|
|
2390
|
-
const densityLabelId = (0,
|
|
2391
|
-
const zebraLabelId = (0,
|
|
2392
|
-
const freezeLabelId = (0,
|
|
2674
|
+
const densityLabelId = (0, import_react30.useId)();
|
|
2675
|
+
const zebraLabelId = (0, import_react30.useId)();
|
|
2676
|
+
const freezeLabelId = (0, import_react30.useId)();
|
|
2393
2677
|
return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "uix-view-menu", children: [
|
|
2394
2678
|
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "uix-view-menu__group", children: [
|
|
2395
2679
|
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { id: densityLabelId, className: "uix-view-menu__label", children: densityLabel }),
|
|
@@ -2438,11 +2722,11 @@ function SavedViewMenu({ items, onSelect, onPinChange, pinLabel, unpinLabel, emp
|
|
|
2438
2722
|
// src/components/Timeline.tsx
|
|
2439
2723
|
var import_jsx_runtime48 = require("react/jsx-runtime");
|
|
2440
2724
|
function Timeline({ children, className, ...props }) {
|
|
2441
|
-
return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("
|
|
2725
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("ol", { className: cx("uix-timeline", className), ...props, children });
|
|
2442
2726
|
}
|
|
2443
2727
|
function TimelineItem({ node, meta, children, className, ...props }) {
|
|
2444
|
-
return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("
|
|
2445
|
-
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "uix-timeline__rail", children: [
|
|
2728
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("li", { className: cx("uix-timeline__item", className), ...props, children: [
|
|
2729
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "uix-timeline__rail", "aria-hidden": "true", children: [
|
|
2446
2730
|
node ?? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "uix-timeline__node" }),
|
|
2447
2731
|
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "uix-timeline__line" })
|
|
2448
2732
|
] }),
|
|
@@ -2454,10 +2738,10 @@ function TimelineItem({ node, meta, children, className, ...props }) {
|
|
|
2454
2738
|
}
|
|
2455
2739
|
|
|
2456
2740
|
// src/components/Pipeline.tsx
|
|
2457
|
-
var
|
|
2741
|
+
var import_react31 = require("react");
|
|
2458
2742
|
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
2459
2743
|
function Pipeline({ detailed = false, children, className, tabIndex, ...props }) {
|
|
2460
|
-
const stages = detailed ?
|
|
2744
|
+
const stages = detailed ? import_react31.Children.map(children, (child) => (0, import_react31.isValidElement)(child) && child.type === PipelineStage ? (0, import_react31.cloneElement)(child, { detailed: child.props.detailed ?? true }) : child) : children;
|
|
2461
2745
|
return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2462
2746
|
"ol",
|
|
2463
2747
|
{
|
|
@@ -2573,7 +2857,7 @@ function Note({ tone, icon, children, className, ...props }) {
|
|
|
2573
2857
|
}
|
|
2574
2858
|
|
|
2575
2859
|
// src/components/Popover.tsx
|
|
2576
|
-
var
|
|
2860
|
+
var import_react32 = require("react");
|
|
2577
2861
|
var import_jsx_runtime52 = require("react/jsx-runtime");
|
|
2578
2862
|
function Popover({
|
|
2579
2863
|
popover = "auto",
|
|
@@ -2584,15 +2868,15 @@ function Popover({
|
|
|
2584
2868
|
children,
|
|
2585
2869
|
...props
|
|
2586
2870
|
}) {
|
|
2587
|
-
const ref = (0,
|
|
2588
|
-
const [open, setOpen] = (0,
|
|
2871
|
+
const ref = (0, import_react32.useRef)(null);
|
|
2872
|
+
const [open, setOpen] = (0, import_react32.useState)(false);
|
|
2589
2873
|
const popoverAttr = { popover };
|
|
2590
2874
|
const reposition = useAnchoredPosition(anchor ?? null, ref, {
|
|
2591
2875
|
open: open && !!anchor,
|
|
2592
2876
|
placement,
|
|
2593
2877
|
offset
|
|
2594
2878
|
});
|
|
2595
|
-
(0,
|
|
2879
|
+
(0, import_react32.useEffect)(() => {
|
|
2596
2880
|
const el = ref.current;
|
|
2597
2881
|
if (!el || !anchor) return;
|
|
2598
2882
|
const sync = () => {
|
|
@@ -2607,34 +2891,138 @@ function Popover({
|
|
|
2607
2891
|
}
|
|
2608
2892
|
|
|
2609
2893
|
// src/components/CommandPalette.tsx
|
|
2894
|
+
var import_react33 = require("react");
|
|
2610
2895
|
var import_jsx_runtime53 = require("react/jsx-runtime");
|
|
2896
|
+
var CmdkContext = (0, import_react33.createContext)(null);
|
|
2611
2897
|
function CommandPalette({ inputProps, children, className, ...props }) {
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
]
|
|
2898
|
+
const listId = (0, import_react33.useId)();
|
|
2899
|
+
const [activeId, setActiveId] = (0, import_react33.useState)(null);
|
|
2900
|
+
const [count, setCount] = (0, import_react33.useState)(0);
|
|
2901
|
+
const [announced, setAnnounced] = (0, import_react33.useState)("");
|
|
2902
|
+
const setActive = (0, import_react33.useCallback)((id, active) => {
|
|
2903
|
+
setActiveId((cur) => active ? id : cur === id ? null : cur);
|
|
2904
|
+
}, []);
|
|
2905
|
+
const registerItem = (0, import_react33.useCallback)(() => {
|
|
2906
|
+
setCount((c) => c + 1);
|
|
2907
|
+
return () => setCount((c) => c - 1);
|
|
2908
|
+
}, []);
|
|
2909
|
+
(0, import_react33.useEffect)(() => {
|
|
2910
|
+
const t = window.setTimeout(() => setAnnounced(`${count} result${count === 1 ? "" : "s"}`), 150);
|
|
2911
|
+
return () => clearTimeout(t);
|
|
2912
|
+
}, [count]);
|
|
2913
|
+
const ctx = (0, import_react33.useMemo)(() => ({ listId, setActive, registerItem }), [listId, setActive, registerItem]);
|
|
2914
|
+
return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(CmdkContext.Provider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: cx("uix-cmdk", className), ...props, children: [
|
|
2915
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
2916
|
+
"input",
|
|
2917
|
+
{
|
|
2918
|
+
className: "uix-cmdk__input",
|
|
2919
|
+
role: "combobox",
|
|
2920
|
+
"aria-expanded": "true",
|
|
2921
|
+
"aria-autocomplete": "list",
|
|
2922
|
+
"aria-controls": listId,
|
|
2923
|
+
"aria-activedescendant": activeId ?? void 0,
|
|
2924
|
+
"aria-label": "Command palette",
|
|
2925
|
+
...inputProps
|
|
2926
|
+
}
|
|
2927
|
+
),
|
|
2928
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "uix-cmdk__list", role: "listbox", id: listId, children }),
|
|
2929
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "uix-visually-hidden", role: "status", children: announced })
|
|
2930
|
+
] }) });
|
|
2616
2931
|
}
|
|
2617
2932
|
function CommandGroup({ label, children }) {
|
|
2618
|
-
|
|
2619
|
-
|
|
2933
|
+
const labelId = (0, import_react33.useId)();
|
|
2934
|
+
return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { role: "group", "aria-labelledby": label != null ? labelId : void 0, children: [
|
|
2935
|
+
label != null && /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "uix-cmdk__group", id: labelId, children: label }),
|
|
2620
2936
|
children
|
|
2621
2937
|
] });
|
|
2622
2938
|
}
|
|
2623
|
-
function CommandItem({ icon, shortcut, active, children, className, ...props }) {
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2939
|
+
function CommandItem({ icon, shortcut, active, children, className, id, ...props }) {
|
|
2940
|
+
const ctx = (0, import_react33.useContext)(CmdkContext);
|
|
2941
|
+
const autoId = (0, import_react33.useId)();
|
|
2942
|
+
const itemId = id ?? autoId;
|
|
2943
|
+
(0, import_react33.useEffect)(() => ctx?.registerItem(), [ctx]);
|
|
2944
|
+
(0, import_react33.useEffect)(() => {
|
|
2945
|
+
if (!ctx || !active) return;
|
|
2946
|
+
ctx.setActive(itemId, true);
|
|
2947
|
+
return () => ctx.setActive(itemId, false);
|
|
2948
|
+
}, [ctx, itemId, active]);
|
|
2949
|
+
return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
2950
|
+
"div",
|
|
2951
|
+
{
|
|
2952
|
+
className: cx("uix-cmdk__item", className),
|
|
2953
|
+
"data-active": active || void 0,
|
|
2954
|
+
id: itemId,
|
|
2955
|
+
role: "option",
|
|
2956
|
+
"aria-selected": !!active,
|
|
2957
|
+
...props,
|
|
2958
|
+
children: [
|
|
2959
|
+
icon,
|
|
2960
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { children }),
|
|
2961
|
+
shortcut
|
|
2962
|
+
]
|
|
2963
|
+
}
|
|
2964
|
+
);
|
|
2629
2965
|
}
|
|
2630
2966
|
|
|
2631
2967
|
// src/components/Inbox.tsx
|
|
2968
|
+
var import_react34 = require("react");
|
|
2632
2969
|
var import_jsx_runtime54 = require("react/jsx-runtime");
|
|
2633
|
-
function Inbox({ children, className, ...props }) {
|
|
2634
|
-
return /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("div", { className: cx("uix-inbox", className), ...props, children });
|
|
2970
|
+
function Inbox({ view, children, className, ...props }) {
|
|
2971
|
+
return /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("div", { className: cx("uix-inbox", className), "data-view": view, ...props, children });
|
|
2635
2972
|
}
|
|
2973
|
+
var InboxListCtx = (0, import_react34.createContext)({});
|
|
2636
2974
|
function InboxList({ children, className, ...props }) {
|
|
2637
|
-
|
|
2975
|
+
const listRef = (0, import_react34.useRef)(null);
|
|
2976
|
+
const [activeId, setActiveId] = (0, import_react34.useState)(void 0);
|
|
2977
|
+
const ctx = (0, import_react34.useMemo)(() => ({ activeId }), [activeId]);
|
|
2978
|
+
const onKeyDown = (e) => {
|
|
2979
|
+
const opts = Array.from(listRef.current?.querySelectorAll('[role="option"]') ?? []);
|
|
2980
|
+
if (opts.length === 0) return;
|
|
2981
|
+
const idx = opts.findIndex((el2) => el2.id === activeId);
|
|
2982
|
+
let next;
|
|
2983
|
+
switch (e.key) {
|
|
2984
|
+
case "ArrowDown":
|
|
2985
|
+
next = Math.min(idx + 1, opts.length - 1);
|
|
2986
|
+
break;
|
|
2987
|
+
case "ArrowUp":
|
|
2988
|
+
next = idx < 0 ? 0 : Math.max(idx - 1, 0);
|
|
2989
|
+
break;
|
|
2990
|
+
case "Home":
|
|
2991
|
+
next = 0;
|
|
2992
|
+
break;
|
|
2993
|
+
case "End":
|
|
2994
|
+
next = opts.length - 1;
|
|
2995
|
+
break;
|
|
2996
|
+
case "Enter":
|
|
2997
|
+
case " ":
|
|
2998
|
+
if (idx >= 0) {
|
|
2999
|
+
e.preventDefault();
|
|
3000
|
+
opts[idx]?.click();
|
|
3001
|
+
}
|
|
3002
|
+
return;
|
|
3003
|
+
default:
|
|
3004
|
+
return;
|
|
3005
|
+
}
|
|
3006
|
+
e.preventDefault();
|
|
3007
|
+
const el = opts[next];
|
|
3008
|
+
if (!el || !el.id) return;
|
|
3009
|
+
setActiveId(el.id);
|
|
3010
|
+
el.scrollIntoView({ block: "nearest" });
|
|
3011
|
+
};
|
|
3012
|
+
return /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(InboxListCtx.Provider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
3013
|
+
"div",
|
|
3014
|
+
{
|
|
3015
|
+
ref: listRef,
|
|
3016
|
+
className: cx("uix-inbox__list", className),
|
|
3017
|
+
role: "listbox",
|
|
3018
|
+
tabIndex: 0,
|
|
3019
|
+
"aria-label": "Inbox",
|
|
3020
|
+
"aria-activedescendant": activeId,
|
|
3021
|
+
onKeyDown,
|
|
3022
|
+
...props,
|
|
3023
|
+
children
|
|
3024
|
+
}
|
|
3025
|
+
) });
|
|
2638
3026
|
}
|
|
2639
3027
|
function InboxItem({
|
|
2640
3028
|
subject,
|
|
@@ -2644,13 +3032,19 @@ function InboxItem({
|
|
|
2644
3032
|
selected,
|
|
2645
3033
|
children,
|
|
2646
3034
|
className,
|
|
3035
|
+
id,
|
|
2647
3036
|
...props
|
|
2648
3037
|
}) {
|
|
3038
|
+
const autoId = (0, import_react34.useId)();
|
|
3039
|
+
const itemId = id ?? autoId;
|
|
3040
|
+
const { activeId } = (0, import_react34.useContext)(InboxListCtx);
|
|
2649
3041
|
return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
|
|
2650
3042
|
"div",
|
|
2651
3043
|
{
|
|
2652
3044
|
role: "option",
|
|
3045
|
+
id: itemId,
|
|
2653
3046
|
"aria-selected": selected ?? false,
|
|
3047
|
+
"data-active": activeId === itemId ? "" : void 0,
|
|
2654
3048
|
className: cx("uix-inbox__item", className),
|
|
2655
3049
|
...unread ? { "data-unread": "" } : {},
|
|
2656
3050
|
...props,
|
|
@@ -2668,29 +3062,60 @@ function InboxDetail({ children, className, ...props }) {
|
|
|
2668
3062
|
}
|
|
2669
3063
|
|
|
2670
3064
|
// src/components/Kanban.tsx
|
|
3065
|
+
var import_react35 = require("react");
|
|
2671
3066
|
var import_jsx_runtime55 = require("react/jsx-runtime");
|
|
2672
|
-
function Kanban({ children, className, ...props }) {
|
|
2673
|
-
return /* @__PURE__ */ (0, import_jsx_runtime55.
|
|
3067
|
+
function Kanban({ announcement, children, className, ...props }) {
|
|
3068
|
+
return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: cx("uix-kanban", className), ...props, children: [
|
|
3069
|
+
announcement != null && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { role: "status", className: "uix-visually-hidden", children: announcement }),
|
|
3070
|
+
children
|
|
3071
|
+
] });
|
|
2674
3072
|
}
|
|
2675
3073
|
function KanbanColumn({ title, count, children, className, ...props }) {
|
|
3074
|
+
const headId = (0, import_react35.useId)();
|
|
3075
|
+
const hasHead = title != null || count != null;
|
|
2676
3076
|
return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: cx("uix-kanban__col", className), ...props, children: [
|
|
2677
|
-
|
|
3077
|
+
hasHead && /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "uix-kanban__head", id: headId, children: [
|
|
2678
3078
|
title,
|
|
2679
3079
|
count != null && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "uix-kanban__count", children: count })
|
|
2680
3080
|
] }),
|
|
2681
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "uix-kanban__body", children })
|
|
3081
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "uix-kanban__body", role: "list", "aria-labelledby": hasHead ? headId : void 0, children })
|
|
2682
3082
|
] });
|
|
2683
3083
|
}
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
3084
|
+
var MOVE_KEYS = {
|
|
3085
|
+
ArrowUp: "up",
|
|
3086
|
+
ArrowDown: "down",
|
|
3087
|
+
ArrowLeft: "left",
|
|
3088
|
+
ArrowRight: "right"
|
|
3089
|
+
};
|
|
3090
|
+
function KanbanCard({ title, meta, children, className, onMove, onKeyDown, ...props }) {
|
|
3091
|
+
const handleKeyDown = (e) => {
|
|
3092
|
+
onKeyDown?.(e);
|
|
3093
|
+
if (!onMove || !e.altKey || e.defaultPrevented) return;
|
|
3094
|
+
const dir = MOVE_KEYS[e.key];
|
|
3095
|
+
if (dir) {
|
|
3096
|
+
e.preventDefault();
|
|
3097
|
+
onMove(dir);
|
|
3098
|
+
}
|
|
3099
|
+
};
|
|
3100
|
+
return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
|
|
3101
|
+
"div",
|
|
3102
|
+
{
|
|
3103
|
+
role: "listitem",
|
|
3104
|
+
tabIndex: 0,
|
|
3105
|
+
className: cx("uix-kanban__card", className),
|
|
3106
|
+
onKeyDown: handleKeyDown,
|
|
3107
|
+
...props,
|
|
3108
|
+
children: [
|
|
3109
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "uix-kanban__card-title", children: title }),
|
|
3110
|
+
meta && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "uix-kanban__card-meta", children: meta }),
|
|
3111
|
+
children
|
|
3112
|
+
]
|
|
3113
|
+
}
|
|
3114
|
+
);
|
|
2690
3115
|
}
|
|
2691
3116
|
|
|
2692
3117
|
// src/components/Tree.tsx
|
|
2693
|
-
var
|
|
3118
|
+
var import_react36 = require("react");
|
|
2694
3119
|
|
|
2695
3120
|
// src/tree-model.ts
|
|
2696
3121
|
function flattenTree(nodes, expanded, level = 1) {
|
|
@@ -2735,6 +3160,14 @@ function treeNav(flat, currentId, key) {
|
|
|
2735
3160
|
case " ":
|
|
2736
3161
|
return cur.hasChildren ? { toggleId: cur.node.id, selectId: cur.node.id } : { selectId: cur.node.id };
|
|
2737
3162
|
default:
|
|
3163
|
+
if (key.length === 1) {
|
|
3164
|
+
const q = key.toLowerCase();
|
|
3165
|
+
for (let step = 1; step <= flat.length; step++) {
|
|
3166
|
+
const f = flat[(idx + step) % flat.length];
|
|
3167
|
+
const label = f.node.typeaheadLabel ?? (typeof f.node.label === "string" ? f.node.label : void 0);
|
|
3168
|
+
if (label != null && label.toLowerCase().startsWith(q)) return { focusId: f.node.id };
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
2738
3171
|
return {};
|
|
2739
3172
|
}
|
|
2740
3173
|
}
|
|
@@ -2802,12 +3235,12 @@ function Tree({
|
|
|
2802
3235
|
className,
|
|
2803
3236
|
...props
|
|
2804
3237
|
}) {
|
|
2805
|
-
const [internalExpanded, setInternalExpanded] = (0,
|
|
3238
|
+
const [internalExpanded, setInternalExpanded] = (0, import_react36.useState)(defaultExpanded ?? /* @__PURE__ */ new Set());
|
|
2806
3239
|
const expanded = controlledExpanded ?? internalExpanded;
|
|
2807
|
-
const rootRef = (0,
|
|
2808
|
-
const [focusedId, setFocusedId] = (0,
|
|
3240
|
+
const rootRef = (0, import_react36.useRef)(null);
|
|
3241
|
+
const [focusedId, setFocusedId] = (0, import_react36.useState)(void 0);
|
|
2809
3242
|
const selectable = selected !== void 0 || onSelect != null;
|
|
2810
|
-
const handleToggle = (0,
|
|
3243
|
+
const handleToggle = (0, import_react36.useCallback)((id) => {
|
|
2811
3244
|
if (!controlledExpanded) {
|
|
2812
3245
|
setInternalExpanded((prev) => {
|
|
2813
3246
|
const next = new Set(prev);
|
|
@@ -2818,7 +3251,7 @@ function Tree({
|
|
|
2818
3251
|
}
|
|
2819
3252
|
onToggle?.(id);
|
|
2820
3253
|
}, [controlledExpanded, onToggle]);
|
|
2821
|
-
const flat = (0,
|
|
3254
|
+
const flat = (0, import_react36.useMemo)(() => flattenTree(nodes, expanded), [nodes, expanded]);
|
|
2822
3255
|
const useVirtual = virtualize ?? shouldVirtualize(flat.length);
|
|
2823
3256
|
const tabbableId = focusedId ?? selected ?? nodes[0]?.id;
|
|
2824
3257
|
if (useVirtual) {
|
|
@@ -2889,6 +3322,13 @@ function Tree({
|
|
|
2889
3322
|
setFocusedId(id);
|
|
2890
3323
|
break;
|
|
2891
3324
|
default:
|
|
3325
|
+
if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
3326
|
+
const act = treeNav(flat, id, e.key);
|
|
3327
|
+
if (act.focusId !== void 0) {
|
|
3328
|
+
e.preventDefault();
|
|
3329
|
+
focusItem(rootRef.current?.querySelector(`[role="treeitem"][data-id="${CSS.escape(act.focusId)}"]`));
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
2892
3332
|
break;
|
|
2893
3333
|
}
|
|
2894
3334
|
};
|
|
@@ -2922,12 +3362,14 @@ function VirtualTreeView({
|
|
|
2922
3362
|
className,
|
|
2923
3363
|
...props
|
|
2924
3364
|
}) {
|
|
2925
|
-
const scrollRef = (0,
|
|
2926
|
-
const [scrollTop, setScrollTop] = (0,
|
|
2927
|
-
const pendingFocus = (0,
|
|
3365
|
+
const scrollRef = (0, import_react36.useRef)(null);
|
|
3366
|
+
const [scrollTop, setScrollTop] = (0, import_react36.useState)(0);
|
|
3367
|
+
const pendingFocus = (0, import_react36.useRef)(false);
|
|
3368
|
+
const hadFocus = (0, import_react36.useRef)(false);
|
|
2928
3369
|
const win = virtualWindow(scrollTop, maxHeight, rowHeight, flat.length);
|
|
2929
3370
|
const rows = flat.slice(win.start, win.end);
|
|
2930
|
-
|
|
3371
|
+
const windowHasTabbable = tabbableId != null && rows.some((f) => f.node.id === tabbableId);
|
|
3372
|
+
(0, import_react36.useEffect)(() => {
|
|
2931
3373
|
if (!pendingFocus.current || focusedId == null) return;
|
|
2932
3374
|
const el = scrollRef.current?.querySelector(`[role="treeitem"][data-id="${CSS.escape(focusedId)}"]`);
|
|
2933
3375
|
if (el) {
|
|
@@ -2935,6 +3377,11 @@ function VirtualTreeView({
|
|
|
2935
3377
|
pendingFocus.current = false;
|
|
2936
3378
|
}
|
|
2937
3379
|
}, [focusedId, win.start, win.end]);
|
|
3380
|
+
(0, import_react36.useEffect)(() => {
|
|
3381
|
+
if (!hadFocus.current || pendingFocus.current) return;
|
|
3382
|
+
const active = document.activeElement;
|
|
3383
|
+
if (active === document.body || active == null) scrollRef.current?.focus({ preventScroll: true });
|
|
3384
|
+
});
|
|
2938
3385
|
const moveTo = (id) => {
|
|
2939
3386
|
const i = flat.findIndex((f) => f.node.id === id);
|
|
2940
3387
|
if (i < 0) return;
|
|
@@ -2950,6 +3397,7 @@ function VirtualTreeView({
|
|
|
2950
3397
|
setFocusedId(id);
|
|
2951
3398
|
};
|
|
2952
3399
|
const onKeyDown = (e) => {
|
|
3400
|
+
if (e.key.length === 1 && (e.ctrlKey || e.metaKey || e.altKey)) return;
|
|
2953
3401
|
const curId = e.target.closest('[role="treeitem"]')?.dataset.id ?? focusedId;
|
|
2954
3402
|
const act = treeNav(flat, curId, e.key);
|
|
2955
3403
|
if (act.focusId === void 0 && act.toggleId === void 0 && act.selectId === void 0) return;
|
|
@@ -2970,13 +3418,20 @@ function VirtualTreeView({
|
|
|
2970
3418
|
ref: scrollRef,
|
|
2971
3419
|
className: cx("uix-tree", "uix-tree--virtual", className),
|
|
2972
3420
|
role: "tree",
|
|
3421
|
+
tabIndex: -1,
|
|
2973
3422
|
style: { maxHeight, overflow: "auto" },
|
|
2974
3423
|
onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
|
|
2975
3424
|
onKeyDown,
|
|
3425
|
+
onFocus: () => {
|
|
3426
|
+
hadFocus.current = true;
|
|
3427
|
+
},
|
|
3428
|
+
onBlur: (e) => {
|
|
3429
|
+
if (!e.currentTarget.contains(e.relatedTarget)) hadFocus.current = false;
|
|
3430
|
+
},
|
|
2976
3431
|
...props,
|
|
2977
3432
|
children: [
|
|
2978
|
-
win.padTop > 0 && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("li", { "aria-hidden": "true", style: { height: win.padTop } }),
|
|
2979
|
-
rows.map((f) => {
|
|
3433
|
+
win.padTop > 0 && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("li", { role: "presentation", "aria-hidden": "true", style: { height: win.padTop } }),
|
|
3434
|
+
rows.map((f, i) => {
|
|
2980
3435
|
const rowStyle = { height: rowHeight, paddingLeft: `calc(${f.level - 1} * var(--uix-space-5))` };
|
|
2981
3436
|
return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
2982
3437
|
"li",
|
|
@@ -2988,7 +3443,7 @@ function VirtualTreeView({
|
|
|
2988
3443
|
"aria-expanded": f.hasChildren ? f.isExpanded : void 0,
|
|
2989
3444
|
"aria-selected": selectable ? selected === f.node.id : void 0,
|
|
2990
3445
|
"data-id": f.node.id,
|
|
2991
|
-
tabIndex: tabbableId === f.node.id ? 0 : -1,
|
|
3446
|
+
tabIndex: tabbableId === f.node.id || !windowHasTabbable && i === 0 ? 0 : -1,
|
|
2992
3447
|
className: "uix-tree__item",
|
|
2993
3448
|
onFocus: (e) => {
|
|
2994
3449
|
if (e.target === e.currentTarget) setFocusedId(f.node.id);
|
|
@@ -3008,7 +3463,7 @@ function VirtualTreeView({
|
|
|
3008
3463
|
f.node.id
|
|
3009
3464
|
);
|
|
3010
3465
|
}),
|
|
3011
|
-
win.padBottom > 0 && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("li", { "aria-hidden": "true", style: { height: win.padBottom } })
|
|
3466
|
+
win.padBottom > 0 && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("li", { role: "presentation", "aria-hidden": "true", style: { height: win.padBottom } })
|
|
3012
3467
|
]
|
|
3013
3468
|
}
|
|
3014
3469
|
);
|
|
@@ -3427,7 +3882,7 @@ function RuleBuilder({
|
|
|
3427
3882
|
}
|
|
3428
3883
|
|
|
3429
3884
|
// src/components/BuilderCanvas.tsx
|
|
3430
|
-
var
|
|
3885
|
+
var import_react37 = require("react");
|
|
3431
3886
|
var import_jsx_runtime59 = require("react/jsx-runtime");
|
|
3432
3887
|
function BuilderCanvas({
|
|
3433
3888
|
items,
|
|
@@ -3443,13 +3898,13 @@ function BuilderCanvas({
|
|
|
3443
3898
|
emptyMessage = "Add an item from the palette to begin.",
|
|
3444
3899
|
className
|
|
3445
3900
|
}) {
|
|
3446
|
-
const [internalSelectedId, setInternalSelectedId] = (0,
|
|
3447
|
-
const [draggedId, setDraggedId] = (0,
|
|
3448
|
-
const propertyHeadingRef = (0,
|
|
3449
|
-
const shouldFocusProperties = (0,
|
|
3901
|
+
const [internalSelectedId, setInternalSelectedId] = (0, import_react37.useState)();
|
|
3902
|
+
const [draggedId, setDraggedId] = (0, import_react37.useState)();
|
|
3903
|
+
const propertyHeadingRef = (0, import_react37.useRef)(null);
|
|
3904
|
+
const shouldFocusProperties = (0, import_react37.useRef)(false);
|
|
3450
3905
|
const selectedId = controlledSelectedId ?? internalSelectedId;
|
|
3451
3906
|
const selected = items.find((item) => item.id === selectedId);
|
|
3452
|
-
(0,
|
|
3907
|
+
(0, import_react37.useEffect)(() => {
|
|
3453
3908
|
if (shouldFocusProperties.current && selected) {
|
|
3454
3909
|
propertyHeadingRef.current?.focus();
|
|
3455
3910
|
shouldFocusProperties.current = false;
|
|
@@ -3540,7 +3995,7 @@ function BuilderCanvas({
|
|
|
3540
3995
|
}
|
|
3541
3996
|
|
|
3542
3997
|
// src/components/SchedulingCalendar.tsx
|
|
3543
|
-
var
|
|
3998
|
+
var import_react38 = require("react");
|
|
3544
3999
|
|
|
3545
4000
|
// src/calendar-model.ts
|
|
3546
4001
|
function fromDateKey(value) {
|
|
@@ -3643,19 +4098,19 @@ function SchedulingCalendar({
|
|
|
3643
4098
|
onRetry,
|
|
3644
4099
|
className
|
|
3645
4100
|
}) {
|
|
3646
|
-
const [internalView, setInternalView] = (0,
|
|
4101
|
+
const [internalView, setInternalView] = (0, import_react38.useState)("month");
|
|
3647
4102
|
const view = controlledView ?? internalView;
|
|
3648
|
-
const [activeDate, setActiveDate] = (0,
|
|
3649
|
-
const gridRef = (0,
|
|
3650
|
-
const visibleEntries = (0,
|
|
3651
|
-
const entrySpans = (0,
|
|
3652
|
-
const overlaySpans = (0,
|
|
3653
|
-
const monthDays = (0,
|
|
4103
|
+
const [activeDate, setActiveDate] = (0, import_react38.useState)(anchorDate);
|
|
4104
|
+
const gridRef = (0, import_react38.useRef)(null);
|
|
4105
|
+
const visibleEntries = (0, import_react38.useMemo)(() => entries.filter((entry) => filter?.(entry) ?? true), [entries, filter]);
|
|
4106
|
+
const entrySpans = (0, import_react38.useMemo)(() => new Map(visibleEntries.map((entry) => [entry.id, zonedDateSpan(entry.start, entry.end, timeZone)])), [visibleEntries, timeZone]);
|
|
4107
|
+
const overlaySpans = (0, import_react38.useMemo)(() => new Map(overlays.map((overlay) => [overlay.id, { start: overlay.start.slice(0, 10), end: overlay.end.slice(0, 10) }])), [overlays]);
|
|
4108
|
+
const monthDays = (0, import_react38.useMemo)(() => buildMonthGrid(startOfMonth(anchorDate)), [anchorDate]);
|
|
3654
4109
|
const weekday = ((/* @__PURE__ */ new Date(`${anchorDate}T00:00:00Z`)).getUTCDay() + 6) % 7;
|
|
3655
4110
|
const weekStart = addCalendarDays(anchorDate, -weekday);
|
|
3656
|
-
const weekDays = (0,
|
|
3657
|
-
const days = (0,
|
|
3658
|
-
(0,
|
|
4111
|
+
const weekDays = (0, import_react38.useMemo)(() => Array.from({ length: 7 }, (_, index) => addCalendarDays(weekStart, index)), [weekStart]);
|
|
4112
|
+
const days = (0, import_react38.useMemo)(() => view === "month" ? monthDays.map((day) => day.date) : weekDays, [view, monthDays, weekDays]);
|
|
4113
|
+
(0, import_react38.useEffect)(() => {
|
|
3659
4114
|
if (view !== "agenda" && !days.includes(activeDate)) setActiveDate(days[0]);
|
|
3660
4115
|
}, [activeDate, days, view]);
|
|
3661
4116
|
const setView = (next) => {
|
|
@@ -3739,7 +4194,7 @@ function SchedulingCalendar({
|
|
|
3739
4194
|
}
|
|
3740
4195
|
|
|
3741
4196
|
// src/components/DateRangePicker.tsx
|
|
3742
|
-
var
|
|
4197
|
+
var import_react39 = require("react");
|
|
3743
4198
|
var import_jsx_runtime61 = require("react/jsx-runtime");
|
|
3744
4199
|
var monthLabel = (date, locale) => new Intl.DateTimeFormat(locale, { month: "long", year: "numeric", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${date}T00:00:00Z`));
|
|
3745
4200
|
var dateLabel2 = (date, locale) => new Intl.DateTimeFormat(locale, { dateStyle: "full", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${date}T00:00:00Z`));
|
|
@@ -3759,25 +4214,25 @@ function DateRangePicker({
|
|
|
3759
4214
|
disabled,
|
|
3760
4215
|
className
|
|
3761
4216
|
}) {
|
|
3762
|
-
const id = (0,
|
|
3763
|
-
const [internalMonth, setInternalMonth] = (0,
|
|
4217
|
+
const id = (0, import_react39.useId)();
|
|
4218
|
+
const [internalMonth, setInternalMonth] = (0, import_react39.useState)(() => startOfMonth(controlledMonth ?? value.start ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)));
|
|
3764
4219
|
const visibleMonth = startOfMonth(controlledMonth ?? internalMonth);
|
|
3765
|
-
const [activeDate, setActiveDate] = (0,
|
|
3766
|
-
const rootRef = (0,
|
|
3767
|
-
const pendingFocus = (0,
|
|
4220
|
+
const [activeDate, setActiveDate] = (0, import_react39.useState)(value.end ?? value.start ?? visibleMonth);
|
|
4221
|
+
const rootRef = (0, import_react39.useRef)(null);
|
|
4222
|
+
const pendingFocus = (0, import_react39.useRef)(false);
|
|
3768
4223
|
const count = Math.max(1, Math.min(3, months));
|
|
3769
|
-
const calendars = (0,
|
|
4224
|
+
const calendars = (0, import_react39.useMemo)(() => Array.from({ length: count }, (_, index) => {
|
|
3770
4225
|
const month = startOfMonth(addCalendarMonths(visibleMonth, index));
|
|
3771
4226
|
return { month, days: buildMonthGrid(month).filter((day) => day.inMonth) };
|
|
3772
4227
|
}), [visibleMonth, count]);
|
|
3773
4228
|
const rangeInvalid = invalid || !!value.start && !!value.end && value.start > value.end;
|
|
3774
|
-
(0,
|
|
4229
|
+
(0, import_react39.useEffect)(() => {
|
|
3775
4230
|
const first = calendars[0]?.days[0]?.date;
|
|
3776
4231
|
const finalDays = calendars[calendars.length - 1]?.days;
|
|
3777
4232
|
const last = finalDays?.[finalDays.length - 1]?.date;
|
|
3778
4233
|
if (first && last && (activeDate < first || activeDate > last)) setActiveDate(first);
|
|
3779
4234
|
}, [activeDate, calendars]);
|
|
3780
|
-
(0,
|
|
4235
|
+
(0, import_react39.useEffect)(() => {
|
|
3781
4236
|
if (!pendingFocus.current) return;
|
|
3782
4237
|
rootRef.current?.querySelector(`[data-date="${activeDate}"]`)?.focus();
|
|
3783
4238
|
pendingFocus.current = false;
|
|
@@ -3858,7 +4313,7 @@ function DateRangePicker({
|
|
|
3858
4313
|
}
|
|
3859
4314
|
|
|
3860
4315
|
// src/components/RelationshipGraph.tsx
|
|
3861
|
-
var
|
|
4316
|
+
var import_react41 = require("react");
|
|
3862
4317
|
|
|
3863
4318
|
// src/relationship-graph-model.ts
|
|
3864
4319
|
function boundRelationshipGraph(nodes, edges, maxNodes = 40) {
|
|
@@ -3923,37 +4378,955 @@ function traverseRelationshipNode(nodes, currentId, direction) {
|
|
|
3923
4378
|
const current = Math.max(0, nodes.findIndex((node) => node.id === currentId));
|
|
3924
4379
|
return nodes[(current + direction + nodes.length) % nodes.length]?.id;
|
|
3925
4380
|
}
|
|
4381
|
+
var LAYERED_GEOMETRY = {
|
|
4382
|
+
standard: { nodeWidth: 176, nodeHeight: 76, columnGap: 96, rowGap: 12 },
|
|
4383
|
+
compact: { nodeWidth: 160, nodeHeight: 60, columnGap: 80, rowGap: 8 }
|
|
4384
|
+
};
|
|
4385
|
+
var LAYERED_PADDING = 16;
|
|
4386
|
+
var LAYERED_HEADER = 28;
|
|
4387
|
+
var GROUP_HEADER = 28;
|
|
4388
|
+
var AUTO_LABEL_MAX_FANOUT = 4;
|
|
4389
|
+
var LABEL_CHAR_WIDTH = 5.6;
|
|
4390
|
+
var LABEL_MAX_LINES = 2;
|
|
4391
|
+
function wrapEdgeLabel(label, width) {
|
|
4392
|
+
const max = Math.max(4, Math.floor(width / LABEL_CHAR_WIDTH));
|
|
4393
|
+
const clip = (word) => word.length > max ? `${word.slice(0, max - 1)}\u2026` : word;
|
|
4394
|
+
const lines = [];
|
|
4395
|
+
let current = "";
|
|
4396
|
+
for (const word of label.split(/\s+/).filter(Boolean)) {
|
|
4397
|
+
const next = current ? `${current} ${word}` : word;
|
|
4398
|
+
if (next.length <= max) {
|
|
4399
|
+
current = next;
|
|
4400
|
+
continue;
|
|
4401
|
+
}
|
|
4402
|
+
if (current) lines.push(current);
|
|
4403
|
+
current = clip(word);
|
|
4404
|
+
if (lines.length === LABEL_MAX_LINES) break;
|
|
4405
|
+
}
|
|
4406
|
+
if (current && lines.length < LABEL_MAX_LINES) lines.push(current);
|
|
4407
|
+
if (lines.length === LABEL_MAX_LINES) {
|
|
4408
|
+
const consumed = lines.join(" ").replace(/…$/, "").length;
|
|
4409
|
+
if (consumed < label.replace(/\s+/g, " ").trim().length) {
|
|
4410
|
+
const last = lines[LABEL_MAX_LINES - 1];
|
|
4411
|
+
lines[LABEL_MAX_LINES - 1] = last.endsWith("\u2026") ? last : `${last.slice(0, Math.max(1, max - 1))}\u2026`;
|
|
4412
|
+
}
|
|
4413
|
+
}
|
|
4414
|
+
return lines;
|
|
4415
|
+
}
|
|
4416
|
+
var drawableEdge = (edge, ids) => ids.has(edge.source) && ids.has(edge.target) && edge.source !== edge.target;
|
|
4417
|
+
function classifyLayeredEdges(nodes, edges, rootId = nodes[0]?.id) {
|
|
4418
|
+
const ids = new Set(nodes.map((node) => node.id));
|
|
4419
|
+
const root = rootId !== void 0 && ids.has(rootId) ? rootId : nodes[0]?.id;
|
|
4420
|
+
const outgoing = /* @__PURE__ */ new Map();
|
|
4421
|
+
for (const edge of edges) {
|
|
4422
|
+
if (!drawableEdge(edge, ids)) continue;
|
|
4423
|
+
const list = outgoing.get(edge.source);
|
|
4424
|
+
if (list) list.push(edge.target);
|
|
4425
|
+
else outgoing.set(edge.source, [edge.target]);
|
|
4426
|
+
}
|
|
4427
|
+
const bfs = /* @__PURE__ */ new Map();
|
|
4428
|
+
if (root !== void 0) {
|
|
4429
|
+
bfs.set(root, 0);
|
|
4430
|
+
const queue = [root];
|
|
4431
|
+
for (let head = 0; head < queue.length; head += 1) {
|
|
4432
|
+
const current = queue[head];
|
|
4433
|
+
for (const next of outgoing.get(current) ?? []) {
|
|
4434
|
+
if (!bfs.has(next)) {
|
|
4435
|
+
bfs.set(next, bfs.get(current) + 1);
|
|
4436
|
+
queue.push(next);
|
|
4437
|
+
}
|
|
4438
|
+
}
|
|
4439
|
+
}
|
|
4440
|
+
}
|
|
4441
|
+
const depthById = /* @__PURE__ */ new Map();
|
|
4442
|
+
let deepest = 0;
|
|
4443
|
+
for (const node of nodes) {
|
|
4444
|
+
const supplied = node.depth !== void 0 && Number.isFinite(node.depth) ? Math.max(0, Math.floor(node.depth)) : void 0;
|
|
4445
|
+
const depth = supplied ?? bfs.get(node.id);
|
|
4446
|
+
if (depth === void 0) continue;
|
|
4447
|
+
depthById.set(node.id, depth);
|
|
4448
|
+
deepest = Math.max(deepest, depth);
|
|
4449
|
+
}
|
|
4450
|
+
for (const node of nodes) if (!depthById.has(node.id)) depthById.set(node.id, deepest + 1);
|
|
4451
|
+
const forwardOut = /* @__PURE__ */ new Map();
|
|
4452
|
+
const kindById = /* @__PURE__ */ new Map();
|
|
4453
|
+
const backEdges = [];
|
|
4454
|
+
for (const edge of edges) {
|
|
4455
|
+
if (!drawableEdge(edge, ids)) continue;
|
|
4456
|
+
const from = depthById.get(edge.source);
|
|
4457
|
+
const to = depthById.get(edge.target);
|
|
4458
|
+
if (to === from + 1) {
|
|
4459
|
+
kindById.set(edge.id, "forward");
|
|
4460
|
+
const list = forwardOut.get(edge.source);
|
|
4461
|
+
if (list) list.push(edge.target);
|
|
4462
|
+
else forwardOut.set(edge.source, [edge.target]);
|
|
4463
|
+
} else if (to < from) {
|
|
4464
|
+
backEdges.push(edge);
|
|
4465
|
+
} else {
|
|
4466
|
+
kindById.set(edge.id, "lateral");
|
|
4467
|
+
}
|
|
4468
|
+
}
|
|
4469
|
+
for (const edge of backEdges) {
|
|
4470
|
+
const limit = depthById.get(edge.source);
|
|
4471
|
+
const seen = /* @__PURE__ */ new Set([edge.target]);
|
|
4472
|
+
const stack = [edge.target];
|
|
4473
|
+
let closes = false;
|
|
4474
|
+
while (stack.length > 0 && !closes) {
|
|
4475
|
+
const current = stack.pop();
|
|
4476
|
+
for (const next of forwardOut.get(current) ?? []) {
|
|
4477
|
+
if (next === edge.source) {
|
|
4478
|
+
closes = true;
|
|
4479
|
+
break;
|
|
4480
|
+
}
|
|
4481
|
+
if (!seen.has(next) && depthById.get(next) < limit) {
|
|
4482
|
+
seen.add(next);
|
|
4483
|
+
stack.push(next);
|
|
4484
|
+
}
|
|
4485
|
+
}
|
|
4486
|
+
}
|
|
4487
|
+
kindById.set(edge.id, closes ? "cycle" : "lateral");
|
|
4488
|
+
}
|
|
4489
|
+
return { depthById, kindById };
|
|
4490
|
+
}
|
|
4491
|
+
function clusterIdFor(parentId, type, edgeType) {
|
|
4492
|
+
return `cluster:${parentId}:${type ?? ""}:${edgeType ?? ""}`;
|
|
4493
|
+
}
|
|
4494
|
+
var mean = (values) => values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
4495
|
+
var round1 = (value) => Math.round(value * 10) / 10;
|
|
4496
|
+
function layoutLayeredGraph(nodes, edges, options = {}) {
|
|
4497
|
+
const { nodeWidth, nodeHeight, columnGap, rowGap } = LAYERED_GEOMETRY[options.density ?? "standard"];
|
|
4498
|
+
const threshold = options.clusterThreshold ?? 6;
|
|
4499
|
+
const expanded = options.expandedClusterIds ?? /* @__PURE__ */ new Set();
|
|
4500
|
+
const rootId = options.rootId !== void 0 && nodes.some((node) => node.id === options.rootId) ? options.rootId : nodes[0]?.id;
|
|
4501
|
+
if (nodes.length === 0) {
|
|
4502
|
+
return {
|
|
4503
|
+
rootId,
|
|
4504
|
+
items: [],
|
|
4505
|
+
edges: [],
|
|
4506
|
+
clusters: [],
|
|
4507
|
+
columns: [],
|
|
4508
|
+
itemOf: {},
|
|
4509
|
+
width: 0,
|
|
4510
|
+
height: 0,
|
|
4511
|
+
nodeWidth,
|
|
4512
|
+
nodeHeight,
|
|
4513
|
+
headerHeight: LAYERED_HEADER
|
|
4514
|
+
};
|
|
4515
|
+
}
|
|
4516
|
+
const { depthById, kindById } = classifyLayeredEdges(nodes, edges, rootId);
|
|
4517
|
+
const drawable = edges.filter((edge) => kindById.has(edge.id));
|
|
4518
|
+
const forwardParents = /* @__PURE__ */ new Map();
|
|
4519
|
+
const forwardChildCount = /* @__PURE__ */ new Map();
|
|
4520
|
+
const inCycle = /* @__PURE__ */ new Set();
|
|
4521
|
+
const nodeDegree = /* @__PURE__ */ new Map();
|
|
4522
|
+
for (const edge of drawable) {
|
|
4523
|
+
nodeDegree.set(edge.source, (nodeDegree.get(edge.source) ?? 0) + 1);
|
|
4524
|
+
nodeDegree.set(edge.target, (nodeDegree.get(edge.target) ?? 0) + 1);
|
|
4525
|
+
if (kindById.get(edge.id) === "forward") {
|
|
4526
|
+
const list = forwardParents.get(edge.target);
|
|
4527
|
+
if (list) list.push(edge);
|
|
4528
|
+
else forwardParents.set(edge.target, [edge]);
|
|
4529
|
+
forwardChildCount.set(edge.source, (forwardChildCount.get(edge.source) ?? 0) + 1);
|
|
4530
|
+
} else {
|
|
4531
|
+
inCycle.add(edge.source);
|
|
4532
|
+
inCycle.add(edge.target);
|
|
4533
|
+
}
|
|
4534
|
+
}
|
|
4535
|
+
const groups = /* @__PURE__ */ new Map();
|
|
4536
|
+
if (threshold > 0) {
|
|
4537
|
+
for (const node of nodes) {
|
|
4538
|
+
if (node.id === rootId || inCycle.has(node.id) || (forwardChildCount.get(node.id) ?? 0) > 0) continue;
|
|
4539
|
+
const parents = forwardParents.get(node.id) ?? [];
|
|
4540
|
+
if (parents.length !== 1) continue;
|
|
4541
|
+
const via = parents[0];
|
|
4542
|
+
const id = clusterIdFor(via.source, node.type, via.type);
|
|
4543
|
+
const group = groups.get(id);
|
|
4544
|
+
if (group) {
|
|
4545
|
+
group.members.push(node);
|
|
4546
|
+
continue;
|
|
4547
|
+
}
|
|
4548
|
+
const created = { id, parentId: via.source, depth: depthById.get(node.id), members: [node] };
|
|
4549
|
+
if (node.type !== void 0) created.type = node.type;
|
|
4550
|
+
if (via.type !== void 0) created.edgeType = via.type;
|
|
4551
|
+
const edgeLabel = via.label ?? via.type;
|
|
4552
|
+
if (edgeLabel !== void 0) created.edgeLabel = edgeLabel;
|
|
4553
|
+
groups.set(id, created);
|
|
4554
|
+
}
|
|
4555
|
+
}
|
|
4556
|
+
const clusterById = /* @__PURE__ */ new Map();
|
|
4557
|
+
const itemOf = {};
|
|
4558
|
+
const expandedGroupOf = /* @__PURE__ */ new Map();
|
|
4559
|
+
for (const group of groups.values()) {
|
|
4560
|
+
if (group.members.length < threshold) continue;
|
|
4561
|
+
if (expanded.has(group.id)) {
|
|
4562
|
+
for (const member of group.members) expandedGroupOf.set(member.id, group);
|
|
4563
|
+
continue;
|
|
4564
|
+
}
|
|
4565
|
+
clusterById.set(group.id, group);
|
|
4566
|
+
for (const member of group.members) itemOf[member.id] = group.id;
|
|
4567
|
+
}
|
|
4568
|
+
for (const node of nodes) if (itemOf[node.id] === void 0) itemOf[node.id] = node.id;
|
|
4569
|
+
const columnsByDepth = /* @__PURE__ */ new Map();
|
|
4570
|
+
const itemById = /* @__PURE__ */ new Map();
|
|
4571
|
+
for (const node of nodes) {
|
|
4572
|
+
const id = itemOf[node.id];
|
|
4573
|
+
if (itemById.has(id)) continue;
|
|
4574
|
+
const cluster = clusterById.get(id);
|
|
4575
|
+
const depth = depthById.get(node.id);
|
|
4576
|
+
const item = {
|
|
4577
|
+
id,
|
|
4578
|
+
kind: cluster ? "cluster" : "node",
|
|
4579
|
+
depth,
|
|
4580
|
+
index: 0,
|
|
4581
|
+
x: 0,
|
|
4582
|
+
y: 0,
|
|
4583
|
+
width: nodeWidth,
|
|
4584
|
+
height: nodeHeight,
|
|
4585
|
+
parents: [],
|
|
4586
|
+
children: [],
|
|
4587
|
+
inCycle: cluster ? false : inCycle.has(node.id),
|
|
4588
|
+
degree: cluster ? cluster.members.length : nodeDegree.get(node.id) ?? 0
|
|
4589
|
+
};
|
|
4590
|
+
if (cluster) item.cluster = cluster;
|
|
4591
|
+
else item.node = node;
|
|
4592
|
+
itemById.set(id, item);
|
|
4593
|
+
const column = columnsByDepth.get(depth);
|
|
4594
|
+
if (column) column.push(item);
|
|
4595
|
+
else columnsByDepth.set(depth, [item]);
|
|
4596
|
+
}
|
|
4597
|
+
for (const edge of drawable) {
|
|
4598
|
+
if (kindById.get(edge.id) !== "forward") continue;
|
|
4599
|
+
const from = itemById.get(itemOf[edge.source]);
|
|
4600
|
+
const to = itemById.get(itemOf[edge.target]);
|
|
4601
|
+
if (!from.children.includes(to.id)) from.children.push(to.id);
|
|
4602
|
+
if (!to.parents.includes(from.id)) to.parents.push(from.id);
|
|
4603
|
+
}
|
|
4604
|
+
const depths = [...columnsByDepth.keys()].sort((a, b) => a - b);
|
|
4605
|
+
const position = /* @__PURE__ */ new Map();
|
|
4606
|
+
const reindex = (column) => {
|
|
4607
|
+
column.forEach((item, index) => {
|
|
4608
|
+
item.index = index;
|
|
4609
|
+
position.set(item.id, index);
|
|
4610
|
+
});
|
|
4611
|
+
};
|
|
4612
|
+
for (const depth of depths) reindex(columnsByDepth.get(depth));
|
|
4613
|
+
const groupOfItem = (item) => item.node ? expandedGroupOf.get(item.node.id) : void 0;
|
|
4614
|
+
const sweep = (depth, neighbours) => {
|
|
4615
|
+
const keyed = columnsByDepth.get(depth).map((item) => {
|
|
4616
|
+
const around = neighbours(item).map((id) => position.get(id)).filter((value) => value !== void 0);
|
|
4617
|
+
return { item, key: around.length > 0 ? mean(around) : item.index, tie: item.index };
|
|
4618
|
+
});
|
|
4619
|
+
keyed.sort((a, b) => a.key - b.key || a.tie - b.tie);
|
|
4620
|
+
const ordered = [];
|
|
4621
|
+
const placed = /* @__PURE__ */ new Set();
|
|
4622
|
+
for (const { item } of keyed) {
|
|
4623
|
+
if (placed.has(item.id)) continue;
|
|
4624
|
+
const group = groupOfItem(item);
|
|
4625
|
+
for (const candidate of group ? keyed.map((entry) => entry.item).filter((other) => groupOfItem(other) === group) : [item]) {
|
|
4626
|
+
if (placed.has(candidate.id)) continue;
|
|
4627
|
+
ordered.push(candidate);
|
|
4628
|
+
placed.add(candidate.id);
|
|
4629
|
+
}
|
|
4630
|
+
}
|
|
4631
|
+
columnsByDepth.set(depth, ordered);
|
|
4632
|
+
reindex(ordered);
|
|
4633
|
+
};
|
|
4634
|
+
const inner = depths.slice(1);
|
|
4635
|
+
for (const depth of inner) sweep(depth, (item) => item.parents);
|
|
4636
|
+
for (const depth of [...inner].reverse()) sweep(depth, (item) => item.children);
|
|
4637
|
+
for (const depth of inner) sweep(depth, (item) => item.parents);
|
|
4638
|
+
const top = LAYERED_PADDING + LAYERED_HEADER;
|
|
4639
|
+
const columns = [];
|
|
4640
|
+
let width = 0;
|
|
4641
|
+
let height = 0;
|
|
4642
|
+
for (const depth of depths) {
|
|
4643
|
+
const column = columnsByDepth.get(depth);
|
|
4644
|
+
const x = LAYERED_PADDING + depth * (nodeWidth + columnGap);
|
|
4645
|
+
let cursor = top;
|
|
4646
|
+
let count = 0;
|
|
4647
|
+
const ledGroups = /* @__PURE__ */ new Set();
|
|
4648
|
+
for (const item of column) {
|
|
4649
|
+
const parentCentres = item.parents.map((id) => itemById.get(id)).filter((parent) => parent.depth < depth).map((parent) => parent.y + parent.height / 2);
|
|
4650
|
+
const group = groupOfItem(item);
|
|
4651
|
+
const leadsGroup = group !== void 0 && !ledGroups.has(group);
|
|
4652
|
+
if (group) ledGroups.add(group);
|
|
4653
|
+
const floor = cursor + (leadsGroup ? GROUP_HEADER : 0);
|
|
4654
|
+
const target = parentCentres.length > 0 ? mean(parentCentres) - item.height / 2 : floor;
|
|
4655
|
+
item.x = x;
|
|
4656
|
+
item.y = round1(Math.max(target, floor));
|
|
4657
|
+
if (leadsGroup) item.expandedGroup = group;
|
|
4658
|
+
cursor = item.y + item.height + rowGap;
|
|
4659
|
+
count += item.cluster ? item.cluster.members.length : 1;
|
|
4660
|
+
width = Math.max(width, x + item.width);
|
|
4661
|
+
height = Math.max(height, item.y + item.height);
|
|
4662
|
+
}
|
|
4663
|
+
columns.push({ depth, x, count });
|
|
4664
|
+
}
|
|
4665
|
+
const byPosition = (a, b) => (position.get(a) ?? 0) - (position.get(b) ?? 0);
|
|
4666
|
+
for (const item of itemById.values()) {
|
|
4667
|
+
item.children.sort(byPosition);
|
|
4668
|
+
item.parents.sort(byPosition);
|
|
4669
|
+
}
|
|
4670
|
+
const routed = /* @__PURE__ */ new Map();
|
|
4671
|
+
for (const edge of drawable) {
|
|
4672
|
+
const kind = kindById.get(edge.id);
|
|
4673
|
+
const from = itemById.get(itemOf[edge.source]);
|
|
4674
|
+
const to = itemById.get(itemOf[edge.target]);
|
|
4675
|
+
const key = from.kind === "cluster" || to.kind === "cluster" ? `${from.id}->${to.id}` : edge.id;
|
|
4676
|
+
const existing = routed.get(key);
|
|
4677
|
+
if (existing) {
|
|
4678
|
+
existing.edgeIds.push(edge.id);
|
|
4679
|
+
continue;
|
|
4680
|
+
}
|
|
4681
|
+
let startX;
|
|
4682
|
+
let startY;
|
|
4683
|
+
let endX;
|
|
4684
|
+
let endY;
|
|
4685
|
+
let path;
|
|
4686
|
+
let labelX;
|
|
4687
|
+
let labelY;
|
|
4688
|
+
let startAngle;
|
|
4689
|
+
let endAngle;
|
|
4690
|
+
if (kind === "forward") {
|
|
4691
|
+
startX = from.x + from.width;
|
|
4692
|
+
startY = from.y + from.height / 2;
|
|
4693
|
+
endX = to.x;
|
|
4694
|
+
endY = to.y + to.height / 2;
|
|
4695
|
+
const bend = Math.max(columnGap / 2, 24);
|
|
4696
|
+
path = `M${round1(startX)} ${round1(startY)} C${round1(startX + bend)} ${round1(startY)} ${round1(endX - bend)} ${round1(endY)} ${round1(endX)} ${round1(endY)}`;
|
|
4697
|
+
labelX = (startX + endX) / 2;
|
|
4698
|
+
labelY = (startY + endY) / 2;
|
|
4699
|
+
startAngle = Math.PI;
|
|
4700
|
+
endAngle = 0;
|
|
4701
|
+
} else if (from.depth === to.depth) {
|
|
4702
|
+
startX = from.x + from.width;
|
|
4703
|
+
startY = from.y + from.height / 2;
|
|
4704
|
+
endX = to.x + to.width;
|
|
4705
|
+
endY = to.y + to.height / 2;
|
|
4706
|
+
const bulge = Math.min(columnGap - 8, 20 + Math.abs(endY - startY) * 0.1);
|
|
4707
|
+
path = `M${round1(startX)} ${round1(startY)} C${round1(startX + bulge)} ${round1(startY)} ${round1(endX + bulge)} ${round1(endY)} ${round1(endX)} ${round1(endY)}`;
|
|
4708
|
+
labelX = startX + bulge * 0.75;
|
|
4709
|
+
labelY = (startY + endY) / 2;
|
|
4710
|
+
startAngle = Math.PI;
|
|
4711
|
+
endAngle = Math.PI;
|
|
4712
|
+
} else {
|
|
4713
|
+
startX = from.x + from.width / 2;
|
|
4714
|
+
startY = from.y + from.height;
|
|
4715
|
+
endX = to.x + to.width / 2;
|
|
4716
|
+
endY = to.y + to.height;
|
|
4717
|
+
const dip = 28 + Math.abs(endX - startX) * 0.12;
|
|
4718
|
+
const low = Math.max(startY, endY) + dip;
|
|
4719
|
+
path = `M${round1(startX)} ${round1(startY)} C${round1(startX)} ${round1(low)} ${round1(endX)} ${round1(low)} ${round1(endX)} ${round1(endY)}`;
|
|
4720
|
+
labelX = (startX + endX) / 2;
|
|
4721
|
+
labelY = low - dip * 0.25;
|
|
4722
|
+
startAngle = -Math.PI / 2;
|
|
4723
|
+
endAngle = -Math.PI / 2;
|
|
4724
|
+
height = Math.max(height, low);
|
|
4725
|
+
}
|
|
4726
|
+
const arrow = edge.arrow ?? "forward";
|
|
4727
|
+
const atStart = arrow === "backward";
|
|
4728
|
+
const entry = {
|
|
4729
|
+
id: key,
|
|
4730
|
+
source: from.id,
|
|
4731
|
+
target: to.id,
|
|
4732
|
+
kind,
|
|
4733
|
+
edgeIds: [edge.id],
|
|
4734
|
+
arrow,
|
|
4735
|
+
emphasis: edge.emphasis === true,
|
|
4736
|
+
path,
|
|
4737
|
+
labelX: round1(labelX),
|
|
4738
|
+
labelY: round1(labelY),
|
|
4739
|
+
arrowX: round1(atStart ? startX : endX),
|
|
4740
|
+
arrowY: round1(atStart ? startY : endY),
|
|
4741
|
+
arrowAngle: atStart ? startAngle : endAngle,
|
|
4742
|
+
labelLines: [],
|
|
4743
|
+
autoLabel: to.kind !== "cluster" && (edge.emphasis === true || kind === "forward" && from.children.length <= AUTO_LABEL_MAX_FANOUT)
|
|
4744
|
+
};
|
|
4745
|
+
if (edge.type !== void 0) entry.type = edge.type;
|
|
4746
|
+
const label = edge.label ?? edge.type;
|
|
4747
|
+
if (label !== void 0) entry.label = label;
|
|
4748
|
+
routed.set(key, entry);
|
|
4749
|
+
}
|
|
4750
|
+
const seenLabels = /* @__PURE__ */ new Set();
|
|
4751
|
+
for (const entry of routed.values()) {
|
|
4752
|
+
if (entry.label === void 0) continue;
|
|
4753
|
+
entry.labelLines = wrapEdgeLabel(entry.label, columnGap - 8);
|
|
4754
|
+
if (!entry.autoLabel || entry.emphasis || entry.kind !== "forward") continue;
|
|
4755
|
+
const key = `${entry.source}|${entry.label}`;
|
|
4756
|
+
if (seenLabels.has(key)) entry.autoLabel = false;
|
|
4757
|
+
else seenLabels.add(key);
|
|
4758
|
+
}
|
|
4759
|
+
return {
|
|
4760
|
+
rootId,
|
|
4761
|
+
items: depths.flatMap((depth) => columnsByDepth.get(depth)),
|
|
4762
|
+
edges: [...routed.values()],
|
|
4763
|
+
clusters: [...clusterById.values()],
|
|
4764
|
+
columns,
|
|
4765
|
+
itemOf,
|
|
4766
|
+
width: round1(width + LAYERED_PADDING),
|
|
4767
|
+
height: round1(height + LAYERED_PADDING + 8),
|
|
4768
|
+
nodeWidth,
|
|
4769
|
+
nodeHeight,
|
|
4770
|
+
headerHeight: LAYERED_HEADER
|
|
4771
|
+
};
|
|
4772
|
+
}
|
|
4773
|
+
function layeredNeighbor(layout, currentId, key) {
|
|
4774
|
+
const root = layout.rootId !== void 0 ? layout.itemOf[layout.rootId] : layout.items[0]?.id;
|
|
4775
|
+
const current = layout.items.find((item) => item.id === currentId);
|
|
4776
|
+
if (!current) return root ?? layout.items[0]?.id;
|
|
4777
|
+
const column = layout.items.filter((item) => item.depth === current.depth);
|
|
4778
|
+
switch (key) {
|
|
4779
|
+
case "ArrowUp":
|
|
4780
|
+
return column[current.index - 1]?.id;
|
|
4781
|
+
case "ArrowDown":
|
|
4782
|
+
return column[current.index + 1]?.id;
|
|
4783
|
+
case "ArrowRight":
|
|
4784
|
+
return current.children[0];
|
|
4785
|
+
case "ArrowLeft":
|
|
4786
|
+
return current.parents[0];
|
|
4787
|
+
case "Home":
|
|
4788
|
+
return root;
|
|
4789
|
+
case "End":
|
|
4790
|
+
return column[column.length - 1]?.id;
|
|
4791
|
+
default:
|
|
4792
|
+
return void 0;
|
|
4793
|
+
}
|
|
4794
|
+
}
|
|
3926
4795
|
|
|
3927
|
-
// src/components/
|
|
4796
|
+
// src/components/RelationshipGraphLayered.tsx
|
|
4797
|
+
var import_react40 = require("react");
|
|
4798
|
+
|
|
4799
|
+
// src/components/RelationshipGraphList.tsx
|
|
3928
4800
|
var import_jsx_runtime62 = require("react/jsx-runtime");
|
|
4801
|
+
function EquivalentRelationshipList({
|
|
4802
|
+
nodes,
|
|
4803
|
+
edges,
|
|
4804
|
+
selectedId,
|
|
4805
|
+
labels,
|
|
4806
|
+
onSelect,
|
|
4807
|
+
onExpandNeighbors,
|
|
4808
|
+
onOpenDetails
|
|
4809
|
+
}) {
|
|
4810
|
+
const labelById = new Map(nodes.map((node) => [node.id, node.label]));
|
|
4811
|
+
return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "uix-relationship-graph__list", role: "region", "aria-label": labels.list, children: [
|
|
4812
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("h3", { children: labels.listHeading }),
|
|
4813
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("ul", { children: nodes.map((node) => {
|
|
4814
|
+
const neighbors = relationshipNeighbors(node.id, edges).map((id) => labelById.get(id) ?? id);
|
|
4815
|
+
return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("li", { "data-selected": selectedId === node.id || void 0, children: [
|
|
4816
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("button", { type: "button", onClick: () => onSelect(node.id), "aria-pressed": selectedId === node.id, children: [
|
|
4817
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("strong", { children: node.label }),
|
|
4818
|
+
node.type && /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { children: node.type }),
|
|
4819
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { children: neighbors.length ? labels.connectedTo(neighbors.join(", ")) : labels.noConnections })
|
|
4820
|
+
] }),
|
|
4821
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "uix-relationship-graph__list-actions", children: [
|
|
4822
|
+
onExpandNeighbors && /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("button", { type: "button", onClick: () => onExpandNeighbors(node), children: labels.expandNeighbors }),
|
|
4823
|
+
onOpenDetails && /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("button", { type: "button", onClick: () => onOpenDetails(node), children: labels.openDetails })
|
|
4824
|
+
] })
|
|
4825
|
+
] }, node.id);
|
|
4826
|
+
}) })
|
|
4827
|
+
] });
|
|
4828
|
+
}
|
|
4829
|
+
|
|
4830
|
+
// src/components/RelationshipGraphLayered.tsx
|
|
4831
|
+
var import_jsx_runtime63 = require("react/jsx-runtime");
|
|
3929
4832
|
var EMPTY_IDS = /* @__PURE__ */ new Set();
|
|
3930
|
-
var
|
|
3931
|
-
|
|
4833
|
+
var MIN_ZOOM = 0.4;
|
|
4834
|
+
var MAX_ZOOM = 1.5;
|
|
4835
|
+
var READABLE_ZOOM = 0.75;
|
|
4836
|
+
var ZOOM_STEP = 1.2;
|
|
4837
|
+
var PAN_STEP = 64;
|
|
4838
|
+
var FOCUS_MARGIN = 24;
|
|
4839
|
+
var NAV_KEYS = ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Home", "End"];
|
|
4840
|
+
var clampZoom = (zoom) => Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, zoom));
|
|
4841
|
+
function initialView(layout, size) {
|
|
4842
|
+
if (size.width === 0 || size.height === 0 || layout.width === 0) return { x: 0, y: 0, zoom: 1 };
|
|
4843
|
+
const fit = Math.min(size.width / layout.width, size.height / layout.height);
|
|
4844
|
+
if (fit < READABLE_ZOOM) return { x: 0, y: 0, zoom: READABLE_ZOOM };
|
|
4845
|
+
const zoom = Math.min(fit, 1);
|
|
4846
|
+
return { x: (size.width - layout.width * zoom) / 2, y: Math.max(0, (size.height - layout.height * zoom) / 2), zoom };
|
|
4847
|
+
}
|
|
4848
|
+
function fitView(layout, size) {
|
|
4849
|
+
if (size.width === 0 || layout.width === 0) return { x: 0, y: 0, zoom: 1 };
|
|
4850
|
+
const zoom = clampZoom(Math.min(size.width / layout.width, size.height / layout.height, 1));
|
|
4851
|
+
return { x: (size.width - layout.width * zoom) / 2, y: Math.max(0, (size.height - layout.height * zoom) / 2), zoom };
|
|
4852
|
+
}
|
|
4853
|
+
var arrowPoints = "0,0 -8,-4.5 -8,4.5";
|
|
4854
|
+
function LayeredRelationshipGraph({
|
|
3932
4855
|
nodes,
|
|
3933
4856
|
edges,
|
|
3934
4857
|
selectedId,
|
|
3935
4858
|
onSelect,
|
|
3936
4859
|
onExpandNeighbors,
|
|
3937
4860
|
onOpenDetails,
|
|
3938
|
-
maxNodes =
|
|
4861
|
+
maxNodes = 400,
|
|
3939
4862
|
density = "standard",
|
|
3940
4863
|
highlightedNodeIds = EMPTY_IDS,
|
|
3941
4864
|
conflictedNodeIds = EMPTY_IDS,
|
|
3942
4865
|
highlightedEdgeIds = EMPTY_IDS,
|
|
3943
4866
|
conflictedEdgeIds = EMPTY_IDS,
|
|
4867
|
+
legend = [],
|
|
4868
|
+
className,
|
|
4869
|
+
labels,
|
|
4870
|
+
rootId,
|
|
4871
|
+
renderNode,
|
|
4872
|
+
nodeAriaLabel,
|
|
4873
|
+
renderCluster,
|
|
4874
|
+
clusterThreshold,
|
|
4875
|
+
expandedClusterIds,
|
|
4876
|
+
onToggleCluster,
|
|
4877
|
+
dimmedNodeIds = EMPTY_IDS,
|
|
4878
|
+
edgeLabels = "auto",
|
|
4879
|
+
showList = true,
|
|
4880
|
+
height,
|
|
4881
|
+
focusId,
|
|
4882
|
+
onFocusChange
|
|
4883
|
+
}) {
|
|
4884
|
+
const hintId = (0, import_react40.useId)();
|
|
4885
|
+
const bounded = (0, import_react40.useMemo)(() => boundRelationshipGraph(nodes, edges, maxNodes), [nodes, edges, maxNodes]);
|
|
4886
|
+
const [localExpanded, setLocalExpanded] = (0, import_react40.useState)(EMPTY_IDS);
|
|
4887
|
+
const expanded = expandedClusterIds ?? localExpanded;
|
|
4888
|
+
const layout = (0, import_react40.useMemo)(
|
|
4889
|
+
() => layoutLayeredGraph(bounded.nodes, bounded.edges, {
|
|
4890
|
+
...rootId !== void 0 ? { rootId } : {},
|
|
4891
|
+
...clusterThreshold !== void 0 ? { clusterThreshold } : {},
|
|
4892
|
+
expandedClusterIds: expanded,
|
|
4893
|
+
density
|
|
4894
|
+
}),
|
|
4895
|
+
[bounded, rootId, clusterThreshold, expanded, density]
|
|
4896
|
+
);
|
|
4897
|
+
const itemById = (0, import_react40.useMemo)(() => new Map(layout.items.map((item) => [item.id, item])), [layout]);
|
|
4898
|
+
const viewportRef = (0, import_react40.useRef)(null);
|
|
4899
|
+
const itemRefs = (0, import_react40.useRef)(/* @__PURE__ */ new Map());
|
|
4900
|
+
const [size, setSize] = (0, import_react40.useState)({ width: 0, height: 0 });
|
|
4901
|
+
const [view, setView] = (0, import_react40.useState)({ x: 0, y: 0, zoom: 1 });
|
|
4902
|
+
const [animate, setAnimate] = (0, import_react40.useState)(false);
|
|
4903
|
+
const [localFocus, setLocalFocus] = (0, import_react40.useState)(void 0);
|
|
4904
|
+
const [hoverId, setHoverId] = (0, import_react40.useState)(void 0);
|
|
4905
|
+
const viewRef = (0, import_react40.useRef)(view);
|
|
4906
|
+
viewRef.current = view;
|
|
4907
|
+
const layoutRef = (0, import_react40.useRef)(layout);
|
|
4908
|
+
layoutRef.current = layout;
|
|
4909
|
+
const pendingFocus = (0, import_react40.useRef)(void 0);
|
|
4910
|
+
const initialisedFor = (0, import_react40.useRef)(void 0);
|
|
4911
|
+
const rootItemId = layout.rootId !== void 0 ? layout.itemOf[layout.rootId] : layout.items[0]?.id;
|
|
4912
|
+
const requestedFocus = focusId !== void 0 ? layout.itemOf[focusId] ?? focusId : localFocus;
|
|
4913
|
+
const focusedId = requestedFocus !== void 0 && itemById.has(requestedFocus) ? requestedFocus : rootItemId;
|
|
4914
|
+
const selectedItemId = selectedId !== void 0 ? layout.itemOf[selectedId] : void 0;
|
|
4915
|
+
const activeIds = (0, import_react40.useMemo)(
|
|
4916
|
+
() => new Set([hoverId, selectedItemId].filter((id) => id !== void 0)),
|
|
4917
|
+
[hoverId, selectedItemId]
|
|
4918
|
+
);
|
|
4919
|
+
(0, import_react40.useEffect)(() => {
|
|
4920
|
+
const element = viewportRef.current;
|
|
4921
|
+
if (!element) return;
|
|
4922
|
+
const measure = () => setSize({ width: element.clientWidth, height: element.clientHeight });
|
|
4923
|
+
measure();
|
|
4924
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
4925
|
+
const observer = new ResizeObserver(measure);
|
|
4926
|
+
observer.observe(element);
|
|
4927
|
+
return () => observer.disconnect();
|
|
4928
|
+
}, []);
|
|
4929
|
+
(0, import_react40.useEffect)(() => {
|
|
4930
|
+
if (size.width === 0) return;
|
|
4931
|
+
const key = `${layout.rootId ?? ""}`;
|
|
4932
|
+
if (initialisedFor.current === key) return;
|
|
4933
|
+
initialisedFor.current = key;
|
|
4934
|
+
setView(initialView(layout, size));
|
|
4935
|
+
}, [layout, size]);
|
|
4936
|
+
const moveView = (0, import_react40.useCallback)((next, smooth) => {
|
|
4937
|
+
setAnimate(smooth);
|
|
4938
|
+
setView(next);
|
|
4939
|
+
}, []);
|
|
4940
|
+
const ensureVisible = (0, import_react40.useCallback)((id, smooth) => {
|
|
4941
|
+
const element = viewportRef.current;
|
|
4942
|
+
const item = layoutRef.current.items.find((candidate) => candidate.id === id);
|
|
4943
|
+
if (!element || !item) return;
|
|
4944
|
+
const { x, y, zoom } = viewRef.current;
|
|
4945
|
+
const width = element.clientWidth;
|
|
4946
|
+
const heightPx = element.clientHeight;
|
|
4947
|
+
const left = item.x * zoom + x;
|
|
4948
|
+
const top = item.y * zoom + y;
|
|
4949
|
+
const right = left + item.width * zoom;
|
|
4950
|
+
const bottom = top + item.height * zoom;
|
|
4951
|
+
let nextX = x;
|
|
4952
|
+
let nextY = y;
|
|
4953
|
+
if (left < FOCUS_MARGIN) nextX += FOCUS_MARGIN - left;
|
|
4954
|
+
else if (right > width - FOCUS_MARGIN) nextX -= right - (width - FOCUS_MARGIN);
|
|
4955
|
+
if (top < FOCUS_MARGIN) nextY += FOCUS_MARGIN - top;
|
|
4956
|
+
else if (bottom > heightPx - FOCUS_MARGIN) nextY -= bottom - (heightPx - FOCUS_MARGIN);
|
|
4957
|
+
if (nextX !== x || nextY !== y) moveView({ x: nextX, y: nextY, zoom }, smooth);
|
|
4958
|
+
}, [moveView]);
|
|
4959
|
+
const focusItem = (0, import_react40.useCallback)((id) => {
|
|
4960
|
+
setLocalFocus(id);
|
|
4961
|
+
onFocusChange?.(id);
|
|
4962
|
+
requestAnimationFrame(() => {
|
|
4963
|
+
itemRefs.current.get(id)?.focus({ preventScroll: true });
|
|
4964
|
+
ensureVisible(id, true);
|
|
4965
|
+
});
|
|
4966
|
+
}, [ensureVisible, onFocusChange]);
|
|
4967
|
+
(0, import_react40.useEffect)(() => {
|
|
4968
|
+
const wanted = pendingFocus.current;
|
|
4969
|
+
if (wanted !== void 0 && itemById.has(wanted)) {
|
|
4970
|
+
pendingFocus.current = void 0;
|
|
4971
|
+
focusItem(wanted);
|
|
4972
|
+
}
|
|
4973
|
+
}, [itemById, focusItem]);
|
|
4974
|
+
const controlledTarget = focusId !== void 0 ? layout.itemOf[focusId] ?? focusId : void 0;
|
|
4975
|
+
const controlledPresent = controlledTarget !== void 0 && itemById.has(controlledTarget);
|
|
4976
|
+
const controlledKey = `${controlledTarget ?? ""}|${controlledPresent}`;
|
|
4977
|
+
const lastControlledKey = (0, import_react40.useRef)(controlledKey);
|
|
4978
|
+
(0, import_react40.useEffect)(() => {
|
|
4979
|
+
if (lastControlledKey.current === controlledKey) return;
|
|
4980
|
+
lastControlledKey.current = controlledKey;
|
|
4981
|
+
if (!controlledPresent || controlledTarget === void 0) return;
|
|
4982
|
+
requestAnimationFrame(() => {
|
|
4983
|
+
itemRefs.current.get(controlledTarget)?.focus({ preventScroll: true });
|
|
4984
|
+
ensureVisible(controlledTarget, true);
|
|
4985
|
+
});
|
|
4986
|
+
}, [controlledKey, controlledTarget, controlledPresent, ensureVisible]);
|
|
4987
|
+
(0, import_react40.useEffect)(() => {
|
|
4988
|
+
const element = viewportRef.current;
|
|
4989
|
+
if (!element) return;
|
|
4990
|
+
const onWheel = (event) => {
|
|
4991
|
+
if (!event.ctrlKey && !event.metaKey) return;
|
|
4992
|
+
event.preventDefault();
|
|
4993
|
+
const rect = element.getBoundingClientRect();
|
|
4994
|
+
const px = event.clientX - rect.left;
|
|
4995
|
+
const py = event.clientY - rect.top;
|
|
4996
|
+
const current = viewRef.current;
|
|
4997
|
+
const zoom = clampZoom(current.zoom * Math.exp(-event.deltaY * 2e-3));
|
|
4998
|
+
const ratio = zoom / current.zoom;
|
|
4999
|
+
setAnimate(false);
|
|
5000
|
+
setView({ zoom, x: px - (px - current.x) * ratio, y: py - (py - current.y) * ratio });
|
|
5001
|
+
};
|
|
5002
|
+
element.addEventListener("wheel", onWheel, { passive: false });
|
|
5003
|
+
return () => element.removeEventListener("wheel", onWheel);
|
|
5004
|
+
}, []);
|
|
5005
|
+
const pointers = (0, import_react40.useRef)(/* @__PURE__ */ new Map());
|
|
5006
|
+
const pinchDistance = (0, import_react40.useRef)(void 0);
|
|
5007
|
+
const onPointerDown = (event) => {
|
|
5008
|
+
if (event.target.closest("button")) return;
|
|
5009
|
+
event.currentTarget.setPointerCapture?.(event.pointerId);
|
|
5010
|
+
pointers.current.set(event.pointerId, { x: event.clientX, y: event.clientY });
|
|
5011
|
+
pinchDistance.current = void 0;
|
|
5012
|
+
setAnimate(false);
|
|
5013
|
+
};
|
|
5014
|
+
const onPointerMove = (event) => {
|
|
5015
|
+
const previous = pointers.current.get(event.pointerId);
|
|
5016
|
+
if (!previous) return;
|
|
5017
|
+
const current = { x: event.clientX, y: event.clientY };
|
|
5018
|
+
pointers.current.set(event.pointerId, current);
|
|
5019
|
+
const all = [...pointers.current.values()];
|
|
5020
|
+
if (all.length >= 2) {
|
|
5021
|
+
const [a, b] = all;
|
|
5022
|
+
const distance = Math.hypot(a.x - b.x, a.y - b.y);
|
|
5023
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
5024
|
+
const mx = (a.x + b.x) / 2 - rect.left;
|
|
5025
|
+
const my = (a.y + b.y) / 2 - rect.top;
|
|
5026
|
+
if (pinchDistance.current !== void 0 && pinchDistance.current > 0) {
|
|
5027
|
+
const base = viewRef.current;
|
|
5028
|
+
const zoom = clampZoom(base.zoom * (distance / pinchDistance.current));
|
|
5029
|
+
const ratio = zoom / base.zoom;
|
|
5030
|
+
setView({ zoom, x: mx - (mx - base.x) * ratio, y: my - (my - base.y) * ratio });
|
|
5031
|
+
}
|
|
5032
|
+
pinchDistance.current = distance;
|
|
5033
|
+
return;
|
|
5034
|
+
}
|
|
5035
|
+
const dx = current.x - previous.x;
|
|
5036
|
+
const dy = current.y - previous.y;
|
|
5037
|
+
setView((value) => ({ ...value, x: value.x + dx, y: value.y + dy }));
|
|
5038
|
+
};
|
|
5039
|
+
const onPointerEnd = (event) => {
|
|
5040
|
+
pointers.current.delete(event.pointerId);
|
|
5041
|
+
pinchDistance.current = void 0;
|
|
5042
|
+
};
|
|
5043
|
+
const zoomBy = (factor) => {
|
|
5044
|
+
const current = viewRef.current;
|
|
5045
|
+
const zoom = clampZoom(current.zoom * factor);
|
|
5046
|
+
const ratio = zoom / current.zoom;
|
|
5047
|
+
const cx0 = size.width / 2;
|
|
5048
|
+
const cy0 = size.height / 2;
|
|
5049
|
+
moveView({ zoom, x: cx0 - (cx0 - current.x) * ratio, y: cy0 - (cy0 - current.y) * ratio }, true);
|
|
5050
|
+
};
|
|
5051
|
+
const panBy = (dx, dy) => moveView({ ...viewRef.current, x: viewRef.current.x + dx, y: viewRef.current.y + dy }, true);
|
|
5052
|
+
const toggleCluster = (clusterId, focusAfter) => {
|
|
5053
|
+
const isExpanded = expanded.has(clusterId);
|
|
5054
|
+
pendingFocus.current = focusAfter;
|
|
5055
|
+
if (expandedClusterIds === void 0) {
|
|
5056
|
+
setLocalExpanded((previous) => {
|
|
5057
|
+
const next = new Set(previous);
|
|
5058
|
+
if (isExpanded) next.delete(clusterId);
|
|
5059
|
+
else next.add(clusterId);
|
|
5060
|
+
return next;
|
|
5061
|
+
});
|
|
5062
|
+
}
|
|
5063
|
+
onToggleCluster?.(clusterId, !isExpanded);
|
|
5064
|
+
};
|
|
5065
|
+
const activate = (item) => {
|
|
5066
|
+
if (item.cluster) {
|
|
5067
|
+
toggleCluster(item.cluster.id, item.cluster.members[0]?.id ?? item.id);
|
|
5068
|
+
return;
|
|
5069
|
+
}
|
|
5070
|
+
if (item.node) {
|
|
5071
|
+
setLocalFocus(item.id);
|
|
5072
|
+
onFocusChange?.(item.id);
|
|
5073
|
+
onSelect?.(item.node);
|
|
5074
|
+
}
|
|
5075
|
+
};
|
|
5076
|
+
const onItemKeyDown = (event, item) => {
|
|
5077
|
+
if (NAV_KEYS.includes(event.key)) {
|
|
5078
|
+
event.preventDefault();
|
|
5079
|
+
const next = layeredNeighbor(layout, item.id, event.key);
|
|
5080
|
+
if (next !== void 0) focusItem(next);
|
|
5081
|
+
}
|
|
5082
|
+
};
|
|
5083
|
+
const labelFor = (item) => {
|
|
5084
|
+
if (item.cluster) {
|
|
5085
|
+
const cluster = item.cluster;
|
|
5086
|
+
return `${labels.cluster(cluster.members.length, cluster.type, cluster.edgeLabel)}, ${labels.expandCluster}`;
|
|
5087
|
+
}
|
|
5088
|
+
const node = item.node;
|
|
5089
|
+
const conflict = conflictedNodeIds.has(node.id) ? labels.conflict : void 0;
|
|
5090
|
+
if (nodeAriaLabel) return [nodeAriaLabel(node), item.inCycle ? labels.cycle : void 0, conflict].filter(Boolean).join(", ");
|
|
5091
|
+
return [node.label, node.type, node.description, labels.relationships(item.degree), item.inCycle ? labels.cycle : void 0, conflict].filter(Boolean).join(", ");
|
|
5092
|
+
};
|
|
5093
|
+
const showLabel = (edge) => {
|
|
5094
|
+
if (edgeLabels === "never" || edge.label === void 0) return false;
|
|
5095
|
+
if (edgeLabels === "always") return true;
|
|
5096
|
+
return edge.autoLabel || activeIds.has(edge.source) || activeIds.has(edge.target) || focusedId === edge.source || focusedId === edge.target;
|
|
5097
|
+
};
|
|
5098
|
+
const viewportStyle = height !== void 0 ? { "--uix-relationship-graph-height": height } : {};
|
|
5099
|
+
const stageStyle = {
|
|
5100
|
+
width: layout.width,
|
|
5101
|
+
height: layout.height,
|
|
5102
|
+
transform: `translate(${view.x}px, ${view.y}px) scale(${view.zoom})`
|
|
5103
|
+
};
|
|
5104
|
+
return /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("section", { className: cx("uix-relationship-graph", "uix-relationship-graph--layered", density === "compact" && "uix-relationship-graph--compact", className), "aria-label": labels.graph, children: [
|
|
5105
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "uix-relationship-graph__toolbar", role: "group", "aria-label": labels.controls, children: [
|
|
5106
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("button", { type: "button", onClick: () => zoomBy(ZOOM_STEP), disabled: view.zoom >= MAX_ZOOM, children: labels.zoomIn }),
|
|
5107
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("button", { type: "button", onClick: () => zoomBy(1 / ZOOM_STEP), disabled: view.zoom <= MIN_ZOOM, children: labels.zoomOut }),
|
|
5108
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("button", { type: "button", onClick: () => moveView(fitView(layout, size), true), children: labels.fit }),
|
|
5109
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("button", { type: "button", onClick: () => moveView(initialView(layout, size), true), children: labels.reset }),
|
|
5110
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("button", { type: "button", onClick: () => panBy(PAN_STEP, 0), "aria-label": labels.panLeft, children: labels.left }),
|
|
5111
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("button", { type: "button", onClick: () => panBy(0, PAN_STEP), "aria-label": labels.panUp, children: labels.up }),
|
|
5112
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("button", { type: "button", onClick: () => panBy(0, -PAN_STEP), "aria-label": labels.panDown, children: labels.down }),
|
|
5113
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("button", { type: "button", onClick: () => panBy(-PAN_STEP, 0), "aria-label": labels.panRight, children: labels.right }),
|
|
5114
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { "aria-live": "polite", children: labels.zoomLevel(Math.round(view.zoom * 100)) })
|
|
5115
|
+
] }),
|
|
5116
|
+
bounded.omittedNodeCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("p", { className: "uix-relationship-graph__bounded", role: "status", children: labels.bounded(bounded.nodes.length, bounded.omittedNodeCount) }),
|
|
5117
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("p", { id: hintId, className: "uix-visually-hidden", children: labels.keyboardHint }),
|
|
5118
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
|
|
5119
|
+
"div",
|
|
5120
|
+
{
|
|
5121
|
+
ref: viewportRef,
|
|
5122
|
+
className: "uix-relationship-graph__viewport",
|
|
5123
|
+
style: viewportStyle,
|
|
5124
|
+
onPointerDown,
|
|
5125
|
+
onPointerMove,
|
|
5126
|
+
onPointerUp: onPointerEnd,
|
|
5127
|
+
onPointerCancel: onPointerEnd,
|
|
5128
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "uix-relationship-graph__stage", style: stageStyle, "data-animate": animate || void 0, onTransitionEnd: () => setAnimate(false), children: [
|
|
5129
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("svg", { className: "uix-relationship-graph__edges", width: layout.width, height: layout.height, "aria-hidden": "true", focusable: "false", children: layout.edges.map((edge) => {
|
|
5130
|
+
const active = activeIds.has(edge.source) || activeIds.has(edge.target) || edge.edgeIds.some((id) => highlightedEdgeIds.has(id));
|
|
5131
|
+
const angle = edge.arrowAngle * 180 / Math.PI;
|
|
5132
|
+
return /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(
|
|
5133
|
+
"g",
|
|
5134
|
+
{
|
|
5135
|
+
className: "uix-relationship-graph__link",
|
|
5136
|
+
"data-kind": edge.kind,
|
|
5137
|
+
"data-active": active || void 0,
|
|
5138
|
+
"data-emphasis": edge.emphasis || void 0,
|
|
5139
|
+
"data-conflicted": edge.edgeIds.some((id) => conflictedEdgeIds.has(id)) || void 0,
|
|
5140
|
+
"data-dimmed": dimmedNodeIds.has(edge.source) && dimmedNodeIds.has(edge.target) || void 0,
|
|
5141
|
+
children: [
|
|
5142
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("path", { d: edge.path }),
|
|
5143
|
+
edge.arrow !== "none" && /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("polygon", { points: arrowPoints, transform: `translate(${edge.arrowX} ${edge.arrowY}) rotate(${angle})` }),
|
|
5144
|
+
showLabel(edge) && /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("text", { x: edge.labelX, y: edge.labelY, textAnchor: "middle", dominantBaseline: "middle", children: (edge.kind === "cycle" ? [...edge.labelLines, labels.cycle] : edge.labelLines).map((line, index, lines) => /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("tspan", { x: edge.labelX, dy: index === 0 ? `${-(lines.length - 1) * 0.55}em` : "1.1em", children: line }, index)) })
|
|
5145
|
+
]
|
|
5146
|
+
},
|
|
5147
|
+
edge.id
|
|
5148
|
+
);
|
|
5149
|
+
}) }),
|
|
5150
|
+
layout.columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
|
|
5151
|
+
"div",
|
|
5152
|
+
{
|
|
5153
|
+
className: "uix-relationship-graph__column",
|
|
5154
|
+
"aria-hidden": "true",
|
|
5155
|
+
style: { left: column.x, width: layout.nodeWidth },
|
|
5156
|
+
children: labels.column(column.depth, column.count)
|
|
5157
|
+
},
|
|
5158
|
+
column.depth
|
|
5159
|
+
)),
|
|
5160
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("div", { role: "group", "aria-roledescription": labels.roleDescription, "aria-label": labels.graphSummary(bounded.nodes.length, bounded.edges.length), "aria-describedby": hintId, children: layout.items.map((item) => {
|
|
5161
|
+
const selected = item.id === selectedItemId;
|
|
5162
|
+
const dimmed = item.node ? dimmedNodeIds.has(item.node.id) : item.cluster.members.every((member) => dimmedNodeIds.has(member.id));
|
|
5163
|
+
const body = item.cluster ? renderCluster?.(item.cluster) ?? /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(import_jsx_runtime63.Fragment, { children: [
|
|
5164
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "uix-relationship-graph__item-label", children: labels.cluster(item.cluster.members.length, item.cluster.type, item.cluster.edgeLabel) }),
|
|
5165
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "uix-relationship-graph__item-meta", children: labels.expandCluster })
|
|
5166
|
+
] }) : renderNode?.(item.node, { selected, dimmed }) ?? /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(import_jsx_runtime63.Fragment, { children: [
|
|
5167
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "uix-relationship-graph__item-label", children: item.node.label }),
|
|
5168
|
+
(item.node.type || item.node.description) && /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "uix-relationship-graph__item-meta", children: [item.node.type, item.node.description].filter(Boolean).join(" \xB7 ") })
|
|
5169
|
+
] });
|
|
5170
|
+
return /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(
|
|
5171
|
+
"button",
|
|
5172
|
+
{
|
|
5173
|
+
ref: (element) => {
|
|
5174
|
+
if (element) itemRefs.current.set(item.id, element);
|
|
5175
|
+
else itemRefs.current.delete(item.id);
|
|
5176
|
+
},
|
|
5177
|
+
type: "button",
|
|
5178
|
+
className: "uix-relationship-graph__item",
|
|
5179
|
+
"data-kind": item.kind,
|
|
5180
|
+
"data-node-id": item.id,
|
|
5181
|
+
"data-selected": selected || void 0,
|
|
5182
|
+
"data-dimmed": dimmed || void 0,
|
|
5183
|
+
"data-highlighted": item.node && highlightedNodeIds.has(item.node.id) || void 0,
|
|
5184
|
+
"data-conflicted": item.node && conflictedNodeIds.has(item.node.id) || void 0,
|
|
5185
|
+
"data-root": item.id === rootItemId || void 0,
|
|
5186
|
+
style: { left: item.x, top: item.y, width: item.width, height: item.height },
|
|
5187
|
+
tabIndex: item.id === focusedId ? 0 : -1,
|
|
5188
|
+
"aria-label": labelFor(item),
|
|
5189
|
+
...item.cluster ? { "aria-expanded": false } : { "aria-pressed": selected },
|
|
5190
|
+
onClick: () => activate(item),
|
|
5191
|
+
onKeyDown: (event) => onItemKeyDown(event, item),
|
|
5192
|
+
onFocus: () => {
|
|
5193
|
+
if (item.id === focusedId) return;
|
|
5194
|
+
setLocalFocus(item.id);
|
|
5195
|
+
onFocusChange?.(item.id);
|
|
5196
|
+
},
|
|
5197
|
+
onPointerEnter: () => setHoverId(item.id),
|
|
5198
|
+
onPointerLeave: () => setHoverId((current) => current === item.id ? void 0 : current),
|
|
5199
|
+
children: [
|
|
5200
|
+
body,
|
|
5201
|
+
item.inCycle && /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "uix-relationship-graph__item-flag", "aria-hidden": "true", children: labels.cycle })
|
|
5202
|
+
]
|
|
5203
|
+
},
|
|
5204
|
+
item.id
|
|
5205
|
+
);
|
|
5206
|
+
}) }),
|
|
5207
|
+
layout.items.filter((item) => item.expandedGroup).map((item) => {
|
|
5208
|
+
const group = item.expandedGroup;
|
|
5209
|
+
return /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
|
|
5210
|
+
"button",
|
|
5211
|
+
{
|
|
5212
|
+
type: "button",
|
|
5213
|
+
className: "uix-relationship-graph__collapse",
|
|
5214
|
+
"aria-expanded": "true",
|
|
5215
|
+
style: { left: item.x, top: item.y - 26, width: item.width },
|
|
5216
|
+
onClick: () => toggleCluster(group.id, group.id),
|
|
5217
|
+
children: labels.collapseCluster(group.members.length, group.type)
|
|
5218
|
+
},
|
|
5219
|
+
`collapse:${group.id}`
|
|
5220
|
+
);
|
|
5221
|
+
})
|
|
5222
|
+
] })
|
|
5223
|
+
}
|
|
5224
|
+
),
|
|
5225
|
+
legend.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("ul", { className: "uix-relationship-graph__legend", "aria-label": labels.legend, children: legend.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("li", { "data-type": entry.id, children: entry.label }, entry.id)) }),
|
|
5226
|
+
showList && /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
|
|
5227
|
+
EquivalentRelationshipList,
|
|
5228
|
+
{
|
|
5229
|
+
nodes: bounded.nodes,
|
|
5230
|
+
edges: bounded.edges,
|
|
5231
|
+
selectedId,
|
|
5232
|
+
labels,
|
|
5233
|
+
onSelect: (id) => {
|
|
5234
|
+
const node = bounded.nodes.find((candidate) => candidate.id === id);
|
|
5235
|
+
if (node) onSelect?.(node);
|
|
5236
|
+
},
|
|
5237
|
+
onExpandNeighbors,
|
|
5238
|
+
onOpenDetails
|
|
5239
|
+
}
|
|
5240
|
+
)
|
|
5241
|
+
] });
|
|
5242
|
+
}
|
|
5243
|
+
|
|
5244
|
+
// src/components/RelationshipGraph.tsx
|
|
5245
|
+
var import_jsx_runtime64 = require("react/jsx-runtime");
|
|
5246
|
+
var DEFAULT_RELATIONSHIP_GRAPH_LABELS = {
|
|
5247
|
+
graph: "Relationship graph",
|
|
5248
|
+
controls: "Graph view controls",
|
|
5249
|
+
list: "Accessible relationship list",
|
|
5250
|
+
listHeading: "Relationship list",
|
|
5251
|
+
legend: "Relationship types",
|
|
5252
|
+
panLeft: "Pan left",
|
|
5253
|
+
panRight: "Pan right",
|
|
5254
|
+
panUp: "Pan up",
|
|
5255
|
+
panDown: "Pan down",
|
|
5256
|
+
left: "Left",
|
|
5257
|
+
right: "Right",
|
|
5258
|
+
up: "Up",
|
|
5259
|
+
down: "Down",
|
|
5260
|
+
zoomIn: "Zoom in",
|
|
5261
|
+
zoomOut: "Zoom out",
|
|
5262
|
+
fit: "Fit",
|
|
5263
|
+
reset: "Reset view",
|
|
5264
|
+
zoomLevel: (percent) => `Zoom ${percent}%`,
|
|
5265
|
+
bounded: (shown, omitted) => `Showing ${shown} nodes; ${omitted} outside this bounded neighborhood.`,
|
|
5266
|
+
graphSummary: (nodes, edges) => `Graph with ${nodes} nodes and ${edges} relationships`,
|
|
5267
|
+
relationship: "Relationship",
|
|
5268
|
+
conflict: "conflict",
|
|
5269
|
+
connectedTo: (names) => `Connected to ${names}`,
|
|
5270
|
+
noConnections: "No visible connections",
|
|
5271
|
+
expandNeighbors: "Expand neighbors",
|
|
5272
|
+
openDetails: "Open details",
|
|
5273
|
+
column: (depth, count) => `${depth === 0 ? "Start" : `${depth} ${depth === 1 ? "hop" : "hops"}`} \xB7 ${count}`,
|
|
5274
|
+
cluster: (count, type, edgeLabel) => `${count} \xD7 ${type ?? "items"}${edgeLabel ? ` \xB7 ${edgeLabel}` : ""}`,
|
|
5275
|
+
expandCluster: "Show all",
|
|
5276
|
+
collapseCluster: (count, type) => `Collapse ${count} ${type ?? "items"}`,
|
|
5277
|
+
cycle: "cycle",
|
|
5278
|
+
relationships: (count) => `${count} ${count === 1 ? "relationship" : "relationships"}`,
|
|
5279
|
+
roleDescription: "relationship map",
|
|
5280
|
+
keyboardHint: "Arrow keys move between connected items, Enter selects, Home returns to the start.",
|
|
5281
|
+
loading: "Loading relationships\u2026",
|
|
5282
|
+
retry: "Try again",
|
|
5283
|
+
emptyTitle: "No relationships to display.",
|
|
5284
|
+
emptyHint: "Adjust the neighborhood or filters to include a node."
|
|
5285
|
+
};
|
|
5286
|
+
var EMPTY_IDS2 = /* @__PURE__ */ new Set();
|
|
5287
|
+
var EMPTY_LEGEND = [];
|
|
5288
|
+
function RelationshipGraph(props) {
|
|
5289
|
+
const labels = (0, import_react41.useMemo)(() => ({ ...DEFAULT_RELATIONSHIP_GRAPH_LABELS, ...props.labels }), [props.labels]);
|
|
5290
|
+
const stateClass = cx("uix-relationship-graph uix-relationship-graph--state", props.className);
|
|
5291
|
+
if (props.loading) return /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("div", { className: stateClass, role: "status", children: labels.loading });
|
|
5292
|
+
if (props.error) {
|
|
5293
|
+
return /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("div", { className: stateClass, role: "alert", children: [
|
|
5294
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("p", { children: props.error }),
|
|
5295
|
+
props.onRetry && /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: props.onRetry, children: labels.retry })
|
|
5296
|
+
] });
|
|
5297
|
+
}
|
|
5298
|
+
if (props.nodes.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("div", { className: stateClass, children: [
|
|
5299
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("p", { children: labels.emptyTitle }),
|
|
5300
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("p", { children: labels.emptyHint })
|
|
5301
|
+
] });
|
|
5302
|
+
if (props.layout === "layered") return /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(LayeredRelationshipGraph, { ...props, labels });
|
|
5303
|
+
return /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(RadialRelationshipGraph, { ...props, labels });
|
|
5304
|
+
}
|
|
5305
|
+
function RadialRelationshipGraph({
|
|
5306
|
+
nodes,
|
|
5307
|
+
edges,
|
|
5308
|
+
selectedId,
|
|
5309
|
+
onSelect,
|
|
5310
|
+
onExpandNeighbors,
|
|
5311
|
+
onOpenDetails,
|
|
5312
|
+
maxNodes = 40,
|
|
5313
|
+
density = "standard",
|
|
5314
|
+
highlightedNodeIds = EMPTY_IDS2,
|
|
5315
|
+
conflictedNodeIds = EMPTY_IDS2,
|
|
5316
|
+
highlightedEdgeIds = EMPTY_IDS2,
|
|
5317
|
+
conflictedEdgeIds = EMPTY_IDS2,
|
|
3944
5318
|
legend = EMPTY_LEGEND,
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
className
|
|
5319
|
+
className,
|
|
5320
|
+
labels,
|
|
5321
|
+
showList = true
|
|
3949
5322
|
}) {
|
|
3950
|
-
const bounded = (0,
|
|
3951
|
-
const positioned = (0,
|
|
3952
|
-
const positionedById = (0,
|
|
3953
|
-
const [zoom, setZoom] = (0,
|
|
3954
|
-
const [pan, setPan] = (0,
|
|
3955
|
-
const [focusedId, setFocusedId] = (0,
|
|
3956
|
-
const svgRef = (0,
|
|
5323
|
+
const bounded = (0, import_react41.useMemo)(() => boundRelationshipGraph(nodes, edges, maxNodes), [nodes, edges, maxNodes]);
|
|
5324
|
+
const positioned = (0, import_react41.useMemo)(() => layoutRelationshipGraph(bounded.nodes, bounded.edges, selectedId), [bounded, selectedId]);
|
|
5325
|
+
const positionedById = (0, import_react41.useMemo)(() => new Map(positioned.map((node) => [node.id, node])), [positioned]);
|
|
5326
|
+
const [zoom, setZoom] = (0, import_react41.useState)(1);
|
|
5327
|
+
const [pan, setPan] = (0, import_react41.useState)({ x: 0, y: 0 });
|
|
5328
|
+
const [focusedId, setFocusedId] = (0, import_react41.useState)(selectedId ?? positioned[0]?.id);
|
|
5329
|
+
const svgRef = (0, import_react41.useRef)(null);
|
|
3957
5330
|
const select = (id) => {
|
|
3958
5331
|
const node = bounded.nodes.find((item) => item.id === id);
|
|
3959
5332
|
if (node) onSelect?.(node);
|
|
@@ -3976,49 +5349,30 @@ function RelationshipGraph({
|
|
|
3976
5349
|
setZoom(1);
|
|
3977
5350
|
setPan({ x: 0, y: 0 });
|
|
3978
5351
|
};
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
/* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "uix-relationship-graph__toolbar", "aria-label": "Graph view controls", children: [
|
|
3990
|
-
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("button", { type: "button", onClick: () => setPan((value) => ({ ...value, x: value.x - 24 })), "aria-label": "Pan left", children: "Left" }),
|
|
3991
|
-
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("button", { type: "button", onClick: () => setPan((value) => ({ ...value, y: value.y - 24 })), "aria-label": "Pan up", children: "Up" }),
|
|
3992
|
-
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("button", { type: "button", onClick: () => setPan((value) => ({ ...value, y: value.y + 24 })), "aria-label": "Pan down", children: "Down" }),
|
|
3993
|
-
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("button", { type: "button", onClick: () => setPan((value) => ({ ...value, x: value.x + 24 })), "aria-label": "Pan right", children: "Right" }),
|
|
3994
|
-
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("button", { type: "button", onClick: () => setZoom((value) => Math.min(2, Number((value + 0.2).toFixed(1)))), "aria-label": "Zoom in", children: "Zoom in" }),
|
|
3995
|
-
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("button", { type: "button", onClick: () => setZoom((value) => Math.max(0.5, Number((value - 0.2).toFixed(1)))), "aria-label": "Zoom out", children: "Zoom out" }),
|
|
3996
|
-
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("button", { type: "button", onClick: reset, children: "Reset view" }),
|
|
3997
|
-
/* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { "aria-live": "polite", children: [
|
|
3998
|
-
"Zoom ",
|
|
3999
|
-
Math.round(zoom * 100),
|
|
4000
|
-
"%"
|
|
4001
|
-
] })
|
|
4002
|
-
] }),
|
|
4003
|
-
bounded.omittedNodeCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("p", { className: "uix-relationship-graph__bounded", role: "status", children: [
|
|
4004
|
-
"Showing ",
|
|
4005
|
-
bounded.nodes.length,
|
|
4006
|
-
" nodes; ",
|
|
4007
|
-
bounded.omittedNodeCount,
|
|
4008
|
-
" outside this bounded neighborhood."
|
|
5352
|
+
return /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("section", { className: cx("uix-relationship-graph", `uix-relationship-graph--${density}`, className), "aria-label": labels.graph, children: [
|
|
5353
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("div", { className: "uix-relationship-graph__toolbar", "aria-label": labels.controls, children: [
|
|
5354
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("button", { type: "button", onClick: () => setPan((value) => ({ ...value, x: value.x - 24 })), "aria-label": labels.panLeft, children: labels.left }),
|
|
5355
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("button", { type: "button", onClick: () => setPan((value) => ({ ...value, y: value.y - 24 })), "aria-label": labels.panUp, children: labels.up }),
|
|
5356
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("button", { type: "button", onClick: () => setPan((value) => ({ ...value, y: value.y + 24 })), "aria-label": labels.panDown, children: labels.down }),
|
|
5357
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("button", { type: "button", onClick: () => setPan((value) => ({ ...value, x: value.x + 24 })), "aria-label": labels.panRight, children: labels.right }),
|
|
5358
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("button", { type: "button", onClick: () => setZoom((value) => Math.min(2, Number((value + 0.2).toFixed(1)))), "aria-label": labels.zoomIn, children: labels.zoomIn }),
|
|
5359
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("button", { type: "button", onClick: () => setZoom((value) => Math.max(0.5, Number((value - 0.2).toFixed(1)))), "aria-label": labels.zoomOut, children: labels.zoomOut }),
|
|
5360
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("button", { type: "button", onClick: reset, children: labels.reset }),
|
|
5361
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { "aria-live": "polite", children: labels.zoomLevel(Math.round(zoom * 100)) })
|
|
4009
5362
|
] }),
|
|
4010
|
-
/* @__PURE__ */ (0,
|
|
5363
|
+
bounded.omittedNodeCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("p", { className: "uix-relationship-graph__bounded", role: "status", children: labels.bounded(bounded.nodes.length, bounded.omittedNodeCount) }),
|
|
5364
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("div", { className: "uix-relationship-graph__visual", children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("svg", { ref: svgRef, viewBox: "0 0 600 400", role: "group", "aria-label": labels.graphSummary(bounded.nodes.length, bounded.edges.length), children: /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("g", { transform: `translate(${pan.x} ${pan.y}) scale(${zoom})`, children: [
|
|
4011
5365
|
bounded.edges.map((edge) => {
|
|
4012
5366
|
const source = positionedById.get(edge.source);
|
|
4013
5367
|
const target = positionedById.get(edge.target);
|
|
4014
|
-
return /* @__PURE__ */ (0,
|
|
5368
|
+
return /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("line", { x1: source.x * 6, y1: source.y * 4, x2: target.x * 6, y2: target.y * 4, className: "uix-relationship-graph__edge", "data-highlighted": highlightedEdgeIds.has(edge.id) || void 0, "data-conflicted": conflictedEdgeIds.has(edge.id) || void 0, children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("title", { children: edge.label ?? edge.type ?? labels.relationship }) }, edge.id);
|
|
4015
5369
|
}),
|
|
4016
|
-
positioned.map((node) => /* @__PURE__ */ (0,
|
|
5370
|
+
positioned.map((node) => /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)(
|
|
4017
5371
|
"g",
|
|
4018
5372
|
{
|
|
4019
5373
|
transform: `translate(${node.x * 6} ${node.y * 4})`,
|
|
4020
5374
|
role: "button",
|
|
4021
|
-
"aria-label": `${node.label}${node.type ? `, ${node.type}` : ""}${conflictedNodeIds.has(node.id) ?
|
|
5375
|
+
"aria-label": `${node.label}${node.type ? `, ${node.type}` : ""}${conflictedNodeIds.has(node.id) ? `, ${labels.conflict}` : ""}`,
|
|
4022
5376
|
tabIndex: focusedId === node.id ? 0 : -1,
|
|
4023
5377
|
"data-node-id": node.id,
|
|
4024
5378
|
className: "uix-relationship-graph__node",
|
|
@@ -4029,37 +5383,21 @@ function RelationshipGraph({
|
|
|
4029
5383
|
onKeyDown: (event) => onNodeKeyDown(event, node.id),
|
|
4030
5384
|
onClick: () => select(node.id),
|
|
4031
5385
|
children: [
|
|
4032
|
-
/* @__PURE__ */ (0,
|
|
4033
|
-
/* @__PURE__ */ (0,
|
|
5386
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("rect", { x: "-55", y: "-18", width: "110", height: "36", rx: "8" }),
|
|
5387
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("text", { textAnchor: "middle", dominantBaseline: "middle", children: node.label.length > 16 ? `${node.label.slice(0, 15)}\u2026` : node.label })
|
|
4034
5388
|
]
|
|
4035
5389
|
},
|
|
4036
5390
|
node.id
|
|
4037
5391
|
))
|
|
4038
5392
|
] }) }) }),
|
|
4039
|
-
legend.length > 0 && /* @__PURE__ */ (0,
|
|
4040
|
-
/* @__PURE__ */ (0,
|
|
4041
|
-
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("h3", { children: "Relationship list" }),
|
|
4042
|
-
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("ul", { children: bounded.nodes.map((node) => {
|
|
4043
|
-
const neighbors = relationshipNeighbors(node.id, bounded.edges).map((id) => bounded.nodes.find((item) => item.id === id)?.label ?? id);
|
|
4044
|
-
return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("li", { "data-selected": selectedId === node.id || void 0, children: [
|
|
4045
|
-
/* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("button", { type: "button", onClick: () => select(node.id), "aria-pressed": selectedId === node.id, children: [
|
|
4046
|
-
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("strong", { children: node.label }),
|
|
4047
|
-
node.type && /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { children: node.type }),
|
|
4048
|
-
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { children: neighbors.length ? `Connected to ${neighbors.join(", ")}` : "No visible connections" })
|
|
4049
|
-
] }),
|
|
4050
|
-
/* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "uix-relationship-graph__list-actions", children: [
|
|
4051
|
-
onExpandNeighbors && /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("button", { type: "button", onClick: () => onExpandNeighbors(node), children: "Expand neighbors" }),
|
|
4052
|
-
onOpenDetails && /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("button", { type: "button", onClick: () => onOpenDetails(node), children: "Open details" })
|
|
4053
|
-
] })
|
|
4054
|
-
] }, node.id);
|
|
4055
|
-
}) })
|
|
4056
|
-
] })
|
|
5393
|
+
legend.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("ul", { className: "uix-relationship-graph__legend", "aria-label": labels.legend, children: legend.map((item) => /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("li", { "data-type": item.id, children: item.label }, item.id)) }),
|
|
5394
|
+
showList && /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(EquivalentRelationshipList, { nodes: bounded.nodes, edges: bounded.edges, selectedId, labels, onSelect: select, onExpandNeighbors, onOpenDetails })
|
|
4057
5395
|
] });
|
|
4058
5396
|
}
|
|
4059
5397
|
|
|
4060
5398
|
// src/components/MatchReview.tsx
|
|
4061
|
-
var
|
|
4062
|
-
var
|
|
5399
|
+
var import_react42 = require("react");
|
|
5400
|
+
var import_jsx_runtime65 = require("react/jsx-runtime");
|
|
4063
5401
|
var valueText = (value) => value == null ? "\u2014" : typeof value === "string" ? value : JSON.stringify(value);
|
|
4064
5402
|
function MatchReview({
|
|
4065
5403
|
incoming,
|
|
@@ -4075,11 +5413,11 @@ function MatchReview({
|
|
|
4075
5413
|
onRetry,
|
|
4076
5414
|
className
|
|
4077
5415
|
}) {
|
|
4078
|
-
const [internalSelection, setInternalSelection] = (0,
|
|
4079
|
-
const [announcement, setAnnouncement] = (0,
|
|
4080
|
-
const [bulkPending, setBulkPending] = (0,
|
|
5416
|
+
const [internalSelection, setInternalSelection] = (0, import_react42.useState)(/* @__PURE__ */ new Set());
|
|
5417
|
+
const [announcement, setAnnouncement] = (0, import_react42.useState)("");
|
|
5418
|
+
const [bulkPending, setBulkPending] = (0, import_react42.useState)(false);
|
|
4081
5419
|
const selection = selectedIds ?? internalSelection;
|
|
4082
|
-
const pending = (0,
|
|
5420
|
+
const pending = (0, import_react42.useMemo)(() => candidates.filter((candidate) => candidate.status !== "resolved"), [candidates]);
|
|
4083
5421
|
const setSelection = (next) => {
|
|
4084
5422
|
if (selectedIds === void 0) setInternalSelection(next);
|
|
4085
5423
|
onSelectionChange?.(next);
|
|
@@ -4107,55 +5445,55 @@ function MatchReview({
|
|
|
4107
5445
|
setBulkPending(false);
|
|
4108
5446
|
}
|
|
4109
5447
|
};
|
|
4110
|
-
if (loading) return /* @__PURE__ */ (0,
|
|
4111
|
-
if (error) return /* @__PURE__ */ (0,
|
|
4112
|
-
/* @__PURE__ */ (0,
|
|
4113
|
-
onRetry && /* @__PURE__ */ (0,
|
|
5448
|
+
if (loading) return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("div", { className: cx("uix-match-review uix-match-review--state", className), role: "status", children: "Loading match candidates\u2026" });
|
|
5449
|
+
if (error) return /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("div", { className: cx("uix-match-review uix-match-review--state", className), role: "alert", children: [
|
|
5450
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("p", { children: error }),
|
|
5451
|
+
onRetry && /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children: "Try again" })
|
|
4114
5452
|
] });
|
|
4115
|
-
if (candidates.length === 0) return /* @__PURE__ */ (0,
|
|
4116
|
-
/* @__PURE__ */ (0,
|
|
4117
|
-
/* @__PURE__ */ (0,
|
|
5453
|
+
if (candidates.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("div", { className: cx("uix-match-review uix-match-review--state", className), children: [
|
|
5454
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("p", { children: "No candidate matches found." }),
|
|
5455
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("p", { children: "Choose another record source or continue without a match." })
|
|
4118
5456
|
] });
|
|
4119
|
-
return /* @__PURE__ */ (0,
|
|
4120
|
-
/* @__PURE__ */ (0,
|
|
4121
|
-
/* @__PURE__ */ (0,
|
|
4122
|
-
/* @__PURE__ */ (0,
|
|
4123
|
-
/* @__PURE__ */ (0,
|
|
5457
|
+
return /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("section", { className: cx("uix-match-review", className), "aria-label": "Match review", children: [
|
|
5458
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("div", { className: "uix-match-review__summary", children: [
|
|
5459
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("div", { children: [
|
|
5460
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("strong", { children: incomingLabel }),
|
|
5461
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("span", { children: [
|
|
4124
5462
|
pending.length,
|
|
4125
5463
|
" pending of ",
|
|
4126
5464
|
candidates.length
|
|
4127
5465
|
] })
|
|
4128
5466
|
] }),
|
|
4129
|
-
/* @__PURE__ */ (0,
|
|
4130
|
-
/* @__PURE__ */ (0,
|
|
5467
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("div", { className: "uix-match-review__incoming", children: fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("span", { children: [
|
|
5468
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("b", { children: field.label }),
|
|
4131
5469
|
field.render?.(incoming[field.id], incoming) ?? valueText(incoming[field.id])
|
|
4132
5470
|
] }, field.id)) })
|
|
4133
5471
|
] }),
|
|
4134
|
-
onBulkDecision && /* @__PURE__ */ (0,
|
|
4135
|
-
/* @__PURE__ */ (0,
|
|
5472
|
+
onBulkDecision && /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("div", { className: "uix-match-review__bulk", "aria-label": "Bulk candidate actions", children: [
|
|
5473
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("span", { children: [
|
|
4136
5474
|
selection.size,
|
|
4137
5475
|
" selected"
|
|
4138
5476
|
] }),
|
|
4139
|
-
/* @__PURE__ */ (0,
|
|
4140
|
-
/* @__PURE__ */ (0,
|
|
5477
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", disabled: selection.size === 0 || bulkPending, onClick: () => void runBulk("accept"), children: bulkPending ? "Applying\u2026" : "Accept selected" }),
|
|
5478
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", disabled: selection.size === 0 || bulkPending, onClick: () => void runBulk("dismiss"), children: bulkPending ? "Applying\u2026" : "Dismiss selected" })
|
|
4141
5479
|
] }),
|
|
4142
|
-
/* @__PURE__ */ (0,
|
|
4143
|
-
/* @__PURE__ */ (0,
|
|
4144
|
-
/* @__PURE__ */ (0,
|
|
4145
|
-
/* @__PURE__ */ (0,
|
|
4146
|
-
/* @__PURE__ */ (0,
|
|
4147
|
-
fields.map((field) => /* @__PURE__ */ (0,
|
|
4148
|
-
/* @__PURE__ */ (0,
|
|
4149
|
-
/* @__PURE__ */ (0,
|
|
5480
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("div", { className: "uix-match-review__table-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("table", { className: "uix-table uix-match-review__table", children: [
|
|
5481
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("caption", { className: "uix-visually-hidden", children: "Candidate matches and available decisions" }),
|
|
5482
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("tr", { children: [
|
|
5483
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("th", { scope: "col", children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("span", { className: "uix-visually-hidden", children: "Select" }) }),
|
|
5484
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("th", { scope: "col", children: "Confidence" }),
|
|
5485
|
+
fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("th", { scope: "col", children: field.label }, field.id)),
|
|
5486
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("th", { scope: "col", children: "Status" }),
|
|
5487
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("th", { scope: "col", children: "Actions" })
|
|
4150
5488
|
] }) }),
|
|
4151
|
-
/* @__PURE__ */ (0,
|
|
5489
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("tbody", { children: candidates.map((candidate) => {
|
|
4152
5490
|
const resolved = candidate.status === "resolved";
|
|
4153
|
-
return /* @__PURE__ */ (0,
|
|
4154
|
-
/* @__PURE__ */ (0,
|
|
4155
|
-
/* @__PURE__ */ (0,
|
|
4156
|
-
fields.map((field) => /* @__PURE__ */ (0,
|
|
4157
|
-
/* @__PURE__ */ (0,
|
|
4158
|
-
/* @__PURE__ */ (0,
|
|
5491
|
+
return /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("tr", { "data-status": candidate.status ?? "pending", children: [
|
|
5492
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("input", { type: "checkbox", checked: selection.has(candidate.id), onChange: () => toggle(candidate.id), "aria-label": `Select candidate ${candidate.id}` }) }),
|
|
5493
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("span", { className: "uix-match-review__confidence", children: candidate.confidence == null ? "Not scored" : `${Math.round(candidate.confidence * 100)}%` }) }),
|
|
5494
|
+
fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("td", { children: field.render?.(candidate.record[field.id], candidate.record) ?? valueText(candidate.record[field.id]) }, field.id)),
|
|
5495
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(StatusPill, { tone: resolved ? "success" : "warning", children: resolved ? candidate.decision ?? "Resolved" : "Pending" }) }),
|
|
5496
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("div", { className: "uix-match-review__actions", children: ["accept", "dismiss", "split", "pick-other"].map((action) => /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
|
|
4159
5497
|
"button",
|
|
4160
5498
|
{
|
|
4161
5499
|
type: "button",
|
|
@@ -4169,12 +5507,12 @@ function MatchReview({
|
|
|
4169
5507
|
] }, candidate.id);
|
|
4170
5508
|
}) })
|
|
4171
5509
|
] }) }),
|
|
4172
|
-
/* @__PURE__ */ (0,
|
|
5510
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("p", { className: "uix-visually-hidden", "aria-live": "polite", children: announcement })
|
|
4173
5511
|
] });
|
|
4174
5512
|
}
|
|
4175
5513
|
|
|
4176
5514
|
// src/components/MetricInput.tsx
|
|
4177
|
-
var
|
|
5515
|
+
var import_react43 = require("react");
|
|
4178
5516
|
|
|
4179
5517
|
// src/metric-model.ts
|
|
4180
5518
|
function decimals(value) {
|
|
@@ -4199,7 +5537,7 @@ function parseMetricValue(value) {
|
|
|
4199
5537
|
}
|
|
4200
5538
|
|
|
4201
5539
|
// src/components/MetricInput.tsx
|
|
4202
|
-
var
|
|
5540
|
+
var import_jsx_runtime66 = require("react/jsx-runtime");
|
|
4203
5541
|
var defaultMetricFormat = (input) => input == null ? "" : String(input);
|
|
4204
5542
|
function MetricInput({
|
|
4205
5543
|
value,
|
|
@@ -4218,11 +5556,11 @@ function MetricInput({
|
|
|
4218
5556
|
className,
|
|
4219
5557
|
id: providedId
|
|
4220
5558
|
}) {
|
|
4221
|
-
const generatedId = (0,
|
|
5559
|
+
const generatedId = (0, import_react43.useId)();
|
|
4222
5560
|
const id = providedId ?? generatedId;
|
|
4223
5561
|
const errorId = `${id}-error`;
|
|
4224
|
-
const [text, setText] = (0,
|
|
4225
|
-
(0,
|
|
5562
|
+
const [text, setText] = (0, import_react43.useState)(() => format(value));
|
|
5563
|
+
(0, import_react43.useEffect)(() => setText(format(value)), [value, format]);
|
|
4226
5564
|
const commit = (input) => {
|
|
4227
5565
|
const parsed = parse(input);
|
|
4228
5566
|
if (input.trim() === "" || parsed !== null) {
|
|
@@ -4248,10 +5586,10 @@ function MetricInput({
|
|
|
4248
5586
|
const parsed = parse(event.currentTarget.value);
|
|
4249
5587
|
setText(parsed === null && event.currentTarget.value.trim() !== "" ? format(value) : format(parsed));
|
|
4250
5588
|
};
|
|
4251
|
-
return /* @__PURE__ */ (0,
|
|
4252
|
-
/* @__PURE__ */ (0,
|
|
4253
|
-
/* @__PURE__ */ (0,
|
|
4254
|
-
/* @__PURE__ */ (0,
|
|
5589
|
+
return /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("div", { className: cx("uix-metric-input", invalid && "uix-metric-input--invalid", className), children: [
|
|
5590
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)("label", { className: "uix-field__label", htmlFor: id, children: label }),
|
|
5591
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("div", { className: "uix-metric-input__control", children: [
|
|
5592
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
|
|
4255
5593
|
Input,
|
|
4256
5594
|
{
|
|
4257
5595
|
id,
|
|
@@ -4270,22 +5608,22 @@ function MetricInput({
|
|
|
4270
5608
|
readOnly
|
|
4271
5609
|
}
|
|
4272
5610
|
),
|
|
4273
|
-
unit && /* @__PURE__ */ (0,
|
|
4274
|
-
/* @__PURE__ */ (0,
|
|
4275
|
-
/* @__PURE__ */ (0,
|
|
4276
|
-
/* @__PURE__ */ (0,
|
|
5611
|
+
unit && /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "uix-metric-input__unit", "aria-hidden": "true", children: unit }),
|
|
5612
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "uix-metric-input__steps", children: [
|
|
5613
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)("button", { type: "button", className: "uix-metric-input__step", onClick: () => stepBy(1), disabled: disabled || readOnly, "aria-label": `Increase ${label}`, children: "+" }),
|
|
5614
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)("button", { type: "button", className: "uix-metric-input__step", onClick: () => stepBy(-1), disabled: disabled || readOnly, "aria-label": `Decrease ${label}`, children: "\u2212" })
|
|
4277
5615
|
] })
|
|
4278
5616
|
] }),
|
|
4279
|
-
unit && /* @__PURE__ */ (0,
|
|
5617
|
+
unit && /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "uix-visually-hidden", children: [
|
|
4280
5618
|
"Unit: ",
|
|
4281
5619
|
unit
|
|
4282
5620
|
] }),
|
|
4283
|
-
invalid && errorMessage && /* @__PURE__ */ (0,
|
|
5621
|
+
invalid && errorMessage && /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { id: errorId, className: "uix-field__error", children: errorMessage })
|
|
4284
5622
|
] });
|
|
4285
5623
|
}
|
|
4286
5624
|
|
|
4287
5625
|
// src/components/LicensePositionBar.tsx
|
|
4288
|
-
var
|
|
5626
|
+
var import_jsx_runtime67 = require("react/jsx-runtime");
|
|
4289
5627
|
var defaultCapacityFormat = (value) => new Intl.NumberFormat().format(value);
|
|
4290
5628
|
function LicensePositionBar({
|
|
4291
5629
|
consumed,
|
|
@@ -4306,18 +5644,18 @@ function LicensePositionBar({
|
|
|
4306
5644
|
const percent = safeEntitled === 0 ? safeConsumed > 0 ? 100 : 0 : safeConsumed / safeEntitled * 100;
|
|
4307
5645
|
const format = (value) => `${formatValue(value)}${unit ? ` ${unit}` : ""}`;
|
|
4308
5646
|
const summary = `${consumedLabel}: ${format(safeConsumed)}. ${entitledLabel}: ${format(safeEntitled)}. ${over > 0 ? `${format(over)} ${overLabel}` : `${format(available)} ${availableLabel}`}.`;
|
|
4309
|
-
return /* @__PURE__ */ (0,
|
|
4310
|
-
/* @__PURE__ */ (0,
|
|
4311
|
-
/* @__PURE__ */ (0,
|
|
5647
|
+
return /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { className: cx("uix-license-position", over > 0 && "uix-license-position--over", className), ...props, children: [
|
|
5648
|
+
/* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { className: "uix-license-position__header", children: [
|
|
5649
|
+
/* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("span", { className: "uix-license-position__value", children: [
|
|
4312
5650
|
format(safeConsumed),
|
|
4313
5651
|
" / ",
|
|
4314
5652
|
format(safeEntitled)
|
|
4315
5653
|
] }),
|
|
4316
|
-
/* @__PURE__ */ (0,
|
|
5654
|
+
/* @__PURE__ */ (0, import_jsx_runtime67.jsx)(StatusPill, { tone: over > 0 ? "danger" : percent >= 80 ? "warning" : "success", children: over > 0 ? `${format(over)} ${overLabel}` : `${format(available)} ${availableLabel}` })
|
|
4317
5655
|
] }),
|
|
4318
|
-
/* @__PURE__ */ (0,
|
|
4319
|
-
/* @__PURE__ */ (0,
|
|
4320
|
-
over > 0 && /* @__PURE__ */ (0,
|
|
5656
|
+
/* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { className: "uix-license-position__track", children: [
|
|
5657
|
+
/* @__PURE__ */ (0, import_jsx_runtime67.jsx)(Meter, { value: Math.min(100, percent), tone: over > 0 ? "danger" : percent >= 80 ? "warning" : "success", "aria-label": summary, "aria-valuetext": summary }),
|
|
5658
|
+
over > 0 && /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(
|
|
4321
5659
|
"span",
|
|
4322
5660
|
{
|
|
4323
5661
|
className: "uix-license-position__overflow",
|
|
@@ -4326,12 +5664,12 @@ function LicensePositionBar({
|
|
|
4326
5664
|
}
|
|
4327
5665
|
)
|
|
4328
5666
|
] }),
|
|
4329
|
-
/* @__PURE__ */ (0,
|
|
5667
|
+
/* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "uix-visually-hidden", children: summary })
|
|
4330
5668
|
] });
|
|
4331
5669
|
}
|
|
4332
5670
|
|
|
4333
5671
|
// src/components/BrandProfiles.tsx
|
|
4334
|
-
var
|
|
5672
|
+
var import_react45 = require("react");
|
|
4335
5673
|
|
|
4336
5674
|
// src/color-model.ts
|
|
4337
5675
|
var clamp2 = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
@@ -4400,8 +5738,8 @@ function meetsContrast(background, foreground, minimum = 4.5) {
|
|
|
4400
5738
|
}
|
|
4401
5739
|
|
|
4402
5740
|
// src/components/ColorPicker.tsx
|
|
4403
|
-
var
|
|
4404
|
-
var
|
|
5741
|
+
var import_react44 = require("react");
|
|
5742
|
+
var import_jsx_runtime68 = require("react/jsx-runtime");
|
|
4405
5743
|
var EMPTY_COLORS = [];
|
|
4406
5744
|
function ColorPicker({
|
|
4407
5745
|
value,
|
|
@@ -4414,17 +5752,17 @@ function ColorPicker({
|
|
|
4414
5752
|
disabled,
|
|
4415
5753
|
className
|
|
4416
5754
|
}) {
|
|
4417
|
-
const id = (0,
|
|
4418
|
-
const triggerRef = (0,
|
|
4419
|
-
const dialogRef = (0,
|
|
4420
|
-
const [open, setOpen] = (0,
|
|
5755
|
+
const id = (0, import_react44.useId)();
|
|
5756
|
+
const triggerRef = (0, import_react44.useRef)(null);
|
|
5757
|
+
const dialogRef = (0, import_react44.useRef)(null);
|
|
5758
|
+
const [open, setOpen] = (0, import_react44.useState)(false);
|
|
4421
5759
|
const normalized = normalizeHex(value) ?? "#000000";
|
|
4422
|
-
const [hexText, setHexText] = (0,
|
|
4423
|
-
const hsv = (0,
|
|
5760
|
+
const [hexText, setHexText] = (0, import_react44.useState)(normalized);
|
|
5761
|
+
const hsv = (0, import_react44.useMemo)(() => rgbToHsv(hexToRgb(normalized)), [normalized]);
|
|
4424
5762
|
const ratio = contrastRatio(normalized, foreground) ?? 0;
|
|
4425
5763
|
const passes = ratio >= minimumContrast;
|
|
4426
|
-
(0,
|
|
4427
|
-
(0,
|
|
5764
|
+
(0, import_react44.useEffect)(() => setHexText(normalized), [normalized]);
|
|
5765
|
+
(0, import_react44.useEffect)(() => {
|
|
4428
5766
|
if (!open) return;
|
|
4429
5767
|
const dialog = dialogRef.current;
|
|
4430
5768
|
dialog?.querySelector("input,button")?.focus();
|
|
@@ -4467,8 +5805,8 @@ function ColorPicker({
|
|
|
4467
5805
|
if (next) onChange(next);
|
|
4468
5806
|
};
|
|
4469
5807
|
const sampleStyle = { backgroundColor: normalized };
|
|
4470
|
-
return /* @__PURE__ */ (0,
|
|
4471
|
-
/* @__PURE__ */ (0,
|
|
5808
|
+
return /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("div", { className: cx("uix-color-picker", className), children: [
|
|
5809
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsxs)(
|
|
4472
5810
|
"button",
|
|
4473
5811
|
{
|
|
4474
5812
|
ref: triggerRef,
|
|
@@ -4480,12 +5818,12 @@ function ColorPicker({
|
|
|
4480
5818
|
onClick: () => setOpen((current) => !current),
|
|
4481
5819
|
disabled,
|
|
4482
5820
|
children: [
|
|
4483
|
-
/* @__PURE__ */ (0,
|
|
4484
|
-
/* @__PURE__ */ (0,
|
|
5821
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { className: "uix-color-picker__swatch", style: sampleStyle, "aria-hidden": "true" }),
|
|
5822
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { children: normalized })
|
|
4485
5823
|
]
|
|
4486
5824
|
}
|
|
4487
5825
|
),
|
|
4488
|
-
open && /* @__PURE__ */ (0,
|
|
5826
|
+
open && /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)(
|
|
4489
5827
|
"div",
|
|
4490
5828
|
{
|
|
4491
5829
|
ref: dialogRef,
|
|
@@ -4496,57 +5834,57 @@ function ColorPicker({
|
|
|
4496
5834
|
className: "uix-color-picker__popover",
|
|
4497
5835
|
onKeyDown: onDialogKeyDown,
|
|
4498
5836
|
children: [
|
|
4499
|
-
/* @__PURE__ */ (0,
|
|
4500
|
-
/* @__PURE__ */ (0,
|
|
5837
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("div", { className: "uix-color-picker__preview", style: sampleStyle, children: /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { style: { color: foreground }, children: "Aa" }) }),
|
|
5838
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("label", { className: "uix-color-picker__range", children: [
|
|
4501
5839
|
"Hue ",
|
|
4502
|
-
/* @__PURE__ */ (0,
|
|
5840
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("span", { children: [
|
|
4503
5841
|
Math.round(hsv.h),
|
|
4504
5842
|
"\xB0"
|
|
4505
5843
|
] }),
|
|
4506
|
-
/* @__PURE__ */ (0,
|
|
5844
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("input", { type: "range", min: "0", max: "359", value: Math.round(hsv.h), onChange: (event) => updateHsv({ h: Number(event.currentTarget.value) }) })
|
|
4507
5845
|
] }),
|
|
4508
|
-
/* @__PURE__ */ (0,
|
|
5846
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("label", { className: "uix-color-picker__range", children: [
|
|
4509
5847
|
"Saturation ",
|
|
4510
|
-
/* @__PURE__ */ (0,
|
|
5848
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("span", { children: [
|
|
4511
5849
|
Math.round(hsv.s * 100),
|
|
4512
5850
|
"%"
|
|
4513
5851
|
] }),
|
|
4514
|
-
/* @__PURE__ */ (0,
|
|
5852
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("input", { type: "range", min: "0", max: "100", value: Math.round(hsv.s * 100), onChange: (event) => updateHsv({ s: Number(event.currentTarget.value) / 100 }) })
|
|
4515
5853
|
] }),
|
|
4516
|
-
/* @__PURE__ */ (0,
|
|
5854
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("label", { className: "uix-color-picker__range", children: [
|
|
4517
5855
|
"Brightness ",
|
|
4518
|
-
/* @__PURE__ */ (0,
|
|
5856
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("span", { children: [
|
|
4519
5857
|
Math.round(hsv.v * 100),
|
|
4520
5858
|
"%"
|
|
4521
5859
|
] }),
|
|
4522
|
-
/* @__PURE__ */ (0,
|
|
5860
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("input", { type: "range", min: "0", max: "100", value: Math.round(hsv.v * 100), onChange: (event) => updateHsv({ v: Number(event.currentTarget.value) / 100 }) })
|
|
4523
5861
|
] }),
|
|
4524
|
-
/* @__PURE__ */ (0,
|
|
5862
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("label", { className: "uix-color-picker__hex", children: [
|
|
4525
5863
|
"Hex",
|
|
4526
|
-
/* @__PURE__ */ (0,
|
|
4527
|
-
/* @__PURE__ */ (0,
|
|
5864
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("span", { className: "uix-color-picker__hex-row", children: [
|
|
5865
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("input", { className: "uix-input", value: hexText, onChange: (event) => setHexText(event.currentTarget.value), onBlur: commitHex, onKeyDown: (event) => {
|
|
4528
5866
|
if (event.key === "Enter") commitHex();
|
|
4529
5867
|
}, "aria-invalid": !normalizeHex(hexText) }),
|
|
4530
|
-
/* @__PURE__ */ (0,
|
|
5868
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", onClick: commitHex, disabled: !normalizeHex(hexText), children: "Set" })
|
|
4531
5869
|
] })
|
|
4532
5870
|
] }),
|
|
4533
|
-
(presets.length > 0 || recent.length > 0) && /* @__PURE__ */ (0,
|
|
4534
|
-
presets.length > 0 && /* @__PURE__ */ (0,
|
|
4535
|
-
/* @__PURE__ */ (0,
|
|
4536
|
-
/* @__PURE__ */ (0,
|
|
5871
|
+
(presets.length > 0 || recent.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("div", { className: "uix-color-picker__palettes", children: [
|
|
5872
|
+
presets.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("fieldset", { children: [
|
|
5873
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("legend", { children: "Presets" }),
|
|
5874
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("div", { children: presets.map((color) => /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("button", { type: "button", className: "uix-color-picker__preset", style: { backgroundColor: normalizeHex(color) ?? color }, onClick: () => choose(color), "aria-label": `Use preset ${color}` }, color)) })
|
|
4537
5875
|
] }),
|
|
4538
|
-
recent.length > 0 && /* @__PURE__ */ (0,
|
|
4539
|
-
/* @__PURE__ */ (0,
|
|
4540
|
-
/* @__PURE__ */ (0,
|
|
5876
|
+
recent.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("fieldset", { children: [
|
|
5877
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("legend", { children: "Recent" }),
|
|
5878
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("div", { children: recent.map((color) => /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("button", { type: "button", className: "uix-color-picker__preset", style: { backgroundColor: normalizeHex(color) ?? color }, onClick: () => choose(color), "aria-label": `Use recent color ${color}` }, color)) })
|
|
4541
5879
|
] })
|
|
4542
5880
|
] }),
|
|
4543
|
-
/* @__PURE__ */ (0,
|
|
5881
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("p", { className: cx("uix-color-picker__contrast", passes ? "uix-color-picker__contrast--pass" : "uix-color-picker__contrast--fail"), role: "status", "aria-live": "polite", children: [
|
|
4544
5882
|
"Contrast ",
|
|
4545
5883
|
ratio.toFixed(2),
|
|
4546
5884
|
":1 \u2014 ",
|
|
4547
5885
|
passes ? `passes WCAG AA (${minimumContrast}:1)` : `does not meet WCAG AA (${minimumContrast}:1)`
|
|
4548
5886
|
] }),
|
|
4549
|
-
/* @__PURE__ */ (0,
|
|
5887
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("button", { type: "button", className: "uix-btn uix-btn--primary uix-btn--sm", onClick: close, children: "Done" })
|
|
4550
5888
|
]
|
|
4551
5889
|
}
|
|
4552
5890
|
)
|
|
@@ -4554,7 +5892,7 @@ function ColorPicker({
|
|
|
4554
5892
|
}
|
|
4555
5893
|
|
|
4556
5894
|
// src/components/BrandProfiles.tsx
|
|
4557
|
-
var
|
|
5895
|
+
var import_jsx_runtime69 = require("react/jsx-runtime");
|
|
4558
5896
|
var PROFILE_PROPERTIES = {
|
|
4559
5897
|
brand: "--uix-brand",
|
|
4560
5898
|
brandForeground: "--uix-brand-fg",
|
|
@@ -4623,7 +5961,7 @@ function BrandProfileEditor({
|
|
|
4623
5961
|
colorPresets = EMPTY_COLORS2,
|
|
4624
5962
|
className
|
|
4625
5963
|
}) {
|
|
4626
|
-
const [announcement, setAnnouncement] = (0,
|
|
5964
|
+
const [announcement, setAnnouncement] = (0, import_react45.useState)("");
|
|
4627
5965
|
const update = (patch) => onChange({ ...value, ...patch });
|
|
4628
5966
|
const apply = () => {
|
|
4629
5967
|
if (applyTarget !== null) applyBrandProfile(value, applyTarget);
|
|
@@ -4640,54 +5978,54 @@ function BrandProfileEditor({
|
|
|
4640
5978
|
...value.typography?.sans ? { "--uix-font-sans": value.typography.sans } : {},
|
|
4641
5979
|
...value.typography?.heading ? { "--uix-font-heading": value.typography.heading } : {}
|
|
4642
5980
|
};
|
|
4643
|
-
return /* @__PURE__ */ (0,
|
|
4644
|
-
/* @__PURE__ */ (0,
|
|
4645
|
-
profiles.length > 0 && /* @__PURE__ */ (0,
|
|
5981
|
+
return /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("section", { className: cx("uix-brand-profiles", className), "aria-label": "Brand profile editor", children: [
|
|
5982
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "uix-brand-profiles__form", children: [
|
|
5983
|
+
profiles.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("label", { className: "uix-field__label", children: [
|
|
4646
5984
|
"Profile",
|
|
4647
|
-
/* @__PURE__ */ (0,
|
|
5985
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("select", { className: "uix-select", value: value.id, onChange: (event) => {
|
|
4648
5986
|
const selected = profiles.find((profile) => profile.id === event.currentTarget.value);
|
|
4649
5987
|
if (selected) onSelectProfile?.(selected);
|
|
4650
|
-
}, children: profiles.map((profile) => /* @__PURE__ */ (0,
|
|
5988
|
+
}, children: profiles.map((profile) => /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("option", { value: profile.id, children: profile.name }, profile.id)) })
|
|
4651
5989
|
] }),
|
|
4652
|
-
/* @__PURE__ */ (0,
|
|
5990
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("label", { className: "uix-field__label", children: [
|
|
4653
5991
|
"Name",
|
|
4654
|
-
/* @__PURE__ */ (0,
|
|
5992
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("input", { className: "uix-input", value: value.name, onChange: (event) => update({ name: event.currentTarget.value }) })
|
|
4655
5993
|
] }),
|
|
4656
|
-
/* @__PURE__ */ (0,
|
|
4657
|
-
/* @__PURE__ */ (0,
|
|
5994
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "uix-brand-profiles__colors", children: [
|
|
5995
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("label", { children: [
|
|
4658
5996
|
"Brand",
|
|
4659
|
-
/* @__PURE__ */ (0,
|
|
5997
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)(ColorPicker, { label: "Brand color", value: value.brand, foreground: value.brandForeground, presets: colorPresets, onChange: (brand) => update({ brand }) })
|
|
4660
5998
|
] }),
|
|
4661
|
-
/* @__PURE__ */ (0,
|
|
5999
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("label", { children: [
|
|
4662
6000
|
"On brand",
|
|
4663
|
-
/* @__PURE__ */ (0,
|
|
6001
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)(ColorPicker, { label: "Brand foreground color", value: value.brandForeground, foreground: value.brand, presets: colorPresets, onChange: (brandForeground) => update({ brandForeground }) })
|
|
4664
6002
|
] })
|
|
4665
6003
|
] }),
|
|
4666
|
-
/* @__PURE__ */ (0,
|
|
6004
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("label", { className: "uix-field__label", children: [
|
|
4667
6005
|
"Body font",
|
|
4668
|
-
/* @__PURE__ */ (0,
|
|
6006
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("input", { className: "uix-input", value: value.typography?.sans ?? "", onChange: (event) => update({ typography: { ...value.typography, sans: event.currentTarget.value || void 0 } }) })
|
|
4669
6007
|
] }),
|
|
4670
|
-
/* @__PURE__ */ (0,
|
|
6008
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("label", { className: "uix-field__label", children: [
|
|
4671
6009
|
"Heading font",
|
|
4672
|
-
/* @__PURE__ */ (0,
|
|
6010
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("input", { className: "uix-input", value: value.typography?.heading ?? "", onChange: (event) => update({ typography: { ...value.typography, heading: event.currentTarget.value || void 0 } }) })
|
|
4673
6011
|
] }),
|
|
4674
|
-
/* @__PURE__ */ (0,
|
|
6012
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("label", { className: "uix-file-upload uix-brand-profiles__upload", children: [
|
|
4675
6013
|
"Logo file",
|
|
4676
|
-
/* @__PURE__ */ (0,
|
|
6014
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("input", { type: "file", accept: "image/*", onChange: onFile })
|
|
4677
6015
|
] }),
|
|
4678
|
-
/* @__PURE__ */ (0,
|
|
4679
|
-
/* @__PURE__ */ (0,
|
|
6016
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("button", { type: "button", className: "uix-btn uix-btn--primary", onClick: apply, children: "Apply profile" }),
|
|
6017
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { className: "uix-visually-hidden", "aria-live": "polite", children: announcement })
|
|
4680
6018
|
] }),
|
|
4681
|
-
/* @__PURE__ */ (0,
|
|
4682
|
-
/* @__PURE__ */ (0,
|
|
4683
|
-
value.logo ? /* @__PURE__ */ (0,
|
|
4684
|
-
/* @__PURE__ */ (0,
|
|
6019
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "uix-brand-profiles__preview", style: previewStyle, "aria-label": `Live preview of ${value.name}`, children: [
|
|
6020
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "uix-brand-profiles__preview-header", children: [
|
|
6021
|
+
value.logo ? /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("img", { src: value.logo.src, alt: value.logo.alt }) : /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { className: "uix-brand-profiles__logo-placeholder", children: "Logo" }),
|
|
6022
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("strong", { children: value.name || "Brand preview" })
|
|
4685
6023
|
] }),
|
|
4686
|
-
/* @__PURE__ */ (0,
|
|
4687
|
-
/* @__PURE__ */ (0,
|
|
4688
|
-
/* @__PURE__ */ (0,
|
|
4689
|
-
/* @__PURE__ */ (0,
|
|
4690
|
-
/* @__PURE__ */ (0,
|
|
6024
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "uix-brand-profiles__preview-body", children: [
|
|
6025
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("h3", { children: "Readable, serializable branding" }),
|
|
6026
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("p", { children: "Accent, links, rings, and muted states continue through the existing UIx token chain." }),
|
|
6027
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("button", { type: "button", className: "uix-btn uix-btn--primary", children: "Primary action" }),
|
|
6028
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("a", { href: "#brand-profile-preview", children: "Preview link" })
|
|
4691
6029
|
] })
|
|
4692
6030
|
] })
|
|
4693
6031
|
] });
|
|
@@ -4695,7 +6033,7 @@ function BrandProfileEditor({
|
|
|
4695
6033
|
var BrandProfiles = BrandProfileEditor;
|
|
4696
6034
|
|
|
4697
6035
|
// src/components/DiffViewer.tsx
|
|
4698
|
-
var
|
|
6036
|
+
var import_react46 = require("react");
|
|
4699
6037
|
|
|
4700
6038
|
// src/diff-model.ts
|
|
4701
6039
|
function isJsonObject(value) {
|
|
@@ -4746,7 +6084,7 @@ function summarizeDiff(entries, resolutions = {}) {
|
|
|
4746
6084
|
}
|
|
4747
6085
|
|
|
4748
6086
|
// src/components/DiffViewer.tsx
|
|
4749
|
-
var
|
|
6087
|
+
var import_jsx_runtime70 = require("react/jsx-runtime");
|
|
4750
6088
|
var GROUPS = [
|
|
4751
6089
|
{ kind: "conflicted", label: "Conflicted" },
|
|
4752
6090
|
{ kind: "changed", label: "Changed" },
|
|
@@ -4765,65 +6103,65 @@ function DiffViewer({
|
|
|
4765
6103
|
labels = DEFAULT_LABELS,
|
|
4766
6104
|
className
|
|
4767
6105
|
}) {
|
|
4768
|
-
const id = (0,
|
|
4769
|
-
const entries = (0,
|
|
4770
|
-
const [internalResolutions, setInternalResolutions] = (0,
|
|
6106
|
+
const id = (0, import_react46.useId)();
|
|
6107
|
+
const entries = (0, import_react46.useMemo)(() => providedEntries ?? buildThreeWayDiff(base, current, incoming), [providedEntries, base, current, incoming]);
|
|
6108
|
+
const [internalResolutions, setInternalResolutions] = (0, import_react46.useState)({});
|
|
4771
6109
|
const resolutions = controlledResolutions ?? internalResolutions;
|
|
4772
6110
|
const summary = summarizeDiff(entries, resolutions);
|
|
4773
6111
|
const resolve2 = (path, resolution) => {
|
|
4774
6112
|
if (controlledResolutions === void 0) setInternalResolutions((currentState) => ({ ...currentState, [path]: resolution }));
|
|
4775
6113
|
onResolutionChange?.(path, resolution);
|
|
4776
6114
|
};
|
|
4777
|
-
if (entries.length === 0) return /* @__PURE__ */ (0,
|
|
4778
|
-
/* @__PURE__ */ (0,
|
|
4779
|
-
/* @__PURE__ */ (0,
|
|
6115
|
+
if (entries.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("section", { className: cx("uix-diff-viewer uix-diff-viewer--empty", className), "aria-label": "Configuration differences", children: [
|
|
6116
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("h3", { children: "No differences" }),
|
|
6117
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("p", { children: "Base, current, and incoming values are aligned." })
|
|
4780
6118
|
] });
|
|
4781
|
-
return /* @__PURE__ */ (0,
|
|
4782
|
-
/* @__PURE__ */ (0,
|
|
4783
|
-
GROUPS.map((group) => /* @__PURE__ */ (0,
|
|
4784
|
-
/* @__PURE__ */ (0,
|
|
6119
|
+
return /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("section", { className: cx("uix-diff-viewer", className), "aria-label": "Configuration differences", children: [
|
|
6120
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("div", { className: "uix-diff-viewer__summary", "aria-label": "Difference summary", children: [
|
|
6121
|
+
GROUPS.map((group) => /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("span", { children: [
|
|
6122
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("b", { children: summary[group.kind] }),
|
|
4785
6123
|
" ",
|
|
4786
6124
|
group.label.toLowerCase()
|
|
4787
6125
|
] }, group.kind)),
|
|
4788
|
-
/* @__PURE__ */ (0,
|
|
4789
|
-
/* @__PURE__ */ (0,
|
|
6126
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("span", { children: [
|
|
6127
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("b", { children: summary.resolved }),
|
|
4790
6128
|
" resolved"
|
|
4791
6129
|
] }),
|
|
4792
|
-
/* @__PURE__ */ (0,
|
|
4793
|
-
/* @__PURE__ */ (0,
|
|
6130
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("span", { children: [
|
|
6131
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("b", { children: summary.pending }),
|
|
4794
6132
|
" pending"
|
|
4795
6133
|
] })
|
|
4796
6134
|
] }),
|
|
4797
6135
|
GROUPS.map((group) => {
|
|
4798
6136
|
const grouped = entries.filter((entry) => entry.kind === group.kind);
|
|
4799
6137
|
if (!grouped.length) return null;
|
|
4800
|
-
return /* @__PURE__ */ (0,
|
|
4801
|
-
/* @__PURE__ */ (0,
|
|
6138
|
+
return /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("section", { className: "uix-diff-viewer__group", "aria-labelledby": `${id}-${group.kind}`, children: [
|
|
6139
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("h3", { id: `${id}-${group.kind}`, children: [
|
|
4802
6140
|
group.label,
|
|
4803
6141
|
" ",
|
|
4804
|
-
/* @__PURE__ */ (0,
|
|
6142
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("span", { children: grouped.length })
|
|
4805
6143
|
] }),
|
|
4806
6144
|
grouped.map((entry) => {
|
|
4807
6145
|
const resolution = resolutions[entry.path] ?? "pending";
|
|
4808
|
-
return /* @__PURE__ */ (0,
|
|
4809
|
-
/* @__PURE__ */ (0,
|
|
4810
|
-
/* @__PURE__ */ (0,
|
|
4811
|
-
/* @__PURE__ */ (0,
|
|
6146
|
+
return /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("details", { className: "uix-diff-viewer__entry", "data-kind": entry.kind, open: entry.kind === "conflicted", children: [
|
|
6147
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("summary", { children: [
|
|
6148
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("code", { children: entry.path }),
|
|
6149
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)(StatusPill, { tone: resolution === "pending" ? "warning" : "success", children: resolution })
|
|
4812
6150
|
] }),
|
|
4813
|
-
/* @__PURE__ */ (0,
|
|
4814
|
-
/* @__PURE__ */ (0,
|
|
4815
|
-
/* @__PURE__ */ (0,
|
|
6151
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("div", { className: "uix-diff-viewer__versions", children: ["base", "current", "incoming"].map((version) => /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("div", { role: "region", "aria-label": `${labels[version] ?? `${version[0].toUpperCase()}${version.slice(1)}`} value for ${entry.path}`, children: [
|
|
6152
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("strong", { children: labels[version] ?? `${version[0].toUpperCase()}${version.slice(1)}` }),
|
|
6153
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("pre", { children: renderValue(entry[version]) })
|
|
4816
6154
|
] }, version)) }),
|
|
4817
|
-
/* @__PURE__ */ (0,
|
|
4818
|
-
/* @__PURE__ */ (0,
|
|
4819
|
-
/* @__PURE__ */ (0,
|
|
4820
|
-
/* @__PURE__ */ (0,
|
|
6155
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("div", { className: "uix-diff-viewer__actions", "aria-label": `Resolve ${entry.path}`, children: [
|
|
6156
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("button", { type: "button", className: "uix-btn uix-btn--primary uix-btn--sm", "aria-pressed": resolution === "accept", onClick: () => resolve2(entry.path, "accept"), children: "Accept incoming" }),
|
|
6157
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", "aria-pressed": resolution === "skip", onClick: () => resolve2(entry.path, "skip"), children: "Keep current" }),
|
|
6158
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", "aria-pressed": resolution === "pending", onClick: () => resolve2(entry.path, "pending"), children: "Mark pending" })
|
|
4821
6159
|
] })
|
|
4822
6160
|
] }, entry.path);
|
|
4823
6161
|
})
|
|
4824
6162
|
] }, group.kind);
|
|
4825
6163
|
}),
|
|
4826
|
-
/* @__PURE__ */ (0,
|
|
6164
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("p", { className: "uix-visually-hidden", "aria-live": "polite", children: [
|
|
4827
6165
|
summary.resolved,
|
|
4828
6166
|
" of ",
|
|
4829
6167
|
entries.length,
|
|
@@ -4863,6 +6201,7 @@ function DiffViewer({
|
|
|
4863
6201
|
ConfirmDialog,
|
|
4864
6202
|
ContentList,
|
|
4865
6203
|
ContentListItem,
|
|
6204
|
+
DEFAULT_RELATIONSHIP_GRAPH_LABELS,
|
|
4866
6205
|
DateRangePicker,
|
|
4867
6206
|
DescriptionItem,
|
|
4868
6207
|
DescriptionList,
|
|
@@ -4947,6 +6286,7 @@ function DiffViewer({
|
|
|
4947
6286
|
SubNavItem,
|
|
4948
6287
|
Switch,
|
|
4949
6288
|
Tab,
|
|
6289
|
+
TabPanel,
|
|
4950
6290
|
Table,
|
|
4951
6291
|
TableWrap,
|
|
4952
6292
|
Tabs,
|
|
@@ -4973,6 +6313,8 @@ function DiffViewer({
|
|
|
4973
6313
|
buildThreeWayDiff,
|
|
4974
6314
|
clampMetricValue,
|
|
4975
6315
|
clampWidth,
|
|
6316
|
+
classifyLayeredEdges,
|
|
6317
|
+
clusterIdFor,
|
|
4976
6318
|
computePosition,
|
|
4977
6319
|
contrastRatio,
|
|
4978
6320
|
cx,
|
|
@@ -4987,6 +6329,8 @@ function DiffViewer({
|
|
|
4987
6329
|
isDateInRange,
|
|
4988
6330
|
isDateUnavailable,
|
|
4989
6331
|
isRuleGroup,
|
|
6332
|
+
layeredNeighbor,
|
|
6333
|
+
layoutLayeredGraph,
|
|
4990
6334
|
layoutRelationshipGraph,
|
|
4991
6335
|
mapRuleGroup,
|
|
4992
6336
|
matchFilter,
|
|
@@ -5028,6 +6372,7 @@ function DiffViewer({
|
|
|
5028
6372
|
useVirtualRows,
|
|
5029
6373
|
validateRuleDefinition,
|
|
5030
6374
|
virtualWindow,
|
|
6375
|
+
wrapEdgeLabel,
|
|
5031
6376
|
zonedDateKey,
|
|
5032
6377
|
zonedDateSpan
|
|
5033
6378
|
});
|