@sanity/workflow-diagram 0.2.0 → 0.3.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/CHANGELOG.md +22 -0
- package/dist/index.cjs +52 -29
- package/dist/index.js +52 -23
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @sanity/workflow-diagram
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- f9389e5: The explain tooltips' activity roster narrates causal waits: an activity that does not complete without a caller shows what it waits on ("Waits on caller action …") under its executor-classification line, on both the static and live tiers.
|
|
8
|
+
- b43de49: elkjs now loads lazily on the first layout instead of riding the package's static import graph. Bundlers split the ~1.4 MB (minified) ELK engine into an async chunk fetched only when a diagram actually renders, cutting the package's synchronous bundle cost by roughly 72% (about 600 KB → 168 KB gzipped on the critical path).
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- Updated dependencies [f9389e5]
|
|
13
|
+
- Updated dependencies [d0c62ea]
|
|
14
|
+
- Updated dependencies [c3eed2e]
|
|
15
|
+
- Updated dependencies [df4bd80]
|
|
16
|
+
- Updated dependencies [092a0d4]
|
|
17
|
+
- Updated dependencies [5a1a9fe]
|
|
18
|
+
- Updated dependencies [e3a7ba2]
|
|
19
|
+
- Updated dependencies [30fed9e]
|
|
20
|
+
- Updated dependencies [1321ba5]
|
|
21
|
+
- Updated dependencies [e683875]
|
|
22
|
+
- Updated dependencies [a8ace4d]
|
|
23
|
+
- @sanity/workflow-engine@0.17.0
|
|
24
|
+
|
|
3
25
|
## 0.2.0
|
|
4
26
|
|
|
5
27
|
### Minor Changes
|
package/dist/index.cjs
CHANGED
|
@@ -32,15 +32,7 @@ var jsxRuntime = require("react/jsx-runtime");
|
|
|
32
32
|
|
|
33
33
|
require("@xyflow/react/dist/style.css");
|
|
34
34
|
|
|
35
|
-
var ui = require("@sanity/ui"), react$1 = require("@xyflow/react"), react = require("react"), workflowEngine = require("@sanity/workflow-engine"),
|
|
36
|
-
|
|
37
|
-
function _interopDefaultCompat(e) {
|
|
38
|
-
return e && typeof e == "object" && "default" in e ? e : {
|
|
39
|
-
default: e
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
var ELK__default = /* @__PURE__ */ _interopDefaultCompat(ELK);
|
|
35
|
+
var ui = require("@sanity/ui"), react$1 = require("@xyflow/react"), react = require("react"), workflowEngine = require("@sanity/workflow-engine"), icons = require("@sanity/icons"), reactDom = require("react-dom");
|
|
44
36
|
|
|
45
37
|
const DevModeContext = react.createContext(!1);
|
|
46
38
|
|
|
@@ -54,6 +46,12 @@ function useHoveredStage() {
|
|
|
54
46
|
return react.useContext(HoveredStageContext);
|
|
55
47
|
}
|
|
56
48
|
|
|
49
|
+
function narratedWaits(answer, definition) {
|
|
50
|
+
return answer.completesWithoutCaller === "yes" ? [] : workflowEngine.narrateAutonomyWaits(answer.waitsOn, {
|
|
51
|
+
definition: definition
|
|
52
|
+
}).map(workflowEngine.sentenceCase);
|
|
53
|
+
}
|
|
54
|
+
|
|
57
55
|
const ExplainContext = react.createContext(void 0);
|
|
58
56
|
|
|
59
57
|
function useExplain() {
|
|
@@ -81,11 +79,15 @@ function indexExplanations(sites, definition) {
|
|
|
81
79
|
live: !1
|
|
82
80
|
}), byStage.set(site.stage, list);
|
|
83
81
|
}
|
|
84
|
-
const activitiesByStage = new Map(definition.stages.map(stage =>
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
82
|
+
const autonomy = workflowEngine.deriveWorkflowAutonomy(definition), activitiesByStage = new Map(definition.stages.map(stage => {
|
|
83
|
+
const stageAutonomy = workflowEngine.stageAutonomyOf(autonomy, stage.name);
|
|
84
|
+
return [ stage.name, (stage.activities ?? []).map(activity => ({
|
|
85
|
+
name: activity.name,
|
|
86
|
+
title: activity.title ?? activity.name,
|
|
87
|
+
classification: workflowEngine.deriveExecutorClassification(activity),
|
|
88
|
+
waits: narratedWaits(workflowEngine.activityAutonomyOf(stageAutonomy, activity.name), definition)
|
|
89
|
+
})) ];
|
|
90
|
+
}));
|
|
89
91
|
return {
|
|
90
92
|
transition: (stage, name) => transitions.get(`${stage}#${name}`),
|
|
91
93
|
stageGates: stage => byStage.get(stage) ?? [],
|
|
@@ -142,7 +144,8 @@ function indexLiveExplanations(evaluation) {
|
|
|
142
144
|
const activities = evaluation.currentStage.activities.map(a => ({
|
|
143
145
|
name: a.activity.name,
|
|
144
146
|
title: a.activity.title ?? a.activity.name,
|
|
145
|
-
classification: a.classification
|
|
147
|
+
classification: a.classification,
|
|
148
|
+
waits: narratedWaits(a.autonomy, evaluation.definition)
|
|
146
149
|
}));
|
|
147
150
|
return {
|
|
148
151
|
transition: (stage, name) => transitions.get(`${stage}#${name}`),
|
|
@@ -565,7 +568,15 @@ function GhostNode({data: data}) {
|
|
|
565
568
|
});
|
|
566
569
|
}
|
|
567
570
|
|
|
568
|
-
|
|
571
|
+
let elkLoad;
|
|
572
|
+
|
|
573
|
+
function loadElk() {
|
|
574
|
+
return elkLoad ??= import("elkjs/lib/elk.bundled.js").then(mod => new mod.default).catch(err => {
|
|
575
|
+
throw elkLoad = void 0, err;
|
|
576
|
+
}), elkLoad;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
const STAGE_CHAR_PX = 8.5, STAGE_PAD_X = 16, STAGE_ICON_W = 25, STAGE_PAD_Y = 13, STAGE_LINE_H = 18, STAGE_MAX_TEXT_WIDTH = 150, STAGE_MIN_TEXT_WIDTH = 44;
|
|
569
580
|
|
|
570
581
|
function wrapStageLabel(title) {
|
|
571
582
|
const words = title.trim().split(/\s+/), lines = [];
|
|
@@ -638,9 +649,9 @@ async function layoutGraph(nodes, edges) {
|
|
|
638
649
|
nodes: [ ...nodes ],
|
|
639
650
|
edges: [ ...edges ]
|
|
640
651
|
};
|
|
641
|
-
const railNodes = new Set(edges.flatMap(e => edgeFlags(e).isPrimary ? [ e.source, e.target ] : [])), boxOf = new Map(nodes.map(n => [ n.id, {
|
|
642
|
-
w: n.width ??
|
|
643
|
-
h: n.height ??
|
|
652
|
+
const elk = await loadElk(), railNodes = new Set(edges.flatMap(e => edgeFlags(e).isPrimary ? [ e.source, e.target ] : [])), boxOf = new Map(nodes.map(n => [ n.id, {
|
|
653
|
+
w: n.width ?? 180,
|
|
654
|
+
h: n.height ?? 72
|
|
644
655
|
} ])), portMeta = /* @__PURE__ */ new Map, portFor = args => {
|
|
645
656
|
const id = `${args.node}.${args.key}`, list = portMeta.get(args.node) ?? [];
|
|
646
657
|
return list.push({
|
|
@@ -691,8 +702,8 @@ async function layoutGraph(nodes, edges) {
|
|
|
691
702
|
layoutOptions: defaultLayoutOptions(),
|
|
692
703
|
children: nodes.map(n => ({
|
|
693
704
|
id: n.id,
|
|
694
|
-
width: n.width ??
|
|
695
|
-
height: n.height ??
|
|
705
|
+
width: n.width ?? 180,
|
|
706
|
+
height: n.height ?? 72,
|
|
696
707
|
layoutOptions: {
|
|
697
708
|
...layerConstraintFor(n).layoutOptions,
|
|
698
709
|
"elk.portConstraints": constraint
|
|
@@ -746,8 +757,8 @@ function faceOffRailPorts(args) {
|
|
|
746
757
|
function pass1Centers(pass1, boxOf) {
|
|
747
758
|
return new Map((pass1.children ?? []).map(c => {
|
|
748
759
|
const box = boxOf.get(c.id) ?? {
|
|
749
|
-
w:
|
|
750
|
-
h:
|
|
760
|
+
w: 180,
|
|
761
|
+
h: 72
|
|
751
762
|
};
|
|
752
763
|
return [ c.id, {
|
|
753
764
|
cx: (c.x ?? 0) + box.w / 2,
|
|
@@ -830,8 +841,8 @@ function pinnedPortPositions(args) {
|
|
|
830
841
|
for (const child of args.pass1.children ?? []) pinChildPorts(child, {
|
|
831
842
|
meta: args.portMeta.get(child.id) ?? [],
|
|
832
843
|
box: args.boxOf.get(child.id) ?? {
|
|
833
|
-
w:
|
|
834
|
-
h:
|
|
844
|
+
w: 180,
|
|
845
|
+
h: 72
|
|
835
846
|
},
|
|
836
847
|
centers: centers,
|
|
837
848
|
out: out
|
|
@@ -1481,10 +1492,22 @@ function ActivityRoster({activities: activities}) {
|
|
|
1481
1492
|
/* @__PURE__ */
|
|
1482
1493
|
return jsxRuntime.jsx(ui.Stack, {
|
|
1483
1494
|
space: 2,
|
|
1484
|
-
children: activities.map(activity => /* @__PURE__ */ jsxRuntime.jsxs(ui.
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1495
|
+
children: activities.map(activity => /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
|
|
1496
|
+
space: 1,
|
|
1497
|
+
children: [
|
|
1498
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
|
|
1499
|
+
size: 1,
|
|
1500
|
+
muted: !0,
|
|
1501
|
+
children: [ activity.title, " — ", workflowEngine.EXECUTOR_CLASSIFICATION_DISPLAY[activity.classification].title ]
|
|
1502
|
+
}), activity.waits.map((wait, i) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
|
|
1503
|
+
size: 1,
|
|
1504
|
+
muted: !0,
|
|
1505
|
+
style: {
|
|
1506
|
+
paddingLeft: 12,
|
|
1507
|
+
lineHeight: 1.4
|
|
1508
|
+
},
|
|
1509
|
+
children: wait
|
|
1510
|
+
}, i)) ]
|
|
1488
1511
|
}, activity.name))
|
|
1489
1512
|
});
|
|
1490
1513
|
}
|
package/dist/index.js
CHANGED
|
@@ -8,9 +8,7 @@ import { MarkerType, getSmoothStepPath, Handle, Position, BaseEdge, EdgeLabelRen
|
|
|
8
8
|
|
|
9
9
|
import { createContext, useContext, Fragment, useState, useMemo, useEffect, useRef, useCallback } from "react";
|
|
10
10
|
|
|
11
|
-
import { sentenceCase, describeSiteHeading, deriveExecutorClassification, describeCondition, checklistLines, isTerminalStage, EXECUTOR_CLASSIFICATION_DISPLAY } from "@sanity/workflow-engine";
|
|
12
|
-
|
|
13
|
-
import ELK from "elkjs/lib/elk.bundled.js";
|
|
11
|
+
import { sentenceCase, describeSiteHeading, deriveWorkflowAutonomy, stageAutonomyOf, activityAutonomyOf, deriveExecutorClassification, describeCondition, narrateAutonomyWaits, checklistLines, isTerminalStage, EXECUTOR_CLASSIFICATION_DISPLAY } from "@sanity/workflow-engine";
|
|
14
12
|
|
|
15
13
|
import { CheckmarkCircleIcon, CircleIcon, LockIcon } from "@sanity/icons";
|
|
16
14
|
|
|
@@ -28,6 +26,12 @@ function useHoveredStage() {
|
|
|
28
26
|
return useContext(HoveredStageContext);
|
|
29
27
|
}
|
|
30
28
|
|
|
29
|
+
function narratedWaits(answer, definition) {
|
|
30
|
+
return answer.completesWithoutCaller === "yes" ? [] : narrateAutonomyWaits(answer.waitsOn, {
|
|
31
|
+
definition: definition
|
|
32
|
+
}).map(sentenceCase);
|
|
33
|
+
}
|
|
34
|
+
|
|
31
35
|
const ExplainContext = createContext(void 0);
|
|
32
36
|
|
|
33
37
|
function useExplain() {
|
|
@@ -55,11 +59,15 @@ function indexExplanations(sites, definition) {
|
|
|
55
59
|
live: !1
|
|
56
60
|
}), byStage.set(site.stage, list);
|
|
57
61
|
}
|
|
58
|
-
const activitiesByStage = new Map(definition.stages.map(stage =>
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
const autonomy = deriveWorkflowAutonomy(definition), activitiesByStage = new Map(definition.stages.map(stage => {
|
|
63
|
+
const stageAutonomy = stageAutonomyOf(autonomy, stage.name);
|
|
64
|
+
return [ stage.name, (stage.activities ?? []).map(activity => ({
|
|
65
|
+
name: activity.name,
|
|
66
|
+
title: activity.title ?? activity.name,
|
|
67
|
+
classification: deriveExecutorClassification(activity),
|
|
68
|
+
waits: narratedWaits(activityAutonomyOf(stageAutonomy, activity.name), definition)
|
|
69
|
+
})) ];
|
|
70
|
+
}));
|
|
63
71
|
return {
|
|
64
72
|
transition: (stage, name) => transitions.get(`${stage}#${name}`),
|
|
65
73
|
stageGates: stage => byStage.get(stage) ?? [],
|
|
@@ -116,7 +124,8 @@ function indexLiveExplanations(evaluation) {
|
|
|
116
124
|
const activities = evaluation.currentStage.activities.map(a => ({
|
|
117
125
|
name: a.activity.name,
|
|
118
126
|
title: a.activity.title ?? a.activity.name,
|
|
119
|
-
classification: a.classification
|
|
127
|
+
classification: a.classification,
|
|
128
|
+
waits: narratedWaits(a.autonomy, evaluation.definition)
|
|
120
129
|
}));
|
|
121
130
|
return {
|
|
122
131
|
transition: (stage, name) => transitions.get(`${stage}#${name}`),
|
|
@@ -539,7 +548,15 @@ function GhostNode({data: data}) {
|
|
|
539
548
|
});
|
|
540
549
|
}
|
|
541
550
|
|
|
542
|
-
|
|
551
|
+
let elkLoad;
|
|
552
|
+
|
|
553
|
+
function loadElk() {
|
|
554
|
+
return elkLoad ??= import("elkjs/lib/elk.bundled.js").then(mod => new mod.default).catch(err => {
|
|
555
|
+
throw elkLoad = void 0, err;
|
|
556
|
+
}), elkLoad;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const STAGE_CHAR_PX = 8.5, STAGE_PAD_X = 16, STAGE_ICON_W = 25, STAGE_PAD_Y = 13, STAGE_LINE_H = 18, STAGE_MAX_TEXT_WIDTH = 150, STAGE_MIN_TEXT_WIDTH = 44;
|
|
543
560
|
|
|
544
561
|
function wrapStageLabel(title) {
|
|
545
562
|
const words = title.trim().split(/\s+/), lines = [];
|
|
@@ -612,9 +629,9 @@ async function layoutGraph(nodes, edges) {
|
|
|
612
629
|
nodes: [ ...nodes ],
|
|
613
630
|
edges: [ ...edges ]
|
|
614
631
|
};
|
|
615
|
-
const railNodes = new Set(edges.flatMap(e => edgeFlags(e).isPrimary ? [ e.source, e.target ] : [])), boxOf = new Map(nodes.map(n => [ n.id, {
|
|
616
|
-
w: n.width ??
|
|
617
|
-
h: n.height ??
|
|
632
|
+
const elk = await loadElk(), railNodes = new Set(edges.flatMap(e => edgeFlags(e).isPrimary ? [ e.source, e.target ] : [])), boxOf = new Map(nodes.map(n => [ n.id, {
|
|
633
|
+
w: n.width ?? 180,
|
|
634
|
+
h: n.height ?? 72
|
|
618
635
|
} ])), portMeta = /* @__PURE__ */ new Map, portFor = args => {
|
|
619
636
|
const id = `${args.node}.${args.key}`, list = portMeta.get(args.node) ?? [];
|
|
620
637
|
return list.push({
|
|
@@ -665,8 +682,8 @@ async function layoutGraph(nodes, edges) {
|
|
|
665
682
|
layoutOptions: defaultLayoutOptions(),
|
|
666
683
|
children: nodes.map(n => ({
|
|
667
684
|
id: n.id,
|
|
668
|
-
width: n.width ??
|
|
669
|
-
height: n.height ??
|
|
685
|
+
width: n.width ?? 180,
|
|
686
|
+
height: n.height ?? 72,
|
|
670
687
|
layoutOptions: {
|
|
671
688
|
...layerConstraintFor(n).layoutOptions,
|
|
672
689
|
"elk.portConstraints": constraint
|
|
@@ -720,8 +737,8 @@ function faceOffRailPorts(args) {
|
|
|
720
737
|
function pass1Centers(pass1, boxOf) {
|
|
721
738
|
return new Map((pass1.children ?? []).map(c => {
|
|
722
739
|
const box = boxOf.get(c.id) ?? {
|
|
723
|
-
w:
|
|
724
|
-
h:
|
|
740
|
+
w: 180,
|
|
741
|
+
h: 72
|
|
725
742
|
};
|
|
726
743
|
return [ c.id, {
|
|
727
744
|
cx: (c.x ?? 0) + box.w / 2,
|
|
@@ -804,8 +821,8 @@ function pinnedPortPositions(args) {
|
|
|
804
821
|
for (const child of args.pass1.children ?? []) pinChildPorts(child, {
|
|
805
822
|
meta: args.portMeta.get(child.id) ?? [],
|
|
806
823
|
box: args.boxOf.get(child.id) ?? {
|
|
807
|
-
w:
|
|
808
|
-
h:
|
|
824
|
+
w: 180,
|
|
825
|
+
h: 72
|
|
809
826
|
},
|
|
810
827
|
centers: centers,
|
|
811
828
|
out: out
|
|
@@ -1455,10 +1472,22 @@ function ActivityRoster({activities: activities}) {
|
|
|
1455
1472
|
/* @__PURE__ */
|
|
1456
1473
|
return jsx(Stack, {
|
|
1457
1474
|
space: 2,
|
|
1458
|
-
children: activities.map(activity => /* @__PURE__ */ jsxs(
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1475
|
+
children: activities.map(activity => /* @__PURE__ */ jsxs(Stack, {
|
|
1476
|
+
space: 1,
|
|
1477
|
+
children: [
|
|
1478
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
1479
|
+
size: 1,
|
|
1480
|
+
muted: !0,
|
|
1481
|
+
children: [ activity.title, " — ", EXECUTOR_CLASSIFICATION_DISPLAY[activity.classification].title ]
|
|
1482
|
+
}), activity.waits.map((wait, i) => /* @__PURE__ */ jsx(Text, {
|
|
1483
|
+
size: 1,
|
|
1484
|
+
muted: !0,
|
|
1485
|
+
style: {
|
|
1486
|
+
paddingLeft: 12,
|
|
1487
|
+
lineHeight: 1.4
|
|
1488
|
+
},
|
|
1489
|
+
children: wait
|
|
1490
|
+
}, i)) ]
|
|
1462
1491
|
}, activity.name))
|
|
1463
1492
|
});
|
|
1464
1493
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/workflow-diagram",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "React component rendering a workflow definition as a stage diagram — ELK layered layout, current-stage and visited-path highlighting, transition tooltips on line hover.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"diagram",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@sanity/icons": "^3.7.7",
|
|
45
45
|
"@xyflow/react": "^12.11.1",
|
|
46
46
|
"elkjs": "^0.11.1",
|
|
47
|
-
"@sanity/workflow-engine": "0.
|
|
47
|
+
"@sanity/workflow-engine": "0.17.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@sanity/pkg-utils": "^10.5.3",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"react-dom": "^19.2.7",
|
|
59
59
|
"vite": "^8.1.2",
|
|
60
60
|
"vitest": "^4.1.8",
|
|
61
|
-
"@sanity/workflow-engine-test": "0.
|
|
62
|
-
"@sanity/workflow-examples": "0.
|
|
61
|
+
"@sanity/workflow-engine-test": "0.12.0",
|
|
62
|
+
"@sanity/workflow-examples": "0.7.0"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|
|
65
65
|
"@sanity/ui": "^3.3.0",
|