@grafloria/element 0.3.23 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +9 -7
  2. package/package.json +7 -9
  3. package/src/index.d.ts +16 -16
  4. package/src/index.js +58 -339
  5. package/src/lib/dashboard-kit/dashboard.d.ts +1 -1
  6. package/src/lib/dashboard-kit/dashboard.js +22 -26
  7. package/src/lib/dashboard-kit/grid-binder.d.ts +1 -1
  8. package/src/lib/dashboard-kit/grid-binder.js +62 -65
  9. package/src/lib/dashboard-kit/grid-mapping.js +14 -26
  10. package/src/lib/dashboard-kit/index.d.ts +5 -5
  11. package/src/lib/dashboard-kit/index.js +5 -30
  12. package/src/lib/dashboard-kit/styles.js +4 -8
  13. package/src/lib/dashboard-kit/widgets.d.ts +1 -1
  14. package/src/lib/dashboard-kit/widgets.js +16 -26
  15. package/src/lib/diagram-kit/card.d.ts +2 -2
  16. package/src/lib/diagram-kit/card.js +22 -32
  17. package/src/lib/diagram-kit/editing.js +15 -20
  18. package/src/lib/diagram-kit/er.d.ts +1 -1
  19. package/src/lib/diagram-kit/er.js +15 -18
  20. package/src/lib/diagram-kit/handles.d.ts +4 -4
  21. package/src/lib/diagram-kit/handles.js +16 -29
  22. package/src/lib/diagram-kit/index.d.ts +10 -10
  23. package/src/lib/diagram-kit/index.js +10 -50
  24. package/src/lib/diagram-kit/join-guidance.js +9 -18
  25. package/src/lib/diagram-kit/rows.js +1 -4
  26. package/src/lib/diagram-kit/styles.js +4 -8
  27. package/src/lib/diagram-kit/uml.js +10 -13
  28. package/src/lib/diagram-kit/update.d.ts +1 -1
  29. package/src/lib/diagram-kit/update.js +19 -26
  30. package/src/lib/grafloria-flow-element.js +19 -24
  31. package/src/lib/grafloria.d.ts +4 -4
  32. package/src/lib/grafloria.js +12 -17
  33. package/src/lib/load.d.ts +3 -3
  34. package/src/lib/load.js +19 -22
  35. package/src/lib/node-type-registry.js +6 -14
  36. package/src/lib/stencil-kit/builders.js +4 -10
  37. package/src/lib/stencil-kit/card-builders.js +14 -17
  38. package/src/lib/stencil-kit/index.d.ts +7 -7
  39. package/src/lib/stencil-kit/index.js +6 -16
  40. package/src/lib/stencil-kit/palette.js +14 -17
  41. package/src/lib/stencil-kit/shape-data.js +25 -28
  42. package/src/lib/stencil-kit/styles.js +1 -4
  43. package/esm/src/index.js +0 -295
  44. package/esm/src/lib/dashboard-kit/dashboard.js +0 -590
  45. package/esm/src/lib/dashboard-kit/grid-binder.js +0 -1633
  46. package/esm/src/lib/dashboard-kit/grid-mapping.js +0 -164
  47. package/esm/src/lib/dashboard-kit/index.js +0 -8
  48. package/esm/src/lib/dashboard-kit/styles.js +0 -205
  49. package/esm/src/lib/dashboard-kit/widgets.js +0 -379
  50. package/esm/src/lib/diagram-kit/card.js +0 -199
  51. package/esm/src/lib/diagram-kit/editing.js +0 -448
  52. package/esm/src/lib/diagram-kit/er.js +0 -160
  53. package/esm/src/lib/diagram-kit/handles.js +0 -312
  54. package/esm/src/lib/diagram-kit/index.js +0 -15
  55. package/esm/src/lib/diagram-kit/join-guidance.js +0 -322
  56. package/esm/src/lib/diagram-kit/rows.js +0 -144
  57. package/esm/src/lib/diagram-kit/styles.js +0 -118
  58. package/esm/src/lib/diagram-kit/uml.js +0 -135
  59. package/esm/src/lib/diagram-kit/update.js +0 -244
  60. package/esm/src/lib/grafloria-flow-element.js +0 -266
  61. package/esm/src/lib/grafloria.js +0 -69
  62. package/esm/src/lib/load.js +0 -252
  63. package/esm/src/lib/node-type-registry.js +0 -42
  64. package/esm/src/lib/stencil-kit/builders.js +0 -18
  65. package/esm/src/lib/stencil-kit/card-builders.js +0 -105
  66. package/esm/src/lib/stencil-kit/index.js +0 -9
  67. package/esm/src/lib/stencil-kit/palette.js +0 -427
  68. package/esm/src/lib/stencil-kit/shape-data.js +0 -537
  69. package/esm/src/lib/stencil-kit/styles.js +0 -126
@@ -1,266 +0,0 @@
1
- import { createDiagram, DARK_THEME, LIGHT_THEME } from '@grafloria/renderer';
2
- import { getNodeType, renderFromTemplate } from './node-type-registry';
3
- /**
4
- * `<grafloria-flow>` — the universal embed.
5
- *
6
- * React Flow serves React. ngx-vflow serves Angular. Each is a wall around one
7
- * framework. A custom element has no wall: Vue, Svelte, Solid, Lit, Alpine,
8
- * plain HTML, a CMS block, a Jupyter/Observable cell and a static site all speak
9
- * "HTML element with attributes and events". This is the piece that lets Grafloria
10
- * reach the long tail those libraries cannot.
11
- *
12
- * ```html
13
- * <grafloria-flow theme="dark" fit-view
14
- * nodes='[{"id":"a","position":{"x":0,"y":0},"label":"A"}]'
15
- * edges='[{"source":"a","target":"b"}]'>
16
- * <template data-node-type="card">
17
- * <div class="card"><h4 data-field="title"></h4></div>
18
- * </template>
19
- * </grafloria-flow>
20
- *
21
- * <script>
22
- * document.querySelector('grafloria-flow')
23
- * .addEventListener('grafloria-connect', e => console.log(e.detail.link));
24
- * </script>
25
- * ```
26
- *
27
- * Rich data goes in as PROPERTIES (`el.nodes = [...]`) and simple data as
28
- * ATTRIBUTES (JSON strings) — the standard custom-element contract that every
29
- * framework's template binding already targets:
30
- * `:nodes` in Vue, `nodes={...}` in Svelte/Solid, `[attr.nodes]` in Angular.
31
- *
32
- * Everything it does is delegation. There is no diagram logic in this file.
33
- */
34
- const THEMES = {
35
- light: LIGHT_THEME,
36
- dark: DARK_THEME,
37
- };
38
- /** Events emitted on the element. All bubble and cross shadow boundaries. */
39
- export const GRAFLORIA_EVENTS = {
40
- ready: 'grafloria-ready',
41
- nodesChange: 'grafloria-nodes-change',
42
- edgesChange: 'grafloria-edges-change',
43
- selectionChange: 'grafloria-selection-change',
44
- connect: 'grafloria-connect',
45
- nodeClick: 'grafloria-node-click',
46
- edgeClick: 'grafloria-edge-click',
47
- viewportChange: 'grafloria-viewport-change',
48
- };
49
- // wave11/gallery BUG FIX — worker-safe base class.
50
- //
51
- // `class GrafloriaFlowElement extends HTMLElement` evaluates `HTMLElement` at MODULE
52
- // LOAD, so merely IMPORTING this package threw `HTMLElement is not defined` in any
53
- // context without a DOM — a Web Worker, or Node. The package doc already promised
54
- // the import is "a no-op on the server", and `defineGrafloriaFlow()` guards
55
- // `customElements`; but the class declaration itself did not, so the promise was
56
- // only half true. It matters concretely: the off-thread-layout demo runs the
57
- // engine's `serveLayout` INSIDE a real Worker by importing this very bundle, which
58
- // is impossible if the top-level class reference throws. The element only ever
59
- // EXTENDS the real HTMLElement in a browser (a worker never instantiates it), so a
60
- // guarded base is behaviourally identical on the client and merely importable off it.
61
- const HTMLElementBase = typeof HTMLElement !== 'undefined'
62
- ? HTMLElement
63
- : class {
64
- };
65
- export class GrafloriaFlowElement extends HTMLElementBase {
66
- constructor() {
67
- super(...arguments);
68
- this.instance = null;
69
- this.canvas = null;
70
- this._nodes = [];
71
- this._edges = [];
72
- this.connected = false;
73
- }
74
- static get observedAttributes() {
75
- return [
76
- 'nodes',
77
- 'edges',
78
- 'theme',
79
- 'fit-view',
80
- 'readonly',
81
- 'zoom',
82
- 'min-zoom',
83
- 'max-zoom',
84
- 'pan',
85
- 'wheel-zoom',
86
- ];
87
- }
88
- // -- properties (the rich path) ---------------------------------------------
89
- get nodes() {
90
- return this._nodes;
91
- }
92
- set nodes(value) {
93
- var _a;
94
- this._nodes = value !== null && value !== void 0 ? value : [];
95
- (_a = this.instance) === null || _a === void 0 ? void 0 : _a.setNodes(this._nodes);
96
- }
97
- get edges() {
98
- return this._edges;
99
- }
100
- set edges(value) {
101
- var _a;
102
- this._edges = value !== null && value !== void 0 ? value : [];
103
- (_a = this.instance) === null || _a === void 0 ? void 0 : _a.setEdges(this._edges);
104
- }
105
- /** The headless instance — the escape hatch to everything else. */
106
- get diagram() {
107
- return this.instance;
108
- }
109
- // -- lifecycle ---------------------------------------------------------------
110
- connectedCallback() {
111
- if (this.connected)
112
- return;
113
- this.connected = true;
114
- // Attributes may have been parsed before we upgraded.
115
- this.readAttributeModel();
116
- // Light DOM, not shadow DOM: the diagram's stylesheet is injected into
117
- // <head> and its CSS variables cascade — a shadow root would cut both off,
118
- // and hosts routinely want to style nodes from their own stylesheet.
119
- this.canvas = document.createElement('div');
120
- this.canvas.className = 'grafloria-flow-canvas';
121
- this.canvas.setAttribute('style', 'position:relative;width:100%;height:100%');
122
- this.appendChild(this.canvas);
123
- if (!this.style.display)
124
- this.style.display = 'block';
125
- this.instance = createDiagram(this.canvas, this.buildOptions());
126
- this.wireEvents(this.instance);
127
- }
128
- disconnectedCallback() {
129
- var _a, _b;
130
- this.connected = false;
131
- (_a = this.instance) === null || _a === void 0 ? void 0 : _a.dispose();
132
- this.instance = null;
133
- (_b = this.canvas) === null || _b === void 0 ? void 0 : _b.remove();
134
- this.canvas = null;
135
- }
136
- attributeChangedCallback(name, previous, next) {
137
- var _a, _b;
138
- if (previous === next)
139
- return;
140
- switch (name) {
141
- case 'nodes':
142
- this._nodes = parseJsonAttribute(next, []);
143
- (_a = this.instance) === null || _a === void 0 ? void 0 : _a.setNodes(this._nodes);
144
- return;
145
- case 'edges':
146
- this._edges = parseJsonAttribute(next, []);
147
- (_b = this.instance) === null || _b === void 0 ? void 0 : _b.setEdges(this._edges);
148
- return;
149
- case 'theme':
150
- if (this.instance)
151
- this.instance.setTheme(this.resolveTheme());
152
- return;
153
- case 'fit-view':
154
- if (this.instance && next !== null)
155
- this.instance.fitView();
156
- return;
157
- case 'zoom':
158
- if (this.instance && next !== null)
159
- this.instance.viewport.setZoom(Number(next));
160
- return;
161
- default:
162
- // pan / wheel-zoom / readonly / min-zoom / max-zoom are read at mount:
163
- // they configure the event binder, which is created once. Changing them
164
- // afterwards is rare enough that we do not tear the instance down.
165
- return;
166
- }
167
- }
168
- // -- API ---------------------------------------------------------------------
169
- fitView(padding) {
170
- var _a;
171
- (_a = this.instance) === null || _a === void 0 ? void 0 : _a.fitView(padding);
172
- }
173
- // -- internals ---------------------------------------------------------------
174
- buildOptions() {
175
- return {
176
- nodes: this._nodes,
177
- edges: this._edges,
178
- theme: this.resolveTheme(),
179
- fitView: this.hasAttribute('fit-view'),
180
- readonly: this.hasAttribute('readonly'),
181
- enablePan: this.getAttribute('pan') !== 'false',
182
- enableZoom: this.getAttribute('wheel-zoom') !== 'false',
183
- zoom: this.hasAttribute('zoom') ? Number(this.getAttribute('zoom')) : undefined,
184
- minZoom: this.hasAttribute('min-zoom')
185
- ? Number(this.getAttribute('min-zoom'))
186
- : undefined,
187
- maxZoom: this.hasAttribute('max-zoom')
188
- ? Number(this.getAttribute('max-zoom'))
189
- : undefined,
190
- renderCustomNode: (node, element) => this.renderCustomNode(node, element),
191
- };
192
- }
193
- /**
194
- * Custom nodes, two ways and no framework: a registered renderer
195
- * (`Grafloria.registerNodeType`), or a slotted `<template data-node-type="…">`.
196
- */
197
- renderCustomNode(node, element) {
198
- const renderer = getNodeType(node.type);
199
- if (renderer) {
200
- renderer(node, element);
201
- return;
202
- }
203
- const template = this.querySelector(`template[data-node-type="${cssEscape(node.type)}"]`);
204
- if (template) {
205
- renderFromTemplate(template, node, element);
206
- return;
207
- }
208
- // Nothing registered: leave the host empty rather than throwing inside a
209
- // render. The node still exists, is selectable and is draggable.
210
- }
211
- resolveTheme() {
212
- var _a, _b;
213
- return (_b = THEMES[(_a = this.getAttribute('theme')) !== null && _a !== void 0 ? _a : 'light']) !== null && _b !== void 0 ? _b : LIGHT_THEME;
214
- }
215
- readAttributeModel() {
216
- if (this._nodes.length === 0 && this.hasAttribute('nodes')) {
217
- this._nodes = parseJsonAttribute(this.getAttribute('nodes'), []);
218
- }
219
- if (this._edges.length === 0 && this.hasAttribute('edges')) {
220
- this._edges = parseJsonAttribute(this.getAttribute('edges'), []);
221
- }
222
- }
223
- /** Instance events → DOM CustomEvents. `composed` so they escape a shadow root. */
224
- wireEvents(instance) {
225
- const forward = (type, detail) => {
226
- this.dispatchEvent(new CustomEvent(type, { detail, bubbles: true, composed: true }));
227
- };
228
- instance.on('ready', () => forward(GRAFLORIA_EVENTS.ready, { diagram: instance }));
229
- instance.on('nodes:change', (payload) => forward(GRAFLORIA_EVENTS.nodesChange, payload));
230
- instance.on('edges:change', (payload) => forward(GRAFLORIA_EVENTS.edgesChange, payload));
231
- instance.on('selection:change', (payload) => forward(GRAFLORIA_EVENTS.selectionChange, payload));
232
- instance.on('connect', (payload) => forward(GRAFLORIA_EVENTS.connect, payload));
233
- instance.on('node:click', (payload) => forward(GRAFLORIA_EVENTS.nodeClick, payload));
234
- instance.on('edge:click', (payload) => forward(GRAFLORIA_EVENTS.edgeClick, payload));
235
- instance.on('viewport:change', (payload) => forward(GRAFLORIA_EVENTS.viewportChange, payload));
236
- }
237
- }
238
- /** JSON attribute → value. A malformed attribute must not take the page down. */
239
- function parseJsonAttribute(raw, fallback) {
240
- if (!raw)
241
- return fallback;
242
- try {
243
- return JSON.parse(raw);
244
- }
245
- catch (_a) {
246
- console.warn('[grafloria-flow] ignoring malformed JSON attribute:', raw);
247
- return fallback;
248
- }
249
- }
250
- /** Minimal CSS.escape for the attribute selector (jsdom/older browsers lack it). */
251
- function cssEscape(value) {
252
- return value.replace(/["\\]/g, '\\$&');
253
- }
254
- /**
255
- * Register the element. Idempotent, and safe to call on the server (where
256
- * `customElements` does not exist) — which is what lets a bundle be imported
257
- * from an SSR entry point without a `typeof window` dance at every call site.
258
- */
259
- export function defineGrafloriaFlow(tagName = 'grafloria-flow') {
260
- if (typeof customElements === 'undefined')
261
- return;
262
- if (customElements.get(tagName))
263
- return;
264
- customElements.define(tagName, GrafloriaFlowElement);
265
- }
266
- //# sourceMappingURL=grafloria-flow-element.js.map
@@ -1,69 +0,0 @@
1
- import { createDiagram, renderToStaticSVG } from '@grafloria/renderer';
2
- import { defineGrafloriaFlow } from './grafloria-flow-element';
3
- import { registerNodeType, registeredNodeTypes, getNodeType } from './node-type-registry';
4
- /**
5
- * Mount `spec` into `target` and return the live instance.
6
- *
7
- * `target` may be an element or a CSS selector. Custom nodes (`custom: true`)
8
- * are rendered by the types registered with {@link registerNodeType}.
9
- *
10
- * SCOPE, stated plainly: `spec` is data (an object or its JSON), not a Mermaid-
11
- * style text DSL. The engine does have a DSL, but wiring it in is a separate
12
- * card — `render()` is the embedding surface, not a parser.
13
- */
14
- export function render(spec, target, options = {}) {
15
- var _a, _b, _c, _d;
16
- const element = typeof target === 'string'
17
- ? document.querySelector(target)
18
- : target;
19
- if (!element) {
20
- throw new Error(`Grafloria.render: no element matched ${JSON.stringify(target)}`);
21
- }
22
- // Kit specs (dashboard()) carry looser node typing than DiagramSpec — both
23
- // flow into createDiagram's NodeInput[] the same way.
24
- const parsed = (typeof spec === 'string' ? parseSpec(spec) : spec);
25
- const instance = createDiagram(element, Object.assign(Object.assign({}, options), { nodes: (_a = parsed.nodes) !== null && _a !== void 0 ? _a : [], edges: (_b = parsed.edges) !== null && _b !== void 0 ? _b : [],
26
- // Wire the global registry in, so `registerNodeType` works for the tiny API
27
- // exactly as it does for `<grafloria-flow>` — unless the caller supplies their
28
- // own. A KIT SPEC may also carry its own painter (dashboard() does: every
29
- // widget is a custom HTML node), and it must be honoured — otherwise the
30
- // documented one-liner `render(dashboard({…}), host)` mounts a board whose
31
- // widgets never paint. Precedence: explicit option > spec > registry.
32
- renderCustomNode: (_d = (_c = options.renderCustomNode) !== null && _c !== void 0 ? _c : parsed.renderCustomNode) !== null && _d !== void 0 ? _d : ((node, host) => { var _a; return (_a = getNodeType(node.type)) === null || _a === void 0 ? void 0 : _a(node, host); }) }));
33
- // Diagram-kit specs (erDiagram/umlDiagram) carry a `finalize(api)` for the
34
- // post-render wiring that needs the LIVE instance — row interactions,
35
- // multiplicity chips. Auto-run it so a kit diagram is fully wired from one
36
- // render() call; finalize is idempotent, so a caller may still call it too.
37
- const maybeFinalize = spec.finalize;
38
- if (typeof maybeFinalize === 'function') {
39
- try {
40
- maybeFinalize(instance);
41
- }
42
- catch (_e) {
43
- /* a kit's finalize must never break the mount */
44
- }
45
- }
46
- return instance;
47
- }
48
- /** Server-side render (Card 6). Re-exported so the tiny API is self-contained. */
49
- export function renderStatic(options = {}) {
50
- return renderToStaticSVG(options);
51
- }
52
- function parseSpec(spec) {
53
- try {
54
- return JSON.parse(spec);
55
- }
56
- catch (error) {
57
- throw new Error(`Grafloria.render: spec must be an object or a JSON string (${error.message})`);
58
- }
59
- }
60
- /** The namespace object, for `import { Grafloria }` and for `<script>` globals. */
61
- export const Grafloria = {
62
- render,
63
- renderStatic,
64
- registerNodeType,
65
- registeredNodeTypes,
66
- /** Register `<grafloria-flow>` (called for you when you import this package). */
67
- define: defineGrafloriaFlow,
68
- };
69
- //# sourceMappingURL=grafloria.js.map
@@ -1,252 +0,0 @@
1
- /**
2
- * `fromDocument()` — the LOAD front door.
3
- *
4
- * ---------------------------------------------------------------------------
5
- * THE GAP THIS CLOSES
6
- * ---------------------------------------------------------------------------
7
- * Saving was never the problem. `DiagramSerializer.serialize()` already emits a
8
- * document that round-trips byte-identically, carrying nodes, links, ports,
9
- * groups and every scrap of kit metadata. LOADING was the problem, in three
10
- * layers:
11
- *
12
- * 1. THERE WAS NO FRONT DOOR. `CreateDiagramOptions` takes `nodes`/`edges`;
13
- * nothing took a saved document. `deserialize()` handed back a
14
- * `DiagramModel` that no entry point would render.
15
- * 2. THE KITS' POST-RENDER WIRING NEVER RAN. `erDiagram()`/`umlDiagram()` do
16
- * their interaction wiring in `finalize(api)` — row selection, in-canvas
17
- * editing, the suppressed resize handles. A document has no `finalize`, so
18
- * a "successfully loaded" ERD was a PICTURE of an ERD: it looked right and
19
- * did nothing.
20
- * 3. CUSTOM NODES HAD NO PAINTER. `renderCustomNode` is a function; functions
21
- * do not serialize. A loaded dashboard mounted 14 widget hosts and painted
22
- * into none of them.
23
- *
24
- * ---------------------------------------------------------------------------
25
- * WHY THE NODES GO BACK IN AS LIVE MODELS
26
- * ---------------------------------------------------------------------------
27
- * The obvious implementation projects every loaded `NodeModel` back down into a
28
- * `NodeSpec` and lets `buildNode()` rebuild it. That is a LOSSY detour, and it
29
- * loses exactly the things a saved diagram is made of: `toNodeSpec()` carries
30
- * id/position/size/data/label/shape and nothing else — no ports, no metadata,
31
- * no behavior. An ER card would come back with four default side ports instead
32
- * of its field ports, and every FK→PK edge would silently reroute.
33
- *
34
- * It is also unnecessary. `applyNodes()`/`applyEdges()` have always accepted a
35
- * live `NodeModel`/`LinkModel` and pass it through untouched — the documented
36
- * "mix data with your own model" seam. So the loaded models ARE the input, and
37
- * nothing is projected, converted or lost.
38
- *
39
- * ---------------------------------------------------------------------------
40
- * HOW PAINTERS COME BACK
41
- * ---------------------------------------------------------------------------
42
- * ER and UML need NOTHING: their card is `metadata.html`, a structured tree the
43
- * renderer paints itself, and metadata round-trips. Measured, not assumed — a
44
- * loaded ER document paints byte-identical cards with no painter in sight.
45
- *
46
- * Dashboard widgets are custom HTML nodes and do need one. The kit REBUILDS the
47
- * widget spec from the node's own metadata (`widgetKind` / `widgetSpec` /
48
- * `widgetTitle`) and hands it to the same `defaultWidgetRenderer` the authoring
49
- * path uses, so a loaded board is drawn by the identical code.
50
- *
51
- * Precedence, and why:
52
- *
53
- * caller's `renderCustomNode` — an explicit override outranks everything.
54
- * caller's `renderWidget` — the app's own chart painter; a board that was
55
- * authored with one must be reloaded with it.
56
- * the kit's own painter — but ONLY for nodes the kit actually stamped.
57
- * {@link getNodeType} registry — everything else, exactly as `render()` does.
58
- *
59
- * The kit claims a node by its METADATA, never by `type === 'widget'` alone.
60
- * Hijacking a type name would mean any unrelated diagram with a node called
61
- * "widget" got dashboard chrome painted over it; requiring the stamp the kit
62
- * itself wrote keeps the registry the general seam it is meant to be.
63
- */
64
- import { DiagramSerializer } from '@grafloria/engine';
65
- import { getNodeType } from './node-type-registry';
66
- import { bindRowInteractions } from './diagram-kit/rows';
67
- import { bindCardEditing } from './diagram-kit/editing';
68
- import { ensureDiagramKitStyles } from './diagram-kit/styles';
69
- import { bindDashboardGrid } from './dashboard-kit/grid-binder';
70
- import { ensureDashboardKitStyles } from './dashboard-kit/styles';
71
- import { defaultWidgetRenderer } from './dashboard-kit/widgets';
72
- import { createDashboardHandle, } from './dashboard-kit/dashboard';
73
- /** True when the node is an ER entity card or a UML class card. */
74
- function isDiagramKitCard(node) {
75
- return node.getMetadata('kitEntity') !== undefined || node.getMetadata('kitClass') !== undefined;
76
- }
77
- /**
78
- * Rebuild the widget spec the kit's renderers eat, from the node's own metadata.
79
- * Returns null for a node the dashboard kit did not stamp — see the header on
80
- * why the type name alone is not enough.
81
- */
82
- function widgetSpecOf(node) {
83
- var _a;
84
- const kind = node.getMetadata('widgetKind');
85
- if (kind === undefined)
86
- return null;
87
- const title = node.getMetadata('widgetTitle');
88
- return Object.assign(Object.assign({ id: node.id, kind: kind }, (typeof title === 'string' ? { title } : {})), { data: ((_a = node.getMetadata('widgetSpec')) !== null && _a !== void 0 ? _a : {}), span: node.getMetadata('columnSpan'), rows: node.getMetadata('rowSpan') });
89
- }
90
- /**
91
- * Turn a saved document back into something `render()` can mount.
92
- *
93
- * ```ts
94
- * const json = JSON.stringify(new DiagramSerializer().serialize(api.getModel()));
95
- * // …later, in a fresh page:
96
- * render(fromDocument(json), host);
97
- * ```
98
- *
99
- * Accepts the flat serializer form, the portable envelope, or the JSON string
100
- * of either.
101
- */
102
- export function fromDocument(document, options = {}) {
103
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
104
- const parsed = typeof document === 'string' ? parseDocument(document) : document;
105
- const model = new DiagramSerializer().deserialize(parsed);
106
- const nodes = model.getNodes();
107
- const groups = model.getGroups();
108
- const boards = new Map();
109
- // The kits inject their stylesheet from their builder; a load never calls one,
110
- // so an un-styled card would come back as unstyled divs. Only for documents
111
- // that actually contain that kit's nodes.
112
- if (nodes.some(isDiagramKitCard))
113
- ensureDiagramKitStyles();
114
- if (nodes.some((n) => widgetSpecOf(n) !== null))
115
- ensureDashboardKitStyles();
116
- const paintWidget = (_a = options.renderWidget) !== null && _a !== void 0 ? _a : defaultWidgetRenderer;
117
- // -- reconstruct the dashboard handle's context from the loaded model -------
118
- // Every board group carries `dashboardBoard` geometry; its widget members
119
- // carry `widgetSpec`/title/span/rows — the canonical, lossless source for
120
- // widgets (the load.ts principle that non-widget nodes go back as LIVE models
121
- // is untouched; only widgets are rebuilt from metadata, exactly as the
122
- // renderCustomNode painter already does). That is enough to build the SAME
123
- // handle dashboard() does, through the SAME builder — no drifting twin.
124
- const dashGroups = groups.filter((g) => g.getMetadata('dashboardBoard') !== undefined);
125
- const specById = new Map();
126
- const viewOfWidget = new Map();
127
- const ctxViews = dashGroups.map((g) => {
128
- var _a, _b, _c;
129
- const board = g.getMetadata('dashboardBoard');
130
- const widgets = [];
131
- for (const memberId of (_a = g.members) !== null && _a !== void 0 ? _a : []) {
132
- const node = model.getNode(memberId);
133
- const ws = node ? widgetSpecOf(node) : null;
134
- if (!ws)
135
- continue; // non-widget members (e.g. a nested slab group) are not widgets
136
- specById.set(ws.id, ws);
137
- viewOfWidget.set(ws.id, g.id);
138
- widgets.push(ws);
139
- }
140
- return { id: g.id, name: g.name, widgets, columns: board.columns, width: (_b = g.size) === null || _b === void 0 ? void 0 : _b.width, height: (_c = g.size) === null || _c === void 0 ? void 0 : _c.height };
141
- });
142
- const firstBoard = (_b = dashGroups[0]) === null || _b === void 0 ? void 0 : _b.getMetadata('dashboardBoard');
143
- // The active view is the one the save left ON camera (x≈0); the others were
144
- // parked far off-screen by showView. Falls back to the first board when
145
- // positions are ambiguous (e.g. a single view, or positions not restored).
146
- const activeGroup = (_c = dashGroups.find((g) => g.position.x > -1000)) !== null && _c !== void 0 ? _c : dashGroups[0];
147
- const ctx = {
148
- views: ctxViews,
149
- groups: new Map(dashGroups.map((g) => [g.id, g])),
150
- // The SAME map the LoadedDiagramSpec exposes as `boards` — derived, not a copy.
151
- binders: boards,
152
- specById,
153
- viewOfWidget,
154
- hosts: new Map(),
155
- renderWidget: paintWidget,
156
- columns: (_d = firstBoard === null || firstBoard === void 0 ? void 0 : firstBoard.columns) !== null && _d !== void 0 ? _d : 12,
157
- gap: (_e = firstBoard === null || firstBoard === void 0 ? void 0 : firstBoard.gap) !== null && _e !== void 0 ? _e : 8,
158
- rowHeight: (_f = firstBoard === null || firstBoard === void 0 ? void 0 : firstBoard.baseRowHeight) !== null && _f !== void 0 ? _f : 130,
159
- boardW: (_j = (_h = (_g = dashGroups[0]) === null || _g === void 0 ? void 0 : _g.size) === null || _h === void 0 ? void 0 : _h.width) !== null && _j !== void 0 ? _j : 1180,
160
- boardH: (_m = (_l = (_k = dashGroups[0]) === null || _k === void 0 ? void 0 : _k.size) === null || _l === void 0 ? void 0 : _l.height) !== null && _m !== void 0 ? _m : 660,
161
- // responsive is NOT in the document (a runtime seam), so it is deliberately
162
- // absent from the round-trip; width/height/columns/gap/sizing/float/rtl are.
163
- optionsBase: firstBoard
164
- ? {
165
- columns: firstBoard.columns,
166
- gap: firstBoard.gap,
167
- rowHeight: firstBoard.baseRowHeight,
168
- sizing: firstBoard.sizing,
169
- float: firstBoard.float,
170
- rtl: firstBoard.rtl,
171
- width: (_p = (_o = dashGroups[0]) === null || _o === void 0 ? void 0 : _o.size) === null || _p === void 0 ? void 0 : _p.width,
172
- height: (_r = (_q = dashGroups[0]) === null || _q === void 0 ? void 0 : _q.size) === null || _r === void 0 ? void 0 : _r.height,
173
- }
174
- : {},
175
- active: (_s = activeGroup === null || activeGroup === void 0 ? void 0 : activeGroup.id) !== null && _s !== void 0 ? _s : 'main',
176
- apiRef: null,
177
- };
178
- const handle = createDashboardHandle(ctx);
179
- const renderCustomNode = (node, host) => {
180
- var _a, _b;
181
- if (options.renderCustomNode)
182
- return options.renderCustomNode(node, host);
183
- // Prefer the ctx spec object so a later handle.update()/repaint() mutates the
184
- // SAME object this initial paint used; fall back to a fresh rebuild for a
185
- // loose widget node that belongs to no reconstructed board.
186
- const widget = (_a = specById.get(node.id)) !== null && _a !== void 0 ? _a : widgetSpecOf(node);
187
- if (widget) {
188
- ctx.hosts.set(node.id, host); // captured so update()/repaint() can find the host
189
- return paintWidget(widget, host);
190
- }
191
- (_b = getNodeType(node.type)) === null || _b === void 0 ? void 0 : _b(node, host);
192
- };
193
- const finalize = (api) => {
194
- var _a, _b, _c, _d;
195
- const a = api;
196
- if (!a)
197
- return;
198
- // -- groups ---------------------------------------------------------------
199
- // Groups are not part of `nodes`/`edges`, so nothing else would carry them
200
- // across. A dashboard without its view group is a set of loose widgets: no
201
- // board frame, and nothing for the grid binder to bind to.
202
- const live = (_a = a.getModel) === null || _a === void 0 ? void 0 : _a.call(a);
203
- if (live) {
204
- for (const group of groups) {
205
- if (!((_b = live.getGroup) === null || _b === void 0 ? void 0 : _b.call(live, group.id)))
206
- (_c = live.addGroup) === null || _c === void 0 ? void 0 : _c.call(live, group);
207
- }
208
- }
209
- if (options.interactive === false)
210
- return;
211
- // -- ER / UML -------------------------------------------------------------
212
- const cards = nodes.filter(isDiagramKitCard);
213
- if (cards.length > 0 && a.container) {
214
- for (const node of cards) {
215
- // The card draws its own selection ring; the node's resize handles would
216
- // frame it a second time. `erDiagram().finalize` does this on the live
217
- // model for the same reason — and behaviour is NOT in the document.
218
- (_d = node.setBehavior) === null || _d === void 0 ? void 0 : _d.call(node, { resizable: false });
219
- }
220
- // `rowSelection: false` is recorded only when it is the non-default, so an
221
- // ordinary document stays byte-identical to what it always was.
222
- if (!cards.some((n) => n.getMetadata('kitRowSelection') === false)) {
223
- bindRowInteractions(a);
224
- }
225
- if (cards.some((n) => n.getMetadata('kitEditable') === true)) {
226
- bindCardEditing(a);
227
- }
228
- }
229
- // -- dashboard boards -----------------------------------------------------
230
- // Wire the render API into the handle's boxed cell, then bind each board.
231
- // `bindDashboardGrid` sync()s on construction, so the handle's cellOf/toJSON
232
- // work immediately — no camera move, no showView, so the paint is byte-for-
233
- // byte what a boards-only load produced.
234
- ctx.apiRef = a;
235
- for (const group of groups) {
236
- const board = group.getMetadata('dashboardBoard');
237
- if (!board)
238
- continue;
239
- boards.set(group.id, bindDashboardGrid(a, group, Object.assign({}, board)));
240
- }
241
- };
242
- return { nodes, edges: model.getLinks(), renderCustomNode, finalize, model, boards, handle };
243
- }
244
- function parseDocument(json) {
245
- try {
246
- return JSON.parse(json);
247
- }
248
- catch (error) {
249
- throw new Error(`fromDocument: not a saved diagram (${error.message})`);
250
- }
251
- }
252
- //# sourceMappingURL=load.js.map
@@ -1,42 +0,0 @@
1
- const registry = new Map();
2
- /** Register (or replace) a node type globally. */
3
- export function registerNodeType(type, renderer) {
4
- registry.set(type, renderer);
5
- }
6
- export function getNodeType(type) {
7
- return registry.get(type);
8
- }
9
- export function hasNodeType(type) {
10
- return registry.has(type);
11
- }
12
- /** Drop a registration (mostly for tests). */
13
- export function unregisterNodeType(type) {
14
- registry.delete(type);
15
- }
16
- /** Every registered type name. */
17
- export function registeredNodeTypes() {
18
- return [...registry.keys()];
19
- }
20
- /**
21
- * Render a node from a slotted `<template data-node-type="...">`.
22
- *
23
- * Clones the template's content into `element` and substitutes `node.data` into
24
- * every `[data-field="key"]` descendant's text. Values are written with
25
- * `textContent`, never `innerHTML`: a diagram's `data` is frequently
26
- * user-supplied, and a template engine that injected raw HTML here would be an
27
- * XSS vector in every host that embeds us.
28
- */
29
- export function renderFromTemplate(template, node, element) {
30
- const fragment = template.content.cloneNode(true);
31
- const fields = fragment.querySelectorAll('[data-field]');
32
- for (const field of Array.from(fields)) {
33
- const key = field.getAttribute('data-field');
34
- if (!key)
35
- continue;
36
- const value = key === 'id' ? node.id : node.data[key];
37
- field.textContent = value === undefined || value === null ? '' : String(value);
38
- }
39
- element.textContent = '';
40
- element.appendChild(fragment);
41
- }
42
- //# sourceMappingURL=node-type-registry.js.map
@@ -1,18 +0,0 @@
1
- const builders = new Map();
2
- /** Register the builder for a master id. Last registration wins. */
3
- export function registerStencilBuilder(masterId, builder) {
4
- builders.set(masterId, builder);
5
- }
6
- /** The builder for a master id, or undefined for the default template path. */
7
- export function getStencilBuilder(masterId) {
8
- return builders.get(masterId);
9
- }
10
- /** Drop a registration (tests, or a host replacing a built-in). */
11
- export function unregisterStencilBuilder(masterId) {
12
- return builders.delete(masterId);
13
- }
14
- /** Every master id that currently resolves to a card builder. */
15
- export function registeredStencilBuilders() {
16
- return [...builders.keys()].sort();
17
- }
18
- //# sourceMappingURL=builders.js.map