@peter.prib/renderengine 0.2.5 → 0.2.7

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 (67) hide show
  1. package/README.md +219 -213
  2. package/REFERENCE.md +272 -6
  3. package/dist/renderengine-workspace.umd.js +11980 -7421
  4. package/dist/renderengine.umd.js +8991 -5784
  5. package/examples/archimate.html +68 -66
  6. package/examples/data-model.html +534 -0
  7. package/package.json +70 -68
  8. package/src/bian.js +182 -0
  9. package/src/charting/CheckboxTreeSelector.js +373 -304
  10. package/src/charting/DiagramEditor.js +53 -32
  11. package/src/charting/DiagramTypeRegistry.js +13 -3
  12. package/src/charting/ModelCheckDialog.js +63 -0
  13. package/src/charting/ModelChecker.js +26 -0
  14. package/src/charting/ViewTabStrip.js +94 -81
  15. package/src/charting/archimate/ArchimateCanvasEditor.js +825 -815
  16. package/src/charting/archimate/ArchimateElementTemplate.js +208 -195
  17. package/src/charting/datamodel/BianLandscapeReader.js +185 -0
  18. package/src/charting/datamodel/BianSemanticApiReader.js +183 -0
  19. package/src/charting/datamodel/DataModel.js +645 -0
  20. package/src/charting/datamodel/DataModelChecker.js +92 -0
  21. package/src/charting/datamodel/DataModelCompared.js +127 -0
  22. package/src/charting/datamodel/DataModelContext.js +108 -0
  23. package/src/charting/datamodel/DataModelFileFormat.js +33 -0
  24. package/src/charting/datamodel/DataModelPerspective.js +129 -0
  25. package/src/charting/datamodel/DataType.js +108 -0
  26. package/src/charting/datamodel/EnumerationType.js +51 -0
  27. package/src/charting/datamodel/LevelMapping.js +38 -0
  28. package/src/charting/datamodel/LevelSchema.js +41 -0
  29. package/src/charting/datamodel/ServiceDomain.js +177 -0
  30. package/src/charting/datamodel/ServiceLandscape.js +104 -0
  31. package/src/charting/datamodel/StructuredType.js +63 -0
  32. package/src/charting/datamodel/index.js +15 -0
  33. package/src/charting/diagramModel/MergeModelCommand.js +43 -37
  34. package/src/charting/diagramModel/OpenViewList.js +75 -0
  35. package/src/charting/diagramModel/ViewSetChecker.js +22 -34
  36. package/src/charting/diagrams/DataModelDiagram.js +160 -86
  37. package/src/charting/diagrams/DataModelRelationshipStrategy.js +94 -38
  38. package/src/charting/diagrams/EdgeGeometry.js +578 -553
  39. package/src/charting/diagrams/GraphDiagram.js +11 -1
  40. package/src/charting/diagrams/NodeGeometry.js +169 -158
  41. package/src/charting/diagrams/nesting.js +31 -0
  42. package/src/charting/glyphs/PersonGlyph.js +24 -23
  43. package/src/charting/modelTree/ModelTree.js +202 -164
  44. package/src/charting/modelTree/ModelTreePanel.js +36 -3
  45. package/src/charting/modelTree/TreeViewMode.js +131 -110
  46. package/src/charting/modelTree/modes/FlatTreeMode.js +32 -46
  47. package/src/charting/modelTree/modes/LayerGroupedTreeMode.js +47 -62
  48. package/src/charting/modelTree/modes/TypeGroupedTreeMode.js +46 -60
  49. package/src/datamodel.js +132 -122
  50. package/src/diagramCatalog.js +35 -30
  51. package/src/index.js +2666 -2659
  52. package/src/workspace/EditorShell.js +166 -67
  53. package/src/workspace/PanelLayout.js +56 -0
  54. package/src/workspace/index.js +1 -0
  55. package/src/workspace/layout/TabsNode.js +205 -191
  56. package/src/workspace/parts/ArchimateCanvasPart.js +10 -9
  57. package/src/workspace/parts/ChartPart.js +6 -1
  58. package/src/workspace/parts/DataModelPart.js +544 -0
  59. package/src/workspace/parts/DiagramEditorPart.js +2 -8
  60. package/src/workspace/parts/EditorPart.js +461 -407
  61. package/src/workspace/parts/ModelFile.js +216 -187
  62. package/src/workspace/parts/ModelsPart.js +483 -192
  63. package/src/workspace/storage/BackupFolder.js +145 -0
  64. package/src/workspace/storage/KeyValueStore.js +92 -0
  65. package/src/workspace/storage/ModelLibrary.js +65 -0
  66. package/src/workspace/workArea.js +417 -413
  67. package/src/yaml/YamlReader.js +372 -0
@@ -1,67 +1,166 @@
1
- import { el } from './dom.js';
2
-
3
- /**
4
- * EditorShell — the workbench layout diagram editors share: a toolbar row, a
5
- * side panel (model tree, palette), the main area (tabs + canvas) and an
6
- * optional right-hand aside. Built with el() and inline styles, so it needs no
7
- * stylesheet and never restyles the host page.
8
- *
9
- * Not a custom element, so the engine bundle can use it under node too.
10
- */
11
- export class EditorShell {
12
- /**
13
- * @param {Object} [options]
14
- * @param {Document} [options.document]
15
- * @param {string} [options.sideWidth]
16
- * @param {boolean} [options.aside] - Add a right-hand panel
17
- * @param {string} [options.asideWidth]
18
- * @param {Object<string, string>} [options.ids] - Element ids per region: toolbar, side, main, aside
19
- * @param {boolean} [options.fullPage] - Fill the viewport (and drop the body margin)
20
- */
21
- constructor({ document: doc = document, sideWidth = '280px', aside = false, asideWidth = '300px', ids = {}, fullPage = false } = {}) {
22
- this.doc = doc;
23
- this.sideWidth = sideWidth;
24
- this.aside = aside;
25
- this.asideWidth = asideWidth;
26
- this.ids = ids;
27
- this.fullPage = fullPage;
28
- }
29
-
30
- /** @returns {{root: HTMLElement, toolbar: HTMLElement, side: HTMLElement, main: HTMLElement, aside?: HTMLElement}} */
31
- build() {
32
- const regions = {};
33
- const region = (name, style) => ({
34
- attrs: { id: this.ids[name] ?? null, 'data-shell-region': name },
35
- style,
36
- ref: (n) => { regions[name] = n; },
37
- });
38
- const panel = { background: '#fafafa', overflowY: 'auto', minHeight: '0', boxSizing: 'border-box' };
39
- regions.root = el({
40
- attrs: { 'data-editor-shell': '' },
41
- style: {
42
- display: 'flex', flexDirection: 'column', height: this.fullPage ? '100vh' : '100%', minHeight: '0',
43
- fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", color: '#333', background: '#f5f5f5',
44
- },
45
- children: [
46
- region('toolbar', { flex: '0 0 auto' }),
47
- {
48
- style: { display: 'flex', flex: '1 1 auto', minHeight: '0', overflow: 'hidden' },
49
- children: [
50
- region('side', { ...panel, flex: `0 0 ${this.sideWidth}`, borderRight: '1px solid #ddd' }),
51
- region('main', { flex: '1 1 auto', display: 'flex', flexDirection: 'column', minWidth: '0', minHeight: '0', background: '#fff', position: 'relative' }),
52
- this.aside && region('aside', { ...panel, flex: `0 0 ${this.asideWidth}`, borderLeft: '1px solid #ddd' }),
53
- ].filter(Boolean),
54
- },
55
- ],
56
- }, this.doc);
57
- return regions;
58
- }
59
-
60
- /** Replace the host's contents with the shell. */
61
- mount(host) {
62
- const regions = this.build();
63
- host.replaceChildren(regions.root);
64
- if (this.fullPage) this.doc.body.style.margin = '0';
65
- return regions;
66
- }
67
- }
1
+ import { el } from './dom.js';
2
+ import { PanelLayout } from './PanelLayout.js';
3
+
4
+ /** How wide a splitter is, and a folded panel's stub. */
5
+ const SPLITTER_WIDTH = 6;
6
+ const STUB_WIDTH = 24;
7
+
8
+ /**
9
+ * EditorShell — the workbench layout diagram editors share: a toolbar row, a
10
+ * side panel (model tree, palette), the main area (tabs + canvas) and an
11
+ * optional right-hand aside. Built with el() and inline styles, so it needs no
12
+ * stylesheet and never restyles the host page.
13
+ *
14
+ * With a `layout` (PanelLayout, kept by the part so it outlives this shell), a
15
+ * side panel's inner edge is a splitter: drag it to resize the panel, drag it
16
+ * nearly shut or double-click it to fold the panel to a stub (a strip with its
17
+ * name), and click the stub to open it at the width it had.
18
+ *
19
+ * Not a custom element, so the engine bundle can use it under node too.
20
+ */
21
+ export class EditorShell {
22
+ /**
23
+ * @param {Object} [options]
24
+ * @param {Document} [options.document]
25
+ * @param {string} [options.sideWidth]
26
+ * @param {boolean} [options.aside] - Add a right-hand panel
27
+ * @param {string} [options.asideWidth]
28
+ * @param {Object<string, string>} [options.ids] - Element ids per region: toolbar, side, main, aside
29
+ * @param {boolean} [options.fullPage] - Fill the viewport (and drop the body margin)
30
+ * @param {PanelLayout} [options.layout] - Resizable, foldable side panels (their widths kept here)
31
+ * @param {{side?: string, aside?: string}} [options.labels] - What a folded panel's stub says
32
+ */
33
+ constructor({ document: doc = document, sideWidth = '280px', aside = false, asideWidth = '300px', ids = {}, fullPage = false, layout = null, labels = {} } = {}) {
34
+ this.doc = doc;
35
+ this.sideWidth = sideWidth;
36
+ this.aside = aside;
37
+ this.asideWidth = asideWidth;
38
+ this.ids = ids;
39
+ this.fullPage = fullPage;
40
+ this.layout = layout;
41
+ this.labels = { side: 'Side panel', aside: 'Panel', ...labels };
42
+ /** Each side panel's elements (with a layout): region, splitter, stub. */
43
+ this._panels = {};
44
+ }
45
+
46
+ /** @returns {{root: HTMLElement, toolbar: HTMLElement, side: HTMLElement, main: HTMLElement, aside?: HTMLElement}} */
47
+ build() {
48
+ const regions = {};
49
+ const region = (name, style) => ({
50
+ attrs: { id: this.ids[name] ?? null, 'data-shell-region': name },
51
+ style,
52
+ ref: (n) => { regions[name] = n; },
53
+ });
54
+ const panel = { background: '#fafafa', overflowY: 'auto', minHeight: '0', boxSizing: 'border-box' };
55
+ const width = (name, given) => (this.layout ? `${this.layout.panel(name, parseInt(given, 10)).width}px` : given);
56
+ // With a layout: [stub] side [splitter] main [splitter] aside [stub], the stubs at the outer edges.
57
+ const extra = (make) => (this.layout ? [make()] : []);
58
+ const row = [
59
+ ...extra(() => this._stub('side')),
60
+ region('side', { ...panel, flex: `0 0 ${width('side', this.sideWidth)}`, borderRight: '1px solid #ddd' }),
61
+ ...extra(() => this._splitter('side')),
62
+ region('main', { flex: '1 1 auto', display: 'flex', flexDirection: 'column', minWidth: '0', minHeight: '0', background: '#fff', position: 'relative' }),
63
+ ...(this.aside ? [
64
+ ...extra(() => this._splitter('aside')),
65
+ region('aside', { ...panel, flex: `0 0 ${width('aside', this.asideWidth)}`, borderLeft: '1px solid #ddd' }),
66
+ ...extra(() => this._stub('aside')),
67
+ ] : []),
68
+ ];
69
+ regions.root = el({
70
+ attrs: { 'data-editor-shell': '' },
71
+ style: {
72
+ display: 'flex', flexDirection: 'column', height: this.fullPage ? '100vh' : '100%', minHeight: '0',
73
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", color: '#333', background: '#f5f5f5',
74
+ },
75
+ children: [
76
+ region('toolbar', { flex: '0 0 auto' }),
77
+ { style: { display: 'flex', flex: '1 1 auto', minHeight: '0', overflow: 'hidden' }, children: row },
78
+ ],
79
+ }, this.doc);
80
+ for (const name of Object.keys(this._panels)) {
81
+ this._panels[name].region = regions[name];
82
+ this._show(name);
83
+ }
84
+ return regions;
85
+ }
86
+
87
+ /** A folded panel's strip: its name, upright; a click opens it. @private */
88
+ _stub(name) {
89
+ const parts = (this._panels[name] ??= {});
90
+ return {
91
+ attrs: { 'data-shell-stub': name, role: 'button', tabindex: '0', title: `Show ${this.labels[name]}`, 'aria-label': `Show ${this.labels[name]}` },
92
+ style: {
93
+ flex: `0 0 ${STUB_WIDTH}px`, display: 'none', alignItems: 'center', justifyContent: 'flex-start', paddingTop: '8px',
94
+ writingMode: 'vertical-rl', background: '#eee', borderRight: name === 'side' ? '1px solid #ddd' : 'none',
95
+ borderLeft: name === 'aside' ? '1px solid #ddd' : 'none', cursor: 'pointer', fontSize: '12px', color: '#555', userSelect: 'none',
96
+ },
97
+ text: this.labels[name],
98
+ on: {
99
+ click: () => this.toggle(name),
100
+ keydown: (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); this.toggle(name); } },
101
+ },
102
+ ref: (n) => { parts.stub = n; },
103
+ };
104
+ }
105
+
106
+ /** A panel's inner edge: drag to resize (nearly shut folds it), double-click to fold or open. @private */
107
+ _splitter(name) {
108
+ const parts = (this._panels[name] ??= {});
109
+ return {
110
+ attrs: { 'data-shell-splitter': name, role: 'separator', 'aria-orientation': 'vertical', title: 'Drag to resize; double-click to fold' },
111
+ style: { flex: `0 0 ${SPLITTER_WIDTH}px`, cursor: 'col-resize', background: 'transparent', touchAction: 'none' },
112
+ on: {
113
+ pointerdown: (e) => this._drag(name, e),
114
+ dblclick: () => this.toggle(name),
115
+ },
116
+ ref: (n) => { parts.splitter = n; },
117
+ };
118
+ }
119
+
120
+ /** Follow a drag of a panel's edge until the pointer is let go. @private */
121
+ _drag(name, down) {
122
+ const p = this.layout.panels[name];
123
+ if (!p || p.collapsed) return;
124
+ down.preventDefault();
125
+ const start = down.clientX, width = p.width;
126
+ const sign = name === 'side' ? 1 : -1; // the aside grows leftwards
127
+ const win = this.doc.defaultView ?? globalThis;
128
+ const move = (e) => this.resize(name, width + sign * (e.clientX - start));
129
+ const up = () => { win.removeEventListener('pointermove', move); win.removeEventListener('pointerup', up); };
130
+ win.addEventListener('pointermove', move);
131
+ win.addEventListener('pointerup', up);
132
+ }
133
+
134
+ /** Give a panel a width (PanelLayout#resize: nearly shut folds it), and show it so. */
135
+ resize(name, width) {
136
+ this.layout?.resize(name, width);
137
+ this._show(name);
138
+ }
139
+
140
+ /** Fold a panel to its stub, or open it again. */
141
+ toggle(name) {
142
+ this.layout?.toggle(name);
143
+ this._show(name);
144
+ }
145
+
146
+ /** Show a panel as its layout says: open at its width with its splitter, or folded to its stub. @private */
147
+ _show(name) {
148
+ const parts = this._panels[name];
149
+ const p = this.layout?.panels[name];
150
+ if (!parts?.region || !p) return;
151
+ parts.region.style.display = p.collapsed ? 'none' : '';
152
+ parts.region.style.flex = `0 0 ${p.width}px`;
153
+ if (parts.splitter) parts.splitter.style.display = p.collapsed ? 'none' : '';
154
+ if (parts.stub) parts.stub.style.display = p.collapsed ? 'flex' : 'none';
155
+ }
156
+
157
+ /** Replace the host's contents with the shell. */
158
+ mount(host) {
159
+ const regions = this.build();
160
+ host.replaceChildren(regions.root);
161
+ if (this.fullPage) this.doc.body.style.margin = '0';
162
+ return regions;
163
+ }
164
+ }
165
+
166
+ export { SPLITTER_WIDTH, STUB_WIDTH };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * PanelLayout — the widths of an editor's side panels (EditorShell's `side`
3
+ * and `aside`) and which are folded to a stub, kept by the part so they
4
+ * survive the shell being built anew on every render. Pure: EditorShell's
5
+ * splitters and stubs call it, and tests need no DOM.
6
+ *
7
+ * Dragged narrower than `collapseBelow`, a panel folds; opened again, it
8
+ * takes back the width it had.
9
+ */
10
+ export class PanelLayout {
11
+ /** Narrower than this a panel folds to its stub. */
12
+ static COLLAPSE_BELOW = 60;
13
+ static MIN_WIDTH = 120;
14
+ static MAX_WIDTH = 900;
15
+
16
+ /**
17
+ * @param {Object<string, {width: number, collapsed?: boolean}>} [panels] - By name ('side', 'aside')
18
+ */
19
+ constructor(panels = {}) {
20
+ this.panels = Object.fromEntries(Object.entries(panels).map(([name, p]) => [name, { width: p.width, collapsed: !!p.collapsed }]));
21
+ }
22
+
23
+ /** A panel's state, made with `width` the first time it is asked for. */
24
+ panel(name, width) {
25
+ return (this.panels[name] ??= { width, collapsed: false });
26
+ }
27
+
28
+ /** A panel's width as drawn: 0 folded, else its own. */
29
+ widthOf(name) {
30
+ const p = this.panels[name];
31
+ return !p ? null : p.collapsed ? 0 : p.width;
32
+ }
33
+
34
+ /**
35
+ * A drag of the panel's edge to `width`: narrower than COLLAPSE_BELOW folds
36
+ * it (keeping the width it had), else it takes the width, kept within bounds.
37
+ */
38
+ resize(name, width) {
39
+ const p = this.panels[name];
40
+ if (!p) return null;
41
+ if (width < PanelLayout.COLLAPSE_BELOW) p.collapsed = true;
42
+ else Object.assign(p, { collapsed: false, width: Math.round(Math.min(PanelLayout.MAX_WIDTH, Math.max(PanelLayout.MIN_WIDTH, width))) });
43
+ return p;
44
+ }
45
+
46
+ /** Fold a panel to its stub, or open it at the width it had. */
47
+ toggle(name) {
48
+ const p = this.panels[name];
49
+ if (p) p.collapsed = !p.collapsed;
50
+ return p;
51
+ }
52
+
53
+ toJSON() {
54
+ return Object.fromEntries(Object.entries(this.panels).map(([name, p]) => [name, { ...p }]));
55
+ }
56
+ }
@@ -56,6 +56,7 @@ export {
56
56
  LIGHT_THEME, DARK_THEME, MIDNIGHT_THEME, SEPIA_THEME,
57
57
  } from './themes.js';
58
58
  export { ChartPart } from './parts/ChartPart.js';
59
+ export { DataModelPart } from './parts/DataModelPart.js';
59
60
  export { EditorPart } from './parts/EditorPart.js';
60
61
  export { DiagramEditorPart } from './parts/DiagramEditorPart.js';
61
62
  export { ArchimateEditorPart } from './parts/ArchimateEditorPart.js';