@tensor_1/react 2.11.0 → 2.13.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.
Files changed (65) hide show
  1. package/README.md +26 -1
  2. package/dist/ChartCore-Bt1C84ty.d.cts +22 -0
  3. package/dist/calendar-model.d.ts +31 -0
  4. package/dist/calendar-model.js +89 -0
  5. package/dist/chart-preset.cjs +168 -0
  6. package/dist/chart-preset.d.cts +9 -0
  7. package/dist/chart-preset.d.ts +9 -0
  8. package/dist/chart-preset.js +35 -0
  9. package/dist/chart.cjs +58 -32
  10. package/dist/chart.d.cts +6 -21
  11. package/dist/chart.d.ts +2 -1
  12. package/dist/color-model.d.ts +20 -0
  13. package/dist/color-model.js +74 -0
  14. package/dist/components/BrandProfiles.d.ts +45 -0
  15. package/dist/components/BrandProfiles.js +150 -0
  16. package/dist/components/BuilderCanvas.d.ts +34 -0
  17. package/dist/components/BuilderCanvas.js +117 -0
  18. package/dist/components/Chart.d.ts +6 -21
  19. package/dist/components/Chart.js +5 -76
  20. package/dist/components/ChartCore.d.ts +33 -0
  21. package/dist/components/ChartCore.js +100 -0
  22. package/dist/components/ColorPicker.d.ts +17 -0
  23. package/dist/components/ColorPicker.js +158 -0
  24. package/dist/components/DateRangePicker.d.ts +23 -0
  25. package/dist/components/DateRangePicker.js +123 -0
  26. package/dist/components/DiffViewer.d.ts +18 -0
  27. package/dist/components/DiffViewer.js +93 -0
  28. package/dist/components/Inbox.js +0 -1
  29. package/dist/components/LicensePositionBar.d.ts +17 -0
  30. package/dist/components/LicensePositionBar.js +50 -0
  31. package/dist/components/MatchReview.d.ts +39 -0
  32. package/dist/components/MatchReview.js +120 -0
  33. package/dist/components/Meter.js +0 -1
  34. package/dist/components/MetricInput.d.ts +23 -0
  35. package/dist/components/MetricInput.js +92 -0
  36. package/dist/components/NavFavourites.js +35 -108
  37. package/dist/components/RelationshipGraph.d.ts +30 -0
  38. package/dist/components/RelationshipGraph.js +138 -0
  39. package/dist/components/RuleBuilder.d.ts +45 -0
  40. package/dist/components/RuleBuilder.js +163 -0
  41. package/dist/components/SchedulingCalendar.d.ts +42 -0
  42. package/dist/components/SchedulingCalendar.js +124 -0
  43. package/dist/components/Segmented.js +3 -2
  44. package/dist/components/Table.d.ts +1 -1
  45. package/dist/components/Table.js +4 -3
  46. package/dist/components/Tabs.js +3 -2
  47. package/dist/diff-model.d.ts +23 -0
  48. package/dist/diff-model.js +50 -0
  49. package/dist/hooks/useTable.js +11 -7
  50. package/dist/hooks/useVirtualRows.d.ts +27 -0
  51. package/dist/hooks/useVirtualRows.js +50 -0
  52. package/dist/index.cjs +1930 -194
  53. package/dist/index.d.cts +464 -2
  54. package/dist/index.d.ts +19 -0
  55. package/dist/index.js +93 -1
  56. package/dist/json-value.d.ts +10 -0
  57. package/dist/json-value.js +19 -0
  58. package/dist/metric-model.d.ts +9 -0
  59. package/dist/metric-model.js +25 -0
  60. package/dist/relationship-graph-model.d.ts +31 -0
  61. package/dist/relationship-graph-model.js +68 -0
  62. package/dist/rule-builder-model.d.ts +41 -0
  63. package/dist/rule-builder-model.js +101 -0
  64. package/dist/table-engine.js +8 -6
  65. package/package.json +16 -2
@@ -0,0 +1,100 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useId, useRef } from "react";
4
+ import { cx } from "../cx.js";
5
+ function readToken(name) {
6
+ if (typeof getComputedStyle === "undefined") return "";
7
+ return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
8
+ }
9
+ function uixChartPalette() {
10
+ return [1, 2, 3, 4, 5, 6, 7, 8].map((i) => readToken(`--uix-chart-${i}`));
11
+ }
12
+ function ChartCore({
13
+ engine,
14
+ option,
15
+ title,
16
+ subtitle,
17
+ height = 280,
18
+ className,
19
+ style,
20
+ ariaLabel,
21
+ tableData,
22
+ tableHeaders,
23
+ onReady,
24
+ header
25
+ }) {
26
+ const containerRef = useRef(null);
27
+ const chartRef = useRef(null);
28
+ const appliedOptionRef = useRef(null);
29
+ const latestOptionRef = useRef(option);
30
+ const onReadyRef = useRef(onReady);
31
+ const tableId = useId();
32
+ latestOptionRef.current = option;
33
+ onReadyRef.current = onReady;
34
+ useEffect(() => {
35
+ const element = containerRef.current;
36
+ if (!element) return;
37
+ const chart = engine.init(element, null, { renderer: "svg" });
38
+ chartRef.current = chart;
39
+ chart.setOption(latestOptionRef.current);
40
+ appliedOptionRef.current = latestOptionRef.current;
41
+ onReadyRef.current?.(chart);
42
+ let frame = 0;
43
+ let width = element.clientWidth;
44
+ let height2 = element.clientHeight;
45
+ const observer = new ResizeObserver(() => {
46
+ const nextWidth = element.clientWidth;
47
+ const nextHeight = element.clientHeight;
48
+ if (nextWidth === width && nextHeight === height2) return;
49
+ width = nextWidth;
50
+ height2 = nextHeight;
51
+ if (frame) return;
52
+ frame = requestAnimationFrame(() => {
53
+ frame = 0;
54
+ chart.resize();
55
+ });
56
+ });
57
+ observer.observe(element);
58
+ return () => {
59
+ observer.disconnect();
60
+ if (frame) cancelAnimationFrame(frame);
61
+ chart.dispose();
62
+ chartRef.current = null;
63
+ appliedOptionRef.current = null;
64
+ };
65
+ }, [engine]);
66
+ useEffect(() => {
67
+ const chart = chartRef.current;
68
+ if (!chart || appliedOptionRef.current === option) return;
69
+ chart.setOption(option, { notMerge: false });
70
+ appliedOptionRef.current = option;
71
+ }, [option]);
72
+ const heightValue = typeof height === "number" ? `${height}px` : height;
73
+ const effectiveAriaLabel = ariaLabel ?? title ?? "Chart";
74
+ const hasTable = Boolean(tableData?.length && tableHeaders?.length);
75
+ return /* @__PURE__ */ jsxs("div", { className: cx("uix-chart", className), style, children: [
76
+ title != null || subtitle != null || header != null ? /* @__PURE__ */ jsxs("div", { children: [
77
+ header,
78
+ title ? /* @__PURE__ */ jsx("div", { className: "uix-chart__title", children: title }) : null,
79
+ subtitle ? /* @__PURE__ */ jsx("div", { className: "uix-chart__subtitle", children: subtitle }) : null
80
+ ] }) : null,
81
+ /* @__PURE__ */ jsx(
82
+ "div",
83
+ {
84
+ ref: containerRef,
85
+ style: { height: heightValue },
86
+ role: "img",
87
+ "aria-label": effectiveAriaLabel,
88
+ "aria-describedby": hasTable ? tableId : void 0
89
+ }
90
+ ),
91
+ hasTable ? /* @__PURE__ */ jsxs("table", { id: tableId, className: "sr-only", "aria-label": `${effectiveAriaLabel} \u2014 data table`, children: [
92
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: tableHeaders.map((label) => /* @__PURE__ */ jsx("th", { scope: "col", children: label }, label)) }) }),
93
+ /* @__PURE__ */ jsx("tbody", { children: tableData.map((row, index) => /* @__PURE__ */ jsx("tr", { children: tableHeaders.map((label) => /* @__PURE__ */ jsx("td", { children: row[label] }, label)) }, index)) })
94
+ ] }) : null
95
+ ] });
96
+ }
97
+ export {
98
+ ChartCore,
99
+ uixChartPalette
100
+ };
@@ -0,0 +1,17 @@
1
+ import * as react from 'react';
2
+
3
+ interface ColorPickerProps {
4
+ value: string;
5
+ onChange: (value: string) => void;
6
+ foreground: string;
7
+ presets?: string[];
8
+ recent?: string[];
9
+ minimumContrast?: number;
10
+ label?: string;
11
+ disabled?: boolean;
12
+ className?: string;
13
+ }
14
+ /** Token-friendly HSV/hex picker with a fully keyboard-operable path and contrast feedback. */
15
+ declare function ColorPicker({ value, onChange, foreground, presets, recent, minimumContrast, label, disabled, className, }: ColorPickerProps): react.JSX.Element;
16
+
17
+ export { ColorPicker, type ColorPickerProps };
@@ -0,0 +1,158 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useId, useMemo, useRef, useState } from "react";
4
+ import { cx } from "../cx.js";
5
+ import { contrastRatio, hexToRgb, hsvToHex, normalizeHex, rgbToHsv } from "../color-model.js";
6
+ const EMPTY_COLORS = [];
7
+ function ColorPicker({
8
+ value,
9
+ onChange,
10
+ foreground,
11
+ presets = EMPTY_COLORS,
12
+ recent = EMPTY_COLORS,
13
+ minimumContrast = 4.5,
14
+ label = "Choose color",
15
+ disabled,
16
+ className
17
+ }) {
18
+ const id = useId();
19
+ const triggerRef = useRef(null);
20
+ const dialogRef = useRef(null);
21
+ const [open, setOpen] = useState(false);
22
+ const normalized = normalizeHex(value) ?? "#000000";
23
+ const [hexText, setHexText] = useState(normalized);
24
+ const hsv = useMemo(() => rgbToHsv(hexToRgb(normalized)), [normalized]);
25
+ const ratio = contrastRatio(normalized, foreground) ?? 0;
26
+ const passes = ratio >= minimumContrast;
27
+ useEffect(() => setHexText(normalized), [normalized]);
28
+ useEffect(() => {
29
+ if (!open) return;
30
+ const dialog = dialogRef.current;
31
+ dialog?.querySelector("input,button")?.focus();
32
+ const onPointer = (event) => {
33
+ if (!dialog?.contains(event.target) && !triggerRef.current?.contains(event.target)) setOpen(false);
34
+ };
35
+ document.addEventListener("mousedown", onPointer);
36
+ return () => document.removeEventListener("mousedown", onPointer);
37
+ }, [open]);
38
+ const close = () => {
39
+ setOpen(false);
40
+ requestAnimationFrame(() => triggerRef.current?.focus());
41
+ };
42
+ const onDialogKeyDown = (event) => {
43
+ if (event.key === "Escape") {
44
+ event.preventDefault();
45
+ close();
46
+ return;
47
+ }
48
+ if (event.key !== "Tab") return;
49
+ const focusable = Array.from(dialogRef.current?.querySelectorAll('button:not([disabled]),input:not([disabled]),[tabindex="0"]') ?? []);
50
+ if (!focusable.length) return;
51
+ const first = focusable[0];
52
+ const last = focusable[focusable.length - 1];
53
+ if (event.shiftKey && document.activeElement === first) {
54
+ event.preventDefault();
55
+ last.focus();
56
+ } else if (!event.shiftKey && document.activeElement === last) {
57
+ event.preventDefault();
58
+ first.focus();
59
+ }
60
+ };
61
+ const updateHsv = (part) => onChange(hsvToHex({ ...hsv, ...part }));
62
+ const commitHex = () => {
63
+ const next = normalizeHex(hexText);
64
+ if (next) onChange(next);
65
+ };
66
+ const choose = (candidate) => {
67
+ const next = normalizeHex(candidate);
68
+ if (next) onChange(next);
69
+ };
70
+ const sampleStyle = { backgroundColor: normalized };
71
+ return /* @__PURE__ */ jsxs("div", { className: cx("uix-color-picker", className), children: [
72
+ /* @__PURE__ */ jsxs(
73
+ "button",
74
+ {
75
+ ref: triggerRef,
76
+ type: "button",
77
+ className: "uix-color-picker__trigger",
78
+ "aria-label": `${label}: ${normalized}`,
79
+ "aria-expanded": open,
80
+ "aria-controls": `${id}-dialog`,
81
+ onClick: () => setOpen((current) => !current),
82
+ disabled,
83
+ children: [
84
+ /* @__PURE__ */ jsx("span", { className: "uix-color-picker__swatch", style: sampleStyle, "aria-hidden": "true" }),
85
+ /* @__PURE__ */ jsx("span", { children: normalized })
86
+ ]
87
+ }
88
+ ),
89
+ open && /* @__PURE__ */ jsxs(
90
+ "div",
91
+ {
92
+ ref: dialogRef,
93
+ id: `${id}-dialog`,
94
+ role: "dialog",
95
+ "aria-modal": "true",
96
+ "aria-label": label,
97
+ className: "uix-color-picker__popover",
98
+ onKeyDown: onDialogKeyDown,
99
+ children: [
100
+ /* @__PURE__ */ jsx("div", { className: "uix-color-picker__preview", style: sampleStyle, children: /* @__PURE__ */ jsx("span", { style: { color: foreground }, children: "Aa" }) }),
101
+ /* @__PURE__ */ jsxs("label", { className: "uix-color-picker__range", children: [
102
+ "Hue ",
103
+ /* @__PURE__ */ jsxs("span", { children: [
104
+ Math.round(hsv.h),
105
+ "\xB0"
106
+ ] }),
107
+ /* @__PURE__ */ jsx("input", { type: "range", min: "0", max: "359", value: Math.round(hsv.h), onChange: (event) => updateHsv({ h: Number(event.currentTarget.value) }) })
108
+ ] }),
109
+ /* @__PURE__ */ jsxs("label", { className: "uix-color-picker__range", children: [
110
+ "Saturation ",
111
+ /* @__PURE__ */ jsxs("span", { children: [
112
+ Math.round(hsv.s * 100),
113
+ "%"
114
+ ] }),
115
+ /* @__PURE__ */ jsx("input", { type: "range", min: "0", max: "100", value: Math.round(hsv.s * 100), onChange: (event) => updateHsv({ s: Number(event.currentTarget.value) / 100 }) })
116
+ ] }),
117
+ /* @__PURE__ */ jsxs("label", { className: "uix-color-picker__range", children: [
118
+ "Brightness ",
119
+ /* @__PURE__ */ jsxs("span", { children: [
120
+ Math.round(hsv.v * 100),
121
+ "%"
122
+ ] }),
123
+ /* @__PURE__ */ jsx("input", { type: "range", min: "0", max: "100", value: Math.round(hsv.v * 100), onChange: (event) => updateHsv({ v: Number(event.currentTarget.value) / 100 }) })
124
+ ] }),
125
+ /* @__PURE__ */ jsxs("label", { className: "uix-color-picker__hex", children: [
126
+ "Hex",
127
+ /* @__PURE__ */ jsxs("span", { className: "uix-color-picker__hex-row", children: [
128
+ /* @__PURE__ */ jsx("input", { className: "uix-input", value: hexText, onChange: (event) => setHexText(event.currentTarget.value), onBlur: commitHex, onKeyDown: (event) => {
129
+ if (event.key === "Enter") commitHex();
130
+ }, "aria-invalid": !normalizeHex(hexText) }),
131
+ /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", onClick: commitHex, disabled: !normalizeHex(hexText), children: "Set" })
132
+ ] })
133
+ ] }),
134
+ (presets.length > 0 || recent.length > 0) && /* @__PURE__ */ jsxs("div", { className: "uix-color-picker__palettes", children: [
135
+ presets.length > 0 && /* @__PURE__ */ jsxs("fieldset", { children: [
136
+ /* @__PURE__ */ jsx("legend", { children: "Presets" }),
137
+ /* @__PURE__ */ jsx("div", { children: presets.map((color) => /* @__PURE__ */ jsx("button", { type: "button", className: "uix-color-picker__preset", style: { backgroundColor: normalizeHex(color) ?? color }, onClick: () => choose(color), "aria-label": `Use preset ${color}` }, color)) })
138
+ ] }),
139
+ recent.length > 0 && /* @__PURE__ */ jsxs("fieldset", { children: [
140
+ /* @__PURE__ */ jsx("legend", { children: "Recent" }),
141
+ /* @__PURE__ */ jsx("div", { children: recent.map((color) => /* @__PURE__ */ jsx("button", { type: "button", className: "uix-color-picker__preset", style: { backgroundColor: normalizeHex(color) ?? color }, onClick: () => choose(color), "aria-label": `Use recent color ${color}` }, color)) })
142
+ ] })
143
+ ] }),
144
+ /* @__PURE__ */ 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: [
145
+ "Contrast ",
146
+ ratio.toFixed(2),
147
+ ":1 \u2014 ",
148
+ passes ? `passes WCAG AA (${minimumContrast}:1)` : `does not meet WCAG AA (${minimumContrast}:1)`
149
+ ] }),
150
+ /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--primary uix-btn--sm", onClick: close, children: "Done" })
151
+ ]
152
+ }
153
+ )
154
+ ] });
155
+ }
156
+ export {
157
+ ColorPicker
158
+ };
@@ -0,0 +1,23 @@
1
+ import * as react from 'react';
2
+ import { DateRangeValue } from '../calendar-model.js';
3
+
4
+ interface DateRangePickerProps {
5
+ value: DateRangeValue;
6
+ onChange: (value: DateRangeValue) => void;
7
+ visibleMonth?: string;
8
+ onVisibleMonthChange?: (month: string) => void;
9
+ months?: number;
10
+ min?: string;
11
+ max?: string;
12
+ isDateDisabled?: (date: string) => boolean;
13
+ locale?: string;
14
+ label?: string;
15
+ invalid?: boolean;
16
+ invalidMessage?: string;
17
+ disabled?: boolean;
18
+ className?: string;
19
+ }
20
+ /** Controlled, two-month range picker using the UIx calendar language. */
21
+ declare function DateRangePicker({ value, onChange, visibleMonth: controlledMonth, onVisibleMonthChange, months, min, max, isDateDisabled, locale, label, invalid, invalidMessage, disabled, className, }: DateRangePickerProps): react.JSX.Element;
22
+
23
+ export { DateRangePicker, type DateRangePickerProps };
@@ -0,0 +1,123 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useId, useMemo, useRef, useState } from "react";
4
+ import { addCalendarDays, addCalendarMonths, buildMonthGrid, isDateInRange, isDateUnavailable, selectRangeDate, startOfMonth } from "../calendar-model.js";
5
+ import { cx } from "../cx.js";
6
+ const monthLabel = (date, locale) => new Intl.DateTimeFormat(locale, { month: "long", year: "numeric", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${date}T00:00:00Z`));
7
+ const dateLabel = (date, locale) => new Intl.DateTimeFormat(locale, { dateStyle: "full", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${date}T00:00:00Z`));
8
+ function DateRangePicker({
9
+ value,
10
+ onChange,
11
+ visibleMonth: controlledMonth,
12
+ onVisibleMonthChange,
13
+ months = 2,
14
+ min,
15
+ max,
16
+ isDateDisabled,
17
+ locale,
18
+ label = "Choose date range",
19
+ invalid,
20
+ invalidMessage = "The selected date range is invalid.",
21
+ disabled,
22
+ className
23
+ }) {
24
+ const id = useId();
25
+ const [internalMonth, setInternalMonth] = useState(() => startOfMonth(controlledMonth ?? value.start ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)));
26
+ const visibleMonth = startOfMonth(controlledMonth ?? internalMonth);
27
+ const [activeDate, setActiveDate] = useState(value.end ?? value.start ?? visibleMonth);
28
+ const rootRef = useRef(null);
29
+ const pendingFocus = useRef(false);
30
+ const count = Math.max(1, Math.min(3, months));
31
+ const calendars = useMemo(() => Array.from({ length: count }, (_, index) => {
32
+ const month = startOfMonth(addCalendarMonths(visibleMonth, index));
33
+ return { month, days: buildMonthGrid(month).filter((day) => day.inMonth) };
34
+ }), [visibleMonth, count]);
35
+ const rangeInvalid = invalid || !!value.start && !!value.end && value.start > value.end;
36
+ useEffect(() => {
37
+ const first = calendars[0]?.days[0]?.date;
38
+ const finalDays = calendars[calendars.length - 1]?.days;
39
+ const last = finalDays?.[finalDays.length - 1]?.date;
40
+ if (first && last && (activeDate < first || activeDate > last)) setActiveDate(first);
41
+ }, [activeDate, calendars]);
42
+ useEffect(() => {
43
+ if (!pendingFocus.current) return;
44
+ rootRef.current?.querySelector(`[data-date="${activeDate}"]`)?.focus();
45
+ pendingFocus.current = false;
46
+ }, [activeDate, visibleMonth]);
47
+ const setMonth = (month) => {
48
+ const next = startOfMonth(month);
49
+ if (controlledMonth === void 0) setInternalMonth(next);
50
+ onVisibleMonthChange?.(next);
51
+ };
52
+ const focusDate = (date) => {
53
+ setActiveDate(date);
54
+ if (date < calendars[0].month || date.slice(0, 7) > calendars[calendars.length - 1].month.slice(0, 7)) setMonth(startOfMonth(date));
55
+ pendingFocus.current = true;
56
+ };
57
+ const choose = (date) => {
58
+ if (disabled || isDateUnavailable(date, { min, max, isDisabled: isDateDisabled })) return;
59
+ onChange(selectRangeDate(value, date));
60
+ setActiveDate(date);
61
+ };
62
+ const onDateKeyDown = (event, date) => {
63
+ const offsets = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 };
64
+ if (event.key in offsets) {
65
+ event.preventDefault();
66
+ focusDate(addCalendarDays(date, offsets[event.key]));
67
+ return;
68
+ }
69
+ if (event.key === "Home" || event.key === "End") {
70
+ event.preventDefault();
71
+ const weekday = (/* @__PURE__ */ new Date(`${date}T00:00:00Z`)).getUTCDay();
72
+ const mondayIndex = (weekday + 6) % 7;
73
+ focusDate(addCalendarDays(date, event.key === "Home" ? -mondayIndex : 6 - mondayIndex));
74
+ } else if (event.key === "PageUp" || event.key === "PageDown") {
75
+ event.preventDefault();
76
+ focusDate(addCalendarMonths(date, event.key === "PageUp" ? -1 : 1));
77
+ }
78
+ };
79
+ const announcement = !value.start ? "No date range selected." : !value.end ? `Start date ${dateLabel(value.start, locale)} selected. Choose an end date.` : `${dateLabel(value.start, locale)} to ${dateLabel(value.end, locale)} selected.`;
80
+ return /* @__PURE__ */ jsxs("section", { className: cx("uix-date-range-picker", rangeInvalid && "uix-date-range-picker--invalid", className), "aria-labelledby": `${id}-label`, children: [
81
+ /* @__PURE__ */ jsxs("div", { className: "uix-date-range-picker__header", children: [
82
+ /* @__PURE__ */ jsx("h3", { id: `${id}-label`, children: label }),
83
+ /* @__PURE__ */ jsxs("div", { children: [
84
+ /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => setMonth(addCalendarMonths(visibleMonth, -1)), "aria-label": "Show previous month", children: "Previous" }),
85
+ /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => setMonth(addCalendarMonths(visibleMonth, 1)), "aria-label": "Show next month", children: "Next" })
86
+ ] })
87
+ ] }),
88
+ /* @__PURE__ */ jsx("div", { ref: rootRef, className: "uix-date-range-picker__months", children: calendars.map(({ month, days }) => /* @__PURE__ */ jsxs("div", { className: "uix-date-range-picker__month", children: [
89
+ /* @__PURE__ */ jsx("h4", { children: monthLabel(month, locale) }),
90
+ /* @__PURE__ */ jsx("div", { className: "uix-date-range-picker__weekdays", "aria-hidden": "true", children: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].map((day) => /* @__PURE__ */ jsx("span", { children: day }, day)) }),
91
+ /* @__PURE__ */ jsxs("div", { className: "uix-date-range-picker__grid", role: "group", "aria-label": monthLabel(month, locale), children: [
92
+ Array.from({ length: ((/* @__PURE__ */ new Date(`${month}T00:00:00Z`)).getUTCDay() + 6) % 7 }, (_, index) => /* @__PURE__ */ jsx("span", { "aria-hidden": "true" }, `blank-${index}`)),
93
+ days.map((day) => {
94
+ const unavailable = disabled || isDateUnavailable(day.date, { min, max, isDisabled: isDateDisabled });
95
+ const edge = day.date === value.start ? "start" : day.date === value.end ? "end" : void 0;
96
+ return /* @__PURE__ */ jsx(
97
+ "button",
98
+ {
99
+ type: "button",
100
+ "data-date": day.date,
101
+ "data-range-edge": edge,
102
+ "data-in-range": isDateInRange(day.date, value) || void 0,
103
+ "aria-pressed": edge != null || isDateInRange(day.date, value),
104
+ "aria-label": `${dateLabel(day.date, locale)}${edge ? `, ${edge} of selected range` : isDateInRange(day.date, value) ? ", in selected range" : ""}`,
105
+ tabIndex: activeDate === day.date ? 0 : -1,
106
+ disabled: unavailable,
107
+ onFocus: () => setActiveDate(day.date),
108
+ onKeyDown: (event) => onDateKeyDown(event, day.date),
109
+ onClick: () => choose(day.date),
110
+ children: day.day
111
+ },
112
+ day.date
113
+ );
114
+ })
115
+ ] })
116
+ ] }, month)) }),
117
+ /* @__PURE__ */ jsx("p", { className: "uix-date-range-picker__selection", "aria-live": "polite", children: announcement }),
118
+ rangeInvalid && /* @__PURE__ */ jsx("p", { className: "uix-field__error", role: "alert", children: invalidMessage })
119
+ ] });
120
+ }
121
+ export {
122
+ DateRangePicker
123
+ };
@@ -0,0 +1,18 @@
1
+ import * as react from 'react';
2
+ import { JsonValue } from '../json-value.js';
3
+ import { DiffEntry, DiffResolution } from '../diff-model.js';
4
+
5
+ interface DiffViewerProps {
6
+ base?: JsonValue;
7
+ current?: JsonValue;
8
+ incoming?: JsonValue;
9
+ entries?: DiffEntry[];
10
+ resolutions?: Record<string, DiffResolution>;
11
+ onResolutionChange?: (path: string, resolution: DiffResolution) => void;
12
+ labels?: Partial<Record<'base' | 'current' | 'incoming', string>>;
13
+ className?: string;
14
+ }
15
+ /** Three-way configuration diff with per-entry keyboard-operable resolution. */
16
+ declare function DiffViewer({ base, current, incoming, entries: providedEntries, resolutions: controlledResolutions, onResolutionChange, labels, className, }: DiffViewerProps): react.JSX.Element;
17
+
18
+ export { DiffViewer, type DiffViewerProps };
@@ -0,0 +1,93 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useId, useMemo, useState } from "react";
4
+ import { buildThreeWayDiff, summarizeDiff } from "../diff-model.js";
5
+ import { cx } from "../cx.js";
6
+ import { StatusPill } from "./StatusPill.js";
7
+ const GROUPS = [
8
+ { kind: "conflicted", label: "Conflicted" },
9
+ { kind: "changed", label: "Changed" },
10
+ { kind: "added", label: "Added" },
11
+ { kind: "removed", label: "Removed" }
12
+ ];
13
+ const DEFAULT_LABELS = {};
14
+ const renderValue = (value) => value === void 0 ? "Not present" : JSON.stringify(value, null, 2);
15
+ function DiffViewer({
16
+ base,
17
+ current,
18
+ incoming,
19
+ entries: providedEntries,
20
+ resolutions: controlledResolutions,
21
+ onResolutionChange,
22
+ labels = DEFAULT_LABELS,
23
+ className
24
+ }) {
25
+ const id = useId();
26
+ const entries = useMemo(() => providedEntries ?? buildThreeWayDiff(base, current, incoming), [providedEntries, base, current, incoming]);
27
+ const [internalResolutions, setInternalResolutions] = useState({});
28
+ const resolutions = controlledResolutions ?? internalResolutions;
29
+ const summary = summarizeDiff(entries, resolutions);
30
+ const resolve = (path, resolution) => {
31
+ if (controlledResolutions === void 0) setInternalResolutions((currentState) => ({ ...currentState, [path]: resolution }));
32
+ onResolutionChange?.(path, resolution);
33
+ };
34
+ if (entries.length === 0) return /* @__PURE__ */ jsxs("section", { className: cx("uix-diff-viewer uix-diff-viewer--empty", className), "aria-label": "Configuration differences", children: [
35
+ /* @__PURE__ */ jsx("h3", { children: "No differences" }),
36
+ /* @__PURE__ */ jsx("p", { children: "Base, current, and incoming values are aligned." })
37
+ ] });
38
+ return /* @__PURE__ */ jsxs("section", { className: cx("uix-diff-viewer", className), "aria-label": "Configuration differences", children: [
39
+ /* @__PURE__ */ jsxs("div", { className: "uix-diff-viewer__summary", "aria-label": "Difference summary", children: [
40
+ GROUPS.map((group) => /* @__PURE__ */ jsxs("span", { children: [
41
+ /* @__PURE__ */ jsx("b", { children: summary[group.kind] }),
42
+ " ",
43
+ group.label.toLowerCase()
44
+ ] }, group.kind)),
45
+ /* @__PURE__ */ jsxs("span", { children: [
46
+ /* @__PURE__ */ jsx("b", { children: summary.resolved }),
47
+ " resolved"
48
+ ] }),
49
+ /* @__PURE__ */ jsxs("span", { children: [
50
+ /* @__PURE__ */ jsx("b", { children: summary.pending }),
51
+ " pending"
52
+ ] })
53
+ ] }),
54
+ GROUPS.map((group) => {
55
+ const grouped = entries.filter((entry) => entry.kind === group.kind);
56
+ if (!grouped.length) return null;
57
+ return /* @__PURE__ */ jsxs("section", { className: "uix-diff-viewer__group", "aria-labelledby": `${id}-${group.kind}`, children: [
58
+ /* @__PURE__ */ jsxs("h3", { id: `${id}-${group.kind}`, children: [
59
+ group.label,
60
+ " ",
61
+ /* @__PURE__ */ jsx("span", { children: grouped.length })
62
+ ] }),
63
+ grouped.map((entry) => {
64
+ const resolution = resolutions[entry.path] ?? "pending";
65
+ return /* @__PURE__ */ jsxs("details", { className: "uix-diff-viewer__entry", "data-kind": entry.kind, open: entry.kind === "conflicted", children: [
66
+ /* @__PURE__ */ jsxs("summary", { children: [
67
+ /* @__PURE__ */ jsx("code", { children: entry.path }),
68
+ /* @__PURE__ */ jsx(StatusPill, { tone: resolution === "pending" ? "warning" : "success", children: resolution })
69
+ ] }),
70
+ /* @__PURE__ */ jsx("div", { className: "uix-diff-viewer__versions", children: ["base", "current", "incoming"].map((version) => /* @__PURE__ */ jsxs("div", { role: "region", "aria-label": `${labels[version] ?? `${version[0].toUpperCase()}${version.slice(1)}`} value for ${entry.path}`, children: [
71
+ /* @__PURE__ */ jsx("strong", { children: labels[version] ?? `${version[0].toUpperCase()}${version.slice(1)}` }),
72
+ /* @__PURE__ */ jsx("pre", { children: renderValue(entry[version]) })
73
+ ] }, version)) }),
74
+ /* @__PURE__ */ jsxs("div", { className: "uix-diff-viewer__actions", "aria-label": `Resolve ${entry.path}`, children: [
75
+ /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--primary uix-btn--sm", "aria-pressed": resolution === "accept", onClick: () => resolve(entry.path, "accept"), children: "Accept incoming" }),
76
+ /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", "aria-pressed": resolution === "skip", onClick: () => resolve(entry.path, "skip"), children: "Keep current" }),
77
+ /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", "aria-pressed": resolution === "pending", onClick: () => resolve(entry.path, "pending"), children: "Mark pending" })
78
+ ] })
79
+ ] }, entry.path);
80
+ })
81
+ ] }, group.kind);
82
+ }),
83
+ /* @__PURE__ */ jsxs("p", { className: "uix-visually-hidden", "aria-live": "polite", children: [
84
+ summary.resolved,
85
+ " of ",
86
+ entries.length,
87
+ " differences resolved."
88
+ ] })
89
+ ] });
90
+ }
91
+ export {
92
+ DiffViewer
93
+ };
@@ -1,4 +1,3 @@
1
- "use client";
2
1
  import { jsx, jsxs } from "react/jsx-runtime";
3
2
  import { cx } from "../cx.js";
4
3
  function Inbox({ children, className, ...props }) {
@@ -0,0 +1,17 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes } from 'react';
3
+
4
+ interface LicensePositionBarProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
5
+ consumed: number;
6
+ entitled: number;
7
+ consumedLabel?: string;
8
+ entitledLabel?: string;
9
+ availableLabel?: string;
10
+ overLabel?: string;
11
+ unit?: string;
12
+ formatValue?: (value: number) => string;
13
+ }
14
+ /** Capacity-versus-limit recipe with a visible and textual over-allocation state. */
15
+ declare function LicensePositionBar({ consumed, entitled, consumedLabel, entitledLabel, availableLabel, overLabel, unit, formatValue, className, ...props }: LicensePositionBarProps): react.JSX.Element;
16
+
17
+ export { LicensePositionBar, type LicensePositionBarProps };
@@ -0,0 +1,50 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cx } from "../cx.js";
3
+ import { Meter } from "./Meter.js";
4
+ import { StatusPill } from "./StatusPill.js";
5
+ const defaultCapacityFormat = (value) => new Intl.NumberFormat().format(value);
6
+ function LicensePositionBar({
7
+ consumed,
8
+ entitled,
9
+ consumedLabel = "Consumed",
10
+ entitledLabel = "Entitled",
11
+ availableLabel = "available",
12
+ overLabel = "over limit",
13
+ unit = "",
14
+ formatValue = defaultCapacityFormat,
15
+ className,
16
+ ...props
17
+ }) {
18
+ const safeConsumed = Math.max(0, consumed);
19
+ const safeEntitled = Math.max(0, entitled);
20
+ const over = Math.max(0, safeConsumed - safeEntitled);
21
+ const available = Math.max(0, safeEntitled - safeConsumed);
22
+ const percent = safeEntitled === 0 ? safeConsumed > 0 ? 100 : 0 : safeConsumed / safeEntitled * 100;
23
+ const format = (value) => `${formatValue(value)}${unit ? ` ${unit}` : ""}`;
24
+ const summary = `${consumedLabel}: ${format(safeConsumed)}. ${entitledLabel}: ${format(safeEntitled)}. ${over > 0 ? `${format(over)} ${overLabel}` : `${format(available)} ${availableLabel}`}.`;
25
+ return /* @__PURE__ */ jsxs("div", { className: cx("uix-license-position", over > 0 && "uix-license-position--over", className), ...props, children: [
26
+ /* @__PURE__ */ jsxs("div", { className: "uix-license-position__header", children: [
27
+ /* @__PURE__ */ jsxs("span", { className: "uix-license-position__value", children: [
28
+ format(safeConsumed),
29
+ " / ",
30
+ format(safeEntitled)
31
+ ] }),
32
+ /* @__PURE__ */ jsx(StatusPill, { tone: over > 0 ? "danger" : percent >= 80 ? "warning" : "success", children: over > 0 ? `${format(over)} ${overLabel}` : `${format(available)} ${availableLabel}` })
33
+ ] }),
34
+ /* @__PURE__ */ jsxs("div", { className: "uix-license-position__track", children: [
35
+ /* @__PURE__ */ jsx(Meter, { value: Math.min(100, percent), tone: over > 0 ? "danger" : percent >= 80 ? "warning" : "success", "aria-label": summary, "aria-valuetext": summary }),
36
+ over > 0 && /* @__PURE__ */ jsx(
37
+ "span",
38
+ {
39
+ className: "uix-license-position__overflow",
40
+ style: { width: `${Math.min(100, safeEntitled === 0 ? 100 : over / safeEntitled * 100)}%` },
41
+ "aria-hidden": "true"
42
+ }
43
+ )
44
+ ] }),
45
+ /* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: summary })
46
+ ] });
47
+ }
48
+ export {
49
+ LicensePositionBar
50
+ };
@@ -0,0 +1,39 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { JsonValue } from '../json-value.js';
4
+
5
+ interface MatchReviewField {
6
+ id: string;
7
+ label: string;
8
+ render?: (value: JsonValue | undefined, record: Record<string, JsonValue>) => ReactNode;
9
+ }
10
+ interface MatchReviewCandidate {
11
+ id: string;
12
+ record: Record<string, JsonValue>;
13
+ confidence?: number;
14
+ status?: 'pending' | 'resolved';
15
+ decision?: 'accepted' | 'dismissed' | 'split' | 'other';
16
+ }
17
+ type MatchReviewAction = 'accept' | 'dismiss' | 'split' | 'pick-other';
18
+ interface MatchBulkResult {
19
+ succeeded: number;
20
+ failed?: number;
21
+ }
22
+ interface MatchReviewProps {
23
+ incoming: Record<string, JsonValue>;
24
+ incomingLabel?: string;
25
+ fields: MatchReviewField[];
26
+ candidates: MatchReviewCandidate[];
27
+ selectedIds?: Set<string>;
28
+ onSelectionChange?: (ids: Set<string>) => void;
29
+ onDecision: (candidateId: string, action: MatchReviewAction) => void;
30
+ onBulkDecision?: (candidateIds: string[], action: Extract<MatchReviewAction, 'accept' | 'dismiss'>) => void | MatchBulkResult | Promise<void | MatchBulkResult>;
31
+ loading?: boolean;
32
+ error?: string;
33
+ onRetry?: () => void;
34
+ className?: string;
35
+ }
36
+ /** Generic descriptor-driven incoming-record to candidate-match decision surface. */
37
+ declare function MatchReview({ incoming, incomingLabel, fields, candidates, selectedIds, onSelectionChange, onDecision, onBulkDecision, loading, error, onRetry, className, }: MatchReviewProps): react.JSX.Element;
38
+
39
+ export { type MatchBulkResult, MatchReview, type MatchReviewAction, type MatchReviewCandidate, type MatchReviewField, type MatchReviewProps };