@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,144 +0,0 @@
1
- /**
2
- * Row interactions — "select a column / field" on kit cards (P1 of the
3
- * ER/UML editing track).
4
- *
5
- * Kit cards are `interactive: true` (their rows are real DOM targets — the
6
- * binder's node click/drag resolution is geometric, so dragging and selecting
7
- * the TABLE keeps working; proven live before this was built). This module is
8
- * the runtime the kit's `finalize(api)` binds once per diagram:
9
- *
10
- * - click a row → it becomes THE selected row (one per diagram), painted with
11
- * `.axk-row-selected`; click again or click anywhere else → deselected;
12
- * - every row click dispatches `axk:row-click` on the container, and every
13
- * selection CHANGE dispatches `axk:row-select` — both bubbling
14
- * CustomEvents, matching the element package's "DOM events out" contract;
15
- * - the selected row survives card re-renders: the renderer keys the
16
- * foreignObject by content hash, so an html edit swaps the subtree and
17
- * drops the class — a MutationObserver re-applies it by (nodeId, rowIndex);
18
- * - selection is EPHEMERAL view state, exactly like node selection: never
19
- * serialized, never in the op log, not undoable.
20
- *
21
- * Row identity: `rowIndex` is the position among the card's rows (ER columns,
22
- * or UML members across both compartments); `name` resolves from the spec the
23
- * kit stored in node metadata (`kitEntity` / `kitClass`), falling back to the
24
- * row's text.
25
- */
26
- const ROW_SELECTOR = '.axk-row, .axk-member';
27
- const SELECTED_CLASS = 'axk-row-selected';
28
- /** One binding per container — rebinding disposes the previous one. */
29
- const bindings = new WeakMap();
30
- function rowsOfNode(container, nodeId) {
31
- const esc = (window.CSS && CSS.escape) ? CSS.escape(nodeId) : nodeId.replace(/"/g, '\\"');
32
- const group = container.querySelector(`[data-node-id="${esc}"]`);
33
- return group ? Array.from(group.querySelectorAll(ROW_SELECTOR)) : [];
34
- }
35
- function resolveRef(container, api, rowEl) {
36
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
37
- const group = rowEl.closest('[data-node-id]');
38
- if (!group)
39
- return null;
40
- const nodeId = group.getAttribute('data-node-id');
41
- const rows = Array.from(group.querySelectorAll(ROW_SELECTOR));
42
- const rowIndex = rows.indexOf(rowEl);
43
- if (rowIndex === -1)
44
- return null;
45
- const kind = rowEl.classList.contains('axk-member') ? 'uml' : 'er';
46
- const node = (_c = (_a = api.getModel) === null || _a === void 0 ? void 0 : (_b = _a.call(api)).getNode) === null || _c === void 0 ? void 0 : _c.call(_b, nodeId);
47
- const meta = (key) => { var _a; return (_a = node === null || node === void 0 ? void 0 : node.getMetadata) === null || _a === void 0 ? void 0 : _a.call(node, key); };
48
- if (kind === 'er') {
49
- const entity = meta('kitEntity');
50
- const name = (_h = (_f = (_e = (_d = entity === null || entity === void 0 ? void 0 : entity.columns) === null || _d === void 0 ? void 0 : _d[rowIndex]) === null || _e === void 0 ? void 0 : _e.name) !== null && _f !== void 0 ? _f : (_g = rowEl.querySelector('.axk-col')) === null || _g === void 0 ? void 0 : _g.textContent) !== null && _h !== void 0 ? _h : undefined;
51
- return { nodeId, rowIndex, name: name !== null && name !== void 0 ? name : undefined, kind };
52
- }
53
- const cls = meta('kitClass');
54
- const attrCount = (_k = (_j = cls === null || cls === void 0 ? void 0 : cls.attributes) === null || _j === void 0 ? void 0 : _j.length) !== null && _k !== void 0 ? _k : countFirstCompartment(rowEl);
55
- const section = rowIndex < attrCount ? 'attributes' : 'methods';
56
- const name = (_q = (_o = (section === 'attributes' ? (_l = cls === null || cls === void 0 ? void 0 : cls.attributes) === null || _l === void 0 ? void 0 : _l[rowIndex] : (_m = cls === null || cls === void 0 ? void 0 : cls.methods) === null || _m === void 0 ? void 0 : _m[rowIndex - attrCount])) !== null && _o !== void 0 ? _o : (_p = rowEl.textContent) === null || _p === void 0 ? void 0 : _p.trim()) !== null && _q !== void 0 ? _q : undefined;
57
- return { nodeId, rowIndex, name, kind, section };
58
- }
59
- /** Without spec metadata, the first compartment's member count splits sections. */
60
- function countFirstCompartment(rowEl) {
61
- const card = rowEl.closest('.axk-uml');
62
- const firstComp = card === null || card === void 0 ? void 0 : card.querySelector('.axk-uml-comp');
63
- return firstComp ? firstComp.querySelectorAll('.axk-member').length : Number.MAX_SAFE_INTEGER;
64
- }
65
- export function bindRowInteractions(api) {
66
- var _a;
67
- const container = api.container;
68
- (_a = bindings.get(container)) === null || _a === void 0 ? void 0 : _a.dispose();
69
- let selected = null;
70
- const paint = () => {
71
- var _a;
72
- for (const el of Array.from(container.querySelectorAll(`.${SELECTED_CLASS}`))) {
73
- el.classList.remove(SELECTED_CLASS);
74
- }
75
- if (selected)
76
- (_a = rowsOfNode(container, selected.nodeId)[selected.rowIndex]) === null || _a === void 0 ? void 0 : _a.classList.add(SELECTED_CLASS);
77
- };
78
- const emitSelect = () => {
79
- container.dispatchEvent(new CustomEvent('axk:row-select', { bubbles: true, detail: { selected } }));
80
- };
81
- const setSelected = (next) => {
82
- const same = (next === null && selected === null) ||
83
- (next !== null && selected !== null && next.nodeId === selected.nodeId && next.rowIndex === selected.rowIndex);
84
- if (same)
85
- return;
86
- selected = next;
87
- paint();
88
- emitSelect();
89
- };
90
- const onClick = (event) => {
91
- var _a;
92
- const target = event.target instanceof Element ? event.target : null;
93
- const rowEl = (_a = target === null || target === void 0 ? void 0 : target.closest) === null || _a === void 0 ? void 0 : _a.call(target, ROW_SELECTOR);
94
- if (!rowEl || !container.contains(rowEl)) {
95
- setSelected(null);
96
- return;
97
- }
98
- const ref = resolveRef(container, api, rowEl);
99
- if (!ref)
100
- return;
101
- container.dispatchEvent(new CustomEvent('axk:row-click', { bubbles: true, detail: Object.assign({}, ref) }));
102
- const isReclick = selected && selected.nodeId === ref.nodeId && selected.rowIndex === ref.rowIndex;
103
- setSelected(isReclick ? null : ref);
104
- };
105
- // Card re-renders replace the foreignObject subtree (content-hash keyed) and
106
- // lose the class — re-apply whenever the selected row's paint went missing.
107
- const observer = new MutationObserver(() => {
108
- if (!selected)
109
- return;
110
- const el = rowsOfNode(container, selected.nodeId)[selected.rowIndex];
111
- if (el && !el.classList.contains(SELECTED_CLASS))
112
- paint();
113
- else if (!el) {
114
- // The row (or its node) is gone — the selection dies with it.
115
- setSelected(null);
116
- }
117
- });
118
- container.addEventListener('click', onClick);
119
- observer.observe(container, { childList: true, subtree: true });
120
- const handle = {
121
- getSelected: () => (selected ? Object.assign({}, selected) : null),
122
- select: (ref) => {
123
- if (ref === null) {
124
- setSelected(null);
125
- return;
126
- }
127
- const el = rowsOfNode(container, ref.nodeId)[ref.rowIndex];
128
- if (!el)
129
- return;
130
- const resolved = resolveRef(container, api, el);
131
- if (resolved)
132
- setSelected(resolved);
133
- },
134
- dispose: () => {
135
- container.removeEventListener('click', onClick);
136
- observer.disconnect();
137
- if (bindings.get(container) === handle)
138
- bindings.delete(container);
139
- },
140
- };
141
- bindings.set(container, handle);
142
- return handle;
143
- }
144
- //# sourceMappingURL=rows.js.map
@@ -1,118 +0,0 @@
1
- /**
2
- * Diagram-kit stylesheet — injected once, on first use of any kit builder.
3
- *
4
- * Everything is prefixed `axk-` and the selection overrides are scoped with
5
- * `:has(...)` to kit cards only, so embedding the kit can never restyle a
6
- * host's own nodes. The rules encode the lessons the diagrams/* demos learned
7
- * the hard way:
8
- * - the card fills the node and draws the ONLY border (the node's own rect is
9
- * hidden by the builders, and suppressed again on selection because the
10
- * theme paints `.selected` with an accent stroke that overrides inline
11
- * transparency);
12
- * - the default `.selection-highlight` outline (a dashed rect a few px
13
- * OUTSIDE the node) reads as a second floating box around a bordered card —
14
- * the kit hides it and rings the card itself instead.
15
- */
16
- export const DIAGRAM_KIT_STYLE_ID = 'grafloria-diagram-kit-styles';
17
- const CSS = `
18
- /* ===== ER entity (table) cards ===== */
19
- .axk-entity { font: 12px/1.5 system-ui, sans-serif; border: 1px solid #64748b;
20
- border-radius: 6px; overflow: hidden; background: #fff;
21
- width: 100%; height: 100%; box-sizing: border-box;
22
- display: flex; flex-direction: column; }
23
- .axk-entity-body { flex: 1; min-height: 0; overflow-y: hidden; }
24
- .axk-entity-body.axk-scroll { overflow-y: auto; scrollbar-width: thin; }
25
- .axk-entity-head { background: #334155; color: #fff; font-weight: 600;
26
- letter-spacing: .3px; padding: 5px 10px; text-transform: uppercase; font-size: 11px; }
27
- .axk-row { display: flex; align-items: center; gap: 8px; padding: 3px 10px;
28
- border-top: 1px solid #e2e8f0; }
29
- .axk-key { width: 22px; font-size: 9px; font-weight: 700; color: #b45309; }
30
- .axk-key.axk-fk { color: #6d28d9; }
31
- .axk-col { flex: 1; color: #0f172a; }
32
- .axk-ty {
33
- color: #64748b; font-size: 11px;
34
- /* A new column starts with an EMPTY type, which collapsed the cell to zero
35
- width — there was nothing to double-click, so a type could never be set on
36
- a field you just added. Reserve a target and hint that it is editable. */
37
- min-width: 52px; text-align: right; cursor: text;
38
- }
39
- .axk-ty:empty::before { content: 'type'; color: #cbd5e1; font-style: italic; }
40
- .axk-ty:hover { color: #0f172a; }
41
- .axk-row.axk-pk .axk-col { font-weight: 600; }
42
-
43
- /* ===== UML class cards ===== */
44
- .axk-uml { font: 12px/1.5 system-ui, sans-serif; border: 1px solid #475569;
45
- border-radius: 4px; overflow: hidden; background: #fff;
46
- width: 100%; height: 100%; box-sizing: border-box;
47
- display: flex; flex-direction: column; }
48
- .axk-uml-body { flex: 1; min-height: 0; overflow-y: hidden; }
49
- .axk-uml-body.axk-scroll { overflow-y: auto; scrollbar-width: thin; }
50
- .axk-uml-name { text-align: center; font-weight: 700; padding: 5px 10px;
51
- background: #eef2ff; color: #1e1b4b; }
52
- .axk-uml-name.axk-abstract { font-style: italic; }
53
- .axk-uml-stereo { display: block; font-size: 10px; font-weight: 500; opacity: .8; }
54
- .axk-uml-comp { border-top: 1px solid #475569; padding: 3px 0; }
55
- .axk-uml-comp.axk-empty { min-height: 8px; }
56
- .axk-member { padding: 1px 10px; font: 11px/1.5 ui-monospace, Menlo, monospace;
57
- color: #0f172a; white-space: nowrap; }
58
-
59
- /* ===== Row interactivity (cards are interactive; drag stays geometric) ===== */
60
- .axk-entity, .axk-uml { user-select: none; -webkit-user-select: none; }
61
- .axk-row:hover { background: rgba(37, 99, 235, .07); }
62
- .axk-member:hover { background: rgba(79, 70, 229, .07); }
63
- .axk-row-selected, .axk-row-selected:hover { background: rgba(37, 99, 235, .16);
64
- box-shadow: inset 2px 0 0 #2563eb; }
65
- .axk-member.axk-row-selected, .axk-member.axk-row-selected:hover {
66
- background: rgba(79, 70, 229, .16); box-shadow: inset 2px 0 0 #4f46e5; }
67
-
68
- /* ===== Selection: ring the CARD, never a detached rectangle ===== */
69
- g.node-group:has(.axk-entity) .selection-highlight,
70
- g.node-group:has(.axk-uml) .selection-highlight { display: none; }
71
- g.node-group[data-selected="true"] .axk-entity {
72
- border-color: #2563eb; box-shadow: 0 0 0 2px rgba(37, 99, 235, .45); }
73
- g.node-group[data-selected="true"] .axk-uml {
74
- border-color: #4f46e5; box-shadow: 0 0 0 2px rgba(79, 70, 229, .45); }
75
- g.node-group[data-selected="true"]:has(.axk-entity) rect.diagram-node,
76
- g.node-group[data-selected="true"]:has(.axk-uml) rect.diagram-node {
77
- stroke: transparent !important; fill: none !important; }
78
-
79
- /* ===== In-canvas editing chrome (only present when editable) ===== */
80
- .axk-col-del { width: 14px; text-align: center; color: #94a3b8; cursor: pointer;
81
- font-weight: 700; opacity: 0; transition: opacity .1s; flex: 0 0 auto; }
82
- .axk-row:hover .axk-col-del, .axk-member:hover .axk-col-del { opacity: 1; }
83
- .axk-col-del:hover { color: #dc2626; }
84
- /* Only editable members (which wrap their text in .axk-mtext) go flex — a
85
- read-only member stays a plain text div, so its golden never shifts. */
86
- .axk-member:has(.axk-mtext) { display: flex; align-items: center; }
87
- .axk-member .axk-mtext { flex: 1; }
88
- .axk-entity-add, .axk-uml-add { padding: 3px 10px; font-size: 11px; font-weight: 600;
89
- color: #2563eb; cursor: pointer; border-top: 1px dashed #cbd5e1; user-select: none; }
90
- .axk-uml-add { color: #4f46e5; border-top: 1px dashed #c7d2fe; text-align: left; }
91
- .axk-entity-add:hover, .axk-uml-add:hover { background: rgba(37, 99, 235, .08); }
92
- .axk-edit-input { font: 12px/1.4 system-ui, sans-serif; box-sizing: border-box;
93
- border: 1px solid #2563eb; border-radius: 3px; padding: 1px 6px; margin: 0;
94
- background: #fff; color: #0f172a; outline: none; box-shadow: 0 1px 4px rgba(0,0,0,.2); }
95
-
96
- /* ===== Dark mode ===== */
97
- @media (prefers-color-scheme: dark) {
98
- .axk-entity { background: #1e293b; border-color: #475569; }
99
- .axk-row { border-top-color: #334155; }
100
- .axk-col { color: #e2e8f0; }
101
- .axk-uml { background: #1e293b; border-color: #64748b; }
102
- .axk-uml-name { background: #312e81; color: #e0e7ff; }
103
- .axk-uml-comp { border-top-color: #64748b; }
104
- .axk-member { color: #e2e8f0; }
105
- }
106
- `;
107
- /** Inject the kit stylesheet once. Safe to call repeatedly and in SSR. */
108
- export function ensureDiagramKitStyles(doc = typeof document !== 'undefined' ? document : undefined) {
109
- if (!doc)
110
- return;
111
- if (doc.getElementById(DIAGRAM_KIT_STYLE_ID))
112
- return;
113
- const style = doc.createElement('style');
114
- style.id = DIAGRAM_KIT_STYLE_ID;
115
- style.textContent = CSS;
116
- doc.head.appendChild(style);
117
- }
118
- //# sourceMappingURL=styles.js.map
@@ -1,135 +0,0 @@
1
- /**
2
- * UML class-diagram kit — typed builders for the full relationship vocabulary.
3
- *
4
- * The packaged form of demos/diagrams/class-uml.html + uml-relationships.html:
5
- * three-compartment class cards («stereotype», name, attributes, methods) and
6
- * every standard relationship with its real notation:
7
- *
8
- * inheritance ── solid, hollow triangle at the parent
9
- * realization ┄┄ dashed, hollow triangle at the interface
10
- * association ── plain line (multiplicity chips optional)
11
- * directed-association ── open arrow at the target
12
- * aggregation ── hollow diamond at the whole (source)
13
- * composition ── FILLED diamond at the whole (source)
14
- * dependency ┄┄ dashed, open arrow («uses»)
15
- *
16
- * ```ts
17
- * const spec = umlDiagram({
18
- * classes: [{ id: 'Shape', abstract: true, attributes: ['# x: float'], methods: ['+ area(): float'] }],
19
- * relationships: [{ from: 'Circle', to: 'Shape', kind: 'inheritance' }],
20
- * });
21
- * const api = render(spec, container);
22
- * spec.finalize(api); // attaches multiplicity chips (needs the live model)
23
- * ```
24
- */
25
- import { ensureDiagramKitStyles } from './styles';
26
- import { bindRowInteractions } from './rows';
27
- import { classCardContent, classAutoHeight } from './card';
28
- import { bindCardEditing } from './editing';
29
- const DEFAULT_WIDTH = 200;
30
- const STROKE = '#475569';
31
- const DASH = '6,4';
32
- /**
33
- * The notation table. Every kind sets arrowHead EXPLICITLY — a plain line must
34
- * carry `arrowHead: none`, or the renderer paints its default arrow (the
35
- * stray-arrowhead bug the demos hit live).
36
- */
37
- function edgeStyleFor(kind) {
38
- const none = { type: 'none', size: 0, filled: false };
39
- switch (kind) {
40
- case 'inheritance':
41
- return { stroke: STROKE, strokeWidth: 1.5, arrowHead: { type: 'generalization', size: 16, filled: false }, arrowTail: none };
42
- case 'realization':
43
- return { stroke: STROKE, strokeWidth: 1.5, strokeDasharray: DASH, arrowHead: { type: 'generalization', size: 16, filled: false }, arrowTail: none };
44
- case 'association':
45
- return { stroke: STROKE, strokeWidth: 1.5, arrowHead: none, arrowTail: none };
46
- case 'directed-association':
47
- return { stroke: STROKE, strokeWidth: 1.5, arrowHead: { type: 'open-arrow', size: 12, filled: false }, arrowTail: none };
48
- case 'aggregation':
49
- return { stroke: STROKE, strokeWidth: 1.5, arrowHead: none, arrowTail: { type: 'hollow-diamond', size: 14, filled: false } };
50
- case 'composition':
51
- return { stroke: STROKE, strokeWidth: 1.5, arrowHead: none, arrowTail: { type: 'filled-diamond', size: 14, filled: true } };
52
- case 'dependency':
53
- return { stroke: STROKE, strokeWidth: 1.5, strokeDasharray: DASH, arrowHead: { type: 'open-arrow', size: 12, filled: false }, arrowTail: none };
54
- }
55
- }
56
- /** Multiplicity chip, positioned near an edge end. */
57
- const chip = (text, slot) => ({
58
- text,
59
- slot,
60
- offset: { x: 0, y: -7 },
61
- style: { fontSize: 11, fontWeight: '600', color: '#0f172a', background: '#ffffffdd', padding: 1, borderRadius: 2 },
62
- });
63
- /**
64
- * Build a render() spec for a UML class diagram. Call `spec.finalize(api)`
65
- * after render() — multiplicity chips are positioned labels, which only the
66
- * live model's `link.addLabel` can express.
67
- */
68
- export function umlDiagram(options) {
69
- var _a;
70
- ensureDiagramKitStyles();
71
- const editable = options.editable === true;
72
- const nodes = options.classes.map((cls, i) => {
73
- var _a, _b;
74
- return {
75
- id: cls.id,
76
- position: (_a = cls.position) !== null && _a !== void 0 ? _a : { x: 80 + (i % 3) * 320, y: 60 + Math.floor(i / 3) * 260 },
77
- size: { width: (_b = cls.width) !== null && _b !== void 0 ? _b : DEFAULT_WIDTH, height: classAutoHeight(cls, editable) },
78
- // interactive: members are real DOM targets (hover, row selection, inline
79
- // editing); node drag/select stay geometric in the binder.
80
- metadata: Object.assign({ html: { content: classCardContent(cls, editable), interactive: true }, kitClass: cls, kitEditable: editable }, (options.rowSelection === false ? { kitRowSelection: false } : {})),
81
- shape: { type: 'rect', fill: 'none', stroke: 'none' },
82
- style: { fill: 'transparent', stroke: 'transparent', strokeWidth: 0 },
83
- };
84
- });
85
- const rels = (_a = options.relationships) !== null && _a !== void 0 ? _a : [];
86
- const edges = rels.map((rel, i) => {
87
- var _a, _b;
88
- return (Object.assign(Object.assign(Object.assign({ id: (_a = rel.id) !== null && _a !== void 0 ? _a : `uml-rel-${i + 1}`, source: rel.from, target: rel.to, label: rel.label, type: 'orthogonal' }, (rel.fromSide ? { sourceHandle: rel.fromSide } : {})), (rel.toSide ? { targetHandle: rel.toSide } : {})), { style: edgeStyleFor((_b = rel.kind) !== null && _b !== void 0 ? _b : 'association') }));
89
- });
90
- // finalize is idempotent per api: render() auto-runs it, and a demo may also
91
- // call it — chips must not be added twice.
92
- const finalized = new WeakSet();
93
- const finalize = (api) => {
94
- var _a, _b, _c, _d, _e, _f;
95
- const a0 = api;
96
- // Own card ring only — suppress the node resize handles on the live model
97
- // (the render-input path does not honour a spec-level `behavior`).
98
- const m0 = (_a = a0 === null || a0 === void 0 ? void 0 : a0.getModel) === null || _a === void 0 ? void 0 : _a.call(a0);
99
- if (m0)
100
- for (const cls of options.classes)
101
- (_d = (_c = (_b = m0.getNode) === null || _b === void 0 ? void 0 : _b.call(m0, cls.id)) === null || _c === void 0 ? void 0 : _c.setBehavior) === null || _d === void 0 ? void 0 : _d.call(_c, { resizable: false });
102
- if (options.rowSelection !== false) {
103
- const a = api;
104
- // bindRowInteractions disposes any prior binding for the container, so it
105
- // is safe to run twice.
106
- if (a === null || a === void 0 ? void 0 : a.container)
107
- bindRowInteractions(a);
108
- }
109
- if (editable && (a0 === null || a0 === void 0 ? void 0 : a0.container))
110
- bindCardEditing(a0);
111
- if (api && typeof api === 'object') {
112
- if (finalized.has(api))
113
- return;
114
- finalized.add(api);
115
- }
116
- const model = (_e = api === null || api === void 0 ? void 0 : api.getModel) === null || _e === void 0 ? void 0 : _e.call(api);
117
- if (!model)
118
- return;
119
- let added = false;
120
- rels.forEach((rel, i) => {
121
- if (!rel.multiplicity)
122
- return;
123
- const link = model.getLink(edges[i].id);
124
- if (!link)
125
- return;
126
- link.addLabel(chip(rel.multiplicity[0], 'start'));
127
- link.addLabel(chip(rel.multiplicity[1], 'end'));
128
- added = true;
129
- });
130
- if (added)
131
- (_f = api === null || api === void 0 ? void 0 : api.renderNow) === null || _f === void 0 ? void 0 : _f.call(api);
132
- };
133
- return { nodes, edges, finalize };
134
- }
135
- //# sourceMappingURL=uml.js.map
@@ -1,244 +0,0 @@
1
- /**
2
- * Live editing — `updateEntity` / `updateClass` mutate a rendered kit card in
3
- * place (P3, the foundation the in-canvas editing chrome is built on).
4
- *
5
- * A delta changes the DATA (a table's name, its columns; a class's name,
6
- * attributes, methods, width/height) and this module:
7
- * 1. reads the stored spec (`kitEntity` / `kitClass`) — the source of truth;
8
- * 2. applies the delta to a NEW spec (the caller's objects are never mutated,
9
- * so undo can restore the old one cleanly);
10
- * 3. regenerates the card html from the SAME builder er.ts/uml.ts use
11
- * (`card.ts`), so an edited card is identical to a freshly-built one;
12
- * 4. recomputes the node size;
13
- * 5. RECONCILES the field ports — every port pinned to a surviving column
14
- * moves to that column's new row (`erRowCenterY`), ports on a removed
15
- * column are dropped along with their edges. Port ids never change, so an
16
- * edge stays glued to its row across inserts/removes/reorders (the exact
17
- * failure this solves: a port frozen at the old row while the rows shifted
18
- * under it);
19
- * 6. repaints — as ONE undoable step.
20
- *
21
- * ONE UNDO STEP. The change is applied through `api.getEngine().commandManager`
22
- * — the same public seam `renderer/ext/public-api.ts` uses — wrapped in a
23
- * single kit-local {@link Command}. There is no `SetNodeMetadataCommand` in the
24
- * engine (the html/kit-spec mutation has no built-in command), so rather than
25
- * reach into engine internals or compose a half-undoable macro, the whole edit
26
- * lives in one Command subclass here (subclassing `Command` is the engine's
27
- * intended extension point — every basic command does it). When no engine is
28
- * present the edit still applies, just non-undoably.
29
- */
30
- import { __awaiter } from "tslib";
31
- import { Command, PortModel, LinkModel } from '@grafloria/engine';
32
- import { entityCardContent, entityAutoHeight, classCardContent, classAutoHeight, erRowCenterY, rowIndexFromY, matchColumns, } from './card';
33
- /**
34
- * The single undoable edit. Captures the card's whole before-state on first
35
- * execute and restores it on undo, so an edit — however many rows and edges it
36
- * touched — is one Ctrl+Z.
37
- */
38
- class UpdateCardCommand extends Command {
39
- constructor(nodeId, kind, build) {
40
- super(kind === 'er' ? 'Edit table' : 'Edit class');
41
- this.nodeId = nodeId;
42
- this.kind = kind;
43
- this.build = build;
44
- this.captured = false;
45
- }
46
- execute(context) {
47
- var _a, _b, _c, _d, _e, _f, _g, _h;
48
- const model = context.diagram;
49
- const node = model.getNode(this.nodeId);
50
- if (!node)
51
- return;
52
- const kitKey = this.kind === 'er' ? 'kitEntity' : 'kitClass';
53
- const oldKit = ((_a = node.getMetadata(kitKey)) !== null && _a !== void 0 ? _a : {});
54
- if (!this.captured) {
55
- this.captured = true;
56
- this.before = {
57
- html: node.getMetadata('html'),
58
- kit: oldKit,
59
- size: Object.assign({}, node.size),
60
- ports: node.getPorts().map((p) => p.serialize()),
61
- links: [],
62
- };
63
- }
64
- const result = this.build(oldKit);
65
- const newWidth = (_b = result.width) !== null && _b !== void 0 ? _b : node.size.width;
66
- const removedLinks = [];
67
- if (this.kind === 'er') {
68
- const oldCols = ((_c = oldKit['columns']) !== null && _c !== void 0 ? _c : []);
69
- const newCols = ((_d = result.newKit['columns']) !== null && _d !== void 0 ? _d : []);
70
- const map = matchColumns(oldCols, newCols);
71
- for (const port of [...node.getPorts()]) {
72
- const args = (_e = port.layout) === null || _e === void 0 ? void 0 : _e.args;
73
- if (((_f = port.layout) === null || _f === void 0 ? void 0 : _f.strategy) !== 'absolute' || !args || args.units !== 'px' || args.y == null)
74
- continue;
75
- const oldIdx = rowIndexFromY(args.y);
76
- if (oldIdx < 0 || oldIdx >= oldCols.length)
77
- continue; // not a row-pinned field port
78
- const newIdx = map.get(oldIdx);
79
- if (newIdx === undefined) {
80
- // The column is gone — drop the port and every edge riding it.
81
- for (const link of model.getLinks()) {
82
- if (link.sourcePortId === port.id || link.targetPortId === port.id)
83
- removedLinks.push(link.serialize());
84
- }
85
- for (const link of model.getLinks().filter((l) => l.sourcePortId === port.id || l.targetPortId === port.id)) {
86
- model.removeLink(link.id);
87
- }
88
- node.removePort(port.id);
89
- }
90
- else {
91
- // Move the port to its column's NEW row; keep the ±dy spread. x tracks
92
- // the (possibly changed) width by side, so a widened table stays pinned.
93
- args.y = erRowCenterY(newIdx);
94
- const side = (_g = port.alignment) === null || _g === void 0 ? void 0 : _g.side;
95
- args.x = side === 'left' ? 0 : side === 'right' ? newWidth : newWidth / 2;
96
- // A nested-arg mutation is not tracked — nudge the port so the model
97
- // marks it dirty and the renderer repositions it this frame.
98
- port.setOffset(Object.assign({}, ((_h = port.offset) !== null && _h !== void 0 ? _h : { x: 0, y: 0 })));
99
- }
100
- }
101
- }
102
- node.setMetadata('html', { content: result.content, interactive: true });
103
- node.setMetadata(kitKey, result.newKit);
104
- node.setSize(newWidth, result.height);
105
- if (this.before)
106
- this.before.links = removedLinks;
107
- }
108
- undo(context) {
109
- var _a;
110
- const model = context.diagram;
111
- const node = model.getNode(this.nodeId);
112
- if (!node || !this.before)
113
- return;
114
- node.setMetadata('html', this.before.html);
115
- node.setMetadata(this.kind === 'er' ? 'kitEntity' : 'kitClass', this.before.kit);
116
- node.setSize(this.before.size.width, this.before.size.height);
117
- const beforeIds = new Set(this.before.ports.map((p) => p['id']));
118
- for (const p of [...node.getPorts()])
119
- if (!beforeIds.has(p.id))
120
- node.removePort(p.id);
121
- for (const sp of this.before.ports) {
122
- const live = node.getPort(sp['id']);
123
- if (live) {
124
- live.layout = sp['layout'];
125
- live.setOffset(Object.assign({}, ((_a = sp['offset']) !== null && _a !== void 0 ? _a : { x: 0, y: 0 })));
126
- }
127
- else {
128
- node.addPort(PortModel.fromJSON(sp));
129
- }
130
- }
131
- for (const sl of this.before.links) {
132
- if (!model.getLink(sl['id']))
133
- model.addLink(LinkModel.fromJSON(sl));
134
- }
135
- }
136
- canExecute(context) {
137
- var _a;
138
- return !!((_a = context.diagram) === null || _a === void 0 ? void 0 : _a.getNode(this.nodeId));
139
- }
140
- serialize() {
141
- return { id: this.id, name: this.name, timestamp: this.timestamp, data: { nodeId: this.nodeId, kind: this.kind } };
142
- }
143
- }
144
- /** Route a build through the engine's command stack (one undo step) or apply it directly. */
145
- function runUpdate(api, nodeId, kind, build) {
146
- return __awaiter(this, void 0, void 0, function* () {
147
- var _a, _b, _c, _d, _e;
148
- const model = (_a = api.getModel) === null || _a === void 0 ? void 0 : _a.call(api);
149
- const node = (_b = model === null || model === void 0 ? void 0 : model.getNode) === null || _b === void 0 ? void 0 : _b.call(model, nodeId);
150
- if (!model || !node)
151
- return false;
152
- const command = new UpdateCardCommand(nodeId, kind, build);
153
- const cm = (_d = (_c = api.getEngine) === null || _c === void 0 ? void 0 : _c.call(api)) === null || _d === void 0 ? void 0 : _d.commandManager;
154
- if (cm === null || cm === void 0 ? void 0 : cm.execute) {
155
- yield cm.execute(command); // ONE undoable step (Command enters history once)
156
- }
157
- else {
158
- command.execute({ diagram: model }); // no engine — apply, non-undoably
159
- }
160
- (_e = api.renderNow) === null || _e === void 0 ? void 0 : _e.call(api);
161
- return true;
162
- });
163
- }
164
- const isEditable = (node) => (node === null || node === void 0 ? void 0 : node.getMetadata('kitEditable')) === true;
165
- /**
166
- * Edit a rendered ER table in place. `delta` may change the name, the column
167
- * list (add / remove / reorder / rename / retype), and the width/height. Field
168
- * ports and their edges are reconciled so the diagram stays correct, as ONE
169
- * undoable step.
170
- *
171
- * @returns whether the node existed and the edit was applied.
172
- */
173
- export function updateEntity(api, entityId, delta) {
174
- var _a, _b, _c;
175
- const editable = isEditable((_c = (_b = (_a = api.getModel) === null || _a === void 0 ? void 0 : _a.call(api)) === null || _b === void 0 ? void 0 : _b.getNode) === null || _c === void 0 ? void 0 : _c.call(_b, entityId));
176
- const build = (oldKit) => {
177
- const newEntity = Object.assign({}, oldKit);
178
- if (delta.name !== undefined)
179
- newEntity.name = delta.name;
180
- if (delta.width !== undefined)
181
- newEntity.width = delta.width;
182
- if (delta.height !== undefined)
183
- newEntity.height = delta.height;
184
- if (delta.columns !== undefined)
185
- newEntity.columns = delta.columns;
186
- return {
187
- newKit: newEntity,
188
- content: entityCardContent(newEntity, editable),
189
- height: entityAutoHeight(newEntity, editable),
190
- width: newEntity.width,
191
- };
192
- };
193
- return runUpdate(api, entityId, 'er', build);
194
- }
195
- /**
196
- * Edit a rendered UML class in place. `delta` may change the name, stereotype,
197
- * abstract flag, attributes, methods, and width/height. One undoable step.
198
- */
199
- export function updateClass(api, classId, delta) {
200
- var _a, _b, _c;
201
- const editable = isEditable((_c = (_b = (_a = api.getModel) === null || _a === void 0 ? void 0 : _a.call(api)) === null || _b === void 0 ? void 0 : _b.getNode) === null || _c === void 0 ? void 0 : _c.call(_b, classId));
202
- const build = (oldKit) => {
203
- const newClass = Object.assign({}, oldKit);
204
- if (delta.name !== undefined)
205
- newClass.name = delta.name;
206
- if (delta.stereotype !== undefined)
207
- newClass.stereotype = delta.stereotype;
208
- if (delta.abstract !== undefined)
209
- newClass.abstract = delta.abstract;
210
- if (delta.width !== undefined)
211
- newClass.width = delta.width;
212
- if (delta.height !== undefined)
213
- newClass.height = delta.height;
214
- if (delta.attributes !== undefined)
215
- newClass.attributes = delta.attributes;
216
- if (delta.methods !== undefined)
217
- newClass.methods = delta.methods;
218
- return {
219
- newKit: newClass,
220
- content: classCardContent(newClass, editable),
221
- height: classAutoHeight(newClass, editable),
222
- width: newClass.width,
223
- };
224
- };
225
- return runUpdate(api, classId, 'uml', build);
226
- }
227
- // -- small column helpers, for the editing chrome and embedders --------------
228
- /** Append a column (immutably) — returns the new column array. */
229
- export function addColumnAt(columns, column, at = columns.length) {
230
- const next = columns.slice();
231
- next.splice(Math.max(0, Math.min(at, next.length)), 0, column);
232
- return next;
233
- }
234
- /** Remove the column at `index` — returns the new column array. */
235
- export function removeColumnAt(columns, index) {
236
- const next = columns.slice();
237
- next.splice(index, 1);
238
- return next;
239
- }
240
- /** Rename the column at `index` — returns a new array with a NEW object there. */
241
- export function renameColumnAt(columns, index, name) {
242
- return columns.map((c, i) => (i === index ? Object.assign(Object.assign({}, c), { name }) : c));
243
- }
244
- //# sourceMappingURL=update.js.map