@grafloria/element 0.3.23 → 0.4.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 +7 -7
- package/package.json +7 -9
- package/src/index.d.ts +16 -16
- package/src/index.js +58 -339
- package/src/lib/dashboard-kit/dashboard.d.ts +1 -1
- package/src/lib/dashboard-kit/dashboard.js +22 -26
- package/src/lib/dashboard-kit/grid-binder.d.ts +1 -1
- package/src/lib/dashboard-kit/grid-binder.js +62 -65
- package/src/lib/dashboard-kit/grid-mapping.js +14 -26
- package/src/lib/dashboard-kit/index.d.ts +5 -5
- package/src/lib/dashboard-kit/index.js +5 -30
- package/src/lib/dashboard-kit/styles.js +4 -8
- package/src/lib/dashboard-kit/widgets.d.ts +1 -1
- package/src/lib/dashboard-kit/widgets.js +16 -26
- package/src/lib/diagram-kit/card.d.ts +2 -2
- package/src/lib/diagram-kit/card.js +22 -32
- package/src/lib/diagram-kit/editing.js +15 -20
- package/src/lib/diagram-kit/er.d.ts +1 -1
- package/src/lib/diagram-kit/er.js +15 -18
- package/src/lib/diagram-kit/handles.d.ts +4 -4
- package/src/lib/diagram-kit/handles.js +16 -29
- package/src/lib/diagram-kit/index.d.ts +10 -10
- package/src/lib/diagram-kit/index.js +10 -50
- package/src/lib/diagram-kit/join-guidance.js +9 -18
- package/src/lib/diagram-kit/rows.js +1 -4
- package/src/lib/diagram-kit/styles.js +4 -8
- package/src/lib/diagram-kit/uml.js +10 -13
- package/src/lib/diagram-kit/update.d.ts +1 -1
- package/src/lib/diagram-kit/update.js +19 -26
- package/src/lib/grafloria-flow-element.js +19 -24
- package/src/lib/grafloria.d.ts +4 -4
- package/src/lib/grafloria.js +12 -17
- package/src/lib/load.d.ts +3 -3
- package/src/lib/load.js +19 -22
- package/src/lib/node-type-registry.js +6 -14
- package/src/lib/stencil-kit/builders.js +4 -10
- package/src/lib/stencil-kit/card-builders.js +14 -17
- package/src/lib/stencil-kit/index.d.ts +7 -7
- package/src/lib/stencil-kit/index.js +6 -16
- package/src/lib/stencil-kit/palette.js +14 -17
- package/src/lib/stencil-kit/shape-data.js +25 -28
- package/src/lib/stencil-kit/styles.js +1 -4
- package/esm/src/index.js +0 -295
- package/esm/src/lib/dashboard-kit/dashboard.js +0 -590
- package/esm/src/lib/dashboard-kit/grid-binder.js +0 -1633
- package/esm/src/lib/dashboard-kit/grid-mapping.js +0 -164
- package/esm/src/lib/dashboard-kit/index.js +0 -8
- package/esm/src/lib/dashboard-kit/styles.js +0 -205
- package/esm/src/lib/dashboard-kit/widgets.js +0 -379
- package/esm/src/lib/diagram-kit/card.js +0 -199
- package/esm/src/lib/diagram-kit/editing.js +0 -448
- package/esm/src/lib/diagram-kit/er.js +0 -160
- package/esm/src/lib/diagram-kit/handles.js +0 -312
- package/esm/src/lib/diagram-kit/index.js +0 -15
- package/esm/src/lib/diagram-kit/join-guidance.js +0 -322
- package/esm/src/lib/diagram-kit/rows.js +0 -144
- package/esm/src/lib/diagram-kit/styles.js +0 -118
- package/esm/src/lib/diagram-kit/uml.js +0 -135
- package/esm/src/lib/diagram-kit/update.js +0 -244
- package/esm/src/lib/grafloria-flow-element.js +0 -266
- package/esm/src/lib/grafloria.js +0 -69
- package/esm/src/lib/load.js +0 -252
- package/esm/src/lib/node-type-registry.js +0 -42
- package/esm/src/lib/stencil-kit/builders.js +0 -18
- package/esm/src/lib/stencil-kit/card-builders.js +0 -105
- package/esm/src/lib/stencil-kit/index.js +0 -9
- package/esm/src/lib/stencil-kit/palette.js +0 -427
- package/esm/src/lib/stencil-kit/shape-data.js +0 -537
- 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
|