@peter.prib/renderengine 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +480 -302
- package/bin/renderengine.mjs +157 -0
- package/dist/renderengine-workspace.umd.js +49217 -37484
- package/dist/renderengine.umd.js +24899 -13093
- package/documentation/REFERENCE.md +68 -1
- package/package.json +13 -3
- package/src/CanvasRenderer.js +225 -200
- package/src/SVGRenderer.js +476 -454
- package/src/archimate.js +22 -2
- package/src/archimateArchiFormat.js +1735 -1701
- package/src/archimateArchive.js +36 -7
- package/src/archimateCSV.js +93 -37
- package/src/archimateDrawio.js +2 -0
- package/src/archimateExchangeFormat.js +3 -1
- package/src/chartCatalog.js +269 -3
- package/src/charting/Annotations.js +169 -0
- package/src/charting/ArchimateDiagramEditor.js +54 -10
- package/src/charting/Axis.js +217 -5
- package/src/charting/Chart.js +99 -9
- package/src/charting/Chart3D.js +7 -2
- package/src/charting/ChartBox.js +8 -1
- package/src/charting/ColorPalette.js +42 -4
- package/src/charting/DiagramEditor.js +174 -39
- package/src/charting/DiagramEditorCommands.js +28 -0
- package/src/charting/Edge.js +7 -0
- package/src/charting/Hierarchy.js +142 -0
- package/src/charting/InteractionManager.js +4 -0
- package/src/charting/ModelFileFormat.js +5 -1
- package/src/charting/SettingsManager.js +35 -0
- package/src/charting/ShapePaths.js +82 -10
- package/src/charting/archimate/ArchimateCsvFileFormat.js +51 -12
- package/src/charting/archimate/ArchimateElementTemplate.js +235 -231
- package/src/charting/ask/AskBar.js +388 -0
- package/src/charting/ask/AskView.js +400 -0
- package/src/charting/charts/AlluvialDiagram.js +311 -0
- package/src/charting/charts/AreaChart.js +69 -15
- package/src/charting/charts/BarChart.js +38 -3
- package/src/charting/charts/BarChart3D.js +6 -4
- package/src/charting/charts/BarChart3DOrbit.js +3 -1
- package/src/charting/charts/BarChartBase.js +26 -6
- package/src/charting/charts/BeeswarmChart.js +164 -0
- package/src/charting/charts/BoxPlotChart.js +25 -3
- package/src/charting/charts/BubbleChart.js +4 -1
- package/src/charting/charts/CalendarHeatmapChart.js +2 -1
- package/src/charting/charts/CandlestickChart.js +172 -4
- package/src/charting/charts/ChordDiagram.js +107 -4
- package/src/charting/charts/CirclePackChart.js +81 -0
- package/src/charting/charts/ComboChart.js +233 -0
- package/src/charting/charts/ContourChart.js +310 -0
- package/src/charting/charts/DensityChart.js +1 -1
- package/src/charting/charts/DonutChart3D.js +13 -0
- package/src/charting/charts/DumbbellChart.js +217 -0
- package/src/charting/charts/EcdfChart.js +150 -0
- package/src/charting/charts/FunnelChart.js +5 -2
- package/src/charting/charts/GeoMapChart.js +132 -11
- package/src/charting/charts/GroupedBarChart.js +44 -3
- package/src/charting/charts/GroupedDistributionChart.js +26 -1
- package/src/charting/charts/HeatmapChart.js +6 -3
- package/src/charting/charts/HexbinChart.js +137 -0
- package/src/charting/charts/HierarchyChart.js +77 -3
- package/src/charting/charts/HistogramChart.js +2 -2
- package/src/charting/charts/HorizonChart.js +105 -0
- package/src/charting/charts/IcicleChart.js +47 -0
- package/src/charting/charts/KagiChart.js +167 -0
- package/src/charting/charts/LineChart.js +74 -18
- package/src/charting/charts/LollipopChart.js +104 -0
- package/src/charting/charts/MarimekkoChart.js +75 -0
- package/src/charting/charts/OhlcChart.js +79 -0
- package/src/charting/charts/OutlineChart.js +10 -8
- package/src/charting/charts/ParallelCoordinatesChart.js +11 -3
- package/src/charting/charts/ParetoChart.js +92 -0
- package/src/charting/charts/PieChart.js +195 -17
- package/src/charting/charts/PieChart3D.js +137 -0
- package/src/charting/charts/PipeChart.js +5 -2
- package/src/charting/charts/PointAndFigureChart.js +198 -0
- package/src/charting/charts/PolarAreaChart.js +189 -0
- package/src/charting/charts/PopulationPyramidChart.js +4 -4
- package/src/charting/charts/RadarChart.js +12 -5
- package/src/charting/charts/RadialBarChart.js +6 -3
- package/src/charting/charts/RenkoChart.js +178 -0
- package/src/charting/charts/RidgelineChart.js +113 -0
- package/src/charting/charts/SankeyDiagram.js +129 -6
- package/src/charting/charts/ScatterChart.js +19 -3
- package/src/charting/charts/ScatterMatrixChart.js +119 -0
- package/src/charting/charts/SlopegraphChart.js +205 -0
- package/src/charting/charts/SparklineTableChart.js +166 -0
- package/src/charting/charts/StackedBarChart.js +125 -11
- package/src/charting/charts/StackedBarChart3DOrbit.js +14 -6
- package/src/charting/charts/StreamGraphChart.js +18 -3
- package/src/charting/charts/SunburstChart.js +28 -8
- package/src/charting/charts/SurfaceChart3D.js +157 -0
- package/src/charting/charts/TreemapChart.js +31 -11
- package/src/charting/charts/ViolinChart.js +20 -2
- package/src/charting/charts/WaterfallChart.js +2 -2
- package/src/charting/charts/WordCloudChart.js +2 -2
- package/src/charting/datamodel/ArchimateDataLink.js +70 -0
- package/src/charting/datamodel/DataLineage.js +150 -0
- package/src/charting/datamodel/DataModel.js +83 -1
- package/src/charting/datamodel/ModelMapping.js +87 -0
- package/src/charting/datamodel/SqlDdl.js +110 -85
- package/src/charting/datamodel/index.js +3 -0
- package/src/charting/decision/DecisionWorkbench.js +295 -0
- package/src/charting/diagrams/ArchimateDiagram.js +25 -9
- package/src/charting/diagrams/ArchimateModel.js +1382 -1238
- package/src/charting/diagrams/GraphDiagram.js +1424 -1350
- package/src/charting/diagrams/SketchView.js +121 -0
- package/src/charting/diagrams/sketchBoxKinds.js +30 -0
- package/src/charting/modelTree/AnalysisPanel.js +101 -23
- package/src/charting/modelTree/NavigatorModel.js +410 -0
- package/src/charting/modelTree/NavigatorPanel.js +453 -0
- package/src/charting/pivot/PivotChart.js +310 -13
- package/src/charting/pivot/PivotGridChart.js +322 -34
- package/src/charting/pivot/PivotView.js +626 -21
- package/src/charting/report/TemplatedReport.js +547 -0
- package/src/charting/templates/BoxTemplate.js +28 -27
- package/src/charting/templates/PackageTemplate.js +26 -25
- package/src/charting/templates/SwimlaneTemplate.js +19 -18
- package/src/charting/templates/Template.js +219 -212
- package/src/cli.js +146 -0
- package/src/index.js +3370 -2899
- package/src/workspace/Workspace.js +126 -7
- package/src/workspace/archimateServices.js +4 -0
- package/src/workspace/parts/ArchimateCanvasPart.js +1 -1
- package/src/workspace/styles.js +36 -1
|
@@ -594,6 +594,10 @@ export class InteractionManager {
|
|
|
594
594
|
|
|
595
595
|
const onMouseMove = (event) => {
|
|
596
596
|
const hitShape = engine.hitTest(event.clientX, event.clientY);
|
|
597
|
+
if (engine.renderer?.type === 'canvas' && engine.renderer.canvas) {
|
|
598
|
+
const hasClick = hitShape && (hitShape.legendItem || typeof engine._activeChart?.options?.onDatumClick === 'function');
|
|
599
|
+
engine.renderer.canvas.style.cursor = (hitShape?.tooltip || hasClick) ? 'pointer' : 'default';
|
|
600
|
+
}
|
|
597
601
|
if (hitShape && hitShape.tooltip) {
|
|
598
602
|
tooltip.show(hitShape.tooltip, event.clientX, event.clientY);
|
|
599
603
|
} else {
|
|
@@ -84,7 +84,7 @@ export class ModelFileFormat {
|
|
|
84
84
|
* @returns {Array<Object>} menu items (with separators between formats)
|
|
85
85
|
*/
|
|
86
86
|
export function modelFileMenuItems(formats, {
|
|
87
|
-
model, onImport, enabled = () => true, document: doc = () => globalThis.document,
|
|
87
|
+
model, onImport, onMerge = null, enabled = () => true, document: doc = () => globalThis.document,
|
|
88
88
|
download = null, chooseFile = null, report = null, only = null,
|
|
89
89
|
} = {}) {
|
|
90
90
|
const save = download ?? (({ data, mimeType }, fileName) => downloadBlob(new (doc().defaultView ?? globalThis).Blob([data], { type: mimeType }), fileName));
|
|
@@ -96,6 +96,10 @@ export function modelFileMenuItems(formats, {
|
|
|
96
96
|
label: only ? `${format.label}…` : `Import ${format.label}…`,
|
|
97
97
|
action: () => importFile(format, { choose, onImport, report: tell }),
|
|
98
98
|
},
|
|
99
|
+
only !== 'export' && format.imports && onMerge && enabled(format, 'merge') && {
|
|
100
|
+
label: only ? `${format.label} (merge)…` : `Merge ${format.label}…`,
|
|
101
|
+
action: () => importFile(format, { choose, onImport: onMerge, report: tell }),
|
|
102
|
+
},
|
|
99
103
|
].filter(Boolean)).filter(group => group.length);
|
|
100
104
|
if (only) return groups.flat();
|
|
101
105
|
return groups.flatMap((group, i) => (i ? [{ separator: true }, ...group] : group));
|
|
@@ -53,6 +53,7 @@ export const TREE_SETTINGS_PROPERTIES = Object.freeze([
|
|
|
53
53
|
export const TOOL_SETTINGS_DEFAULTS = Object.freeze({
|
|
54
54
|
theme: 'light',
|
|
55
55
|
defaultZoom: 1,
|
|
56
|
+
elementColors: {},
|
|
56
57
|
...Object.fromEntries(TREE_SETTINGS_PROPERTIES.map(p => [p.key, p.default])),
|
|
57
58
|
});
|
|
58
59
|
|
|
@@ -159,6 +160,39 @@ export class SettingsManager {
|
|
|
159
160
|
this._notifyListeners({ type: 'view', viewId: view.id, settings: view._settings });
|
|
160
161
|
}
|
|
161
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Get configured element color for an element type, resolving model -> tool -> null.
|
|
165
|
+
*/
|
|
166
|
+
getElementColor(typeName, modelId = null) {
|
|
167
|
+
if (modelId) {
|
|
168
|
+
const modelColors = this.getModelOverrides(modelId).elementColors;
|
|
169
|
+
if (modelColors?.[typeName]) return modelColors[typeName];
|
|
170
|
+
}
|
|
171
|
+
return this.getToolSettings().elementColors?.[typeName] ?? null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Set type-level color override at tool level (default) or model level.
|
|
176
|
+
*/
|
|
177
|
+
setElementColor(typeName, color, { modelId = null } = {}) {
|
|
178
|
+
if (modelId) {
|
|
179
|
+
const existing = this.getModelOverrides(modelId).elementColors ?? {};
|
|
180
|
+
this.saveModelSettings(modelId, { elementColors: { ...existing, [typeName]: color } });
|
|
181
|
+
} else {
|
|
182
|
+
const existing = this.getToolSettings().elementColors ?? {};
|
|
183
|
+
this.saveToolSettings({ elementColors: { ...existing, [typeName]: color } });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Merged element color overrides for a model (model overrides tool).
|
|
189
|
+
*/
|
|
190
|
+
elementColors(modelId = null) {
|
|
191
|
+
const toolColors = this.getToolSettings().elementColors ?? {};
|
|
192
|
+
const modelColors = modelId ? (this.getModelOverrides(modelId).elementColors ?? {}) : {};
|
|
193
|
+
return { ...toolColors, ...modelColors };
|
|
194
|
+
}
|
|
195
|
+
|
|
162
196
|
// ── Resolve Settings (hierarchy: tool ← model ← view) ──
|
|
163
197
|
|
|
164
198
|
/** The cascade for one view: its own settings, then the model's, then the tool's, then defaults. */
|
|
@@ -185,6 +219,7 @@ export class SettingsManager {
|
|
|
185
219
|
titlePosition: viewSettings.titlePosition,
|
|
186
220
|
background: viewSettings.background,
|
|
187
221
|
backgroundImage: viewSettings.backgroundImage,
|
|
222
|
+
elementColors: this.elementColors(modelId),
|
|
188
223
|
};
|
|
189
224
|
}
|
|
190
225
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export class ShapePaths {
|
|
5
5
|
/**
|
|
6
|
-
* Build an SVG path for an annular pie/doughnut slice.
|
|
6
|
+
* Build an SVG path for an annular pie/doughnut slice (circular).
|
|
7
7
|
* @param {number} cx center x
|
|
8
8
|
* @param {number} cy center y
|
|
9
9
|
* @param {number} rOuter outer radius
|
|
@@ -13,15 +13,87 @@ export class ShapePaths {
|
|
|
13
13
|
* @returns {string}
|
|
14
14
|
*/
|
|
15
15
|
static donutSlicePath(cx, cy, rOuter, rInner, startAngle, endAngle) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
return ShapePaths.ellipticalSlicePath(cx, cy, rOuter, rOuter, rInner, rInner, startAngle, endAngle);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Build an SVG path for a circular or tilted-elliptical pie/doughnut slice.
|
|
21
|
+
* Handles full-circle (`2π`) spans cleanly by clamping slightly below `2π` so SVG arc
|
|
22
|
+
* start and end points do not coincide.
|
|
23
|
+
* @param {number} cx
|
|
24
|
+
* @param {number} cy
|
|
25
|
+
* @param {number} rxOuter
|
|
26
|
+
* @param {number} ryOuter
|
|
27
|
+
* @param {number} rxInner
|
|
28
|
+
* @param {number} ryInner
|
|
29
|
+
* @param {number} startAngle
|
|
30
|
+
* @param {number} endAngle
|
|
31
|
+
* @returns {string}
|
|
32
|
+
*/
|
|
33
|
+
static ellipticalSlicePath(cx, cy, rxOuter, ryOuter, rxInner, ryInner, startAngle, endAngle) {
|
|
34
|
+
const span = endAngle - startAngle;
|
|
35
|
+
const effectiveEnd = span >= Math.PI * 2 - 1e-5 ? startAngle + Math.PI * 2 - 1e-4 : endAngle;
|
|
36
|
+
const largeArc = (effectiveEnd - startAngle) > Math.PI ? 1 : 0;
|
|
37
|
+
const outerStart = { x: cx + rxOuter * Math.cos(startAngle), y: cy + ryOuter * Math.sin(startAngle) };
|
|
38
|
+
const outerEnd = { x: cx + rxOuter * Math.cos(effectiveEnd), y: cy + ryOuter * Math.sin(effectiveEnd) };
|
|
39
|
+
if (rxInner <= 0 || ryInner <= 0) {
|
|
40
|
+
return `M${cx},${cy} L${outerStart.x},${outerStart.y} A${rxOuter},${ryOuter} 0 ${largeArc} 1 ${outerEnd.x},${outerEnd.y} Z`;
|
|
41
|
+
}
|
|
42
|
+
const innerStart = { x: cx + rxInner * Math.cos(effectiveEnd), y: cy + ryInner * Math.sin(effectiveEnd) };
|
|
43
|
+
const innerEnd = { x: cx + rxInner * Math.cos(startAngle), y: cy + ryInner * Math.sin(startAngle) };
|
|
44
|
+
return `M${outerStart.x},${outerStart.y} A${rxOuter},${ryOuter} 0 ${largeArc} 1 ${outerEnd.x},${outerEnd.y} `
|
|
45
|
+
+ `L${innerStart.x},${innerStart.y} A${rxInner},${ryInner} 0 ${largeArc} 0 ${innerEnd.x},${innerEnd.y} Z`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Build an SVG path for a vertical extruded cylinder skirt along `[startAngle, endAngle]`
|
|
50
|
+
* between `(cx, cy)` and `(cx, cy + depth)`.
|
|
51
|
+
*/
|
|
52
|
+
static cylinderSkirtPath(cx, cy, rx, ry, depth, startAngle, endAngle) {
|
|
53
|
+
const span = endAngle - startAngle;
|
|
54
|
+
if (span <= 1e-6 || depth <= 0) return '';
|
|
55
|
+
const effectiveEnd = span >= Math.PI * 2 - 1e-5 ? startAngle + Math.PI * 2 - 1e-4 : endAngle;
|
|
56
|
+
const largeArc = (effectiveEnd - startAngle) > Math.PI ? 1 : 0;
|
|
57
|
+
const x0 = cx + rx * Math.cos(startAngle);
|
|
58
|
+
const y0 = cy + ry * Math.sin(startAngle);
|
|
59
|
+
const x1 = cx + rx * Math.cos(effectiveEnd);
|
|
60
|
+
const y1 = cy + ry * Math.sin(effectiveEnd);
|
|
61
|
+
return `M${x0},${y0} A${rx},${ry} 0 ${largeArc} 1 ${x1},${y1} `
|
|
62
|
+
+ `L${x1},${y1 + depth} A${rx},${ry} 0 ${largeArc} 0 ${x0},${y0 + depth} Z`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Build an SVG path for a flat vertical radial cut wall at `angle` from inner to outer radius
|
|
67
|
+
* and extruded down by `depth`.
|
|
68
|
+
*/
|
|
69
|
+
static radialWallPath(cx, cy, rxInner, ryInner, rxOuter, ryOuter, depth, angle) {
|
|
70
|
+
const cos = Math.cos(angle);
|
|
71
|
+
const sin = Math.sin(angle);
|
|
72
|
+
const xIn = cx + rxInner * cos;
|
|
73
|
+
const yIn = cy + ryInner * sin;
|
|
74
|
+
const xOut = cx + rxOuter * cos;
|
|
75
|
+
const yOut = cy + ryOuter * sin;
|
|
76
|
+
return `M${xIn},${yIn} L${xOut},${yOut} L${xOut},${yOut + depth} L${xIn},${yIn + depth} Z`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Intersect `[startAngle, endAngle]` with the angular intervals where `sin(θ) >= 0` (front outer rim)
|
|
81
|
+
* or `sin(θ) <= 0` (back inner rim), handling wrap-around across `[-π, 3π]`.
|
|
82
|
+
* @param {number} startAngle
|
|
83
|
+
* @param {number} endAngle
|
|
84
|
+
* @param {'front'|'back'} hemisphere `'front'` = `sin(θ) >= 0` (`[0, π]`), `'back'` = `sin(θ) <= 0` (`[-π, 0]`)
|
|
85
|
+
* @returns {Array<[number, number]>}
|
|
86
|
+
*/
|
|
87
|
+
static visibleArcIntervals(startAngle, endAngle, hemisphere = 'front') {
|
|
88
|
+
const intervals = [];
|
|
89
|
+
const windows = hemisphere === 'front'
|
|
90
|
+
? [[-Math.PI * 2, -Math.PI], [0, Math.PI], [Math.PI * 2, Math.PI * 3]]
|
|
91
|
+
: [[-Math.PI * 3, -Math.PI * 2], [-Math.PI, 0], [Math.PI, Math.PI * 2]];
|
|
92
|
+
for (const [w0, w1] of windows) {
|
|
93
|
+
const a = Math.max(startAngle, w0);
|
|
94
|
+
const b = Math.min(endAngle, w1);
|
|
95
|
+
if (b - a > 1e-5) intervals.push([a, b]);
|
|
21
96
|
}
|
|
22
|
-
|
|
23
|
-
const innerEnd = { x: cx + rInner * Math.cos(startAngle), y: cy + rInner * Math.sin(startAngle) };
|
|
24
|
-
return `M${outerStart.x},${outerStart.y} A${rOuter},${rOuter} 0 ${largeArc} 1 ${outerEnd.x},${outerEnd.y} `
|
|
25
|
-
+ `L${innerStart.x},${innerStart.y} A${rInner},${rInner} 0 ${largeArc} 0 ${innerEnd.x},${innerEnd.y} Z`;
|
|
97
|
+
return intervals;
|
|
26
98
|
}
|
|
27
99
|
}
|
|
@@ -2,14 +2,15 @@ import { ModelFileFormat } from '../ModelFileFormat.js';
|
|
|
2
2
|
import { ArchimateModel } from '../diagrams/ArchimateModel.js';
|
|
3
3
|
import { exportModelCSV, importModelCSV } from '../../archimateCSV.js';
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
const ELEMENTS_HEADER = '=== elements.csv
|
|
7
|
-
const
|
|
5
|
+
/** Table segment headers in the combined CSV file. */
|
|
6
|
+
const ELEMENTS_HEADER = '=== elements.csv ===';
|
|
7
|
+
const RELATIONS_HEADER = '=== relations.csv ===';
|
|
8
|
+
const RELATIONSHIPS_HEADER = '=== relationships.csv ===';
|
|
9
|
+
const PROPERTIES_HEADER = '=== properties.csv ===';
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
|
-
* The model's elements and
|
|
11
|
-
*
|
|
12
|
-
* its elements and relationships with one view of them all, laid out.
|
|
12
|
+
* The model's elements, relationships, and properties as CSV, all tables in one file.
|
|
13
|
+
* Compatible with both renderengine format and Archi CSV export/import.
|
|
13
14
|
*/
|
|
14
15
|
export class ArchimateCsvFileFormat extends ModelFileFormat {
|
|
15
16
|
get label() { return 'CSV'; }
|
|
@@ -18,16 +19,54 @@ export class ArchimateCsvFileFormat extends ModelFileFormat {
|
|
|
18
19
|
get featureName() { return 'CSV'; }
|
|
19
20
|
|
|
20
21
|
export(model) {
|
|
21
|
-
const { elementsCSV, relationshipsCSV } = exportModelCSV({
|
|
22
|
-
|
|
22
|
+
const { elementsCSV, relationshipsCSV, propertiesCSV } = exportModelCSV({
|
|
23
|
+
elements: model.elements(),
|
|
24
|
+
relationships: model.relationships(),
|
|
25
|
+
});
|
|
26
|
+
const parts = [
|
|
27
|
+
`${ELEMENTS_HEADER}\n${elementsCSV}`,
|
|
28
|
+
`${RELATIONSHIPS_HEADER}\n${relationshipsCSV}`,
|
|
29
|
+
];
|
|
30
|
+
if (propertiesCSV && propertiesCSV.trim().split('\n').length > 1) {
|
|
31
|
+
parts.push(`${PROPERTIES_HEADER}\n${propertiesCSV}`);
|
|
32
|
+
}
|
|
33
|
+
return { data: parts.join('\n\n'), mimeType: 'text/csv' };
|
|
23
34
|
}
|
|
24
35
|
|
|
25
36
|
import(text) {
|
|
26
37
|
const normal = String(text).replace(/\r\n?/g, '\n');
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
38
|
+
|
|
39
|
+
let elementsCSV = '';
|
|
40
|
+
let relationshipsCSV = '';
|
|
41
|
+
let propertiesCSV = '';
|
|
42
|
+
|
|
43
|
+
// Check if file uses section markers
|
|
44
|
+
const hasSections = normal.includes(ELEMENTS_HEADER)
|
|
45
|
+
|| normal.includes(RELATIONS_HEADER)
|
|
46
|
+
|| normal.includes(RELATIONSHIPS_HEADER);
|
|
47
|
+
|
|
48
|
+
if (!hasSections) {
|
|
49
|
+
// Single table: assume elements.csv
|
|
50
|
+
elementsCSV = normal.trim();
|
|
51
|
+
} else {
|
|
52
|
+
// Split by section markers
|
|
53
|
+
const sections = normal.split(/(?:^|\n)(?====\s*[\w\.-]+\s*===)/g);
|
|
54
|
+
for (const sec of sections) {
|
|
55
|
+
const trimmed = sec.trim();
|
|
56
|
+
if (!trimmed) continue;
|
|
57
|
+
if (trimmed.startsWith(ELEMENTS_HEADER)) {
|
|
58
|
+
elementsCSV = trimmed.slice(ELEMENTS_HEADER.length).trim();
|
|
59
|
+
} else if (trimmed.startsWith(RELATIONS_HEADER)) {
|
|
60
|
+
relationshipsCSV = trimmed.slice(RELATIONS_HEADER.length).trim();
|
|
61
|
+
} else if (trimmed.startsWith(RELATIONSHIPS_HEADER)) {
|
|
62
|
+
relationshipsCSV = trimmed.slice(RELATIONSHIPS_HEADER.length).trim();
|
|
63
|
+
} else if (trimmed.startsWith(PROPERTIES_HEADER)) {
|
|
64
|
+
propertiesCSV = trimmed.slice(PROPERTIES_HEADER.length).trim();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const { elements, relationships } = importModelCSV(elementsCSV, relationshipsCSV, propertiesCSV);
|
|
31
70
|
const model = new ArchimateModel({ elements, relationships, views: [] });
|
|
32
71
|
if (elements.length) model.generateView(elements.map(e => e.id), { name: 'Imported elements' });
|
|
33
72
|
return model;
|