@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,105 +0,0 @@
1
- /**
2
- * Card builders for the stencil palette — the bridge that finally lets a
3
- * dropped "Entity" or "Class" be a REAL kit card instead of a rectangle.
4
- *
5
- * Everything here is reuse. `entityCardContent` / `classCardContent` and their
6
- * auto-height helpers are the same functions `erDiagram()` / `umlDiagram()`
7
- * use, so a dropped card is byte-for-byte the card those kits produce — which
8
- * means the row-selection layer (`axk:row-select`), the inline editors and the
9
- * `erTable(api, id)` / `umlClass(api, id)` handles all work on it for free.
10
- *
11
- * The kit's own bindings are idempotent per container, so binding them again
12
- * after a drop is safe and is what makes the FIRST dropped card interactive on
13
- * a canvas that was never built by a kit.
14
- */
15
- import { NodeModel } from '@grafloria/engine';
16
- import { entityCardContent, entityAutoHeight, classCardContent, classAutoHeight, bindRowInteractions, bindCardEditing, ensureDiagramKitStyles, } from '../diagram-kit';
17
- import { registerStencilBuilder } from './builders';
18
- /** Cards are transparent: the HTML body IS the card, the shape must not paint. */
19
- const CARD_SHAPE = { type: 'rect', fill: 'none', stroke: 'none' };
20
- const CARD_STYLE = { fill: 'transparent', stroke: 'transparent', strokeWidth: 0 };
21
- let seq = 0;
22
- const nextId = (prefix) => `${prefix}-${++seq}`;
23
- /** Turn the kit's card spec into a live node, and make the canvas interactive. */
24
- function mountCard(ctx, opts) {
25
- var _a, _b, _c, _d;
26
- ensureDiagramKitStyles();
27
- const node = new NodeModel({
28
- id: opts.id,
29
- type: 'kit-card',
30
- position: { x: ctx.at.x, y: ctx.at.y },
31
- size: { width: opts.width, height: opts.height },
32
- });
33
- // The SAME metadata shape erDiagram()/umlDiagram() stamp — that is what the
34
- // row and editing layers key off.
35
- node.setMetadata('html', { content: opts.content, interactive: true });
36
- node.setMetadata(opts.metaKey, opts.spec);
37
- node.setMetadata('kitEditable', true);
38
- node.setMetadata('shape', CARD_SHAPE);
39
- (_a = node.setStyle) === null || _a === void 0 ? void 0 : _a.call(node, CARD_STYLE);
40
- ctx.api.getModel().addNode(node);
41
- // Bind the kit runtime for this container. Both are idempotent, so a canvas
42
- // that already ran a kit's finalize() is unaffected, and one that never did
43
- // becomes editable on the first drop.
44
- const container = (_b = ctx.api.container) !== null && _b !== void 0 ? _b : (_d = (_c = ctx.api).getContainer) === null || _d === void 0 ? void 0 : _d.call(_c);
45
- if (container) {
46
- const kitApi = Object.assign(Object.assign({}, ctx.api), { container });
47
- try {
48
- bindRowInteractions(kitApi);
49
- }
50
- catch ( /* optional */_e) { /* optional */ }
51
- try {
52
- bindCardEditing(kitApi);
53
- }
54
- catch ( /* optional */_f) { /* optional */ }
55
- }
56
- return node;
57
- }
58
- /** Register the built-in card builders (ER entity/table, UML classifiers). */
59
- export function registerCardBuilders() {
60
- for (const masterId of ['erd-entity', 'erd-table', 'erd-associative-entity', 'erd-bridge-entity']) {
61
- registerStencilBuilder(masterId, (ctx) => {
62
- var _a, _b;
63
- const id = nextId('entity');
64
- const spec = {
65
- id,
66
- name: (_b = (_a = ctx.master.meta) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : 'Entity',
67
- columns: [
68
- { name: 'id', type: 'uuid', pk: true },
69
- { name: 'name', type: 'varchar' },
70
- ],
71
- };
72
- return mountCard(ctx, {
73
- id,
74
- width: 220,
75
- height: entityAutoHeight(spec, true),
76
- content: entityCardContent(spec, true),
77
- metaKey: 'kitEntity',
78
- spec,
79
- });
80
- });
81
- }
82
- for (const masterId of ['uml-class', 'uml-abstract-class', 'uml-interface']) {
83
- registerStencilBuilder(masterId, (ctx) => {
84
- var _a, _b;
85
- const id = nextId('class');
86
- const spec = {
87
- id,
88
- name: (_b = (_a = ctx.master.meta) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : 'Class',
89
- abstract: masterId === 'uml-abstract-class',
90
- stereotype: masterId === 'uml-interface' ? 'interface' : undefined,
91
- attributes: ['- field: Type'],
92
- methods: ['+ method(): void'],
93
- };
94
- return mountCard(ctx, {
95
- id,
96
- width: 220,
97
- height: classAutoHeight(spec, true),
98
- content: classCardContent(spec, true),
99
- metaKey: 'kitClass',
100
- spec,
101
- });
102
- });
103
- }
104
- }
105
- //# sourceMappingURL=card-builders.js.map
@@ -1,9 +0,0 @@
1
- export { bindStencilPalette } from './palette';
2
- export { bindShapeDataPanel } from './shape-data';
3
- export { ensureStencilKitStyles } from './styles';
4
- export { registerStencilBuilder, getStencilBuilder, unregisterStencilBuilder, registeredStencilBuilders, } from './builders';
5
- import { registerCardBuilders } from './card-builders';
6
- // The ER/UML card builders ship registered — dropping "Entity" gives a real,
7
- // editable table out of the box.
8
- registerCardBuilders();
9
- //# sourceMappingURL=index.js.map
@@ -1,427 +0,0 @@
1
- import { __awaiter } from "tslib";
2
- /**
3
- * The stencil palette — the signature Visio surface: a categorized, searchable
4
- * list of shape masters you DRAG ONTO THE CANVAS.
5
- *
6
- * Two halves, deliberately in one module because they share the drag contract:
7
- * T6 the palette UI — sections from `listStencils()`, live search, and a
8
- * thumbnail drawn from the master's OWN outline
9
- * geometry (`getShape(type).outline(w,h)`), so what
10
- * you see in the palette is the shape you get.
11
- * T7 drag-to-place — HTML5 drag-and-drop (not pointer events: the canvas
12
- * binder consumes pointerdown, and DnD is the gesture
13
- * browsers already route across two elements). The
14
- * drop lands the master at the cursor as ONE undoable
15
- * BatchCommand.
16
- */
17
- import { listStencils, NodeFactory, AddNodeCommand, BatchCommand, } from '@grafloria/engine';
18
- import { getShape } from '@grafloria/renderer';
19
- import { ensureStencilKitStyles } from './styles';
20
- import { getStencilBuilder } from './builders';
21
- const SVG_NS = 'http://www.w3.org/2000/svg';
22
- /** The MIME the palette drags with — namespaced so a page's other DnD is untouched. */
23
- const DND_TYPE = 'application/x-grafloria-master';
24
- /**
25
- * Repaint a placed master from the host's per-notation palette.
26
- *
27
- * Colour used to live only in the 80 generated templates, so restyling meant
28
- * editing template data — impossible for an embedder. This applies the caller's
29
- * scheme on top, and `'theme'` resolves against the live theme so a colour-mode
30
- * swap carries the stencils with it.
31
- */
32
- function applyNotationTheme(node, stencilId, scheme, api) {
33
- var _a, _b, _c, _d, _e;
34
- if (!stencilId || !scheme)
35
- return;
36
- const want = scheme[stencilId];
37
- if (!want)
38
- return;
39
- const shape = Object.assign({}, ((_b = (_a = node.getMetadata) === null || _a === void 0 ? void 0 : _a.call(node, 'shape')) !== null && _b !== void 0 ? _b : {}));
40
- const theme = (_e = (_d = (_c = api).getTheme) === null || _d === void 0 ? void 0 : _d.call(_c)) !== null && _e !== void 0 ? _e : null;
41
- const resolve = (v, token) => {
42
- var _a, _b, _c, _d;
43
- return v === 'theme'
44
- ? (_d = (token === 'surface' ? (_b = (_a = theme === null || theme === void 0 ? void 0 : theme.colors) === null || _a === void 0 ? void 0 : _a.background) === null || _b === void 0 ? void 0 : _b.paper : (_c = theme === null || theme === void 0 ? void 0 : theme.colors) === null || _c === void 0 ? void 0 : _c.primary)) !== null && _d !== void 0 ? _d : undefined
45
- : v;
46
- };
47
- const fill = resolve(want.fill, 'surface');
48
- const stroke = resolve(want.stroke, 'ink');
49
- if (fill !== undefined)
50
- shape.fill = fill;
51
- if (stroke !== undefined)
52
- shape.stroke = stroke;
53
- node.setMetadata('shape', shape);
54
- }
55
- /** UML classifiers get a name compartment + a member compartment.
56
- * Keys are MASTER IDS — `uml-enum` / `uml-primitive-type` are the generated
57
- * ids (the old `uml-enumeration` / `uml-primitivetype` keys matched nothing,
58
- * so those two masters dropped as bare rectangles with no card). */
59
- const UML_CLASSIFIERS = {
60
- 'uml-class': null, 'uml-abstract-class': '«abstract»', 'uml-interface': '«interface»',
61
- 'uml-enum': '«enumeration»', 'uml-datatype': '«dataType»',
62
- 'uml-primitive-type': '«primitive»', 'uml-signal': '«signal»', 'uml-object': null,
63
- };
64
- /** BPMN event types carry their trigger glyph inside the circle. */
65
- const BPMN_EVENT_GLYPH = {
66
- 'bpmn-message-event': '✉', 'bpmn-timer-event': '⏱', 'bpmn-error-event': '⚡',
67
- };
68
- /**
69
- * Give a placed master its notation furniture: UML compartments, or a BPMN
70
- * event trigger glyph. Both ride `metadata.panel`, so they paint in SVG and
71
- * survive export — see the note at the call site.
72
- */
73
- function applyNotationPanel(node, masterId, master) {
74
- var _a, _b, _c;
75
- const glyph = BPMN_EVENT_GLYPH[masterId];
76
- if (glyph) {
77
- // CENTRED, as BPMN draws its trigger glyphs — the caption now paints BELOW
78
- // the circle (labelPlacement), so the middle belongs to the badge.
79
- node.setMetadata('panel', { icon: { glyph, size: 16, corner: 'c' } });
80
- return;
81
- }
82
- if (!(masterId in UML_CLASSIFIERS))
83
- return;
84
- const stereotype = UML_CLASSIFIERS[masterId];
85
- const name = (_b = (_a = master.meta) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : 'Class';
86
- // Placeholder members, so a dropped classifier looks like a UML card the user
87
- // can then edit rather than an empty box.
88
- const rows = masterId === 'uml-enum'
89
- ? [{ text: 'VALUE_A' }, { text: 'VALUE_B' }]
90
- : [{ text: '+ field: Type' }, { text: '+ method(): void' }];
91
- node.setMetadata('panel', {
92
- // A stereotyped classifier stacks «stereotype» OVER the name, the way UML
93
- // draws its cards — inline ("«enumeration» Enumeration") the header line
94
- // was wider than the card and painted cut off at both edges.
95
- header: stereotype ? { lines: [stereotype, name] } : { text: name },
96
- rows,
97
- rowHeight: 18,
98
- });
99
- // The header IS the classifier's name, so the node's own centred label would
100
- // simply repeat it in the gap between the compartments.
101
- (_c = node.setLabel) === null || _c === void 0 ? void 0 : _c.call(node, '');
102
- }
103
- /** A thumbnail SVG for a master, drawn from its real outline + declared paint. */
104
- function thumbnail(master, box = 34) {
105
- var _a, _b, _c, _d, _e, _f;
106
- const svg = document.createElementNS(SVG_NS, 'svg');
107
- svg.setAttribute('width', String(box));
108
- svg.setAttribute('height', String(box));
109
- svg.setAttribute('viewBox', `0 0 ${box} ${box}`);
110
- svg.setAttribute('aria-hidden', 'true');
111
- const struct = (_a = master.structure) !== null && _a !== void 0 ? _a : {};
112
- const paint = (_b = struct.shape) !== null && _b !== void 0 ? _b : {};
113
- // Preserve the master's aspect ratio inside the square, with a hairline inset
114
- // so a 2px stroke isn't clipped at the edges.
115
- const w0 = Number((_c = struct.size) === null || _c === void 0 ? void 0 : _c.width) || 100;
116
- const h0 = Number((_d = struct.size) === null || _d === void 0 ? void 0 : _d.height) || 60;
117
- const pad = 3;
118
- const s = Math.min((box - pad * 2) / w0, (box - pad * 2) / h0);
119
- const w = Math.max(6, w0 * s);
120
- const h = Math.max(6, h0 * s);
121
- let el;
122
- try {
123
- const spec = getShape(paint.type).outline(w, h);
124
- el = document.createElementNS(SVG_NS, spec.el);
125
- for (const [k, v] of Object.entries(spec.geom))
126
- el.setAttribute(k, String(v));
127
- }
128
- catch (_g) {
129
- el = document.createElementNS(SVG_NS, 'rect');
130
- el.setAttribute('width', String(w));
131
- el.setAttribute('height', String(h));
132
- el.setAttribute('rx', '3');
133
- }
134
- el.setAttribute('fill', (_e = paint.fill) !== null && _e !== void 0 ? _e : '#eef1fb');
135
- el.setAttribute('stroke', (_f = paint.stroke) !== null && _f !== void 0 ? _f : '#3B52D9');
136
- el.setAttribute('stroke-width', String(Math.min(Number(paint.strokeWidth) || 1.5, 2)));
137
- const g = document.createElementNS(SVG_NS, 'g');
138
- g.setAttribute('transform', `translate(${(box - w) / 2} ${(box - h) / 2})`);
139
- g.appendChild(el);
140
- svg.appendChild(g);
141
- return svg;
142
- }
143
- /** Root + descendants, so a multi-node master commits (and undoes) as a unit. */
144
- function subtree(diagram, root) {
145
- var _a;
146
- const out = [root];
147
- const stack = [root];
148
- while (stack.length) {
149
- const n = stack.pop();
150
- const ids = Array.from((_a = n === null || n === void 0 ? void 0 : n.children) !== null && _a !== void 0 ? _a : []);
151
- for (const id of ids) {
152
- const c = diagram.getNode(id);
153
- if (c) {
154
- out.push(c);
155
- stack.push(c);
156
- }
157
- }
158
- }
159
- return out;
160
- }
161
- /**
162
- * Build a stencil palette in `palette` that drops masters onto `canvas`.
163
- *
164
- * @param api the diagram instance (engine + model + viewport)
165
- * @param hosts `palette`: where the list renders · `canvas`: the drop target
166
- * (the element the diagram is mounted in)
167
- */
168
- export function bindStencilPalette(api, hosts, options = {}) {
169
- var _a, _b, _c;
170
- ensureStencilKitStyles((_a = hosts.palette.ownerDocument) !== null && _a !== void 0 ? _a : document);
171
- const stencils = (_b = options.stencils) !== null && _b !== void 0 ? _b : listStencils();
172
- /** master id → stencil id, so a drop knows which notation it belongs to. */
173
- const stencilOf = new Map();
174
- for (const st of stencils)
175
- for (const m of st.masters)
176
- stencilOf.set(m.id, st.id);
177
- const collapsed = new Set((_c = options.collapsed) !== null && _c !== void 0 ? _c : stencils.slice(1).map((s) => s.id));
178
- const { palette, canvas } = hosts;
179
- let query = '';
180
- palette.classList.add('gf-stencil');
181
- palette.innerHTML = '';
182
- // ── search ───────────────────────────────────────────────────────────────
183
- let input = null;
184
- if (options.search !== false) {
185
- const wrap = document.createElement('div');
186
- wrap.className = 'gf-stencil-search';
187
- input = document.createElement('input');
188
- input.type = 'search';
189
- input.placeholder = 'Search shapes…';
190
- input.setAttribute('aria-label', 'Search shapes');
191
- input.addEventListener('input', () => { query = input.value.trim().toLowerCase(); renderList(); });
192
- wrap.appendChild(input);
193
- palette.appendChild(wrap);
194
- }
195
- const body = document.createElement('div');
196
- body.className = 'gf-stencil-body';
197
- palette.appendChild(body);
198
- /** A master matches on its display name, its id, or any of its tags. */
199
- const matches = (m) => {
200
- var _a, _b;
201
- if (!query)
202
- return true;
203
- const meta = (_a = m.meta) !== null && _a !== void 0 ? _a : {};
204
- const hay = [meta.name, m.id, ...((_b = meta.tags) !== null && _b !== void 0 ? _b : [])].join(' ').toLowerCase();
205
- return hay.includes(query);
206
- };
207
- function renderList() {
208
- body.innerHTML = '';
209
- let shown = 0;
210
- for (const stencil of stencils) {
211
- const hits = stencil.masters.filter(matches);
212
- if (hits.length === 0)
213
- continue;
214
- shown += hits.length;
215
- const group = document.createElement('details');
216
- group.className = 'gf-stencil-group';
217
- // A search always opens the sections that matched; otherwise honour the
218
- // caller's collapsed set.
219
- group.open = query ? true : !collapsed.has(stencil.id);
220
- group.addEventListener('toggle', () => {
221
- if (query)
222
- return;
223
- if (group.open)
224
- collapsed.delete(stencil.id);
225
- else
226
- collapsed.add(stencil.id);
227
- });
228
- const summary = document.createElement('summary');
229
- summary.append(stencil.name);
230
- const count = document.createElement('span');
231
- count.className = 'gf-stencil-count';
232
- count.textContent = String(hits.length);
233
- summary.appendChild(count);
234
- summary.title = stencil.description;
235
- group.appendChild(summary);
236
- const items = document.createElement('div');
237
- items.className = 'gf-stencil-items';
238
- for (const master of hits)
239
- items.appendChild(itemEl(master));
240
- group.appendChild(items);
241
- body.appendChild(group);
242
- }
243
- if (shown === 0) {
244
- const empty = document.createElement('div');
245
- empty.className = 'gf-stencil-empty';
246
- empty.textContent = query ? `No shapes match “${query}”.` : 'No stencils.';
247
- body.appendChild(empty);
248
- }
249
- }
250
- // ── drag ghost ───────────────────────────────────────────────────────────
251
- // A lightweight preview that follows the cursor during a palette drag, so
252
- // the user can see WHAT they are holding and WHERE it will land. A DOM
253
- // element rather than `setDragImage`: the native drag image is browser
254
- // chrome — it never appears in a screenshot, and headless/some platforms do
255
- // not paint it at all, which is exactly how "dragging from the rail shows no
256
- // preview" shipped unnoticed.
257
- let ghost = null;
258
- const moveGhost = (e) => {
259
- if (!ghost)
260
- return;
261
- ghost.style.left = `${e.clientX}px`;
262
- ghost.style.top = `${e.clientY}px`;
263
- };
264
- const killGhost = () => {
265
- ghost === null || ghost === void 0 ? void 0 : ghost.remove();
266
- ghost = null;
267
- document.removeEventListener('dragover', moveGhost);
268
- };
269
- const spawnGhost = (master, e) => {
270
- var _a, _b;
271
- killGhost();
272
- const meta = (_a = master.meta) !== null && _a !== void 0 ? _a : {};
273
- ghost = document.createElement('div');
274
- ghost.className = 'gf-stencil-ghost';
275
- ghost.appendChild(thumbnail(master, 28));
276
- ghost.append((_b = meta.name) !== null && _b !== void 0 ? _b : master.id);
277
- ghost.style.left = `${e.clientX}px`;
278
- ghost.style.top = `${e.clientY}px`;
279
- document.body.appendChild(ghost);
280
- // dragover fires document-wide throughout an HTML5 drag — the one stream
281
- // that reliably carries live coordinates on every platform.
282
- document.addEventListener('dragover', moveGhost);
283
- };
284
- function itemEl(master) {
285
- var _a, _b, _c;
286
- const meta = (_a = master.meta) !== null && _a !== void 0 ? _a : {};
287
- const el = document.createElement('div');
288
- el.className = 'gf-stencil-item';
289
- el.draggable = true;
290
- el.dataset['masterId'] = master.id;
291
- el.title = meta.description ? `${meta.name} — ${meta.description}` : (_b = meta.name) !== null && _b !== void 0 ? _b : master.id;
292
- el.appendChild(thumbnail(master));
293
- const label = document.createElement('span');
294
- label.className = 'gf-stencil-label';
295
- label.textContent = (_c = meta.name) !== null && _c !== void 0 ? _c : master.id;
296
- el.appendChild(label);
297
- el.addEventListener('dragstart', (e) => {
298
- var _a;
299
- const dt = e.dataTransfer;
300
- if (!dt)
301
- return;
302
- dt.setData(DND_TYPE, master.id);
303
- dt.setData('text/plain', (_a = meta.name) !== null && _a !== void 0 ? _a : master.id); // so a stray drop is harmless text
304
- dt.effectAllowed = 'copy';
305
- // Suppress the native drag snapshot where it IS painted — otherwise the
306
- // ghost and the platform preview would ride the cursor together.
307
- if (typeof Image !== 'undefined' && dt.setDragImage) {
308
- const blank = new Image();
309
- blank.src =
310
- 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
311
- dt.setDragImage(blank, 0, 0);
312
- }
313
- spawnGhost(master, e);
314
- });
315
- el.addEventListener('dragend', killGhost);
316
- return el;
317
- }
318
- // ── drop-to-place on the canvas ──────────────────────────────────────────
319
- const heldMaster = (e) => {
320
- const dt = e.dataTransfer;
321
- if (!dt)
322
- return null;
323
- // `getData` is empty during dragover in most browsers — the TYPE is the
324
- // reliable signal there, and the id itself is read on drop.
325
- return dt.types.includes(DND_TYPE) ? (dt.getData(DND_TYPE) || '') : null;
326
- };
327
- const onDragOver = (e) => {
328
- if (heldMaster(e) === null)
329
- return;
330
- e.preventDefault(); // required, or the drop never fires
331
- if (e.dataTransfer)
332
- e.dataTransfer.dropEffect = 'copy';
333
- canvas.classList.add('gf-stencil-target');
334
- };
335
- const onDragLeave = (e) => {
336
- if (e.relatedTarget && canvas.contains(e.relatedTarget))
337
- return;
338
- canvas.classList.remove('gf-stencil-target');
339
- };
340
- const onDrop = (e) => {
341
- const id = heldMaster(e);
342
- canvas.classList.remove('gf-stencil-target');
343
- killGhost();
344
- if (!id)
345
- return;
346
- e.preventDefault();
347
- const rect = canvas.getBoundingClientRect();
348
- void place(id, api.viewport.clientToWorld(e.clientX, e.clientY, rect));
349
- };
350
- canvas.addEventListener('dragover', onDragOver);
351
- canvas.addEventListener('dragleave', onDragLeave);
352
- canvas.addEventListener('drop', onDrop);
353
- /**
354
- * Stamp `masterId` centred on a world point, as ONE undo entry.
355
- *
356
- * `NodeFactory.createFromTemplate` adds straight to the model (no command), so
357
- * the created subtree is captured, taken back out, and re-applied through the
358
- * CommandManager — one Ctrl+Z removes the whole shape, and collab sees a
359
- * normal add.
360
- */
361
- function place(masterId, world) {
362
- return __awaiter(this, void 0, void 0, function* () {
363
- var _a, _b, _c, _d, _e, _f, _g, _h;
364
- const engine = api.getEngine();
365
- const diagram = api.getModel();
366
- const registry = engine === null || engine === void 0 ? void 0 : engine.templateRegistry;
367
- const master = registry === null || registry === void 0 ? void 0 : registry.get(masterId);
368
- if (!master || !diagram)
369
- return null;
370
- const struct = (_a = master.structure) !== null && _a !== void 0 ? _a : {};
371
- const w = Number((_b = struct.size) === null || _b === void 0 ? void 0 : _b.width) || 100;
372
- const h = Number((_c = struct.size) === null || _c === void 0 ? void 0 : _c.height) || 60;
373
- const at = { x: world.x - w / 2, y: world.y - h / 2 }; // drop centres on the cursor
374
- // A master may resolve to a registered BUILDER (an ER entity / UML class is
375
- // a kit card, not a silhouette). Anything without one takes the template
376
- // path — see stencil-kit/builders.ts for why this is a registry and not an
377
- // id check inside the palette.
378
- const builder = getStencilBuilder(masterId);
379
- const built = builder ? builder({ api, master, at }) : null;
380
- const factory = built ? null : new NodeFactory(registry, diagram);
381
- const root = built !== null && built !== void 0 ? built : factory.createFromTemplate(masterId, (_e = (_d = options.data) === null || _d === void 0 ? void 0 : _d.call(options, master)) !== null && _e !== void 0 ? _e : {}, at);
382
- const created = built ? [built] : subtree(diagram, root);
383
- // NOTE: masters used to need `useHTMLLayer` stripped here or they rendered
384
- // as empty groups. That is fixed at the source now (NodeFactory no longer
385
- // sets the flag — see NodeFactory.html-contract.spec.ts), so the workaround
386
- // is gone. `htmlLayer: true` is still honoured for hosts that set the flag
387
- // themselves and DO run a layer that paints it.
388
- if (options.htmlLayer === true) {
389
- for (const n of created)
390
- n.setMetadata('useHTMLLayer', true);
391
- }
392
- // COMPARTMENTS & EVENT MARKERS. `metadata.panel` drives the renderer's
393
- // composite-panel overlay (header band + stacked rows + a corner glyph) —
394
- // a complete, themed, SVG-NATIVE subsystem that shipped with zero callers.
395
- // Using it here rather than the UML kit's `metadata.html` route is
396
- // deliberate: an HTML-layer card is not vector-exportable and depends on a
397
- // layer the plain SVG embed does not run — the exact dependency that made
398
- // every dropped master render blank.
399
- applyNotationPanel(root, masterId, master);
400
- applyNotationTheme(root, stencilOf.get(masterId), options.notationTheme, api);
401
- // Serialize into commands BEFORE detaching (AddNodeCommand snapshots in its
402
- // constructor), then detach and replay through the command manager.
403
- const cmds = created.map((n) => new AddNodeCommand(n));
404
- for (const n of [...created].reverse())
405
- diagram.removeNode(n.id);
406
- yield engine.commandManager.execute(new BatchCommand(`Place ${(_g = (_f = master.meta) === null || _f === void 0 ? void 0 : _f.name) !== null && _g !== void 0 ? _g : masterId}`, cmds));
407
- (_h = options.onPlace) === null || _h === void 0 ? void 0 : _h.call(options, { master, nodeId: root.id, x: at.x, y: at.y });
408
- return root.id;
409
- });
410
- }
411
- renderList();
412
- return {
413
- setSearch(q) { query = (q !== null && q !== void 0 ? q : '').trim().toLowerCase(); if (input)
414
- input.value = q !== null && q !== void 0 ? q : ''; renderList(); },
415
- place,
416
- destroy() {
417
- killGhost();
418
- canvas.removeEventListener('dragover', onDragOver);
419
- canvas.removeEventListener('dragleave', onDragLeave);
420
- canvas.removeEventListener('drop', onDrop);
421
- canvas.classList.remove('gf-stencil-target');
422
- palette.classList.remove('gf-stencil');
423
- palette.innerHTML = '';
424
- },
425
- };
426
- }
427
- //# sourceMappingURL=palette.js.map