@stealthscale/component-graphs 0.1.0 → 0.2.2
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/dist/cyclonedx/bom.json +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -3807
- package/dist/pages/directed-graph/directed-graph.js +1170 -0
- package/dist/pages/directed-graph/directed-graph.props.js +3 -0
- package/dist/pages/graph/graph.js +1619 -0
- package/dist/pages/graph/graph.props.js +3 -0
- package/dist/pages/network-graph/network-graph.js +1542 -0
- package/dist/pages/network-graph/network-graph.props.js +3 -0
- package/dist/pages/pages.json +43 -0
- package/dist/{recipe-BkmwNoaQ.js → recipes-lWPA4IJC.js} +19 -19
- package/dist/shared-DOF4V8-Q.js +3808 -0
- package/dist/theme.js +1 -1
- package/locales/en/specimen/directed-graph.json +88 -0
- package/locales/en/specimen/graph.json +155 -0
- package/locales/en/specimen/network-graph.json +99 -0
- package/package.json +19 -18
package/dist/index.js
CHANGED
|
@@ -1,3808 +1,2 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
import { t as recipe } from "./recipe-BkmwNoaQ.js";
|
|
3
|
-
import { createContext, isValidElement, use, useEffect, useId, useRef, useState } from "react";
|
|
4
|
-
import { c } from "react/compiler-runtime";
|
|
5
|
-
import { Button, ButtonPropsProvider, IconButton } from "@stealthscale/component-actions";
|
|
6
|
-
import { Badge, Status } from "@stealthscale/component-data";
|
|
7
|
-
import { createLabelling, createRequiredContext, omitUndefined, useControllableState } from "@stealthscale/hooks";
|
|
8
|
-
import { Graph, layout } from "@dagrejs/dagre";
|
|
9
|
-
import { Background, BaseEdge, EdgeLabelRenderer, Handle, MiniMap, Position, ReactFlow, ReactFlowProvider, addEdge, applyNodeChanges, getBezierPath, getViewportForBounds, useNodeId, useReactFlow, useStore, useStoreApi } from "@xyflow/react";
|
|
10
|
-
import { createSlotRecipeContext } from "@stealthscale/theme";
|
|
11
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
-
import { RovingFocus } from "@stealthscale/component-a11y";
|
|
13
|
-
import { LocaleContext } from "@stealthscale/provider-locale";
|
|
14
|
-
import { forceCenter, forceCollide, forceLink, forceManyBody, forceSimulation, forceX, forceY } from "d3-force";
|
|
15
|
-
//#region src/graph/words.ts
|
|
16
|
-
/**
|
|
17
|
-
* Instruction every node of an editable canvas is described by, unless stated.
|
|
18
|
-
*/
|
|
19
|
-
const NODE = "Press Enter or Space to select the node, the arrow keys to move it, Delete to remove it and Escape to clear the selection.";
|
|
20
|
-
/**
|
|
21
|
-
* Instruction every edge of an editable canvas is described by, unless stated.
|
|
22
|
-
*/
|
|
23
|
-
const EDGE = "Press Enter or Space to select the connection, Delete to remove it and Escape to clear the selection.";
|
|
24
|
-
/**
|
|
25
|
-
* Writes the announcement of a move, unless stated.
|
|
26
|
-
*/
|
|
27
|
-
function moveAnnouncementOf({ direction }) {
|
|
28
|
-
return `Moved the selected node ${direction}.`;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Writes the name of an edge from the names of its ends, unless stated.
|
|
32
|
-
*/
|
|
33
|
-
function edgeNameOf({ source, target }) {
|
|
34
|
-
return `${source} to ${target}`;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Writes the name of an edge's remove control from the names of its ends, unless stated.
|
|
38
|
-
*/
|
|
39
|
-
function removeNameOf({ source, target }) {
|
|
40
|
-
return `Remove the connection from ${source} to ${target}`;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Returns React Flow's words for a canvas: the caller's or the English defaults while it takes
|
|
44
|
-
* edits, and no instructions while it is read-only.
|
|
45
|
-
*
|
|
46
|
-
* @param words - The caller's words.
|
|
47
|
-
* @param editable - Whether the canvas takes edits.
|
|
48
|
-
*/
|
|
49
|
-
function ariaWordsOf(words, editable) {
|
|
50
|
-
const node = editable ? words.nodeDescription ?? NODE : "";
|
|
51
|
-
return {
|
|
52
|
-
"edge.a11yDescription.default": editable ? words.edgeDescription ?? EDGE : "",
|
|
53
|
-
"node.a11yDescription.ariaLiveMessage": words.moveAnnouncement ?? moveAnnouncementOf,
|
|
54
|
-
"node.a11yDescription.default": node,
|
|
55
|
-
"node.a11yDescription.keyboardDisabled": node
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
//#endregion
|
|
59
|
-
//#region src/diff/diff.ts
|
|
60
|
-
/**
|
|
61
|
-
* Compares two versions of a graph: the nodes and edges the later version adds, changes or removes.
|
|
62
|
-
*
|
|
63
|
-
* @remarks
|
|
64
|
-
* A node is the same node in both versions while its id is the same, and it is changed while a
|
|
65
|
-
* field of its `data` differs. Each field is compared by its JSON with object keys sorted, so a
|
|
66
|
-
* node that only moved, or whose data lists its keys in another order, is unchanged. A React
|
|
67
|
-
* element in the data, such as an icon, is compared by its component and its props. An edge is
|
|
68
|
-
* the same edge in both versions while it joins the same ports of the same nodes, so an edge is
|
|
69
|
-
* added or removed, never changed. The entries follow the later version's order, and the removed
|
|
70
|
-
* ones follow in the earlier version's order.
|
|
71
|
-
*/
|
|
72
|
-
/**
|
|
73
|
-
* Returns a function that writes a value's JSON with object keys sorted and each React element as
|
|
74
|
-
* its component's number of first sight and its props.
|
|
75
|
-
*/
|
|
76
|
-
function canonicalOf() {
|
|
77
|
-
const components = /* @__PURE__ */ new Map();
|
|
78
|
-
/**
|
|
79
|
-
* Replaces a React element and an object with the forms the comparison reads.
|
|
80
|
-
*/
|
|
81
|
-
const replacer = (_key, value) => {
|
|
82
|
-
if (isValidElement(value)) {
|
|
83
|
-
if (!components.has(value.type)) components.set(value.type, components.size);
|
|
84
|
-
return {
|
|
85
|
-
component: components.get(value.type),
|
|
86
|
-
props: value.props
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) return value;
|
|
90
|
-
const fields = new Map(Object.entries(value));
|
|
91
|
-
return Object.fromEntries([...fields.keys()].toSorted().map((key) => [key, fields.get(key)]));
|
|
92
|
-
};
|
|
93
|
-
return (value) => JSON.stringify(value, replacer) ?? "";
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Returns a node's name: its data's string `label`, else its id.
|
|
97
|
-
*/
|
|
98
|
-
function labelOf(node) {
|
|
99
|
-
const label = node.data["label"];
|
|
100
|
-
return typeof label === "string" ? label : node.id;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Returns the fields of two versions of a node's data that differ, sorted.
|
|
104
|
-
*/
|
|
105
|
-
function fieldsOf(before, after, canonical) {
|
|
106
|
-
return [.../* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])].filter((key) => canonical(before[key]) !== canonical(after[key])).toSorted();
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Returns a node's entry with the fields of its data that differ.
|
|
110
|
-
*/
|
|
111
|
-
function nodeEntryOf(node, change, fields = []) {
|
|
112
|
-
return {
|
|
113
|
-
change,
|
|
114
|
-
fields,
|
|
115
|
-
id: node.id,
|
|
116
|
-
label: labelOf(node)
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Returns an entry for every node of either version.
|
|
121
|
-
*/
|
|
122
|
-
function nodesOf(before, after) {
|
|
123
|
-
const canonical = canonicalOf();
|
|
124
|
-
const earlier = new Map(before.nodes.map((node) => [node.id, node]));
|
|
125
|
-
const later = new Set(after.nodes.map((node) => node.id));
|
|
126
|
-
const kept = after.nodes.map((node) => {
|
|
127
|
-
const previous = earlier.get(node.id);
|
|
128
|
-
if (previous === void 0) return nodeEntryOf(node, "added");
|
|
129
|
-
const fields = fieldsOf(previous.data, node.data, canonical);
|
|
130
|
-
return nodeEntryOf(node, fields.length > 0 ? "changed" : "unchanged", fields);
|
|
131
|
-
});
|
|
132
|
-
const gone = before.nodes.filter((node) => !later.has(node.id));
|
|
133
|
-
return [...kept, ...gone.map((node) => nodeEntryOf(node, "removed"))];
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Returns the name of each node of a version by its id.
|
|
137
|
-
*/
|
|
138
|
-
function namesOf(version) {
|
|
139
|
-
return new Map(version.nodes.map((node) => [node.id, labelOf(node)]));
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Returns the key an edge is the same edge by: its ends and its ports.
|
|
143
|
-
*/
|
|
144
|
-
function keyOf(edge) {
|
|
145
|
-
return JSON.stringify([
|
|
146
|
-
edge.source,
|
|
147
|
-
edge.sourceHandle ?? null,
|
|
148
|
-
edge.target,
|
|
149
|
-
edge.targetHandle ?? null
|
|
150
|
-
]);
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Returns an entry for every edge of either version, each named from the names of its ends in the
|
|
154
|
-
* version it is in.
|
|
155
|
-
*/
|
|
156
|
-
function edgesOf$2(before, after, edgeName) {
|
|
157
|
-
const earlier = new Set(before.edges.map(keyOf));
|
|
158
|
-
const later = new Set(after.edges.map(keyOf));
|
|
159
|
-
const [earlierNames, laterNames] = [namesOf(before), namesOf(after)];
|
|
160
|
-
/**
|
|
161
|
-
* Returns an edge's entry, named from the names of its version's nodes.
|
|
162
|
-
*/
|
|
163
|
-
const entry = (edge, change, names) => ({
|
|
164
|
-
change,
|
|
165
|
-
fields: [],
|
|
166
|
-
id: edge.id,
|
|
167
|
-
label: edgeName({
|
|
168
|
-
source: names.get(edge.source) ?? edge.source,
|
|
169
|
-
target: names.get(edge.target) ?? edge.target
|
|
170
|
-
})
|
|
171
|
-
});
|
|
172
|
-
return [...after.edges.map((edge) => entry(edge, earlier.has(keyOf(edge)) ? "unchanged" : "added", laterNames)), ...before.edges.filter((edge) => !later.has(keyOf(edge))).map((edge) => entry(edge, "removed", earlierNames))];
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Returns what the later version of a graph adds, changes and removes, node by node and edge by
|
|
176
|
-
* edge.
|
|
177
|
-
*
|
|
178
|
-
* @param before - The earlier version.
|
|
179
|
-
* @param after - The later version.
|
|
180
|
-
* @param edgeName - Writes an edge's name from the names of its ends. `Orders to Revenue` unless
|
|
181
|
-
* stated.
|
|
182
|
-
*/
|
|
183
|
-
function diffGraphs(before, after, edgeName = edgeNameOf) {
|
|
184
|
-
return {
|
|
185
|
-
edges: edgesOf$2(before, after, edgeName),
|
|
186
|
-
nodes: nodesOf(before, after)
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
//#endregion
|
|
190
|
-
//#region src/directed-graph/branches.ts
|
|
191
|
-
/**
|
|
192
|
-
* Returns the ids each node's links lead to, from the links that leave a known node, or from every
|
|
193
|
-
* link without a set of known nodes.
|
|
194
|
-
*/
|
|
195
|
-
function childrenOf(links, known) {
|
|
196
|
-
const children = /* @__PURE__ */ new Map();
|
|
197
|
-
for (const { source, target } of links) if (known === void 0 || known.has(source)) children.set(source, [...children.get(source) ?? [], target]);
|
|
198
|
-
return children;
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Returns the ids a walk visits from its starts, leaving the children of a node it stops at out of
|
|
202
|
-
* the walk.
|
|
203
|
-
*/
|
|
204
|
-
function walkFrom(starts, children, stops) {
|
|
205
|
-
const visited = new Set(starts);
|
|
206
|
-
const queue = [...visited];
|
|
207
|
-
for (const id of queue) for (const child of stops.has(id) ? [] : children.get(id) ?? []) if (!visited.has(child)) {
|
|
208
|
-
visited.add(child);
|
|
209
|
-
queue.push(child);
|
|
210
|
-
}
|
|
211
|
-
return visited;
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* Returns the ids of every node a node leads to, not counting the node itself.
|
|
215
|
-
*/
|
|
216
|
-
function descendantsOf(links, id) {
|
|
217
|
-
const visited = walkFrom([id], childrenOf(links), /* @__PURE__ */ new Set());
|
|
218
|
-
visited.delete(id);
|
|
219
|
-
return visited;
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Returns the ids of the nodes the collapsed nodes hide.
|
|
223
|
-
*
|
|
224
|
-
* @param ids - Every node of the graph.
|
|
225
|
-
* @param links - The graph's links.
|
|
226
|
-
* @param collapsed - The nodes whose branches are closed.
|
|
227
|
-
*/
|
|
228
|
-
function hiddenBy(ids, links, collapsed) {
|
|
229
|
-
const children = childrenOf(links, new Set(ids));
|
|
230
|
-
const entered = new Set([...children.values()].flat());
|
|
231
|
-
const roots = ids.filter((id) => !entered.has(id));
|
|
232
|
-
const rooted = walkFrom(roots, children, /* @__PURE__ */ new Set());
|
|
233
|
-
const shown = walkFrom([...roots, ...ids.filter((id) => !rooted.has(id))], children, collapsed);
|
|
234
|
-
return new Set(ids.filter((id) => !shown.has(id)));
|
|
235
|
-
}
|
|
236
|
-
//#endregion
|
|
237
|
-
//#region src/graph/walk.ts
|
|
238
|
-
/**
|
|
239
|
-
* Returns the nodes a walk along the links visits from a node within a number of hops.
|
|
240
|
-
*
|
|
241
|
-
* @param links - The graph's links.
|
|
242
|
-
* @param from - The node the walk starts from, which it never counts.
|
|
243
|
-
* @param depth - How many hops the walk takes.
|
|
244
|
-
* @param forward - Whether the walk follows a link from its source to its target, or back.
|
|
245
|
-
*/
|
|
246
|
-
function walk(links, from, depth, forward) {
|
|
247
|
-
const visited = /* @__PURE__ */ new Set();
|
|
248
|
-
let frontier = /* @__PURE__ */ new Set([from]);
|
|
249
|
-
for (let hop = 0; hop < depth && frontier.size > 0; hop += 1) {
|
|
250
|
-
const next = /* @__PURE__ */ new Set();
|
|
251
|
-
for (const link of links) {
|
|
252
|
-
const [start, end] = forward ? [link.source, link.target] : [link.target, link.source];
|
|
253
|
-
if (frontier.has(start) && end !== from && !visited.has(end)) {
|
|
254
|
-
visited.add(end);
|
|
255
|
-
next.add(end);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
frontier = next;
|
|
259
|
-
}
|
|
260
|
-
return visited;
|
|
261
|
-
}
|
|
262
|
-
//#endregion
|
|
263
|
-
//#region src/directed-graph/trace.ts
|
|
264
|
-
/**
|
|
265
|
-
* Traces a directed graph from one node: the nodes that feed it and the nodes it feeds, within a
|
|
266
|
-
* number of hops.
|
|
267
|
-
*
|
|
268
|
-
* @remarks
|
|
269
|
-
* A lineage question is "what feeds this" and "what breaks if this breaks". The focused node is
|
|
270
|
-
* in neither set, so a cycle that leads back to it does not make it its own ancestor. A node that
|
|
271
|
-
* is both upstream and downstream, which happens only in a cycle, counts as downstream, because
|
|
272
|
-
* what breaks is the more consequential of the two. The walks stop at a node they visited
|
|
273
|
-
* before, so a cycle never hangs them.
|
|
274
|
-
*/
|
|
275
|
-
/**
|
|
276
|
-
* Returns the nodes that feed a node and the nodes it feeds, within `depth` hops.
|
|
277
|
-
*
|
|
278
|
-
* @param links - Every edge of the graph, by the ids of its ends.
|
|
279
|
-
* @param id - The node the trace starts from, which is in neither set.
|
|
280
|
-
* @param depth - How many hops each way count. Every hop unless stated.
|
|
281
|
-
*/
|
|
282
|
-
function traceGraph(links, id, depth = Number.POSITIVE_INFINITY) {
|
|
283
|
-
return {
|
|
284
|
-
downstream: walk(links, id, depth, true),
|
|
285
|
-
upstream: walk(links, id, depth, false)
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Returns where a node is against the focused node of a trace.
|
|
290
|
-
*/
|
|
291
|
-
function relationOf(id, focus, trace) {
|
|
292
|
-
if (id === focus) return "focus";
|
|
293
|
-
if (trace.downstream.has(id)) return "downstream";
|
|
294
|
-
return trace.upstream.has(id) ? "upstream" : "unrelated";
|
|
295
|
-
}
|
|
296
|
-
//#endregion
|
|
297
|
-
//#region src/graph/marks.ts
|
|
298
|
-
/**
|
|
299
|
-
* Returns the attributes that mark an edge on or off a traced path.
|
|
300
|
-
*
|
|
301
|
-
* @param on - Whether the edge is on the traced path.
|
|
302
|
-
*/
|
|
303
|
-
function traceMarkOf(on) {
|
|
304
|
-
return { "data-trace": on ? "on" : "off" };
|
|
305
|
-
}
|
|
306
|
-
//#endregion
|
|
307
|
-
//#region src/layout/size.ts
|
|
308
|
-
/**
|
|
309
|
-
* Size of a node that states none and has not been measured, in pixels.
|
|
310
|
-
*/
|
|
311
|
-
const SIZE = {
|
|
312
|
-
height: 44,
|
|
313
|
-
width: 256
|
|
314
|
-
};
|
|
315
|
-
/**
|
|
316
|
-
* Returns the size a layout places a node by: the measured size, the stated size, the initial
|
|
317
|
-
* size, or the default.
|
|
318
|
-
*/
|
|
319
|
-
function sizeOf(node) {
|
|
320
|
-
return {
|
|
321
|
-
height: node.measured?.height ?? node.height ?? node.initialHeight ?? SIZE.height,
|
|
322
|
-
width: node.measured?.width ?? node.width ?? node.initialWidth ?? SIZE.width
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
//#endregion
|
|
326
|
-
//#region src/layout/rank.ts
|
|
327
|
-
/**
|
|
328
|
-
* Places a node graph's nodes in ranks with dagre, so its edges run down the canvas or across it.
|
|
329
|
-
*
|
|
330
|
-
* @remarks
|
|
331
|
-
* Dagre centres a node on the point it returns, and React Flow places a node by its top-left
|
|
332
|
-
* corner, so each position is that point less half the node's size. A node's size is the size
|
|
333
|
-
* React Flow measured, else its stated `width` and `height`, else its `initialWidth` and
|
|
334
|
-
* `initialHeight`, else 256 by 44 pixels. A node comes back with that size as its initial size,
|
|
335
|
-
* which React Flow applies to the node's wrapper until it measures the card inside and which the
|
|
336
|
-
* overview map sizes the node by. After the measurement the wrapper, and the focus ring on it,
|
|
337
|
-
* fit the card. Ranks are 128px apart and the nodes of one
|
|
338
|
-
* rank 48px apart, which leaves room for the names of ports outside a node's box. An edge whose
|
|
339
|
-
* ends are not both nodes of the graph takes no part. Dagre knows no ports, and it places the
|
|
340
|
-
* targets of one node in the reverse of the order their edges are added, so the edges are added
|
|
341
|
-
* last first: the targets of a node, and the sources of a node, follow the order of their edges,
|
|
342
|
-
* and edges listed in their ports' order do not cross.
|
|
343
|
-
*/
|
|
344
|
-
/**
|
|
345
|
-
* Space between two ranks and between two nodes of one rank, in pixels.
|
|
346
|
-
*/
|
|
347
|
-
const SPACING = {
|
|
348
|
-
nodesep: 48,
|
|
349
|
-
ranksep: 128
|
|
350
|
-
};
|
|
351
|
-
/**
|
|
352
|
-
* Returns the nodes placed in ranks, each with the size it was placed by as its initial size and
|
|
353
|
-
* the sides its edges leave and enter.
|
|
354
|
-
*
|
|
355
|
-
* @typeParam N - One node of the graph.
|
|
356
|
-
* @param nodes - The graph's nodes, in any order.
|
|
357
|
-
* @param edges - The links between the nodes, of which one to a node outside `nodes` takes no part.
|
|
358
|
-
* @param options - The direction and the spacing.
|
|
359
|
-
*/
|
|
360
|
-
function layoutGraph(nodes, edges, options = {}) {
|
|
361
|
-
const { direction = "down", nodesep = SPACING.nodesep, ranksep = SPACING.ranksep } = options;
|
|
362
|
-
const graph = new Graph();
|
|
363
|
-
const down = direction === "down";
|
|
364
|
-
graph.setDefaultEdgeLabel(() => ({}));
|
|
365
|
-
graph.setGraph({
|
|
366
|
-
nodesep,
|
|
367
|
-
rankdir: down ? "TB" : "LR",
|
|
368
|
-
ranksep
|
|
369
|
-
});
|
|
370
|
-
for (const node of nodes) graph.setNode(node.id, { ...sizeOf(node) });
|
|
371
|
-
for (const edge of edges.toReversed()) if (graph.hasNode(edge.source) && graph.hasNode(edge.target)) graph.setEdge(edge.source, edge.target);
|
|
372
|
-
layout(graph);
|
|
373
|
-
return nodes.map((node) => {
|
|
374
|
-
const { height, width } = sizeOf(node);
|
|
375
|
-
const { x = 0, y = 0 } = graph.node(node.id);
|
|
376
|
-
return {
|
|
377
|
-
...node,
|
|
378
|
-
initialHeight: height,
|
|
379
|
-
initialWidth: width,
|
|
380
|
-
position: {
|
|
381
|
-
x: x - width / 2,
|
|
382
|
-
y: y - height / 2
|
|
383
|
-
},
|
|
384
|
-
sourcePosition: down ? Position.Bottom : Position.Right,
|
|
385
|
-
targetPosition: down ? Position.Top : Position.Left
|
|
386
|
-
};
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
//#endregion
|
|
390
|
-
//#region src/directed-graph/view.ts
|
|
391
|
-
/**
|
|
392
|
-
* Returns the ids of the nodes the canvas shows.
|
|
393
|
-
*/
|
|
394
|
-
function shownOf(input, hidden, traced) {
|
|
395
|
-
const kept = input.nodes.map((node) => node.id).filter((id) => !hidden.has(id));
|
|
396
|
-
if (input.trace !== "isolate" || traced === void 0) return new Set(kept);
|
|
397
|
-
return new Set(kept.filter((id) => id === input.focus || traced.upstream.has(id) || traced.downstream.has(id)));
|
|
398
|
-
}
|
|
399
|
-
/**
|
|
400
|
-
* Returns the relation of each shown node to the focused node, while a trace shows relations.
|
|
401
|
-
*/
|
|
402
|
-
function relationsOf(input, shown, traced) {
|
|
403
|
-
const { focus, trace } = input;
|
|
404
|
-
if (focus === null || traced === void 0 || trace === "off") return /* @__PURE__ */ new Map();
|
|
405
|
-
return new Map([...shown].map((id) => [id, relationOf(id, focus, traced)]));
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* Returns the word for a relation, or undefined for an unrelated node.
|
|
409
|
-
*/
|
|
410
|
-
function tagOf$1(relation, words) {
|
|
411
|
-
if (relation === "focus") return words.focusLabel;
|
|
412
|
-
if (relation === "upstream") return words.upstreamLabel;
|
|
413
|
-
return relation === "downstream" ? words.downstreamLabel : void 0;
|
|
414
|
-
}
|
|
415
|
-
/**
|
|
416
|
-
* Returns a node's branch control: the number of nodes a press hides or shows, or undefined while
|
|
417
|
-
* a press changes nothing.
|
|
418
|
-
*/
|
|
419
|
-
function branchOf(id, input, scope) {
|
|
420
|
-
if (!input.collapsible) return void 0;
|
|
421
|
-
const expanded = !input.collapsed.has(id);
|
|
422
|
-
const toggled = new Set(input.collapsed);
|
|
423
|
-
if (expanded) toggled.add(id);
|
|
424
|
-
else toggled.delete(id);
|
|
425
|
-
const count = Math.abs(hiddenBy(scope.ids, input.edges, toggled).size - scope.hidden.size);
|
|
426
|
-
const label = expanded ? input.words.collapseLabel(count) : input.words.expandLabel(count);
|
|
427
|
-
return count === 0 ? void 0 : {
|
|
428
|
-
expanded,
|
|
429
|
-
label
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
/**
|
|
433
|
-
* Returns the card of one node, unplaced.
|
|
434
|
-
*/
|
|
435
|
-
function cardOf(node, input, scope) {
|
|
436
|
-
const { words } = input;
|
|
437
|
-
const relation = scope.relations.get(node.id);
|
|
438
|
-
const tag = tagOf$1(relation, words);
|
|
439
|
-
const size = input.sizes.get(node.id);
|
|
440
|
-
const data = {
|
|
441
|
-
branch: branchOf(node.id, input, scope),
|
|
442
|
-
detail: node.detail,
|
|
443
|
-
dimmed: relation === "unrelated",
|
|
444
|
-
icon: node.icon,
|
|
445
|
-
inputs: [{
|
|
446
|
-
id: "in",
|
|
447
|
-
label: words.upstreamLabel,
|
|
448
|
-
unused: !scope.entered.has(node.id)
|
|
449
|
-
}],
|
|
450
|
-
kind: node.kind,
|
|
451
|
-
label: node.label,
|
|
452
|
-
outputs: [{
|
|
453
|
-
id: "out",
|
|
454
|
-
label: words.downstreamLabel,
|
|
455
|
-
unused: !scope.left.has(node.id)
|
|
456
|
-
}],
|
|
457
|
-
status: node.status,
|
|
458
|
-
tag
|
|
459
|
-
};
|
|
460
|
-
return {
|
|
461
|
-
ariaLabel: tag === void 0 ? node.label : `${node.label}, ${tag}`,
|
|
462
|
-
data,
|
|
463
|
-
id: node.id,
|
|
464
|
-
position: {
|
|
465
|
-
x: 0,
|
|
466
|
-
y: 0
|
|
467
|
-
},
|
|
468
|
-
selected: node.id === input.focus,
|
|
469
|
-
type: "directed",
|
|
470
|
-
...size === void 0 ? {} : { measured: size }
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
/**
|
|
474
|
-
* Returns the edges whose ends are both among the visible nodes, each with the nodes at its ends.
|
|
475
|
-
*/
|
|
476
|
-
function keptOf(edges, visible) {
|
|
477
|
-
return edges.flatMap((edge) => {
|
|
478
|
-
const from = visible.get(edge.source);
|
|
479
|
-
const to = visible.get(edge.target);
|
|
480
|
-
return from === void 0 || to === void 0 ? [] : [{
|
|
481
|
-
edge,
|
|
482
|
-
from,
|
|
483
|
-
to
|
|
484
|
-
}];
|
|
485
|
-
});
|
|
486
|
-
}
|
|
487
|
-
/**
|
|
488
|
-
* Returns the kept edges for React Flow, named by their ends and marked on or off a traced path
|
|
489
|
-
* while a trace shows relations.
|
|
490
|
-
*/
|
|
491
|
-
function edgesOf$1(input, kept, scope) {
|
|
492
|
-
return kept.map(({ edge, from, to }) => {
|
|
493
|
-
const on = [from, to].every((end) => scope.relations.get(end.id) !== "unrelated");
|
|
494
|
-
return {
|
|
495
|
-
ariaLabel: input.words.edgeName({
|
|
496
|
-
source: from.label,
|
|
497
|
-
target: to.label
|
|
498
|
-
}),
|
|
499
|
-
id: edge.id ?? `${from.id}-${to.id}`,
|
|
500
|
-
selectable: false,
|
|
501
|
-
source: from.id,
|
|
502
|
-
target: to.id,
|
|
503
|
-
...scope.relations.size === 0 ? {} : { domAttributes: traceMarkOf(on) }
|
|
504
|
-
};
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
/**
|
|
508
|
-
* Returns the trace of the focused node in numbers, named by the node's label, else by its id.
|
|
509
|
-
*/
|
|
510
|
-
function countsOf(input, traced) {
|
|
511
|
-
const { focus, nodes } = input;
|
|
512
|
-
if (focus === null || traced === void 0) return void 0;
|
|
513
|
-
return {
|
|
514
|
-
downstream: traced.downstream.size,
|
|
515
|
-
name: nodes.find((node) => node.id === focus)?.label ?? focus,
|
|
516
|
-
upstream: traced.upstream.size
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
/**
|
|
520
|
-
* Returns the view of a directed graph: its cards and edges, placed once every node has been
|
|
521
|
-
* measured.
|
|
522
|
-
*
|
|
523
|
-
* @param input - The graph, its state, the measured sizes and the words.
|
|
524
|
-
*/
|
|
525
|
-
function viewOf$1(input) {
|
|
526
|
-
const ids = input.nodes.map((node) => node.id);
|
|
527
|
-
const complete = ids.every((id) => input.sizes.has(id));
|
|
528
|
-
const traced = input.focus === null ? void 0 : traceGraph(input.edges, input.focus, input.depth);
|
|
529
|
-
const hidden = input.collapsible ? hiddenBy(ids, input.edges, input.collapsed) : /* @__PURE__ */ new Set();
|
|
530
|
-
const shown = complete ? shownOf(input, hidden, traced) : new Set(ids);
|
|
531
|
-
const visible = new Map(input.nodes.filter((node) => shown.has(node.id)).map((node) => [node.id, node]));
|
|
532
|
-
const kept = keptOf(input.edges, visible);
|
|
533
|
-
const scope = {
|
|
534
|
-
entered: new Set(kept.map(({ to }) => to.id)),
|
|
535
|
-
hidden,
|
|
536
|
-
ids,
|
|
537
|
-
left: new Set(kept.map(({ from }) => from.id)),
|
|
538
|
-
relations: relationsOf(input, shown, traced)
|
|
539
|
-
};
|
|
540
|
-
const cards = [...visible.values()].map((node) => cardOf(node, input, scope));
|
|
541
|
-
const edges = edgesOf$1(input, kept, scope);
|
|
542
|
-
return {
|
|
543
|
-
complete,
|
|
544
|
-
counts: countsOf(input, traced),
|
|
545
|
-
edges,
|
|
546
|
-
nodes: complete ? layoutGraph(cards, edges, { direction: input.direction }) : cards
|
|
547
|
-
};
|
|
548
|
-
}
|
|
549
|
-
//#endregion
|
|
550
|
-
//#region src/graph/changes.ts
|
|
551
|
-
/**
|
|
552
|
-
* Returns the id of the node the changes select, `null` when they only clear the selection, or
|
|
553
|
-
* undefined when they change no selection.
|
|
554
|
-
*/
|
|
555
|
-
function selectedBy(changes) {
|
|
556
|
-
const selections = changes.filter((change) => change.type === "select");
|
|
557
|
-
if (selections.length === 0) return void 0;
|
|
558
|
-
return selections.find((change) => change.selected)?.id ?? null;
|
|
559
|
-
}
|
|
560
|
-
/**
|
|
561
|
-
* Returns the sizes with every size the changes measured, or the same sizes when they measured
|
|
562
|
-
* none that differs.
|
|
563
|
-
*
|
|
564
|
-
* @param sizes - The sizes measured before.
|
|
565
|
-
* @param changes - The changes React Flow reported.
|
|
566
|
-
*/
|
|
567
|
-
function measuredBy(sizes, changes) {
|
|
568
|
-
const next = new Map(sizes);
|
|
569
|
-
let changed = false;
|
|
570
|
-
for (const change of changes) if (change.type === "dimensions" && change.dimensions !== void 0) {
|
|
571
|
-
const { height, width } = change.dimensions;
|
|
572
|
-
const known = sizes.get(change.id);
|
|
573
|
-
if (known?.height !== height || known.width !== width) {
|
|
574
|
-
next.set(change.id, {
|
|
575
|
-
height,
|
|
576
|
-
width
|
|
577
|
-
});
|
|
578
|
-
changed = true;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
return changed ? next : sizes;
|
|
582
|
-
}
|
|
583
|
-
/**
|
|
584
|
-
* Returns the positions with every position the changes moved a node to, or the same positions
|
|
585
|
-
* when they moved none.
|
|
586
|
-
*
|
|
587
|
-
* @param positions - The positions people moved nodes to before.
|
|
588
|
-
* @param changes - The changes React Flow reported.
|
|
589
|
-
*/
|
|
590
|
-
function movedBy(positions, changes) {
|
|
591
|
-
const moves = changes.flatMap((change) => change.type === "position" && change.position !== void 0 ? [[change.id, change.position]] : []);
|
|
592
|
-
return moves.length === 0 ? positions : new Map([...positions, ...moves]);
|
|
593
|
-
}
|
|
594
|
-
//#endregion
|
|
595
|
-
//#region src/directed-graph/state.ts
|
|
596
|
-
/**
|
|
597
|
-
* Keeps a directed graph's focus, closed branches and measured sizes, and shares the function that
|
|
598
|
-
* opens and closes a branch with the graph's cards.
|
|
599
|
-
*
|
|
600
|
-
* @remarks
|
|
601
|
-
* The focus and the closed branches are controlled where the caller passes them. A focus of
|
|
602
|
-
* `null` is a controlled graph with no focus, because `undefined` leaves the focus to the graph.
|
|
603
|
-
* React Flow renders each card inside the canvas, where the graph's state is out of reach of the
|
|
604
|
-
* card's props, so the graph provides the function around the canvas.
|
|
605
|
-
*/
|
|
606
|
-
/**
|
|
607
|
-
* Provides the function that opens and closes the branch of the node whose id it receives, and
|
|
608
|
-
* reads it back.
|
|
609
|
-
*/
|
|
610
|
-
const [BranchProvider, useToggle] = createRequiredContext("DirectedGraph");
|
|
611
|
-
/**
|
|
612
|
-
* Returns the ids of the closed branches after a press on a node's branch control.
|
|
613
|
-
*/
|
|
614
|
-
function toggled(closed, id) {
|
|
615
|
-
return closed.includes(id) ? closed.filter((known) => known !== id) : [...closed, id];
|
|
616
|
-
}
|
|
617
|
-
/**
|
|
618
|
-
* Returns the graph's view in its current state and the functions that change the state.
|
|
619
|
-
*
|
|
620
|
-
* @param props - The graph, its focus and branches, its words and its direction.
|
|
621
|
-
*/
|
|
622
|
-
function useTraced(props) {
|
|
623
|
-
const $ = c(36);
|
|
624
|
-
let t0;
|
|
625
|
-
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
626
|
-
t0 = /* @__PURE__ */ new Map();
|
|
627
|
-
$[0] = t0;
|
|
628
|
-
} else t0 = $[0];
|
|
629
|
-
const [sizes, setSizes] = useState(t0);
|
|
630
|
-
const t1 = props.defaultFocus ?? null;
|
|
631
|
-
let t2;
|
|
632
|
-
if ($[1] !== props.focus || $[2] !== props.onFocusChange || $[3] !== t1) {
|
|
633
|
-
t2 = {
|
|
634
|
-
defaultValue: t1,
|
|
635
|
-
onChange: props.onFocusChange,
|
|
636
|
-
value: props.focus
|
|
637
|
-
};
|
|
638
|
-
$[1] = props.focus;
|
|
639
|
-
$[2] = props.onFocusChange;
|
|
640
|
-
$[3] = t1;
|
|
641
|
-
$[4] = t2;
|
|
642
|
-
} else t2 = $[4];
|
|
643
|
-
const [focus, setFocus] = useControllableState(t2);
|
|
644
|
-
let t3;
|
|
645
|
-
if ($[5] !== props.defaultCollapsed) {
|
|
646
|
-
t3 = props.defaultCollapsed ?? [];
|
|
647
|
-
$[5] = props.defaultCollapsed;
|
|
648
|
-
$[6] = t3;
|
|
649
|
-
} else t3 = $[6];
|
|
650
|
-
let t4;
|
|
651
|
-
if ($[7] !== t3) {
|
|
652
|
-
t4 = [...t3];
|
|
653
|
-
$[7] = t3;
|
|
654
|
-
$[8] = t4;
|
|
655
|
-
} else t4 = $[8];
|
|
656
|
-
let t5;
|
|
657
|
-
if ($[9] !== props.collapsed) {
|
|
658
|
-
t5 = props.collapsed === void 0 ? void 0 : [...props.collapsed];
|
|
659
|
-
$[9] = props.collapsed;
|
|
660
|
-
$[10] = t5;
|
|
661
|
-
} else t5 = $[10];
|
|
662
|
-
let t6;
|
|
663
|
-
if ($[11] !== props.onCollapsedChange || $[12] !== t4 || $[13] !== t5) {
|
|
664
|
-
t6 = {
|
|
665
|
-
defaultValue: t4,
|
|
666
|
-
onChange: props.onCollapsedChange,
|
|
667
|
-
value: t5
|
|
668
|
-
};
|
|
669
|
-
$[11] = props.onCollapsedChange;
|
|
670
|
-
$[12] = t4;
|
|
671
|
-
$[13] = t5;
|
|
672
|
-
$[14] = t6;
|
|
673
|
-
} else t6 = $[14];
|
|
674
|
-
const [collapsed, setCollapsed] = useControllableState(t6);
|
|
675
|
-
let t7;
|
|
676
|
-
if ($[15] !== collapsed || $[16] !== focus || $[17] !== props.collapsible || $[18] !== props.depth || $[19] !== props.direction || $[20] !== props.edges || $[21] !== props.nodes || $[22] !== props.trace || $[23] !== props.words || $[24] !== sizes) {
|
|
677
|
-
t7 = viewOf$1({
|
|
678
|
-
collapsed: new Set(collapsed),
|
|
679
|
-
collapsible: props.collapsible === true,
|
|
680
|
-
depth: props.depth ?? Number.POSITIVE_INFINITY,
|
|
681
|
-
direction: props.direction,
|
|
682
|
-
edges: props.edges,
|
|
683
|
-
focus,
|
|
684
|
-
nodes: props.nodes,
|
|
685
|
-
sizes,
|
|
686
|
-
trace: props.trace ?? "highlight",
|
|
687
|
-
words: props.words
|
|
688
|
-
});
|
|
689
|
-
$[15] = collapsed;
|
|
690
|
-
$[16] = focus;
|
|
691
|
-
$[17] = props.collapsible;
|
|
692
|
-
$[18] = props.depth;
|
|
693
|
-
$[19] = props.direction;
|
|
694
|
-
$[20] = props.edges;
|
|
695
|
-
$[21] = props.nodes;
|
|
696
|
-
$[22] = props.trace;
|
|
697
|
-
$[23] = props.words;
|
|
698
|
-
$[24] = sizes;
|
|
699
|
-
$[25] = t7;
|
|
700
|
-
} else t7 = $[25];
|
|
701
|
-
const view = t7;
|
|
702
|
-
let t8;
|
|
703
|
-
let t9;
|
|
704
|
-
if ($[26] !== setFocus) {
|
|
705
|
-
t8 = () => {
|
|
706
|
-
setFocus(null);
|
|
707
|
-
};
|
|
708
|
-
t9 = (changes) => {
|
|
709
|
-
const selected = selectedBy(changes);
|
|
710
|
-
if (selected !== void 0) setFocus(selected);
|
|
711
|
-
setSizes((known) => measuredBy(known, changes));
|
|
712
|
-
};
|
|
713
|
-
$[26] = setFocus;
|
|
714
|
-
$[27] = t8;
|
|
715
|
-
$[28] = t9;
|
|
716
|
-
} else {
|
|
717
|
-
t8 = $[27];
|
|
718
|
-
t9 = $[28];
|
|
719
|
-
}
|
|
720
|
-
let t10;
|
|
721
|
-
if ($[29] !== setCollapsed) {
|
|
722
|
-
t10 = (id) => {
|
|
723
|
-
setCollapsed((closed) => toggled(closed, id));
|
|
724
|
-
};
|
|
725
|
-
$[29] = setCollapsed;
|
|
726
|
-
$[30] = t10;
|
|
727
|
-
} else t10 = $[30];
|
|
728
|
-
let t11;
|
|
729
|
-
if ($[31] !== t10 || $[32] !== t8 || $[33] !== t9 || $[34] !== view) {
|
|
730
|
-
t11 = {
|
|
731
|
-
clear: t8,
|
|
732
|
-
onNodesChange: t9,
|
|
733
|
-
toggle: t10,
|
|
734
|
-
view
|
|
735
|
-
};
|
|
736
|
-
$[31] = t10;
|
|
737
|
-
$[32] = t8;
|
|
738
|
-
$[33] = t9;
|
|
739
|
-
$[34] = view;
|
|
740
|
-
$[35] = t11;
|
|
741
|
-
} else t11 = $[35];
|
|
742
|
-
return t11;
|
|
743
|
-
}
|
|
744
|
-
//#endregion
|
|
745
|
-
//#region src/graph/context.ts
|
|
746
|
-
/**
|
|
747
|
-
* Binds the graph recipe to its parts.
|
|
748
|
-
*
|
|
749
|
-
* @remarks
|
|
750
|
-
* The binding is apart from the recipe, because a compiler reads the recipe at build time and the
|
|
751
|
-
* binding imports the runtime.
|
|
752
|
-
*/
|
|
753
|
-
/**
|
|
754
|
-
* Binds the recipe once. The root provides the variants and every other part reads them.
|
|
755
|
-
*/
|
|
756
|
-
const { withContext, withProvider } = createSlotRecipeContext(recipe);
|
|
757
|
-
//#endregion
|
|
758
|
-
//#region src/graph/diffed.ts
|
|
759
|
-
/**
|
|
760
|
-
* Shares a comparison's changes with a canvas's nodes, names the changes, and marks each edge with
|
|
761
|
-
* its change for the recipe.
|
|
762
|
-
*
|
|
763
|
-
* @remarks
|
|
764
|
-
* A canvas given `changes` provides each node's change by its id and the words of the changes. A
|
|
765
|
-
* node's card reads its own through React Flow's node id: a changed, added or removed card takes
|
|
766
|
-
* the change as its tag, and an unchanged card recedes. An edge takes `data-change` on React
|
|
767
|
-
* Flow's element, which the recipe reads. A node or an edge the comparison does not list, and
|
|
768
|
-
* every node and edge of a canvas without `changes`, is marked by nothing. Each marked copy of an
|
|
769
|
-
* edge is kept for the edge it was made from while its change is the same, so an unchanged edge
|
|
770
|
-
* yields the same copy on every render.
|
|
771
|
-
*/
|
|
772
|
-
/**
|
|
773
|
-
* Provides the changes a canvas marks, or undefined while it marks none.
|
|
774
|
-
*/
|
|
775
|
-
const DiffContext = createContext(void 0);
|
|
776
|
-
/**
|
|
777
|
-
* Marked copy of each edge object, kept while the edge's change is the same.
|
|
778
|
-
*/
|
|
779
|
-
const MARKED = /* @__PURE__ */ new WeakMap();
|
|
780
|
-
/**
|
|
781
|
-
* Returns the word a change is marked by, or undefined for no change.
|
|
782
|
-
*/
|
|
783
|
-
function wordOf(change, words) {
|
|
784
|
-
if (change === "added") return words.addedLabel ?? "Added";
|
|
785
|
-
if (change === "changed") return words.changedLabel ?? "Changed";
|
|
786
|
-
return change === "removed" ? words.removedLabel ?? "Removed" : void 0;
|
|
787
|
-
}
|
|
788
|
-
/**
|
|
789
|
-
* Returns the changes a canvas marks from its comparison and words, or undefined without a
|
|
790
|
-
* comparison.
|
|
791
|
-
*/
|
|
792
|
-
function diffedOf(changes, words) {
|
|
793
|
-
if (changes === void 0) return void 0;
|
|
794
|
-
return {
|
|
795
|
-
edges: new Map(changes.edges.map(({ change, id }) => [id, change])),
|
|
796
|
-
nodes: new Map(changes.nodes.map(({ change, id }) => [id, change])),
|
|
797
|
-
words
|
|
798
|
-
};
|
|
799
|
-
}
|
|
800
|
-
/**
|
|
801
|
-
* Returns the copy of an edge marked with a change: the kept copy while its change matches, else a
|
|
802
|
-
* new copy, which replaces it.
|
|
803
|
-
*/
|
|
804
|
-
function marked(kept, edge, change) {
|
|
805
|
-
const known = kept.get(edge);
|
|
806
|
-
if (known?.change === change) return known.copy;
|
|
807
|
-
const attributes = {
|
|
808
|
-
...edge.domAttributes,
|
|
809
|
-
"data-change": change
|
|
810
|
-
};
|
|
811
|
-
const copy = {
|
|
812
|
-
...edge,
|
|
813
|
-
domAttributes: attributes
|
|
814
|
-
};
|
|
815
|
-
kept.set(edge, {
|
|
816
|
-
change,
|
|
817
|
-
copy
|
|
818
|
-
});
|
|
819
|
-
return copy;
|
|
820
|
-
}
|
|
821
|
-
/**
|
|
822
|
-
* Returns the edges, each the comparison lists marked with its change.
|
|
823
|
-
*
|
|
824
|
-
* @typeParam E - One edge of the graph.
|
|
825
|
-
*/
|
|
826
|
-
function markedEdges(edges, diffed) {
|
|
827
|
-
const kept = MARKED;
|
|
828
|
-
return edges.map((edge) => {
|
|
829
|
-
const change = diffed.edges.get(edge.id);
|
|
830
|
-
return change === void 0 ? edge : marked(kept, edge, change);
|
|
831
|
-
});
|
|
832
|
-
}
|
|
833
|
-
/**
|
|
834
|
-
* Returns the change of the node a card renders for, or undefined outside a canvas that marks
|
|
835
|
-
* changes and for a node the comparison does not list.
|
|
836
|
-
*/
|
|
837
|
-
function useNodeChange() {
|
|
838
|
-
const $ = c(4);
|
|
839
|
-
const id = useNodeId();
|
|
840
|
-
const diffed = use(DiffContext);
|
|
841
|
-
if (id === null || diffed === void 0) return;
|
|
842
|
-
let t0;
|
|
843
|
-
if ($[0] !== diffed.nodes || $[1] !== diffed.words || $[2] !== id) {
|
|
844
|
-
const change = diffed.nodes.get(id);
|
|
845
|
-
t0 = change === void 0 ? void 0 : {
|
|
846
|
-
change,
|
|
847
|
-
word: wordOf(change, diffed.words)
|
|
848
|
-
};
|
|
849
|
-
$[0] = diffed.nodes;
|
|
850
|
-
$[1] = diffed.words;
|
|
851
|
-
$[2] = id;
|
|
852
|
-
$[3] = t0;
|
|
853
|
-
} else t0 = $[3];
|
|
854
|
-
return t0;
|
|
855
|
-
}
|
|
856
|
-
//#endregion
|
|
857
|
-
//#region src/graph/ports.tsx
|
|
858
|
-
/**
|
|
859
|
-
* Renders the port's name with the recipe's port name class.
|
|
860
|
-
*/
|
|
861
|
-
const Name$1 = withContext("span", "portName");
|
|
862
|
-
/**
|
|
863
|
-
* Returns the side of the node the ports of a kind are on.
|
|
864
|
-
*/
|
|
865
|
-
function sideOf(direction, kind) {
|
|
866
|
-
if (direction === "right") return kind === "target" ? Position.Left : Position.Right;
|
|
867
|
-
return kind === "target" ? Position.Top : Position.Bottom;
|
|
868
|
-
}
|
|
869
|
-
/**
|
|
870
|
-
* Renders a handle per port, spread along the side, each containing its name.
|
|
871
|
-
*
|
|
872
|
-
* @param props - The direction, the kind and the ports.
|
|
873
|
-
*/
|
|
874
|
-
function Ports(t0) {
|
|
875
|
-
const $ = c(17);
|
|
876
|
-
const { direction, kind, ports } = t0;
|
|
877
|
-
const connectable = useStore(_temp$4);
|
|
878
|
-
let t1;
|
|
879
|
-
if ($[0] !== direction || $[1] !== kind) {
|
|
880
|
-
t1 = sideOf(direction, kind);
|
|
881
|
-
$[0] = direction;
|
|
882
|
-
$[1] = kind;
|
|
883
|
-
$[2] = t1;
|
|
884
|
-
} else t1 = $[2];
|
|
885
|
-
const side = t1;
|
|
886
|
-
const shown = ports.length > 1;
|
|
887
|
-
let t2;
|
|
888
|
-
if ($[3] !== connectable || $[4] !== kind || $[5] !== ports || $[6] !== shown || $[7] !== side) {
|
|
889
|
-
let t3;
|
|
890
|
-
if ($[9] !== connectable || $[10] !== kind || $[11] !== ports.length || $[12] !== shown || $[13] !== side) {
|
|
891
|
-
t3 = (port, index) => {
|
|
892
|
-
const open = connectable && port.disabled !== true;
|
|
893
|
-
const along = { "--graph-port-along": `${String((index + 1) / (ports.length + 1) * 100)}%` };
|
|
894
|
-
return /* @__PURE__ */ jsx(Handle, {
|
|
895
|
-
"data-unused": port.unused === true ? "" : void 0,
|
|
896
|
-
id: port.id,
|
|
897
|
-
isConnectable: open,
|
|
898
|
-
isConnectableStart: open,
|
|
899
|
-
position: side,
|
|
900
|
-
style: along,
|
|
901
|
-
type: kind,
|
|
902
|
-
children: /* @__PURE__ */ jsx(Name$1, {
|
|
903
|
-
"data-hidden": shown ? void 0 : "",
|
|
904
|
-
children: port.label
|
|
905
|
-
})
|
|
906
|
-
}, port.id);
|
|
907
|
-
};
|
|
908
|
-
$[9] = connectable;
|
|
909
|
-
$[10] = kind;
|
|
910
|
-
$[11] = ports.length;
|
|
911
|
-
$[12] = shown;
|
|
912
|
-
$[13] = side;
|
|
913
|
-
$[14] = t3;
|
|
914
|
-
} else t3 = $[14];
|
|
915
|
-
t2 = ports.map(t3);
|
|
916
|
-
$[3] = connectable;
|
|
917
|
-
$[4] = kind;
|
|
918
|
-
$[5] = ports;
|
|
919
|
-
$[6] = shown;
|
|
920
|
-
$[7] = side;
|
|
921
|
-
$[8] = t2;
|
|
922
|
-
} else t2 = $[8];
|
|
923
|
-
let t3;
|
|
924
|
-
if ($[15] !== t2) {
|
|
925
|
-
t3 = /* @__PURE__ */ jsx(Fragment, { children: t2 });
|
|
926
|
-
$[15] = t2;
|
|
927
|
-
$[16] = t3;
|
|
928
|
-
} else t3 = $[16];
|
|
929
|
-
return t3;
|
|
930
|
-
}
|
|
931
|
-
function _temp$4(state) {
|
|
932
|
-
return state.nodesConnectable;
|
|
933
|
-
}
|
|
934
|
-
//#endregion
|
|
935
|
-
//#region src/graph/state.ts
|
|
936
|
-
/**
|
|
937
|
-
* Shares the graph's direction, its caption's identity and the edges' remove controls with the
|
|
938
|
-
* parts.
|
|
939
|
-
*
|
|
940
|
-
* @remarks
|
|
941
|
-
* A node places its ports on the sides its edges leave and enter. It reads the direction from the
|
|
942
|
-
* root, so a caller states it once per graph. The caption reports itself to the root, which names
|
|
943
|
-
* the figure by it while it renders.
|
|
944
|
-
*/
|
|
945
|
-
/**
|
|
946
|
-
* Provides the way the graph's edges run, down unless a root states otherwise.
|
|
947
|
-
*/
|
|
948
|
-
const DirectionContext = createContext("down");
|
|
949
|
-
/**
|
|
950
|
-
* Returns the way the graph's edges run.
|
|
951
|
-
*/
|
|
952
|
-
function useGraphDirection() {
|
|
953
|
-
return use(DirectionContext);
|
|
954
|
-
}
|
|
955
|
-
/**
|
|
956
|
-
* Provides the root's setter the caption reports itself through, and the hook that reports it.
|
|
957
|
-
*/
|
|
958
|
-
const [LabellingProvider, useLabelled] = createLabelling("Graph");
|
|
959
|
-
/**
|
|
960
|
-
* Provides the ID the caption renders with, and reads it back.
|
|
961
|
-
*/
|
|
962
|
-
const [CaptionProvider, useCaptionId] = createRequiredContext("GraphCaption");
|
|
963
|
-
/**
|
|
964
|
-
* Provides the edges' remove glyph, the words that name each remove control, and whether the
|
|
965
|
-
* canvas takes edits.
|
|
966
|
-
*/
|
|
967
|
-
const RemovalContext = createContext({
|
|
968
|
-
editable: false,
|
|
969
|
-
glyph: void 0,
|
|
970
|
-
name: removeNameOf
|
|
971
|
-
});
|
|
972
|
-
//#endregion
|
|
973
|
-
//#region src/graph/node.tsx
|
|
974
|
-
/**
|
|
975
|
-
* Renders the card with the recipe's node class.
|
|
976
|
-
*/
|
|
977
|
-
const Card$1 = withContext("div", "node");
|
|
978
|
-
/**
|
|
979
|
-
* Renders the header row with the recipe's node header class.
|
|
980
|
-
*/
|
|
981
|
-
const Header = withContext("div", "nodeHeader");
|
|
982
|
-
/**
|
|
983
|
-
* Renders the icon's box with the recipe's node icon class.
|
|
984
|
-
*/
|
|
985
|
-
const Icon = withContext("span", "nodeIcon");
|
|
986
|
-
/**
|
|
987
|
-
* Renders the title and the subtitle's column with the recipe's node text class.
|
|
988
|
-
*/
|
|
989
|
-
const Text = withContext("span", "nodeText");
|
|
990
|
-
/**
|
|
991
|
-
* Renders the title with the recipe's node title class.
|
|
992
|
-
*/
|
|
993
|
-
const Title = withContext("span", "nodeTitle");
|
|
994
|
-
/**
|
|
995
|
-
* Renders the subtitle with the recipe's node subtitle class.
|
|
996
|
-
*/
|
|
997
|
-
const Subtitle = withContext("span", "nodeSubtitle");
|
|
998
|
-
/**
|
|
999
|
-
* Renders the actions' row with the recipe's node actions class.
|
|
1000
|
-
*/
|
|
1001
|
-
const Actions = withContext("span", "nodeActions");
|
|
1002
|
-
/**
|
|
1003
|
-
* Renders the body with the recipe's node body class.
|
|
1004
|
-
*/
|
|
1005
|
-
const Body = withContext("div", "nodeBody");
|
|
1006
|
-
/**
|
|
1007
|
-
* Renders the problem line with the recipe's node problem class.
|
|
1008
|
-
*/
|
|
1009
|
-
const Problem = withContext("div", "nodeProblem");
|
|
1010
|
-
/**
|
|
1011
|
-
* Renders the tag's box with the recipe's node tag class.
|
|
1012
|
-
*/
|
|
1013
|
-
const Tag = withContext("span", "nodeTag");
|
|
1014
|
-
/**
|
|
1015
|
-
* Renders the branch control's box with the recipe's node branch class.
|
|
1016
|
-
*/
|
|
1017
|
-
const Branch = withContext("span", "nodeBranch");
|
|
1018
|
-
/**
|
|
1019
|
-
* Palette of each change's tag.
|
|
1020
|
-
*/
|
|
1021
|
-
const PALETTES = {
|
|
1022
|
-
added: "success",
|
|
1023
|
-
changed: "warning",
|
|
1024
|
-
removed: "error",
|
|
1025
|
-
unchanged: "neutral"
|
|
1026
|
-
};
|
|
1027
|
-
/**
|
|
1028
|
-
* Mark of each change's tag, before its word, so a color is never the only signal.
|
|
1029
|
-
*/
|
|
1030
|
-
const MARKS = {
|
|
1031
|
-
added: "+",
|
|
1032
|
-
changed: "~",
|
|
1033
|
-
removed: "−",
|
|
1034
|
-
unchanged: ""
|
|
1035
|
-
};
|
|
1036
|
-
/**
|
|
1037
|
-
* Returns the card's header: the icon, the title and the subtitle, the status and the actions.
|
|
1038
|
-
*/
|
|
1039
|
-
function headerOf({ actions, icon, status, subtitle, title }) {
|
|
1040
|
-
return /* @__PURE__ */ jsxs(Header, { children: [
|
|
1041
|
-
icon === void 0 ? null : /* @__PURE__ */ jsx(Icon, {
|
|
1042
|
-
"aria-hidden": true,
|
|
1043
|
-
children: icon
|
|
1044
|
-
}),
|
|
1045
|
-
/* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx(Title, { children: title }), subtitle === void 0 ? null : /* @__PURE__ */ jsx(Subtitle, { children: subtitle })] }),
|
|
1046
|
-
status === void 0 ? null : /* @__PURE__ */ jsxs(Status.Root, {
|
|
1047
|
-
palette: status.palette,
|
|
1048
|
-
size: "sm",
|
|
1049
|
-
children: [/* @__PURE__ */ jsx(Status.Indicator, {}), status.label]
|
|
1050
|
-
}),
|
|
1051
|
-
actions === void 0 ? null : /* @__PURE__ */ jsx(Actions, {
|
|
1052
|
-
className: "nodrag nopan",
|
|
1053
|
-
children: actions
|
|
1054
|
-
})
|
|
1055
|
-
] });
|
|
1056
|
-
}
|
|
1057
|
-
/**
|
|
1058
|
-
* Returns the card's body, its problem line while the node is invalid, and its tag.
|
|
1059
|
-
*/
|
|
1060
|
-
function contentOf({ children, invalid, problem, tag }) {
|
|
1061
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1062
|
-
children === void 0 ? null : /* @__PURE__ */ jsx(Body, { children }),
|
|
1063
|
-
invalid === true && problem !== void 0 ? /* @__PURE__ */ jsx(Problem, { children: problem }) : null,
|
|
1064
|
-
tag === void 0 ? null : /* @__PURE__ */ jsx(Tag, { children: tag })
|
|
1065
|
-
] });
|
|
1066
|
-
}
|
|
1067
|
-
/**
|
|
1068
|
-
* Returns the tag of a card's change: a badge with the change's mark and word, or nothing for an
|
|
1069
|
-
* unchanged node and for a node without a change.
|
|
1070
|
-
*/
|
|
1071
|
-
function changeTagOf(change) {
|
|
1072
|
-
if (change?.word === void 0) return void 0;
|
|
1073
|
-
return /* @__PURE__ */ jsx(Badge, {
|
|
1074
|
-
"aria-hidden": true,
|
|
1075
|
-
palette: PALETTES[change.change],
|
|
1076
|
-
size: "sm",
|
|
1077
|
-
variant: "surface",
|
|
1078
|
-
children: `${MARKS[change.change]} ${change.word}`
|
|
1079
|
-
});
|
|
1080
|
-
}
|
|
1081
|
-
/**
|
|
1082
|
-
* Renders the card with its ports, header, body, problem line, tag and branch control.
|
|
1083
|
-
*
|
|
1084
|
-
* @remarks
|
|
1085
|
-
* The branch control renders after the ports, so it covers the handle of the side it is on.
|
|
1086
|
-
* @param props - The title, the details, the ports and the states.
|
|
1087
|
-
*/
|
|
1088
|
-
function Node$1(props) {
|
|
1089
|
-
const $ = c(28);
|
|
1090
|
-
const { branch, inputs: t0, outputs: t1 } = props;
|
|
1091
|
-
let t2;
|
|
1092
|
-
if ($[0] !== t0) {
|
|
1093
|
-
t2 = t0 === void 0 ? [] : t0;
|
|
1094
|
-
$[0] = t0;
|
|
1095
|
-
$[1] = t2;
|
|
1096
|
-
} else t2 = $[1];
|
|
1097
|
-
const inputs = t2;
|
|
1098
|
-
let t3;
|
|
1099
|
-
if ($[2] !== t1) {
|
|
1100
|
-
t3 = t1 === void 0 ? [] : t1;
|
|
1101
|
-
$[2] = t1;
|
|
1102
|
-
$[3] = t3;
|
|
1103
|
-
} else t3 = $[3];
|
|
1104
|
-
const outputs = t3;
|
|
1105
|
-
const flow = useGraphDirection();
|
|
1106
|
-
const change = useNodeChange();
|
|
1107
|
-
const direction = props.direction ?? flow;
|
|
1108
|
-
const t4 = props.dimmed ?? change?.change === "unchanged" ? "" : void 0;
|
|
1109
|
-
const t5 = props.invalid === true ? "" : void 0;
|
|
1110
|
-
let t6;
|
|
1111
|
-
if ($[4] !== direction || $[5] !== inputs) {
|
|
1112
|
-
t6 = /* @__PURE__ */ jsx(Ports, {
|
|
1113
|
-
direction,
|
|
1114
|
-
kind: "target",
|
|
1115
|
-
ports: inputs
|
|
1116
|
-
});
|
|
1117
|
-
$[4] = direction;
|
|
1118
|
-
$[5] = inputs;
|
|
1119
|
-
$[6] = t6;
|
|
1120
|
-
} else t6 = $[6];
|
|
1121
|
-
let t7;
|
|
1122
|
-
if ($[7] !== props) {
|
|
1123
|
-
t7 = headerOf(props);
|
|
1124
|
-
$[7] = props;
|
|
1125
|
-
$[8] = t7;
|
|
1126
|
-
} else t7 = $[8];
|
|
1127
|
-
let t8;
|
|
1128
|
-
if ($[9] !== change || $[10] !== props.tag) {
|
|
1129
|
-
t8 = props.tag ?? changeTagOf(change);
|
|
1130
|
-
$[9] = change;
|
|
1131
|
-
$[10] = props.tag;
|
|
1132
|
-
$[11] = t8;
|
|
1133
|
-
} else t8 = $[11];
|
|
1134
|
-
let t9;
|
|
1135
|
-
if ($[12] !== props || $[13] !== t8) {
|
|
1136
|
-
t9 = contentOf({
|
|
1137
|
-
...props,
|
|
1138
|
-
tag: t8
|
|
1139
|
-
});
|
|
1140
|
-
$[12] = props;
|
|
1141
|
-
$[13] = t8;
|
|
1142
|
-
$[14] = t9;
|
|
1143
|
-
} else t9 = $[14];
|
|
1144
|
-
let t10;
|
|
1145
|
-
if ($[15] !== direction || $[16] !== outputs) {
|
|
1146
|
-
t10 = /* @__PURE__ */ jsx(Ports, {
|
|
1147
|
-
direction,
|
|
1148
|
-
kind: "source",
|
|
1149
|
-
ports: outputs
|
|
1150
|
-
});
|
|
1151
|
-
$[15] = direction;
|
|
1152
|
-
$[16] = outputs;
|
|
1153
|
-
$[17] = t10;
|
|
1154
|
-
} else t10 = $[17];
|
|
1155
|
-
let t11;
|
|
1156
|
-
if ($[18] !== branch) {
|
|
1157
|
-
t11 = branch === void 0 ? null : /* @__PURE__ */ jsx(Branch, { children: branch });
|
|
1158
|
-
$[18] = branch;
|
|
1159
|
-
$[19] = t11;
|
|
1160
|
-
} else t11 = $[19];
|
|
1161
|
-
let t12;
|
|
1162
|
-
if ($[20] !== t10 || $[21] !== t11 || $[22] !== t4 || $[23] !== t5 || $[24] !== t6 || $[25] !== t7 || $[26] !== t9) {
|
|
1163
|
-
t12 = /* @__PURE__ */ jsxs(Card$1, {
|
|
1164
|
-
"data-dimmed": t4,
|
|
1165
|
-
"data-invalid": t5,
|
|
1166
|
-
children: [
|
|
1167
|
-
t6,
|
|
1168
|
-
t7,
|
|
1169
|
-
t9,
|
|
1170
|
-
t10,
|
|
1171
|
-
t11
|
|
1172
|
-
]
|
|
1173
|
-
});
|
|
1174
|
-
$[20] = t10;
|
|
1175
|
-
$[21] = t11;
|
|
1176
|
-
$[22] = t4;
|
|
1177
|
-
$[23] = t5;
|
|
1178
|
-
$[24] = t6;
|
|
1179
|
-
$[25] = t7;
|
|
1180
|
-
$[26] = t9;
|
|
1181
|
-
$[27] = t12;
|
|
1182
|
-
} else t12 = $[27];
|
|
1183
|
-
return t12;
|
|
1184
|
-
}
|
|
1185
|
-
//#endregion
|
|
1186
|
-
//#region src/directed-graph/card.tsx
|
|
1187
|
-
/**
|
|
1188
|
-
* Renders the node's card from its data.
|
|
1189
|
-
*
|
|
1190
|
-
* @param props - React Flow's node props: the node's id and its data among them.
|
|
1191
|
-
*/
|
|
1192
|
-
function Card(t0) {
|
|
1193
|
-
const $ = c(17);
|
|
1194
|
-
const { data, id } = t0;
|
|
1195
|
-
const toggle = useToggle();
|
|
1196
|
-
const { branch, tag } = data;
|
|
1197
|
-
let t1;
|
|
1198
|
-
if ($[0] !== branch || $[1] !== id || $[2] !== toggle) {
|
|
1199
|
-
t1 = branch === void 0 ? void 0 : /* @__PURE__ */ jsx(Button, {
|
|
1200
|
-
"aria-expanded": branch.expanded,
|
|
1201
|
-
className: "nokey nodrag nopan",
|
|
1202
|
-
onClick: (event) => {
|
|
1203
|
-
event.stopPropagation();
|
|
1204
|
-
toggle(id);
|
|
1205
|
-
},
|
|
1206
|
-
palette: "neutral",
|
|
1207
|
-
size: "xs",
|
|
1208
|
-
variant: "surface",
|
|
1209
|
-
children: branch.label
|
|
1210
|
-
});
|
|
1211
|
-
$[0] = branch;
|
|
1212
|
-
$[1] = id;
|
|
1213
|
-
$[2] = toggle;
|
|
1214
|
-
$[3] = t1;
|
|
1215
|
-
} else t1 = $[3];
|
|
1216
|
-
let t2;
|
|
1217
|
-
if ($[4] !== tag) {
|
|
1218
|
-
t2 = tag === void 0 ? void 0 : /* @__PURE__ */ jsx(Badge, {
|
|
1219
|
-
"aria-hidden": true,
|
|
1220
|
-
palette: "neutral",
|
|
1221
|
-
size: "sm",
|
|
1222
|
-
variant: "surface",
|
|
1223
|
-
children: tag
|
|
1224
|
-
});
|
|
1225
|
-
$[4] = tag;
|
|
1226
|
-
$[5] = t2;
|
|
1227
|
-
} else t2 = $[5];
|
|
1228
|
-
let t3;
|
|
1229
|
-
if ($[6] !== data.detail || $[7] !== data.dimmed || $[8] !== data.icon || $[9] !== data.inputs || $[10] !== data.kind || $[11] !== data.label || $[12] !== data.outputs || $[13] !== data.status || $[14] !== t1 || $[15] !== t2) {
|
|
1230
|
-
t3 = /* @__PURE__ */ jsx(Node$1, {
|
|
1231
|
-
branch: t1,
|
|
1232
|
-
dimmed: data.dimmed,
|
|
1233
|
-
icon: data.icon,
|
|
1234
|
-
inputs: data.inputs,
|
|
1235
|
-
outputs: data.outputs,
|
|
1236
|
-
status: data.status,
|
|
1237
|
-
subtitle: data.kind,
|
|
1238
|
-
tag: t2,
|
|
1239
|
-
title: data.label,
|
|
1240
|
-
children: data.detail
|
|
1241
|
-
});
|
|
1242
|
-
$[6] = data.detail;
|
|
1243
|
-
$[7] = data.dimmed;
|
|
1244
|
-
$[8] = data.icon;
|
|
1245
|
-
$[9] = data.inputs;
|
|
1246
|
-
$[10] = data.kind;
|
|
1247
|
-
$[11] = data.label;
|
|
1248
|
-
$[12] = data.outputs;
|
|
1249
|
-
$[13] = data.status;
|
|
1250
|
-
$[14] = t1;
|
|
1251
|
-
$[15] = t2;
|
|
1252
|
-
$[16] = t3;
|
|
1253
|
-
} else t3 = $[16];
|
|
1254
|
-
return t3;
|
|
1255
|
-
}
|
|
1256
|
-
//#endregion
|
|
1257
|
-
//#region src/graph/drop.ts
|
|
1258
|
-
/**
|
|
1259
|
-
* Type of the drag data a palette item writes its id under.
|
|
1260
|
-
*/
|
|
1261
|
-
const ITEM = "application/x-stealth-graph-item";
|
|
1262
|
-
/**
|
|
1263
|
-
* Returns the handlers that take a palette item's drop and report it with the point it was dropped
|
|
1264
|
-
* at, or no handler without a caller to report to.
|
|
1265
|
-
*
|
|
1266
|
-
* @param onDropItem - Called with the item's id and the point it was dropped at.
|
|
1267
|
-
*/
|
|
1268
|
-
function useDrop(onDropItem) {
|
|
1269
|
-
const $ = c(4);
|
|
1270
|
-
const flow = useReactFlow();
|
|
1271
|
-
if (onDropItem === void 0) {
|
|
1272
|
-
let t0;
|
|
1273
|
-
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
1274
|
-
t0 = {};
|
|
1275
|
-
$[0] = t0;
|
|
1276
|
-
} else t0 = $[0];
|
|
1277
|
-
return t0;
|
|
1278
|
-
}
|
|
1279
|
-
let t0;
|
|
1280
|
-
if ($[1] !== flow || $[2] !== onDropItem) {
|
|
1281
|
-
t0 = {
|
|
1282
|
-
onDragOver: _temp$3,
|
|
1283
|
-
onDrop: (event_0) => {
|
|
1284
|
-
const item = event_0.dataTransfer.getData(ITEM);
|
|
1285
|
-
if (item === "") return;
|
|
1286
|
-
event_0.preventDefault();
|
|
1287
|
-
onDropItem(item, flow.screenToFlowPosition({
|
|
1288
|
-
x: event_0.clientX,
|
|
1289
|
-
y: event_0.clientY
|
|
1290
|
-
}));
|
|
1291
|
-
}
|
|
1292
|
-
};
|
|
1293
|
-
$[1] = flow;
|
|
1294
|
-
$[2] = onDropItem;
|
|
1295
|
-
$[3] = t0;
|
|
1296
|
-
} else t0 = $[3];
|
|
1297
|
-
return t0;
|
|
1298
|
-
}
|
|
1299
|
-
function _temp$3(event) {
|
|
1300
|
-
if (!event.dataTransfer.types.includes("application/x-stealth-graph-item")) return;
|
|
1301
|
-
event.preventDefault();
|
|
1302
|
-
event.dataTransfer.dropEffect = "copy";
|
|
1303
|
-
}
|
|
1304
|
-
/**
|
|
1305
|
-
* Returns a function that returns the middle of what the canvas shows, in the graph's coordinates,
|
|
1306
|
-
* or the graph's origin while no canvas renders.
|
|
1307
|
-
*/
|
|
1308
|
-
function useViewCentre() {
|
|
1309
|
-
const $ = c(3);
|
|
1310
|
-
const flow = useReactFlow();
|
|
1311
|
-
const store = useStoreApi();
|
|
1312
|
-
let t0;
|
|
1313
|
-
if ($[0] !== flow || $[1] !== store) {
|
|
1314
|
-
t0 = () => {
|
|
1315
|
-
const { domNode, height, width } = store.getState();
|
|
1316
|
-
if (domNode === null) return {
|
|
1317
|
-
x: 0,
|
|
1318
|
-
y: 0
|
|
1319
|
-
};
|
|
1320
|
-
const box = domNode.getBoundingClientRect();
|
|
1321
|
-
return flow.screenToFlowPosition({
|
|
1322
|
-
x: box.left + width / 2,
|
|
1323
|
-
y: box.top + height / 2
|
|
1324
|
-
});
|
|
1325
|
-
};
|
|
1326
|
-
$[0] = flow;
|
|
1327
|
-
$[1] = store;
|
|
1328
|
-
$[2] = t0;
|
|
1329
|
-
} else t0 = $[2];
|
|
1330
|
-
return t0;
|
|
1331
|
-
}
|
|
1332
|
-
//#endregion
|
|
1333
|
-
//#region src/graph/editing.ts
|
|
1334
|
-
/**
|
|
1335
|
-
* Turns React Flow's changes into whole graphs for a caller's history: a connection, a move that
|
|
1336
|
-
* ends and a removal each arrive once, as the graph after the edit and a step that names it.
|
|
1337
|
-
*
|
|
1338
|
-
* @remarks
|
|
1339
|
-
* A drag reports a node's position on every frame and a last position with `dragging` false, and
|
|
1340
|
-
* an arrow key reports one position with `dragging` false, so a move arrives once, where it ends.
|
|
1341
|
-
* A removal by Delete, Backspace or an edge's remove control arrives once with every node and
|
|
1342
|
-
* edge it removed, and a removed node takes its edges with it. A connection that repeats an edge
|
|
1343
|
-
* the graph has already adds nothing and arrives as nothing. A selection and a measurement are no
|
|
1344
|
-
* edits. The caller's own `onConnect`, `onDelete` and `onNodesChange` run first, so a caller that
|
|
1345
|
-
* applies React Flow's changes keeps doing so.
|
|
1346
|
-
*/
|
|
1347
|
-
/**
|
|
1348
|
-
* Returns the positions a move ended at, or no change while no move ended.
|
|
1349
|
-
*/
|
|
1350
|
-
function endsOf(changes) {
|
|
1351
|
-
return changes.filter((change) => change.type === "position" && change.dragging === false);
|
|
1352
|
-
}
|
|
1353
|
-
/**
|
|
1354
|
-
* Returns the handlers that report each edit through `onGraphChange` after the caller's own.
|
|
1355
|
-
*
|
|
1356
|
-
* @typeParam N - One node of the graph.
|
|
1357
|
-
* @typeParam E - One edge of the graph.
|
|
1358
|
-
*/
|
|
1359
|
-
function editingOf(input) {
|
|
1360
|
-
const { edges, nodes, onGraphChange } = input;
|
|
1361
|
-
return {
|
|
1362
|
-
onConnect: (connection) => {
|
|
1363
|
-
input.onConnect?.(connection);
|
|
1364
|
-
const next = addEdge(connection, edges);
|
|
1365
|
-
const added = next.filter((edge) => !edges.includes(edge)).map(({ id }) => id);
|
|
1366
|
-
if (added.length > 0) onGraphChange({
|
|
1367
|
-
edges: next,
|
|
1368
|
-
nodes
|
|
1369
|
-
}, {
|
|
1370
|
-
edges: added,
|
|
1371
|
-
nodes: [],
|
|
1372
|
-
type: "connect"
|
|
1373
|
-
});
|
|
1374
|
-
},
|
|
1375
|
-
onDelete: (removed) => {
|
|
1376
|
-
input.onDelete?.(removed);
|
|
1377
|
-
const [nodeIds, edgeIds] = [new Set(removed.nodes.map(({ id }) => id)), new Set(removed.edges.map(({ id }) => id))];
|
|
1378
|
-
const graph = {
|
|
1379
|
-
edges: edges.filter(({ id }) => !edgeIds.has(id)),
|
|
1380
|
-
nodes: nodes.filter(({ id }) => !nodeIds.has(id))
|
|
1381
|
-
};
|
|
1382
|
-
onGraphChange(graph, {
|
|
1383
|
-
edges: [...edgeIds],
|
|
1384
|
-
nodes: [...nodeIds],
|
|
1385
|
-
type: "remove"
|
|
1386
|
-
});
|
|
1387
|
-
},
|
|
1388
|
-
onNodesChange: (changes) => {
|
|
1389
|
-
input.onNodesChange?.(changes);
|
|
1390
|
-
const ends = endsOf(changes);
|
|
1391
|
-
if (ends.length > 0) onGraphChange({
|
|
1392
|
-
edges,
|
|
1393
|
-
nodes: applyNodeChanges(ends, nodes)
|
|
1394
|
-
}, {
|
|
1395
|
-
edges: [],
|
|
1396
|
-
nodes: ends.map(({ id }) => id),
|
|
1397
|
-
type: "move"
|
|
1398
|
-
});
|
|
1399
|
-
}
|
|
1400
|
-
};
|
|
1401
|
-
}
|
|
1402
|
-
/**
|
|
1403
|
-
* Returns the handlers a canvas gives React Flow for its edits and drops: the drop target, and the
|
|
1404
|
-
* edit handlers while the caller takes edits through `onGraphChange`.
|
|
1405
|
-
*
|
|
1406
|
-
* @typeParam N - One node of the graph.
|
|
1407
|
-
* @typeParam E - One edge of the graph.
|
|
1408
|
-
*/
|
|
1409
|
-
function useEdits(input) {
|
|
1410
|
-
const $ = c(14);
|
|
1411
|
-
const drop = useDrop(input.onDropItem);
|
|
1412
|
-
const { onGraphChange } = input;
|
|
1413
|
-
if (onGraphChange === void 0) return drop;
|
|
1414
|
-
let t0;
|
|
1415
|
-
if ($[0] !== input.edges) {
|
|
1416
|
-
t0 = input.edges ?? [];
|
|
1417
|
-
$[0] = input.edges;
|
|
1418
|
-
$[1] = t0;
|
|
1419
|
-
} else t0 = $[1];
|
|
1420
|
-
let t1;
|
|
1421
|
-
if ($[2] !== input.nodes) {
|
|
1422
|
-
t1 = input.nodes ?? [];
|
|
1423
|
-
$[2] = input.nodes;
|
|
1424
|
-
$[3] = t1;
|
|
1425
|
-
} else t1 = $[3];
|
|
1426
|
-
let t2;
|
|
1427
|
-
let t3;
|
|
1428
|
-
if ($[4] !== drop || $[5] !== input || $[6] !== onGraphChange || $[7] !== t0 || $[8] !== t1) {
|
|
1429
|
-
const graph = {
|
|
1430
|
-
edges: t0,
|
|
1431
|
-
nodes: t1
|
|
1432
|
-
};
|
|
1433
|
-
t2 = drop;
|
|
1434
|
-
t3 = editingOf({
|
|
1435
|
-
...input,
|
|
1436
|
-
...graph,
|
|
1437
|
-
onGraphChange
|
|
1438
|
-
});
|
|
1439
|
-
$[4] = drop;
|
|
1440
|
-
$[5] = input;
|
|
1441
|
-
$[6] = onGraphChange;
|
|
1442
|
-
$[7] = t0;
|
|
1443
|
-
$[8] = t1;
|
|
1444
|
-
$[9] = t2;
|
|
1445
|
-
$[10] = t3;
|
|
1446
|
-
} else {
|
|
1447
|
-
t2 = $[9];
|
|
1448
|
-
t3 = $[10];
|
|
1449
|
-
}
|
|
1450
|
-
let t4;
|
|
1451
|
-
if ($[11] !== t2 || $[12] !== t3) {
|
|
1452
|
-
t4 = {
|
|
1453
|
-
...t2,
|
|
1454
|
-
...t3
|
|
1455
|
-
};
|
|
1456
|
-
$[11] = t2;
|
|
1457
|
-
$[12] = t3;
|
|
1458
|
-
$[13] = t4;
|
|
1459
|
-
} else t4 = $[13];
|
|
1460
|
-
return t4;
|
|
1461
|
-
}
|
|
1462
|
-
//#endregion
|
|
1463
|
-
//#region src/graph/names.ts
|
|
1464
|
-
/**
|
|
1465
|
-
* Named copy of each node object, kept while the node's name is unchanged.
|
|
1466
|
-
*/
|
|
1467
|
-
const NODES = /* @__PURE__ */ new WeakMap();
|
|
1468
|
-
/**
|
|
1469
|
-
* Named copy of each edge object, kept while the edge's name is unchanged.
|
|
1470
|
-
*/
|
|
1471
|
-
const EDGES = /* @__PURE__ */ new WeakMap();
|
|
1472
|
-
/**
|
|
1473
|
-
* Returns the name a node takes: its own `ariaLabel`, else its data's string `label`.
|
|
1474
|
-
*/
|
|
1475
|
-
function nameOf(node) {
|
|
1476
|
-
const label = node.data["label"];
|
|
1477
|
-
return node.ariaLabel ?? (typeof label === "string" ? label : void 0);
|
|
1478
|
-
}
|
|
1479
|
-
/**
|
|
1480
|
-
* Returns the copy of an element named `name`: the kept copy while its name matches, else a new
|
|
1481
|
-
* copy, which replaces it.
|
|
1482
|
-
*/
|
|
1483
|
-
function named(kept, element, name) {
|
|
1484
|
-
const copy = kept.get(element);
|
|
1485
|
-
if (copy?.ariaLabel === name) return copy;
|
|
1486
|
-
const made = {
|
|
1487
|
-
...element,
|
|
1488
|
-
ariaLabel: name
|
|
1489
|
-
};
|
|
1490
|
-
kept.set(element, made);
|
|
1491
|
-
return made;
|
|
1492
|
-
}
|
|
1493
|
-
/**
|
|
1494
|
-
* Returns no word, for a node or an edge the canvas marks no change on.
|
|
1495
|
-
*/
|
|
1496
|
-
function none() {}
|
|
1497
|
-
/**
|
|
1498
|
-
* Returns the nodes, each without a name of its own named by its data's `label`, and each with a
|
|
1499
|
-
* change named with the change's word after its name, else after its id.
|
|
1500
|
-
*
|
|
1501
|
-
* @typeParam N - One node of the graph.
|
|
1502
|
-
* @param nodes - The graph's nodes.
|
|
1503
|
-
* @param changeOf - Returns the word of a node's change by its id, or undefined for none.
|
|
1504
|
-
*/
|
|
1505
|
-
function namedNodes(nodes, changeOf = none) {
|
|
1506
|
-
const kept = NODES;
|
|
1507
|
-
return nodes.map((node) => {
|
|
1508
|
-
const name = nameOf(node);
|
|
1509
|
-
const word = changeOf(node.id);
|
|
1510
|
-
if (word !== void 0) return named(kept, node, `${name ?? node.id}, ${word}`);
|
|
1511
|
-
return node.ariaLabel !== void 0 || name === void 0 ? node : named(kept, node, name);
|
|
1512
|
-
});
|
|
1513
|
-
}
|
|
1514
|
-
/**
|
|
1515
|
-
* Returns the edges, each without a name of its own named by the names of its ends, and each with a
|
|
1516
|
-
* change named with the change's word after its name.
|
|
1517
|
-
*
|
|
1518
|
-
* @typeParam E - One edge of the graph.
|
|
1519
|
-
* @param edges - Links between the nodes.
|
|
1520
|
-
* @param nodes - The graph's nodes, whose names name the ends. An end without a name is named by
|
|
1521
|
-
* its id.
|
|
1522
|
-
* @param nameEdge - The canvas's words for an edge between two named ends.
|
|
1523
|
-
* @param changeOf - Returns the word of an edge's change by its id, or undefined for none.
|
|
1524
|
-
*/
|
|
1525
|
-
function namedEdges(edges, nodes, nameEdge, changeOf = none) {
|
|
1526
|
-
const kept = EDGES;
|
|
1527
|
-
const names = new Map(nodes.map((node) => [node.id, nameOf(node)]));
|
|
1528
|
-
return edges.map((edge) => {
|
|
1529
|
-
const word = changeOf(edge.id);
|
|
1530
|
-
if (edge.ariaLabel !== void 0 && word === void 0) return edge;
|
|
1531
|
-
const name = edge.ariaLabel ?? nameEdge({
|
|
1532
|
-
source: names.get(edge.source) ?? edge.source,
|
|
1533
|
-
target: names.get(edge.target) ?? edge.target
|
|
1534
|
-
});
|
|
1535
|
-
return named(kept, edge, word === void 0 ? name : `${name}, ${word}`);
|
|
1536
|
-
});
|
|
1537
|
-
}
|
|
1538
|
-
/**
|
|
1539
|
-
* Returns the function that reads the word of a node's or an edge's change, or no word while the
|
|
1540
|
-
* canvas marks no change.
|
|
1541
|
-
*/
|
|
1542
|
-
function changesOf(diffed, kind) {
|
|
1543
|
-
return diffed === void 0 ? none : (id) => wordOf(diffed[kind].get(id), diffed.words);
|
|
1544
|
-
}
|
|
1545
|
-
/**
|
|
1546
|
-
* Returns the edges named, and each marked with its change while the canvas marks changes.
|
|
1547
|
-
*/
|
|
1548
|
-
function edgesOf(edges, nodes, nameEdge, diffed) {
|
|
1549
|
-
const labelled = namedEdges(edges, nodes, nameEdge, changesOf(diffed, "edges"));
|
|
1550
|
-
return diffed === void 0 ? labelled : markedEdges(labelled, diffed);
|
|
1551
|
-
}
|
|
1552
|
-
/**
|
|
1553
|
-
* Returns the graph with its nodes and edges named, leaving out what the graph leaves out.
|
|
1554
|
-
*
|
|
1555
|
-
* @typeParam N - One node of the graph.
|
|
1556
|
-
* @typeParam E - One edge of the graph.
|
|
1557
|
-
* @param graph - The nodes and the edges, either of which may be absent.
|
|
1558
|
-
* @param nameEdge - The canvas's words for an edge between two named ends.
|
|
1559
|
-
* @param diffed - The changes the canvas marks, or undefined for none.
|
|
1560
|
-
*/
|
|
1561
|
-
function namedGraph({ edges, nodes }, nameEdge, diffed) {
|
|
1562
|
-
return omitUndefined({
|
|
1563
|
-
edges: edges === void 0 ? void 0 : edgesOf(edges, nodes ?? [], nameEdge, diffed),
|
|
1564
|
-
nodes: nodes === void 0 ? void 0 : namedNodes(nodes, changesOf(diffed, "nodes"))
|
|
1565
|
-
});
|
|
1566
|
-
}
|
|
1567
|
-
/**
|
|
1568
|
-
* Room a fit keeps clear on each side of the canvas, in pixels.
|
|
1569
|
-
*/
|
|
1570
|
-
const PADDING = {
|
|
1571
|
-
bottom: "32px",
|
|
1572
|
-
left: "24px",
|
|
1573
|
-
right: "24px",
|
|
1574
|
-
top: "32px"
|
|
1575
|
-
};
|
|
1576
|
-
/**
|
|
1577
|
-
* How the view fits the graph: at most at its own size, clear of the canvas's edges and the
|
|
1578
|
-
* attribution.
|
|
1579
|
-
*/
|
|
1580
|
-
const FIT = {
|
|
1581
|
-
maxZoom: 1,
|
|
1582
|
-
padding: PADDING
|
|
1583
|
-
};
|
|
1584
|
-
/**
|
|
1585
|
-
* Smallest zoom the view takes.
|
|
1586
|
-
*/
|
|
1587
|
-
const MIN_ZOOM = .1;
|
|
1588
|
-
//#endregion
|
|
1589
|
-
//#region src/graph/edge.tsx
|
|
1590
|
-
/**
|
|
1591
|
-
* Renders the kit's edge: React Flow's bezier between two ports, and a control that removes it.
|
|
1592
|
-
*
|
|
1593
|
-
* @remarks
|
|
1594
|
-
* React Flow's edge is a path 1px wide with a thin area the pointer can hit, which a mouse hits
|
|
1595
|
-
* with difficulty and a finger misses. While the canvas takes edits and has a `removeGlyph`, a
|
|
1596
|
-
* deletable edge renders the actions `IconButton` at its middle with the glyph, in React Flow's
|
|
1597
|
-
* label layer, a tab stop like any button. The control is named by the edge data's
|
|
1598
|
-
* `removeLabel`, else by the canvas's `removeName` over the names of the edge's ends, so an edge
|
|
1599
|
-
* a person connects has a control too. A press removes the edge through React Flow, which calls
|
|
1600
|
-
* `onEdgesDelete` as the Delete key does. The control leaves with its edge, so focus then moves
|
|
1601
|
-
* to the node the edge left, unless the removal was refused.
|
|
1602
|
-
*/
|
|
1603
|
-
/**
|
|
1604
|
-
* Renders the box the remove control is placed in, with the recipe's remove class.
|
|
1605
|
-
*/
|
|
1606
|
-
const Place = withContext("span", "remove");
|
|
1607
|
-
/**
|
|
1608
|
-
* Returns the name of an edge's end: the node's own name, else its id.
|
|
1609
|
-
*/
|
|
1610
|
-
function endOf(state, id) {
|
|
1611
|
-
return nameOf(state.nodeLookup.get(id)) ?? id;
|
|
1612
|
-
}
|
|
1613
|
-
/**
|
|
1614
|
-
* Focuses the node of an id inside a canvas, where it renders.
|
|
1615
|
-
*
|
|
1616
|
-
* @param canvas - React Flow's element, which contains the canvas's nodes.
|
|
1617
|
-
*/
|
|
1618
|
-
function focusNode(canvas, id) {
|
|
1619
|
-
[...canvas.querySelectorAll(".react-flow__node")].find((node) => node.dataset["id"] === id)?.focus();
|
|
1620
|
-
}
|
|
1621
|
-
/**
|
|
1622
|
-
* Removes an edge through React Flow, then focuses the node it left unless the removal was
|
|
1623
|
-
* refused.
|
|
1624
|
-
*
|
|
1625
|
-
* @param flow - React Flow's instance, which removes the edge and calls the canvas's handlers.
|
|
1626
|
-
* @param canvas - React Flow's element, which contains the canvas's nodes.
|
|
1627
|
-
* @param edge - The edge's id and the id of the node it leaves.
|
|
1628
|
-
*/
|
|
1629
|
-
async function removed(flow, canvas, edge) {
|
|
1630
|
-
const { deletedEdges } = await flow.deleteElements({ edges: [{ id: edge.id }] });
|
|
1631
|
-
if (deletedEdges.length > 0) focusNode(canvas, edge.source);
|
|
1632
|
-
}
|
|
1633
|
-
/**
|
|
1634
|
-
* Renders the edge's path, and its remove control while the canvas takes edits.
|
|
1635
|
-
*
|
|
1636
|
-
* @param props - React Flow's edge props: its ends, its sides and its data among them.
|
|
1637
|
-
*/
|
|
1638
|
-
function Edge$1(props) {
|
|
1639
|
-
const $ = c(29);
|
|
1640
|
-
let t0;
|
|
1641
|
-
if ($[0] !== props) {
|
|
1642
|
-
t0 = getBezierPath(props);
|
|
1643
|
-
$[0] = props;
|
|
1644
|
-
$[1] = t0;
|
|
1645
|
-
} else t0 = $[1];
|
|
1646
|
-
const [path, labelX, labelY] = t0;
|
|
1647
|
-
const flow = useReactFlow();
|
|
1648
|
-
const canvas = useStore(_temp$2);
|
|
1649
|
-
let t1;
|
|
1650
|
-
if ($[2] !== props.source) {
|
|
1651
|
-
t1 = (state_0) => endOf(state_0, props.source);
|
|
1652
|
-
$[2] = props.source;
|
|
1653
|
-
$[3] = t1;
|
|
1654
|
-
} else t1 = $[3];
|
|
1655
|
-
const source = useStore(t1);
|
|
1656
|
-
let t2;
|
|
1657
|
-
if ($[4] !== props.target) {
|
|
1658
|
-
t2 = (state_1) => endOf(state_1, props.target);
|
|
1659
|
-
$[4] = props.target;
|
|
1660
|
-
$[5] = t2;
|
|
1661
|
-
} else t2 = $[5];
|
|
1662
|
-
const target = useStore(t2);
|
|
1663
|
-
const { editable, glyph, name } = use(RemovalContext);
|
|
1664
|
-
const t3 = `${String(labelX)}px`;
|
|
1665
|
-
const t4 = `${String(labelY)}px`;
|
|
1666
|
-
let t5;
|
|
1667
|
-
if ($[6] !== t3 || $[7] !== t4) {
|
|
1668
|
-
t5 = {
|
|
1669
|
-
"--graph-edge-x": t3,
|
|
1670
|
-
"--graph-edge-y": t4
|
|
1671
|
-
};
|
|
1672
|
-
$[6] = t3;
|
|
1673
|
-
$[7] = t4;
|
|
1674
|
-
$[8] = t5;
|
|
1675
|
-
} else t5 = $[8];
|
|
1676
|
-
const at = t5;
|
|
1677
|
-
const t6 = props.id;
|
|
1678
|
-
let t7;
|
|
1679
|
-
if ($[9] !== props.markerEnd || $[10] !== props.markerStart) {
|
|
1680
|
-
t7 = omitUndefined({
|
|
1681
|
-
markerEnd: props.markerEnd,
|
|
1682
|
-
markerStart: props.markerStart
|
|
1683
|
-
});
|
|
1684
|
-
$[9] = props.markerEnd;
|
|
1685
|
-
$[10] = props.markerStart;
|
|
1686
|
-
$[11] = t7;
|
|
1687
|
-
} else t7 = $[11];
|
|
1688
|
-
let t8;
|
|
1689
|
-
if ($[12] !== path || $[13] !== props.id || $[14] !== t7) {
|
|
1690
|
-
t8 = /* @__PURE__ */ jsx(BaseEdge, {
|
|
1691
|
-
id: t6,
|
|
1692
|
-
path,
|
|
1693
|
-
...t7
|
|
1694
|
-
});
|
|
1695
|
-
$[12] = path;
|
|
1696
|
-
$[13] = props.id;
|
|
1697
|
-
$[14] = t7;
|
|
1698
|
-
$[15] = t8;
|
|
1699
|
-
} else t8 = $[15];
|
|
1700
|
-
let t9;
|
|
1701
|
-
if ($[16] !== at || $[17] !== canvas || $[18] !== editable || $[19] !== flow || $[20] !== glyph || $[21] !== name || $[22] !== props || $[23] !== source || $[24] !== target) {
|
|
1702
|
-
t9 = editable && props.deletable !== false && glyph !== void 0 ? /* @__PURE__ */ jsx(EdgeLabelRenderer, { children: /* @__PURE__ */ jsx(Place, {
|
|
1703
|
-
className: "nodrag nopan",
|
|
1704
|
-
style: at,
|
|
1705
|
-
children: /* @__PURE__ */ jsx(IconButton, {
|
|
1706
|
-
"aria-label": props.data?.removeLabel ?? name({
|
|
1707
|
-
source,
|
|
1708
|
-
target
|
|
1709
|
-
}),
|
|
1710
|
-
onClick: () => {
|
|
1711
|
-
removed(flow, canvas, props);
|
|
1712
|
-
},
|
|
1713
|
-
size: "xs",
|
|
1714
|
-
variant: "outline",
|
|
1715
|
-
children: glyph
|
|
1716
|
-
})
|
|
1717
|
-
}) }) : null;
|
|
1718
|
-
$[16] = at;
|
|
1719
|
-
$[17] = canvas;
|
|
1720
|
-
$[18] = editable;
|
|
1721
|
-
$[19] = flow;
|
|
1722
|
-
$[20] = glyph;
|
|
1723
|
-
$[21] = name;
|
|
1724
|
-
$[22] = props;
|
|
1725
|
-
$[23] = source;
|
|
1726
|
-
$[24] = target;
|
|
1727
|
-
$[25] = t9;
|
|
1728
|
-
} else t9 = $[25];
|
|
1729
|
-
let t10;
|
|
1730
|
-
if ($[26] !== t8 || $[27] !== t9) {
|
|
1731
|
-
t10 = /* @__PURE__ */ jsxs(Fragment, { children: [t8, t9] });
|
|
1732
|
-
$[26] = t8;
|
|
1733
|
-
$[27] = t9;
|
|
1734
|
-
$[28] = t10;
|
|
1735
|
-
} else t10 = $[28];
|
|
1736
|
-
return t10;
|
|
1737
|
-
}
|
|
1738
|
-
function _temp$2(state) {
|
|
1739
|
-
return state.domNode;
|
|
1740
|
-
}
|
|
1741
|
-
//#endregion
|
|
1742
|
-
//#region src/graph/label-node.tsx
|
|
1743
|
-
/**
|
|
1744
|
-
* Port edges enter, unless the data states the node's inputs.
|
|
1745
|
-
*/
|
|
1746
|
-
const INPUTS = [{
|
|
1747
|
-
id: "in",
|
|
1748
|
-
label: "In"
|
|
1749
|
-
}];
|
|
1750
|
-
/**
|
|
1751
|
-
* Port edges leave, unless the data states the node's outputs.
|
|
1752
|
-
*/
|
|
1753
|
-
const OUTPUTS = [{
|
|
1754
|
-
id: "out",
|
|
1755
|
-
label: "Out"
|
|
1756
|
-
}];
|
|
1757
|
-
/**
|
|
1758
|
-
* Renders the card of a built-in node from its data, with the ports its type has.
|
|
1759
|
-
*
|
|
1760
|
-
* @param props - React Flow's node props: its data and its type among them.
|
|
1761
|
-
*/
|
|
1762
|
-
function LabelNode(t0) {
|
|
1763
|
-
const $ = c(18);
|
|
1764
|
-
const { data, type } = t0;
|
|
1765
|
-
let t1;
|
|
1766
|
-
if ($[0] !== data.inputs || $[1] !== type) {
|
|
1767
|
-
t1 = data.inputs ?? (type === "input" ? [] : INPUTS);
|
|
1768
|
-
$[0] = data.inputs;
|
|
1769
|
-
$[1] = type;
|
|
1770
|
-
$[2] = t1;
|
|
1771
|
-
} else t1 = $[2];
|
|
1772
|
-
const inputs = t1;
|
|
1773
|
-
let t2;
|
|
1774
|
-
if ($[3] !== data.outputs || $[4] !== type) {
|
|
1775
|
-
t2 = data.outputs ?? (type === "output" ? [] : OUTPUTS);
|
|
1776
|
-
$[3] = data.outputs;
|
|
1777
|
-
$[4] = type;
|
|
1778
|
-
$[5] = t2;
|
|
1779
|
-
} else t2 = $[5];
|
|
1780
|
-
const outputs = t2;
|
|
1781
|
-
const t3 = data.label;
|
|
1782
|
-
let t4;
|
|
1783
|
-
if ($[6] !== data.icon || $[7] !== data.invalid || $[8] !== data.problem || $[9] !== data.status || $[10] !== data.subtitle) {
|
|
1784
|
-
t4 = omitUndefined({
|
|
1785
|
-
icon: data.icon,
|
|
1786
|
-
invalid: data.invalid,
|
|
1787
|
-
problem: data.problem,
|
|
1788
|
-
status: data.status,
|
|
1789
|
-
subtitle: data.subtitle
|
|
1790
|
-
});
|
|
1791
|
-
$[6] = data.icon;
|
|
1792
|
-
$[7] = data.invalid;
|
|
1793
|
-
$[8] = data.problem;
|
|
1794
|
-
$[9] = data.status;
|
|
1795
|
-
$[10] = data.subtitle;
|
|
1796
|
-
$[11] = t4;
|
|
1797
|
-
} else t4 = $[11];
|
|
1798
|
-
let t5;
|
|
1799
|
-
if ($[12] !== data.detail || $[13] !== data.label || $[14] !== inputs || $[15] !== outputs || $[16] !== t4) {
|
|
1800
|
-
t5 = /* @__PURE__ */ jsx(Node$1, {
|
|
1801
|
-
inputs,
|
|
1802
|
-
outputs,
|
|
1803
|
-
title: t3,
|
|
1804
|
-
...t4,
|
|
1805
|
-
children: data.detail
|
|
1806
|
-
});
|
|
1807
|
-
$[12] = data.detail;
|
|
1808
|
-
$[13] = data.label;
|
|
1809
|
-
$[14] = inputs;
|
|
1810
|
-
$[15] = outputs;
|
|
1811
|
-
$[16] = t4;
|
|
1812
|
-
$[17] = t5;
|
|
1813
|
-
} else t5 = $[17];
|
|
1814
|
-
return t5;
|
|
1815
|
-
}
|
|
1816
|
-
//#endregion
|
|
1817
|
-
//#region src/graph/types.ts
|
|
1818
|
-
/**
|
|
1819
|
-
* Maps React Flow's `default`, `input` and `output` node types to the kit's node.
|
|
1820
|
-
*/
|
|
1821
|
-
const NODE_TYPES$2 = {
|
|
1822
|
-
default: LabelNode,
|
|
1823
|
-
input: LabelNode,
|
|
1824
|
-
output: LabelNode
|
|
1825
|
-
};
|
|
1826
|
-
/**
|
|
1827
|
-
* Maps React Flow's `default` edge type to the kit's edge.
|
|
1828
|
-
*/
|
|
1829
|
-
const EDGE_TYPES = { default: Edge$1 };
|
|
1830
|
-
//#endregion
|
|
1831
|
-
//#region src/graph/settings.ts
|
|
1832
|
-
/**
|
|
1833
|
-
* React Flow's switches for a canvas that is read and not edited.
|
|
1834
|
-
*/
|
|
1835
|
-
const READ_ONLY = {
|
|
1836
|
-
deleteKeyCode: null,
|
|
1837
|
-
disableKeyboardA11y: true,
|
|
1838
|
-
edgesFocusable: false,
|
|
1839
|
-
elementsSelectable: false,
|
|
1840
|
-
nodesConnectable: false,
|
|
1841
|
-
nodesDraggable: false
|
|
1842
|
-
};
|
|
1843
|
-
/**
|
|
1844
|
-
* React Flow's switches for a canvas that takes edits: both keys that remove the selection.
|
|
1845
|
-
*/
|
|
1846
|
-
const EDITABLE = { deleteKeyCode: ["Backspace", "Delete"] };
|
|
1847
|
-
/**
|
|
1848
|
-
* Returns the props a canvas gives React Flow before the caller's own, for its mode and its
|
|
1849
|
-
* words.
|
|
1850
|
-
*
|
|
1851
|
-
* @param readOnly - Whether the canvas is read and not edited.
|
|
1852
|
-
* @param words - The words a screen reader hears.
|
|
1853
|
-
*/
|
|
1854
|
-
function settingsOf(readOnly, words) {
|
|
1855
|
-
return {
|
|
1856
|
-
ariaLabelConfig: ariaWordsOf(omitUndefined(words), !readOnly),
|
|
1857
|
-
attributionPosition: "bottom-left",
|
|
1858
|
-
edgeTypes: EDGE_TYPES,
|
|
1859
|
-
fitView: true,
|
|
1860
|
-
fitViewOptions: FIT,
|
|
1861
|
-
minZoom: MIN_ZOOM,
|
|
1862
|
-
nodeTypes: NODE_TYPES$2,
|
|
1863
|
-
...readOnly ? READ_ONLY : EDITABLE
|
|
1864
|
-
};
|
|
1865
|
-
}
|
|
1866
|
-
//#endregion
|
|
1867
|
-
//#region src/graph/canvas.tsx
|
|
1868
|
-
/**
|
|
1869
|
-
* Renders the canvas's box with the recipe's canvas class.
|
|
1870
|
-
*/
|
|
1871
|
-
const Box$2 = withContext("div", "canvas");
|
|
1872
|
-
/**
|
|
1873
|
-
* Renders React Flow in the canvas's box with the kit's types, words, names, switches and edits.
|
|
1874
|
-
*
|
|
1875
|
-
* @typeParam N - One node of the graph.
|
|
1876
|
-
* @typeParam E - One edge of the graph.
|
|
1877
|
-
* @param props - The graph, its name, its mode, its words, its edits, and React Flow's props.
|
|
1878
|
-
*/
|
|
1879
|
-
function Canvas(props) {
|
|
1880
|
-
const $ = c(57);
|
|
1881
|
-
let addedLabel;
|
|
1882
|
-
let changedLabel;
|
|
1883
|
-
let changes;
|
|
1884
|
-
let children;
|
|
1885
|
-
let edgeDescription;
|
|
1886
|
-
let edges;
|
|
1887
|
-
let flow;
|
|
1888
|
-
let label;
|
|
1889
|
-
let moveAnnouncement;
|
|
1890
|
-
let nodeDescription;
|
|
1891
|
-
let nodes;
|
|
1892
|
-
let onDropItem;
|
|
1893
|
-
let onGraphChange;
|
|
1894
|
-
let removeGlyph;
|
|
1895
|
-
let removedLabel;
|
|
1896
|
-
let t0;
|
|
1897
|
-
let t1;
|
|
1898
|
-
let t2;
|
|
1899
|
-
let t3;
|
|
1900
|
-
if ($[0] !== props) {
|
|
1901
|
-
({addedLabel, background: t0, changedLabel, changes, children, edgeDescription, edgeName: t1, edges, label, moveAnnouncement, nodeDescription, nodes, onDropItem, onGraphChange, readOnly: t2, removedLabel, removeGlyph, removeName: t3, ...flow} = props);
|
|
1902
|
-
$[0] = props;
|
|
1903
|
-
$[1] = addedLabel;
|
|
1904
|
-
$[2] = changedLabel;
|
|
1905
|
-
$[3] = changes;
|
|
1906
|
-
$[4] = children;
|
|
1907
|
-
$[5] = edgeDescription;
|
|
1908
|
-
$[6] = edges;
|
|
1909
|
-
$[7] = flow;
|
|
1910
|
-
$[8] = label;
|
|
1911
|
-
$[9] = moveAnnouncement;
|
|
1912
|
-
$[10] = nodeDescription;
|
|
1913
|
-
$[11] = nodes;
|
|
1914
|
-
$[12] = onDropItem;
|
|
1915
|
-
$[13] = onGraphChange;
|
|
1916
|
-
$[14] = removeGlyph;
|
|
1917
|
-
$[15] = removedLabel;
|
|
1918
|
-
$[16] = t0;
|
|
1919
|
-
$[17] = t1;
|
|
1920
|
-
$[18] = t2;
|
|
1921
|
-
$[19] = t3;
|
|
1922
|
-
} else {
|
|
1923
|
-
addedLabel = $[1];
|
|
1924
|
-
changedLabel = $[2];
|
|
1925
|
-
changes = $[3];
|
|
1926
|
-
children = $[4];
|
|
1927
|
-
edgeDescription = $[5];
|
|
1928
|
-
edges = $[6];
|
|
1929
|
-
flow = $[7];
|
|
1930
|
-
label = $[8];
|
|
1931
|
-
moveAnnouncement = $[9];
|
|
1932
|
-
nodeDescription = $[10];
|
|
1933
|
-
nodes = $[11];
|
|
1934
|
-
onDropItem = $[12];
|
|
1935
|
-
onGraphChange = $[13];
|
|
1936
|
-
removeGlyph = $[14];
|
|
1937
|
-
removedLabel = $[15];
|
|
1938
|
-
t0 = $[16];
|
|
1939
|
-
t1 = $[17];
|
|
1940
|
-
t2 = $[18];
|
|
1941
|
-
t3 = $[19];
|
|
1942
|
-
}
|
|
1943
|
-
const background = t0 === void 0 ? true : t0;
|
|
1944
|
-
const edgeName = t1 === void 0 ? edgeNameOf : t1;
|
|
1945
|
-
const readOnly = t2 === void 0 ? false : t2;
|
|
1946
|
-
const removeName = t3 === void 0 ? removeNameOf : t3;
|
|
1947
|
-
const diffed = diffedOf(changes, {
|
|
1948
|
-
addedLabel,
|
|
1949
|
-
changedLabel,
|
|
1950
|
-
removedLabel
|
|
1951
|
-
});
|
|
1952
|
-
let t4;
|
|
1953
|
-
if ($[20] !== edges || $[21] !== flow || $[22] !== nodes || $[23] !== onDropItem || $[24] !== onGraphChange) {
|
|
1954
|
-
t4 = {
|
|
1955
|
-
...flow,
|
|
1956
|
-
edges,
|
|
1957
|
-
nodes,
|
|
1958
|
-
onDropItem,
|
|
1959
|
-
onGraphChange
|
|
1960
|
-
};
|
|
1961
|
-
$[20] = edges;
|
|
1962
|
-
$[21] = flow;
|
|
1963
|
-
$[22] = nodes;
|
|
1964
|
-
$[23] = onDropItem;
|
|
1965
|
-
$[24] = onGraphChange;
|
|
1966
|
-
$[25] = t4;
|
|
1967
|
-
} else t4 = $[25];
|
|
1968
|
-
const edits = useEdits(t4);
|
|
1969
|
-
const T0 = RemovalContext;
|
|
1970
|
-
const t5 = !readOnly;
|
|
1971
|
-
let t6;
|
|
1972
|
-
if ($[26] !== removeGlyph || $[27] !== removeName || $[28] !== t5) {
|
|
1973
|
-
t6 = {
|
|
1974
|
-
editable: t5,
|
|
1975
|
-
glyph: removeGlyph,
|
|
1976
|
-
name: removeName
|
|
1977
|
-
};
|
|
1978
|
-
$[26] = removeGlyph;
|
|
1979
|
-
$[27] = removeName;
|
|
1980
|
-
$[28] = t5;
|
|
1981
|
-
$[29] = t6;
|
|
1982
|
-
} else t6 = $[29];
|
|
1983
|
-
const T1 = DiffContext;
|
|
1984
|
-
const T2 = Box$2;
|
|
1985
|
-
const T3 = ReactFlow;
|
|
1986
|
-
let t7;
|
|
1987
|
-
if ($[30] !== edgeDescription || $[31] !== moveAnnouncement || $[32] !== nodeDescription || $[33] !== readOnly) {
|
|
1988
|
-
t7 = settingsOf(readOnly, {
|
|
1989
|
-
edgeDescription,
|
|
1990
|
-
moveAnnouncement,
|
|
1991
|
-
nodeDescription
|
|
1992
|
-
});
|
|
1993
|
-
$[30] = edgeDescription;
|
|
1994
|
-
$[31] = moveAnnouncement;
|
|
1995
|
-
$[32] = nodeDescription;
|
|
1996
|
-
$[33] = readOnly;
|
|
1997
|
-
$[34] = t7;
|
|
1998
|
-
} else t7 = $[34];
|
|
1999
|
-
const t8 = namedGraph({
|
|
2000
|
-
edges,
|
|
2001
|
-
nodes
|
|
2002
|
-
}, edgeName, diffed);
|
|
2003
|
-
let t9;
|
|
2004
|
-
if ($[35] !== background) {
|
|
2005
|
-
t9 = background ? /* @__PURE__ */ jsx(Background, {}) : null;
|
|
2006
|
-
$[35] = background;
|
|
2007
|
-
$[36] = t9;
|
|
2008
|
-
} else t9 = $[36];
|
|
2009
|
-
let t10;
|
|
2010
|
-
if ($[37] !== T3 || $[38] !== children || $[39] !== edits || $[40] !== flow || $[41] !== label || $[42] !== t7 || $[43] !== t8 || $[44] !== t9) {
|
|
2011
|
-
t10 = /* @__PURE__ */ jsxs(T3, {
|
|
2012
|
-
"aria-label": label,
|
|
2013
|
-
...t7,
|
|
2014
|
-
...flow,
|
|
2015
|
-
...edits,
|
|
2016
|
-
...t8,
|
|
2017
|
-
children: [t9, children]
|
|
2018
|
-
});
|
|
2019
|
-
$[37] = T3;
|
|
2020
|
-
$[38] = children;
|
|
2021
|
-
$[39] = edits;
|
|
2022
|
-
$[40] = flow;
|
|
2023
|
-
$[41] = label;
|
|
2024
|
-
$[42] = t7;
|
|
2025
|
-
$[43] = t8;
|
|
2026
|
-
$[44] = t9;
|
|
2027
|
-
$[45] = t10;
|
|
2028
|
-
} else t10 = $[45];
|
|
2029
|
-
let t11;
|
|
2030
|
-
if ($[46] !== T2 || $[47] !== t10) {
|
|
2031
|
-
t11 = /* @__PURE__ */ jsx(T2, { children: t10 });
|
|
2032
|
-
$[46] = T2;
|
|
2033
|
-
$[47] = t10;
|
|
2034
|
-
$[48] = t11;
|
|
2035
|
-
} else t11 = $[48];
|
|
2036
|
-
let t12;
|
|
2037
|
-
if ($[49] !== T1 || $[50] !== diffed || $[51] !== t11) {
|
|
2038
|
-
t12 = /* @__PURE__ */ jsx(T1, {
|
|
2039
|
-
value: diffed,
|
|
2040
|
-
children: t11
|
|
2041
|
-
});
|
|
2042
|
-
$[49] = T1;
|
|
2043
|
-
$[50] = diffed;
|
|
2044
|
-
$[51] = t11;
|
|
2045
|
-
$[52] = t12;
|
|
2046
|
-
} else t12 = $[52];
|
|
2047
|
-
let t13;
|
|
2048
|
-
if ($[53] !== T0 || $[54] !== t12 || $[55] !== t6) {
|
|
2049
|
-
t13 = /* @__PURE__ */ jsx(T0, {
|
|
2050
|
-
value: t6,
|
|
2051
|
-
children: t12
|
|
2052
|
-
});
|
|
2053
|
-
$[53] = T0;
|
|
2054
|
-
$[54] = t12;
|
|
2055
|
-
$[55] = t6;
|
|
2056
|
-
$[56] = t13;
|
|
2057
|
-
} else t13 = $[56];
|
|
2058
|
-
return t13;
|
|
2059
|
-
}
|
|
2060
|
-
//#endregion
|
|
2061
|
-
//#region src/graph/placed.ts
|
|
2062
|
-
/**
|
|
2063
|
-
* Fits a preset's view to its layout and reports whether the canvas is still placing its nodes.
|
|
2064
|
-
*
|
|
2065
|
-
* @remarks
|
|
2066
|
-
* React Flow takes a controlled graph's nodes into its store in an effect of its own, one step
|
|
2067
|
-
* after the render that laid them out, so a canvas shown in that step renders the old positions.
|
|
2068
|
-
* The canvas places until every node has been measured and until React Flow's store has every
|
|
2069
|
-
* node where the layout put it, and the recipe hides the viewport while it places. The view fits
|
|
2070
|
-
* the layout once every node has been measured and whenever the layout moves a node after that,
|
|
2071
|
-
* in an effect that runs after React Flow's, so the bounds are the measured nodes at their new
|
|
2072
|
-
* positions. The fit is at most at the graph's own size and clear of the
|
|
2073
|
-
* canvas's edges, as `Graph.Canvas` fits it. A change that moves no node, such as a new focus,
|
|
2074
|
-
* keeps the view where a person panned and zoomed it. A node a person moved counts for neither:
|
|
2075
|
-
* a drag moves no view, and the step the store trails a drag by does not hide the viewport.
|
|
2076
|
-
*/
|
|
2077
|
-
/**
|
|
2078
|
-
* Ids of no node, for a graph whose nodes nobody moved.
|
|
2079
|
-
*/
|
|
2080
|
-
const NONE = /* @__PURE__ */ new Set();
|
|
2081
|
-
/**
|
|
2082
|
-
* Returns a string that differs whenever a node is added, removed or moved.
|
|
2083
|
-
*/
|
|
2084
|
-
function signatureOf(nodes) {
|
|
2085
|
-
return nodes.map(({ id, position }) => `${id}:${String(position.x)},${String(position.y)}`).join(";");
|
|
2086
|
-
}
|
|
2087
|
-
/**
|
|
2088
|
-
* Fits the view to the laid-out nodes whenever the layout moves them, and returns whether the
|
|
2089
|
-
* canvas is still placing them.
|
|
2090
|
-
*
|
|
2091
|
-
* @param nodes - The nodes where the layout put them.
|
|
2092
|
-
* @param complete - Whether every node of the graph has been measured and laid out.
|
|
2093
|
-
* @param moved - Ids of the nodes a person moved away from where the layout put them.
|
|
2094
|
-
*/
|
|
2095
|
-
function usePlaced(nodes, complete, t0) {
|
|
2096
|
-
const $ = c(16);
|
|
2097
|
-
const moved = t0 === void 0 ? NONE : t0;
|
|
2098
|
-
const flow = useReactFlow();
|
|
2099
|
-
const store = useStoreApi();
|
|
2100
|
-
let t1;
|
|
2101
|
-
if ($[0] !== nodes) {
|
|
2102
|
-
t1 = signatureOf(nodes);
|
|
2103
|
-
$[0] = nodes;
|
|
2104
|
-
$[1] = t1;
|
|
2105
|
-
} else t1 = $[1];
|
|
2106
|
-
const signature = t1;
|
|
2107
|
-
let t2;
|
|
2108
|
-
if ($[2] !== moved || $[3] !== nodes) {
|
|
2109
|
-
let t3;
|
|
2110
|
-
if ($[5] !== moved) {
|
|
2111
|
-
t3 = (node) => !moved.has(node.id);
|
|
2112
|
-
$[5] = moved;
|
|
2113
|
-
$[6] = t3;
|
|
2114
|
-
} else t3 = $[6];
|
|
2115
|
-
t2 = signatureOf(nodes.filter(t3));
|
|
2116
|
-
$[2] = moved;
|
|
2117
|
-
$[3] = nodes;
|
|
2118
|
-
$[4] = t2;
|
|
2119
|
-
} else t2 = $[4];
|
|
2120
|
-
const settled = t2;
|
|
2121
|
-
let t3;
|
|
2122
|
-
if ($[7] !== moved) {
|
|
2123
|
-
t3 = (state) => signatureOf(state.nodes.filter((node_0) => !moved.has(node_0.id)));
|
|
2124
|
-
$[7] = moved;
|
|
2125
|
-
$[8] = t3;
|
|
2126
|
-
} else t3 = $[8];
|
|
2127
|
-
const stored = useStore(t3);
|
|
2128
|
-
const fitted = useRef("");
|
|
2129
|
-
let t4;
|
|
2130
|
-
let t5;
|
|
2131
|
-
if ($[9] !== complete || $[10] !== flow || $[11] !== nodes || $[12] !== signature || $[13] !== store) {
|
|
2132
|
-
t4 = () => {
|
|
2133
|
-
if (!complete || fitted.current === signature) return;
|
|
2134
|
-
const { height, width } = store.getState();
|
|
2135
|
-
const bounds = flow.getNodesBounds([...nodes]);
|
|
2136
|
-
fitted.current = signature;
|
|
2137
|
-
flow.setViewport(getViewportForBounds(bounds, width, height, MIN_ZOOM, 1, PADDING));
|
|
2138
|
-
};
|
|
2139
|
-
t5 = [
|
|
2140
|
-
complete,
|
|
2141
|
-
flow,
|
|
2142
|
-
nodes,
|
|
2143
|
-
signature,
|
|
2144
|
-
store
|
|
2145
|
-
];
|
|
2146
|
-
$[9] = complete;
|
|
2147
|
-
$[10] = flow;
|
|
2148
|
-
$[11] = nodes;
|
|
2149
|
-
$[12] = signature;
|
|
2150
|
-
$[13] = store;
|
|
2151
|
-
$[14] = t4;
|
|
2152
|
-
$[15] = t5;
|
|
2153
|
-
} else {
|
|
2154
|
-
t4 = $[14];
|
|
2155
|
-
t5 = $[15];
|
|
2156
|
-
}
|
|
2157
|
-
useEffect(t4, t5);
|
|
2158
|
-
return !complete || stored !== settled;
|
|
2159
|
-
}
|
|
2160
|
-
//#endregion
|
|
2161
|
-
//#region src/graph/summary.ts
|
|
2162
|
-
/**
|
|
2163
|
-
* Renders the `div` with the recipe's summary class.
|
|
2164
|
-
*/
|
|
2165
|
-
const Summary = withContext("div", "summary");
|
|
2166
|
-
//#endregion
|
|
2167
|
-
//#region src/graph/readout.tsx
|
|
2168
|
-
/**
|
|
2169
|
-
* Renders the words, the clear control and the elements at the row's end in the graph's summary
|
|
2170
|
-
* row.
|
|
2171
|
-
*
|
|
2172
|
-
* @param props - The words, the focus, the clear handler and the elements at the row's end.
|
|
2173
|
-
*/
|
|
2174
|
-
function Readout(t0) {
|
|
2175
|
-
const $ = c(13);
|
|
2176
|
-
const { children, clearLabel, focused, onClear, text } = t0;
|
|
2177
|
-
let t1;
|
|
2178
|
-
if ($[0] !== text) {
|
|
2179
|
-
t1 = /* @__PURE__ */ jsx("output", { children: text });
|
|
2180
|
-
$[0] = text;
|
|
2181
|
-
$[1] = t1;
|
|
2182
|
-
} else t1 = $[1];
|
|
2183
|
-
const t2 = focused ? void 0 : true;
|
|
2184
|
-
let t3;
|
|
2185
|
-
if ($[2] !== focused || $[3] !== onClear) {
|
|
2186
|
-
t3 = () => {
|
|
2187
|
-
if (focused) onClear();
|
|
2188
|
-
};
|
|
2189
|
-
$[2] = focused;
|
|
2190
|
-
$[3] = onClear;
|
|
2191
|
-
$[4] = t3;
|
|
2192
|
-
} else t3 = $[4];
|
|
2193
|
-
let t4;
|
|
2194
|
-
if ($[5] !== clearLabel || $[6] !== t2 || $[7] !== t3) {
|
|
2195
|
-
t4 = /* @__PURE__ */ jsx(Button, {
|
|
2196
|
-
"aria-disabled": t2,
|
|
2197
|
-
onClick: t3,
|
|
2198
|
-
size: "sm",
|
|
2199
|
-
variant: "outline",
|
|
2200
|
-
children: clearLabel
|
|
2201
|
-
});
|
|
2202
|
-
$[5] = clearLabel;
|
|
2203
|
-
$[6] = t2;
|
|
2204
|
-
$[7] = t3;
|
|
2205
|
-
$[8] = t4;
|
|
2206
|
-
} else t4 = $[8];
|
|
2207
|
-
let t5;
|
|
2208
|
-
if ($[9] !== children || $[10] !== t1 || $[11] !== t4) {
|
|
2209
|
-
t5 = /* @__PURE__ */ jsxs(Summary, { children: [
|
|
2210
|
-
t1,
|
|
2211
|
-
t4,
|
|
2212
|
-
children
|
|
2213
|
-
] });
|
|
2214
|
-
$[9] = children;
|
|
2215
|
-
$[10] = t1;
|
|
2216
|
-
$[11] = t4;
|
|
2217
|
-
$[12] = t5;
|
|
2218
|
-
} else t5 = $[12];
|
|
2219
|
-
return t5;
|
|
2220
|
-
}
|
|
2221
|
-
//#endregion
|
|
2222
|
-
//#region src/directed-graph/traced.tsx
|
|
2223
|
-
/**
|
|
2224
|
-
* Maps the directed graph's node type to its card.
|
|
2225
|
-
*/
|
|
2226
|
-
const NODE_TYPES$1 = { directed: Card };
|
|
2227
|
-
/**
|
|
2228
|
-
* Renders the canvas over the graph's view and the readout below it.
|
|
2229
|
-
*
|
|
2230
|
-
* @param props - The graph, its focus and branches, its words and its direction.
|
|
2231
|
-
*/
|
|
2232
|
-
function Traced(props) {
|
|
2233
|
-
const $ = c(20);
|
|
2234
|
-
const { clear, onNodesChange, toggle, view } = useTraced(props);
|
|
2235
|
-
const placing = usePlaced(view.nodes, view.complete);
|
|
2236
|
-
const { counts } = view;
|
|
2237
|
-
const { words } = props;
|
|
2238
|
-
const t0 = placing ? "" : void 0;
|
|
2239
|
-
let t1;
|
|
2240
|
-
if ($[0] !== onNodesChange || $[1] !== props.label || $[2] !== t0 || $[3] !== view.edges || $[4] !== view.nodes || $[5] !== words.nodeDescription) {
|
|
2241
|
-
t1 = /* @__PURE__ */ jsx(Canvas, {
|
|
2242
|
-
"data-placing": t0,
|
|
2243
|
-
edges: view.edges,
|
|
2244
|
-
edgesFocusable: false,
|
|
2245
|
-
label: props.label,
|
|
2246
|
-
multiSelectionKeyCode: null,
|
|
2247
|
-
nodeDescription: words.nodeDescription,
|
|
2248
|
-
nodes: view.nodes,
|
|
2249
|
-
nodesConnectable: false,
|
|
2250
|
-
nodesDraggable: false,
|
|
2251
|
-
nodeTypes: NODE_TYPES$1,
|
|
2252
|
-
onNodesChange,
|
|
2253
|
-
selectionKeyCode: null
|
|
2254
|
-
});
|
|
2255
|
-
$[0] = onNodesChange;
|
|
2256
|
-
$[1] = props.label;
|
|
2257
|
-
$[2] = t0;
|
|
2258
|
-
$[3] = view.edges;
|
|
2259
|
-
$[4] = view.nodes;
|
|
2260
|
-
$[5] = words.nodeDescription;
|
|
2261
|
-
$[6] = t1;
|
|
2262
|
-
} else t1 = $[6];
|
|
2263
|
-
const t2 = counts !== void 0;
|
|
2264
|
-
let t3;
|
|
2265
|
-
if ($[7] !== counts || $[8] !== words) {
|
|
2266
|
-
t3 = counts === void 0 ? words.promptLabel : words.summary(counts);
|
|
2267
|
-
$[7] = counts;
|
|
2268
|
-
$[8] = words;
|
|
2269
|
-
$[9] = t3;
|
|
2270
|
-
} else t3 = $[9];
|
|
2271
|
-
let t4;
|
|
2272
|
-
if ($[10] !== clear || $[11] !== props.overview || $[12] !== t2 || $[13] !== t3 || $[14] !== words.clearLabel) {
|
|
2273
|
-
t4 = /* @__PURE__ */ jsx(Readout, {
|
|
2274
|
-
clearLabel: words.clearLabel,
|
|
2275
|
-
focused: t2,
|
|
2276
|
-
onClear: clear,
|
|
2277
|
-
text: t3,
|
|
2278
|
-
children: props.overview
|
|
2279
|
-
});
|
|
2280
|
-
$[10] = clear;
|
|
2281
|
-
$[11] = props.overview;
|
|
2282
|
-
$[12] = t2;
|
|
2283
|
-
$[13] = t3;
|
|
2284
|
-
$[14] = words.clearLabel;
|
|
2285
|
-
$[15] = t4;
|
|
2286
|
-
} else t4 = $[15];
|
|
2287
|
-
let t5;
|
|
2288
|
-
if ($[16] !== t1 || $[17] !== t4 || $[18] !== toggle) {
|
|
2289
|
-
t5 = /* @__PURE__ */ jsxs(BranchProvider, {
|
|
2290
|
-
value: toggle,
|
|
2291
|
-
children: [t1, t4]
|
|
2292
|
-
});
|
|
2293
|
-
$[16] = t1;
|
|
2294
|
-
$[17] = t4;
|
|
2295
|
-
$[18] = toggle;
|
|
2296
|
-
$[19] = t5;
|
|
2297
|
-
} else t5 = $[19];
|
|
2298
|
-
return t5;
|
|
2299
|
-
}
|
|
2300
|
-
//#endregion
|
|
2301
|
-
//#region src/directed-graph/words.ts
|
|
2302
|
-
/**
|
|
2303
|
-
* Writes the words a directed graph renders, from the caller's words and the English defaults.
|
|
2304
|
-
*
|
|
2305
|
-
* @remarks
|
|
2306
|
-
* A caller that states a word replaces the default and keeps every other default. The counts of
|
|
2307
|
-
* a branch control are the nodes the press hides or shows, so the words name no kind of node and
|
|
2308
|
-
* suit a lineage and an org chart alike.
|
|
2309
|
-
*/
|
|
2310
|
-
/**
|
|
2311
|
-
* Writes the words of a control that closes a branch, unless stated.
|
|
2312
|
-
*/
|
|
2313
|
-
function hidden(count) {
|
|
2314
|
-
return `Hide ${String(count)}`;
|
|
2315
|
-
}
|
|
2316
|
-
/**
|
|
2317
|
-
* Writes the words of a control that opens a branch, unless stated.
|
|
2318
|
-
*/
|
|
2319
|
-
function shown(count) {
|
|
2320
|
-
return `Show ${String(count)}`;
|
|
2321
|
-
}
|
|
2322
|
-
/**
|
|
2323
|
-
* Writes the summary of a trace, unless stated.
|
|
2324
|
-
*/
|
|
2325
|
-
function summarised({ downstream, name, upstream }) {
|
|
2326
|
-
return `${name}: ${String(upstream)} upstream, ${String(downstream)} downstream`;
|
|
2327
|
-
}
|
|
2328
|
-
/**
|
|
2329
|
-
* English words of a directed graph.
|
|
2330
|
-
*/
|
|
2331
|
-
const WORDS$1 = {
|
|
2332
|
-
clearLabel: "Clear focus",
|
|
2333
|
-
collapseLabel: hidden,
|
|
2334
|
-
downstreamLabel: "Downstream",
|
|
2335
|
-
edgeName: edgeNameOf,
|
|
2336
|
-
emptyLabel: "No nodes to show.",
|
|
2337
|
-
expandLabel: shown,
|
|
2338
|
-
focusLabel: "Focus",
|
|
2339
|
-
nodeDescription: "Press Enter or Space to trace the node, and Escape to clear the trace.",
|
|
2340
|
-
promptLabel: "Select a node to trace what feeds it and what it feeds.",
|
|
2341
|
-
summary: summarised,
|
|
2342
|
-
upstreamLabel: "Upstream"
|
|
2343
|
-
};
|
|
2344
|
-
/**
|
|
2345
|
-
* Returns the caller's words over the English defaults.
|
|
2346
|
-
*
|
|
2347
|
-
* @param words - The words the caller states.
|
|
2348
|
-
*/
|
|
2349
|
-
function wordsOf$1(words) {
|
|
2350
|
-
return {
|
|
2351
|
-
...WORDS$1,
|
|
2352
|
-
...omitUndefined(words)
|
|
2353
|
-
};
|
|
2354
|
-
}
|
|
2355
|
-
//#endregion
|
|
2356
|
-
//#region src/graph/caption.tsx
|
|
2357
|
-
/**
|
|
2358
|
-
* Renders the `figcaption` with the recipe's caption class.
|
|
2359
|
-
*/
|
|
2360
|
-
const Shown$1 = withContext("figcaption", "caption");
|
|
2361
|
-
/**
|
|
2362
|
-
* Renders the caption with the ID the root names the figure by.
|
|
2363
|
-
*
|
|
2364
|
-
* @param props - The props of the `figcaption`.
|
|
2365
|
-
*/
|
|
2366
|
-
function Caption(props) {
|
|
2367
|
-
const $ = c(3);
|
|
2368
|
-
useLabelled();
|
|
2369
|
-
const t0 = useCaptionId();
|
|
2370
|
-
let t1;
|
|
2371
|
-
if ($[0] !== props || $[1] !== t0) {
|
|
2372
|
-
t1 = /* @__PURE__ */ jsx(Shown$1, {
|
|
2373
|
-
id: t0,
|
|
2374
|
-
...props
|
|
2375
|
-
});
|
|
2376
|
-
$[0] = props;
|
|
2377
|
-
$[1] = t0;
|
|
2378
|
-
$[2] = t1;
|
|
2379
|
-
} else t1 = $[2];
|
|
2380
|
-
return t1;
|
|
2381
|
-
}
|
|
2382
|
-
//#endregion
|
|
2383
|
-
//#region src/graph/empty.ts
|
|
2384
|
-
/**
|
|
2385
|
-
* Renders the `div` with the recipe's empty class.
|
|
2386
|
-
*/
|
|
2387
|
-
const Empty = withContext("div", "empty");
|
|
2388
|
-
//#endregion
|
|
2389
|
-
//#region src/graph/root.tsx
|
|
2390
|
-
/**
|
|
2391
|
-
* Renders a graph's figure and the React Flow provider every part reads.
|
|
2392
|
-
*
|
|
2393
|
-
* @remarks
|
|
2394
|
-
* The element is a `figure`, named by its `Graph.Caption` through `aria-labelledby` while a
|
|
2395
|
-
* caption renders. The root renders React Flow's provider, so the controls and the overview map
|
|
2396
|
-
* read the canvas's store from anywhere inside the figure. `direction` tells every node which
|
|
2397
|
-
* sides its ports are on. The root takes the recipe's `ratio`, which sets the canvas's height.
|
|
2398
|
-
*/
|
|
2399
|
-
/**
|
|
2400
|
-
* Renders the `figure` that provides the recipe's variants.
|
|
2401
|
-
*/
|
|
2402
|
-
const Figure = withProvider("figure", "root");
|
|
2403
|
-
/**
|
|
2404
|
-
* Renders the figure inside React Flow's provider, named by its caption while one renders.
|
|
2405
|
-
*
|
|
2406
|
-
* @param props - The direction, the recipe's variants and the props of a `figure`.
|
|
2407
|
-
* @returns The `figure` element inside the providers.
|
|
2408
|
-
*/
|
|
2409
|
-
function Root(t0) {
|
|
2410
|
-
const $ = c(12);
|
|
2411
|
-
let props;
|
|
2412
|
-
let t1;
|
|
2413
|
-
if ($[0] !== t0) {
|
|
2414
|
-
({direction: t1, ...props} = t0);
|
|
2415
|
-
$[0] = t0;
|
|
2416
|
-
$[1] = props;
|
|
2417
|
-
$[2] = t1;
|
|
2418
|
-
} else {
|
|
2419
|
-
props = $[1];
|
|
2420
|
-
t1 = $[2];
|
|
2421
|
-
}
|
|
2422
|
-
const direction = t1 === void 0 ? "down" : t1;
|
|
2423
|
-
const captionId = useId();
|
|
2424
|
-
const [captioned, setCaptioned] = useState(false);
|
|
2425
|
-
const t2 = captioned ? captionId : void 0;
|
|
2426
|
-
let t3;
|
|
2427
|
-
if ($[3] !== props || $[4] !== t2) {
|
|
2428
|
-
t3 = /* @__PURE__ */ jsx(Figure, {
|
|
2429
|
-
"aria-labelledby": t2,
|
|
2430
|
-
...props
|
|
2431
|
-
});
|
|
2432
|
-
$[3] = props;
|
|
2433
|
-
$[4] = t2;
|
|
2434
|
-
$[5] = t3;
|
|
2435
|
-
} else t3 = $[5];
|
|
2436
|
-
let t4;
|
|
2437
|
-
if ($[6] !== captionId || $[7] !== t3) {
|
|
2438
|
-
t4 = /* @__PURE__ */ jsx(LabellingProvider, {
|
|
2439
|
-
value: setCaptioned,
|
|
2440
|
-
children: /* @__PURE__ */ jsx(CaptionProvider, {
|
|
2441
|
-
value: captionId,
|
|
2442
|
-
children: t3
|
|
2443
|
-
})
|
|
2444
|
-
});
|
|
2445
|
-
$[6] = captionId;
|
|
2446
|
-
$[7] = t3;
|
|
2447
|
-
$[8] = t4;
|
|
2448
|
-
} else t4 = $[8];
|
|
2449
|
-
let t5;
|
|
2450
|
-
if ($[9] !== direction || $[10] !== t4) {
|
|
2451
|
-
t5 = /* @__PURE__ */ jsx(ReactFlowProvider, { children: /* @__PURE__ */ jsx(DirectionContext, {
|
|
2452
|
-
value: direction,
|
|
2453
|
-
children: t4
|
|
2454
|
-
}) });
|
|
2455
|
-
$[9] = direction;
|
|
2456
|
-
$[10] = t4;
|
|
2457
|
-
$[11] = t5;
|
|
2458
|
-
} else t5 = $[11];
|
|
2459
|
-
return t5;
|
|
2460
|
-
}
|
|
2461
|
-
//#endregion
|
|
2462
|
-
//#region src/directed-graph/directed-graph.tsx
|
|
2463
|
-
/**
|
|
2464
|
-
* Splits a directed graph's props into its words, the canvas's props and the figure's props.
|
|
2465
|
-
*/
|
|
2466
|
-
function split$1(props) {
|
|
2467
|
-
const { clearLabel, collapsed, collapseLabel, collapsible, defaultCollapsed, defaultFocus, depth, downstreamLabel, edgeName, edges, emptyLabel, expandLabel, focus, focusLabel, label, nodeDescription, nodes, onCollapsedChange, onFocusChange, overview, promptLabel, summary, trace, upstreamLabel, ...rest } = props;
|
|
2468
|
-
const words = {
|
|
2469
|
-
clearLabel,
|
|
2470
|
-
collapseLabel,
|
|
2471
|
-
downstreamLabel,
|
|
2472
|
-
edgeName,
|
|
2473
|
-
emptyLabel,
|
|
2474
|
-
expandLabel
|
|
2475
|
-
};
|
|
2476
|
-
const more = {
|
|
2477
|
-
focusLabel,
|
|
2478
|
-
nodeDescription,
|
|
2479
|
-
promptLabel,
|
|
2480
|
-
summary,
|
|
2481
|
-
upstreamLabel
|
|
2482
|
-
};
|
|
2483
|
-
const state = {
|
|
2484
|
-
collapsed,
|
|
2485
|
-
collapsible,
|
|
2486
|
-
defaultCollapsed,
|
|
2487
|
-
defaultFocus,
|
|
2488
|
-
depth,
|
|
2489
|
-
focus,
|
|
2490
|
-
trace
|
|
2491
|
-
};
|
|
2492
|
-
const graph = {
|
|
2493
|
-
edges,
|
|
2494
|
-
label,
|
|
2495
|
-
nodes,
|
|
2496
|
-
onCollapsedChange,
|
|
2497
|
-
onFocusChange,
|
|
2498
|
-
overview
|
|
2499
|
-
};
|
|
2500
|
-
return [
|
|
2501
|
-
{
|
|
2502
|
-
...words,
|
|
2503
|
-
...more
|
|
2504
|
-
},
|
|
2505
|
-
{
|
|
2506
|
-
...state,
|
|
2507
|
-
...graph
|
|
2508
|
-
},
|
|
2509
|
-
rest
|
|
2510
|
-
];
|
|
2511
|
-
}
|
|
2512
|
-
/**
|
|
2513
|
-
* Renders the figure: the controls, the canvas, its readout with the overview, and the caption,
|
|
2514
|
-
* or the empty state while the graph has no node.
|
|
2515
|
-
*
|
|
2516
|
-
* @param props - The graph, its focus and branches, its parts, its words and the figure's props.
|
|
2517
|
-
*/
|
|
2518
|
-
function DirectedGraph(props) {
|
|
2519
|
-
const $ = c(21);
|
|
2520
|
-
let caption;
|
|
2521
|
-
let controls;
|
|
2522
|
-
let direction;
|
|
2523
|
-
let own;
|
|
2524
|
-
let root;
|
|
2525
|
-
let t0;
|
|
2526
|
-
if ($[0] !== props) {
|
|
2527
|
-
const [stated, t1, rest] = split$1(props);
|
|
2528
|
-
own = t1;
|
|
2529
|
-
const { caption: t2, controls: t3, direction: t4, ...t5 } = rest;
|
|
2530
|
-
caption = t2;
|
|
2531
|
-
controls = t3;
|
|
2532
|
-
root = t5;
|
|
2533
|
-
direction = t4 === void 0 ? "down" : t4;
|
|
2534
|
-
t0 = wordsOf$1(stated);
|
|
2535
|
-
$[0] = props;
|
|
2536
|
-
$[1] = caption;
|
|
2537
|
-
$[2] = controls;
|
|
2538
|
-
$[3] = direction;
|
|
2539
|
-
$[4] = own;
|
|
2540
|
-
$[5] = root;
|
|
2541
|
-
$[6] = t0;
|
|
2542
|
-
} else {
|
|
2543
|
-
caption = $[1];
|
|
2544
|
-
controls = $[2];
|
|
2545
|
-
direction = $[3];
|
|
2546
|
-
own = $[4];
|
|
2547
|
-
root = $[5];
|
|
2548
|
-
t0 = $[6];
|
|
2549
|
-
}
|
|
2550
|
-
const words = t0;
|
|
2551
|
-
let t1;
|
|
2552
|
-
if ($[7] !== direction || $[8] !== root) {
|
|
2553
|
-
t1 = {
|
|
2554
|
-
direction,
|
|
2555
|
-
...root
|
|
2556
|
-
};
|
|
2557
|
-
$[7] = direction;
|
|
2558
|
-
$[8] = root;
|
|
2559
|
-
$[9] = t1;
|
|
2560
|
-
} else t1 = $[9];
|
|
2561
|
-
const figure = t1;
|
|
2562
|
-
let t2;
|
|
2563
|
-
if ($[10] !== controls || $[11] !== direction || $[12] !== own || $[13] !== words) {
|
|
2564
|
-
t2 = own.nodes.length === 0 ? /* @__PURE__ */ jsx(Empty, { children: words.emptyLabel }) : /* @__PURE__ */ jsxs(Fragment, { children: [controls, /* @__PURE__ */ jsx(Traced, {
|
|
2565
|
-
direction,
|
|
2566
|
-
words,
|
|
2567
|
-
...own
|
|
2568
|
-
})] });
|
|
2569
|
-
$[10] = controls;
|
|
2570
|
-
$[11] = direction;
|
|
2571
|
-
$[12] = own;
|
|
2572
|
-
$[13] = words;
|
|
2573
|
-
$[14] = t2;
|
|
2574
|
-
} else t2 = $[14];
|
|
2575
|
-
let t3;
|
|
2576
|
-
if ($[15] !== caption) {
|
|
2577
|
-
t3 = caption === void 0 ? null : /* @__PURE__ */ jsx(Caption, { children: caption });
|
|
2578
|
-
$[15] = caption;
|
|
2579
|
-
$[16] = t3;
|
|
2580
|
-
} else t3 = $[16];
|
|
2581
|
-
let t4;
|
|
2582
|
-
if ($[17] !== figure || $[18] !== t2 || $[19] !== t3) {
|
|
2583
|
-
t4 = /* @__PURE__ */ jsxs(Root, {
|
|
2584
|
-
...figure,
|
|
2585
|
-
children: [t2, t3]
|
|
2586
|
-
});
|
|
2587
|
-
$[17] = figure;
|
|
2588
|
-
$[18] = t2;
|
|
2589
|
-
$[19] = t3;
|
|
2590
|
-
$[20] = t4;
|
|
2591
|
-
} else t4 = $[20];
|
|
2592
|
-
return t4;
|
|
2593
|
-
}
|
|
2594
|
-
//#endregion
|
|
2595
|
-
//#region src/directed-graph/tree.ts
|
|
2596
|
-
/**
|
|
2597
|
-
* Returns an edge from each item's parent to the item, for every item whose parent is in the list.
|
|
2598
|
-
*
|
|
2599
|
-
* @typeParam T - One item of the list.
|
|
2600
|
-
* @param items - The items, in any order.
|
|
2601
|
-
* @param parentOf - Returns an item's parent's id, or undefined for an item without one.
|
|
2602
|
-
*/
|
|
2603
|
-
function treeEdges(items, parentOf) {
|
|
2604
|
-
const ids = new Set(items.map((item) => item.id));
|
|
2605
|
-
return items.flatMap((item) => {
|
|
2606
|
-
const parent = parentOf(item);
|
|
2607
|
-
return parent !== void 0 && ids.has(parent) ? [{
|
|
2608
|
-
id: `${parent}-${item.id}`,
|
|
2609
|
-
source: parent,
|
|
2610
|
-
target: item.id
|
|
2611
|
-
}] : [];
|
|
2612
|
-
});
|
|
2613
|
-
}
|
|
2614
|
-
//#endregion
|
|
2615
|
-
//#region src/graph/control.tsx
|
|
2616
|
-
/**
|
|
2617
|
-
* Renders the control as a toolbar item that zooms or fits the view when pressed.
|
|
2618
|
-
*
|
|
2619
|
-
* @param props - The action, the name and the item's props.
|
|
2620
|
-
*/
|
|
2621
|
-
function Control(t0) {
|
|
2622
|
-
const $ = c(11);
|
|
2623
|
-
let action;
|
|
2624
|
-
let label;
|
|
2625
|
-
let props;
|
|
2626
|
-
if ($[0] !== t0) {
|
|
2627
|
-
({action, label, ...props} = t0);
|
|
2628
|
-
$[0] = t0;
|
|
2629
|
-
$[1] = action;
|
|
2630
|
-
$[2] = label;
|
|
2631
|
-
$[3] = props;
|
|
2632
|
-
} else {
|
|
2633
|
-
action = $[1];
|
|
2634
|
-
label = $[2];
|
|
2635
|
-
props = $[3];
|
|
2636
|
-
}
|
|
2637
|
-
const flow = useReactFlow();
|
|
2638
|
-
let t1;
|
|
2639
|
-
if ($[4] !== action || $[5] !== flow) {
|
|
2640
|
-
t1 = function act() {
|
|
2641
|
-
if (action === "fit") flow.fitView(FIT);
|
|
2642
|
-
else if (action === "zoomIn") flow.zoomIn();
|
|
2643
|
-
else flow.zoomOut();
|
|
2644
|
-
};
|
|
2645
|
-
$[4] = action;
|
|
2646
|
-
$[5] = flow;
|
|
2647
|
-
$[6] = t1;
|
|
2648
|
-
} else t1 = $[6];
|
|
2649
|
-
const act = t1;
|
|
2650
|
-
let t2;
|
|
2651
|
-
if ($[7] !== act || $[8] !== label || $[9] !== props) {
|
|
2652
|
-
t2 = /* @__PURE__ */ jsx(RovingFocus.Item, {
|
|
2653
|
-
"aria-label": label,
|
|
2654
|
-
as: IconButton,
|
|
2655
|
-
onClick: act,
|
|
2656
|
-
...props
|
|
2657
|
-
});
|
|
2658
|
-
$[7] = act;
|
|
2659
|
-
$[8] = label;
|
|
2660
|
-
$[9] = props;
|
|
2661
|
-
$[10] = t2;
|
|
2662
|
-
} else t2 = $[10];
|
|
2663
|
-
return t2;
|
|
2664
|
-
}
|
|
2665
|
-
//#endregion
|
|
2666
|
-
//#region src/graph/controls.tsx
|
|
2667
|
-
/**
|
|
2668
|
-
* Renders the roving focus group with the recipe's controls class.
|
|
2669
|
-
*/
|
|
2670
|
-
const Bar = withContext(RovingFocus.Root, "controls");
|
|
2671
|
-
/**
|
|
2672
|
-
* Renders the toolbar with small ghost buttons.
|
|
2673
|
-
*
|
|
2674
|
-
* @param props - The name and the group's props.
|
|
2675
|
-
*/
|
|
2676
|
-
function Controls(t0) {
|
|
2677
|
-
const $ = c(7);
|
|
2678
|
-
let props;
|
|
2679
|
-
let t1;
|
|
2680
|
-
if ($[0] !== t0) {
|
|
2681
|
-
({label: t1, ...props} = t0);
|
|
2682
|
-
$[0] = t0;
|
|
2683
|
-
$[1] = props;
|
|
2684
|
-
$[2] = t1;
|
|
2685
|
-
} else {
|
|
2686
|
-
props = $[1];
|
|
2687
|
-
t1 = $[2];
|
|
2688
|
-
}
|
|
2689
|
-
const label = t1 === void 0 ? "Canvas" : t1;
|
|
2690
|
-
let t2;
|
|
2691
|
-
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
|
2692
|
-
t2 = {
|
|
2693
|
-
size: "sm",
|
|
2694
|
-
variant: "ghost"
|
|
2695
|
-
};
|
|
2696
|
-
$[3] = t2;
|
|
2697
|
-
} else t2 = $[3];
|
|
2698
|
-
let t3;
|
|
2699
|
-
if ($[4] !== label || $[5] !== props) {
|
|
2700
|
-
t3 = /* @__PURE__ */ jsx(ButtonPropsProvider, {
|
|
2701
|
-
value: t2,
|
|
2702
|
-
children: /* @__PURE__ */ jsx(Bar, {
|
|
2703
|
-
"aria-label": label,
|
|
2704
|
-
className: "nokey",
|
|
2705
|
-
role: "toolbar",
|
|
2706
|
-
...props
|
|
2707
|
-
})
|
|
2708
|
-
});
|
|
2709
|
-
$[4] = label;
|
|
2710
|
-
$[5] = props;
|
|
2711
|
-
$[6] = t3;
|
|
2712
|
-
} else t3 = $[6];
|
|
2713
|
-
return t3;
|
|
2714
|
-
}
|
|
2715
|
-
//#endregion
|
|
2716
|
-
//#region src/graph/minimap.tsx
|
|
2717
|
-
/**
|
|
2718
|
-
* Renders the box the map is placed in, with the recipe's overview class.
|
|
2719
|
-
*/
|
|
2720
|
-
const Box$1 = withContext("div", "overview");
|
|
2721
|
-
/**
|
|
2722
|
-
* Renders React Flow's minimap in the overview box, named, pannable and zoomable.
|
|
2723
|
-
*
|
|
2724
|
-
* @param props - The name and the props of React Flow's minimap.
|
|
2725
|
-
*/
|
|
2726
|
-
function MiniMap$1(t0) {
|
|
2727
|
-
const $ = c(6);
|
|
2728
|
-
let props;
|
|
2729
|
-
let t1;
|
|
2730
|
-
if ($[0] !== t0) {
|
|
2731
|
-
({label: t1, ...props} = t0);
|
|
2732
|
-
$[0] = t0;
|
|
2733
|
-
$[1] = props;
|
|
2734
|
-
$[2] = t1;
|
|
2735
|
-
} else {
|
|
2736
|
-
props = $[1];
|
|
2737
|
-
t1 = $[2];
|
|
2738
|
-
}
|
|
2739
|
-
const label = t1 === void 0 ? "Overview" : t1;
|
|
2740
|
-
let t2;
|
|
2741
|
-
if ($[3] !== label || $[4] !== props) {
|
|
2742
|
-
t2 = /* @__PURE__ */ jsx(Box$1, { children: /* @__PURE__ */ jsx(MiniMap, {
|
|
2743
|
-
ariaLabel: label,
|
|
2744
|
-
pannable: true,
|
|
2745
|
-
zoomable: true,
|
|
2746
|
-
...props
|
|
2747
|
-
}) });
|
|
2748
|
-
$[3] = label;
|
|
2749
|
-
$[4] = props;
|
|
2750
|
-
$[5] = t2;
|
|
2751
|
-
} else t2 = $[5];
|
|
2752
|
-
return t2;
|
|
2753
|
-
}
|
|
2754
|
-
//#endregion
|
|
2755
|
-
//#region src/graph/palette-item.tsx
|
|
2756
|
-
/**
|
|
2757
|
-
* Renders the item as a button a person drags onto the canvas or presses.
|
|
2758
|
-
*
|
|
2759
|
-
* @param props - The kind of node, what a press does, and the button's props.
|
|
2760
|
-
*/
|
|
2761
|
-
function PaletteItem(t0) {
|
|
2762
|
-
const $ = c(14);
|
|
2763
|
-
let item;
|
|
2764
|
-
let onAdd;
|
|
2765
|
-
let props;
|
|
2766
|
-
if ($[0] !== t0) {
|
|
2767
|
-
({item, onAdd, ...props} = t0);
|
|
2768
|
-
$[0] = t0;
|
|
2769
|
-
$[1] = item;
|
|
2770
|
-
$[2] = onAdd;
|
|
2771
|
-
$[3] = props;
|
|
2772
|
-
} else {
|
|
2773
|
-
item = $[1];
|
|
2774
|
-
onAdd = $[2];
|
|
2775
|
-
props = $[3];
|
|
2776
|
-
}
|
|
2777
|
-
const centre = useViewCentre();
|
|
2778
|
-
let t1;
|
|
2779
|
-
if ($[4] !== centre || $[5] !== item || $[6] !== onAdd) {
|
|
2780
|
-
t1 = () => {
|
|
2781
|
-
onAdd(item, centre());
|
|
2782
|
-
};
|
|
2783
|
-
$[4] = centre;
|
|
2784
|
-
$[5] = item;
|
|
2785
|
-
$[6] = onAdd;
|
|
2786
|
-
$[7] = t1;
|
|
2787
|
-
} else t1 = $[7];
|
|
2788
|
-
let t2;
|
|
2789
|
-
if ($[8] !== item) {
|
|
2790
|
-
t2 = (event) => {
|
|
2791
|
-
event.dataTransfer.setData(ITEM, item);
|
|
2792
|
-
event.dataTransfer.effectAllowed = "copy";
|
|
2793
|
-
};
|
|
2794
|
-
$[8] = item;
|
|
2795
|
-
$[9] = t2;
|
|
2796
|
-
} else t2 = $[9];
|
|
2797
|
-
let t3;
|
|
2798
|
-
if ($[10] !== props || $[11] !== t1 || $[12] !== t2) {
|
|
2799
|
-
t3 = /* @__PURE__ */ jsx(Button, {
|
|
2800
|
-
draggable: true,
|
|
2801
|
-
onClick: t1,
|
|
2802
|
-
onDragStart: t2,
|
|
2803
|
-
variant: "outline",
|
|
2804
|
-
...props
|
|
2805
|
-
});
|
|
2806
|
-
$[10] = props;
|
|
2807
|
-
$[11] = t1;
|
|
2808
|
-
$[12] = t2;
|
|
2809
|
-
$[13] = t3;
|
|
2810
|
-
} else t3 = $[13];
|
|
2811
|
-
return t3;
|
|
2812
|
-
}
|
|
2813
|
-
//#endregion
|
|
2814
|
-
//#region src/graph/zoom-level.tsx
|
|
2815
|
-
/**
|
|
2816
|
-
* Renders the canvas's zoom level as a percentage in the toolbar.
|
|
2817
|
-
*
|
|
2818
|
-
* @remarks
|
|
2819
|
-
* The element is an `output`, which reads its value to a screen reader when it changes. The
|
|
2820
|
-
* percentage is written in `locale`, else the nearest `LocaleProvider`'s, else the runtime's.
|
|
2821
|
-
*/
|
|
2822
|
-
/**
|
|
2823
|
-
* Renders the `output` with the recipe's level class.
|
|
2824
|
-
*/
|
|
2825
|
-
const Shown = withContext("output", "level");
|
|
2826
|
-
/**
|
|
2827
|
-
* Renders the canvas's zoom as a percentage.
|
|
2828
|
-
*
|
|
2829
|
-
* @param props - The locale and the props of the `output`.
|
|
2830
|
-
*/
|
|
2831
|
-
function ZoomLevel(t0) {
|
|
2832
|
-
const $ = c(14);
|
|
2833
|
-
let locale;
|
|
2834
|
-
let props;
|
|
2835
|
-
if ($[0] !== t0) {
|
|
2836
|
-
({locale, ...props} = t0);
|
|
2837
|
-
$[0] = t0;
|
|
2838
|
-
$[1] = locale;
|
|
2839
|
-
$[2] = props;
|
|
2840
|
-
} else {
|
|
2841
|
-
locale = $[1];
|
|
2842
|
-
props = $[2];
|
|
2843
|
-
}
|
|
2844
|
-
const zoom = useStore(_temp$1);
|
|
2845
|
-
const scoped = use(LocaleContext)?.locale;
|
|
2846
|
-
let T0;
|
|
2847
|
-
let t1;
|
|
2848
|
-
let t2;
|
|
2849
|
-
if ($[3] !== locale || $[4] !== props || $[5] !== scoped || $[6] !== zoom) {
|
|
2850
|
-
const percent = new Intl.NumberFormat(locale ?? scoped, { style: "percent" });
|
|
2851
|
-
T0 = Shown;
|
|
2852
|
-
t1 = props;
|
|
2853
|
-
t2 = percent.format(zoom);
|
|
2854
|
-
$[3] = locale;
|
|
2855
|
-
$[4] = props;
|
|
2856
|
-
$[5] = scoped;
|
|
2857
|
-
$[6] = zoom;
|
|
2858
|
-
$[7] = T0;
|
|
2859
|
-
$[8] = t1;
|
|
2860
|
-
$[9] = t2;
|
|
2861
|
-
} else {
|
|
2862
|
-
T0 = $[7];
|
|
2863
|
-
t1 = $[8];
|
|
2864
|
-
t2 = $[9];
|
|
2865
|
-
}
|
|
2866
|
-
let t3;
|
|
2867
|
-
if ($[10] !== T0 || $[11] !== t1 || $[12] !== t2) {
|
|
2868
|
-
t3 = /* @__PURE__ */ jsx(T0, {
|
|
2869
|
-
...t1,
|
|
2870
|
-
children: t2
|
|
2871
|
-
});
|
|
2872
|
-
$[10] = T0;
|
|
2873
|
-
$[11] = t1;
|
|
2874
|
-
$[12] = t2;
|
|
2875
|
-
$[13] = t3;
|
|
2876
|
-
} else t3 = $[13];
|
|
2877
|
-
return t3;
|
|
2878
|
-
}
|
|
2879
|
-
function _temp$1(state) {
|
|
2880
|
-
return state.transform[2];
|
|
2881
|
-
}
|
|
2882
|
-
//#endregion
|
|
2883
|
-
//#region src/graph/index.ts
|
|
2884
|
-
var graph_exports = /* @__PURE__ */ __exportAll({
|
|
2885
|
-
Canvas: () => Canvas,
|
|
2886
|
-
Caption: () => Caption,
|
|
2887
|
-
Control: () => Control,
|
|
2888
|
-
Controls: () => Controls,
|
|
2889
|
-
Edge: () => Edge$1,
|
|
2890
|
-
Empty: () => Empty,
|
|
2891
|
-
LabelNode: () => LabelNode,
|
|
2892
|
-
MiniMap: () => MiniMap$1,
|
|
2893
|
-
Node: () => Node$1,
|
|
2894
|
-
PaletteItem: () => PaletteItem,
|
|
2895
|
-
Root: () => Root,
|
|
2896
|
-
Summary: () => Summary,
|
|
2897
|
-
ZoomLevel: () => ZoomLevel,
|
|
2898
|
-
edgeTypes: () => EDGE_TYPES,
|
|
2899
|
-
nodeTypes: () => NODE_TYPES$2,
|
|
2900
|
-
useGraphDirection: () => useGraphDirection
|
|
2901
|
-
});
|
|
2902
|
-
//#endregion
|
|
2903
|
-
//#region src/layout/start.ts
|
|
2904
|
-
/**
|
|
2905
|
-
* Returns a seat for each id, linked to the seats its links reach. A link with an end outside the
|
|
2906
|
-
* graph links nothing.
|
|
2907
|
-
*/
|
|
2908
|
-
function seatsOf(ids, links) {
|
|
2909
|
-
const seats = ids.map((id) => ({
|
|
2910
|
-
children: [],
|
|
2911
|
-
depth: -1,
|
|
2912
|
-
id,
|
|
2913
|
-
leaves: 1,
|
|
2914
|
-
neighbours: /* @__PURE__ */ new Set()
|
|
2915
|
-
}));
|
|
2916
|
-
const byId = new Map(seats.map((seat) => [seat.id, seat]));
|
|
2917
|
-
for (const { source, target } of links) {
|
|
2918
|
-
const [from, to] = [byId.get(source), byId.get(target)];
|
|
2919
|
-
if (from !== void 0 && to !== void 0) {
|
|
2920
|
-
from.neighbours.add(to);
|
|
2921
|
-
to.neighbours.add(from);
|
|
2922
|
-
}
|
|
2923
|
-
}
|
|
2924
|
-
return seats;
|
|
2925
|
-
}
|
|
2926
|
-
/**
|
|
2927
|
-
* Returns the seats in an order the number source shuffles.
|
|
2928
|
-
*/
|
|
2929
|
-
function shuffled(seats, random) {
|
|
2930
|
-
return seats.map((seat) => ({
|
|
2931
|
-
key: random(),
|
|
2932
|
-
seat
|
|
2933
|
-
})).toSorted((a, b) => a.key - b.key).map(({ seat }) => seat);
|
|
2934
|
-
}
|
|
2935
|
-
/**
|
|
2936
|
-
* Returns the seats in their order, except that each seat linked to the one before it comes next.
|
|
2937
|
-
*/
|
|
2938
|
-
function chained(seats) {
|
|
2939
|
-
const chain = [];
|
|
2940
|
-
const left = [...seats];
|
|
2941
|
-
for (let at = 0; at < seats.length; at += 1) {
|
|
2942
|
-
const last = chain.at(-1);
|
|
2943
|
-
const next = last === void 0 ? 0 : left.findIndex((seat) => last.neighbours.has(seat));
|
|
2944
|
-
chain.push(...left.splice(Math.max(next, 0), 1));
|
|
2945
|
-
}
|
|
2946
|
-
return chain;
|
|
2947
|
-
}
|
|
2948
|
-
/**
|
|
2949
|
-
* Walks each part breadth first from its most-linked seat, gives every seat its depth and its
|
|
2950
|
-
* children, and returns the seat each part starts from.
|
|
2951
|
-
*/
|
|
2952
|
-
function rootsOf(seats, random) {
|
|
2953
|
-
const roots = [];
|
|
2954
|
-
for (const hub of seats.toSorted((a, b) => b.neighbours.size - a.neighbours.size)) {
|
|
2955
|
-
if (hub.depth >= 0) continue;
|
|
2956
|
-
const part = [hub];
|
|
2957
|
-
hub.depth = 0;
|
|
2958
|
-
roots.push(hub);
|
|
2959
|
-
for (const seat of part) {
|
|
2960
|
-
const unvisited = [...seat.neighbours].filter((neighbour) => neighbour.depth < 0);
|
|
2961
|
-
for (const child of chained(shuffled(unvisited, random))) {
|
|
2962
|
-
child.depth = seat.depth + 1;
|
|
2963
|
-
seat.children.push(child);
|
|
2964
|
-
part.push(child);
|
|
2965
|
-
}
|
|
2966
|
-
}
|
|
2967
|
-
for (const seat of part.toReversed()) if (seat.children.length > 0) seat.leaves = seat.children.reduce((sum, child) => sum + child.leaves, 0);
|
|
2968
|
-
}
|
|
2969
|
-
return roots;
|
|
2970
|
-
}
|
|
2971
|
-
/**
|
|
2972
|
-
* Returns the start of every node of a force layout.
|
|
2973
|
-
*
|
|
2974
|
-
* @param ids - The graph's nodes.
|
|
2975
|
-
* @param links - The links between the nodes, read as undirected.
|
|
2976
|
-
* @param random - The number source the order of each node's neighbours and the first angle come
|
|
2977
|
-
* from.
|
|
2978
|
-
* @param step - The distance between two rings, in pixels.
|
|
2979
|
-
*/
|
|
2980
|
-
function startsOf(ids, links, random, step) {
|
|
2981
|
-
const roots = rootsOf(seatsOf(ids, links), random);
|
|
2982
|
-
const total = roots.reduce((sum, root) => sum + root.leaves, 0);
|
|
2983
|
-
const offset = roots.length > 1 ? 1 : 0;
|
|
2984
|
-
const arcs = [];
|
|
2985
|
-
const starts = /* @__PURE__ */ new Map();
|
|
2986
|
-
let from = random() * Math.PI * 2;
|
|
2987
|
-
for (const seat of roots) {
|
|
2988
|
-
const to = from + Math.PI * 2 * seat.leaves / total;
|
|
2989
|
-
arcs.push({
|
|
2990
|
-
from,
|
|
2991
|
-
seat,
|
|
2992
|
-
to
|
|
2993
|
-
});
|
|
2994
|
-
from = to;
|
|
2995
|
-
}
|
|
2996
|
-
for (const arc of arcs) {
|
|
2997
|
-
const angle = (arc.from + arc.to) / 2;
|
|
2998
|
-
const radius = (arc.seat.depth + offset) * step;
|
|
2999
|
-
let start = arc.from;
|
|
3000
|
-
starts.set(arc.seat.id, {
|
|
3001
|
-
x: Math.cos(angle) * radius,
|
|
3002
|
-
y: Math.sin(angle) * radius
|
|
3003
|
-
});
|
|
3004
|
-
for (const seat of arc.seat.children) {
|
|
3005
|
-
const end = start + (arc.to - arc.from) * seat.leaves / arc.seat.leaves;
|
|
3006
|
-
arcs.push({
|
|
3007
|
-
from: start,
|
|
3008
|
-
seat,
|
|
3009
|
-
to: end
|
|
3010
|
-
});
|
|
3011
|
-
start = end;
|
|
3012
|
-
}
|
|
3013
|
-
}
|
|
3014
|
-
return starts;
|
|
3015
|
-
}
|
|
3016
|
-
//#endregion
|
|
3017
|
-
//#region src/layout/force.ts
|
|
3018
|
-
/**
|
|
3019
|
-
* Length of a link of strength 1, in pixels.
|
|
3020
|
-
*/
|
|
3021
|
-
const DISTANCE = 110;
|
|
3022
|
-
/**
|
|
3023
|
-
* Push between every two nodes. A negative strength pushes.
|
|
3024
|
-
*/
|
|
3025
|
-
const CHARGE = -500;
|
|
3026
|
-
/**
|
|
3027
|
-
* Strength of the pull to the middle across the canvas.
|
|
3028
|
-
*/
|
|
3029
|
-
const PULL = .05;
|
|
3030
|
-
/**
|
|
3031
|
-
* How much stronger the pull to the middle is up and down than across.
|
|
3032
|
-
*/
|
|
3033
|
-
const FLATTEN = 3;
|
|
3034
|
-
/**
|
|
3035
|
-
* Returns how strongly a link ties its ends, from 1 to 2: its strength, 1 unless stated.
|
|
3036
|
-
*/
|
|
3037
|
-
function strengthOf(link) {
|
|
3038
|
-
return Math.min(Math.max(link.strength ?? 1, 1), 2);
|
|
3039
|
-
}
|
|
3040
|
-
/**
|
|
3041
|
-
* Returns a number source that yields the same numbers for the same seed.
|
|
3042
|
-
*/
|
|
3043
|
-
function seeded(seed) {
|
|
3044
|
-
let state = seed;
|
|
3045
|
-
return () => {
|
|
3046
|
-
state = (state * 1664525 + 1013904223) % 4294967296;
|
|
3047
|
-
return state / 4294967296;
|
|
3048
|
-
};
|
|
3049
|
-
}
|
|
3050
|
-
/**
|
|
3051
|
-
* Returns the simulation's nodes at their starts.
|
|
3052
|
-
*/
|
|
3053
|
-
function bodiesOf(nodes, starts) {
|
|
3054
|
-
return nodes.map((node) => {
|
|
3055
|
-
const size = sizeOf(node);
|
|
3056
|
-
const { x, y } = starts.get(node.id);
|
|
3057
|
-
return {
|
|
3058
|
-
node,
|
|
3059
|
-
radius: Math.hypot(size.width, size.height) / 2,
|
|
3060
|
-
size,
|
|
3061
|
-
x,
|
|
3062
|
-
y
|
|
3063
|
-
};
|
|
3064
|
-
});
|
|
3065
|
-
}
|
|
3066
|
-
/**
|
|
3067
|
-
* Returns the simulation's links: each link between two different nodes of the graph.
|
|
3068
|
-
*/
|
|
3069
|
-
function tiesOf(links, ids) {
|
|
3070
|
-
return links.filter(({ source, target }) => source !== target && ids.has(source) && ids.has(target)).map((link) => ({
|
|
3071
|
-
source: link.source,
|
|
3072
|
-
strength: strengthOf(link),
|
|
3073
|
-
target: link.target
|
|
3074
|
-
}));
|
|
3075
|
-
}
|
|
3076
|
-
/**
|
|
3077
|
-
* Returns the nodes placed by the force simulation, each with the size it was placed by as its
|
|
3078
|
-
* initial size.
|
|
3079
|
-
*
|
|
3080
|
-
* @typeParam N - One node of the graph.
|
|
3081
|
-
* @param nodes - The graph's nodes, in any order.
|
|
3082
|
-
* @param links - The links between the nodes, read as undirected.
|
|
3083
|
-
* @param options - The number of steps and the seed.
|
|
3084
|
-
*/
|
|
3085
|
-
function layoutForce(nodes, links, options = {}) {
|
|
3086
|
-
const { iterations = 300, seed = 1 } = options;
|
|
3087
|
-
const random = seeded(seed);
|
|
3088
|
-
const ids = nodes.map((node) => node.id);
|
|
3089
|
-
const bodies = bodiesOf(nodes, startsOf(ids, links, random, DISTANCE));
|
|
3090
|
-
const ties = tiesOf(links, new Set(ids));
|
|
3091
|
-
forceSimulation(bodies).randomSource(random).force("link", forceLink(ties).id((body) => body.node.id).distance((tie) => DISTANCE / tie.strength)).force("charge", forceManyBody().strength(CHARGE)).force("collide", forceCollide((body) => body.radius).iterations(2)).force("x", forceX().strength(PULL)).force("y", forceY().strength(PULL * FLATTEN)).force("center", forceCenter()).stop().tick(iterations);
|
|
3092
|
-
const placed = [];
|
|
3093
|
-
for (const { node, size, x = 0, y = 0 } of bodies) placed.push({
|
|
3094
|
-
...node,
|
|
3095
|
-
initialHeight: size.height,
|
|
3096
|
-
initialWidth: size.width,
|
|
3097
|
-
position: {
|
|
3098
|
-
x: x - size.width / 2,
|
|
3099
|
-
y: y - size.height / 2
|
|
3100
|
-
}
|
|
3101
|
-
});
|
|
3102
|
-
return placed;
|
|
3103
|
-
}
|
|
3104
|
-
//#endregion
|
|
3105
|
-
//#region src/network-graph/disc.tsx
|
|
3106
|
-
/**
|
|
3107
|
-
* Renders the node's box with the recipe's disc node class.
|
|
3108
|
-
*/
|
|
3109
|
-
const Box = withContext("div", "discNode");
|
|
3110
|
-
/**
|
|
3111
|
-
* Renders the disc with the recipe's disc class.
|
|
3112
|
-
*/
|
|
3113
|
-
const Round = withContext("div", "disc");
|
|
3114
|
-
/**
|
|
3115
|
-
* Renders the hub the handles are in with the recipe's disc hub class.
|
|
3116
|
-
*/
|
|
3117
|
-
const Hub = withContext("div", "discHub");
|
|
3118
|
-
/**
|
|
3119
|
-
* Renders the name with the recipe's disc label class.
|
|
3120
|
-
*/
|
|
3121
|
-
const Name = withContext("span", "discLabel");
|
|
3122
|
-
/**
|
|
3123
|
-
* Renders the node's disc, its handles, its icon and its name from its data.
|
|
3124
|
-
*
|
|
3125
|
-
* @param props - React Flow's node props: the node's data among them.
|
|
3126
|
-
*/
|
|
3127
|
-
function Disc(t0) {
|
|
3128
|
-
const $ = c(12);
|
|
3129
|
-
const { data } = t0;
|
|
3130
|
-
const t1 = `${String(data.disc)}px`;
|
|
3131
|
-
let t2;
|
|
3132
|
-
if ($[0] !== t1) {
|
|
3133
|
-
t2 = { "--graph-disc": t1 };
|
|
3134
|
-
$[0] = t1;
|
|
3135
|
-
$[1] = t2;
|
|
3136
|
-
} else t2 = $[1];
|
|
3137
|
-
const size = t2;
|
|
3138
|
-
const t3 = data.dimmed ? "" : void 0;
|
|
3139
|
-
let t4;
|
|
3140
|
-
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
|
3141
|
-
t4 = /* @__PURE__ */ jsxs(Hub, { children: [/* @__PURE__ */ jsx(Handle, {
|
|
3142
|
-
isConnectable: false,
|
|
3143
|
-
position: Position.Top,
|
|
3144
|
-
type: "target"
|
|
3145
|
-
}), /* @__PURE__ */ jsx(Handle, {
|
|
3146
|
-
isConnectable: false,
|
|
3147
|
-
position: Position.Top,
|
|
3148
|
-
type: "source"
|
|
3149
|
-
})] });
|
|
3150
|
-
$[2] = t4;
|
|
3151
|
-
} else t4 = $[2];
|
|
3152
|
-
let t5;
|
|
3153
|
-
if ($[3] !== data.icon || $[4] !== size) {
|
|
3154
|
-
t5 = /* @__PURE__ */ jsxs(Round, {
|
|
3155
|
-
"aria-hidden": true,
|
|
3156
|
-
style: size,
|
|
3157
|
-
children: [t4, data.icon]
|
|
3158
|
-
});
|
|
3159
|
-
$[3] = data.icon;
|
|
3160
|
-
$[4] = size;
|
|
3161
|
-
$[5] = t5;
|
|
3162
|
-
} else t5 = $[5];
|
|
3163
|
-
let t6;
|
|
3164
|
-
if ($[6] !== data.label) {
|
|
3165
|
-
t6 = /* @__PURE__ */ jsx(Name, { children: data.label });
|
|
3166
|
-
$[6] = data.label;
|
|
3167
|
-
$[7] = t6;
|
|
3168
|
-
} else t6 = $[7];
|
|
3169
|
-
let t7;
|
|
3170
|
-
if ($[8] !== t3 || $[9] !== t5 || $[10] !== t6) {
|
|
3171
|
-
t7 = /* @__PURE__ */ jsxs(Box, {
|
|
3172
|
-
"data-dimmed": t3,
|
|
3173
|
-
children: [t5, t6]
|
|
3174
|
-
});
|
|
3175
|
-
$[8] = t3;
|
|
3176
|
-
$[9] = t5;
|
|
3177
|
-
$[10] = t6;
|
|
3178
|
-
$[11] = t7;
|
|
3179
|
-
} else t7 = $[11];
|
|
3180
|
-
return t7;
|
|
3181
|
-
}
|
|
3182
|
-
//#endregion
|
|
3183
|
-
//#region src/network-graph/network.ts
|
|
3184
|
-
/**
|
|
3185
|
-
* Reads a network's links: the links the canvas renders with the nodes at their ends, and the
|
|
3186
|
-
* nodes one node connects to.
|
|
3187
|
-
*
|
|
3188
|
-
* @remarks
|
|
3189
|
-
* A link whose end is not a node of the graph is ordinary in a filtered graph, and a link from a
|
|
3190
|
-
* node to itself renders nothing, so neither is rendered, counted or weighed. A network's links
|
|
3191
|
-
* read the same either way round, so the walk from a node follows each link both ways. The walk
|
|
3192
|
-
* never counts the node it starts from, and stops at a node it visited before.
|
|
3193
|
-
*/
|
|
3194
|
-
/**
|
|
3195
|
-
* Returns the links between two different nodes of the graph, each with the nodes at its ends.
|
|
3196
|
-
*/
|
|
3197
|
-
function drawnOf(nodes, links) {
|
|
3198
|
-
const known = new Map(nodes.map((node) => [node.id, node]));
|
|
3199
|
-
return links.flatMap((link) => {
|
|
3200
|
-
const from = known.get(link.source);
|
|
3201
|
-
const to = known.get(link.target);
|
|
3202
|
-
return from === void 0 || to === void 0 || from === to ? [] : [{
|
|
3203
|
-
from,
|
|
3204
|
-
link,
|
|
3205
|
-
to
|
|
3206
|
-
}];
|
|
3207
|
-
});
|
|
3208
|
-
}
|
|
3209
|
-
/**
|
|
3210
|
-
* Returns the nodes within `depth` hops of a node, following each link either way round.
|
|
3211
|
-
*
|
|
3212
|
-
* @param links - The graph's links.
|
|
3213
|
-
* @param id - The node the walk starts from, which is never among the nodes it returns.
|
|
3214
|
-
* @param depth - How many hops count. 1 unless stated.
|
|
3215
|
-
*/
|
|
3216
|
-
function neighborsOf(links, id, depth = 1) {
|
|
3217
|
-
return walk(links.flatMap(({ source, target }) => [{
|
|
3218
|
-
source,
|
|
3219
|
-
target
|
|
3220
|
-
}, {
|
|
3221
|
-
source: target,
|
|
3222
|
-
target: source
|
|
3223
|
-
}]), id, depth, true);
|
|
3224
|
-
}
|
|
3225
|
-
//#endregion
|
|
3226
|
-
//#region src/network-graph/discs.ts
|
|
3227
|
-
/**
|
|
3228
|
-
* Smallest and largest diameter of a disc, in pixels.
|
|
3229
|
-
*/
|
|
3230
|
-
const DIAMETER = {
|
|
3231
|
-
max: 68,
|
|
3232
|
-
min: 36
|
|
3233
|
-
};
|
|
3234
|
-
/**
|
|
3235
|
-
* Returns each node, in order, with the diameter of its disc.
|
|
3236
|
-
*
|
|
3237
|
-
* @param nodes - The graph's nodes.
|
|
3238
|
-
* @param links - The links the canvas renders, which a node without a weight is weighed by.
|
|
3239
|
-
*/
|
|
3240
|
-
function discsOf(nodes, links) {
|
|
3241
|
-
const degree = /* @__PURE__ */ new Map();
|
|
3242
|
-
for (const { source, target } of links) {
|
|
3243
|
-
degree.set(source, (degree.get(source) ?? 0) + 1);
|
|
3244
|
-
degree.set(target, (degree.get(target) ?? 0) + 1);
|
|
3245
|
-
}
|
|
3246
|
-
const weighed = nodes.map((node) => ({
|
|
3247
|
-
node,
|
|
3248
|
-
weight: Math.max(node.weight ?? degree.get(node.id) ?? 0, 0)
|
|
3249
|
-
}));
|
|
3250
|
-
const heaviest = Math.max(0, ...weighed.map(({ weight }) => weight));
|
|
3251
|
-
const span = DIAMETER.max - DIAMETER.min;
|
|
3252
|
-
return weighed.map(({ node, weight }) => ({
|
|
3253
|
-
disc: DIAMETER.min + span * (heaviest === 0 ? 0 : Math.sqrt(weight / heaviest)),
|
|
3254
|
-
node
|
|
3255
|
-
}));
|
|
3256
|
-
}
|
|
3257
|
-
//#endregion
|
|
3258
|
-
//#region src/network-graph/places.ts
|
|
3259
|
-
/**
|
|
3260
|
-
* Width a name takes for each of its characters, in pixels.
|
|
3261
|
-
*/
|
|
3262
|
-
const CHARACTER = 8.5;
|
|
3263
|
-
/**
|
|
3264
|
-
* Inline padding of a name's pill, both sides together, in pixels.
|
|
3265
|
-
*/
|
|
3266
|
-
const PILL = 8;
|
|
3267
|
-
/**
|
|
3268
|
-
* Height of a name's line under the disc and the gap above it, in pixels.
|
|
3269
|
-
*/
|
|
3270
|
-
const NAME = 26;
|
|
3271
|
-
/**
|
|
3272
|
-
* Returns the box the layout places a node by: as wide as its disc or its name, whichever is
|
|
3273
|
-
* wider, and as tall as the disc and the name's line.
|
|
3274
|
-
*/
|
|
3275
|
-
function boxOf({ disc, node }) {
|
|
3276
|
-
return {
|
|
3277
|
-
height: disc + NAME,
|
|
3278
|
-
width: Math.max(disc, node.label.length * CHARACTER + PILL)
|
|
3279
|
-
};
|
|
3280
|
-
}
|
|
3281
|
-
/**
|
|
3282
|
-
* Returns each node, in order, with its disc and the middle of its top edge where the layout put
|
|
3283
|
-
* it.
|
|
3284
|
-
*
|
|
3285
|
-
* @param input - The nodes, the links, the seed and the steps.
|
|
3286
|
-
*/
|
|
3287
|
-
function placesOf(input) {
|
|
3288
|
-
const boxes = discsOf(input.nodes, input.links).map((sized) => ({
|
|
3289
|
-
data: { sized },
|
|
3290
|
-
id: sized.node.id,
|
|
3291
|
-
measured: boxOf(sized),
|
|
3292
|
-
position: {
|
|
3293
|
-
x: 0,
|
|
3294
|
-
y: 0
|
|
3295
|
-
}
|
|
3296
|
-
}));
|
|
3297
|
-
const options = {
|
|
3298
|
-
iterations: input.iterations,
|
|
3299
|
-
seed: input.seed
|
|
3300
|
-
};
|
|
3301
|
-
return layoutForce(boxes, input.links, options).map(({ data, measured, position }) => ({
|
|
3302
|
-
disc: data.sized.disc,
|
|
3303
|
-
node: data.sized.node,
|
|
3304
|
-
position: {
|
|
3305
|
-
x: position.x + measured.width / 2,
|
|
3306
|
-
y: position.y
|
|
3307
|
-
}
|
|
3308
|
-
}));
|
|
3309
|
-
}
|
|
3310
|
-
//#endregion
|
|
3311
|
-
//#region src/network-graph/view.ts
|
|
3312
|
-
/**
|
|
3313
|
-
* Point of a disc node its position is: the middle of its top edge.
|
|
3314
|
-
*/
|
|
3315
|
-
const TOP_MIDDLE = [.5, 0];
|
|
3316
|
-
/**
|
|
3317
|
-
* Returns the word a node's accessible name ends with: the focus's, a lit node's, or none.
|
|
3318
|
-
*/
|
|
3319
|
-
function tagOf(id, input, lit) {
|
|
3320
|
-
if (id === input.focus) return input.words.focusLabel;
|
|
3321
|
-
return lit?.has(id) === true ? input.words.neighborLabel : void 0;
|
|
3322
|
-
}
|
|
3323
|
-
/**
|
|
3324
|
-
* Returns the disc of one node, where the layout put it.
|
|
3325
|
-
*/
|
|
3326
|
-
function discOf({ disc, node, position }, input, lit) {
|
|
3327
|
-
const tag = tagOf(node.id, input, lit);
|
|
3328
|
-
const size = input.sizes.get(node.id);
|
|
3329
|
-
return {
|
|
3330
|
-
ariaLabel: tag === void 0 ? node.label : `${node.label}, ${tag}`,
|
|
3331
|
-
data: {
|
|
3332
|
-
dimmed: lit !== void 0 && tag === void 0,
|
|
3333
|
-
disc,
|
|
3334
|
-
icon: node.icon,
|
|
3335
|
-
label: node.label
|
|
3336
|
-
},
|
|
3337
|
-
id: node.id,
|
|
3338
|
-
origin: TOP_MIDDLE,
|
|
3339
|
-
position,
|
|
3340
|
-
selected: node.id === input.focus,
|
|
3341
|
-
type: "disc",
|
|
3342
|
-
...size === void 0 ? {} : { measured: size }
|
|
3343
|
-
};
|
|
3344
|
-
}
|
|
3345
|
-
/**
|
|
3346
|
-
* Returns a link for React Flow: a straight line named by its ends, as wide as its tie, and marked
|
|
3347
|
-
* on or off the lit set while a node is focused.
|
|
3348
|
-
*/
|
|
3349
|
-
function edgeOf({ from, link, to }, input, lit) {
|
|
3350
|
-
const strength = { "--graph-strength": String(strengthOf(link)) };
|
|
3351
|
-
const on = [from.id, to.id].every((id) => id === input.focus || lit?.has(id) === true);
|
|
3352
|
-
return {
|
|
3353
|
-
ariaLabel: input.words.edgeName({
|
|
3354
|
-
source: from.label,
|
|
3355
|
-
target: to.label
|
|
3356
|
-
}),
|
|
3357
|
-
id: link.id ?? `${from.id}-${to.id}`,
|
|
3358
|
-
selectable: false,
|
|
3359
|
-
source: from.id,
|
|
3360
|
-
style: strength,
|
|
3361
|
-
target: to.id,
|
|
3362
|
-
type: "straight",
|
|
3363
|
-
...lit === void 0 ? {} : { domAttributes: traceMarkOf(on) }
|
|
3364
|
-
};
|
|
3365
|
-
}
|
|
3366
|
-
/**
|
|
3367
|
-
* Returns a focus's connections: the focused node's name, else its id, and the number of nodes lit.
|
|
3368
|
-
*/
|
|
3369
|
-
function connectionsOf(input, lit) {
|
|
3370
|
-
const { focus, placed } = input;
|
|
3371
|
-
if (focus === null || lit === void 0) return void 0;
|
|
3372
|
-
return {
|
|
3373
|
-
count: lit.size,
|
|
3374
|
-
name: placed.find(({ node }) => node.id === focus)?.node.label ?? focus
|
|
3375
|
-
};
|
|
3376
|
-
}
|
|
3377
|
-
/**
|
|
3378
|
-
* Returns the view of a network graph: its discs where the layout put them and its links.
|
|
3379
|
-
*
|
|
3380
|
-
* @param input - The placed nodes, the links, the focus, the sizes and the words.
|
|
3381
|
-
*/
|
|
3382
|
-
function viewOf(input) {
|
|
3383
|
-
const links = input.drawn.map(({ link }) => link);
|
|
3384
|
-
const lit = input.focus === null ? void 0 : neighborsOf(links, input.focus, input.depth);
|
|
3385
|
-
return {
|
|
3386
|
-
complete: input.placed.every(({ node }) => input.sizes.has(node.id)),
|
|
3387
|
-
connections: connectionsOf(input, lit),
|
|
3388
|
-
edges: input.drawn.map((drawn) => edgeOf(drawn, input, lit)),
|
|
3389
|
-
nodes: input.placed.map((placed) => discOf(placed, input, lit))
|
|
3390
|
-
};
|
|
3391
|
-
}
|
|
3392
|
-
//#endregion
|
|
3393
|
-
//#region src/network-graph/state.ts
|
|
3394
|
-
/**
|
|
3395
|
-
* Keeps a network graph's focus, measured sizes and the positions people moved nodes to, and
|
|
3396
|
-
* works out its view.
|
|
3397
|
-
*
|
|
3398
|
-
* @remarks
|
|
3399
|
-
* The focus is controlled where the caller passes it. A focus of `null` is a controlled graph
|
|
3400
|
-
* with no focus, because `undefined` leaves the focus to the graph. The measured sizes go back to
|
|
3401
|
-
* React Flow with each node, which keeps its handles' measurements, and play no part in the
|
|
3402
|
-
* layout. A moved node keeps its position while the layout is the one it was moved on, and goes
|
|
3403
|
-
* back to the layout's position when the layout changes, such as for new data or a new seed. A
|
|
3404
|
-
* change that changes no state renders nothing again, so a report React Flow repeats for nodes
|
|
3405
|
-
* it measures again cannot start a loop of renders.
|
|
3406
|
-
*/
|
|
3407
|
-
/**
|
|
3408
|
-
* No position, for a layout nobody moved a node on.
|
|
3409
|
-
*/
|
|
3410
|
-
const UNMOVED = /* @__PURE__ */ new Map();
|
|
3411
|
-
/**
|
|
3412
|
-
* Returns the nodes with each moved node at the position a person moved it to.
|
|
3413
|
-
*/
|
|
3414
|
-
function movedTo(nodes, positions) {
|
|
3415
|
-
return nodes.map((node) => {
|
|
3416
|
-
const position = positions.get(node.id);
|
|
3417
|
-
return position === void 0 ? node : {
|
|
3418
|
-
...node,
|
|
3419
|
-
position
|
|
3420
|
-
};
|
|
3421
|
-
});
|
|
3422
|
-
}
|
|
3423
|
-
/**
|
|
3424
|
-
* Returns the network's view in its current state and the functions that change the state.
|
|
3425
|
-
*
|
|
3426
|
-
* @param props - The graph, its focus, its layout and its words.
|
|
3427
|
-
*/
|
|
3428
|
-
function useLinked(props) {
|
|
3429
|
-
const $ = c(35);
|
|
3430
|
-
let t0;
|
|
3431
|
-
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
3432
|
-
t0 = /* @__PURE__ */ new Map();
|
|
3433
|
-
$[0] = t0;
|
|
3434
|
-
} else t0 = $[0];
|
|
3435
|
-
const [sizes, setSizes] = useState(t0);
|
|
3436
|
-
let t1;
|
|
3437
|
-
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
|
3438
|
-
t1 = {
|
|
3439
|
-
layout: "",
|
|
3440
|
-
positions: UNMOVED
|
|
3441
|
-
};
|
|
3442
|
-
$[1] = t1;
|
|
3443
|
-
} else t1 = $[1];
|
|
3444
|
-
const [moves, setMoves] = useState(t1);
|
|
3445
|
-
const t2 = props.defaultFocus ?? null;
|
|
3446
|
-
let t3;
|
|
3447
|
-
if ($[2] !== props.focus || $[3] !== props.onFocusChange || $[4] !== t2) {
|
|
3448
|
-
t3 = {
|
|
3449
|
-
defaultValue: t2,
|
|
3450
|
-
onChange: props.onFocusChange,
|
|
3451
|
-
value: props.focus
|
|
3452
|
-
};
|
|
3453
|
-
$[2] = props.focus;
|
|
3454
|
-
$[3] = props.onFocusChange;
|
|
3455
|
-
$[4] = t2;
|
|
3456
|
-
$[5] = t3;
|
|
3457
|
-
} else t3 = $[5];
|
|
3458
|
-
const [focus, setFocus] = useControllableState(t3);
|
|
3459
|
-
let layout;
|
|
3460
|
-
let t4;
|
|
3461
|
-
let t5;
|
|
3462
|
-
let t6;
|
|
3463
|
-
let view;
|
|
3464
|
-
if ($[6] !== focus || $[7] !== moves.layout || $[8] !== moves.positions || $[9] !== props.depth || $[10] !== props.iterations || $[11] !== props.links || $[12] !== props.nodes || $[13] !== props.seed || $[14] !== props.words || $[15] !== setFocus || $[16] !== sizes) {
|
|
3465
|
-
const drawn = drawnOf(props.nodes, props.links);
|
|
3466
|
-
const placed = placesOf({
|
|
3467
|
-
iterations: props.iterations ?? 300,
|
|
3468
|
-
links: drawn.map(_temp),
|
|
3469
|
-
nodes: props.nodes,
|
|
3470
|
-
seed: props.seed ?? 1
|
|
3471
|
-
});
|
|
3472
|
-
view = viewOf({
|
|
3473
|
-
depth: props.depth ?? 1,
|
|
3474
|
-
drawn,
|
|
3475
|
-
focus,
|
|
3476
|
-
placed,
|
|
3477
|
-
sizes,
|
|
3478
|
-
words: props.words
|
|
3479
|
-
});
|
|
3480
|
-
layout = signatureOf(view.nodes);
|
|
3481
|
-
const positions = moves.layout === layout ? moves.positions : UNMOVED;
|
|
3482
|
-
if ($[22] !== setFocus) {
|
|
3483
|
-
t4 = () => {
|
|
3484
|
-
setFocus(null);
|
|
3485
|
-
};
|
|
3486
|
-
$[22] = setFocus;
|
|
3487
|
-
$[23] = t4;
|
|
3488
|
-
} else t4 = $[23];
|
|
3489
|
-
if ($[24] !== positions) {
|
|
3490
|
-
t5 = new Set(positions.keys());
|
|
3491
|
-
$[24] = positions;
|
|
3492
|
-
$[25] = t5;
|
|
3493
|
-
} else t5 = $[25];
|
|
3494
|
-
t6 = movedTo(view.nodes, positions);
|
|
3495
|
-
$[6] = focus;
|
|
3496
|
-
$[7] = moves.layout;
|
|
3497
|
-
$[8] = moves.positions;
|
|
3498
|
-
$[9] = props.depth;
|
|
3499
|
-
$[10] = props.iterations;
|
|
3500
|
-
$[11] = props.links;
|
|
3501
|
-
$[12] = props.nodes;
|
|
3502
|
-
$[13] = props.seed;
|
|
3503
|
-
$[14] = props.words;
|
|
3504
|
-
$[15] = setFocus;
|
|
3505
|
-
$[16] = sizes;
|
|
3506
|
-
$[17] = layout;
|
|
3507
|
-
$[18] = t4;
|
|
3508
|
-
$[19] = t5;
|
|
3509
|
-
$[20] = t6;
|
|
3510
|
-
$[21] = view;
|
|
3511
|
-
} else {
|
|
3512
|
-
layout = $[17];
|
|
3513
|
-
t4 = $[18];
|
|
3514
|
-
t5 = $[19];
|
|
3515
|
-
t6 = $[20];
|
|
3516
|
-
view = $[21];
|
|
3517
|
-
}
|
|
3518
|
-
let t7;
|
|
3519
|
-
if ($[26] !== layout || $[27] !== setFocus) {
|
|
3520
|
-
t7 = (changes) => {
|
|
3521
|
-
const selected = selectedBy(changes);
|
|
3522
|
-
if (selected !== void 0) setFocus(selected);
|
|
3523
|
-
setSizes((known) => measuredBy(known, changes));
|
|
3524
|
-
setMoves((known_0) => {
|
|
3525
|
-
const kept = known_0.layout === layout ? known_0.positions : UNMOVED;
|
|
3526
|
-
const next = movedBy(kept, changes);
|
|
3527
|
-
return next === kept ? known_0 : {
|
|
3528
|
-
layout,
|
|
3529
|
-
positions: next
|
|
3530
|
-
};
|
|
3531
|
-
});
|
|
3532
|
-
};
|
|
3533
|
-
$[26] = layout;
|
|
3534
|
-
$[27] = setFocus;
|
|
3535
|
-
$[28] = t7;
|
|
3536
|
-
} else t7 = $[28];
|
|
3537
|
-
let t8;
|
|
3538
|
-
if ($[29] !== t4 || $[30] !== t5 || $[31] !== t6 || $[32] !== t7 || $[33] !== view) {
|
|
3539
|
-
t8 = {
|
|
3540
|
-
clear: t4,
|
|
3541
|
-
moved: t5,
|
|
3542
|
-
nodes: t6,
|
|
3543
|
-
onNodesChange: t7,
|
|
3544
|
-
view
|
|
3545
|
-
};
|
|
3546
|
-
$[29] = t4;
|
|
3547
|
-
$[30] = t5;
|
|
3548
|
-
$[31] = t6;
|
|
3549
|
-
$[32] = t7;
|
|
3550
|
-
$[33] = view;
|
|
3551
|
-
$[34] = t8;
|
|
3552
|
-
} else t8 = $[34];
|
|
3553
|
-
return t8;
|
|
3554
|
-
}
|
|
3555
|
-
function _temp(t0) {
|
|
3556
|
-
const { link } = t0;
|
|
3557
|
-
return link;
|
|
3558
|
-
}
|
|
3559
|
-
//#endregion
|
|
3560
|
-
//#region src/network-graph/linked.tsx
|
|
3561
|
-
/**
|
|
3562
|
-
* Maps the network's node type to its disc.
|
|
3563
|
-
*/
|
|
3564
|
-
const NODE_TYPES = { disc: Disc };
|
|
3565
|
-
/**
|
|
3566
|
-
* Renders the canvas over the network's view and the readout below it.
|
|
3567
|
-
*
|
|
3568
|
-
* @param props - The graph, its focus, its layout and its words.
|
|
3569
|
-
*/
|
|
3570
|
-
function Linked(props) {
|
|
3571
|
-
const $ = c(21);
|
|
3572
|
-
const { clear, moved, nodes, onNodesChange, view } = useLinked(props);
|
|
3573
|
-
const placing = usePlaced(view.nodes, view.complete, moved);
|
|
3574
|
-
const { connections } = view;
|
|
3575
|
-
const { words } = props;
|
|
3576
|
-
const t0 = placing ? "" : void 0;
|
|
3577
|
-
let t1;
|
|
3578
|
-
if ($[0] !== nodes || $[1] !== onNodesChange || $[2] !== props.draggable || $[3] !== props.label || $[4] !== t0 || $[5] !== view.edges || $[6] !== words.moveAnnouncement || $[7] !== words.nodeDescription) {
|
|
3579
|
-
t1 = /* @__PURE__ */ jsx(Canvas, {
|
|
3580
|
-
"data-placing": t0,
|
|
3581
|
-
edges: view.edges,
|
|
3582
|
-
edgesFocusable: false,
|
|
3583
|
-
label: props.label,
|
|
3584
|
-
moveAnnouncement: words.moveAnnouncement,
|
|
3585
|
-
multiSelectionKeyCode: null,
|
|
3586
|
-
nodeDescription: words.nodeDescription,
|
|
3587
|
-
nodes,
|
|
3588
|
-
nodesConnectable: false,
|
|
3589
|
-
nodesDraggable: props.draggable,
|
|
3590
|
-
nodeTypes: NODE_TYPES,
|
|
3591
|
-
onNodesChange,
|
|
3592
|
-
selectionKeyCode: null
|
|
3593
|
-
});
|
|
3594
|
-
$[0] = nodes;
|
|
3595
|
-
$[1] = onNodesChange;
|
|
3596
|
-
$[2] = props.draggable;
|
|
3597
|
-
$[3] = props.label;
|
|
3598
|
-
$[4] = t0;
|
|
3599
|
-
$[5] = view.edges;
|
|
3600
|
-
$[6] = words.moveAnnouncement;
|
|
3601
|
-
$[7] = words.nodeDescription;
|
|
3602
|
-
$[8] = t1;
|
|
3603
|
-
} else t1 = $[8];
|
|
3604
|
-
const t2 = connections !== void 0;
|
|
3605
|
-
let t3;
|
|
3606
|
-
if ($[9] !== connections || $[10] !== words) {
|
|
3607
|
-
t3 = connections === void 0 ? words.promptLabel : words.summary(connections);
|
|
3608
|
-
$[9] = connections;
|
|
3609
|
-
$[10] = words;
|
|
3610
|
-
$[11] = t3;
|
|
3611
|
-
} else t3 = $[11];
|
|
3612
|
-
let t4;
|
|
3613
|
-
if ($[12] !== clear || $[13] !== props.overview || $[14] !== t2 || $[15] !== t3 || $[16] !== words.clearLabel) {
|
|
3614
|
-
t4 = /* @__PURE__ */ jsx(Readout, {
|
|
3615
|
-
clearLabel: words.clearLabel,
|
|
3616
|
-
focused: t2,
|
|
3617
|
-
onClear: clear,
|
|
3618
|
-
text: t3,
|
|
3619
|
-
children: props.overview
|
|
3620
|
-
});
|
|
3621
|
-
$[12] = clear;
|
|
3622
|
-
$[13] = props.overview;
|
|
3623
|
-
$[14] = t2;
|
|
3624
|
-
$[15] = t3;
|
|
3625
|
-
$[16] = words.clearLabel;
|
|
3626
|
-
$[17] = t4;
|
|
3627
|
-
} else t4 = $[17];
|
|
3628
|
-
let t5;
|
|
3629
|
-
if ($[18] !== t1 || $[19] !== t4) {
|
|
3630
|
-
t5 = /* @__PURE__ */ jsxs(Fragment, { children: [t1, t4] });
|
|
3631
|
-
$[18] = t1;
|
|
3632
|
-
$[19] = t4;
|
|
3633
|
-
$[20] = t5;
|
|
3634
|
-
} else t5 = $[20];
|
|
3635
|
-
return t5;
|
|
3636
|
-
}
|
|
3637
|
-
//#endregion
|
|
3638
|
-
//#region src/network-graph/words.ts
|
|
3639
|
-
/**
|
|
3640
|
-
* Writes the words a network graph renders, from the caller's words and the English defaults.
|
|
3641
|
-
*
|
|
3642
|
-
* @remarks
|
|
3643
|
-
* A caller that states a word replaces the default and keeps every other default. A link names
|
|
3644
|
-
* its ends with "and", because a link reads the same either way round. The description offers
|
|
3645
|
-
* the arrow keys only while nodes can be moved.
|
|
3646
|
-
*/
|
|
3647
|
-
/**
|
|
3648
|
-
* Instruction every node is described by while nodes can be moved, unless stated.
|
|
3649
|
-
*/
|
|
3650
|
-
const MOVABLE = "Press Enter or Space to focus the node, the arrow keys to move it, and Escape to clear the focus.";
|
|
3651
|
-
/**
|
|
3652
|
-
* Instruction every node is described by while nodes cannot be moved, unless stated.
|
|
3653
|
-
*/
|
|
3654
|
-
const FIXED = "Press Enter or Space to focus the node, and Escape to clear the focus.";
|
|
3655
|
-
/**
|
|
3656
|
-
* Writes the name of a link from the names of its ends, unless stated.
|
|
3657
|
-
*/
|
|
3658
|
-
function linked({ source, target }) {
|
|
3659
|
-
return `${source} and ${target}`;
|
|
3660
|
-
}
|
|
3661
|
-
/**
|
|
3662
|
-
* Writes the summary of a focus, unless stated.
|
|
3663
|
-
*/
|
|
3664
|
-
function counted({ count, name }) {
|
|
3665
|
-
return `${name}: ${String(count)} ${count === 1 ? "connection" : "connections"}`;
|
|
3666
|
-
}
|
|
3667
|
-
/**
|
|
3668
|
-
* English words of a network graph whose nodes can be moved.
|
|
3669
|
-
*/
|
|
3670
|
-
const WORDS = {
|
|
3671
|
-
clearLabel: "Clear focus",
|
|
3672
|
-
edgeName: linked,
|
|
3673
|
-
emptyLabel: "No nodes to show.",
|
|
3674
|
-
focusLabel: "Focus",
|
|
3675
|
-
moveAnnouncement: moveAnnouncementOf,
|
|
3676
|
-
neighborLabel: "Connected",
|
|
3677
|
-
nodeDescription: MOVABLE,
|
|
3678
|
-
promptLabel: "Select a node to show what it connects to.",
|
|
3679
|
-
summary: counted
|
|
3680
|
-
};
|
|
3681
|
-
/**
|
|
3682
|
-
* Returns the caller's words over the English defaults.
|
|
3683
|
-
*
|
|
3684
|
-
* @param words - The words the caller states.
|
|
3685
|
-
* @param draggable - Whether a person can move the nodes, which the default description offers.
|
|
3686
|
-
*/
|
|
3687
|
-
function wordsOf(words, draggable) {
|
|
3688
|
-
return {
|
|
3689
|
-
...WORDS,
|
|
3690
|
-
nodeDescription: draggable ? MOVABLE : FIXED,
|
|
3691
|
-
...omitUndefined(words)
|
|
3692
|
-
};
|
|
3693
|
-
}
|
|
3694
|
-
//#endregion
|
|
3695
|
-
//#region src/network-graph/network-graph.tsx
|
|
3696
|
-
/**
|
|
3697
|
-
* Splits a network graph's props into its words, the canvas's props, the dragging and the
|
|
3698
|
-
* figure's props.
|
|
3699
|
-
*/
|
|
3700
|
-
function split(props) {
|
|
3701
|
-
const { clearLabel, defaultFocus, depth, draggable = true, edgeName, emptyLabel, focus, focusLabel, iterations, label, links, moveAnnouncement, neighborLabel, nodeDescription, nodes, onFocusChange, overview, promptLabel, seed, summary, ...rest } = props;
|
|
3702
|
-
const words = {
|
|
3703
|
-
clearLabel,
|
|
3704
|
-
edgeName,
|
|
3705
|
-
emptyLabel,
|
|
3706
|
-
focusLabel,
|
|
3707
|
-
moveAnnouncement
|
|
3708
|
-
};
|
|
3709
|
-
const more = {
|
|
3710
|
-
neighborLabel,
|
|
3711
|
-
nodeDescription,
|
|
3712
|
-
promptLabel,
|
|
3713
|
-
summary
|
|
3714
|
-
};
|
|
3715
|
-
const own = {
|
|
3716
|
-
defaultFocus,
|
|
3717
|
-
depth,
|
|
3718
|
-
focus,
|
|
3719
|
-
iterations,
|
|
3720
|
-
label,
|
|
3721
|
-
links,
|
|
3722
|
-
nodes,
|
|
3723
|
-
onFocusChange
|
|
3724
|
-
};
|
|
3725
|
-
return [
|
|
3726
|
-
{
|
|
3727
|
-
...words,
|
|
3728
|
-
...more
|
|
3729
|
-
},
|
|
3730
|
-
{
|
|
3731
|
-
...own,
|
|
3732
|
-
overview,
|
|
3733
|
-
seed
|
|
3734
|
-
},
|
|
3735
|
-
draggable,
|
|
3736
|
-
rest
|
|
3737
|
-
];
|
|
3738
|
-
}
|
|
3739
|
-
/**
|
|
3740
|
-
* Renders the figure: the controls, the canvas, its readout with the overview, and the caption,
|
|
3741
|
-
* or the empty state while the graph has no node.
|
|
3742
|
-
*
|
|
3743
|
-
* @param props - The graph, its focus, its layout, its parts, its words and the figure's props.
|
|
3744
|
-
*/
|
|
3745
|
-
function NetworkGraph(props) {
|
|
3746
|
-
const $ = c(18);
|
|
3747
|
-
let caption;
|
|
3748
|
-
let controls;
|
|
3749
|
-
let draggable;
|
|
3750
|
-
let own;
|
|
3751
|
-
let root;
|
|
3752
|
-
let t0;
|
|
3753
|
-
if ($[0] !== props) {
|
|
3754
|
-
const [stated, t1, t2, rest] = split(props);
|
|
3755
|
-
own = t1;
|
|
3756
|
-
draggable = t2;
|
|
3757
|
-
({caption, controls, ...root} = rest);
|
|
3758
|
-
t0 = wordsOf(stated, draggable);
|
|
3759
|
-
$[0] = props;
|
|
3760
|
-
$[1] = caption;
|
|
3761
|
-
$[2] = controls;
|
|
3762
|
-
$[3] = draggable;
|
|
3763
|
-
$[4] = own;
|
|
3764
|
-
$[5] = root;
|
|
3765
|
-
$[6] = t0;
|
|
3766
|
-
} else {
|
|
3767
|
-
caption = $[1];
|
|
3768
|
-
controls = $[2];
|
|
3769
|
-
draggable = $[3];
|
|
3770
|
-
own = $[4];
|
|
3771
|
-
root = $[5];
|
|
3772
|
-
t0 = $[6];
|
|
3773
|
-
}
|
|
3774
|
-
const words = t0;
|
|
3775
|
-
let t1;
|
|
3776
|
-
if ($[7] !== controls || $[8] !== draggable || $[9] !== own || $[10] !== words) {
|
|
3777
|
-
t1 = own.nodes.length === 0 ? /* @__PURE__ */ jsx(Empty, { children: words.emptyLabel }) : /* @__PURE__ */ jsxs(Fragment, { children: [controls, /* @__PURE__ */ jsx(Linked, {
|
|
3778
|
-
draggable,
|
|
3779
|
-
words,
|
|
3780
|
-
...own
|
|
3781
|
-
})] });
|
|
3782
|
-
$[7] = controls;
|
|
3783
|
-
$[8] = draggable;
|
|
3784
|
-
$[9] = own;
|
|
3785
|
-
$[10] = words;
|
|
3786
|
-
$[11] = t1;
|
|
3787
|
-
} else t1 = $[11];
|
|
3788
|
-
let t2;
|
|
3789
|
-
if ($[12] !== caption) {
|
|
3790
|
-
t2 = caption === void 0 ? null : /* @__PURE__ */ jsx(Caption, { children: caption });
|
|
3791
|
-
$[12] = caption;
|
|
3792
|
-
$[13] = t2;
|
|
3793
|
-
} else t2 = $[13];
|
|
3794
|
-
let t3;
|
|
3795
|
-
if ($[14] !== root || $[15] !== t1 || $[16] !== t2) {
|
|
3796
|
-
t3 = /* @__PURE__ */ jsxs(Root, {
|
|
3797
|
-
...root,
|
|
3798
|
-
children: [t1, t2]
|
|
3799
|
-
});
|
|
3800
|
-
$[14] = root;
|
|
3801
|
-
$[15] = t1;
|
|
3802
|
-
$[16] = t2;
|
|
3803
|
-
$[17] = t3;
|
|
3804
|
-
} else t3 = $[17];
|
|
3805
|
-
return t3;
|
|
3806
|
-
}
|
|
3807
|
-
//#endregion
|
|
1
|
+
import { _ as relationOf, b as hiddenBy, d as DirectedGraph, g as layoutGraph, i as graph_exports, n as neighborsOf, r as layoutForce, t as NetworkGraph, u as treeEdges, v as traceGraph, x as diffGraphs, y as descendantsOf } from "./shared-DOF4V8-Q.js";
|
|
3808
2
|
export { DirectedGraph, graph_exports as Graph, NetworkGraph, descendantsOf, diffGraphs, hiddenBy, layoutForce, layoutGraph, neighborsOf, relationOf, traceGraph, treeEdges };
|