@zoneflow/renderer-dom 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.
- package/dist/anchors.d.ts +13 -0
- package/dist/anchors.js +22 -0
- package/dist/engines/componentLayoutEngine.d.ts +2 -0
- package/dist/engines/componentLayoutEngine.js +200 -0
- package/dist/engines/debugDrawEngine.d.ts +7 -0
- package/dist/engines/debugDrawEngine.js +346 -0
- package/dist/engines/densityEngine.d.ts +2 -0
- package/dist/engines/densityEngine.js +47 -0
- package/dist/engines/drawEngine.d.ts +2 -0
- package/dist/engines/drawEngine.js +681 -0
- package/dist/engines/graphLayoutEngine.d.ts +6 -0
- package/dist/engines/graphLayoutEngine.js +255 -0
- package/dist/engines/visibilityEngine.d.ts +2 -0
- package/dist/engines/visibilityEngine.js +76 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +11 -0
- package/dist/pipeline.d.ts +8 -0
- package/dist/pipeline.js +25 -0
- package/dist/renderer.d.ts +2 -0
- package/dist/renderer.js +137 -0
- package/dist/theme.d.ts +23 -0
- package/dist/theme.js +1 -0
- package/dist/themes/defaultTheme.d.ts +9 -0
- package/dist/themes/defaultTheme.js +46 -0
- package/dist/types.d.ts +253 -0
- package/dist/types.js +1 -0
- package/package.json +23 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
export const DEFAULT_PATH_NODE_WIDTH = 120;
|
|
2
|
+
export const DEFAULT_PATH_NODE_HEIGHT = 32;
|
|
3
|
+
export const DEFAULT_PATH_NODE_OFFSET_X = 32;
|
|
4
|
+
export const DEFAULT_PATH_NODE_GAP_Y = 40;
|
|
5
|
+
function typedEntries(record) {
|
|
6
|
+
return Object.entries(record);
|
|
7
|
+
}
|
|
8
|
+
function typedValues(record) {
|
|
9
|
+
return Object.values(record);
|
|
10
|
+
}
|
|
11
|
+
function rectFromLayout(layout) {
|
|
12
|
+
return {
|
|
13
|
+
x: layout.x,
|
|
14
|
+
y: layout.y,
|
|
15
|
+
width: layout.width ?? 0,
|
|
16
|
+
height: layout.height ?? 0,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function centerOfRect(rect) {
|
|
20
|
+
return {
|
|
21
|
+
x: rect.x + rect.width / 2,
|
|
22
|
+
y: rect.y + rect.height / 2,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function resolveAnchorRect(worldPos, rect) {
|
|
26
|
+
if (!rect)
|
|
27
|
+
return undefined;
|
|
28
|
+
return {
|
|
29
|
+
...rect,
|
|
30
|
+
x: worldPos.x + rect.x,
|
|
31
|
+
y: worldPos.y + rect.y,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* relative layout -> absolute layout
|
|
36
|
+
*/
|
|
37
|
+
function resolveLayout(model, layoutModel) {
|
|
38
|
+
const resolvedZoneLayouts = {};
|
|
39
|
+
const resolvedPathLayouts = {};
|
|
40
|
+
const zoneCache = new Map();
|
|
41
|
+
function resolveZonePosition(zoneId) {
|
|
42
|
+
if (zoneCache.has(zoneId)) {
|
|
43
|
+
return zoneCache.get(zoneId);
|
|
44
|
+
}
|
|
45
|
+
const zone = model.zonesById[zoneId];
|
|
46
|
+
const layout = layoutModel.zoneLayoutsById[zoneId];
|
|
47
|
+
if (!zone || !layout) {
|
|
48
|
+
const fallback = { x: 0, y: 0 };
|
|
49
|
+
zoneCache.set(zoneId, fallback);
|
|
50
|
+
return fallback;
|
|
51
|
+
}
|
|
52
|
+
if (!zone.parentZoneId) {
|
|
53
|
+
const rootPos = { x: layout.x, y: layout.y };
|
|
54
|
+
zoneCache.set(zoneId, rootPos);
|
|
55
|
+
return rootPos;
|
|
56
|
+
}
|
|
57
|
+
const parentPos = resolveZonePosition(zone.parentZoneId);
|
|
58
|
+
const worldPos = {
|
|
59
|
+
x: parentPos.x + layout.x,
|
|
60
|
+
y: parentPos.y + layout.y,
|
|
61
|
+
};
|
|
62
|
+
zoneCache.set(zoneId, worldPos);
|
|
63
|
+
return worldPos;
|
|
64
|
+
}
|
|
65
|
+
for (const [typedZoneId, layout] of typedEntries(layoutModel.zoneLayoutsById)) {
|
|
66
|
+
const worldPos = resolveZonePosition(typedZoneId);
|
|
67
|
+
const anchors = layout.anchors;
|
|
68
|
+
const resolvedAnchors = {
|
|
69
|
+
inlet: {
|
|
70
|
+
point: {
|
|
71
|
+
x: worldPos.x + anchors.inlet.point.x,
|
|
72
|
+
y: worldPos.y + anchors.inlet.point.y,
|
|
73
|
+
},
|
|
74
|
+
rect: resolveAnchorRect(worldPos, anchors.inlet.rect),
|
|
75
|
+
},
|
|
76
|
+
outlet: {
|
|
77
|
+
point: {
|
|
78
|
+
x: worldPos.x + anchors.outlet.point.x,
|
|
79
|
+
y: worldPos.y + anchors.outlet.point.y,
|
|
80
|
+
},
|
|
81
|
+
rect: resolveAnchorRect(worldPos, anchors.outlet.rect),
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
resolvedZoneLayouts[typedZoneId] = {
|
|
85
|
+
...layout,
|
|
86
|
+
x: worldPos.x,
|
|
87
|
+
y: worldPos.y,
|
|
88
|
+
anchors: resolvedAnchors,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
for (const [pathId, pathLayout] of typedEntries(layoutModel.pathLayoutsById)) {
|
|
92
|
+
resolvedPathLayouts[pathId] = {
|
|
93
|
+
...pathLayout,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
...layoutModel,
|
|
98
|
+
zoneLayoutsById: resolvedZoneLayouts,
|
|
99
|
+
pathLayoutsById: resolvedPathLayouts,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function resolvePathNodeRect(params) {
|
|
103
|
+
const { layoutModel, pathId, sourceOutlet, fallbackIndex } = params;
|
|
104
|
+
const pathLayout = layoutModel.pathLayoutsById[pathId];
|
|
105
|
+
const preferredComponentLayout = pathLayout?.componentLayoutsById?.body ??
|
|
106
|
+
pathLayout?.componentLayoutsById?.label;
|
|
107
|
+
if (preferredComponentLayout) {
|
|
108
|
+
return rectFromLayout(preferredComponentLayout);
|
|
109
|
+
}
|
|
110
|
+
const routeOffset = pathLayout?.routeOffset;
|
|
111
|
+
return {
|
|
112
|
+
x: sourceOutlet.x + DEFAULT_PATH_NODE_OFFSET_X + (routeOffset?.x ?? 0),
|
|
113
|
+
y: sourceOutlet.y -
|
|
114
|
+
DEFAULT_PATH_NODE_HEIGHT / 2 +
|
|
115
|
+
fallbackIndex * DEFAULT_PATH_NODE_GAP_Y +
|
|
116
|
+
(routeOffset?.y ?? 0),
|
|
117
|
+
width: DEFAULT_PATH_NODE_WIDTH,
|
|
118
|
+
height: DEFAULT_PATH_NODE_HEIGHT,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function resolvePathNodeAnchors(rect) {
|
|
122
|
+
return {
|
|
123
|
+
inlet: {
|
|
124
|
+
x: rect.x,
|
|
125
|
+
y: rect.y + rect.height / 2,
|
|
126
|
+
},
|
|
127
|
+
outlet: {
|
|
128
|
+
x: rect.x + rect.width,
|
|
129
|
+
y: rect.y + rect.height / 2,
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function createZoneVisualNodes(params) {
|
|
134
|
+
const { model, layoutModel } = params;
|
|
135
|
+
const result = {};
|
|
136
|
+
for (const [typedZoneId, zone] of typedEntries(model.zonesById)) {
|
|
137
|
+
const zoneLayout = layoutModel.zoneLayoutsById[typedZoneId];
|
|
138
|
+
if (!zoneLayout)
|
|
139
|
+
continue;
|
|
140
|
+
result[typedZoneId] = {
|
|
141
|
+
universeId: model.universeId,
|
|
142
|
+
zoneId: typedZoneId,
|
|
143
|
+
zone,
|
|
144
|
+
rect: rectFromLayout(zoneLayout),
|
|
145
|
+
anchors: zoneLayout.anchors,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
function createPathVisualNodes(params) {
|
|
151
|
+
const { model, layoutModel, zonesById } = params;
|
|
152
|
+
const result = {};
|
|
153
|
+
for (const zone of typedValues(model.zonesById)) {
|
|
154
|
+
const sourceZoneVisual = zonesById[zone.id];
|
|
155
|
+
if (!sourceZoneVisual)
|
|
156
|
+
continue;
|
|
157
|
+
zone.pathIds.forEach((pathId, index) => {
|
|
158
|
+
const path = zone.pathsById[pathId];
|
|
159
|
+
if (!path)
|
|
160
|
+
return;
|
|
161
|
+
const targetZoneId = path.target?.universeId === model.universeId
|
|
162
|
+
? path.target.zoneId
|
|
163
|
+
: null;
|
|
164
|
+
const sourceOutlet = sourceZoneVisual.anchors.outlet?.point ??
|
|
165
|
+
centerOfRect(sourceZoneVisual.rect);
|
|
166
|
+
const rect = resolvePathNodeRect({
|
|
167
|
+
layoutModel,
|
|
168
|
+
pathId,
|
|
169
|
+
sourceOutlet,
|
|
170
|
+
fallbackIndex: index,
|
|
171
|
+
});
|
|
172
|
+
const anchors = resolvePathNodeAnchors(rect);
|
|
173
|
+
result[pathId] = {
|
|
174
|
+
universeId: model.universeId,
|
|
175
|
+
pathId,
|
|
176
|
+
sourceZoneId: zone.id,
|
|
177
|
+
targetZoneId,
|
|
178
|
+
path,
|
|
179
|
+
rect,
|
|
180
|
+
inlet: anchors.inlet,
|
|
181
|
+
outlet: anchors.outlet,
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
function createEdgeVisuals(params) {
|
|
188
|
+
const { model, zonesById, pathsById } = params;
|
|
189
|
+
const result = {};
|
|
190
|
+
for (const zone of typedValues(model.zonesById)) {
|
|
191
|
+
const sourceZoneVisual = zonesById[zone.id];
|
|
192
|
+
if (!sourceZoneVisual)
|
|
193
|
+
continue;
|
|
194
|
+
zone.pathIds.forEach((pathId) => {
|
|
195
|
+
const pathVisual = pathsById[pathId];
|
|
196
|
+
if (!pathVisual)
|
|
197
|
+
return;
|
|
198
|
+
const targetZoneVisual = pathVisual.targetZoneId
|
|
199
|
+
? zonesById[pathVisual.targetZoneId]
|
|
200
|
+
: undefined;
|
|
201
|
+
const zoneOutlet = sourceZoneVisual.anchors.outlet?.point ??
|
|
202
|
+
centerOfRect(sourceZoneVisual.rect);
|
|
203
|
+
const pathInlet = pathVisual.inlet ??
|
|
204
|
+
(pathVisual.rect ? centerOfRect(pathVisual.rect) : zoneOutlet);
|
|
205
|
+
const pathOutlet = pathVisual.outlet ??
|
|
206
|
+
(pathVisual.rect ? centerOfRect(pathVisual.rect) : zoneOutlet);
|
|
207
|
+
const targetInlet = targetZoneVisual
|
|
208
|
+
? (targetZoneVisual.anchors.inlet?.point ??
|
|
209
|
+
centerOfRect(targetZoneVisual.rect))
|
|
210
|
+
: pathOutlet;
|
|
211
|
+
result[pathId] = [
|
|
212
|
+
{
|
|
213
|
+
id: `${pathId}:z2p`,
|
|
214
|
+
pathId,
|
|
215
|
+
kind: "zone-to-path",
|
|
216
|
+
source: zoneOutlet,
|
|
217
|
+
target: pathInlet,
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
id: `${pathId}:p2z`,
|
|
221
|
+
pathId,
|
|
222
|
+
kind: "path-to-zone",
|
|
223
|
+
source: pathOutlet,
|
|
224
|
+
target: targetInlet,
|
|
225
|
+
},
|
|
226
|
+
];
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
export const defaultGraphLayoutEngine = {
|
|
232
|
+
compute(input) {
|
|
233
|
+
const { model, layoutModel } = input;
|
|
234
|
+
const resolvedLayout = resolveLayout(model, layoutModel);
|
|
235
|
+
const zonesById = createZoneVisualNodes({
|
|
236
|
+
model,
|
|
237
|
+
layoutModel: resolvedLayout,
|
|
238
|
+
});
|
|
239
|
+
const pathsById = createPathVisualNodes({
|
|
240
|
+
model,
|
|
241
|
+
layoutModel: resolvedLayout,
|
|
242
|
+
zonesById,
|
|
243
|
+
});
|
|
244
|
+
const edgesByPathId = createEdgeVisuals({
|
|
245
|
+
model,
|
|
246
|
+
zonesById,
|
|
247
|
+
pathsById,
|
|
248
|
+
});
|
|
249
|
+
return {
|
|
250
|
+
zonesById,
|
|
251
|
+
pathsById,
|
|
252
|
+
edgesByPathId,
|
|
253
|
+
};
|
|
254
|
+
},
|
|
255
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
function intersects(a, b) {
|
|
2
|
+
return !(a.x + a.width <= b.x ||
|
|
3
|
+
b.x + b.width <= a.x ||
|
|
4
|
+
a.y + a.height <= b.y ||
|
|
5
|
+
b.y + b.height <= a.y);
|
|
6
|
+
}
|
|
7
|
+
function containsFully(outer, inner) {
|
|
8
|
+
return (inner.x >= outer.x &&
|
|
9
|
+
inner.y >= outer.y &&
|
|
10
|
+
inner.x + inner.width <= outer.x + outer.width &&
|
|
11
|
+
inner.y + inner.height <= outer.y + outer.height);
|
|
12
|
+
}
|
|
13
|
+
function resolveZoneEmphasis(isVisible, isPartial) {
|
|
14
|
+
if (!isVisible)
|
|
15
|
+
return "hidden";
|
|
16
|
+
if (isPartial)
|
|
17
|
+
return "dim";
|
|
18
|
+
return "strong";
|
|
19
|
+
}
|
|
20
|
+
function resolvePathEmphasis(isVisible, isPartial) {
|
|
21
|
+
if (!isVisible)
|
|
22
|
+
return "hidden";
|
|
23
|
+
if (isPartial)
|
|
24
|
+
return "dim";
|
|
25
|
+
return "normal";
|
|
26
|
+
}
|
|
27
|
+
export const defaultVisibilityEngine = {
|
|
28
|
+
compute(input) {
|
|
29
|
+
const { base, graphLayout, density } = input;
|
|
30
|
+
const worldViewport = base.viewportInfo.world;
|
|
31
|
+
const zoneVisibilityById = {};
|
|
32
|
+
const pathVisibilityById = {};
|
|
33
|
+
Object.values(graphLayout.zonesById).forEach((zone) => {
|
|
34
|
+
const isVisible = intersects(worldViewport, zone.rect);
|
|
35
|
+
const isPartial = isVisible && !containsFully(worldViewport, zone.rect);
|
|
36
|
+
const emphasis = resolveZoneEmphasis(isVisible, isPartial);
|
|
37
|
+
zoneVisibilityById[zone.zoneId] = {
|
|
38
|
+
isVisible,
|
|
39
|
+
isPartial,
|
|
40
|
+
shouldRenderBody: isVisible,
|
|
41
|
+
shouldRenderContent: isVisible && zone.rect.width > 0 && zone.rect.height > 0,
|
|
42
|
+
emphasis,
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
Object.values(graphLayout.pathsById).forEach((path) => {
|
|
46
|
+
const rect = path.rect;
|
|
47
|
+
if (!rect) {
|
|
48
|
+
pathVisibilityById[path.pathId] = {
|
|
49
|
+
isVisible: false,
|
|
50
|
+
isPartial: false,
|
|
51
|
+
shouldRenderNode: false,
|
|
52
|
+
shouldRenderEdge: true,
|
|
53
|
+
shouldRenderLabel: false,
|
|
54
|
+
emphasis: "hidden",
|
|
55
|
+
};
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const isVisible = intersects(worldViewport, rect);
|
|
59
|
+
const isPartial = isVisible && !containsFully(worldViewport, rect);
|
|
60
|
+
const emphasis = resolvePathEmphasis(isVisible, isPartial);
|
|
61
|
+
const densityMode = density.pathDensityById[path.pathId];
|
|
62
|
+
pathVisibilityById[path.pathId] = {
|
|
63
|
+
isVisible,
|
|
64
|
+
isPartial,
|
|
65
|
+
shouldRenderNode: isVisible && densityMode !== "edge-only",
|
|
66
|
+
shouldRenderEdge: true,
|
|
67
|
+
shouldRenderLabel: isVisible && densityMode !== "edge-only",
|
|
68
|
+
emphasis,
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
zoneVisibilityById,
|
|
73
|
+
pathVisibilityById,
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./theme";
|
|
2
|
+
export * from "./types";
|
|
3
|
+
export * from "./anchors";
|
|
4
|
+
export * from "./pipeline";
|
|
5
|
+
export * from "./renderer";
|
|
6
|
+
export * from "./engines/graphLayoutEngine";
|
|
7
|
+
export * from "./engines/densityEngine";
|
|
8
|
+
export * from "./engines/visibilityEngine";
|
|
9
|
+
export * from "./engines/componentLayoutEngine";
|
|
10
|
+
export * from "./engines/drawEngine";
|
|
11
|
+
export * from "./engines/debugDrawEngine";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./theme";
|
|
2
|
+
export * from "./types";
|
|
3
|
+
export * from "./anchors";
|
|
4
|
+
export * from "./pipeline";
|
|
5
|
+
export * from "./renderer";
|
|
6
|
+
export * from "./engines/graphLayoutEngine";
|
|
7
|
+
export * from "./engines/densityEngine";
|
|
8
|
+
export * from "./engines/visibilityEngine";
|
|
9
|
+
export * from "./engines/componentLayoutEngine";
|
|
10
|
+
export * from "./engines/drawEngine";
|
|
11
|
+
export * from "./engines/debugDrawEngine";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ComponentLayoutEngine, DensityEngine, GraphLayoutEngine, RenderPipelineInput, RenderPipelineResult, VisibilityEngine } from "./types";
|
|
2
|
+
export type RenderPipelineEngines = {
|
|
3
|
+
graphLayoutEngine: GraphLayoutEngine;
|
|
4
|
+
densityEngine: DensityEngine;
|
|
5
|
+
visibilityEngine: VisibilityEngine;
|
|
6
|
+
componentLayoutEngine: ComponentLayoutEngine;
|
|
7
|
+
};
|
|
8
|
+
export declare function runRenderPipeline(input: RenderPipelineInput, engines: RenderPipelineEngines): RenderPipelineResult;
|
package/dist/pipeline.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function runRenderPipeline(input, engines) {
|
|
2
|
+
const graphLayout = engines.graphLayoutEngine.compute(input);
|
|
3
|
+
const density = engines.densityEngine.compute({
|
|
4
|
+
base: input,
|
|
5
|
+
graphLayout,
|
|
6
|
+
});
|
|
7
|
+
const visibility = engines.visibilityEngine.compute({
|
|
8
|
+
base: input,
|
|
9
|
+
graphLayout,
|
|
10
|
+
density,
|
|
11
|
+
});
|
|
12
|
+
const componentLayout = engines.componentLayoutEngine.compute({
|
|
13
|
+
base: input,
|
|
14
|
+
graphLayout,
|
|
15
|
+
density,
|
|
16
|
+
visibility,
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
viewportInfo: input.viewportInfo,
|
|
20
|
+
graphLayout,
|
|
21
|
+
density,
|
|
22
|
+
visibility,
|
|
23
|
+
componentLayout,
|
|
24
|
+
};
|
|
25
|
+
}
|
package/dist/renderer.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { resolveTheme } from "./themes/defaultTheme";
|
|
2
|
+
import { runRenderPipeline } from "./pipeline";
|
|
3
|
+
import { defaultGraphLayoutEngine } from "./engines/graphLayoutEngine";
|
|
4
|
+
import { defaultDensityEngine } from "./engines/densityEngine";
|
|
5
|
+
import { defaultVisibilityEngine } from "./engines/visibilityEngine";
|
|
6
|
+
import { defaultComponentLayoutEngine } from "./engines/componentLayoutEngine";
|
|
7
|
+
import { domDrawEngine } from "./engines/drawEngine";
|
|
8
|
+
import { debugDrawEngine } from "./engines/debugDrawEngine";
|
|
9
|
+
const DEFAULT_CAMERA = {
|
|
10
|
+
x: 0,
|
|
11
|
+
y: 0,
|
|
12
|
+
zoom: 1,
|
|
13
|
+
};
|
|
14
|
+
function ensureHostBaseStyle(host) {
|
|
15
|
+
host.style.position = "relative";
|
|
16
|
+
host.style.overflow = "hidden";
|
|
17
|
+
}
|
|
18
|
+
function getHostViewport(host) {
|
|
19
|
+
return {
|
|
20
|
+
x: 0,
|
|
21
|
+
y: 0,
|
|
22
|
+
width: host.clientWidth,
|
|
23
|
+
height: host.clientHeight,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function getEffectiveViewport(hostViewport, viewport) {
|
|
27
|
+
if (viewport?.enabled) {
|
|
28
|
+
const offsetX = viewport.offsetX ?? 0;
|
|
29
|
+
const offsetY = viewport.offsetY ?? 0;
|
|
30
|
+
return {
|
|
31
|
+
x: offsetX,
|
|
32
|
+
y: offsetY,
|
|
33
|
+
width: viewport.width,
|
|
34
|
+
height: viewport.height,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
x: 0,
|
|
39
|
+
y: 0,
|
|
40
|
+
width: hostViewport.width,
|
|
41
|
+
height: hostViewport.height,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function getWorldViewport(camera, effectiveViewport) {
|
|
45
|
+
return {
|
|
46
|
+
x: (effectiveViewport.x - camera.x) / camera.zoom,
|
|
47
|
+
y: (effectiveViewport.y - camera.y) / camera.zoom,
|
|
48
|
+
width: effectiveViewport.width / camera.zoom,
|
|
49
|
+
height: effectiveViewport.height / camera.zoom,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function resolveViewportInfo(host, camera, input) {
|
|
53
|
+
const hostViewport = getHostViewport(host);
|
|
54
|
+
const effectiveViewport = getEffectiveViewport(hostViewport, input.viewport);
|
|
55
|
+
const worldViewport = getWorldViewport(camera, effectiveViewport);
|
|
56
|
+
return {
|
|
57
|
+
host: hostViewport,
|
|
58
|
+
effective: effectiveViewport,
|
|
59
|
+
world: worldViewport,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function createRenderer() {
|
|
63
|
+
let host = null;
|
|
64
|
+
return {
|
|
65
|
+
mount(container) {
|
|
66
|
+
host = container;
|
|
67
|
+
ensureHostBaseStyle(host);
|
|
68
|
+
},
|
|
69
|
+
update(input) {
|
|
70
|
+
if (!host)
|
|
71
|
+
return;
|
|
72
|
+
const { model, layoutModel, theme, textScale = "md", camera = DEFAULT_CAMERA, graphLayoutEngine = defaultGraphLayoutEngine, densityEngine = defaultDensityEngine, visibilityEngine = defaultVisibilityEngine, componentLayoutEngine = defaultComponentLayoutEngine, drawEngine = domDrawEngine, zoneComponentRenderers, pathComponentRenderers, interactionHandlers, exclusionState, debug, } = input;
|
|
73
|
+
const mergedTheme = resolveTheme(theme);
|
|
74
|
+
const viewportInfo = resolveViewportInfo(host, camera, input);
|
|
75
|
+
const pipeline = runRenderPipeline({
|
|
76
|
+
model,
|
|
77
|
+
layoutModel,
|
|
78
|
+
camera,
|
|
79
|
+
viewportInfo,
|
|
80
|
+
theme: mergedTheme,
|
|
81
|
+
textScale,
|
|
82
|
+
}, {
|
|
83
|
+
graphLayoutEngine,
|
|
84
|
+
densityEngine,
|
|
85
|
+
visibilityEngine,
|
|
86
|
+
componentLayoutEngine,
|
|
87
|
+
});
|
|
88
|
+
if (debug?.enabled) {
|
|
89
|
+
debugDrawEngine.draw({
|
|
90
|
+
host,
|
|
91
|
+
model,
|
|
92
|
+
layoutModel,
|
|
93
|
+
camera,
|
|
94
|
+
viewportInfo,
|
|
95
|
+
theme: mergedTheme,
|
|
96
|
+
textScale,
|
|
97
|
+
pipeline,
|
|
98
|
+
exclusionState,
|
|
99
|
+
layers: debug.layers ?? ["graph-layout", "edges", "anchors"],
|
|
100
|
+
});
|
|
101
|
+
return {
|
|
102
|
+
viewportInfo,
|
|
103
|
+
pipeline,
|
|
104
|
+
mounts: {
|
|
105
|
+
zones: [],
|
|
106
|
+
paths: [],
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const mounts = drawEngine.draw({
|
|
111
|
+
host,
|
|
112
|
+
model,
|
|
113
|
+
layoutModel,
|
|
114
|
+
camera,
|
|
115
|
+
viewportInfo,
|
|
116
|
+
theme: mergedTheme,
|
|
117
|
+
textScale,
|
|
118
|
+
pipeline,
|
|
119
|
+
zoneComponentRenderers,
|
|
120
|
+
pathComponentRenderers,
|
|
121
|
+
interactionHandlers,
|
|
122
|
+
exclusionState,
|
|
123
|
+
});
|
|
124
|
+
return {
|
|
125
|
+
viewportInfo,
|
|
126
|
+
pipeline,
|
|
127
|
+
mounts,
|
|
128
|
+
};
|
|
129
|
+
},
|
|
130
|
+
destroy() {
|
|
131
|
+
if (host) {
|
|
132
|
+
host.innerHTML = "";
|
|
133
|
+
}
|
|
134
|
+
host = null;
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type ZoneflowTheme = {
|
|
2
|
+
background: string;
|
|
3
|
+
zoneTitle: string;
|
|
4
|
+
zoneSubtext: string;
|
|
5
|
+
zoneContainerBorder: string;
|
|
6
|
+
zoneActionBorder: string;
|
|
7
|
+
zoneBadgeBg: string;
|
|
8
|
+
pathLabel: string;
|
|
9
|
+
pathEdge: string;
|
|
10
|
+
selection: string;
|
|
11
|
+
density: {
|
|
12
|
+
zone: {
|
|
13
|
+
detail: number;
|
|
14
|
+
near: number;
|
|
15
|
+
mid: number;
|
|
16
|
+
};
|
|
17
|
+
path: {
|
|
18
|
+
full: number;
|
|
19
|
+
chip: number;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export type TextScaleLevel = "sm" | "md" | "lg";
|
package/dist/theme.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ZoneflowTheme } from "../theme";
|
|
2
|
+
/**
|
|
3
|
+
* 기본 테마 (모든 필수 값 포함)
|
|
4
|
+
*/
|
|
5
|
+
export declare const defaultTheme: ZoneflowTheme;
|
|
6
|
+
/**
|
|
7
|
+
* Partial theme을 받아서 완전한 theme으로 보정
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveTheme(theme?: Partial<ZoneflowTheme>): ZoneflowTheme;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 기본 테마 (모든 필수 값 포함)
|
|
3
|
+
*/
|
|
4
|
+
export const defaultTheme = {
|
|
5
|
+
background: "#f3f6fb",
|
|
6
|
+
zoneTitle: "#0f172a",
|
|
7
|
+
zoneSubtext: "#5f6f86",
|
|
8
|
+
zoneContainerBorder: "#cbd5e1",
|
|
9
|
+
zoneActionBorder: "#f59e0b",
|
|
10
|
+
zoneBadgeBg: "#e0f2fe",
|
|
11
|
+
pathLabel: "#1e293b",
|
|
12
|
+
pathEdge: "#7a8aa0",
|
|
13
|
+
selection: "#2e90fa",
|
|
14
|
+
density: {
|
|
15
|
+
zone: {
|
|
16
|
+
detail: 200,
|
|
17
|
+
near: 140,
|
|
18
|
+
mid: 90,
|
|
19
|
+
},
|
|
20
|
+
path: {
|
|
21
|
+
full: 120,
|
|
22
|
+
chip: 60,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Partial theme을 받아서 완전한 theme으로 보정
|
|
28
|
+
*/
|
|
29
|
+
export function resolveTheme(theme) {
|
|
30
|
+
if (!theme)
|
|
31
|
+
return defaultTheme;
|
|
32
|
+
return {
|
|
33
|
+
...defaultTheme,
|
|
34
|
+
...theme,
|
|
35
|
+
density: {
|
|
36
|
+
zone: {
|
|
37
|
+
...defaultTheme.density.zone,
|
|
38
|
+
...theme.density?.zone,
|
|
39
|
+
},
|
|
40
|
+
path: {
|
|
41
|
+
...defaultTheme.density.path,
|
|
42
|
+
...theme.density?.path,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|