@peter.prib/renderengine 0.15.0 → 0.15.2
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/dist/renderengine-workspace.umd.js +776 -229
- package/dist/renderengine.umd.js +390 -107
- package/documentation/REFERENCE.md +496 -6
- package/examples/decision-workbench.html +26 -0
- package/package.json +2 -1
- package/src/charting/ArchimateDiagramEditor.js +2 -1
- package/src/charting/Edge.js +22 -0
- package/src/charting/archimate/ArchimateCanvasEditor.js +21 -5
- package/src/charting/archimate/ViewGroupTemplate.js +5 -2
- package/src/charting/diagramModel/DiagramJsonFileFormat.js +32 -0
- package/src/charting/diagramModel/DiagramModel.js +39 -0
- package/src/charting/diagramModel/DiagramView.js +97 -61
- package/src/charting/diagramModel/index.js +11 -10
- package/src/charting/diagrams/AddGroupToViewCommand.js +35 -0
- package/src/charting/diagrams/ArchimateModel.js +53 -13
- package/src/charting/diagrams/NestInstanceCommand.js +2 -1
- package/src/charting/templates/templateRegistry.js +70 -0
- package/src/index.js +4 -3
- package/src/workspace/index.js +3 -0
- package/src/workspace/parts/ArchimateEditorPart.js +2 -2
- package/src/workspace/parts/DecisionWorkbenchPart.js +58 -0
- package/src/workspace/parts/DiagramEditorPart.js +144 -131
- package/src/workspace/parts/ViewsPart.js +196 -0
|
@@ -1034,15 +1034,28 @@ export class ArchimateCanvasEditor extends DiagramEditor {
|
|
|
1034
1034
|
return element ? treeLayerOf(element) : 'other';
|
|
1035
1035
|
}
|
|
1036
1036
|
|
|
1037
|
-
/** An element's box holds any box
|
|
1037
|
+
/** An element's box holds any box; a group holds any box or nested group; on a canvas, a block holds boxes. */
|
|
1038
1038
|
canHold(parentId, childId) {
|
|
1039
|
+
if (!parentId || !childId || parentId === childId) return false;
|
|
1039
1040
|
const parent = this.templates.get(parentId), child = this.templates.get(childId);
|
|
1040
|
-
if (!parent
|
|
1041
|
+
if (!parent || !child) return false;
|
|
1042
|
+
if (child instanceof ViewGroupTemplate && parent instanceof ViewGroupTemplate) {
|
|
1043
|
+
return !this.isAncestor(childId, parentId);
|
|
1044
|
+
}
|
|
1045
|
+
if (parent instanceof ViewGroupTemplate) {
|
|
1046
|
+
return !this.isAncestor(childId, parentId);
|
|
1047
|
+
}
|
|
1048
|
+
if (child instanceof ViewGroupTemplate) return false;
|
|
1049
|
+
if (!parent.instance || !child.instance) return false;
|
|
1041
1050
|
return !!this._elementOf(parentId) || parent.instance.content?.archiVisualType === 'canvas-model-block';
|
|
1042
1051
|
}
|
|
1043
1052
|
|
|
1044
|
-
/** An element dropped on another nests there when a relationship a nesting stands for (NESTING_KINDS) is allowed between them. */
|
|
1053
|
+
/** An element dropped on another nests there when a relationship a nesting stands for (NESTING_KINDS) is allowed between them, or when dropped into a group. */
|
|
1045
1054
|
nestsOnDrop(parentId, childId) {
|
|
1055
|
+
const parent = this.templates.get(parentId);
|
|
1056
|
+
if (parent instanceof ViewGroupTemplate) {
|
|
1057
|
+
return this.canHold(parentId, childId);
|
|
1058
|
+
}
|
|
1046
1059
|
if (!this._elementOf(parentId) || !this._elementOf(childId)) return false;
|
|
1047
1060
|
return this.allowedKinds(parentId, childId).some(kind => NESTING_KINDS.includes(kind));
|
|
1048
1061
|
}
|
|
@@ -1050,13 +1063,16 @@ export class ArchimateCanvasEditor extends DiagramEditor {
|
|
|
1050
1063
|
nestCommand(childId, parentId) {
|
|
1051
1064
|
const child = this.templates.get(childId)?.instance;
|
|
1052
1065
|
if (!child) return null;
|
|
1053
|
-
|
|
1066
|
+
const parentTemplate = parentId ? this.templates.get(parentId) : null;
|
|
1067
|
+
const parentTarget = parentTemplate?.instance ?? (parentTemplate instanceof ViewGroupTemplate ? parentTemplate : null);
|
|
1068
|
+
return new NestInstanceCommand(child, parentTarget);
|
|
1054
1069
|
}
|
|
1055
1070
|
|
|
1056
|
-
/** After nesting an element in another: offer the relationship the nesting stands for (nestingKinds). */
|
|
1071
|
+
/** After nesting an element in another: offer the relationship the nesting stands for (nestingKinds). Groups have no relationship. */
|
|
1057
1072
|
nested(changes) {
|
|
1058
1073
|
let chain = Promise.resolve();
|
|
1059
1074
|
for (const { childId, parentId } of changes) {
|
|
1075
|
+
if (this.templates.get(parentId) instanceof ViewGroupTemplate) continue;
|
|
1060
1076
|
chain = chain.then(() => this.offerNestingRelationship(parentId, childId)); // taken out: nothing to offer
|
|
1061
1077
|
}
|
|
1062
1078
|
return chain;
|
|
@@ -47,8 +47,11 @@ export class ViewGroupTemplate extends GlyphTemplate {
|
|
|
47
47
|
return this.view.model.getFolder(this.id);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
get name() { return this.folder?.name ?? 'Group'; }
|
|
51
|
-
set name(value) {
|
|
50
|
+
get name() { return this.folder?.name ?? this.layout?.name ?? 'Group'; }
|
|
51
|
+
set name(value) {
|
|
52
|
+
if (this.folder) this.folder.name = value || 'Group';
|
|
53
|
+
if (this.layout) this.layout.name = value || 'Group';
|
|
54
|
+
}
|
|
52
55
|
|
|
53
56
|
// The layout keeps the top-left corner; a template is placed by its centre.
|
|
54
57
|
get x() { return this.layout.x + this.width / 2; }
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ModelFileFormat } from '../ModelFileFormat.js';
|
|
2
|
+
import { DiagramModel } from './DiagramModel.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* DiagramJsonFileFormat — a diagram model as a file (`.diagram.json` or `.json`):
|
|
6
|
+
* its name, palette category, line style defaults, views with templates, edges,
|
|
7
|
+
* waypoints and arrangements (DiagramModel#toJSON), read back by DiagramModel.fromJSON.
|
|
8
|
+
*/
|
|
9
|
+
export class DiagramJsonFileFormat extends ModelFileFormat {
|
|
10
|
+
get label() { return '.diagram.json'; }
|
|
11
|
+
get accept() { return '.diagram.json,.json'; }
|
|
12
|
+
get fileName() { return 'diagrams.diagram.json'; }
|
|
13
|
+
get featureName() { return 'DiagramJson'; }
|
|
14
|
+
|
|
15
|
+
/** JSON with a diagram model's shape: templateType and views array, or views array of diagrams. */
|
|
16
|
+
recognizes(text) {
|
|
17
|
+
try {
|
|
18
|
+
const json = JSON.parse(text);
|
|
19
|
+
return !!json && typeof json === 'object' && (Array.isArray(json.views) || typeof json.templateType === 'string');
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export(model) {
|
|
26
|
+
return { data: JSON.stringify(model.toJSON(), null, 2), mimeType: 'application/json' };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
import(text) {
|
|
30
|
+
return DiagramModel.fromJSON(JSON.parse(text));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -36,4 +36,43 @@ export class DiagramModel extends ViewSetModel {
|
|
|
36
36
|
setTemplateType(templateType) {
|
|
37
37
|
this.templateType = templateType;
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
/** The diagram model as plain JSON: its name, templateType, line style defaults, active view and views. */
|
|
41
|
+
toJSON() {
|
|
42
|
+
return {
|
|
43
|
+
version: 1,
|
|
44
|
+
name: this.name,
|
|
45
|
+
templateType: this.templateType,
|
|
46
|
+
edgeStyle: JSON.parse(JSON.stringify(this.edgeStyle ?? {})),
|
|
47
|
+
currentViewId: this.currentViewId,
|
|
48
|
+
views: this.views().map(v => v.toJSON()),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Reconstruct a DiagramModel from plain JSON. */
|
|
53
|
+
static fromJSON(json, historyOptions = {}) {
|
|
54
|
+
if (!json) throw new TypeError('DiagramModel.fromJSON requires a JSON object');
|
|
55
|
+
const model = new DiagramModel({
|
|
56
|
+
name: json.name ?? '',
|
|
57
|
+
templateType: json.templateType ?? 'generic',
|
|
58
|
+
edgeStyle: json.edgeStyle ?? {},
|
|
59
|
+
historyOptions,
|
|
60
|
+
});
|
|
61
|
+
const views = (json.views ?? []).map(vData => DiagramView.fromJSON(vData, model.historyOptions));
|
|
62
|
+
for (const view of views) {
|
|
63
|
+
model._insertView(model._adopt(view), { current: view.id === json.currentViewId });
|
|
64
|
+
}
|
|
65
|
+
if (json.currentViewId && model._views.has(json.currentViewId)) {
|
|
66
|
+
model.currentViewId = json.currentViewId;
|
|
67
|
+
} else if (model._views.size > 0 && !model.currentViewId) {
|
|
68
|
+
model.currentViewId = model._views.keys().next().value;
|
|
69
|
+
}
|
|
70
|
+
const maxNum = Math.max(0, ...model.views().map(v => {
|
|
71
|
+
const m = String(v.id).match(/\d+$/);
|
|
72
|
+
return m ? parseInt(m[0], 10) : 0;
|
|
73
|
+
}));
|
|
74
|
+
model._nextId = maxNum + 1;
|
|
75
|
+
model.markSaved();
|
|
76
|
+
return model;
|
|
77
|
+
}
|
|
39
78
|
}
|
|
@@ -1,61 +1,97 @@
|
|
|
1
|
-
import { DiagramEditorState } from '../DiagramEditorState.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
* @param {
|
|
16
|
-
* @param {
|
|
17
|
-
* @param {
|
|
18
|
-
* @param {
|
|
19
|
-
* @param {string} [options.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
this.
|
|
28
|
-
this.
|
|
29
|
-
this.
|
|
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
|
-
|
|
1
|
+
import { DiagramEditorState } from '../DiagramEditorState.js';
|
|
2
|
+
import { Edge } from '../Edge.js';
|
|
3
|
+
import { Arrangement } from '../arrangement/Arrangement.js';
|
|
4
|
+
import { templateClassFor, templateTypeKey } from '../templates/templateRegistry.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* DiagramView — one diagram (tab) of a DiagramModel: its shapes, lines,
|
|
8
|
+
* diagram-level line style and its own undo history (from DiagramEditorState).
|
|
9
|
+
*
|
|
10
|
+
* A DiagramEditor is bound directly to `templates`/`edges`, so edits land here
|
|
11
|
+
* with nothing to save back, and the history survives the editor being rebuilt.
|
|
12
|
+
*/
|
|
13
|
+
export class DiagramView extends DiagramEditorState {
|
|
14
|
+
/**
|
|
15
|
+
* @param {Object} options
|
|
16
|
+
* @param {string} options.id
|
|
17
|
+
* @param {string} options.title
|
|
18
|
+
* @param {Map<string, Template>} [options.templates]
|
|
19
|
+
* @param {Map<string, Edge>} [options.edges]
|
|
20
|
+
* @param {Object} [options.edgeStyle] - Diagram-level line style defaults
|
|
21
|
+
* @param {Object} [options.historyOptions] - Passed to CommandHistory (e.g. onChange)
|
|
22
|
+
* @param {string} [options.documentation]
|
|
23
|
+
* @param {Arrangement} [options.arrangement]
|
|
24
|
+
*/
|
|
25
|
+
constructor({ id, title, templates = new Map(), edges = new Map(), edgeStyle = {}, historyOptions = {}, documentation = undefined, arrangement = null }) {
|
|
26
|
+
super({ historyOptions, arrangement });
|
|
27
|
+
this.historyOptions = historyOptions;
|
|
28
|
+
this.id = id;
|
|
29
|
+
this.title = title;
|
|
30
|
+
this.documentation = documentation;
|
|
31
|
+
this.templates = templates;
|
|
32
|
+
this.edges = edges;
|
|
33
|
+
this.edgeStyle = edgeStyle;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The shapes are what stack. */
|
|
37
|
+
layerItems() {
|
|
38
|
+
return [...this.templates.values()];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The diagram as plain JSON: its shapes (by id and stable type), lines, line style and documentation. */
|
|
42
|
+
toJSON() {
|
|
43
|
+
return {
|
|
44
|
+
id: this.id,
|
|
45
|
+
title: this.title,
|
|
46
|
+
documentation: this.documentation,
|
|
47
|
+
templates: [...this.templates].map(([id, t]) => ({
|
|
48
|
+
id,
|
|
49
|
+
type: templateTypeKey(t),
|
|
50
|
+
name: t.name,
|
|
51
|
+
data: t.serialize(),
|
|
52
|
+
})),
|
|
53
|
+
edges: [...this.edges.values()].map(e => (typeof e.toJSON === 'function' ? e.toJSON() : JSON.parse(JSON.stringify(e)))),
|
|
54
|
+
edgeStyle: JSON.parse(JSON.stringify(this.edgeStyle ?? {})),
|
|
55
|
+
...(this.arrangement && { arrangement: this.arrangement.toJSON() }),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Reconstruct a DiagramView from plain JSON. */
|
|
60
|
+
static fromJSON(json, historyOptions = {}) {
|
|
61
|
+
if (!json) throw new TypeError('DiagramView.fromJSON requires a JSON object');
|
|
62
|
+
const templates = new Map();
|
|
63
|
+
for (const t of (json.templates ?? [])) {
|
|
64
|
+
const Class = templateClassFor(t.type);
|
|
65
|
+
templates.set(t.id, new Class(t.id, t.name, t.data ?? {}));
|
|
66
|
+
}
|
|
67
|
+
const edges = new Map();
|
|
68
|
+
for (const e of (json.edges ?? [])) {
|
|
69
|
+
edges.set(e.id, Edge.fromJSON(e));
|
|
70
|
+
}
|
|
71
|
+
const arrangement = json.arrangement ? Arrangement.fromJSON(json.arrangement) : null;
|
|
72
|
+
return new DiagramView({
|
|
73
|
+
id: json.id,
|
|
74
|
+
title: json.title,
|
|
75
|
+
templates,
|
|
76
|
+
edges,
|
|
77
|
+
edgeStyle: json.edgeStyle ?? {},
|
|
78
|
+
documentation: json.documentation,
|
|
79
|
+
historyOptions,
|
|
80
|
+
arrangement,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** An independent copy: shapes, lines, style and documentation are copied; history starts empty (same options). */
|
|
85
|
+
copy(id, title, historyOptions = this.historyOptions) {
|
|
86
|
+
return new DiagramView({
|
|
87
|
+
id,
|
|
88
|
+
title,
|
|
89
|
+
templates: new Map([...this.templates].map(([tid, t]) => [tid, new t.constructor(tid, t.name, t.serialize())])),
|
|
90
|
+
edges: new Map([...this.edges].map(([eid, e]) => [eid, e.clone()])),
|
|
91
|
+
edgeStyle: { ...this.edgeStyle },
|
|
92
|
+
documentation: this.documentation,
|
|
93
|
+
historyOptions,
|
|
94
|
+
arrangement: this.arrangement.copy(), // template ids are kept
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
export { DiagramModel } from './DiagramModel.js';
|
|
2
|
-
export { DiagramView } from './DiagramView.js';
|
|
3
|
-
export { ViewSetModel } from './ViewSetModel.js';
|
|
4
|
-
export { ModelSearch, SEARCH_FIELDS } from './ModelSearch.js';
|
|
5
|
-
export { DiagramModelSearch } from './DiagramModelSearch.js';
|
|
6
|
-
export { ReplaceTextCommand } from './ReplaceTextCommand.js';
|
|
7
|
-
export { ModelComparison } from './ModelComparison.js';
|
|
8
|
-
export { ComparedKind } from './ComparedKind.js';
|
|
9
|
-
export { ViewsCompared } from './ViewsCompared.js';
|
|
10
|
-
export { MergeModelCommand } from './MergeModelCommand.js';
|
|
1
|
+
export { DiagramModel } from './DiagramModel.js';
|
|
2
|
+
export { DiagramView } from './DiagramView.js';
|
|
3
|
+
export { ViewSetModel } from './ViewSetModel.js';
|
|
4
|
+
export { ModelSearch, SEARCH_FIELDS } from './ModelSearch.js';
|
|
5
|
+
export { DiagramModelSearch } from './DiagramModelSearch.js';
|
|
6
|
+
export { ReplaceTextCommand } from './ReplaceTextCommand.js';
|
|
7
|
+
export { ModelComparison } from './ModelComparison.js';
|
|
8
|
+
export { ComparedKind } from './ComparedKind.js';
|
|
9
|
+
export { ViewsCompared } from './ViewsCompared.js';
|
|
10
|
+
export { MergeModelCommand } from './MergeModelCommand.js';
|
|
11
|
+
export { DiagramJsonFileFormat } from './DiagramJsonFileFormat.js';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Command } from '../CommandHistory.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AddGroupToViewCommand — add a view-specific group to a view: its layout entry
|
|
5
|
+
* is stored in view.folderLayout. Undo removes it.
|
|
6
|
+
*/
|
|
7
|
+
export class AddGroupToViewCommand extends Command {
|
|
8
|
+
/**
|
|
9
|
+
* @param {ArchimateView} view
|
|
10
|
+
* @param {Object} [options]
|
|
11
|
+
* @param {string} [options.id]
|
|
12
|
+
* @param {string} [options.name]
|
|
13
|
+
* @param {number} [options.x]
|
|
14
|
+
* @param {number} [options.y]
|
|
15
|
+
* @param {number} [options.width]
|
|
16
|
+
* @param {number} [options.height]
|
|
17
|
+
* @param {number} [options.zIndex]
|
|
18
|
+
* @param {Object} [options.appearance]
|
|
19
|
+
* @param {string} [options.parentId]
|
|
20
|
+
*/
|
|
21
|
+
constructor(view, { id = `group-${Date.now().toString(36)}`, name = 'Group', x = 100, y = 100, width = 400, height = 140, zIndex = 0, appearance = {}, parentId = undefined } = {}) {
|
|
22
|
+
super('Add group to view');
|
|
23
|
+
this.view = view;
|
|
24
|
+
this.groupId = id;
|
|
25
|
+
this.entry = { x, y, width, height, name, zIndex, appearance, parentId };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
execute() {
|
|
29
|
+
this.view.folderLayout[this.groupId] = { ...this.entry };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
undo() {
|
|
33
|
+
delete this.view.folderLayout[this.groupId];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -17,6 +17,8 @@ import { DIAGRAM_BOX_KINDS } from '../canvas/diagramBoxKinds.js';
|
|
|
17
17
|
import { RemoveLineCommand } from './RemoveLineCommand.js';
|
|
18
18
|
import { CompositeCommand } from '../CommandHistory.js';
|
|
19
19
|
import { AddToViewCommand, DeleteElementCommand, AddRelationshipToViewCommand, RemoveRelationshipFromViewCommand } from './ArchimateCommands.js';
|
|
20
|
+
import { AddGroupToViewCommand } from './AddGroupToViewCommand.js';
|
|
21
|
+
import { RemoveGroupFromViewCommand } from './RemoveGroupFromViewCommand.js';
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
24
|
* Canonical, id-indexed home for ArchiMate model data (elements, relationships, views).
|
|
@@ -1171,27 +1173,65 @@ export class ArchimateView extends DiagramEditorState {
|
|
|
1171
1173
|
}
|
|
1172
1174
|
|
|
1173
1175
|
/**
|
|
1174
|
-
* The folders drawn on this view as groups:
|
|
1175
|
-
*
|
|
1176
|
-
*
|
|
1177
|
-
* folder sits on the view itself).
|
|
1176
|
+
* The folders drawn on this view as groups: both model repository folders
|
|
1177
|
+
* and view-specific groups with bounds in `folderLayout`, bounds applied,
|
|
1178
|
+
* each after its parent. `parentId` is the nearest ancestor also drawn here.
|
|
1178
1179
|
*/
|
|
1179
1180
|
get placedFolders() {
|
|
1180
|
-
const
|
|
1181
|
-
const
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1181
|
+
const drawn = new Set(Object.keys(this.folderLayout));
|
|
1182
|
+
const modelFolders = this.model.allFolders();
|
|
1183
|
+
const modelFolderIds = new Set(modelFolders.map(f => f.id));
|
|
1184
|
+
|
|
1185
|
+
const result = [];
|
|
1186
|
+
for (const folder of modelFolders.filter(f => this.folderLayout[f.id])) {
|
|
1187
|
+
result.push({
|
|
1188
|
+
...folder,
|
|
1189
|
+
...this.folderLayout[folder.id],
|
|
1190
|
+
name: this.folderLayout[folder.id].name ?? folder.name,
|
|
1191
|
+
parentId: this._nearestPlaced(folder.parentId, drawn),
|
|
1192
|
+
});
|
|
1193
|
+
}
|
|
1194
|
+
for (const [id, layout] of Object.entries(this.folderLayout)) {
|
|
1195
|
+
if (!modelFolderIds.has(id)) {
|
|
1196
|
+
result.push({
|
|
1197
|
+
id,
|
|
1198
|
+
name: layout.name || 'Group',
|
|
1199
|
+
...layout,
|
|
1200
|
+
parentId: layout.parentId && drawn.has(layout.parentId) ? layout.parentId : undefined,
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
return result;
|
|
1187
1205
|
}
|
|
1188
1206
|
|
|
1189
|
-
/** The group on this view an element is drawn in: its
|
|
1207
|
+
/** The group on this view an element is drawn in: its explicit view parent group, or its model folder's nearest drawn ancestor. */
|
|
1190
1208
|
placedFolderOf(elementId) {
|
|
1191
|
-
const
|
|
1209
|
+
const inst = this.elementInstancesFor(elementId)[0];
|
|
1210
|
+
if (inst?.archi?.parent && this.folderLayout[inst.archi.parent]) {
|
|
1211
|
+
return inst.archi.parent;
|
|
1212
|
+
}
|
|
1213
|
+
if (inst?.parentId && this.folderLayout[inst.parentId]) {
|
|
1214
|
+
return inst.parentId;
|
|
1215
|
+
}
|
|
1216
|
+
const drawn = new Set(Object.keys(this.folderLayout));
|
|
1192
1217
|
return this._nearestPlaced(this.model.getElementFolder(elementId), drawn);
|
|
1193
1218
|
}
|
|
1194
1219
|
|
|
1220
|
+
/** Add a view-specific group to this view. One undo step (AddGroupToViewCommand). */
|
|
1221
|
+
addGroup(spec = {}) {
|
|
1222
|
+
const id = spec.id || `group-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
1223
|
+
const cmd = new AddGroupToViewCommand(this, { id, ...spec });
|
|
1224
|
+
this.history.execute(cmd);
|
|
1225
|
+
return this.folderLayout[id];
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
/** Remove a group (view-specific or placed model folder) from this view. One undo step (RemoveGroupFromViewCommand). */
|
|
1229
|
+
removeGroup(groupId) {
|
|
1230
|
+
if (!this.folderLayout[groupId]) return false;
|
|
1231
|
+
this.history.execute(new RemoveGroupFromViewCommand(this, groupId));
|
|
1232
|
+
return true;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1195
1235
|
/** @private */
|
|
1196
1236
|
_nearestPlaced(folderId, drawn) {
|
|
1197
1237
|
for (let id = folderId; id; id = this.model.getFolder(id)?.parentId) {
|
|
@@ -23,7 +23,8 @@ export class NestInstanceCommand extends Command {
|
|
|
23
23
|
execute() {
|
|
24
24
|
const { instance, parent } = this;
|
|
25
25
|
const { parent: _was, ...rest } = instance.archi ?? {};
|
|
26
|
-
|
|
26
|
+
const parentId = parent ? (parent.visualId ?? parent.id) : null;
|
|
27
|
+
instance.archi = parentId ? { ...rest, parent: parentId } : (Object.keys(rest).length ? rest : null);
|
|
27
28
|
if (parent && (instance.zIndex ?? 0) <= (parent.zIndex ?? 0)) instance.zIndex = (parent.zIndex ?? 0) + 1;
|
|
28
29
|
}
|
|
29
30
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RectangleTemplate, RoundedRectangleTemplate, StadiumTemplate, PredefinedProcessTemplate,
|
|
3
|
+
ClassTemplate, ComponentTemplate, SwimlaneTemplate, CircleTemplate, FinalStateTemplate,
|
|
4
|
+
EllipseTemplate, DiamondTemplate, TriangleTemplate, PentagonTemplate, HexagonTemplate,
|
|
5
|
+
OctagonTemplate, StarTemplate, ParallelogramTemplate, TrapezoidTemplate, ManualInputTemplate,
|
|
6
|
+
CylinderTemplate, DocumentTemplate, NoteTemplate, CloudTemplate, ActorTemplate,
|
|
7
|
+
PackageTemplate, BoxTemplate, DataStoreTemplate, LifelineTemplate,
|
|
8
|
+
} from './index.js';
|
|
9
|
+
|
|
10
|
+
export const TEMPLATE_CLASSES = Object.freeze({
|
|
11
|
+
'rectangle': RectangleTemplate,
|
|
12
|
+
'rounded-rectangle': RoundedRectangleTemplate,
|
|
13
|
+
'stadium': StadiumTemplate,
|
|
14
|
+
'predefined-process': PredefinedProcessTemplate,
|
|
15
|
+
'class': ClassTemplate,
|
|
16
|
+
'component': ComponentTemplate,
|
|
17
|
+
'swimlane': SwimlaneTemplate,
|
|
18
|
+
'circle': CircleTemplate,
|
|
19
|
+
'final-state': FinalStateTemplate,
|
|
20
|
+
'ellipse': EllipseTemplate,
|
|
21
|
+
'diamond': DiamondTemplate,
|
|
22
|
+
'triangle': TriangleTemplate,
|
|
23
|
+
'pentagon': PentagonTemplate,
|
|
24
|
+
'hexagon': HexagonTemplate,
|
|
25
|
+
'octagon': OctagonTemplate,
|
|
26
|
+
'star': StarTemplate,
|
|
27
|
+
'parallelogram': ParallelogramTemplate,
|
|
28
|
+
'trapezoid': TrapezoidTemplate,
|
|
29
|
+
'manual-input': ManualInputTemplate,
|
|
30
|
+
'cylinder': CylinderTemplate,
|
|
31
|
+
'document': DocumentTemplate,
|
|
32
|
+
'note': NoteTemplate,
|
|
33
|
+
'cloud': CloudTemplate,
|
|
34
|
+
'actor': ActorTemplate,
|
|
35
|
+
'package': PackageTemplate,
|
|
36
|
+
'box': BoxTemplate,
|
|
37
|
+
'data-store': DataStoreTemplate,
|
|
38
|
+
'lifeline': LifelineTemplate,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const CLASS_TO_KEY = new Map(Object.entries(TEMPLATE_CLASSES).map(([k, c]) => [c, k]));
|
|
42
|
+
|
|
43
|
+
// Assign static templateType to each class
|
|
44
|
+
for (const [type, cls] of Object.entries(TEMPLATE_CLASSES)) {
|
|
45
|
+
cls.templateType = type;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Returns the stable serialized type key for a template instance or template class. */
|
|
49
|
+
export function templateTypeKey(templateOrClass) {
|
|
50
|
+
if (!templateOrClass) return 'rectangle';
|
|
51
|
+
const c = typeof templateOrClass === 'function' ? templateOrClass : templateOrClass.constructor;
|
|
52
|
+
if (CLASS_TO_KEY.has(c)) return CLASS_TO_KEY.get(c);
|
|
53
|
+
if (Object.hasOwn(c, 'templateType')) return c.templateType;
|
|
54
|
+
const name = c.name || '';
|
|
55
|
+
const withoutSuffix = name.replace(/Template$/, '');
|
|
56
|
+
const kebab = withoutSuffix.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
|
57
|
+
return TEMPLATE_CLASSES[kebab] ? kebab : 'rectangle';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Resolves a template class by its stable type key or class name, defaulting to RectangleTemplate. */
|
|
61
|
+
export function templateClassFor(key) {
|
|
62
|
+
if (!key) return RectangleTemplate;
|
|
63
|
+
if (TEMPLATE_CLASSES[key]) return TEMPLATE_CLASSES[key];
|
|
64
|
+
const lower = String(key).toLowerCase();
|
|
65
|
+
if (TEMPLATE_CLASSES[lower]) return TEMPLATE_CLASSES[lower];
|
|
66
|
+
for (const [k, c] of Object.entries(TEMPLATE_CLASSES)) {
|
|
67
|
+
if (c.name === key || c.name?.toLowerCase() === lower) return c;
|
|
68
|
+
}
|
|
69
|
+
return RectangleTemplate;
|
|
70
|
+
}
|
package/src/index.js
CHANGED
|
@@ -157,7 +157,8 @@ import { ArchimateElementTemplate, ArchimateEdge, ArchimateCanvasEditor, Archima
|
|
|
157
157
|
import { RelationshipInstance } from './charting/diagrams/RelationshipInstance.js';
|
|
158
158
|
import { DiagramEditor } from './charting/DiagramEditor.js';
|
|
159
159
|
import { DiagramEditorState } from './charting/DiagramEditorState.js';
|
|
160
|
-
import { Template, PolygonTemplate, RegularPolygonTemplate, RectangleTemplate, RoundedRectangleTemplate, StadiumTemplate, PredefinedProcessTemplate, ClassTemplate, ComponentTemplate, SwimlaneTemplate, CircleTemplate, FinalStateTemplate, EllipseTemplate, DiamondTemplate, TriangleTemplate, PentagonTemplate, HexagonTemplate, OctagonTemplate, StarTemplate, ParallelogramTemplate, TrapezoidTemplate, ManualInputTemplate, CylinderTemplate, DocumentTemplate, NoteTemplate, CloudTemplate, ActorTemplate, PackageTemplate, BoxTemplate, DataStoreTemplate, LifelineTemplate } from './charting/templates/index.js';
|
|
160
|
+
import { Template, PolygonTemplate, RegularPolygonTemplate, RectangleTemplate, RoundedRectangleTemplate, StadiumTemplate, PredefinedProcessTemplate, ClassTemplate, ComponentTemplate, SwimlaneTemplate, CircleTemplate, FinalStateTemplate, EllipseTemplate, DiamondTemplate, TriangleTemplate, PentagonTemplate, HexagonTemplate, OctagonTemplate, StarTemplate, ParallelogramTemplate, TrapezoidTemplate, ManualInputTemplate, CylinderTemplate, DocumentTemplate, NoteTemplate, CloudTemplate, ActorTemplate, PackageTemplate, BoxTemplate, DataStoreTemplate, LifelineTemplate } from './charting/templates/index.js';
|
|
161
|
+
import { TEMPLATE_CLASSES, templateTypeKey, templateClassFor } from './charting/templates/templateRegistry.js';
|
|
161
162
|
import { TemplateCatalog } from './charting/TemplateCatalog.js';
|
|
162
163
|
import { DIAGRAM_TYPES, getTemplatesForDiagramType, getAllDiagramTypes, getDiagramTypesByGroup } from './charting/DiagramTypeRegistry.js';
|
|
163
164
|
import { EdgeMarker, ArrowMarker, DiamondMarker, CircleMarker, CrowsFootMarker, HollowDiamondMarker, HollowTriangleMarker, OpenArrowMarker, HalfArrowMarker, EDGE_MARKERS, getEdgeMarker } from './charting/edgeMarkers/index.js';
|
|
@@ -166,7 +167,7 @@ import { MoveElementCommand, BringToFrontCommand, SendToBackCommand, RaiseComman
|
|
|
166
167
|
import { Edge } from './charting/Edge.js';
|
|
167
168
|
import { AddEdgeCommand, DeleteEdgeCommand, ReconnectEdgeCommand, MoveWaypointCommand, AddWaypointCommand, DeleteWaypointCommand } from './charting/DiagramEditorEdgeCommands.js';
|
|
168
169
|
import { EDGE_STYLE_PROPERTIES, LINE_ONLY_PROPERTIES, LINE_STYLE_DASH, EdgeStyleCascade, SetStyleCommand, EdgeStyleDialog } from './charting/edgeStyle/index.js';
|
|
169
|
-
import { DiagramModel, DiagramView, ViewSetModel } from './charting/diagramModel/index.js';
|
|
170
|
+
import { DiagramModel, DiagramView, ViewSetModel, DiagramJsonFileFormat } from './charting/diagramModel/index.js';
|
|
170
171
|
import { ViewMenu } from './charting/ViewMenu.js';
|
|
171
172
|
import { CanvasTemplateLibrary } from './charting/canvas/CanvasTemplateLibrary.js';
|
|
172
173
|
import { CanvasTemplateMenu } from './charting/canvas/CanvasTemplateMenu.js';
|
|
@@ -267,7 +268,7 @@ export {
|
|
|
267
268
|
TREE_MODE_CATALOG, treeModeRegistry, getTreeModeCatalogEntry,
|
|
268
269
|
AXIS_KIND, ENCODING_CHANNEL, COORD_SYSTEM, COLOR_MODE, CAMERA_MODE,
|
|
269
270
|
Command, CompositeCommand, CommandHistory,
|
|
270
|
-
DiagramEditor, DiagramEditorState, Template, PolygonTemplate, RegularPolygonTemplate, RectangleTemplate, RoundedRectangleTemplate, StadiumTemplate, PredefinedProcessTemplate, ClassTemplate, ComponentTemplate, SwimlaneTemplate, CircleTemplate, FinalStateTemplate, EllipseTemplate, DiamondTemplate, TriangleTemplate, PentagonTemplate, HexagonTemplate, OctagonTemplate, StarTemplate, ParallelogramTemplate, TrapezoidTemplate, ManualInputTemplate, CylinderTemplate, DocumentTemplate, NoteTemplate, CloudTemplate, ActorTemplate, PackageTemplate, BoxTemplate, DataStoreTemplate, LifelineTemplate, TemplateCatalog, Palette, PropertiesPanel, MenuBar, MoveElementCommand, BringToFrontCommand, SendToBackCommand, RaiseCommand, LowerCommand, CreateTemplateCommand, EditPropertyCommand, ResizeCommand, RotateCommand, SetPropertiesCommand, ReorderCommand, NestElementCommand, DIAGRAM_TYPES, getTemplatesForDiagramType, getAllDiagramTypes, getDiagramTypesByGroup, EdgeMarker, ArrowMarker, DiamondMarker, CircleMarker, CrowsFootMarker, HollowDiamondMarker, HollowTriangleMarker, OpenArrowMarker, HalfArrowMarker, EDGE_MARKERS, getEdgeMarker, Edge, AddEdgeCommand, DeleteEdgeCommand, ReconnectEdgeCommand, EDGE_STYLE_PROPERTIES, LINE_ONLY_PROPERTIES, LINE_STYLE_DASH, EdgeStyleCascade, SetStyleCommand, EdgeStyleDialog, DiagramModel, DiagramView, ViewSetModel, ViewMenu, ViewPropertiesDialog, SettingsViewPropertiesDialog, DiagramViewPropertiesDialog, inheritableFields, readInheritableFields, LocalJsonStore, PopupMenu, ViewTabStrip, EditorShell, Dialog, FormDialog, ToolbarItem, ToolbarButton, ToolbarMenu, ToolbarSelect, ToolbarCheckbox, ToolbarText, ToolbarSeparator, pickFile, reorderedZ, stackingOrder, MoveWaypointCommand, AddWaypointCommand, DeleteWaypointCommand,
|
|
271
|
+
DiagramEditor, DiagramEditorState, Template, PolygonTemplate, RegularPolygonTemplate, RectangleTemplate, RoundedRectangleTemplate, StadiumTemplate, PredefinedProcessTemplate, ClassTemplate, ComponentTemplate, SwimlaneTemplate, CircleTemplate, FinalStateTemplate, EllipseTemplate, DiamondTemplate, TriangleTemplate, PentagonTemplate, HexagonTemplate, OctagonTemplate, StarTemplate, ParallelogramTemplate, TrapezoidTemplate, ManualInputTemplate, CylinderTemplate, DocumentTemplate, NoteTemplate, CloudTemplate, ActorTemplate, PackageTemplate, BoxTemplate, DataStoreTemplate, LifelineTemplate, TEMPLATE_CLASSES, templateTypeKey, templateClassFor, TemplateCatalog, Palette, PropertiesPanel, MenuBar, MoveElementCommand, BringToFrontCommand, SendToBackCommand, RaiseCommand, LowerCommand, CreateTemplateCommand, EditPropertyCommand, ResizeCommand, RotateCommand, SetPropertiesCommand, ReorderCommand, NestElementCommand, DIAGRAM_TYPES, getTemplatesForDiagramType, getAllDiagramTypes, getDiagramTypesByGroup, EdgeMarker, ArrowMarker, DiamondMarker, CircleMarker, CrowsFootMarker, HollowDiamondMarker, HollowTriangleMarker, OpenArrowMarker, HalfArrowMarker, EDGE_MARKERS, getEdgeMarker, Edge, AddEdgeCommand, DeleteEdgeCommand, ReconnectEdgeCommand, EDGE_STYLE_PROPERTIES, LINE_ONLY_PROPERTIES, LINE_STYLE_DASH, EdgeStyleCascade, SetStyleCommand, EdgeStyleDialog, DiagramModel, DiagramView, ViewSetModel, DiagramJsonFileFormat, ViewMenu, ViewPropertiesDialog, SettingsViewPropertiesDialog, DiagramViewPropertiesDialog, inheritableFields, readInheritableFields, LocalJsonStore, PopupMenu, ViewTabStrip, EditorShell, Dialog, FormDialog, ToolbarItem, ToolbarButton, ToolbarMenu, ToolbarSelect, ToolbarCheckbox, ToolbarText, ToolbarSeparator, pickFile, reorderedZ, stackingOrder, MoveWaypointCommand, AddWaypointCommand, DeleteWaypointCommand,
|
|
271
272
|
ebnfAstToDiagram, parseEbnf,
|
|
272
273
|
createDataWindow, GraphDataWindow,
|
|
273
274
|
ArchimateNotation, ArchimateLayer, ArchimateElementType, ArchimateRelationshipType,
|
package/src/workspace/index.js
CHANGED
|
@@ -56,6 +56,9 @@ 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
|
+
// Chart views in one part (they use ChartPart, so they sit with it, outside BUILTIN_PARTS).
|
|
60
|
+
export { ViewsPart } from './parts/ViewsPart.js';
|
|
61
|
+
export { DecisionWorkbenchPart } from './parts/DecisionWorkbenchPart.js';
|
|
59
62
|
export { DataModelPart } from './parts/DataModelPart.js';
|
|
60
63
|
export { RequirementsPart } from './parts/RequirementsPart.js';
|
|
61
64
|
export { ModelHosted } from './parts/ModelHosted.js';
|
|
@@ -11,8 +11,8 @@ const DEFAULT_FEATURES = Object.freeze({
|
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* ArchimateEditorPart —
|
|
15
|
-
*
|
|
14
|
+
* ArchimateEditorPart — legacy GraphDiagram-based ArchiMate editor part.
|
|
15
|
+
* @deprecated Use ArchimateCanvasPart for ArchiMate editing.
|
|
16
16
|
*
|
|
17
17
|
* Spec (JSON): `elements`, `relationships`, `views` (as ArchimateModel takes
|
|
18
18
|
* them), `features` (toolbar controls, see DEFAULT_FEATURES), `analysis`
|