@peter.prib/renderengine 0.2.6 → 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.
- package/README.md +4 -0
- package/REFERENCE.md +180 -2
- package/dist/renderengine-workspace.umd.js +8704 -5310
- package/dist/renderengine.umd.js +5675 -3398
- package/examples/archimate.html +68 -66
- package/examples/data-model.html +223 -13
- package/package.json +1 -1
- package/src/bian.js +182 -0
- package/src/charting/DiagramEditor.js +5 -26
- package/src/charting/DiagramTypeRegistry.js +8 -2
- package/src/charting/ModelCheckDialog.js +63 -0
- package/src/charting/ModelChecker.js +26 -0
- package/src/charting/ViewTabStrip.js +94 -81
- package/src/charting/datamodel/BianLandscapeReader.js +185 -0
- package/src/charting/datamodel/BianSemanticApiReader.js +183 -0
- package/src/charting/datamodel/DataModel.js +356 -10
- package/src/charting/datamodel/DataModelChecker.js +92 -0
- package/src/charting/datamodel/DataModelCompared.js +127 -0
- package/src/charting/datamodel/DataModelFileFormat.js +33 -0
- package/src/charting/datamodel/DataModelPerspective.js +45 -0
- package/src/charting/datamodel/DataType.js +108 -0
- package/src/charting/datamodel/EnumerationType.js +51 -0
- package/src/charting/datamodel/ServiceDomain.js +177 -0
- package/src/charting/datamodel/ServiceLandscape.js +104 -0
- package/src/charting/datamodel/StructuredType.js +63 -0
- package/src/charting/datamodel/index.js +10 -0
- package/src/charting/diagramModel/MergeModelCommand.js +43 -37
- package/src/charting/diagramModel/OpenViewList.js +75 -0
- package/src/charting/diagramModel/ViewSetChecker.js +22 -34
- package/src/charting/diagrams/DataModelRelationshipStrategy.js +12 -4
- package/src/charting/diagrams/EdgeGeometry.js +578 -553
- package/src/charting/diagrams/GraphDiagram.js +11 -1
- package/src/charting/diagrams/NodeGeometry.js +169 -158
- package/src/charting/glyphs/PersonGlyph.js +24 -23
- package/src/index.js +2666 -2664
- package/src/workspace/EditorShell.js +166 -67
- package/src/workspace/PanelLayout.js +56 -0
- package/src/workspace/layout/TabsNode.js +205 -191
- package/src/workspace/parts/ArchimateCanvasPart.js +5 -7
- package/src/workspace/parts/DataModelPart.js +339 -29
- package/src/workspace/parts/DiagramEditorPart.js +2 -8
- package/src/workspace/parts/EditorPart.js +461 -407
- package/src/workspace/parts/ModelFile.js +216 -187
- package/src/workspace/parts/ModelsPart.js +483 -192
- package/src/workspace/storage/BackupFolder.js +145 -0
- package/src/workspace/storage/KeyValueStore.js +92 -0
- package/src/workspace/storage/ModelLibrary.js +65 -0
- package/src/workspace/workArea.js +417 -413
- package/src/yaml/YamlReader.js +372 -0
|
@@ -1,67 +1,166 @@
|
|
|
1
|
-
import { el } from './dom.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
*
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
+
}
|