@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.
Files changed (42) hide show
  1. package/README.md +16 -0
  2. package/dist/calendar-model.d.ts +31 -0
  3. package/dist/calendar-model.js +89 -0
  4. package/dist/color-model.d.ts +20 -0
  5. package/dist/color-model.js +74 -0
  6. package/dist/components/BrandProfiles.d.ts +45 -0
  7. package/dist/components/BrandProfiles.js +150 -0
  8. package/dist/components/BuilderCanvas.d.ts +34 -0
  9. package/dist/components/BuilderCanvas.js +117 -0
  10. package/dist/components/ColorPicker.d.ts +17 -0
  11. package/dist/components/ColorPicker.js +158 -0
  12. package/dist/components/DateRangePicker.d.ts +23 -0
  13. package/dist/components/DateRangePicker.js +123 -0
  14. package/dist/components/DiffViewer.d.ts +18 -0
  15. package/dist/components/DiffViewer.js +93 -0
  16. package/dist/components/LicensePositionBar.d.ts +17 -0
  17. package/dist/components/LicensePositionBar.js +50 -0
  18. package/dist/components/MatchReview.d.ts +39 -0
  19. package/dist/components/MatchReview.js +120 -0
  20. package/dist/components/MetricInput.d.ts +23 -0
  21. package/dist/components/MetricInput.js +92 -0
  22. package/dist/components/RelationshipGraph.d.ts +30 -0
  23. package/dist/components/RelationshipGraph.js +138 -0
  24. package/dist/components/RuleBuilder.d.ts +45 -0
  25. package/dist/components/RuleBuilder.js +163 -0
  26. package/dist/components/SchedulingCalendar.d.ts +42 -0
  27. package/dist/components/SchedulingCalendar.js +124 -0
  28. package/dist/diff-model.d.ts +23 -0
  29. package/dist/diff-model.js +50 -0
  30. package/dist/index.cjs +1753 -2
  31. package/dist/index.d.cts +439 -1
  32. package/dist/index.d.ts +18 -0
  33. package/dist/index.js +91 -1
  34. package/dist/json-value.d.ts +10 -0
  35. package/dist/json-value.js +19 -0
  36. package/dist/metric-model.d.ts +9 -0
  37. package/dist/metric-model.js +25 -0
  38. package/dist/relationship-graph-model.d.ts +31 -0
  39. package/dist/relationship-graph-model.js +68 -0
  40. package/dist/rule-builder-model.d.ts +41 -0
  41. package/dist/rule-builder-model.js +101 -0
  42. package/package.json +1 -1
@@ -0,0 +1,23 @@
1
+ import * as react from 'react';
2
+
3
+ interface MetricInputProps {
4
+ value: number | null;
5
+ onValueChange: (value: number | null) => void;
6
+ min?: number;
7
+ max?: number;
8
+ step?: number;
9
+ unit?: string;
10
+ label: string;
11
+ invalid?: boolean;
12
+ errorMessage?: string;
13
+ disabled?: boolean;
14
+ readOnly?: boolean;
15
+ parse?: (input: string) => number | null;
16
+ format?: (value: number | null) => string;
17
+ className?: string;
18
+ id?: string;
19
+ }
20
+ /** Accessible numeric recipe composed from Input and the UIx stepper controls. */
21
+ declare function MetricInput({ value, onValueChange, min, max, step, unit, label, invalid, errorMessage, disabled, readOnly, parse, format, className, id: providedId, }: MetricInputProps): react.JSX.Element;
22
+
23
+ export { MetricInput, type MetricInputProps };
@@ -0,0 +1,92 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useId, useState } from "react";
4
+ import { cx } from "../cx.js";
5
+ import { clampMetricValue, parseMetricValue, stepMetricValue } from "../metric-model.js";
6
+ import { Input } from "./Input.js";
7
+ const defaultMetricFormat = (input) => input == null ? "" : String(input);
8
+ function MetricInput({
9
+ value,
10
+ onValueChange,
11
+ min,
12
+ max,
13
+ step = 1,
14
+ unit,
15
+ label,
16
+ invalid,
17
+ errorMessage,
18
+ disabled,
19
+ readOnly,
20
+ parse = parseMetricValue,
21
+ format = defaultMetricFormat,
22
+ className,
23
+ id: providedId
24
+ }) {
25
+ const generatedId = useId();
26
+ const id = providedId ?? generatedId;
27
+ const errorId = `${id}-error`;
28
+ const [text, setText] = useState(() => format(value));
29
+ useEffect(() => setText(format(value)), [value, format]);
30
+ const commit = (input) => {
31
+ const parsed = parse(input);
32
+ if (input.trim() === "" || parsed !== null) {
33
+ onValueChange(parsed === null ? null : clampMetricValue(parsed, min, max));
34
+ }
35
+ };
36
+ const stepBy = (direction) => {
37
+ const next = stepMetricValue(value, direction, { min, max, step });
38
+ onValueChange(next);
39
+ setText(format(next));
40
+ };
41
+ const onKeyDown = (event) => {
42
+ if (event.key === "ArrowUp" || event.key === "ArrowDown") {
43
+ event.preventDefault();
44
+ stepBy(event.key === "ArrowUp" ? 1 : -1);
45
+ }
46
+ };
47
+ const onChange = (event) => {
48
+ setText(event.currentTarget.value);
49
+ commit(event.currentTarget.value);
50
+ };
51
+ const onBlur = (event) => {
52
+ const parsed = parse(event.currentTarget.value);
53
+ setText(parsed === null && event.currentTarget.value.trim() !== "" ? format(value) : format(parsed));
54
+ };
55
+ return /* @__PURE__ */ jsxs("div", { className: cx("uix-metric-input", invalid && "uix-metric-input--invalid", className), children: [
56
+ /* @__PURE__ */ jsx("label", { className: "uix-field__label", htmlFor: id, children: label }),
57
+ /* @__PURE__ */ jsxs("div", { className: "uix-metric-input__control", children: [
58
+ /* @__PURE__ */ jsx(
59
+ Input,
60
+ {
61
+ id,
62
+ value: text,
63
+ onChange,
64
+ onBlur,
65
+ onKeyDown,
66
+ inputMode: "decimal",
67
+ role: "spinbutton",
68
+ "aria-valuenow": value ?? void 0,
69
+ "aria-valuemin": min,
70
+ "aria-valuemax": max,
71
+ "aria-describedby": invalid && errorMessage ? errorId : void 0,
72
+ invalid,
73
+ disabled,
74
+ readOnly
75
+ }
76
+ ),
77
+ unit && /* @__PURE__ */ jsx("span", { className: "uix-metric-input__unit", "aria-hidden": "true", children: unit }),
78
+ /* @__PURE__ */ jsxs("span", { className: "uix-metric-input__steps", children: [
79
+ /* @__PURE__ */ jsx("button", { type: "button", className: "uix-metric-input__step", onClick: () => stepBy(1), disabled: disabled || readOnly, "aria-label": `Increase ${label}`, children: "+" }),
80
+ /* @__PURE__ */ jsx("button", { type: "button", className: "uix-metric-input__step", onClick: () => stepBy(-1), disabled: disabled || readOnly, "aria-label": `Decrease ${label}`, children: "\u2212" })
81
+ ] })
82
+ ] }),
83
+ unit && /* @__PURE__ */ jsxs("span", { className: "uix-visually-hidden", children: [
84
+ "Unit: ",
85
+ unit
86
+ ] }),
87
+ invalid && errorMessage && /* @__PURE__ */ jsx("span", { id: errorId, className: "uix-field__error", children: errorMessage })
88
+ ] });
89
+ }
90
+ export {
91
+ MetricInput
92
+ };
@@ -0,0 +1,30 @@
1
+ import * as react from 'react';
2
+ import { RelationshipGraphNode, RelationshipGraphEdge } from '../relationship-graph-model.js';
3
+
4
+ interface RelationshipGraphLegendItem {
5
+ id: string;
6
+ label: string;
7
+ }
8
+ interface RelationshipGraphProps {
9
+ nodes: RelationshipGraphNode[];
10
+ edges: RelationshipGraphEdge[];
11
+ selectedId?: string;
12
+ onSelect?: (node: RelationshipGraphNode) => void;
13
+ onExpandNeighbors?: (node: RelationshipGraphNode) => void;
14
+ onOpenDetails?: (node: RelationshipGraphNode) => void;
15
+ maxNodes?: number;
16
+ density?: 'compact' | 'standard';
17
+ highlightedNodeIds?: ReadonlySet<string>;
18
+ conflictedNodeIds?: ReadonlySet<string>;
19
+ highlightedEdgeIds?: ReadonlySet<string>;
20
+ conflictedEdgeIds?: ReadonlySet<string>;
21
+ legend?: RelationshipGraphLegendItem[];
22
+ loading?: boolean;
23
+ error?: string;
24
+ onRetry?: () => void;
25
+ className?: string;
26
+ }
27
+ /** Bounded, deterministic SVG relationship view with an equivalent accessible list. */
28
+ declare function RelationshipGraph({ nodes, edges, selectedId, onSelect, onExpandNeighbors, onOpenDetails, maxNodes, density, highlightedNodeIds, conflictedNodeIds, highlightedEdgeIds, conflictedEdgeIds, legend, loading, error, onRetry, className, }: RelationshipGraphProps): react.JSX.Element;
29
+
30
+ export { RelationshipGraph, type RelationshipGraphLegendItem, type RelationshipGraphProps };
@@ -0,0 +1,138 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useMemo, useRef, useState } from "react";
4
+ import { boundRelationshipGraph, layoutRelationshipGraph, relationshipNeighbors, traverseRelationshipNode } from "../relationship-graph-model.js";
5
+ import { cx } from "../cx.js";
6
+ const EMPTY_IDS = /* @__PURE__ */ new Set();
7
+ const EMPTY_LEGEND = [];
8
+ function RelationshipGraph({
9
+ nodes,
10
+ edges,
11
+ selectedId,
12
+ onSelect,
13
+ onExpandNeighbors,
14
+ onOpenDetails,
15
+ maxNodes = 40,
16
+ density = "standard",
17
+ highlightedNodeIds = EMPTY_IDS,
18
+ conflictedNodeIds = EMPTY_IDS,
19
+ highlightedEdgeIds = EMPTY_IDS,
20
+ conflictedEdgeIds = EMPTY_IDS,
21
+ legend = EMPTY_LEGEND,
22
+ loading,
23
+ error,
24
+ onRetry,
25
+ className
26
+ }) {
27
+ const bounded = useMemo(() => boundRelationshipGraph(nodes, edges, maxNodes), [nodes, edges, maxNodes]);
28
+ const positioned = useMemo(() => layoutRelationshipGraph(bounded.nodes, bounded.edges, selectedId), [bounded, selectedId]);
29
+ const positionedById = useMemo(() => new Map(positioned.map((node) => [node.id, node])), [positioned]);
30
+ const [zoom, setZoom] = useState(1);
31
+ const [pan, setPan] = useState({ x: 0, y: 0 });
32
+ const [focusedId, setFocusedId] = useState(selectedId ?? positioned[0]?.id);
33
+ const svgRef = useRef(null);
34
+ const select = (id) => {
35
+ const node = bounded.nodes.find((item) => item.id === id);
36
+ if (node) onSelect?.(node);
37
+ };
38
+ const focusNode = (id) => {
39
+ if (!id) return;
40
+ setFocusedId(id);
41
+ requestAnimationFrame(() => svgRef.current?.querySelector(`[data-node-id="${id}"]`)?.focus());
42
+ };
43
+ const onNodeKeyDown = (event, id) => {
44
+ if (["ArrowRight", "ArrowDown", "ArrowLeft", "ArrowUp"].includes(event.key)) {
45
+ event.preventDefault();
46
+ focusNode(traverseRelationshipNode(positioned, id, event.key === "ArrowRight" || event.key === "ArrowDown" ? 1 : -1));
47
+ } else if (event.key === "Enter" || event.key === " ") {
48
+ event.preventDefault();
49
+ select(id);
50
+ }
51
+ };
52
+ const reset = () => {
53
+ setZoom(1);
54
+ setPan({ x: 0, y: 0 });
55
+ };
56
+ if (loading) return /* @__PURE__ */ jsx("div", { className: cx("uix-relationship-graph uix-relationship-graph--state", className), role: "status", children: "Loading relationships\u2026" });
57
+ if (error) return /* @__PURE__ */ jsxs("div", { className: cx("uix-relationship-graph uix-relationship-graph--state", className), role: "alert", children: [
58
+ /* @__PURE__ */ jsx("p", { children: error }),
59
+ onRetry && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children: "Try again" })
60
+ ] });
61
+ if (bounded.nodes.length === 0) return /* @__PURE__ */ jsxs("div", { className: cx("uix-relationship-graph uix-relationship-graph--state", className), children: [
62
+ /* @__PURE__ */ jsx("p", { children: "No relationships to display." }),
63
+ /* @__PURE__ */ jsx("p", { children: "Adjust the neighborhood or filters to include a node." })
64
+ ] });
65
+ return /* @__PURE__ */ jsxs("section", { className: cx("uix-relationship-graph", `uix-relationship-graph--${density}`, className), "aria-label": "Relationship graph", children: [
66
+ /* @__PURE__ */ jsxs("div", { className: "uix-relationship-graph__toolbar", "aria-label": "Graph view controls", children: [
67
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setPan((value) => ({ ...value, x: value.x - 24 })), "aria-label": "Pan left", children: "Left" }),
68
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setPan((value) => ({ ...value, y: value.y - 24 })), "aria-label": "Pan up", children: "Up" }),
69
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setPan((value) => ({ ...value, y: value.y + 24 })), "aria-label": "Pan down", children: "Down" }),
70
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setPan((value) => ({ ...value, x: value.x + 24 })), "aria-label": "Pan right", children: "Right" }),
71
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setZoom((value) => Math.min(2, Number((value + 0.2).toFixed(1)))), "aria-label": "Zoom in", children: "Zoom in" }),
72
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setZoom((value) => Math.max(0.5, Number((value - 0.2).toFixed(1)))), "aria-label": "Zoom out", children: "Zoom out" }),
73
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: reset, children: "Reset view" }),
74
+ /* @__PURE__ */ jsxs("span", { "aria-live": "polite", children: [
75
+ "Zoom ",
76
+ Math.round(zoom * 100),
77
+ "%"
78
+ ] })
79
+ ] }),
80
+ bounded.omittedNodeCount > 0 && /* @__PURE__ */ jsxs("p", { className: "uix-relationship-graph__bounded", role: "status", children: [
81
+ "Showing ",
82
+ bounded.nodes.length,
83
+ " nodes; ",
84
+ bounded.omittedNodeCount,
85
+ " outside this bounded neighborhood."
86
+ ] }),
87
+ /* @__PURE__ */ jsx("div", { className: "uix-relationship-graph__visual", children: /* @__PURE__ */ jsx("svg", { ref: svgRef, viewBox: "0 0 600 400", role: "group", "aria-label": `Graph with ${bounded.nodes.length} nodes and ${bounded.edges.length} relationships`, children: /* @__PURE__ */ jsxs("g", { transform: `translate(${pan.x} ${pan.y}) scale(${zoom})`, children: [
88
+ bounded.edges.map((edge) => {
89
+ const source = positionedById.get(edge.source);
90
+ const target = positionedById.get(edge.target);
91
+ return /* @__PURE__ */ 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__ */ jsx("title", { children: edge.label ?? edge.type ?? "Relationship" }) }, edge.id);
92
+ }),
93
+ positioned.map((node) => /* @__PURE__ */ jsxs(
94
+ "g",
95
+ {
96
+ transform: `translate(${node.x * 6} ${node.y * 4})`,
97
+ role: "button",
98
+ "aria-label": `${node.label}${node.type ? `, ${node.type}` : ""}${conflictedNodeIds.has(node.id) ? ", conflict" : ""}`,
99
+ tabIndex: focusedId === node.id ? 0 : -1,
100
+ "data-node-id": node.id,
101
+ className: "uix-relationship-graph__node",
102
+ "data-selected": selectedId === node.id || void 0,
103
+ "data-highlighted": highlightedNodeIds.has(node.id) || void 0,
104
+ "data-conflicted": conflictedNodeIds.has(node.id) || void 0,
105
+ onFocus: () => setFocusedId(node.id),
106
+ onKeyDown: (event) => onNodeKeyDown(event, node.id),
107
+ onClick: () => select(node.id),
108
+ children: [
109
+ /* @__PURE__ */ jsx("rect", { x: "-55", y: "-18", width: "110", height: "36", rx: "8" }),
110
+ /* @__PURE__ */ jsx("text", { textAnchor: "middle", dominantBaseline: "middle", children: node.label.length > 16 ? `${node.label.slice(0, 15)}\u2026` : node.label })
111
+ ]
112
+ },
113
+ node.id
114
+ ))
115
+ ] }) }) }),
116
+ legend.length > 0 && /* @__PURE__ */ jsx("ul", { className: "uix-relationship-graph__legend", "aria-label": "Relationship types", children: legend.map((item) => /* @__PURE__ */ jsx("li", { "data-type": item.id, children: item.label }, item.id)) }),
117
+ /* @__PURE__ */ jsxs("div", { className: "uix-relationship-graph__list", role: "region", "aria-label": "Accessible relationship list", children: [
118
+ /* @__PURE__ */ jsx("h3", { children: "Relationship list" }),
119
+ /* @__PURE__ */ jsx("ul", { children: bounded.nodes.map((node) => {
120
+ const neighbors = relationshipNeighbors(node.id, bounded.edges).map((id) => bounded.nodes.find((item) => item.id === id)?.label ?? id);
121
+ return /* @__PURE__ */ jsxs("li", { "data-selected": selectedId === node.id || void 0, children: [
122
+ /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => select(node.id), "aria-pressed": selectedId === node.id, children: [
123
+ /* @__PURE__ */ jsx("strong", { children: node.label }),
124
+ node.type && /* @__PURE__ */ jsx("span", { children: node.type }),
125
+ /* @__PURE__ */ jsx("span", { children: neighbors.length ? `Connected to ${neighbors.join(", ")}` : "No visible connections" })
126
+ ] }),
127
+ /* @__PURE__ */ jsxs("span", { className: "uix-relationship-graph__list-actions", children: [
128
+ onExpandNeighbors && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => onExpandNeighbors(node), children: "Expand neighbors" }),
129
+ onOpenDetails && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => onOpenDetails(node), children: "Open details" })
130
+ ] })
131
+ ] }, node.id);
132
+ }) })
133
+ ] })
134
+ ] });
135
+ }
136
+ export {
137
+ RelationshipGraph
138
+ };
@@ -0,0 +1,45 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { JsonValue } from '../json-value.js';
4
+ import { RuleAction, RuleDefinition, RuleCondition, RuleValidationIssue } from '../rule-builder-model.js';
5
+
6
+ interface RuleValueEditorProps {
7
+ condition: RuleCondition;
8
+ onChange: (value: JsonValue | undefined) => void;
9
+ disabled?: boolean;
10
+ }
11
+ interface RuleFieldDefinition {
12
+ id: string;
13
+ label: string;
14
+ description?: string;
15
+ operators?: string[];
16
+ renderValueEditor?: (props: RuleValueEditorProps) => ReactNode;
17
+ }
18
+ interface RuleOperatorDefinition {
19
+ id: string;
20
+ label: string;
21
+ requiresValue?: boolean;
22
+ }
23
+ interface RuleActionDefinition {
24
+ id: string;
25
+ label: string;
26
+ renderParameters?: (action: RuleAction, onChange: (parameters: Record<string, JsonValue>) => void, disabled: boolean) => ReactNode;
27
+ }
28
+ interface RuleBuilderProps {
29
+ value: RuleDefinition;
30
+ onChange: (value: RuleDefinition) => void;
31
+ fields: RuleFieldDefinition[];
32
+ operators: RuleOperatorDefinition[];
33
+ actions: RuleActionDefinition[];
34
+ maxDepth?: number;
35
+ readOnly?: boolean;
36
+ validate?: (value: RuleDefinition) => RuleValidationIssue[];
37
+ className?: string;
38
+ addConditionLabel?: string;
39
+ addGroupLabel?: string;
40
+ addActionLabel?: string;
41
+ }
42
+ /** Declarative controlled when-conditions-to-actions editor with bounded nesting. */
43
+ declare function RuleBuilder({ value, onChange, fields, operators, actions, maxDepth, readOnly, validate, className, addConditionLabel, addGroupLabel, addActionLabel, }: RuleBuilderProps): react.JSX.Element;
44
+
45
+ export { type RuleActionDefinition, RuleBuilder, type RuleBuilderProps, type RuleFieldDefinition, type RuleOperatorDefinition, type RuleValueEditorProps };
@@ -0,0 +1,163 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { cx } from "../cx.js";
4
+ import {
5
+ appendRuleNode,
6
+ isRuleGroup,
7
+ mapRuleGroup,
8
+ moveRuleNode,
9
+ removeRuleNode,
10
+ summarizeRule,
11
+ validateRuleDefinition
12
+ } from "../rule-builder-model.js";
13
+ let ruleId = 0;
14
+ const nextId = (prefix) => `${prefix}-${Date.now().toString(36)}-${(++ruleId).toString(36)}`;
15
+ function defaultCondition(fields, operators) {
16
+ const field = fields[0];
17
+ const operator = operators.find((item) => field?.operators?.includes(item.id)) ?? operators[0];
18
+ return { id: nextId("condition"), field: field?.id ?? "", operator: operator?.id ?? "", value: "" };
19
+ }
20
+ function RuleGroupEditor({ group, depth, root, fields, operators, maxDepth, disabled, onRootChange, labels }) {
21
+ const updateNode = (id, update) => onRootChange(mapRuleGroup(root, id, update));
22
+ const addCondition = () => onRootChange(appendRuleNode(root, group.id, defaultCondition(fields, operators), maxDepth));
23
+ const addGroup = () => onRootChange(appendRuleNode(root, group.id, {
24
+ id: nextId("group"),
25
+ combinator: "and",
26
+ conditions: [defaultCondition(fields, operators)]
27
+ }, maxDepth));
28
+ return /* @__PURE__ */ jsxs("fieldset", { className: "uix-rule-builder__group", "data-depth": depth, children: [
29
+ /* @__PURE__ */ jsxs("legend", { className: "uix-rule-builder__group-heading", children: [
30
+ /* @__PURE__ */ jsx("span", { children: depth === 1 ? "When" : "Group" }),
31
+ /* @__PURE__ */ jsxs("label", { children: [
32
+ "Match",
33
+ /* @__PURE__ */ jsxs("select", { className: "uix-select uix-select--sm", value: group.combinator, disabled, onChange: (event) => updateNode(group.id, (node) => ({ ...node, combinator: event.currentTarget.value })), children: [
34
+ /* @__PURE__ */ jsx("option", { value: "and", children: "all (AND)" }),
35
+ /* @__PURE__ */ jsx("option", { value: "or", children: "any (OR)" })
36
+ ] })
37
+ ] })
38
+ ] }),
39
+ /* @__PURE__ */ jsx("div", { className: "uix-rule-builder__rows", children: group.conditions.map((node, index) => isRuleGroup(node) ? /* @__PURE__ */ jsxs("div", { className: "uix-rule-builder__nested", children: [
40
+ /* @__PURE__ */ jsx(RuleGroupEditor, { ...{ group: node, depth: depth + 1, root, fields, operators, maxDepth, disabled, onRootChange, labels } }),
41
+ /* @__PURE__ */ jsx(RowControls, { label: "group", first: index === 0, last: index === group.conditions.length - 1, disabled, onUp: () => onRootChange(moveRuleNode(root, node.id, -1)), onDown: () => onRootChange(moveRuleNode(root, node.id, 1)), onRemove: () => onRootChange(removeRuleNode(root, node.id)) })
42
+ ] }, node.id) : /* @__PURE__ */ jsx(
43
+ ConditionRow,
44
+ {
45
+ condition: node,
46
+ fields,
47
+ operators,
48
+ disabled,
49
+ onChange: (condition) => updateNode(node.id, () => condition),
50
+ controls: /* @__PURE__ */ jsx(RowControls, { label: "condition", first: index === 0, last: index === group.conditions.length - 1, disabled, onUp: () => onRootChange(moveRuleNode(root, node.id, -1)), onDown: () => onRootChange(moveRuleNode(root, node.id, 1)), onRemove: () => onRootChange(removeRuleNode(root, node.id)) })
51
+ },
52
+ node.id
53
+ )) }),
54
+ /* @__PURE__ */ jsxs("div", { className: "uix-rule-builder__add", children: [
55
+ /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", onClick: addCondition, disabled: disabled || fields.length === 0 || operators.length === 0, children: labels.addCondition }),
56
+ /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: addGroup, disabled: disabled || depth >= maxDepth, children: labels.addGroup })
57
+ ] })
58
+ ] });
59
+ }
60
+ function ConditionRow({ condition, fields, operators, disabled, onChange, controls }) {
61
+ const field = fields.find((item) => item.id === condition.field);
62
+ const availableOperators = field?.operators?.length ? operators.filter((item) => field.operators.includes(item.id)) : operators;
63
+ const operator = operators.find((item) => item.id === condition.operator);
64
+ const setField = (id) => {
65
+ const nextField = fields.find((item) => item.id === id);
66
+ const nextOperator = operators.find((item) => nextField?.operators?.includes(item.id)) ?? availableOperators[0] ?? operators[0];
67
+ onChange({ ...condition, field: id, operator: nextOperator?.id ?? "" });
68
+ };
69
+ return /* @__PURE__ */ jsxs("div", { className: "uix-rule-builder__condition", children: [
70
+ /* @__PURE__ */ jsxs("label", { children: [
71
+ /* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: "Field" }),
72
+ /* @__PURE__ */ jsx("select", { className: "uix-select", value: condition.field, onChange: (event) => setField(event.currentTarget.value), disabled, children: fields.map((item) => /* @__PURE__ */ jsx("option", { value: item.id, children: item.label }, item.id)) })
73
+ ] }),
74
+ /* @__PURE__ */ jsxs("label", { children: [
75
+ /* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: "Operator" }),
76
+ /* @__PURE__ */ jsx("select", { className: "uix-select", value: condition.operator, onChange: (event) => onChange({ ...condition, operator: event.currentTarget.value }), disabled, children: availableOperators.map((item) => /* @__PURE__ */ jsx("option", { value: item.id, children: item.label }, item.id)) })
77
+ ] }),
78
+ operator?.requiresValue !== false && /* @__PURE__ */ jsx("div", { className: "uix-rule-builder__value", children: field?.renderValueEditor?.({ condition, onChange: (value) => onChange({ ...condition, value }), disabled }) ?? /* @__PURE__ */ jsx("input", { className: "uix-input", value: typeof condition.value === "string" || typeof condition.value === "number" ? condition.value : condition.value == null ? "" : JSON.stringify(condition.value), onChange: (event) => onChange({ ...condition, value: event.currentTarget.value }), disabled, "aria-label": `Value for ${field?.label ?? "condition"}` }) }),
79
+ controls
80
+ ] });
81
+ }
82
+ function RowControls({ label, first, last, disabled, onUp, onDown, onRemove }) {
83
+ return /* @__PURE__ */ jsxs("span", { className: "uix-rule-builder__row-actions", children: [
84
+ /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: onUp, disabled: disabled || first, "aria-label": `Move ${label} up`, children: "Up" }),
85
+ /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: onDown, disabled: disabled || last, "aria-label": `Move ${label} down`, children: "Down" }),
86
+ /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: onRemove, disabled, "aria-label": `Remove ${label}`, children: "Remove" })
87
+ ] });
88
+ }
89
+ function RuleBuilder({
90
+ value,
91
+ onChange,
92
+ fields,
93
+ operators,
94
+ actions,
95
+ maxDepth = 3,
96
+ readOnly = false,
97
+ validate,
98
+ className,
99
+ addConditionLabel = "Add condition",
100
+ addGroupLabel = "Add group",
101
+ addActionLabel = "Add action"
102
+ }) {
103
+ const issues = [...validateRuleDefinition(value, maxDepth), ...validate?.(value) ?? []];
104
+ const fieldLabels = Object.fromEntries(fields.map((field) => [field.id, field.label]));
105
+ const operatorLabels = Object.fromEntries(operators.map((operator) => [operator.id, operator.label]));
106
+ const actionLabels = Object.fromEntries(actions.map((action) => [action.id, action.label]));
107
+ if (readOnly) return /* @__PURE__ */ jsxs("section", { className: cx("uix-rule-builder uix-rule-builder--summary", className), "aria-label": "Rule summary", children: [
108
+ /* @__PURE__ */ jsx("p", { children: summarizeRule(value, { fields: fieldLabels, operators: operatorLabels, actions: actionLabels }) }),
109
+ issues.length > 0 && /* @__PURE__ */ jsxs("p", { className: "uix-rule-builder__invalid", children: [
110
+ "Rule has ",
111
+ issues.length,
112
+ " validation issue",
113
+ issues.length === 1 ? "" : "s",
114
+ "."
115
+ ] })
116
+ ] });
117
+ const updateActions = (next) => onChange({ ...value, then: next });
118
+ return /* @__PURE__ */ jsxs("section", { className: cx("uix-rule-builder", className), "aria-label": "Rule builder", children: [
119
+ /* @__PURE__ */ jsx(RuleGroupEditor, { group: value.when, depth: 1, root: value.when, fields, operators, maxDepth, disabled: readOnly, onRootChange: (when) => onChange({ ...value, when }), labels: { addCondition: addConditionLabel, addGroup: addGroupLabel } }),
120
+ /* @__PURE__ */ jsxs("fieldset", { className: "uix-rule-builder__actions", children: [
121
+ /* @__PURE__ */ jsx("legend", { children: "Then" }),
122
+ value.then.map((action, index) => {
123
+ const definition = actions.find((item) => item.id === action.type);
124
+ return /* @__PURE__ */ jsxs("div", { className: "uix-rule-builder__action", children: [
125
+ /* @__PURE__ */ jsx("select", { className: "uix-select", value: action.type, onChange: (event) => updateActions(value.then.map((item) => item.id === action.id ? { ...item, type: event.currentTarget.value } : item)), "aria-label": "Action type", children: actions.map((item) => /* @__PURE__ */ jsx("option", { value: item.id, children: item.label }, item.id)) }),
126
+ definition?.renderParameters?.(action, (parameters) => updateActions(value.then.map((item) => item.id === action.id ? { ...item, parameters } : item)), false),
127
+ /* @__PURE__ */ jsx(
128
+ RowControls,
129
+ {
130
+ label: "action",
131
+ first: index === 0,
132
+ last: index === value.then.length - 1,
133
+ disabled: false,
134
+ onUp: () => {
135
+ const next = [...value.then];
136
+ [next[index - 1], next[index]] = [next[index], next[index - 1]];
137
+ updateActions(next);
138
+ },
139
+ onDown: () => {
140
+ const next = [...value.then];
141
+ [next[index + 1], next[index]] = [next[index], next[index + 1]];
142
+ updateActions(next);
143
+ },
144
+ onRemove: () => updateActions(value.then.filter((item) => item.id !== action.id))
145
+ }
146
+ )
147
+ ] }, action.id);
148
+ }),
149
+ /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", disabled: actions.length === 0, onClick: () => updateActions([...value.then, { id: nextId("action"), type: actions[0]?.id ?? "", parameters: {} }]), children: addActionLabel })
150
+ ] }),
151
+ /* @__PURE__ */ jsx("div", { className: "uix-rule-builder__validation", "aria-live": "polite", children: issues.length === 0 ? /* @__PURE__ */ jsx("span", { children: "Rule is valid." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
152
+ /* @__PURE__ */ jsxs("strong", { children: [
153
+ issues.length,
154
+ " validation issue",
155
+ issues.length === 1 ? "" : "s"
156
+ ] }),
157
+ /* @__PURE__ */ jsx("ul", { children: issues.map((issue, index) => /* @__PURE__ */ jsx("li", { children: issue.message }, `${issue.path}-${index}`)) })
158
+ ] }) })
159
+ ] });
160
+ }
161
+ export {
162
+ RuleBuilder
163
+ };
@@ -0,0 +1,42 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ type SchedulingCalendarView = 'month' | 'week' | 'agenda';
5
+ type SchedulingEntryState = 'scheduled' | 'conflicted' | 'in-progress' | 'blackout-violation';
6
+ type SchedulingOverlayKind = 'maintenance' | 'blackout';
7
+ interface SchedulingCalendarEntry {
8
+ id: string;
9
+ title: string;
10
+ start: string;
11
+ end: string;
12
+ state?: SchedulingEntryState;
13
+ meta?: string;
14
+ }
15
+ interface SchedulingCalendarOverlay {
16
+ id: string;
17
+ label: string;
18
+ start: string;
19
+ end: string;
20
+ kind: SchedulingOverlayKind;
21
+ }
22
+ interface SchedulingCalendarProps {
23
+ entries: SchedulingCalendarEntry[];
24
+ anchorDate: string;
25
+ timeZone: string;
26
+ view?: SchedulingCalendarView;
27
+ onViewChange?: (view: SchedulingCalendarView) => void;
28
+ onAnchorDateChange?: (date: string) => void;
29
+ onSelectEntry?: (entry: SchedulingCalendarEntry) => void;
30
+ overlays?: SchedulingCalendarOverlay[];
31
+ filter?: (entry: SchedulingCalendarEntry) => boolean;
32
+ renderEntry?: (entry: SchedulingCalendarEntry) => ReactNode;
33
+ locale?: string;
34
+ loading?: boolean;
35
+ error?: string;
36
+ onRetry?: () => void;
37
+ className?: string;
38
+ }
39
+ /** Month/week/agenda calendar for UTC ranges rendered in an explicit IANA time zone. */
40
+ declare function SchedulingCalendar({ entries, anchorDate, timeZone, view: controlledView, onViewChange, onAnchorDateChange, onSelectEntry, overlays, filter, renderEntry, locale, loading, error, onRetry, className, }: SchedulingCalendarProps): react.JSX.Element;
41
+
42
+ export { SchedulingCalendar, type SchedulingCalendarEntry, type SchedulingCalendarOverlay, type SchedulingCalendarProps, type SchedulingCalendarView, type SchedulingEntryState, type SchedulingOverlayKind };