@dforge-core/diagram 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +45 -0
  3. package/dist/EntityNode.svelte +167 -0
  4. package/dist/EntityNode.svelte.d.ts +8 -0
  5. package/dist/EntityNode.svelte.d.ts.map +1 -0
  6. package/dist/IconButton.svelte +83 -0
  7. package/dist/IconButton.svelte.d.ts +13 -0
  8. package/dist/IconButton.svelte.d.ts.map +1 -0
  9. package/dist/SimpleDiagram/DiagramDebug.svelte +499 -0
  10. package/dist/SimpleDiagram/DiagramDebug.svelte.d.ts +4 -0
  11. package/dist/SimpleDiagram/DiagramDebug.svelte.d.ts.map +1 -0
  12. package/dist/SimpleDiagram/ModelDiagram.svelte +569 -0
  13. package/dist/SimpleDiagram/ModelDiagram.svelte.d.ts +18 -0
  14. package/dist/SimpleDiagram/ModelDiagram.svelte.d.ts.map +1 -0
  15. package/dist/SimpleDiagram/NOTE.md +14 -0
  16. package/dist/SimpleDiagram/SimpleDiagram.svelte +755 -0
  17. package/dist/SimpleDiagram/SimpleDiagram.svelte.d.ts +13 -0
  18. package/dist/SimpleDiagram/SimpleDiagram.svelte.d.ts.map +1 -0
  19. package/dist/SimpleDiagram/diagram-geometry.d.ts +33 -0
  20. package/dist/SimpleDiagram/diagram-geometry.d.ts.map +1 -0
  21. package/dist/SimpleDiagram/diagram-geometry.js +137 -0
  22. package/dist/SimpleDiagram/simple-layout.d.ts +27 -0
  23. package/dist/SimpleDiagram/simple-layout.d.ts.map +1 -0
  24. package/dist/SimpleDiagram/simple-layout.js +364 -0
  25. package/dist/WorkspaceMap/ModuleCard.svelte +165 -0
  26. package/dist/WorkspaceMap/ModuleCard.svelte.d.ts +8 -0
  27. package/dist/WorkspaceMap/ModuleCard.svelte.d.ts.map +1 -0
  28. package/dist/WorkspaceMap/WorkspaceMap.svelte +319 -0
  29. package/dist/WorkspaceMap/WorkspaceMap.svelte.d.ts +12 -0
  30. package/dist/WorkspaceMap/WorkspaceMap.svelte.d.ts.map +1 -0
  31. package/dist/WorkspaceMap/workspace-graph.d.ts +18 -0
  32. package/dist/WorkspaceMap/workspace-graph.d.ts.map +1 -0
  33. package/dist/WorkspaceMap/workspace-graph.js +168 -0
  34. package/dist/WorkspaceMap/workspace-types.d.ts +32 -0
  35. package/dist/WorkspaceMap/workspace-types.d.ts.map +1 -0
  36. package/dist/WorkspaceMap/workspace-types.js +1 -0
  37. package/dist/diagram-types.d.ts +47 -0
  38. package/dist/diagram-types.d.ts.map +1 -0
  39. package/dist/diagram-types.js +2 -0
  40. package/dist/edge-routing.d.ts +82 -0
  41. package/dist/edge-routing.d.ts.map +1 -0
  42. package/dist/edge-routing.js +388 -0
  43. package/dist/i18n.d.ts +13 -0
  44. package/dist/i18n.d.ts.map +1 -0
  45. package/dist/i18n.js +20 -0
  46. package/dist/index.d.ts +23 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +21 -0
  49. package/dist/local-storage-sync.d.ts +10 -0
  50. package/dist/local-storage-sync.d.ts.map +1 -0
  51. package/dist/local-storage-sync.js +39 -0
  52. package/dist/model-graph.d.ts +20 -0
  53. package/dist/model-graph.d.ts.map +1 -0
  54. package/dist/model-graph.js +100 -0
  55. package/dist/model-types.d.ts +55 -0
  56. package/dist/model-types.d.ts.map +1 -0
  57. package/dist/model-types.js +11 -0
  58. package/dist/pan-zoom.svelte.d.ts +34 -0
  59. package/dist/pan-zoom.svelte.d.ts.map +1 -0
  60. package/dist/pan-zoom.svelte.js +130 -0
  61. package/package.json +55 -0
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Creates a debounced localStorage sync helper.
3
+ * Accepts a key getter (function) or static string for reactivity with Svelte 5 props.
4
+ */
5
+ export declare function createLocalStorageSync<T>(key: string | (() => string | undefined) | undefined, debounceMs?: number): {
6
+ load: () => T | null;
7
+ save: (value: T) => void;
8
+ clear: () => void;
9
+ };
10
+ //# sourceMappingURL=local-storage-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-storage-sync.d.ts","sourceRoot":"","sources":["../src/lib/local-storage-sync.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,EAAE,UAAU,SAAM;gBAQ9F,CAAC,GAAG,IAAI;kBAYJ,CAAC;;EAgBtB"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Creates a debounced localStorage sync helper.
3
+ * Accepts a key getter (function) or static string for reactivity with Svelte 5 props.
4
+ */
5
+ export function createLocalStorageSync(key, debounceMs = 500) {
6
+ let timer;
7
+ const resolveKey = () => typeof key === 'function' ? key() : key;
8
+ const hasStorage = typeof localStorage !== 'undefined';
9
+ const load = () => {
10
+ const k = resolveKey();
11
+ if (!k || !hasStorage)
12
+ return null;
13
+ try {
14
+ const raw = localStorage.getItem(k);
15
+ if (!raw)
16
+ return null;
17
+ return JSON.parse(raw);
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ };
23
+ const save = (value) => {
24
+ const k = resolveKey();
25
+ if (!k || !hasStorage)
26
+ return;
27
+ clearTimeout(timer);
28
+ timer = setTimeout(() => {
29
+ try {
30
+ localStorage.setItem(k, JSON.stringify(value));
31
+ }
32
+ catch { /* quota exceeded — ignore */ }
33
+ }, debounceMs);
34
+ };
35
+ const clear = () => {
36
+ clearTimeout(timer);
37
+ };
38
+ return { load, save, clear };
39
+ }
@@ -0,0 +1,20 @@
1
+ import type { DiagramNode, DiagramEdge } from './diagram-types';
2
+ import type { ModelGetResponse, ModelModule } from './model-types';
3
+ export declare const MODULE_COLORS: string[];
4
+ export declare function getModuleColor(index: number): string;
5
+ /**
6
+ * Build a diagram graph from the given modules.
7
+ * Nodes start at position {0,0} — call simpleLayout() to compute positions.
8
+ */
9
+ export declare function buildGraph(modules: ModelModule[], entityMap: Map<number, string>, options?: {
10
+ intraModuleEdgesOnly?: boolean;
11
+ colorOffset?: number;
12
+ }): {
13
+ nodes: DiagramNode[];
14
+ edges: DiagramEdge[];
15
+ };
16
+ /**
17
+ * Build the full entityId → label map from a ModelGetResponse.
18
+ */
19
+ export declare function buildEntityMap(response: ModelGetResponse): Map<number, string>;
20
+ //# sourceMappingURL=model-graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-graph.d.ts","sourceRoot":"","sources":["../src/lib/model-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEnE,eAAO,MAAM,aAAa,UASzB,CAAC;AAEF,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAMD;;;GAGG;AACH,wBAAgB,UAAU,CACzB,OAAO,EAAE,WAAW,EAAE,EACtB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,OAAO,CAAC,EAAE;IAAE,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAChE;IAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IAAC,KAAK,EAAE,WAAW,EAAE,CAAA;CAAE,CA6EhD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,gBAAgB,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAQ9E"}
@@ -0,0 +1,100 @@
1
+ export const MODULE_COLORS = [
2
+ '#0d6efd', // blue
3
+ '#198754', // green
4
+ '#dc3545', // red
5
+ '#fd7e14', // orange
6
+ '#6f42c1', // purple
7
+ '#20c997', // teal
8
+ '#d63384', // pink
9
+ '#0dcaf0', // cyan
10
+ ];
11
+ export function getModuleColor(index) {
12
+ return MODULE_COLORS[index % MODULE_COLORS.length];
13
+ }
14
+ const NODE_WIDTH = 260;
15
+ const NODE_HEIGHT_BASE = 60;
16
+ const ROW_HEIGHT = 22;
17
+ /**
18
+ * Build a diagram graph from the given modules.
19
+ * Nodes start at position {0,0} — call simpleLayout() to compute positions.
20
+ */
21
+ export function buildGraph(modules, entityMap, options) {
22
+ const graphNodes = [];
23
+ const graphEdges = [];
24
+ const entityNodeMap = new Map();
25
+ // Collect all entity IDs in the filtered modules (for edge filtering)
26
+ const moduleEntityIds = new Set();
27
+ for (const mod of modules) {
28
+ for (const ent of mod.entities) {
29
+ moduleEntityIds.add(ent.entityId);
30
+ }
31
+ }
32
+ const offset = options?.colorOffset ?? 0;
33
+ for (let mi = 0; mi < modules.length; mi++) {
34
+ const mod = modules[mi];
35
+ const color = getModuleColor(mi + offset);
36
+ for (const ent of mod.entities) {
37
+ const nodeId = `entity-${ent.entityId}`;
38
+ entityNodeMap.set(ent.entityId, nodeId);
39
+ const nodeHeight = NODE_HEIGHT_BASE + ent.columns.length * ROW_HEIGHT;
40
+ graphNodes.push({
41
+ id: nodeId,
42
+ x: 0,
43
+ y: 0,
44
+ width: NODE_WIDTH,
45
+ height: nodeHeight,
46
+ data: {
47
+ entityCd: ent.entityCd,
48
+ resLabel: ent.resLabel,
49
+ resIcon: ent.resIcon,
50
+ moduleCd: mod.moduleCd,
51
+ moduleColor: color,
52
+ columns: ent.columns,
53
+ entityMap,
54
+ },
55
+ });
56
+ }
57
+ }
58
+ // Build edges for FK columns (type 'R')
59
+ for (let mi = 0; mi < modules.length; mi++) {
60
+ const mod = modules[mi];
61
+ const color = getModuleColor(mi + offset);
62
+ for (const ent of mod.entities) {
63
+ const sourceNodeId = entityNodeMap.get(ent.entityId);
64
+ if (!sourceNodeId)
65
+ continue;
66
+ for (const col of ent.columns) {
67
+ if (col.columnType === 'R' && col.refEntityId) {
68
+ const targetNodeId = entityNodeMap.get(col.refEntityId);
69
+ if (!targetNodeId)
70
+ continue;
71
+ if (options?.intraModuleEdgesOnly && !moduleEntityIds.has(col.refEntityId)) {
72
+ continue;
73
+ }
74
+ graphEdges.push({
75
+ id: `edge-${ent.entityId}-${col.columnCd}`,
76
+ source: sourceNodeId,
77
+ sourceHandle: col.columnCd,
78
+ target: targetNodeId,
79
+ color,
80
+ label: col.resLabel || col.columnCd,
81
+ isNullable: col.isNullable ?? false,
82
+ });
83
+ }
84
+ }
85
+ }
86
+ }
87
+ return { nodes: graphNodes, edges: graphEdges };
88
+ }
89
+ /**
90
+ * Build the full entityId → label map from a ModelGetResponse.
91
+ */
92
+ export function buildEntityMap(response) {
93
+ const entityMap = new Map();
94
+ for (const mod of response.modules) {
95
+ for (const ent of mod.entities) {
96
+ entityMap.set(ent.entityId, ent.resLabel || ent.entityCd);
97
+ }
98
+ }
99
+ return entityMap;
100
+ }
@@ -0,0 +1,55 @@
1
+ export interface ModelGetResponse {
2
+ modules: ModelModule[];
3
+ }
4
+ export interface ModelModule {
5
+ moduleId: number;
6
+ moduleCd: string;
7
+ moduleGuid: string;
8
+ description?: string;
9
+ isSystem: boolean;
10
+ version?: string;
11
+ displayName?: string;
12
+ icon?: string;
13
+ category?: string;
14
+ license?: string;
15
+ authorName?: string;
16
+ tags?: string;
17
+ entities: ModelEntity[];
18
+ }
19
+ export interface ModelEntity {
20
+ entityId: number;
21
+ entityCd: string;
22
+ description?: string;
23
+ schemaName?: string;
24
+ dbObject?: string;
25
+ toString?: string;
26
+ resLabel?: string;
27
+ resIcon?: string;
28
+ columns: ModelColumn[];
29
+ constraints: ModelConstraint[];
30
+ }
31
+ export interface ModelConstraint {
32
+ constraintName: string;
33
+ type?: string;
34
+ expression?: string;
35
+ fields?: string[];
36
+ message?: string;
37
+ }
38
+ export interface ModelColumn {
39
+ columnCd: string;
40
+ description?: string;
41
+ columnType?: string;
42
+ isPk: boolean;
43
+ isNullable?: boolean;
44
+ fieldTypeCd?: string;
45
+ baseDatatypeCd: string;
46
+ maxLen?: number;
47
+ precision?: number;
48
+ flags?: string;
49
+ formula?: string;
50
+ refEntityId?: number;
51
+ thisKey?: string;
52
+ otherKey?: string;
53
+ resLabel?: string;
54
+ }
55
+ //# sourceMappingURL=model-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-types.d.ts","sourceRoot":"","sources":["../src/lib/model-types.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,WAAW,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,WAAW,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,WAAW,EAAE,eAAe,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,eAAe;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB"}
@@ -0,0 +1,11 @@
1
+ // Metadata model contract consumed by the diagram components.
2
+ //
3
+ // These interfaces mirror the `Model*` types served by the dForge API
4
+ // (`metadata.getModel`). They are duplicated here — rather than imported
5
+ // from `@dforge/sdk` — so this package can be published and consumed
6
+ // standalone (e.g. by editor extensions) without pulling in the SDK.
7
+ //
8
+ // TypeScript is structurally typed, so SDK-produced objects can still be
9
+ // passed directly into these components. Keep the shapes in sync with
10
+ // `clients/packages/sdk/src/types/metadata.ts`.
11
+ export {};
@@ -0,0 +1,34 @@
1
+ import type { ViewTransform } from './diagram-types';
2
+ export interface PanZoomConfig {
3
+ minZoom?: number;
4
+ maxZoom?: number;
5
+ bgClass?: string;
6
+ getSvgEl: () => SVGSVGElement | undefined;
7
+ getContainerEl: () => HTMLElement | undefined;
8
+ }
9
+ export interface ContentBounds {
10
+ minX: number;
11
+ minY: number;
12
+ maxX: number;
13
+ maxY: number;
14
+ }
15
+ /**
16
+ * Creates a pan/zoom controller with reactive Svelte 5 state.
17
+ * Encapsulates transform, zooming, panning, and coordinate conversion.
18
+ */
19
+ export declare function createPanZoom(config: PanZoomConfig): {
20
+ readonly transform: ViewTransform;
21
+ readonly isPanning: boolean;
22
+ clientToSvg: (clientX: number, clientY: number) => {
23
+ x: number;
24
+ y: number;
25
+ };
26
+ handleWheel: (e: WheelEvent) => void;
27
+ startPan: (e: PointerEvent) => boolean;
28
+ onPointerMove: (e: PointerEvent) => boolean;
29
+ endPan: (e: PointerEvent) => void;
30
+ zoomBy: (factor: number) => void;
31
+ fitView: (bounds: ContentBounds, padding?: number) => void;
32
+ setTransform: (t: ViewTransform) => void;
33
+ };
34
+ //# sourceMappingURL=pan-zoom.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pan-zoom.svelte.d.ts","sourceRoot":"","sources":["../src/lib/pan-zoom.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,WAAW,aAAa;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,aAAa,GAAG,SAAS,CAAC;IAC1C,cAAc,EAAE,MAAM,WAAW,GAAG,SAAS,CAAC;CAC9C;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa;;;2BASpB,MAAM,WAAW,MAAM,KAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE;qBAWxD,UAAU;kBAmBb,YAAY,KAAG,OAAO;uBAkBjB,YAAY,KAAG,OAAO;gBAW7B,YAAY;qBASP,MAAM;sBAgBL,aAAa;sBAqBb,aAAa;EAgBtC"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Creates a pan/zoom controller with reactive Svelte 5 state.
3
+ * Encapsulates transform, zooming, panning, and coordinate conversion.
4
+ */
5
+ export function createPanZoom(config) {
6
+ let transform = $state({ x: 0, y: 0, k: 1 });
7
+ let panning = $state(null);
8
+ function clampZoom(k) {
9
+ return Math.min(config.maxZoom ?? 2, Math.max(config.minZoom ?? 0.1, k));
10
+ }
11
+ /** Convert client (screen) coordinates to SVG content coordinates. */
12
+ function clientToSvg(clientX, clientY) {
13
+ const svgEl = config.getSvgEl();
14
+ if (!svgEl)
15
+ return { x: 0, y: 0 };
16
+ const rect = svgEl.getBoundingClientRect();
17
+ return {
18
+ x: (clientX - rect.left - transform.x) / transform.k,
19
+ y: (clientY - rect.top - transform.y) / transform.k,
20
+ };
21
+ }
22
+ /** Zoom toward the cursor position. Attach to wheel event (passive: false). */
23
+ function handleWheel(e) {
24
+ e.preventDefault();
25
+ const svgEl = config.getSvgEl();
26
+ if (!svgEl)
27
+ return;
28
+ const rect = svgEl.getBoundingClientRect();
29
+ const mouseX = e.clientX - rect.left;
30
+ const mouseY = e.clientY - rect.top;
31
+ const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1;
32
+ const newK = clampZoom(transform.k * factor);
33
+ const ratio = newK / transform.k;
34
+ transform = {
35
+ x: mouseX - (mouseX - transform.x) * ratio,
36
+ y: mouseY - (mouseY - transform.y) * ratio,
37
+ k: newK,
38
+ };
39
+ }
40
+ /** Start panning on background pointer down. Returns true if pan started. */
41
+ function startPan(e) {
42
+ const svgEl = config.getSvgEl();
43
+ if (!svgEl)
44
+ return false;
45
+ const target = e.target;
46
+ const bgClass = config.bgClass ?? 'diagram-bg';
47
+ if (target !== svgEl && !target.classList.contains(bgClass))
48
+ return false;
49
+ e.preventDefault();
50
+ panning = {
51
+ startX: e.clientX,
52
+ startY: e.clientY,
53
+ startTx: transform.x,
54
+ startTy: transform.y,
55
+ };
56
+ svgEl.setPointerCapture(e.pointerId);
57
+ return true;
58
+ }
59
+ /** Handle pointer move for panning. Returns true if panning was active. */
60
+ function onPointerMove(e) {
61
+ if (!panning)
62
+ return false;
63
+ transform = {
64
+ ...transform,
65
+ x: panning.startTx + (e.clientX - panning.startX),
66
+ y: panning.startTy + (e.clientY - panning.startY),
67
+ };
68
+ return true;
69
+ }
70
+ /** End panning on pointer up. */
71
+ function endPan(e) {
72
+ if (panning) {
73
+ const svgEl = config.getSvgEl();
74
+ if (svgEl)
75
+ svgEl.releasePointerCapture(e.pointerId);
76
+ panning = null;
77
+ }
78
+ }
79
+ /** Zoom by a factor toward the container center. factor > 1 zooms in. */
80
+ function zoomBy(factor) {
81
+ const containerEl = config.getContainerEl();
82
+ if (!containerEl)
83
+ return;
84
+ const rect = containerEl.getBoundingClientRect();
85
+ const cx = rect.width / 2;
86
+ const cy = rect.height / 2;
87
+ const newK = clampZoom(transform.k * factor);
88
+ const ratio = newK / transform.k;
89
+ transform = {
90
+ x: cx - (cx - transform.x) * ratio,
91
+ y: cy - (cy - transform.y) * ratio,
92
+ k: newK,
93
+ };
94
+ }
95
+ /** Fit the view to show all content within the given bounds. */
96
+ function fitView(bounds, padding = 40) {
97
+ const containerEl = config.getContainerEl();
98
+ if (!containerEl)
99
+ return;
100
+ const rect = containerEl.getBoundingClientRect();
101
+ const contentW = bounds.maxX - bounds.minX;
102
+ const contentH = bounds.maxY - bounds.minY;
103
+ if (contentW <= 0 || contentH <= 0)
104
+ return;
105
+ const scaleX = (rect.width - padding * 2) / contentW;
106
+ const scaleY = (rect.height - padding * 2) / contentH;
107
+ const k = clampZoom(Math.min(scaleX, scaleY));
108
+ transform = {
109
+ k,
110
+ x: (rect.width - contentW * k) / 2 - bounds.minX * k,
111
+ y: (rect.height - contentH * k) / 2 - bounds.minY * k,
112
+ };
113
+ }
114
+ /** Directly set the transform (e.g. when restoring from storage). */
115
+ function setTransform(t) {
116
+ transform = t;
117
+ }
118
+ return {
119
+ get transform() { return transform; },
120
+ get isPanning() { return panning !== null; },
121
+ clientToSvg,
122
+ handleWheel,
123
+ startPan,
124
+ onPointerMove,
125
+ endPan,
126
+ zoomBy,
127
+ fitView,
128
+ setTransform,
129
+ };
130
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@dforge-core/diagram",
3
+ "version": "0.0.1",
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
+ ],
41
+ "peerDependencies": {
42
+ "svelte": "^5.0.0"
43
+ },
44
+ "devDependencies": {
45
+ "@sveltejs/package": "^2.5.7",
46
+ "@sveltejs/vite-plugin-svelte": "^7.1.2",
47
+ "svelte": "^5.55.10",
48
+ "typescript": "^6.0.2"
49
+ },
50
+ "scripts": {
51
+ "build": "svelte-package",
52
+ "dev": "svelte-package --watch",
53
+ "package": "pnpm pack"
54
+ }
55
+ }