@dforge-core/diagram 0.0.4 → 0.0.5
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/CHANGELOG.md +112 -0
- package/dist/SimpleDiagram/SimpleDiagram.svelte +100 -13
- package/dist/SimpleDiagram/SimpleDiagram.svelte.d.ts.map +1 -1
- package/dist/SimpleDiagram/simple-layout.d.ts.map +1 -1
- package/dist/SimpleDiagram/simple-layout.js +13 -5
- package/dist/i18n.d.ts +2 -0
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +2 -0
- package/dist/icons.d.ts.map +1 -1
- package/dist/icons.js +2 -0
- package/package.json +59 -57
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@dforge-core/diagram` are documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this package adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.0.4] — 2026-09-14
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Nodes no longer scale wrongly in Safari/WebKit.** Entity nodes rendered inside an
|
|
13
|
+
SVG `<foreignObject>`, and WebKit does not apply a scaled SVG ancestor transform to
|
|
14
|
+
the HTML content of a `foreignObject` — so zooming moved the boxes without resizing
|
|
15
|
+
their contents. Nodes now render in an HTML overlay layer that carries the same
|
|
16
|
+
pan/zoom transform as the SVG `<g>` beneath it, which every browser applies the same
|
|
17
|
+
way.
|
|
18
|
+
|
|
19
|
+
The layer is `pointer-events: none` with each node opting back in, so empty space
|
|
20
|
+
still falls through to the SVG for panning, area-select and edge hit areas. Pointer
|
|
21
|
+
and double-click handlers moved from the `<svg>` up to the container, since events
|
|
22
|
+
now originate on both sides of that boundary; the delegated walk-up stops at the
|
|
23
|
+
container rather than the SVG element. Overlaid zoom controls are excluded from the
|
|
24
|
+
delegated handler so their clicks aren't read as a pan.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- **`buildGraph` takes `localized` and `moduleColorIndex` options.**
|
|
29
|
+
|
|
30
|
+
`localized` draws `resLabelLocalized` in place of `resLabel` wherever a translation
|
|
31
|
+
exists, so a tenant that has translated its metadata can read the diagram in its own
|
|
32
|
+
language. The base label stays the default: the model browser is a developer view,
|
|
33
|
+
and `metadata.getModel` deliberately loads install-time labels.
|
|
34
|
+
|
|
35
|
+
`moduleColorIndex` overrides the default palette assignment of "position in
|
|
36
|
+
`modules`". Needed when `modules` is an arbitrary subset — a module plus the ones it
|
|
37
|
+
connects to — which must keep the colors those modules carry on their own diagrams.
|
|
38
|
+
`intraModuleEdgesOnly` and `colorOffset` are unchanged.
|
|
39
|
+
|
|
40
|
+
- **`findRelatedModules` and `expandBridgeSelection`**, for drawing a module alongside
|
|
41
|
+
its neighbours. A bridge module (`crm-fin`, `wms-fin`) owns no tables of its own — it
|
|
42
|
+
only adds extension columns to the entities of the modules it bridges — so its
|
|
43
|
+
diagram was empty, hiding the one thing the module does.
|
|
44
|
+
|
|
45
|
+
`findRelatedModules(response, moduleCd)` returns the modules on either end of a
|
|
46
|
+
dependency plus those reachable through a cross-module reference.
|
|
47
|
+
`expandBridgeSelection(response, moduleCds)` pulls in a selected bridge's non-system,
|
|
48
|
+
non-bridge dependencies, so picking the bridge alone still draws something.
|
|
49
|
+
|
|
50
|
+
- **`entityLabel(ent, localized?)` and `columnLabel(col, localized?)`** — the label
|
|
51
|
+
fallback used internally by `buildGraph`, exported so a consumer rendering its own
|
|
52
|
+
chrome resolves labels the same way.
|
|
53
|
+
|
|
54
|
+
- **`SimpleDiagram` accepts `fitKey`** (`string | number`, optional). Changing it
|
|
55
|
+
re-fits the content to the viewport. Added for the print view, where the diagram is
|
|
56
|
+
laid out into a fixed sheet box rather than the screen viewport, and the fit has to
|
|
57
|
+
be recomputed when that box appears.
|
|
58
|
+
|
|
59
|
+
- **Three optional fields on the model contract**, mirroring what `metadata.getModel`
|
|
60
|
+
now returns: `ModelModule.dependencies` (manifest `dependencies` keys),
|
|
61
|
+
`ModelEntity.resLabelLocalized` and `ModelColumn.resLabelLocalized` (label for the
|
|
62
|
+
viewer's culture, absent when no translation exists).
|
|
63
|
+
|
|
64
|
+
All of the above are additive. Component props are unchanged apart from the optional
|
|
65
|
+
`fitKey`, so 0.0.3 code compiles and behaves the same against this release.
|
|
66
|
+
|
|
67
|
+
### Changed
|
|
68
|
+
|
|
69
|
+
- `@dforge-core/metadata` moves from `0.0.2` to `0.0.22`. It is a type-only import
|
|
70
|
+
(`model-types.ts` pulls `BaseDatatypeCd`, `ColumnTypeCd` and `FieldTypeCd` from it),
|
|
71
|
+
so the jump carries no runtime code.
|
|
72
|
+
|
|
73
|
+
## [0.0.3] — 2026-06-13
|
|
74
|
+
|
|
75
|
+
### Changed
|
|
76
|
+
|
|
77
|
+
- **The model contract's code unions come from `@dforge-core/metadata`.**
|
|
78
|
+
`model-types.ts` had restated `BaseDatatypeCd`, `ColumnTypeCd` and `FieldTypeCd`
|
|
79
|
+
inline; it now type-imports them from the package that owns those registries, which
|
|
80
|
+
is the same source the SDK reads. Adds `@dforge-core/metadata` as this package's only
|
|
81
|
+
dependency.
|
|
82
|
+
|
|
83
|
+
## [0.0.2] — 2026-06-12
|
|
84
|
+
|
|
85
|
+
### Removed
|
|
86
|
+
|
|
87
|
+
- **The `bootstrap-icons` dependency.** `IconButton` rendered icons as Bootstrap Icons
|
|
88
|
+
font classes, which silently drew nothing unless the host page happened to load that
|
|
89
|
+
stylesheet. The handful of icons the diagram controls need are now inline SVG paths
|
|
90
|
+
in `icons.ts`, so the package renders correctly on any page.
|
|
91
|
+
|
|
92
|
+
## [0.0.1] — 2026-06-09
|
|
93
|
+
|
|
94
|
+
### Added
|
|
95
|
+
|
|
96
|
+
- Initial release, published so the diagram can be used outside this repo (the VS Code
|
|
97
|
+
extension is the first such consumer). `SimpleDiagram`, `ModelDiagram`, `EntityNode`,
|
|
98
|
+
`WorkspaceMap` and `ModuleCard`, with the layout, edge-routing, graph-building,
|
|
99
|
+
geometry, pan/zoom and i18n helpers behind them.
|
|
100
|
+
- `model-types.ts` — a local mirror of the dForge API `metadata.getModel` shape, so the
|
|
101
|
+
package types the payload it draws without depending on the SDK.
|
|
102
|
+
- MIT `LICENSE` and `README.md`.
|
|
103
|
+
|
|
104
|
+
### Changed
|
|
105
|
+
|
|
106
|
+
- Renamed from `@dforge/diagram` to `@dforge-core/diagram`.
|
|
107
|
+
|
|
108
|
+
### Removed
|
|
109
|
+
|
|
110
|
+
- **The `@dforge/sdk` and `@dforge/ui` dependencies**, both workspace-only packages
|
|
111
|
+
that made this one unpublishable. The single component used from `@dforge/ui`,
|
|
112
|
+
`IconButton`, is now local. `svelte` is the only peer dependency.
|
|
@@ -41,6 +41,66 @@
|
|
|
41
41
|
let draggingSeg = $state<{ edgeId: string; segIndex: number; orientation: 'H' | 'V' } | null>(null);
|
|
42
42
|
let patternId = `grid-${Math.random().toString(36).slice(2, 8)}`;
|
|
43
43
|
|
|
44
|
+
// --- Undo / redo of layout edits (node moves, edge reshapes, resets) ---
|
|
45
|
+
|
|
46
|
+
interface LayoutSnapshot {
|
|
47
|
+
dragOffsets: Record<string, { dx: number; dy: number }>;
|
|
48
|
+
edgeOverrides: Record<string, Point[]>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const HISTORY_LIMIT = 100;
|
|
52
|
+
let undoStack = $state.raw<LayoutSnapshot[]>([]);
|
|
53
|
+
let redoStack = $state.raw<LayoutSnapshot[]>([]);
|
|
54
|
+
let gestureStart: LayoutSnapshot | null = null;
|
|
55
|
+
let gestureMoved = false;
|
|
56
|
+
|
|
57
|
+
const takeSnapshot = (): LayoutSnapshot =>
|
|
58
|
+
$state.snapshot({ dragOffsets, edgeOverrides }) as LayoutSnapshot;
|
|
59
|
+
|
|
60
|
+
function pushHistory(snap: LayoutSnapshot) {
|
|
61
|
+
undoStack = [...undoStack.slice(-(HISTORY_LIMIT - 1)), snap];
|
|
62
|
+
redoStack = [];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Record the gesture's starting state, if the gesture changed anything. */
|
|
66
|
+
function commitGesture() {
|
|
67
|
+
gestureMoved = false;
|
|
68
|
+
if (!gestureStart) return;
|
|
69
|
+
if (JSON.stringify(gestureStart) !== JSON.stringify(takeSnapshot())) pushHistory(gestureStart);
|
|
70
|
+
gestureStart = null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function applySnapshot(snap: LayoutSnapshot) {
|
|
74
|
+
dragOffsets = snap.dragOffsets;
|
|
75
|
+
edgeOverrides = snap.edgeOverrides;
|
|
76
|
+
autoEdges = routeWithOffsets(snap.dragOffsets);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function undo() {
|
|
80
|
+
const snap = undoStack.at(-1);
|
|
81
|
+
if (!snap) return;
|
|
82
|
+
redoStack = [...redoStack, takeSnapshot()];
|
|
83
|
+
undoStack = undoStack.slice(0, -1);
|
|
84
|
+
applySnapshot(snap);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function redo() {
|
|
88
|
+
const snap = redoStack.at(-1);
|
|
89
|
+
if (!snap) return;
|
|
90
|
+
undoStack = [...undoStack, takeSnapshot()];
|
|
91
|
+
redoStack = redoStack.slice(0, -1);
|
|
92
|
+
applySnapshot(snap);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function handleKeyDown(e: KeyboardEvent) {
|
|
96
|
+
if (!(e.metaKey || e.ctrlKey) || e.altKey || dragging || draggingSeg) return;
|
|
97
|
+
const key = e.key.toLowerCase();
|
|
98
|
+
if (key === 'z' && !e.shiftKey) undo();
|
|
99
|
+
else if ((key === 'z' && e.shiftKey) || (key === 'y' && e.ctrlKey)) redo();
|
|
100
|
+
else return;
|
|
101
|
+
e.preventDefault();
|
|
102
|
+
}
|
|
103
|
+
|
|
44
104
|
const pz = createPanZoom({
|
|
45
105
|
get minZoom() { return minZoom; },
|
|
46
106
|
get maxZoom() { return maxZoom; },
|
|
@@ -87,18 +147,8 @@
|
|
|
87
147
|
edgeOverrides = saved.edgeOverrides;
|
|
88
148
|
pz.setTransform(saved.transform);
|
|
89
149
|
restoredTransform = true;
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
const posMap: Record<string, DiagramNode> = {};
|
|
93
|
-
for (const n of nodes) {
|
|
94
|
-
const off = offsets[n.id];
|
|
95
|
-
posMap[n.id] = off ? { ...n, x: n.x + off.dx, y: n.y + off.dy } : n;
|
|
96
|
-
}
|
|
97
|
-
const posLookup = (id: string) => posMap[id];
|
|
98
|
-
autoEdges = edges.map((e) => {
|
|
99
|
-
if (!movedIds.has(e.source) && !movedIds.has(e.target)) return e;
|
|
100
|
-
return routeEdge(e, posLookup);
|
|
101
|
-
});
|
|
150
|
+
// Use local `offsets` to avoid reading $state
|
|
151
|
+
autoEdges = routeWithOffsets(offsets);
|
|
102
152
|
} else if (saved) {
|
|
103
153
|
dragOffsets = {};
|
|
104
154
|
edgeOverrides = saved.edgeOverrides;
|
|
@@ -113,8 +163,25 @@
|
|
|
113
163
|
}
|
|
114
164
|
selectedNodeIds = new Set();
|
|
115
165
|
selectedEdgeId = null;
|
|
166
|
+
undoStack = [];
|
|
167
|
+
redoStack = [];
|
|
116
168
|
});
|
|
117
169
|
|
|
170
|
+
/** Auto-route edges for the given node offsets; edges between unmoved nodes keep their layout route. */
|
|
171
|
+
function routeWithOffsets(offsets: Record<string, { dx: number; dy: number }>): ResolvedEdge[] {
|
|
172
|
+
const movedIds = new Set(Object.keys(offsets));
|
|
173
|
+
const posMap: Record<string, DiagramNode> = {};
|
|
174
|
+
for (const n of nodes) {
|
|
175
|
+
const off = offsets[n.id];
|
|
176
|
+
posMap[n.id] = off ? { ...n, x: n.x + off.dx, y: n.y + off.dy } : n;
|
|
177
|
+
}
|
|
178
|
+
const posLookup = (id: string) => posMap[id];
|
|
179
|
+
return edges.map((e) => {
|
|
180
|
+
if (!movedIds.has(e.source) && !movedIds.has(e.target)) return e;
|
|
181
|
+
return routeEdge(e, posLookup);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
118
185
|
// Derive node positions with drag offsets applied
|
|
119
186
|
const nodePositions = $derived.by(() => {
|
|
120
187
|
const pos: Record<string, DiagramNode> = {};
|
|
@@ -209,6 +276,9 @@
|
|
|
209
276
|
// clicks would otherwise reach this delegated handler.
|
|
210
277
|
if (target.closest('.diagram-controls')) return;
|
|
211
278
|
|
|
279
|
+
// Take focus so Ctrl/Cmd+Z reaches handleKeyDown
|
|
280
|
+
containerEl.focus({ preventScroll: true });
|
|
281
|
+
|
|
212
282
|
// Segment drag handle
|
|
213
283
|
const handle = closest(target, 'data-handle');
|
|
214
284
|
if (handle) {
|
|
@@ -286,6 +356,7 @@
|
|
|
286
356
|
}
|
|
287
357
|
if (pz.onPointerMove(e)) return;
|
|
288
358
|
if (draggingSeg) {
|
|
359
|
+
gestureMoved = true;
|
|
289
360
|
const pos = pz.clientToSvg(e.clientX, e.clientY);
|
|
290
361
|
const pts = getEdgePoints(draggingSeg.edgeId);
|
|
291
362
|
if (pts.length === 0) return;
|
|
@@ -294,6 +365,7 @@
|
|
|
294
365
|
return;
|
|
295
366
|
}
|
|
296
367
|
if (dragging) {
|
|
368
|
+
gestureMoved = true;
|
|
297
369
|
const pos = pz.clientToSvg(e.clientX, e.clientY);
|
|
298
370
|
const newOffsets = { ...dragOffsets };
|
|
299
371
|
for (const [nid, off] of Object.entries(dragging.offsets)) {
|
|
@@ -334,12 +406,16 @@
|
|
|
334
406
|
if (draggingSeg) {
|
|
335
407
|
const edgeId = draggingSeg.edgeId;
|
|
336
408
|
const pts = edgeOverrides[edgeId];
|
|
337
|
-
|
|
409
|
+
// A handle click without a move must not turn the auto route into an override
|
|
410
|
+
if (!gestureMoved && gestureStart) {
|
|
411
|
+
edgeOverrides = gestureStart.edgeOverrides;
|
|
412
|
+
} else if (pts) {
|
|
338
413
|
edgeOverrides = { ...edgeOverrides, [edgeId]: consolidatePoints(pts) };
|
|
339
414
|
}
|
|
340
415
|
}
|
|
341
416
|
dragging = null;
|
|
342
417
|
draggingSeg = null;
|
|
418
|
+
commitGesture();
|
|
343
419
|
}
|
|
344
420
|
|
|
345
421
|
// --- Node interaction ---
|
|
@@ -367,12 +443,15 @@
|
|
|
367
443
|
offsets[nid] = { offsetX: pos.x - np.x, offsetY: pos.y - np.y };
|
|
368
444
|
}
|
|
369
445
|
}
|
|
446
|
+
gestureStart = takeSnapshot();
|
|
370
447
|
dragging = { nodeId, offsets };
|
|
371
448
|
}
|
|
372
449
|
|
|
373
450
|
// --- Edge interaction ---
|
|
374
451
|
|
|
375
452
|
function resetEdgeRoute(edgeId: string) {
|
|
453
|
+
if (!edgeOverrides[edgeId]) return;
|
|
454
|
+
pushHistory(takeSnapshot());
|
|
376
455
|
const { [edgeId]: _, ...rest } = edgeOverrides;
|
|
377
456
|
edgeOverrides = rest;
|
|
378
457
|
}
|
|
@@ -380,6 +459,7 @@
|
|
|
380
459
|
function startSegDrag(_e: PointerEvent, edgeId: string, segIndex: number, orientation: 'H' | 'V') {
|
|
381
460
|
const pts = getEdgePoints(edgeId);
|
|
382
461
|
if (pts.length === 0) return;
|
|
462
|
+
gestureStart = takeSnapshot();
|
|
383
463
|
|
|
384
464
|
if (!edgeOverrides[edgeId]) {
|
|
385
465
|
edgeOverrides = { ...edgeOverrides, [edgeId]: pts.map((p) => ({ ...p })) };
|
|
@@ -393,6 +473,7 @@
|
|
|
393
473
|
function startSplitDrag(_e: PointerEvent, edgeId: string, segIndex: number, orientation: 'H' | 'V') {
|
|
394
474
|
const pts = getEdgePoints(edgeId);
|
|
395
475
|
if (pts.length === 0) return;
|
|
476
|
+
gestureStart = takeSnapshot();
|
|
396
477
|
|
|
397
478
|
const jog = insertJog(pts, segIndex, orientation);
|
|
398
479
|
|
|
@@ -421,6 +502,7 @@
|
|
|
421
502
|
}
|
|
422
503
|
|
|
423
504
|
function resetLayout() {
|
|
505
|
+
if (Object.keys(dragOffsets).length > 0 || Object.keys(edgeOverrides).length > 0) pushHistory(takeSnapshot());
|
|
424
506
|
dragOffsets = {};
|
|
425
507
|
edgeOverrides = {};
|
|
426
508
|
selectedNodeIds = new Set();
|
|
@@ -464,6 +546,8 @@
|
|
|
464
546
|
<div
|
|
465
547
|
class="model-diagram"
|
|
466
548
|
bind:this={containerEl}
|
|
549
|
+
tabindex="-1"
|
|
550
|
+
onkeydown={handleKeyDown}
|
|
467
551
|
onpointerdown={handlePointerDown}
|
|
468
552
|
onpointermove={handlePointerMove}
|
|
469
553
|
onpointerup={handlePointerUp}
|
|
@@ -638,6 +722,8 @@
|
|
|
638
722
|
<IconButton icon="bi-plus-lg" title={t.zoom_in} size="sm" onclick={() => pz.zoomBy(1.3)} />
|
|
639
723
|
<IconButton icon="bi-dash-lg" title={t.zoom_out} size="sm" onclick={() => pz.zoomBy(1 / 1.3)} />
|
|
640
724
|
<IconButton icon="bi-fullscreen" title={t.fit_view} size="sm" onclick={fitView} />
|
|
725
|
+
<IconButton icon="bi-arrow-90deg-left" title={t.undo} size="sm" disabled={undoStack.length === 0} onclick={undo} />
|
|
726
|
+
<IconButton icon="bi-arrow-90deg-right" title={t.redo} size="sm" disabled={redoStack.length === 0} onclick={redo} />
|
|
641
727
|
<IconButton icon="bi-arrow-counterclockwise" title={t.reset_layout} size="sm" onclick={resetLayout} />
|
|
642
728
|
</div>
|
|
643
729
|
</div>
|
|
@@ -648,6 +734,7 @@
|
|
|
648
734
|
height: 100%;
|
|
649
735
|
position: relative;
|
|
650
736
|
overflow: hidden;
|
|
737
|
+
outline: none;
|
|
651
738
|
}
|
|
652
739
|
|
|
653
740
|
.model-diagram svg {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SimpleDiagram.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/SimpleDiagram/SimpleDiagram.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAS,MAAM,kBAAkB,CAAC;AAUxE,UAAU,KAAK;IACd,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C;
|
|
1
|
+
{"version":3,"file":"SimpleDiagram.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/SimpleDiagram/SimpleDiagram.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAS,MAAM,kBAAkB,CAAC;AAUxE,UAAU,KAAK;IACd,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C;AAooBF,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simple-layout.d.ts","sourceRoot":"","sources":["../../src/lib/SimpleDiagram/simple-layout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAS,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"simple-layout.d.ts","sourceRoot":"","sources":["../../src/lib/SimpleDiagram/simple-layout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAS,MAAM,kBAAkB,CAAC;AAetF,MAAM,MAAM,UAAU,GAAG,CAAC,EAAE,EAAE,MAAM,KAAK,WAAW,GAAG,SAAS,CAAC;AA0HjE;;GAEG;AACH,wBAAgB,YAAY,CAC3B,KAAK,EAAE,WAAW,EAAE,EACpB,KAAK,EAAE,WAAW,EAAE,GAClB;IAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IAAC,KAAK,EAAE,YAAY,EAAE,CAAA;CAAE,CAejD;AAqFD;;;;GAIG;AACH,wBAAgB,YAAY,CAC3B,KAAK,EAAE,WAAW,EAAE,EACpB,KAAK,EAAE,WAAW,EAAE,GAClB;IAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IAAC,KAAK,EAAE,YAAY,EAAE,CAAA;CAAE,CAmCjD;AA+GD;;GAEG;AACH,wBAAgB,SAAS,CACxB,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,UAAU,GAChB,YAAY,CAcd;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC5B,KAAK,EAAE,WAAW,EAAE,EACpB,KAAK,EAAE,WAAW,EAAE,GAClB,YAAY,EAAE,CAIhB"}
|
|
@@ -7,6 +7,7 @@ const HEADER_HEIGHT = 30;
|
|
|
7
7
|
const ROW_HEIGHT = 22;
|
|
8
8
|
const MODULE_BLOCK_GAP = 80;
|
|
9
9
|
const OVERLAP_MARGIN = 30;
|
|
10
|
+
const MIN_ROUTE_GAP = 12;
|
|
10
11
|
/** Group items by a key function into a Map of arrays. */
|
|
11
12
|
function groupBy(items, key) {
|
|
12
13
|
const map = new Map();
|
|
@@ -271,24 +272,31 @@ function routeSelfEdge(edge, node, srcY, tgtY) {
|
|
|
271
272
|
}
|
|
272
273
|
/** Route stubs that avoid crossing through the connected nodes. */
|
|
273
274
|
function stubRoute(src, tgt, dir, srcNode, tgtNode) {
|
|
274
|
-
|
|
275
|
-
|
|
275
|
+
let stubSrc = src.x + dir * STUB_LENGTH;
|
|
276
|
+
let stubTgt = tgt.x - dir * STUB_LENGTH;
|
|
276
277
|
// Find midY that doesn't cross through either node
|
|
277
278
|
const srcBottom = srcNode.y + srcNode.height;
|
|
278
279
|
const tgtTop = tgtNode.y;
|
|
279
280
|
const srcTop = srcNode.y;
|
|
280
281
|
const tgtBottom = tgtNode.y + tgtNode.height;
|
|
281
282
|
let midY;
|
|
282
|
-
if (srcBottom +
|
|
283
|
+
if (srcBottom + MIN_ROUTE_GAP < tgtTop) {
|
|
283
284
|
// Gap between nodes — route through it
|
|
284
285
|
midY = (srcBottom + tgtTop) / 2;
|
|
285
286
|
}
|
|
286
|
-
else if (tgtBottom +
|
|
287
|
+
else if (tgtBottom + MIN_ROUTE_GAP < srcTop) {
|
|
287
288
|
midY = (tgtBottom + srcTop) / 2;
|
|
288
289
|
}
|
|
289
290
|
else {
|
|
290
|
-
// Nodes overlap vertically — route above both
|
|
291
|
+
// Nodes overlap vertically — route above both; a stub that would land inside the other node moves outside both
|
|
291
292
|
midY = Math.min(srcTop, tgtTop) - OVERLAP_MARGIN;
|
|
293
|
+
const left = Math.min(srcNode.x, tgtNode.x) - STUB_LENGTH;
|
|
294
|
+
const right = Math.max(srcNode.x + srcNode.width, tgtNode.x + tgtNode.width) + STUB_LENGTH;
|
|
295
|
+
const inside = (x, n) => x > n.x && x < n.x + n.width;
|
|
296
|
+
if (inside(stubSrc, tgtNode))
|
|
297
|
+
stubSrc = dir > 0 ? right : left;
|
|
298
|
+
if (inside(stubTgt, srcNode))
|
|
299
|
+
stubTgt = dir > 0 ? left : right;
|
|
292
300
|
}
|
|
293
301
|
return [
|
|
294
302
|
src,
|
package/dist/i18n.d.ts
CHANGED
package/dist/i18n.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../src/lib/i18n.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,uBAAuB,EAAE,
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../src/lib/i18n.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,uBAAuB,EAAE,mBAUrC,CAAC;AAIF,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,mBAAmB,GAAG,IAAI,CAE9E;AAED,wBAAgB,sBAAsB,IAAI,mBAAmB,CAK5D"}
|
package/dist/i18n.js
CHANGED
package/dist/icons.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../src/lib/icons.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../src/lib/icons.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAaxC,CAAC"}
|
package/dist/icons.js
CHANGED
|
@@ -13,5 +13,7 @@ export const ICONS = {
|
|
|
13
13
|
'bi-plus-lg': '<path fill-rule="evenodd" d="M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2"/>',
|
|
14
14
|
'bi-dash-lg': '<path fill-rule="evenodd" d="M2 8a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11A.5.5 0 0 1 2 8"/>',
|
|
15
15
|
'bi-fullscreen': '<path d="M1.5 1a.5.5 0 0 0-.5.5v4a.5.5 0 0 1-1 0v-4A1.5 1.5 0 0 1 1.5 0h4a.5.5 0 0 1 0 1zM10 .5a.5.5 0 0 1 .5-.5h4A1.5 1.5 0 0 1 16 1.5v4a.5.5 0 0 1-1 0v-4a.5.5 0 0 0-.5-.5h-4a.5.5 0 0 1-.5-.5M.5 10a.5.5 0 0 1 .5.5v4a.5.5 0 0 0 .5.5h4a.5.5 0 0 1 0 1h-4A1.5 1.5 0 0 1 0 14.5v-4a.5.5 0 0 1 .5-.5m15 0a.5.5 0 0 1 .5.5v4a1.5 1.5 0 0 1-1.5 1.5h-4a.5.5 0 0 1 0-1h4a.5.5 0 0 0 .5-.5v-4a.5.5 0 0 1 .5-.5"/>',
|
|
16
|
+
'bi-arrow-90deg-left': '<path fill-rule="evenodd" d="M1.146 4.854a.5.5 0 0 1 0-.708l4-4a.5.5 0 1 1 .708.708L2.707 4H12.5A2.5 2.5 0 0 1 15 6.5v8a.5.5 0 0 1-1 0v-8A1.5 1.5 0 0 0 12.5 5H2.707l3.147 3.146a.5.5 0 1 1-.708.708z"/>',
|
|
17
|
+
'bi-arrow-90deg-right': '<path fill-rule="evenodd" d="M14.854 4.854a.5.5 0 0 0 0-.708l-4-4a.5.5 0 0 0-.708.708L13.293 4H3.5A2.5 2.5 0 0 0 1 6.5v8a.5.5 0 0 0 1 0v-8A1.5 1.5 0 0 1 3.5 5h9.793l-3.147 3.146a.5.5 0 0 0 .708.708z"/>',
|
|
16
18
|
'bi-arrow-counterclockwise': '<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2z"/><path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466"/>',
|
|
17
19
|
};
|
package/package.json
CHANGED
|
@@ -1,58 +1,60 @@
|
|
|
1
1
|
{
|
|
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
|
-
|
|
2
|
+
"name": "@dforge-core/diagram",
|
|
3
|
+
"version": "0.0.5",
|
|
4
|
+
"description": "Entity-relationship and workspace diagram components for dForge models. Standalone Svelte 5 library with no dForge runtime dependencies.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "dForge",
|
|
7
|
+
"homepage": "https://github.com/iash44/dForge-core/tree/main/clients/packages/diagram#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/iash44/dForge-core.git",
|
|
11
|
+
"directory": "clients/packages/diagram"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/iash44/dForge-core/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"svelte",
|
|
18
|
+
"diagram",
|
|
19
|
+
"erd",
|
|
20
|
+
"entity-relationship",
|
|
21
|
+
"dforge",
|
|
22
|
+
"visualization"
|
|
23
|
+
],
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"type": "module",
|
|
28
|
+
"svelte": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"source": "./src/lib/index.ts",
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"svelte": "./dist/index.js",
|
|
35
|
+
"default": "./dist/index.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist",
|
|
40
|
+
"CHANGELOG.md"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "svelte-package",
|
|
44
|
+
"dev": "svelte-package --watch",
|
|
45
|
+
"prepack": "pnpm build",
|
|
46
|
+
"package": "pnpm pack"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@dforge-core/metadata": "workspace:*"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"svelte": "^5.0.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@sveltejs/package": "^2.5.8",
|
|
56
|
+
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
57
|
+
"svelte": "^5.56.8",
|
|
58
|
+
"typescript": "^6.0.3"
|
|
59
|
+
}
|
|
60
|
+
}
|