@tensor_1/react 2.12.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.
- package/README.md +16 -0
- package/dist/calendar-model.d.ts +31 -0
- package/dist/calendar-model.js +89 -0
- package/dist/color-model.d.ts +20 -0
- package/dist/color-model.js +74 -0
- package/dist/components/BrandProfiles.d.ts +45 -0
- package/dist/components/BrandProfiles.js +150 -0
- package/dist/components/BuilderCanvas.d.ts +34 -0
- package/dist/components/BuilderCanvas.js +117 -0
- package/dist/components/ColorPicker.d.ts +17 -0
- package/dist/components/ColorPicker.js +158 -0
- package/dist/components/DateRangePicker.d.ts +23 -0
- package/dist/components/DateRangePicker.js +123 -0
- package/dist/components/DiffViewer.d.ts +18 -0
- package/dist/components/DiffViewer.js +93 -0
- package/dist/components/LicensePositionBar.d.ts +17 -0
- package/dist/components/LicensePositionBar.js +50 -0
- package/dist/components/MatchReview.d.ts +39 -0
- package/dist/components/MatchReview.js +120 -0
- package/dist/components/MetricInput.d.ts +23 -0
- package/dist/components/MetricInput.js +92 -0
- package/dist/components/RelationshipGraph.d.ts +30 -0
- package/dist/components/RelationshipGraph.js +138 -0
- package/dist/components/RuleBuilder.d.ts +45 -0
- package/dist/components/RuleBuilder.js +163 -0
- package/dist/components/SchedulingCalendar.d.ts +42 -0
- package/dist/components/SchedulingCalendar.js +124 -0
- package/dist/diff-model.d.ts +23 -0
- package/dist/diff-model.js +50 -0
- package/dist/index.cjs +1753 -2
- package/dist/index.d.cts +439 -1
- package/dist/index.d.ts +18 -0
- package/dist/index.js +91 -1
- package/dist/json-value.d.ts +10 -0
- package/dist/json-value.js +19 -0
- package/dist/metric-model.d.ts +9 -0
- package/dist/metric-model.js +25 -0
- package/dist/relationship-graph-model.d.ts +31 -0
- package/dist/relationship-graph-model.js +68 -0
- package/dist/rule-builder-model.d.ts +41 -0
- package/dist/rule-builder-model.js +101 -0
- package/package.json +1 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { addCalendarDays, addCalendarMonths, buildMonthGrid, startOfMonth, zonedDateSpan } from "../calendar-model.js";
|
|
5
|
+
import { cx } from "../cx.js";
|
|
6
|
+
import { StatusPill } from "./StatusPill.js";
|
|
7
|
+
const stateLabel = { scheduled: "Scheduled", conflicted: "Conflicted", "in-progress": "In progress", "blackout-violation": "Blackout violation" };
|
|
8
|
+
const stateTone = (state) => state === "scheduled" ? "info" : state === "in-progress" ? "success" : "danger";
|
|
9
|
+
const dateLabel = (date, locale) => new Intl.DateTimeFormat(locale, { dateStyle: "full", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${date}T00:00:00Z`));
|
|
10
|
+
const EMPTY_OVERLAYS = [];
|
|
11
|
+
function SchedulingCalendar({
|
|
12
|
+
entries,
|
|
13
|
+
anchorDate,
|
|
14
|
+
timeZone,
|
|
15
|
+
view: controlledView,
|
|
16
|
+
onViewChange,
|
|
17
|
+
onAnchorDateChange,
|
|
18
|
+
onSelectEntry,
|
|
19
|
+
overlays = EMPTY_OVERLAYS,
|
|
20
|
+
filter,
|
|
21
|
+
renderEntry,
|
|
22
|
+
locale,
|
|
23
|
+
loading,
|
|
24
|
+
error,
|
|
25
|
+
onRetry,
|
|
26
|
+
className
|
|
27
|
+
}) {
|
|
28
|
+
const [internalView, setInternalView] = useState("month");
|
|
29
|
+
const view = controlledView ?? internalView;
|
|
30
|
+
const [activeDate, setActiveDate] = useState(anchorDate);
|
|
31
|
+
const gridRef = useRef(null);
|
|
32
|
+
const visibleEntries = useMemo(() => entries.filter((entry) => filter?.(entry) ?? true), [entries, filter]);
|
|
33
|
+
const entrySpans = useMemo(() => new Map(visibleEntries.map((entry) => [entry.id, zonedDateSpan(entry.start, entry.end, timeZone)])), [visibleEntries, timeZone]);
|
|
34
|
+
const overlaySpans = useMemo(() => new Map(overlays.map((overlay) => [overlay.id, { start: overlay.start.slice(0, 10), end: overlay.end.slice(0, 10) }])), [overlays]);
|
|
35
|
+
const monthDays = useMemo(() => buildMonthGrid(startOfMonth(anchorDate)), [anchorDate]);
|
|
36
|
+
const weekday = ((/* @__PURE__ */ new Date(`${anchorDate}T00:00:00Z`)).getUTCDay() + 6) % 7;
|
|
37
|
+
const weekStart = addCalendarDays(anchorDate, -weekday);
|
|
38
|
+
const weekDays = useMemo(() => Array.from({ length: 7 }, (_, index) => addCalendarDays(weekStart, index)), [weekStart]);
|
|
39
|
+
const days = useMemo(() => view === "month" ? monthDays.map((day) => day.date) : weekDays, [view, monthDays, weekDays]);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (view !== "agenda" && !days.includes(activeDate)) setActiveDate(days[0]);
|
|
42
|
+
}, [activeDate, days, view]);
|
|
43
|
+
const setView = (next) => {
|
|
44
|
+
if (controlledView === void 0) setInternalView(next);
|
|
45
|
+
onViewChange?.(next);
|
|
46
|
+
};
|
|
47
|
+
const movePeriod = (direction) => onAnchorDateChange?.(view === "month" ? addCalendarMonths(anchorDate, direction) : addCalendarDays(anchorDate, direction * 7));
|
|
48
|
+
const focusDate = (date) => {
|
|
49
|
+
setActiveDate(date);
|
|
50
|
+
requestAnimationFrame(() => gridRef.current?.querySelector(`[data-calendar-date="${date}"]`)?.focus());
|
|
51
|
+
};
|
|
52
|
+
const onDayKeyDown = (event, date) => {
|
|
53
|
+
const offset = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 }[event.key];
|
|
54
|
+
if (offset) {
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
focusDate(addCalendarDays(date, offset));
|
|
57
|
+
} else if (event.key === "Home") {
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
focusDate(days[0]);
|
|
60
|
+
} else if (event.key === "End") {
|
|
61
|
+
event.preventDefault();
|
|
62
|
+
focusDate(days[days.length - 1]);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
const entriesFor = (date) => visibleEntries.filter((entry) => {
|
|
66
|
+
const span = entrySpans.get(entry.id);
|
|
67
|
+
return date >= span.start && date <= span.end;
|
|
68
|
+
});
|
|
69
|
+
const overlaysFor = (date) => overlays.filter((overlay) => {
|
|
70
|
+
const span = overlaySpans.get(overlay.id);
|
|
71
|
+
return date >= span.start && date <= span.end;
|
|
72
|
+
});
|
|
73
|
+
const formatInstant = (instant) => new Intl.DateTimeFormat(locale, { timeZone, dateStyle: "medium", timeStyle: "short" }).format(new Date(instant));
|
|
74
|
+
return /* @__PURE__ */ jsxs("section", { className: cx("uix-scheduling-calendar", className), "aria-label": `Scheduling calendar in ${timeZone}`, children: [
|
|
75
|
+
/* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__header", children: [
|
|
76
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
77
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => movePeriod(-1), disabled: !onAnchorDateChange, children: "Previous" }),
|
|
78
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => movePeriod(1), disabled: !onAnchorDateChange, children: "Next" })
|
|
79
|
+
] }),
|
|
80
|
+
/* @__PURE__ */ jsx("strong", { children: view === "month" ? new Intl.DateTimeFormat(locale, { month: "long", year: "numeric", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${anchorDate}T00:00:00Z`)) : `${dateLabel(weekStart, locale)} \u2013 ${dateLabel(weekDays[weekDays.length - 1], locale)}` }),
|
|
81
|
+
/* @__PURE__ */ jsx("div", { className: "uix-segmented", role: "group", "aria-label": "Calendar view", children: ["month", "week", "agenda"].map((option) => /* @__PURE__ */ jsxs("button", { type: "button", className: "uix-segmented__option", "aria-pressed": view === option, "data-selected": view === option || void 0, onClick: () => setView(option), children: [
|
|
82
|
+
option[0].toUpperCase(),
|
|
83
|
+
option.slice(1)
|
|
84
|
+
] }, option)) })
|
|
85
|
+
] }),
|
|
86
|
+
loading ? /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__state", role: "status", children: "Loading schedule\u2026" }) : error ? /* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__state", role: "alert", children: [
|
|
87
|
+
/* @__PURE__ */ jsx("p", { children: error }),
|
|
88
|
+
onRetry && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children: "Try again" })
|
|
89
|
+
] }) : view === "agenda" ? /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__agenda", role: "region", "aria-label": "Schedule agenda", children: visibleEntries.length === 0 ? /* @__PURE__ */ jsx("p", { children: "No scheduled entries match the current filters." }) : /* @__PURE__ */ jsx("ol", { children: [...visibleEntries].sort((a, b) => a.start.localeCompare(b.start)).map((entry) => {
|
|
90
|
+
const state = entry.state ?? "scheduled";
|
|
91
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => onSelectEntry?.(entry), children: [
|
|
92
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
93
|
+
/* @__PURE__ */ jsx("strong", { children: renderEntry?.(entry) ?? entry.title }),
|
|
94
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
95
|
+
formatInstant(entry.start),
|
|
96
|
+
" \u2013 ",
|
|
97
|
+
formatInstant(entry.end)
|
|
98
|
+
] }),
|
|
99
|
+
entry.meta && /* @__PURE__ */ jsx("span", { children: entry.meta })
|
|
100
|
+
] }),
|
|
101
|
+
/* @__PURE__ */ jsx(StatusPill, { tone: stateTone(state), children: stateLabel[state] })
|
|
102
|
+
] }) }, entry.id);
|
|
103
|
+
}) }) }) : /* @__PURE__ */ jsx("div", { ref: gridRef, className: cx("uix-scheduling-calendar__grid", view === "week" && "uix-scheduling-calendar__grid--week"), role: "group", "aria-label": `${view} schedule`, children: days.map((date, index) => {
|
|
104
|
+
const dayEntries = entriesFor(date);
|
|
105
|
+
const dayOverlays = overlaysFor(date);
|
|
106
|
+
return /* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__day", "data-outside": view === "month" && !monthDays[index].inMonth || void 0, children: [
|
|
107
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-scheduling-calendar__date", "data-calendar-date": date, tabIndex: activeDate === date ? 0 : -1, onFocus: () => setActiveDate(date), onKeyDown: (event) => onDayKeyDown(event, date), "aria-label": dateLabel(date, locale), children: Number(date.slice(-2)) }),
|
|
108
|
+
dayOverlays.map((overlay) => /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__overlay", "data-kind": overlay.kind, children: overlay.label }, overlay.id)),
|
|
109
|
+
/* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__entries", children: dayEntries.map((entry) => {
|
|
110
|
+
const span = entrySpans.get(entry.id);
|
|
111
|
+
const state = entry.state ?? "scheduled";
|
|
112
|
+
return /* @__PURE__ */ jsxs("button", { type: "button", className: "uix-scheduling-calendar__entry", "data-state": state, "data-range-start": date === span.start || void 0, "data-range-end": date === span.end || void 0, onClick: () => onSelectEntry?.(entry), "aria-label": `${entry.title}, ${stateLabel[state]}, ${formatInstant(entry.start)} to ${formatInstant(entry.end)}`, children: [
|
|
113
|
+
/* @__PURE__ */ jsx("span", { children: renderEntry?.(entry) ?? entry.title }),
|
|
114
|
+
/* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: stateLabel[state] })
|
|
115
|
+
] }, entry.id);
|
|
116
|
+
}) })
|
|
117
|
+
] }, date);
|
|
118
|
+
}) }),
|
|
119
|
+
/* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__legend", "aria-label": "Schedule state legend", children: ["scheduled", "conflicted", "in-progress", "blackout-violation"].map((state) => /* @__PURE__ */ jsx("span", { "data-state": state, children: stateLabel[state] }, state)) })
|
|
120
|
+
] });
|
|
121
|
+
}
|
|
122
|
+
export {
|
|
123
|
+
SchedulingCalendar
|
|
124
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { JsonValue } from './json-value.js';
|
|
2
|
+
|
|
3
|
+
type DiffKind = 'added' | 'removed' | 'changed' | 'conflicted';
|
|
4
|
+
type DiffResolution = 'accept' | 'skip' | 'pending';
|
|
5
|
+
interface DiffEntry {
|
|
6
|
+
path: string;
|
|
7
|
+
kind: DiffKind;
|
|
8
|
+
base?: JsonValue;
|
|
9
|
+
current?: JsonValue;
|
|
10
|
+
incoming?: JsonValue;
|
|
11
|
+
}
|
|
12
|
+
interface DiffSummary {
|
|
13
|
+
added: number;
|
|
14
|
+
removed: number;
|
|
15
|
+
changed: number;
|
|
16
|
+
conflicted: number;
|
|
17
|
+
resolved: number;
|
|
18
|
+
pending: number;
|
|
19
|
+
}
|
|
20
|
+
declare function buildThreeWayDiff(base: JsonValue | undefined, current: JsonValue | undefined, incoming: JsonValue | undefined, path?: string): DiffEntry[];
|
|
21
|
+
declare function summarizeDiff(entries: DiffEntry[], resolutions?: Record<string, DiffResolution>): DiffSummary;
|
|
22
|
+
|
|
23
|
+
export { type DiffEntry, type DiffKind, type DiffResolution, type DiffSummary, buildThreeWayDiff, summarizeDiff };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
function isJsonObject(value) {
|
|
2
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
function jsonEqual(a, b) {
|
|
5
|
+
if (a === b) return true;
|
|
6
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
7
|
+
return a.length === b.length && a.every((value, index) => jsonEqual(value, b[index]));
|
|
8
|
+
}
|
|
9
|
+
if (isJsonObject(a) && isJsonObject(b)) {
|
|
10
|
+
const aKeys = Object.keys(a).sort();
|
|
11
|
+
const bKeys = Object.keys(b).sort();
|
|
12
|
+
return aKeys.length === bKeys.length && aKeys.every((key, index) => key === bKeys[index] && jsonEqual(a[key], b[key]));
|
|
13
|
+
}
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
function classify(base, current, incoming) {
|
|
17
|
+
if (base === void 0) return "added";
|
|
18
|
+
if (incoming === void 0) return "removed";
|
|
19
|
+
const currentChanged = !jsonEqual(base, current);
|
|
20
|
+
const incomingChanged = !jsonEqual(base, incoming);
|
|
21
|
+
if (currentChanged && incomingChanged && !jsonEqual(current, incoming)) return "conflicted";
|
|
22
|
+
return "changed";
|
|
23
|
+
}
|
|
24
|
+
function buildThreeWayDiff(base, current, incoming, path = "$") {
|
|
25
|
+
if (jsonEqual(current, incoming)) return [];
|
|
26
|
+
if (isJsonObject(base) || isJsonObject(current) || isJsonObject(incoming)) {
|
|
27
|
+
const objects = [base, current, incoming].filter(isJsonObject);
|
|
28
|
+
const keys = [...new Set(objects.flatMap((value) => Object.keys(value)))].sort();
|
|
29
|
+
return keys.flatMap((key) => buildThreeWayDiff(
|
|
30
|
+
isJsonObject(base) ? base[key] : void 0,
|
|
31
|
+
isJsonObject(current) ? current[key] : void 0,
|
|
32
|
+
isJsonObject(incoming) ? incoming[key] : void 0,
|
|
33
|
+
`${path}.${key}`
|
|
34
|
+
));
|
|
35
|
+
}
|
|
36
|
+
return [{ path, kind: classify(base, current, incoming), base, current, incoming }];
|
|
37
|
+
}
|
|
38
|
+
function summarizeDiff(entries, resolutions = {}) {
|
|
39
|
+
const summary = { added: 0, removed: 0, changed: 0, conflicted: 0, resolved: 0, pending: 0 };
|
|
40
|
+
entries.forEach((entry) => {
|
|
41
|
+
summary[entry.kind] += 1;
|
|
42
|
+
if ((resolutions[entry.path] ?? "pending") === "pending") summary.pending += 1;
|
|
43
|
+
else summary.resolved += 1;
|
|
44
|
+
});
|
|
45
|
+
return summary;
|
|
46
|
+
}
|
|
47
|
+
export {
|
|
48
|
+
buildThreeWayDiff,
|
|
49
|
+
summarizeDiff
|
|
50
|
+
};
|