@stocksharp/diagram 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -1
- package/dist/esm/canvas-renderer.js +166 -25
- package/dist/esm/canvas-renderer.js.map +1 -1
- package/dist/esm/color.js +42 -0
- package/dist/esm/color.js.map +1 -1
- package/dist/esm/core/document.js +11 -0
- package/dist/esm/core/document.js.map +1 -1
- package/dist/esm/core/model.js.map +1 -1
- package/dist/esm/diagram/stocksharp-diagram.js +23 -3
- package/dist/esm/diagram/stocksharp-diagram.js.map +1 -1
- package/dist/esm/embed.js +104 -20
- package/dist/esm/embed.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/ssdiagram-headless.js +195 -21
- package/dist/ssdiagram-headless.js.map +2 -2
- package/dist/ssdiagram.js +300 -43
- package/dist/ssdiagram.js.map +3 -3
- package/dist/types/canvas-renderer.d.ts +25 -3
- package/dist/types/canvas-renderer.d.ts.map +1 -1
- package/dist/types/color.d.ts +7 -0
- package/dist/types/color.d.ts.map +1 -1
- package/dist/types/core/model.d.ts +7 -0
- package/dist/types/core/model.d.ts.map +1 -1
- package/dist/types/diagram/api.d.ts +6 -0
- package/dist/types/diagram/api.d.ts.map +1 -1
- package/dist/types/diagram/stocksharp-diagram.d.ts +11 -0
- package/dist/types/diagram/stocksharp-diagram.d.ts.map +1 -1
- package/dist/types/embed.d.ts +21 -0
- package/dist/types/embed.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/canvas-renderer.ts +169 -25
- package/src/color.ts +48 -0
- package/src/core/document.ts +12 -0
- package/src/core/model.ts +7 -0
- package/src/diagram/api.ts +6 -0
- package/src/diagram/stocksharp-diagram.ts +27 -3
- package/src/embed.ts +147 -29
- package/src/index.ts +2 -0
package/dist/ssdiagram.js
CHANGED
|
@@ -53,6 +53,7 @@ var SSDiagram = (() => {
|
|
|
53
53
|
parseDiagramViewState: () => parseDiagramViewState,
|
|
54
54
|
renderAll: () => renderAll,
|
|
55
55
|
renderDiagramSvg: () => renderDiagramSvg,
|
|
56
|
+
renderDocument: () => renderDocument,
|
|
56
57
|
renderFromInline: () => renderFromInline,
|
|
57
58
|
renderFromSource: () => renderFromSource,
|
|
58
59
|
renderScheme: () => renderScheme,
|
|
@@ -162,10 +163,12 @@ var SSDiagram = (() => {
|
|
|
162
163
|
function normalizeNode(value, path) {
|
|
163
164
|
const input = requireStructure(value, path);
|
|
164
165
|
const id = requireIdentifier(input.id, `${path}.id`);
|
|
166
|
+
const subtitle = requireOptionalText(input.subtitle, `${path}.subtitle`);
|
|
165
167
|
return {
|
|
166
168
|
id,
|
|
167
169
|
typeId: requireIdentifier(input.typeId ?? id, `${path}.typeId`),
|
|
168
170
|
name: requireString(input.name, `${path}.name`),
|
|
171
|
+
...subtitle === "" ? {} : { subtitle },
|
|
169
172
|
description: requireString(input.description ?? "", `${path}.description`),
|
|
170
173
|
groupName: requireString(input.groupName ?? "Common", `${path}.groupName`),
|
|
171
174
|
x: requireFiniteNumber(input.x ?? 0, `${path}.x`),
|
|
@@ -216,14 +219,19 @@ var SSDiagram = (() => {
|
|
|
216
219
|
}
|
|
217
220
|
function normalizeLink(value, path) {
|
|
218
221
|
const input = requireStructure(value, path);
|
|
222
|
+
const label2 = requireOptionalText(input.label, `${path}.label`);
|
|
219
223
|
return {
|
|
220
224
|
id: requireIdentifier(input.id, `${path}.id`),
|
|
221
225
|
from: normalizeEndpoint(input.from, `${path}.from`),
|
|
222
226
|
to: normalizeEndpoint(input.to, `${path}.to`),
|
|
223
227
|
style: requireLinkStyle(input.style, `${path}.style`),
|
|
228
|
+
...label2 === "" ? {} : { label: label2 },
|
|
224
229
|
metadata: cloneJsonObject(input.metadata ?? {}, `${path}.metadata`)
|
|
225
230
|
};
|
|
226
231
|
}
|
|
232
|
+
function requireOptionalText(value, path) {
|
|
233
|
+
return value === void 0 || value === null ? "" : requireString(value, path);
|
|
234
|
+
}
|
|
227
235
|
function requireLinkStyle(value, path) {
|
|
228
236
|
if (value === void 0 || value === null) return "solid";
|
|
229
237
|
if (value !== "solid" && value !== "dashed") {
|
|
@@ -263,6 +271,7 @@ var SSDiagram = (() => {
|
|
|
263
271
|
id: requireString(node.id, `${path}.id`),
|
|
264
272
|
typeId: requireString(node.typeId, `${path}.typeId`),
|
|
265
273
|
name: requireString(node.name, `${path}.name`),
|
|
274
|
+
subtitle: node.subtitle,
|
|
266
275
|
description: requireString(node.description, `${path}.description`),
|
|
267
276
|
groupName: requireString(node.groupName, `${path}.groupName`),
|
|
268
277
|
x: requireNumber(node.x, `${path}.x`),
|
|
@@ -318,6 +327,7 @@ var SSDiagram = (() => {
|
|
|
318
327
|
from: parseEndpoint(link.from, `${path}.from`),
|
|
319
328
|
to: parseEndpoint(link.to, `${path}.to`),
|
|
320
329
|
style: link.style === void 0 ? void 0 : requireLinkStyle(link.style, `${path}.style`),
|
|
330
|
+
label: link.label,
|
|
321
331
|
metadata: cloneJsonObject(link.metadata, `${path}.metadata`)
|
|
322
332
|
}, path);
|
|
323
333
|
}
|
|
@@ -913,6 +923,42 @@ var SSDiagram = (() => {
|
|
|
913
923
|
return color;
|
|
914
924
|
return `hsl(${Math.round(hue * 10) / 10}, ${Math.round(saturation * 1e3) / 10}%, ${Math.round(maxLightness * 1e3) / 10}%)`;
|
|
915
925
|
}
|
|
926
|
+
function resolveCssVariables(value, lookup) {
|
|
927
|
+
return resolveAt(value, lookup, 0);
|
|
928
|
+
}
|
|
929
|
+
function resolveAt(value, lookup, depth) {
|
|
930
|
+
const start = value.indexOf("var(");
|
|
931
|
+
if (start < 0)
|
|
932
|
+
return value;
|
|
933
|
+
if (depth > 8)
|
|
934
|
+
return null;
|
|
935
|
+
let level = 0;
|
|
936
|
+
let end = -1;
|
|
937
|
+
for (let i = start + 3; i < value.length; i += 1) {
|
|
938
|
+
if (value[i] === "(")
|
|
939
|
+
level += 1;
|
|
940
|
+
else if (value[i] === ")" && --level === 0) {
|
|
941
|
+
end = i;
|
|
942
|
+
break;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
if (end < 0)
|
|
946
|
+
return null;
|
|
947
|
+
const inner = value.slice(start + 4, end);
|
|
948
|
+
const comma = inner.indexOf(",");
|
|
949
|
+
const name = (comma < 0 ? inner : inner.slice(0, comma)).trim();
|
|
950
|
+
const fallback = comma < 0 ? "" : inner.slice(comma + 1).trim();
|
|
951
|
+
let replacement = name.startsWith("--") ? lookup(name).trim() : "";
|
|
952
|
+
if (replacement === "")
|
|
953
|
+
replacement = fallback;
|
|
954
|
+
if (replacement === "")
|
|
955
|
+
return null;
|
|
956
|
+
const resolved = resolveAt(replacement, lookup, depth + 1);
|
|
957
|
+
const rest = resolveAt(value.slice(end + 1), lookup, depth);
|
|
958
|
+
if (resolved === null || rest === null)
|
|
959
|
+
return null;
|
|
960
|
+
return value.slice(0, start) + resolved + rest;
|
|
961
|
+
}
|
|
916
962
|
|
|
917
963
|
// src/core/history.ts
|
|
918
964
|
var DiagramCommandHistory = class {
|
|
@@ -1202,6 +1248,7 @@ var SSDiagram = (() => {
|
|
|
1202
1248
|
this.id = id;
|
|
1203
1249
|
this.typeId = init.typeId ?? id;
|
|
1204
1250
|
this.name = init.name;
|
|
1251
|
+
this.subtitle = init.subtitle ?? "";
|
|
1205
1252
|
this.description = init.description ?? "";
|
|
1206
1253
|
this.groupName = init.groupName ?? "Common";
|
|
1207
1254
|
this.color = init.color ?? "#d7d7d7";
|
|
@@ -1227,6 +1274,7 @@ var SSDiagram = (() => {
|
|
|
1227
1274
|
id: this.id,
|
|
1228
1275
|
typeId: this.typeId,
|
|
1229
1276
|
name: this.name,
|
|
1277
|
+
...this.subtitle === "" ? {} : { subtitle: this.subtitle },
|
|
1230
1278
|
description: this.description,
|
|
1231
1279
|
groupName: this.groupName,
|
|
1232
1280
|
color: this.color,
|
|
@@ -1248,12 +1296,13 @@ var SSDiagram = (() => {
|
|
|
1248
1296
|
}
|
|
1249
1297
|
};
|
|
1250
1298
|
var LinkModel = class {
|
|
1251
|
-
constructor(from, fromPort, to, toPort, id = "", metadata, style = "solid") {
|
|
1299
|
+
constructor(from, fromPort, to, toPort, id = "", metadata, style = "solid", label2 = "") {
|
|
1252
1300
|
this.from = from;
|
|
1253
1301
|
this.fromPort = fromPort;
|
|
1254
1302
|
this.to = to;
|
|
1255
1303
|
this.toPort = toPort;
|
|
1256
1304
|
this.style = style;
|
|
1305
|
+
this.label = label2;
|
|
1257
1306
|
this.id = id;
|
|
1258
1307
|
this.metadata = copyJsonObject(metadata);
|
|
1259
1308
|
}
|
|
@@ -1268,6 +1317,7 @@ var SSDiagram = (() => {
|
|
|
1268
1317
|
to: this.to,
|
|
1269
1318
|
toPort: this.toPort,
|
|
1270
1319
|
style: this.style,
|
|
1320
|
+
...this.label === "" ? {} : { label: this.label },
|
|
1271
1321
|
metadata: copyJsonObject(this.metadata)
|
|
1272
1322
|
};
|
|
1273
1323
|
}
|
|
@@ -1288,6 +1338,15 @@ var SSDiagram = (() => {
|
|
|
1288
1338
|
var INTRO_RISE = 70;
|
|
1289
1339
|
var ERROR_FLASH_MS = 1100;
|
|
1290
1340
|
var ERROR_RED = "#f6465d";
|
|
1341
|
+
var DEFAULT_NODE_COLOR = "#d7d7d7";
|
|
1342
|
+
var DEFAULT_NODE_BORDER = "#8c8c8c";
|
|
1343
|
+
var DEFAULT_ZONE_TINT = "#8a8f98";
|
|
1344
|
+
var TITLE_FONT = "600 12px Segoe UI, Tahoma, sans-serif";
|
|
1345
|
+
var SUBTITLE_FONT = "11px Segoe UI, Tahoma, sans-serif";
|
|
1346
|
+
var SUBTITLE_MIN_H = 62;
|
|
1347
|
+
var LINK_LABEL_FONT = "600 10px Segoe UI, Tahoma, sans-serif";
|
|
1348
|
+
var ZONE_CAPTION_TOP = 9;
|
|
1349
|
+
var ZONE_CAPTION_H = 14;
|
|
1291
1350
|
var ERROR_BACKGROUND = "#7d2632";
|
|
1292
1351
|
var clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
|
|
1293
1352
|
function hashHue(s) {
|
|
@@ -1385,10 +1444,14 @@ var SSDiagram = (() => {
|
|
|
1385
1444
|
this.ovGeo = null;
|
|
1386
1445
|
this.validator = null;
|
|
1387
1446
|
this.handlers = /* @__PURE__ */ new Map();
|
|
1447
|
+
// Computed style of the host, read at most once per frame: colours written as var(--token)
|
|
1448
|
+
// resolve against it, so a frame drawn after a theme switch picks up the new values.
|
|
1449
|
+
this.hostStyle = null;
|
|
1388
1450
|
this.opts = opts;
|
|
1389
1451
|
if (opts.typeColors !== void 0) this.setTypeColorsInternal(opts.typeColors);
|
|
1390
1452
|
this.host = opts.host;
|
|
1391
1453
|
this.gridSnapEnabled = opts.gridSnap ?? false;
|
|
1454
|
+
this.pageScroll = opts.pageScroll ?? false;
|
|
1392
1455
|
this.gridSize = this.normalizeGridSize(opts.gridSize ?? DEFAULT_GRID_SIZE);
|
|
1393
1456
|
this.history = new DiagramCommandHistory(() => {
|
|
1394
1457
|
this.emit("undoStackChanged", { canUndo: this.canUndo(), canRedo: this.canRedo() });
|
|
@@ -1397,7 +1460,7 @@ var SSDiagram = (() => {
|
|
|
1397
1460
|
this.canvas.style.display = "block";
|
|
1398
1461
|
this.canvas.tabIndex = 0;
|
|
1399
1462
|
this.canvas.style.outline = "none";
|
|
1400
|
-
this.
|
|
1463
|
+
this.applyTouchAction();
|
|
1401
1464
|
this.canvas.style.userSelect = "none";
|
|
1402
1465
|
this.canvas.style.webkitUserSelect = "none";
|
|
1403
1466
|
this.canvas.style.webkitTouchCallout = "none";
|
|
@@ -1451,6 +1514,19 @@ var SSDiagram = (() => {
|
|
|
1451
1514
|
this.gridSize = nextSize;
|
|
1452
1515
|
this.scheduleDraw();
|
|
1453
1516
|
}
|
|
1517
|
+
/** See DiagramOptions.pageScroll. */
|
|
1518
|
+
setPageScroll(enabled) {
|
|
1519
|
+
this.pageScroll = enabled;
|
|
1520
|
+
this.applyTouchAction();
|
|
1521
|
+
}
|
|
1522
|
+
isPageScrollEnabled() {
|
|
1523
|
+
return this.pageScroll;
|
|
1524
|
+
}
|
|
1525
|
+
// pan-y hands a vertical swipe to the browser, which scrolls the page; a horizontal one and a
|
|
1526
|
+
// two-finger pinch still reach the canvas.
|
|
1527
|
+
applyTouchAction() {
|
|
1528
|
+
this.canvas.style.touchAction = this.pageScroll ? "pan-y" : "none";
|
|
1529
|
+
}
|
|
1454
1530
|
getGridSnap() {
|
|
1455
1531
|
return { enabled: this.gridSnapEnabled, size: this.gridSize };
|
|
1456
1532
|
}
|
|
@@ -1666,7 +1742,7 @@ var SSDiagram = (() => {
|
|
|
1666
1742
|
this.emit("linkValidation", { fromNode: fn, from: fp, toNode: tn, to: tp, ...validation });
|
|
1667
1743
|
if (!validation.allowed) return false;
|
|
1668
1744
|
}
|
|
1669
|
-
const link = new LinkModel(init.from, init.fromPort, init.to, init.toPort, init.id ?? this.nextLinkId(), init.metadata, init.style ?? "solid");
|
|
1745
|
+
const link = new LinkModel(init.from, init.fromPort, init.to, init.toPort, init.id ?? this.nextLinkId(), init.metadata, init.style ?? "solid", init.label ?? "");
|
|
1670
1746
|
if (this.links.some((l) => l.id === link.id)) return false;
|
|
1671
1747
|
this.links.splice(clamp(Math.trunc(index), 0, this.links.length), 0, link);
|
|
1672
1748
|
this.emit("linkAdded", { link });
|
|
@@ -1938,6 +2014,7 @@ var SSDiagram = (() => {
|
|
|
1938
2014
|
updateNode(nodeId, patch) {
|
|
1939
2015
|
return this.updateNodeState(nodeId, "update node", (node) => {
|
|
1940
2016
|
if (patch.name !== void 0) node.name = patch.name;
|
|
2017
|
+
if (patch.subtitle !== void 0) node.subtitle = patch.subtitle;
|
|
1941
2018
|
if (patch.description !== void 0) node.description = patch.description;
|
|
1942
2019
|
if (patch.color !== void 0) node.color = patch.color;
|
|
1943
2020
|
if (patch.border !== void 0) node.border = patch.border;
|
|
@@ -1983,6 +2060,7 @@ var SSDiagram = (() => {
|
|
|
1983
2060
|
const restored = new NodeModel(snapshot, nodeId);
|
|
1984
2061
|
node.typeId = restored.typeId;
|
|
1985
2062
|
node.name = restored.name;
|
|
2063
|
+
node.subtitle = restored.subtitle;
|
|
1986
2064
|
node.description = restored.description;
|
|
1987
2065
|
node.groupName = restored.groupName;
|
|
1988
2066
|
node.color = restored.color;
|
|
@@ -2013,6 +2091,10 @@ var SSDiagram = (() => {
|
|
|
2013
2091
|
return true;
|
|
2014
2092
|
}
|
|
2015
2093
|
load(nodes, links) {
|
|
2094
|
+
this.loadModel(nodes, links, []);
|
|
2095
|
+
}
|
|
2096
|
+
// Zones are placed before the view is fitted, so the first frame frames them too.
|
|
2097
|
+
loadModel(nodes, links, zones) {
|
|
2016
2098
|
this.clear();
|
|
2017
2099
|
this.idSeq = 1;
|
|
2018
2100
|
this.linkSeq = 1;
|
|
@@ -2039,9 +2121,10 @@ var SSDiagram = (() => {
|
|
|
2039
2121
|
if (this.links.some((existing) => existing.id === id)) {
|
|
2040
2122
|
throw new Error(`ssdiagram: duplicate link id "${id}"`);
|
|
2041
2123
|
}
|
|
2042
|
-
const model = new LinkModel(link.from, link.fromPort, link.to, link.toPort, id, link.metadata, link.style ?? "solid");
|
|
2124
|
+
const model = new LinkModel(link.from, link.fromPort, link.to, link.toPort, id, link.metadata, link.style ?? "solid", link.label ?? "");
|
|
2043
2125
|
if (!this.links.some((existing) => existing.key() === model.key())) this.links.push(model);
|
|
2044
2126
|
}
|
|
2127
|
+
this.zones = zones.map((zone) => ({ ...zone, metadata: copyJsonObject(zone.metadata) }));
|
|
2045
2128
|
for (const node of this.nodes) {
|
|
2046
2129
|
if (node.loadError.length === 0) continue;
|
|
2047
2130
|
const state = createDiagramNodeRuntimeState();
|
|
@@ -2064,13 +2147,14 @@ var SSDiagram = (() => {
|
|
|
2064
2147
|
to: l.to,
|
|
2065
2148
|
toPort: l.toPort,
|
|
2066
2149
|
style: l.style,
|
|
2150
|
+
...l.label === "" ? {} : { label: l.label },
|
|
2067
2151
|
metadata: copyJsonObject(l.metadata)
|
|
2068
2152
|
}))
|
|
2069
2153
|
};
|
|
2070
2154
|
}
|
|
2071
2155
|
loadDocument(source) {
|
|
2072
2156
|
const document2 = parseDiagramDocument(source);
|
|
2073
|
-
this.
|
|
2157
|
+
this.loadModel(
|
|
2074
2158
|
document2.nodes,
|
|
2075
2159
|
document2.links.map((link) => ({
|
|
2076
2160
|
id: link.id,
|
|
@@ -2079,10 +2163,11 @@ var SSDiagram = (() => {
|
|
|
2079
2163
|
to: link.to.nodeId,
|
|
2080
2164
|
toPort: link.to.portId,
|
|
2081
2165
|
style: link.style,
|
|
2166
|
+
label: link.label,
|
|
2082
2167
|
metadata: link.metadata
|
|
2083
|
-
}))
|
|
2168
|
+
})),
|
|
2169
|
+
document2.zones
|
|
2084
2170
|
);
|
|
2085
|
-
this.zones = document2.zones.map((zone) => ({ ...zone, metadata: copyJsonObject(zone.metadata) }));
|
|
2086
2171
|
this.documentMetadata = copyJsonObject(document2.metadata);
|
|
2087
2172
|
}
|
|
2088
2173
|
saveDocument() {
|
|
@@ -2093,6 +2178,7 @@ var SSDiagram = (() => {
|
|
|
2093
2178
|
from: { nodeId: link.from, portId: link.fromPort },
|
|
2094
2179
|
to: { nodeId: link.to, portId: link.toPort },
|
|
2095
2180
|
style: link.style,
|
|
2181
|
+
label: link.label,
|
|
2096
2182
|
metadata: link.metadata
|
|
2097
2183
|
})),
|
|
2098
2184
|
zones: this.zones.map((zone) => ({ ...zone, metadata: copyJsonObject(zone.metadata) })),
|
|
@@ -2519,8 +2605,10 @@ var SSDiagram = (() => {
|
|
|
2519
2605
|
if (t2.overviewViewportFill !== void 0) this.opts.overviewViewportFill = t2.overviewViewportFill;
|
|
2520
2606
|
this.scheduleDraw();
|
|
2521
2607
|
}
|
|
2608
|
+
// Zones count: fitting or exporting only the nodes cut off the places they sit in, captions
|
|
2609
|
+
// included. A caption that is taller than its zone still has to fit.
|
|
2522
2610
|
graphBounds() {
|
|
2523
|
-
if (this.nodes.length === 0) return null;
|
|
2611
|
+
if (this.nodes.length === 0 && this.zones.length === 0) return null;
|
|
2524
2612
|
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
2525
2613
|
for (const n2 of this.nodes) {
|
|
2526
2614
|
minX = Math.min(minX, n2.x);
|
|
@@ -2528,6 +2616,13 @@ var SSDiagram = (() => {
|
|
|
2528
2616
|
maxX = Math.max(maxX, n2.x + n2.w);
|
|
2529
2617
|
maxY = Math.max(maxY, n2.y + n2.h);
|
|
2530
2618
|
}
|
|
2619
|
+
for (const zone of this.zones) {
|
|
2620
|
+
const bottom = zone.name.length > 0 ? Math.max(zone.height, ZONE_CAPTION_TOP + ZONE_CAPTION_H) : zone.height;
|
|
2621
|
+
minX = Math.min(minX, zone.x - 1);
|
|
2622
|
+
minY = Math.min(minY, zone.y - 1);
|
|
2623
|
+
maxX = Math.max(maxX, zone.x + zone.width + 1);
|
|
2624
|
+
maxY = Math.max(maxY, zone.y + bottom + 1);
|
|
2625
|
+
}
|
|
2531
2626
|
return { minX, minY, maxX, maxY };
|
|
2532
2627
|
}
|
|
2533
2628
|
resize(w, h) {
|
|
@@ -2579,11 +2674,16 @@ var SSDiagram = (() => {
|
|
|
2579
2674
|
// ---- geometry ---------------------------------------------------
|
|
2580
2675
|
layoutNode(n2) {
|
|
2581
2676
|
const ctx = this.ctx;
|
|
2582
|
-
ctx.font =
|
|
2677
|
+
ctx.font = TITLE_FONT;
|
|
2583
2678
|
const titleW = ctx.measureText(n2.name).width;
|
|
2679
|
+
let subtitleW = 0;
|
|
2680
|
+
if (n2.subtitle.length > 0) {
|
|
2681
|
+
ctx.font = SUBTITLE_FONT;
|
|
2682
|
+
subtitleW = ctx.measureText(n2.subtitle).width;
|
|
2683
|
+
}
|
|
2584
2684
|
const rows = Math.max(n2.inPorts.length, n2.outPorts.length, 1);
|
|
2585
|
-
n2.w = Math.max(170, Math.ceil(titleW) + 28);
|
|
2586
|
-
n2.h = Math.max(48, rows * PORT_ROW_H + 16);
|
|
2685
|
+
n2.w = Math.max(170, Math.ceil(titleW) + 28, Math.ceil(subtitleW) + 28);
|
|
2686
|
+
n2.h = Math.max(n2.subtitle.length > 0 ? SUBTITLE_MIN_H : 48, rows * PORT_ROW_H + 16);
|
|
2587
2687
|
const off = PORT_SQ / 2;
|
|
2588
2688
|
const place = (ports, x) => {
|
|
2589
2689
|
const stack = ports.some(isGrowAnchor) ? [...ports.filter((p) => !isGrowAnchor(p)), ...ports.filter(isGrowAnchor)] : ports;
|
|
@@ -2942,6 +3042,7 @@ var SSDiagram = (() => {
|
|
|
2942
3042
|
// The clipboard is built from saveDocument(), so it carries the style;
|
|
2943
3043
|
// rebuilding without it silently turned every pasted dashed link solid.
|
|
2944
3044
|
style: l.style,
|
|
3045
|
+
label: l.label,
|
|
2945
3046
|
metadata: l.metadata
|
|
2946
3047
|
});
|
|
2947
3048
|
}
|
|
@@ -3327,6 +3428,7 @@ var SSDiagram = (() => {
|
|
|
3327
3428
|
this.listen(window, "pointerup", finish);
|
|
3328
3429
|
this.listen(window, "pointercancel", finish);
|
|
3329
3430
|
this.listen(this.canvas, "wheel", (e) => {
|
|
3431
|
+
if (this.pageScroll && !e.ctrlKey && !e.metaKey) return;
|
|
3330
3432
|
e.preventDefault();
|
|
3331
3433
|
const [sx, sy] = localXY(e);
|
|
3332
3434
|
const [wx, wy] = this.toWorld(sx, sy);
|
|
@@ -3472,6 +3574,7 @@ var SSDiagram = (() => {
|
|
|
3472
3574
|
}
|
|
3473
3575
|
draw(options = SCREEN_RENDER_OPTIONS) {
|
|
3474
3576
|
const ctx = this.ctx;
|
|
3577
|
+
this.hostStyle = null;
|
|
3475
3578
|
ctx.clearRect(0, 0, this.width, this.height);
|
|
3476
3579
|
const background = this.opts.background ?? "#1b1b1f";
|
|
3477
3580
|
ctx.fillStyle = background;
|
|
@@ -3499,6 +3602,7 @@ var SSDiagram = (() => {
|
|
|
3499
3602
|
);
|
|
3500
3603
|
this.drawZones();
|
|
3501
3604
|
const prior = [];
|
|
3605
|
+
const labels = [];
|
|
3502
3606
|
const hoveredNode = this.hoverPort?.node ?? this.hoverNode;
|
|
3503
3607
|
for (const l of this.links) {
|
|
3504
3608
|
const a = this.endpoint(l, "from");
|
|
@@ -3512,6 +3616,7 @@ var SSDiagram = (() => {
|
|
|
3512
3616
|
const pts = this.routeLink(a, b, /* @__PURE__ */ new Set([l.from, l.to]));
|
|
3513
3617
|
this.strokeRoute(pts, color, width, prior, l.style);
|
|
3514
3618
|
this.drawArrow(pts, color);
|
|
3619
|
+
if (l.label.length > 0) labels.push({ pts, text: l.label, color });
|
|
3515
3620
|
for (let k = 1; k < pts.length; k += 1) {
|
|
3516
3621
|
const [x1, y1] = pts[k - 1];
|
|
3517
3622
|
const [x2, y2] = pts[k];
|
|
@@ -3519,6 +3624,7 @@ var SSDiagram = (() => {
|
|
|
3519
3624
|
else if (y1 === y2 && x1 !== x2) prior.push({ h: true, c: y1, a: Math.min(x1, x2), b: Math.max(x1, x2) });
|
|
3520
3625
|
}
|
|
3521
3626
|
}
|
|
3627
|
+
for (const label2 of labels) this.drawLinkLabel(label2.pts, label2.text, label2.color, background);
|
|
3522
3628
|
if (options.transient && (this.linking !== null || this.relinking !== null)) this.drawPendingLink();
|
|
3523
3629
|
this.portFeed = this.feedTypes();
|
|
3524
3630
|
for (const n2 of this.nodes) this.drawNode(n2, options.selection && this.selectedNodes.has(n2), options);
|
|
@@ -3673,8 +3779,9 @@ ${runtime.error}`;
|
|
|
3673
3779
|
drawZones() {
|
|
3674
3780
|
if (this.zones.length === 0) return;
|
|
3675
3781
|
const ctx = this.ctx;
|
|
3782
|
+
const defaultTint = this.resolveColor(this.opts.zoneColor ?? DEFAULT_ZONE_TINT, DEFAULT_ZONE_TINT);
|
|
3676
3783
|
for (const zone of this.zones) {
|
|
3677
|
-
const tint = zone.color.length > 0 ? zone.color :
|
|
3784
|
+
const tint = zone.color.length > 0 ? this.resolveColor(zone.color, defaultTint) : defaultTint;
|
|
3678
3785
|
ctx.save();
|
|
3679
3786
|
roundRect(ctx, zone.x, zone.y, zone.width, zone.height, 10);
|
|
3680
3787
|
ctx.globalAlpha = 0.12;
|
|
@@ -3690,10 +3797,10 @@ ${runtime.error}`;
|
|
|
3690
3797
|
ctx.save();
|
|
3691
3798
|
ctx.globalAlpha = 0.75;
|
|
3692
3799
|
ctx.fillStyle = tint;
|
|
3693
|
-
ctx.font =
|
|
3800
|
+
ctx.font = TITLE_FONT;
|
|
3694
3801
|
ctx.textAlign = "left";
|
|
3695
3802
|
ctx.textBaseline = "top";
|
|
3696
|
-
ctx.fillText(zone.name, zone.x + 12, zone.y +
|
|
3803
|
+
ctx.fillText(zone.name, zone.x + 12, zone.y + ZONE_CAPTION_TOP, Math.max(0, zone.width - 24));
|
|
3697
3804
|
ctx.restore();
|
|
3698
3805
|
}
|
|
3699
3806
|
}
|
|
@@ -3738,11 +3845,12 @@ ${runtime.error}`;
|
|
|
3738
3845
|
const hasRuntimeError = options.runtime && n2.runtimeError.length > 0;
|
|
3739
3846
|
const runtime = options.runtime ? getJsonKey(this.runtimeState.nodes, n2.id) : void 0;
|
|
3740
3847
|
const active = options.runtime && (this.runtimeState.activeNodeId === n2.id || runtime?.active === true);
|
|
3848
|
+
const fill = this.resolveColor(n2.color, DEFAULT_NODE_COLOR);
|
|
3741
3849
|
roundRect(ctx, n2.x, n2.y, n2.w, n2.h, 6);
|
|
3742
|
-
ctx.fillStyle = hasLoadError ? ERROR_BACKGROUND : active ? "#ffd1dc" :
|
|
3850
|
+
ctx.fillStyle = hasLoadError ? ERROR_BACKGROUND : active ? "#ffd1dc" : fill;
|
|
3743
3851
|
ctx.fill();
|
|
3744
3852
|
ctx.lineWidth = selected ? 2 : 1.5;
|
|
3745
|
-
ctx.strokeStyle = selected ? this.selectionColor() : n2.border;
|
|
3853
|
+
ctx.strokeStyle = selected ? this.selectionColor() : this.resolveColor(n2.border, DEFAULT_NODE_BORDER);
|
|
3746
3854
|
ctx.stroke();
|
|
3747
3855
|
if (hasLoadError || hasRuntimeError) {
|
|
3748
3856
|
let alpha = 1;
|
|
@@ -3775,11 +3883,22 @@ ${runtime.error}`;
|
|
|
3775
3883
|
}
|
|
3776
3884
|
}
|
|
3777
3885
|
const titleShift = n2.icon ? iconW + 4 : 0;
|
|
3778
|
-
|
|
3779
|
-
|
|
3886
|
+
const textX = n2.x + titleShift + (n2.w - titleShift) / 2;
|
|
3887
|
+
const textWidth = n2.w - titleShift - 14;
|
|
3888
|
+
ctx.fillStyle = hasLoadError ? "#ffffff" : readableTextOn(active ? "#ffd1dc" : fill);
|
|
3889
|
+
ctx.font = TITLE_FONT;
|
|
3780
3890
|
ctx.textAlign = "center";
|
|
3781
3891
|
ctx.textBaseline = "middle";
|
|
3782
|
-
|
|
3892
|
+
if (n2.subtitle.length === 0) {
|
|
3893
|
+
ctx.fillText(n2.name, textX, n2.y + n2.h / 2, textWidth);
|
|
3894
|
+
} else {
|
|
3895
|
+
ctx.fillText(n2.name, textX, n2.y + n2.h / 2 - 8, textWidth);
|
|
3896
|
+
ctx.save();
|
|
3897
|
+
ctx.globalAlpha *= 0.72;
|
|
3898
|
+
ctx.font = SUBTITLE_FONT;
|
|
3899
|
+
ctx.fillText(n2.subtitle, textX, n2.y + n2.h / 2 + 9, textWidth);
|
|
3900
|
+
ctx.restore();
|
|
3901
|
+
}
|
|
3783
3902
|
for (const p of n2.inPorts) this.drawPort(n2, p, options);
|
|
3784
3903
|
for (const p of n2.outPorts) this.drawPort(n2, p, options);
|
|
3785
3904
|
}
|
|
@@ -3896,6 +4015,45 @@ ${runtime.error}`;
|
|
|
3896
4015
|
ctx.fill();
|
|
3897
4016
|
ctx.restore();
|
|
3898
4017
|
}
|
|
4018
|
+
// A plate of the canvas colour under the text, edged in the wire's colour: the label reads the same
|
|
4019
|
+
// whatever the wire crosses, and still says which wire it belongs to.
|
|
4020
|
+
drawLinkLabel(pts, text, color, background) {
|
|
4021
|
+
const [x, y] = pathMidpoint(pts);
|
|
4022
|
+
const ctx = this.ctx;
|
|
4023
|
+
ctx.save();
|
|
4024
|
+
ctx.font = LINK_LABEL_FONT;
|
|
4025
|
+
const w = Math.ceil(ctx.measureText(text).width) + 12;
|
|
4026
|
+
const h = 16;
|
|
4027
|
+
roundRect(ctx, x - w / 2, y - h / 2, w, h, h / 2);
|
|
4028
|
+
ctx.fillStyle = background;
|
|
4029
|
+
ctx.fill();
|
|
4030
|
+
ctx.lineWidth = 1;
|
|
4031
|
+
ctx.strokeStyle = color;
|
|
4032
|
+
ctx.stroke();
|
|
4033
|
+
ctx.fillStyle = readableTextOn(background);
|
|
4034
|
+
ctx.textAlign = "center";
|
|
4035
|
+
ctx.textBaseline = "middle";
|
|
4036
|
+
ctx.fillText(text, x, y);
|
|
4037
|
+
ctx.restore();
|
|
4038
|
+
}
|
|
4039
|
+
// Scheme colours may name a custom property of the page, var(--token), which a canvas cannot
|
|
4040
|
+
// paint. They resolve against the host element; one that resolves to nothing and has no
|
|
4041
|
+
// fallback of its own gets the renderer's default for that slot.
|
|
4042
|
+
resolveColor(value, fallback) {
|
|
4043
|
+
if (!value.includes("var(")) return value;
|
|
4044
|
+
return resolveCssVariables(value, (name) => this.hostProperty(name)) ?? fallback;
|
|
4045
|
+
}
|
|
4046
|
+
hostProperty(name) {
|
|
4047
|
+
if (this.hostStyle === null) {
|
|
4048
|
+
if (typeof getComputedStyle !== "function") return "";
|
|
4049
|
+
try {
|
|
4050
|
+
this.hostStyle = getComputedStyle(this.host);
|
|
4051
|
+
} catch {
|
|
4052
|
+
return "";
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
4055
|
+
return this.hostStyle.getPropertyValue(name);
|
|
4056
|
+
}
|
|
3899
4057
|
drawSelectedLinkEndpoints() {
|
|
3900
4058
|
if (this.selectedLink === null) return;
|
|
3901
4059
|
const ctx = this.ctx;
|
|
@@ -4011,9 +4169,9 @@ ${runtime.error}`;
|
|
|
4011
4169
|
ctx.stroke();
|
|
4012
4170
|
ctx.clip();
|
|
4013
4171
|
for (const n2 of this.nodes) {
|
|
4014
|
-
ctx.fillStyle = n2.color;
|
|
4172
|
+
ctx.fillStyle = this.resolveColor(n2.color, DEFAULT_NODE_COLOR);
|
|
4015
4173
|
ctx.fillRect(mx(n2.x), my(n2.y), Math.max(2, n2.w * s), Math.max(2, n2.h * s));
|
|
4016
|
-
ctx.strokeStyle = n2.border;
|
|
4174
|
+
ctx.strokeStyle = this.resolveColor(n2.border, DEFAULT_NODE_BORDER);
|
|
4017
4175
|
ctx.lineWidth = 1;
|
|
4018
4176
|
ctx.strokeRect(mx(n2.x), my(n2.y), Math.max(2, n2.w * s), Math.max(2, n2.h * s));
|
|
4019
4177
|
}
|
|
@@ -4054,6 +4212,23 @@ ${runtime.error}`;
|
|
|
4054
4212
|
ctx.arcTo(x, y, x + w, y, rr);
|
|
4055
4213
|
ctx.closePath();
|
|
4056
4214
|
}
|
|
4215
|
+
function pathMidpoint(pts) {
|
|
4216
|
+
let total = 0;
|
|
4217
|
+
for (let i = 1; i < pts.length; i += 1) total += Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1]);
|
|
4218
|
+
let remaining = total / 2;
|
|
4219
|
+
for (let i = 1; i < pts.length; i += 1) {
|
|
4220
|
+
const [x1, y1] = pts[i - 1];
|
|
4221
|
+
const [x2, y2] = pts[i];
|
|
4222
|
+
const length = Math.hypot(x2 - x1, y2 - y1);
|
|
4223
|
+
if (length > 0 && remaining <= length) {
|
|
4224
|
+
const t2 = remaining / length;
|
|
4225
|
+
return [x1 + (x2 - x1) * t2, y1 + (y2 - y1) * t2];
|
|
4226
|
+
}
|
|
4227
|
+
remaining -= length;
|
|
4228
|
+
}
|
|
4229
|
+
const last = pts[pts.length - 1];
|
|
4230
|
+
return [last[0], last[1]];
|
|
4231
|
+
}
|
|
4057
4232
|
function route(a, b) {
|
|
4058
4233
|
const [ax, ay] = a;
|
|
4059
4234
|
const [bx, by] = b;
|
|
@@ -5038,6 +5213,7 @@ ${runtime.error}`;
|
|
|
5038
5213
|
this.fullscreen = false;
|
|
5039
5214
|
this.fullscreenButtonVisible = true;
|
|
5040
5215
|
this.downloadButtonVisible = false;
|
|
5216
|
+
this.exportEnabled = true;
|
|
5041
5217
|
this.downloadLabel = null;
|
|
5042
5218
|
// null means the host never named the button, so its wording comes from the shared bundle.
|
|
5043
5219
|
// Keeping the two apart is what lets an explicit label win while an untouched control still
|
|
@@ -5054,7 +5230,8 @@ ${runtime.error}`;
|
|
|
5054
5230
|
host: this.div,
|
|
5055
5231
|
typeColors: this.portTypeColors(),
|
|
5056
5232
|
gridSnap: options.gridSnap ?? true,
|
|
5057
|
-
gridSize: options.gridSize
|
|
5233
|
+
gridSize: options.gridSize,
|
|
5234
|
+
pageScroll: options.pageScroll ?? false
|
|
5058
5235
|
});
|
|
5059
5236
|
this.fullscreenButtonVisible = options.showFullscreenButton ?? true;
|
|
5060
5237
|
this.fullscreenLabels = options.fullscreenLabels ?? null;
|
|
@@ -5132,6 +5309,13 @@ ${runtime.error}`;
|
|
|
5132
5309
|
getGridSnap() {
|
|
5133
5310
|
return this.canvas.getGridSnap();
|
|
5134
5311
|
}
|
|
5312
|
+
/** See DiagramOptions.pageScroll. */
|
|
5313
|
+
setPageScroll(enabled) {
|
|
5314
|
+
this.canvas.setPageScroll(enabled);
|
|
5315
|
+
}
|
|
5316
|
+
isPageScrollEnabled() {
|
|
5317
|
+
return this.canvas.isPageScrollEnabled();
|
|
5318
|
+
}
|
|
5135
5319
|
nudgeSelection(dx, dy) {
|
|
5136
5320
|
return this.canvas.nudgeSelection(dx, dy);
|
|
5137
5321
|
}
|
|
@@ -5313,6 +5497,17 @@ ${runtime.error}`;
|
|
|
5313
5497
|
this.downloadButton.hidden = false;
|
|
5314
5498
|
this.downloadButton.style.display = "inline-flex";
|
|
5315
5499
|
}
|
|
5500
|
+
/**
|
|
5501
|
+
* Turns the export commands of the context menu on or off. Off, the menu offers them greyed out and
|
|
5502
|
+
* `executeContextCommand` refuses them, for a page that does not want its diagram saved. The
|
|
5503
|
+
* download button is separate: hide it with `setDownloadButtonVisible(false)`.
|
|
5504
|
+
*/
|
|
5505
|
+
setExportEnabled(enabled) {
|
|
5506
|
+
this.exportEnabled = enabled;
|
|
5507
|
+
}
|
|
5508
|
+
isExportEnabled() {
|
|
5509
|
+
return this.exportEnabled;
|
|
5510
|
+
}
|
|
5316
5511
|
isDownloadButtonVisible() {
|
|
5317
5512
|
return this.downloadButtonVisible;
|
|
5318
5513
|
}
|
|
@@ -5814,15 +6009,15 @@ ${runtime.error}`;
|
|
|
5814
6009
|
// picture, and an empty diagram has none to give. They stay on in read-only mode,
|
|
5815
6010
|
// which forbids changing the diagram, not looking at it.
|
|
5816
6011
|
exportDocument: {
|
|
5817
|
-
canExecute: ({ document: document2 }) => document2.nodes.length > 0,
|
|
6012
|
+
canExecute: ({ document: document2 }) => this.exportEnabled && document2.nodes.length > 0,
|
|
5818
6013
|
execute: () => this.emit("exportRequested", { format: "document" })
|
|
5819
6014
|
},
|
|
5820
6015
|
exportPng: {
|
|
5821
|
-
canExecute: ({ document: document2 }) => document2.nodes.length > 0,
|
|
6016
|
+
canExecute: ({ document: document2 }) => this.exportEnabled && document2.nodes.length > 0,
|
|
5822
6017
|
execute: () => this.emit("exportRequested", { format: "png" })
|
|
5823
6018
|
},
|
|
5824
6019
|
exportSvg: {
|
|
5825
|
-
canExecute: ({ document: document2 }) => document2.nodes.length > 0,
|
|
6020
|
+
canExecute: ({ document: document2 }) => this.exportEnabled && document2.nodes.length > 0,
|
|
5826
6021
|
execute: () => this.emit("exportRequested", { format: "svg" })
|
|
5827
6022
|
},
|
|
5828
6023
|
// Always available: it shows the overview as readily as it hides it, and an empty
|
|
@@ -6458,17 +6653,62 @@ ${runtime.error}`;
|
|
|
6458
6653
|
if (renderRevisions.get(div) !== revision)
|
|
6459
6654
|
return null;
|
|
6460
6655
|
const catalog = buildCatalog(palette);
|
|
6656
|
+
return mountDiagram(div, catalog, options, false, (diagram) => {
|
|
6657
|
+
const missingTypeTemplate = div.dataset.diagramMissingElement ?? t("embedMissingElement", 'Element type "{typeId}" is missing from the palette.');
|
|
6658
|
+
const converted = toDiagramNodes(
|
|
6659
|
+
scheme,
|
|
6660
|
+
catalog,
|
|
6661
|
+
(typeId) => missingTypeTemplate.replace("{typeId}", typeId || "(empty)")
|
|
6662
|
+
);
|
|
6663
|
+
diagram.load(
|
|
6664
|
+
converted.nodes,
|
|
6665
|
+
scheme.links.map((link) => new Link({
|
|
6666
|
+
outNode: link.from,
|
|
6667
|
+
outPort: link.fromPort,
|
|
6668
|
+
inNode: link.to,
|
|
6669
|
+
inPort: link.toPort
|
|
6670
|
+
})),
|
|
6671
|
+
{ nodeErrors: converted.nodeErrors }
|
|
6672
|
+
);
|
|
6673
|
+
});
|
|
6674
|
+
}
|
|
6675
|
+
function renderDocument(div, source, options = {}) {
|
|
6676
|
+
const parsed = readDocument(source);
|
|
6677
|
+
beginRender(div);
|
|
6678
|
+
return mountDocument(div, parsed, options);
|
|
6679
|
+
}
|
|
6680
|
+
function readDocument(source) {
|
|
6681
|
+
let value = source;
|
|
6682
|
+
if (typeof source === "string") {
|
|
6683
|
+
try {
|
|
6684
|
+
value = JSON.parse(source);
|
|
6685
|
+
} catch (error) {
|
|
6686
|
+
throw new DiagramDocumentError(error instanceof Error ? error.message : "invalid JSON");
|
|
6687
|
+
}
|
|
6688
|
+
}
|
|
6689
|
+
if (!isRecord(value))
|
|
6690
|
+
throw new DiagramDocumentError("expected an object");
|
|
6691
|
+
if (value.version !== void 0 && value.version !== DIAGRAM_DOCUMENT_VERSION)
|
|
6692
|
+
throw new DiagramDocumentError(`unsupported document version ${String(value.version)}`, "$.version");
|
|
6693
|
+
return createDiagramDocument(value);
|
|
6694
|
+
}
|
|
6695
|
+
function mountDocument(div, source, options) {
|
|
6696
|
+
return mountDiagram(div, new StockSharpCatalog(), options, true, (diagram) => diagram.loadDocument(source));
|
|
6697
|
+
}
|
|
6698
|
+
function mountDiagram(div, catalog, options, pageScrollByDefault, populate) {
|
|
6461
6699
|
disposeActiveRender(div);
|
|
6462
6700
|
div.classList.remove("ss-diagram-error");
|
|
6463
6701
|
div.replaceChildren();
|
|
6464
6702
|
const [enterLabel, exitLabel] = (div.dataset.diagramFullscreen ?? "").split("|");
|
|
6465
6703
|
const downloadLabel = div.dataset.diagramDownload;
|
|
6466
|
-
const
|
|
6704
|
+
const exportAllowed = options.allowExport !== false && div.dataset.diagramExport !== "off";
|
|
6705
|
+
const showDownloadButton = exportAllowed && options.onExportRequested !== void 0;
|
|
6467
6706
|
let diagram;
|
|
6468
6707
|
try {
|
|
6469
6708
|
diagram = new StockSharpDiagram({
|
|
6470
6709
|
div,
|
|
6471
6710
|
catalog,
|
|
6711
|
+
pageScroll: options.pageScroll ?? pageScrollByDefault,
|
|
6472
6712
|
...enterLabel && exitLabel ? { fullscreenLabels: { enter: enterLabel, exit: exitLabel } } : {},
|
|
6473
6713
|
...showDownloadButton ? { showDownloadButton: true } : {},
|
|
6474
6714
|
...downloadLabel ? { downloadLabel } : {}
|
|
@@ -6518,7 +6758,9 @@ ${runtime.error}`;
|
|
|
6518
6758
|
activeRenders.set(div, handle);
|
|
6519
6759
|
connectedRenders.add(handle);
|
|
6520
6760
|
ensureDisconnectedHostObserver();
|
|
6521
|
-
if (
|
|
6761
|
+
if (!exportAllowed) {
|
|
6762
|
+
diagram.setExportEnabled(false);
|
|
6763
|
+
} else if (options.onExportRequested !== void 0) {
|
|
6522
6764
|
const notifyHost = options.onExportRequested;
|
|
6523
6765
|
cleanups.push(diagram.on("exportRequested", (request) => notifyHost(request, handle)));
|
|
6524
6766
|
}
|
|
@@ -6545,22 +6787,7 @@ ${runtime.error}`;
|
|
|
6545
6787
|
diagram.setReadOnly(true);
|
|
6546
6788
|
diagram.setLinkValidator(() => true);
|
|
6547
6789
|
diagram.setOverviewVisible(false);
|
|
6548
|
-
|
|
6549
|
-
const converted = toDiagramNodes(
|
|
6550
|
-
scheme,
|
|
6551
|
-
catalog,
|
|
6552
|
-
(typeId) => missingTypeTemplate.replace("{typeId}", typeId || "(empty)")
|
|
6553
|
-
);
|
|
6554
|
-
diagram.load(
|
|
6555
|
-
converted.nodes,
|
|
6556
|
-
scheme.links.map((link) => new Link({
|
|
6557
|
-
outNode: link.from,
|
|
6558
|
-
outPort: link.fromPort,
|
|
6559
|
-
inNode: link.to,
|
|
6560
|
-
inPort: link.toPort
|
|
6561
|
-
})),
|
|
6562
|
-
{ nodeErrors: converted.nodeErrors }
|
|
6563
|
-
);
|
|
6790
|
+
populate(diagram);
|
|
6564
6791
|
const fit = () => {
|
|
6565
6792
|
if (!div.isConnected) {
|
|
6566
6793
|
handle.destroy();
|
|
@@ -6657,6 +6884,8 @@ ${runtime.error}`;
|
|
|
6657
6884
|
note(div, errors.load, revision);
|
|
6658
6885
|
return null;
|
|
6659
6886
|
}
|
|
6887
|
+
if (holdsDocument(div, raw))
|
|
6888
|
+
return drawDocumentAtRevision(div, raw, revision, options, errors);
|
|
6660
6889
|
const scheme = parseRawScheme(raw);
|
|
6661
6890
|
if (scheme.nodes.length === 0) {
|
|
6662
6891
|
note(div, errors.empty, revision);
|
|
@@ -6679,6 +6908,8 @@ ${runtime.error}`;
|
|
|
6679
6908
|
note(div, errors.empty, revision);
|
|
6680
6909
|
return null;
|
|
6681
6910
|
}
|
|
6911
|
+
if (holdsDocument(div, raw))
|
|
6912
|
+
return drawDocumentAtRevision(div, raw, revision, options, errors);
|
|
6682
6913
|
const scheme = parseRawScheme(raw);
|
|
6683
6914
|
if (scheme.nodes.length === 0) {
|
|
6684
6915
|
note(div, errors.empty, revision);
|
|
@@ -6691,6 +6922,32 @@ ${runtime.error}`;
|
|
|
6691
6922
|
return null;
|
|
6692
6923
|
}
|
|
6693
6924
|
}
|
|
6925
|
+
function holdsDocument(div, raw) {
|
|
6926
|
+
if (div.dataset.diagramKind === "document")
|
|
6927
|
+
return true;
|
|
6928
|
+
return isRecord(raw) && typeof raw.version === "number" && Array.isArray(raw.nodes);
|
|
6929
|
+
}
|
|
6930
|
+
function drawDocumentAtRevision(div, raw, revision, options, errors) {
|
|
6931
|
+
if (renderRevisions.get(div) !== revision)
|
|
6932
|
+
return null;
|
|
6933
|
+
let parsed;
|
|
6934
|
+
try {
|
|
6935
|
+
parsed = readDocument(raw);
|
|
6936
|
+
} catch {
|
|
6937
|
+
note(div, errors.empty, revision);
|
|
6938
|
+
return null;
|
|
6939
|
+
}
|
|
6940
|
+
if (parsed.nodes.length === 0 && parsed.zones.length === 0) {
|
|
6941
|
+
note(div, errors.empty, revision);
|
|
6942
|
+
return null;
|
|
6943
|
+
}
|
|
6944
|
+
try {
|
|
6945
|
+
return mountDocument(div, parsed, options);
|
|
6946
|
+
} catch {
|
|
6947
|
+
note(div, errors.draw, revision);
|
|
6948
|
+
return null;
|
|
6949
|
+
}
|
|
6950
|
+
}
|
|
6694
6951
|
function renderAll(root = document, options = {}) {
|
|
6695
6952
|
root.querySelectorAll(".ss-diagram-host").forEach((host) => {
|
|
6696
6953
|
if (host.dataset.rendered === "1")
|