@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.
- package/README.md +26 -1
- package/dist/ChartCore-Bt1C84ty.d.cts +22 -0
- package/dist/calendar-model.d.ts +31 -0
- package/dist/calendar-model.js +89 -0
- package/dist/chart-preset.cjs +168 -0
- package/dist/chart-preset.d.cts +9 -0
- package/dist/chart-preset.d.ts +9 -0
- package/dist/chart-preset.js +35 -0
- package/dist/chart.cjs +58 -32
- package/dist/chart.d.cts +6 -21
- package/dist/chart.d.ts +2 -1
- 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/Chart.d.ts +6 -21
- package/dist/components/Chart.js +5 -76
- package/dist/components/ChartCore.d.ts +33 -0
- package/dist/components/ChartCore.js +100 -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/Inbox.js +0 -1
- 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/Meter.js +0 -1
- package/dist/components/MetricInput.d.ts +23 -0
- package/dist/components/MetricInput.js +92 -0
- package/dist/components/NavFavourites.js +35 -108
- 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/components/Segmented.js +3 -2
- package/dist/components/Table.d.ts +1 -1
- package/dist/components/Table.js +4 -3
- package/dist/components/Tabs.js +3 -2
- package/dist/diff-model.d.ts +23 -0
- package/dist/diff-model.js +50 -0
- package/dist/hooks/useTable.js +11 -7
- package/dist/hooks/useVirtualRows.d.ts +27 -0
- package/dist/hooks/useVirtualRows.js +50 -0
- package/dist/index.cjs +1930 -194
- package/dist/index.d.cts +464 -2
- package/dist/index.d.ts +19 -0
- package/dist/index.js +93 -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/dist/table-engine.js +8 -6
- package/package.json +16 -2
|
@@ -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 };
|
|
@@ -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
|
+
};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import { createContext, useContext } from "react";
|
|
3
|
+
import { createContext, useContext, useMemo } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
5
|
const SegmentedCtx = createContext({});
|
|
6
6
|
function Segmented({ value, onChange, children, className, ...props }) {
|
|
7
|
-
|
|
7
|
+
const contextValue = useMemo(() => ({ value, onChange }), [value, onChange]);
|
|
8
|
+
return /* @__PURE__ */ jsx(SegmentedCtx.Provider, { value: contextValue, children: /* @__PURE__ */ jsx("div", { role: "group", className: cx("uix-segmented", className), ...props, children }) });
|
|
8
9
|
}
|
|
9
10
|
function SegmentedOption({
|
|
10
11
|
value,
|
|
@@ -4,7 +4,7 @@ import { HTMLAttributes, ReactNode, ButtonHTMLAttributes, TdHTMLAttributes, Tabl
|
|
|
4
4
|
interface TableWrapProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
5
|
children?: ReactNode;
|
|
6
6
|
}
|
|
7
|
-
declare
|
|
7
|
+
declare const TableWrap: react.ForwardRefExoticComponent<TableWrapProps & react.RefAttributes<HTMLDivElement>>;
|
|
8
8
|
type TableDensity = 'compact' | 'standard' | 'comfortable';
|
|
9
9
|
type SortDirection = 'ascending' | 'descending' | 'none';
|
|
10
10
|
interface TableProps extends TableHTMLAttributes<HTMLTableElement> {
|
package/dist/components/Table.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
2
3
|
import { cx } from "../cx.js";
|
|
3
4
|
import { highlightSegments } from "../table-engine.js";
|
|
4
|
-
|
|
5
|
-
return /* @__PURE__ */ jsx("div", { className: cx("uix-table-wrap", className), ...props, children });
|
|
6
|
-
}
|
|
5
|
+
const TableWrap = forwardRef(function TableWrap2({ children, className, ...props }, ref) {
|
|
6
|
+
return /* @__PURE__ */ jsx("div", { ref, className: cx("uix-table-wrap", className), ...props, children });
|
|
7
|
+
});
|
|
7
8
|
function Table({
|
|
8
9
|
density = "standard",
|
|
9
10
|
zebra = true,
|
package/dist/components/Tabs.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import { createContext, useContext } from "react";
|
|
3
|
+
import { createContext, useContext, useMemo } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
5
|
const TabsCtx = createContext({});
|
|
6
6
|
function Tabs({ variant = "line", value, onChange, children, className }) {
|
|
7
|
-
|
|
7
|
+
const contextValue = useMemo(() => ({ value, onChange }), [value, onChange]);
|
|
8
|
+
return /* @__PURE__ */ jsx(TabsCtx.Provider, { value: contextValue, children: /* @__PURE__ */ jsx("div", { role: "tablist", className: cx("uix-tabs", `uix-tabs--${variant}`, className), children }) });
|
|
8
9
|
}
|
|
9
10
|
function Tab({ value, children, disabled, className, onClick, ...props }) {
|
|
10
11
|
const ctx = useContext(TabsCtx);
|
|
@@ -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 };
|