@erd-studio/renderer 0.1.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.
Files changed (57) hide show
  1. package/LICENSE +167 -0
  2. package/README.md +132 -0
  3. package/dist/chunk-3PJCC7BX.js +5049 -0
  4. package/dist/chunk-6XQXDZ4R.js +0 -0
  5. package/dist/chunk-HOQPSLHQ.js +90 -0
  6. package/dist/chunk-QZGU5NZH.js +0 -0
  7. package/dist/chunk-WOTSQZCQ.js +151 -0
  8. package/dist/editor.js +124 -0
  9. package/dist/index.js +16 -0
  10. package/dist/sizing.js +16 -0
  11. package/dist/store.js +15 -0
  12. package/dist/styles.css +2963 -0
  13. package/dist/types/ErdCanvas.d.ts +54 -0
  14. package/dist/types/canvas/CanvasBackdrop.d.ts +7 -0
  15. package/dist/types/canvas/flowTypes.d.ts +10 -0
  16. package/dist/types/canvas/useCanvasGraph.d.ts +37 -0
  17. package/dist/types/components/DetailPanel/BulkColumnActions.d.ts +15 -0
  18. package/dist/types/components/DetailPanel/ColumnEditor.d.ts +19 -0
  19. package/dist/types/components/DetailPanel/DescriptionEditor.d.ts +16 -0
  20. package/dist/types/components/DetailPanel/DetailPanel.d.ts +10 -0
  21. package/dist/types/components/DetailPanel/GrainEditor.d.ts +16 -0
  22. package/dist/types/components/DetailPanel/ModelRationale.d.ts +27 -0
  23. package/dist/types/components/DetailPanel/RationaleField.d.ts +28 -0
  24. package/dist/types/components/DetailPanel/RoleEditor.d.ts +13 -0
  25. package/dist/types/components/Graph/AnnotationEdge.d.ts +11 -0
  26. package/dist/types/components/Graph/AnnotationNode.d.ts +18 -0
  27. package/dist/types/components/Graph/ColumnTooltip.d.ts +25 -0
  28. package/dist/types/components/Graph/FkEdge.d.ts +21 -0
  29. package/dist/types/components/Graph/HoverTip.d.ts +47 -0
  30. package/dist/types/components/Graph/ModelNode.d.ts +21 -0
  31. package/dist/types/components/Legend/Legend.d.ts +11 -0
  32. package/dist/types/components/common/ColumnRowEditor.d.ts +75 -0
  33. package/dist/types/components/common/DataTypeSelect.d.ts +20 -0
  34. package/dist/types/components/common/KeyBadge.d.ts +16 -0
  35. package/dist/types/components/common/KeyBadgeGroup.d.ts +20 -0
  36. package/dist/types/editor.d.ts +27 -0
  37. package/dist/types/hooks/useColumnExpansion.d.ts +36 -0
  38. package/dist/types/hooks/useColumnReorder.d.ts +36 -0
  39. package/dist/types/hooks/useFocusWithinRow.d.ts +23 -0
  40. package/dist/types/hooks/useLongPressDrag.d.ts +31 -0
  41. package/dist/types/host/canvasEnvironment.d.ts +36 -0
  42. package/dist/types/index.d.ts +5 -0
  43. package/dist/types/lib/annotationColors.d.ts +13 -0
  44. package/dist/types/lib/badgeLabels.d.ts +63 -0
  45. package/dist/types/lib/cardinalityUtils.d.ts +14 -0
  46. package/dist/types/lib/dataTypeColors.d.ts +15 -0
  47. package/dist/types/lib/dragHandleSides.d.ts +27 -0
  48. package/dist/types/lib/edgeDistribution.d.ts +165 -0
  49. package/dist/types/lib/graphTransformer.d.ts +65 -0
  50. package/dist/types/lib/nodeOverlays.d.ts +41 -0
  51. package/dist/types/lib/nodeSizing.d.ts +50 -0
  52. package/dist/types/lib/stageColors.d.ts +16 -0
  53. package/dist/types/sizing.d.ts +1 -0
  54. package/dist/types/store/editorStore.d.ts +185 -0
  55. package/dist/types/store.d.ts +2 -0
  56. package/dist/types/types/graph.d.ts +145 -0
  57. package/package.json +72 -0
File without changes
@@ -0,0 +1,90 @@
1
+ import {
2
+ useEditorStore
3
+ } from "./chunk-WOTSQZCQ.js";
4
+
5
+ // src/hooks/useColumnExpansion.ts
6
+ import { useCallback } from "react";
7
+ var COLLAPSED_COLUMN_LIMIT = 5;
8
+ var NODE_THRESHOLD = 30;
9
+ function useColumnExpansion() {
10
+ const expandedNodes = useEditorStore((s) => s.expandedNodes);
11
+ const allExpanded = useEditorStore((s) => s.allExpanded);
12
+ const toggleExpansion = useEditorStore((s) => s.toggleExpansion);
13
+ const collapseAll = useEditorStore((s) => s.collapseAll);
14
+ const expandAll = useEditorStore((s) => s.expandAll);
15
+ const isExpanded = useCallback(
16
+ (modelId) => allExpanded || expandedNodes.has(modelId),
17
+ [expandedNodes, allExpanded]
18
+ );
19
+ return { isExpanded, toggleExpansion, collapseAll, expandAll, allExpanded };
20
+ }
21
+
22
+ // src/lib/nodeSizing.ts
23
+ var NODE_WIDTH = 280;
24
+ var NODE_HEADER_HEIGHT = 29;
25
+ var NODE_COLUMN_HEIGHT = 25;
26
+ var NODE_COLUMNS_PADDING = 4;
27
+ var NODE_EMPTY_ROW_HEIGHT = 33;
28
+ var NODE_FOOTER_HEIGHT = 25;
29
+ var NODE_BORDER = 4;
30
+ var NODE_GRAIN_HEIGHT = 15;
31
+ function estimateNodeHeight(rowCount, hasGrain) {
32
+ const grainHeight = hasGrain ? NODE_GRAIN_HEIGHT : 0;
33
+ const columnsHeight = rowCount > 0 ? NODE_COLUMNS_PADDING + rowCount * NODE_COLUMN_HEIGHT : NODE_COLUMNS_PADDING + NODE_EMPTY_ROW_HEIGHT;
34
+ return NODE_BORDER + NODE_HEADER_HEIGHT + grainHeight + columnsHeight + NODE_FOOTER_HEIGHT;
35
+ }
36
+ function countVisibleColumnRows(data) {
37
+ const visible = data.isStub ? data.columns.filter((c) => c.isPrimaryKey || c.isNaturalKey) : data.columns;
38
+ const expanded = data.isExpanded ?? false;
39
+ if (expanded) {
40
+ return visible.length + (data.columns.length > COLLAPSED_COLUMN_LIMIT ? 1 : 0);
41
+ }
42
+ if (visible.length > COLLAPSED_COLUMN_LIMIT) {
43
+ return COLLAPSED_COLUMN_LIMIT + 1;
44
+ }
45
+ return visible.length;
46
+ }
47
+ function resolveNodeDimensions(node) {
48
+ return {
49
+ width: node.measured?.width ?? estimateNodeWidth(node.data),
50
+ height: node.measured?.height ?? estimateNodeHeight(countVisibleColumnRows(node.data), !!node.data.grain)
51
+ };
52
+ }
53
+ var CHAR_WIDTH_BODY = 7.5;
54
+ var CHAR_WIDTH_MONO = 7.2;
55
+ var KEY_BADGE_WIDTH = 20;
56
+ var COL_ROW_PADDING = 20;
57
+ var COL_ROW_GAPS = 46;
58
+ var EXTRA_BADGE_WIDTH = 30;
59
+ var HEADER_PADDING = 20;
60
+ var LAYER_BADGE_WIDTH = 36;
61
+ var SOURCE_BADGE_WIDTH = 30;
62
+ var WIDTH_SAFETY_MARGIN = 24;
63
+ var MIN_NODE_WIDTH = 220;
64
+ var MAX_NODE_WIDTH = 560;
65
+ function estimateNodeWidth(data) {
66
+ const { modelName, columns, provenance } = data;
67
+ const headerWidth = HEADER_PADDING + modelName.length * CHAR_WIDTH_BODY + LAYER_BADGE_WIDTH + (provenance ? SOURCE_BADGE_WIDTH : 0);
68
+ let maxColWidth = 0;
69
+ for (const col of columns) {
70
+ const keyBadges = (col.isPrimaryKey ? 1 : 0) + (col.isForeignKey ? 1 : 0) + (col.isNaturalKey ? 1 : 0);
71
+ const extraBadges = (col.scdType !== void 0 ? 1 : 0) + (col.additiveType !== void 0 ? 1 : 0);
72
+ const colWidth = COL_ROW_PADDING + keyBadges * KEY_BADGE_WIDTH + col.name.length * CHAR_WIDTH_BODY + col.dataType.length * CHAR_WIDTH_MONO + extraBadges * EXTRA_BADGE_WIDTH + COL_ROW_GAPS;
73
+ if (colWidth > maxColWidth) {
74
+ maxColWidth = colWidth;
75
+ }
76
+ }
77
+ const rawWidth = Math.max(headerWidth, maxColWidth) + WIDTH_SAFETY_MARGIN;
78
+ return Math.round(Math.max(MIN_NODE_WIDTH, Math.min(MAX_NODE_WIDTH, rawWidth)));
79
+ }
80
+
81
+ export {
82
+ COLLAPSED_COLUMN_LIMIT,
83
+ NODE_THRESHOLD,
84
+ useColumnExpansion,
85
+ NODE_WIDTH,
86
+ estimateNodeHeight,
87
+ countVisibleColumnRows,
88
+ resolveNodeDimensions,
89
+ estimateNodeWidth
90
+ };
File without changes
@@ -0,0 +1,151 @@
1
+ // src/store/editorStore.ts
2
+ import { createContext, createElement, useContext } from "react";
3
+ import { createStore, useStore } from "zustand";
4
+ function createCanvasSlice(set) {
5
+ return {
6
+ // Default state
7
+ searchQuery: "",
8
+ selectedNode: null,
9
+ selectedEdges: [],
10
+ selectedEdge: null,
11
+ detailPanelOpen: false,
12
+ pendingDeleteConfirmation: false,
13
+ domain: null,
14
+ nodes: [],
15
+ edges: [],
16
+ editingAnnotationId: null,
17
+ existingModels: [],
18
+ contextMenu: null,
19
+ legendOpen: false,
20
+ dragLineState: null,
21
+ annotationLinkDrag: null,
22
+ highlightedColumns: /* @__PURE__ */ new Set(),
23
+ expandedNodes: /* @__PURE__ */ new Set(),
24
+ allExpanded: false,
25
+ selectedColumns: [],
26
+ lastSelectedColumn: null,
27
+ editingColumn: null,
28
+ selectedAnnotation: null,
29
+ // Actions
30
+ setSearchQuery: (query) => set({ searchQuery: query }),
31
+ selectNode: (nodeName) => set({ selectedNode: nodeName, selectedAnnotation: null, selectedColumns: [], lastSelectedColumn: null, editingColumn: null }),
32
+ setSelectedEdges: (edgeIds) => set({ selectedEdges: edgeIds }),
33
+ setSelectedEdge: (edgeId) => set({ selectedEdge: edgeId }),
34
+ setDetailPanelOpen: (open) => set({ detailPanelOpen: open }),
35
+ setPendingDeleteConfirmation: (pending) => set({ pendingDeleteConfirmation: pending }),
36
+ // Clear column selection on domain reload
37
+ setDomain: (domain) => set({ domain, selectedColumns: [], lastSelectedColumn: null, editingColumn: null }),
38
+ setNodes: (nodes) => set({ nodes }),
39
+ setEdges: (edges) => set({ edges }),
40
+ setEditingAnnotationId: (id) => set({ editingAnnotationId: id }),
41
+ setExistingModels: (models) => set({ existingModels: models }),
42
+ openEdgeContextMenu: (x, y, data) => set({ contextMenu: { type: "edge", x, y, data } }),
43
+ openNodeContextMenu: (x, y, modelName) => set({ contextMenu: { type: "node", x, y, modelName } }),
44
+ openAnnotationContextMenu: (x, y, annotationId) => set({ contextMenu: { type: "annotation", x, y, annotationId } }),
45
+ closeContextMenu: () => set({ contextMenu: null }),
46
+ setLegendOpen: (open) => set({ legendOpen: open }),
47
+ startDragLine: (modelName, columnName, sourceX, sourceY) => set({
48
+ dragLineState: {
49
+ sourceModelName: modelName,
50
+ sourceColumnName: columnName,
51
+ sourceX,
52
+ sourceY,
53
+ currentX: sourceX,
54
+ currentY: sourceY
55
+ }
56
+ }),
57
+ updateDragLineMouse: (mouseX, mouseY) => set(
58
+ (state) => state.dragLineState ? {
59
+ dragLineState: {
60
+ ...state.dragLineState,
61
+ currentX: mouseX,
62
+ currentY: mouseY
63
+ }
64
+ } : {}
65
+ ),
66
+ endDragLine: () => set({ dragLineState: null }),
67
+ startAnnotationLinkDrag: (annotationId, sourceX, sourceY) => set({ annotationLinkDrag: { annotationId, sourceX, sourceY, currentX: sourceX, currentY: sourceY } }),
68
+ updateAnnotationLinkDrag: (mouseX, mouseY) => set(
69
+ (state) => state.annotationLinkDrag ? { annotationLinkDrag: { ...state.annotationLinkDrag, currentX: mouseX, currentY: mouseY } } : {}
70
+ ),
71
+ endAnnotationLinkDrag: () => set({ annotationLinkDrag: null }),
72
+ setHighlightedColumns: (columns) => set({ highlightedColumns: columns }),
73
+ expandAll: (modelIds) => set({ expandedNodes: new Set(modelIds), allExpanded: true }),
74
+ expandNew: (modelIds) => set((state) => {
75
+ const next = new Set(state.expandedNodes);
76
+ modelIds.forEach((id) => next.add(id));
77
+ return { expandedNodes: next };
78
+ }),
79
+ collapseAll: () => set({ expandedNodes: /* @__PURE__ */ new Set(), allExpanded: false }),
80
+ toggleExpansion: (modelId) => set((state) => {
81
+ const next = new Set(state.expandedNodes);
82
+ if (next.has(modelId)) {
83
+ next.delete(modelId);
84
+ } else {
85
+ next.add(modelId);
86
+ }
87
+ return { expandedNodes: next, allExpanded: false };
88
+ }),
89
+ setExpandedNodes: (nodes, allExpanded) => set({ expandedNodes: new Set(nodes), allExpanded }),
90
+ // Column selection actions
91
+ selectColumn: (name) => set({ selectedColumns: [name], lastSelectedColumn: name, editingColumn: null }),
92
+ toggleColumnSelection: (name) => set((state) => {
93
+ const idx = state.selectedColumns.indexOf(name);
94
+ const next = idx >= 0 ? state.selectedColumns.filter((n) => n !== name) : [...state.selectedColumns, name];
95
+ return { selectedColumns: next, lastSelectedColumn: name, editingColumn: null };
96
+ }),
97
+ selectColumnRange: (name, allNames) => set((state) => {
98
+ const anchor = state.lastSelectedColumn;
99
+ if (!anchor) return { selectedColumns: [name], lastSelectedColumn: name, editingColumn: null };
100
+ const anchorIdx = allNames.indexOf(anchor);
101
+ const targetIdx = allNames.indexOf(name);
102
+ if (anchorIdx < 0 || targetIdx < 0) return { selectedColumns: [name], lastSelectedColumn: name, editingColumn: null };
103
+ const start = Math.min(anchorIdx, targetIdx);
104
+ const end = Math.max(anchorIdx, targetIdx);
105
+ return { selectedColumns: allNames.slice(start, end + 1), editingColumn: null };
106
+ }),
107
+ clearColumnSelection: () => set({ selectedColumns: [], lastSelectedColumn: null, editingColumn: null }),
108
+ setEditingColumn: (name) => set({ editingColumn: name }),
109
+ // Annotation selection actions
110
+ selectAnnotation: (annotationId) => set({
111
+ selectedAnnotation: annotationId,
112
+ selectedNode: null,
113
+ detailPanelOpen: false,
114
+ pendingDeleteConfirmation: false,
115
+ selectedEdge: null,
116
+ selectedEdges: [],
117
+ highlightedColumns: /* @__PURE__ */ new Set(),
118
+ selectedColumns: [],
119
+ lastSelectedColumn: null,
120
+ editingColumn: null
121
+ })
122
+ };
123
+ }
124
+ function createCanvasStore(initial) {
125
+ return createStore()((set) => ({
126
+ ...createCanvasSlice(set),
127
+ ...initial
128
+ }));
129
+ }
130
+ var CanvasStoreContext = createContext(null);
131
+ function CanvasStoreProvider({ store, children }) {
132
+ return createElement(CanvasStoreContext.Provider, { value: store }, children);
133
+ }
134
+ function useEditorStoreApi() {
135
+ const store = useContext(CanvasStoreContext);
136
+ if (!store) {
137
+ throw new Error("useEditorStore must be used inside a CanvasStoreProvider (or <ErdCanvas>).");
138
+ }
139
+ return store;
140
+ }
141
+ function useEditorStore(selector) {
142
+ return useStore(useEditorStoreApi(), selector);
143
+ }
144
+
145
+ export {
146
+ createCanvasSlice,
147
+ createCanvasStore,
148
+ CanvasStoreProvider,
149
+ useEditorStoreApi,
150
+ useEditorStore
151
+ };
package/dist/editor.js ADDED
@@ -0,0 +1,124 @@
1
+ import {
2
+ ADDITIVE_BADGE,
3
+ ADDITIVE_TITLE,
4
+ ANNOTATION_COLORS,
5
+ ANNOTATION_MINIMAP_COLORS,
6
+ AnnotationEdge,
7
+ AnnotationNode,
8
+ CanvasBackdrop,
9
+ CanvasEnvironmentProvider,
10
+ ColumnRowEditor,
11
+ DISCREPANCY_PHRASE,
12
+ DataTypeSelect,
13
+ DetailPanel,
14
+ ErdCanvas,
15
+ FkEdge,
16
+ KeyBadge,
17
+ KeyBadgeGroup,
18
+ Legend,
19
+ ModelNode,
20
+ NOOP_CANVAS_HOST,
21
+ SCD_BADGE,
22
+ SCD_TITLE,
23
+ SOURCE_LABEL,
24
+ SOURCE_LEGEND_DESC,
25
+ SOURCE_ORDER,
26
+ SOURCE_PHRASE,
27
+ STAGE_HEX,
28
+ applyNodeOverlays,
29
+ canvasEdgeTypes,
30
+ canvasNodeTypes,
31
+ edgeHoverText,
32
+ getDataTypeColor,
33
+ nodeRect,
34
+ pickHandleSides,
35
+ repickHandleSides,
36
+ stageNodeColor,
37
+ swapCardinality,
38
+ transformDomain,
39
+ useCanvasGraph,
40
+ useCanvasHost,
41
+ useColumnReorder,
42
+ useFocusWithinRow,
43
+ useIsViewer,
44
+ useLongPressDrag,
45
+ useSend
46
+ } from "./chunk-3PJCC7BX.js";
47
+ import "./chunk-QZGU5NZH.js";
48
+ import "./chunk-6XQXDZ4R.js";
49
+ import {
50
+ COLLAPSED_COLUMN_LIMIT,
51
+ NODE_THRESHOLD,
52
+ NODE_WIDTH,
53
+ countVisibleColumnRows,
54
+ estimateNodeHeight,
55
+ estimateNodeWidth,
56
+ resolveNodeDimensions,
57
+ useColumnExpansion
58
+ } from "./chunk-HOQPSLHQ.js";
59
+ import {
60
+ CanvasStoreProvider,
61
+ createCanvasSlice,
62
+ createCanvasStore,
63
+ useEditorStore,
64
+ useEditorStoreApi
65
+ } from "./chunk-WOTSQZCQ.js";
66
+ export {
67
+ ADDITIVE_BADGE,
68
+ ADDITIVE_TITLE,
69
+ ANNOTATION_COLORS,
70
+ ANNOTATION_MINIMAP_COLORS,
71
+ AnnotationEdge,
72
+ AnnotationNode,
73
+ COLLAPSED_COLUMN_LIMIT,
74
+ CanvasBackdrop,
75
+ CanvasEnvironmentProvider,
76
+ CanvasStoreProvider,
77
+ ColumnRowEditor,
78
+ DISCREPANCY_PHRASE,
79
+ DataTypeSelect,
80
+ DetailPanel,
81
+ ErdCanvas,
82
+ FkEdge,
83
+ KeyBadge,
84
+ KeyBadgeGroup,
85
+ Legend,
86
+ ModelNode,
87
+ NODE_THRESHOLD,
88
+ NODE_WIDTH,
89
+ NOOP_CANVAS_HOST,
90
+ SCD_BADGE,
91
+ SCD_TITLE,
92
+ SOURCE_LABEL,
93
+ SOURCE_LEGEND_DESC,
94
+ SOURCE_ORDER,
95
+ SOURCE_PHRASE,
96
+ STAGE_HEX,
97
+ applyNodeOverlays,
98
+ canvasEdgeTypes,
99
+ canvasNodeTypes,
100
+ countVisibleColumnRows,
101
+ createCanvasSlice,
102
+ createCanvasStore,
103
+ edgeHoverText,
104
+ estimateNodeHeight,
105
+ estimateNodeWidth,
106
+ getDataTypeColor,
107
+ nodeRect,
108
+ pickHandleSides,
109
+ repickHandleSides,
110
+ resolveNodeDimensions,
111
+ stageNodeColor,
112
+ swapCardinality,
113
+ transformDomain,
114
+ useCanvasGraph,
115
+ useCanvasHost,
116
+ useColumnExpansion,
117
+ useColumnReorder,
118
+ useEditorStore,
119
+ useEditorStoreApi,
120
+ useFocusWithinRow,
121
+ useIsViewer,
122
+ useLongPressDrag,
123
+ useSend
124
+ };
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ import {
2
+ ErdCanvas,
3
+ nodeRect,
4
+ pickHandleSides,
5
+ repickHandleSides,
6
+ transformDomain
7
+ } from "./chunk-3PJCC7BX.js";
8
+ import "./chunk-HOQPSLHQ.js";
9
+ import "./chunk-WOTSQZCQ.js";
10
+ export {
11
+ ErdCanvas,
12
+ nodeRect,
13
+ pickHandleSides,
14
+ repickHandleSides,
15
+ transformDomain
16
+ };
package/dist/sizing.js ADDED
@@ -0,0 +1,16 @@
1
+ import "./chunk-6XQXDZ4R.js";
2
+ import {
3
+ NODE_WIDTH,
4
+ countVisibleColumnRows,
5
+ estimateNodeHeight,
6
+ estimateNodeWidth,
7
+ resolveNodeDimensions
8
+ } from "./chunk-HOQPSLHQ.js";
9
+ import "./chunk-WOTSQZCQ.js";
10
+ export {
11
+ NODE_WIDTH,
12
+ countVisibleColumnRows,
13
+ estimateNodeHeight,
14
+ estimateNodeWidth,
15
+ resolveNodeDimensions
16
+ };
package/dist/store.js ADDED
@@ -0,0 +1,15 @@
1
+ import "./chunk-QZGU5NZH.js";
2
+ import {
3
+ CanvasStoreProvider,
4
+ createCanvasSlice,
5
+ createCanvasStore,
6
+ useEditorStore,
7
+ useEditorStoreApi
8
+ } from "./chunk-WOTSQZCQ.js";
9
+ export {
10
+ CanvasStoreProvider,
11
+ createCanvasSlice,
12
+ createCanvasStore,
13
+ useEditorStore,
14
+ useEditorStoreApi
15
+ };