@zoneflow/renderer-dom 0.0.2 → 0.0.4
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 +29 -0
- package/dist/engines/drawEngine.js +100 -22
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/theme.d.ts +35 -0
- package/dist/themes/defaultTheme.js +67 -0
- package/package.json +4 -2
package/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# @zoneflow/renderer-dom
|
|
2
|
+
|
|
3
|
+
`@zoneflow/renderer-dom`은 Zoneflow의 저수준 DOM 렌더러 엔진 패키지입니다.
|
|
4
|
+
|
|
5
|
+
이 패키지는 주로 다음을 포함합니다.
|
|
6
|
+
|
|
7
|
+
- 그래프 레이아웃 파이프라인
|
|
8
|
+
- DOM draw engine
|
|
9
|
+
- renderer frame / mount registry
|
|
10
|
+
- renderer용 타입 정의
|
|
11
|
+
|
|
12
|
+
대부분의 앱에서는 이 패키지를 직접 사용할 필요가 없습니다.
|
|
13
|
+
일반적인 React 앱은 `@zoneflow/react`를 사용하면 충분합니다.
|
|
14
|
+
|
|
15
|
+
## 설치
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @zoneflow/renderer-dom
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 언제 직접 쓰나
|
|
22
|
+
|
|
23
|
+
- React 없이 renderer DOM 레이어를 직접 붙일 때
|
|
24
|
+
- low-level draw engine이나 pipeline을 교체/실험할 때
|
|
25
|
+
- renderer frame을 직접 소비하는 커스텀 환경을 만들 때
|
|
26
|
+
|
|
27
|
+
일반적인 앱 통합은 `@zoneflow/react`를 우선 사용하는 편이 맞습니다.
|
|
28
|
+
|
|
29
|
+
레포지토리: [github.com/groobee/zoneflow](https://github.com/groobee/zoneflow)
|
|
@@ -36,6 +36,56 @@ function resolvePathDisplayName(params) {
|
|
|
36
36
|
return trimmed;
|
|
37
37
|
return params.rule === null ? "Empty" : "Untitled";
|
|
38
38
|
}
|
|
39
|
+
function resolvePathTargetDisplay(params) {
|
|
40
|
+
const targetZoneId = params.pathVisual.targetZoneId;
|
|
41
|
+
if (!targetZoneId) {
|
|
42
|
+
return {
|
|
43
|
+
label: "—",
|
|
44
|
+
status: "unconfigured",
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const targetZone = params.model.zonesById[targetZoneId];
|
|
48
|
+
if (!targetZone) {
|
|
49
|
+
return {
|
|
50
|
+
label: "—",
|
|
51
|
+
status: "missing",
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
label: targetZone.name,
|
|
56
|
+
status: "resolved",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function createPathStatusBadge(params) {
|
|
60
|
+
const { owner, status, theme } = params;
|
|
61
|
+
const badge = document.createElement("div");
|
|
62
|
+
const tone = status === "missing" ? theme.status.warning : theme.status.info;
|
|
63
|
+
const isMissing = status === "missing";
|
|
64
|
+
badge.title = isMissing ? "Broken path target" : "Path target not set";
|
|
65
|
+
badge.setAttribute("aria-label", isMissing ? "Broken path target" : "Path target not set");
|
|
66
|
+
badge.textContent = isMissing ? "⚠" : "?";
|
|
67
|
+
applyStyles(badge, {
|
|
68
|
+
position: "absolute",
|
|
69
|
+
right: "10px",
|
|
70
|
+
top: "10px",
|
|
71
|
+
width: "22px",
|
|
72
|
+
height: "22px",
|
|
73
|
+
display: "flex",
|
|
74
|
+
alignItems: "center",
|
|
75
|
+
justifyContent: "center",
|
|
76
|
+
borderRadius: "999px",
|
|
77
|
+
border: tone.border,
|
|
78
|
+
background: tone.background,
|
|
79
|
+
color: tone.color,
|
|
80
|
+
boxShadow: tone.shadow,
|
|
81
|
+
fontSize: "12px",
|
|
82
|
+
lineHeight: "1",
|
|
83
|
+
fontWeight: "700",
|
|
84
|
+
pointerEvents: "none",
|
|
85
|
+
zIndex: 2,
|
|
86
|
+
});
|
|
87
|
+
owner.appendChild(badge);
|
|
88
|
+
}
|
|
39
89
|
function getOpacity(emphasis) {
|
|
40
90
|
switch (emphasis) {
|
|
41
91
|
case "strong":
|
|
@@ -52,8 +102,10 @@ function getOpacity(emphasis) {
|
|
|
52
102
|
function createSvgElement(tag) {
|
|
53
103
|
return document.createElementNS("http://www.w3.org/2000/svg", tag);
|
|
54
104
|
}
|
|
55
|
-
function getEdgeColor(
|
|
56
|
-
return kind === "zone-to-path"
|
|
105
|
+
function getEdgeColor(params) {
|
|
106
|
+
return params.kind === "zone-to-path"
|
|
107
|
+
? params.theme.pathEdge
|
|
108
|
+
: params.theme.pathInboundEdge;
|
|
57
109
|
}
|
|
58
110
|
function getBezierCurvePathD(params) {
|
|
59
111
|
const { source, target } = params;
|
|
@@ -190,10 +242,11 @@ function renderPathFallback(host, slot, context) {
|
|
|
190
242
|
fontFamily: "'IBM Plex Sans', 'Pretendard', sans-serif",
|
|
191
243
|
};
|
|
192
244
|
if (slot === "label") {
|
|
193
|
-
|
|
245
|
+
const title = resolvePathDisplayName({
|
|
194
246
|
name: context.path.name,
|
|
195
247
|
rule: context.path.rule,
|
|
196
248
|
});
|
|
249
|
+
host.textContent = title;
|
|
197
250
|
applyStyles(host, {
|
|
198
251
|
...base,
|
|
199
252
|
fontSize: "12px",
|
|
@@ -213,11 +266,20 @@ function renderPathFallback(host, slot, context) {
|
|
|
213
266
|
return;
|
|
214
267
|
}
|
|
215
268
|
if (slot === "target") {
|
|
216
|
-
|
|
269
|
+
const targetDisplay = resolvePathTargetDisplay({
|
|
270
|
+
model: context.model,
|
|
271
|
+
pathVisual: context.pathVisual,
|
|
272
|
+
});
|
|
273
|
+
host.textContent = targetDisplay.label;
|
|
217
274
|
applyStyles(host, {
|
|
218
275
|
...base,
|
|
219
|
-
color:
|
|
276
|
+
color: targetDisplay.status === "missing"
|
|
277
|
+
? context.theme.status.warning.color
|
|
278
|
+
: targetDisplay.status === "unconfigured"
|
|
279
|
+
? context.theme.status.info.color
|
|
280
|
+
: context.theme.zoneSubtext,
|
|
220
281
|
fontSize: "11px",
|
|
282
|
+
fontWeight: targetDisplay.status === "resolved" ? 500 : 700,
|
|
221
283
|
});
|
|
222
284
|
return;
|
|
223
285
|
}
|
|
@@ -347,7 +409,10 @@ function drawEdges(params) {
|
|
|
347
409
|
if (!visibility?.shouldRenderEdge)
|
|
348
410
|
continue;
|
|
349
411
|
for (const edge of edges) {
|
|
350
|
-
const stroke = getEdgeColor(
|
|
412
|
+
const stroke = getEdgeColor({
|
|
413
|
+
kind: edge.kind,
|
|
414
|
+
theme: input.theme,
|
|
415
|
+
});
|
|
351
416
|
const path = createSvgElement("path");
|
|
352
417
|
path.setAttribute("d", getBezierCurvePathD({
|
|
353
418
|
source: edge.source,
|
|
@@ -376,7 +441,7 @@ function drawEdges(params) {
|
|
|
376
441
|
}
|
|
377
442
|
}
|
|
378
443
|
function createSurfaceChrome(params) {
|
|
379
|
-
const { owner, accent, radius, topBandOpacity = 0.64 } = params;
|
|
444
|
+
const { owner, accent, radius, theme, topBandOpacity = 0.64 } = params;
|
|
380
445
|
const chrome = document.createElement("div");
|
|
381
446
|
const topBand = document.createElement("div");
|
|
382
447
|
const cornerGlow = document.createElement("div");
|
|
@@ -385,7 +450,7 @@ function createSurfaceChrome(params) {
|
|
|
385
450
|
inset: "0",
|
|
386
451
|
borderRadius: radius,
|
|
387
452
|
pointerEvents: "none",
|
|
388
|
-
background:
|
|
453
|
+
background: theme.surface.chrome.overlay,
|
|
389
454
|
});
|
|
390
455
|
applyStyles(topBand, {
|
|
391
456
|
position: "absolute",
|
|
@@ -395,7 +460,7 @@ function createSurfaceChrome(params) {
|
|
|
395
460
|
height: "44px",
|
|
396
461
|
borderTopLeftRadius: radius,
|
|
397
462
|
borderTopRightRadius: radius,
|
|
398
|
-
background: `linear-gradient(90deg, ${accent} 0%,
|
|
463
|
+
background: `linear-gradient(90deg, ${accent} 0%, ${theme.surface.chrome.accentFade} 72%)`,
|
|
399
464
|
opacity: topBandOpacity,
|
|
400
465
|
pointerEvents: "none",
|
|
401
466
|
});
|
|
@@ -406,7 +471,7 @@ function createSurfaceChrome(params) {
|
|
|
406
471
|
width: "116px",
|
|
407
472
|
height: "116px",
|
|
408
473
|
borderRadius: "999px",
|
|
409
|
-
background:
|
|
474
|
+
background: theme.surface.chrome.glow,
|
|
410
475
|
pointerEvents: "none",
|
|
411
476
|
});
|
|
412
477
|
chrome.appendChild(topBand);
|
|
@@ -419,8 +484,8 @@ function drawZoneAnchors(params) {
|
|
|
419
484
|
? input.theme.zoneActionBorder
|
|
420
485
|
: input.theme.zoneContainerBorder;
|
|
421
486
|
const anchorAccentColor = zone.zone.zoneType === "action"
|
|
422
|
-
?
|
|
423
|
-
:
|
|
487
|
+
? input.theme.surface.anchor.actionAccent
|
|
488
|
+
: input.theme.surface.anchor.containerAccent;
|
|
424
489
|
const shouldRenderAnchor = (kind) => kind === "inlet"
|
|
425
490
|
? isZoneInputEnabled(zone.zone)
|
|
426
491
|
: isZoneOutputEnabled(zone.zone);
|
|
@@ -443,11 +508,11 @@ function drawZoneAnchors(params) {
|
|
|
443
508
|
width: `${rect.width}px`,
|
|
444
509
|
height: `${rect.height}px`,
|
|
445
510
|
borderRadius: "0",
|
|
446
|
-
background:
|
|
511
|
+
background: input.theme.surface.anchor.background,
|
|
447
512
|
border: `1px solid ${zoneBorderColor}`,
|
|
448
513
|
borderRight: kind === "inlet" ? "none" : `1px solid ${zoneBorderColor}`,
|
|
449
514
|
borderLeft: kind === "outlet" ? "none" : `1px solid ${zoneBorderColor}`,
|
|
450
|
-
boxShadow:
|
|
515
|
+
boxShadow: input.theme.surface.anchor.shadow,
|
|
451
516
|
boxSizing: "border-box",
|
|
452
517
|
overflow: "hidden",
|
|
453
518
|
pointerEvents: "none",
|
|
@@ -457,7 +522,7 @@ function drawZoneAnchors(params) {
|
|
|
457
522
|
top: "0",
|
|
458
523
|
bottom: "0",
|
|
459
524
|
width: "10px",
|
|
460
|
-
background:
|
|
525
|
+
background: input.theme.surface.anchor.background,
|
|
461
526
|
right: kind === "inlet" ? "0" : "auto",
|
|
462
527
|
left: kind === "outlet" ? "0" : "auto",
|
|
463
528
|
});
|
|
@@ -592,9 +657,9 @@ export const domDrawEngine = {
|
|
|
592
657
|
border: `1px solid ${zoneVisual.zone.zoneType === "action"
|
|
593
658
|
? theme.zoneActionBorder
|
|
594
659
|
: theme.zoneContainerBorder}`,
|
|
595
|
-
background:
|
|
660
|
+
background: theme.surface.zone.background,
|
|
596
661
|
boxSizing: "border-box",
|
|
597
|
-
boxShadow:
|
|
662
|
+
boxShadow: theme.surface.zone.shadow,
|
|
598
663
|
overflow: "hidden",
|
|
599
664
|
});
|
|
600
665
|
zoneEl.addEventListener("click", (event) => {
|
|
@@ -604,9 +669,10 @@ export const domDrawEngine = {
|
|
|
604
669
|
createSurfaceChrome({
|
|
605
670
|
owner: zoneChromeEl,
|
|
606
671
|
accent: zoneVisual.zone.zoneType === "action"
|
|
607
|
-
?
|
|
608
|
-
:
|
|
672
|
+
? theme.surface.zone.actionAccent
|
|
673
|
+
: theme.surface.zone.containerAccent,
|
|
609
674
|
radius: "0",
|
|
675
|
+
theme,
|
|
610
676
|
});
|
|
611
677
|
zoneBodyEl.appendChild(zoneChromeEl);
|
|
612
678
|
zoneEl.appendChild(zoneBodyEl);
|
|
@@ -646,9 +712,9 @@ export const domDrawEngine = {
|
|
|
646
712
|
height: `${pathVisual.rect.height}px`,
|
|
647
713
|
borderRadius: "18px",
|
|
648
714
|
border: `1px solid ${theme.pathEdge}`,
|
|
649
|
-
background:
|
|
715
|
+
background: theme.surface.path.background,
|
|
650
716
|
boxSizing: "border-box",
|
|
651
|
-
boxShadow:
|
|
717
|
+
boxShadow: theme.surface.path.shadow,
|
|
652
718
|
opacity: getOpacity(visibility.emphasis),
|
|
653
719
|
zIndex: 1,
|
|
654
720
|
overflow: "hidden",
|
|
@@ -659,11 +725,23 @@ export const domDrawEngine = {
|
|
|
659
725
|
});
|
|
660
726
|
createSurfaceChrome({
|
|
661
727
|
owner: pathChromeEl,
|
|
662
|
-
accent:
|
|
728
|
+
accent: theme.surface.path.accent,
|
|
663
729
|
radius: "18px",
|
|
730
|
+
theme,
|
|
664
731
|
topBandOpacity: 0.72,
|
|
665
732
|
});
|
|
666
733
|
pathEl.appendChild(pathChromeEl);
|
|
734
|
+
const targetDisplay = resolvePathTargetDisplay({
|
|
735
|
+
model: input.model,
|
|
736
|
+
pathVisual,
|
|
737
|
+
});
|
|
738
|
+
if (targetDisplay.status !== "resolved") {
|
|
739
|
+
createPathStatusBadge({
|
|
740
|
+
owner: pathEl,
|
|
741
|
+
status: targetDisplay.status,
|
|
742
|
+
theme,
|
|
743
|
+
});
|
|
744
|
+
}
|
|
667
745
|
for (const slot of Object.keys(componentLayout?.slots ?? {})) {
|
|
668
746
|
createPathSlotHost({
|
|
669
747
|
pathVisual,
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/theme.d.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
export type ZoneflowStatusTone = {
|
|
2
|
+
border: string;
|
|
3
|
+
background: string;
|
|
4
|
+
color: string;
|
|
5
|
+
shadow: string;
|
|
6
|
+
};
|
|
1
7
|
export type ZoneflowTheme = {
|
|
2
8
|
background: string;
|
|
3
9
|
zoneTitle: string;
|
|
@@ -7,7 +13,36 @@ export type ZoneflowTheme = {
|
|
|
7
13
|
zoneBadgeBg: string;
|
|
8
14
|
pathLabel: string;
|
|
9
15
|
pathEdge: string;
|
|
16
|
+
pathInboundEdge: string;
|
|
10
17
|
selection: string;
|
|
18
|
+
surface: {
|
|
19
|
+
chrome: {
|
|
20
|
+
overlay: string;
|
|
21
|
+
glow: string;
|
|
22
|
+
accentFade: string;
|
|
23
|
+
};
|
|
24
|
+
zone: {
|
|
25
|
+
background: string;
|
|
26
|
+
shadow: string;
|
|
27
|
+
containerAccent: string;
|
|
28
|
+
actionAccent: string;
|
|
29
|
+
};
|
|
30
|
+
path: {
|
|
31
|
+
background: string;
|
|
32
|
+
shadow: string;
|
|
33
|
+
accent: string;
|
|
34
|
+
};
|
|
35
|
+
anchor: {
|
|
36
|
+
background: string;
|
|
37
|
+
shadow: string;
|
|
38
|
+
containerAccent: string;
|
|
39
|
+
actionAccent: string;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
status: {
|
|
43
|
+
info: ZoneflowStatusTone;
|
|
44
|
+
warning: ZoneflowStatusTone;
|
|
45
|
+
};
|
|
11
46
|
density: {
|
|
12
47
|
zone: {
|
|
13
48
|
detail: number;
|
|
@@ -10,7 +10,46 @@ export const defaultTheme = {
|
|
|
10
10
|
zoneBadgeBg: "#e0f2fe",
|
|
11
11
|
pathLabel: "#1e293b",
|
|
12
12
|
pathEdge: "#7a8aa0",
|
|
13
|
+
pathInboundEdge: "#0f766e",
|
|
13
14
|
selection: "#2e90fa",
|
|
15
|
+
surface: {
|
|
16
|
+
chrome: {
|
|
17
|
+
overlay: "linear-gradient(180deg, rgba(255,255,255,0.74) 0%, rgba(255,255,255,0.08) 42%, rgba(255,255,255,0) 100%)",
|
|
18
|
+
glow: "radial-gradient(circle, rgba(255,255,255,0.92) 0%, rgba(255,255,255,0.22) 36%, rgba(255,255,255,0) 72%)",
|
|
19
|
+
accentFade: "rgba(255,255,255,0.04)",
|
|
20
|
+
},
|
|
21
|
+
zone: {
|
|
22
|
+
background: "linear-gradient(180deg, rgba(255,255,255,0.99) 0%, rgba(248,250,252,0.98) 100%)",
|
|
23
|
+
shadow: "0 18px 34px rgba(15, 23, 42, 0.08), 0 3px 8px rgba(15, 23, 42, 0.05)",
|
|
24
|
+
containerAccent: "rgba(37, 99, 235, 0.12)",
|
|
25
|
+
actionAccent: "rgba(245, 158, 11, 0.18)",
|
|
26
|
+
},
|
|
27
|
+
path: {
|
|
28
|
+
background: "linear-gradient(180deg, rgba(255,255,255,0.99) 0%, rgba(246,248,252,0.98) 100%)",
|
|
29
|
+
shadow: "0 16px 26px rgba(15, 23, 42, 0.08), 0 3px 8px rgba(15, 23, 42, 0.05)",
|
|
30
|
+
accent: "rgba(56, 189, 248, 0.16)",
|
|
31
|
+
},
|
|
32
|
+
anchor: {
|
|
33
|
+
background: "linear-gradient(180deg, rgba(255,255,255,0.99) 0%, rgba(247,250,253,0.98) 100%)",
|
|
34
|
+
shadow: "0 18px 28px rgba(15, 23, 42, 0.08), inset 0 1px 0 rgba(255,255,255,0.9)",
|
|
35
|
+
containerAccent: "rgba(37, 99, 235, 0.96)",
|
|
36
|
+
actionAccent: "rgba(245, 158, 11, 0.96)",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
status: {
|
|
40
|
+
info: {
|
|
41
|
+
border: "1px solid rgba(217, 119, 6, 0.24)",
|
|
42
|
+
background: "linear-gradient(180deg, rgba(255,251,235,0.98) 0%, rgba(254,243,199,0.98) 100%)",
|
|
43
|
+
color: "#b45309",
|
|
44
|
+
shadow: "0 6px 14px rgba(180, 83, 9, 0.16)",
|
|
45
|
+
},
|
|
46
|
+
warning: {
|
|
47
|
+
border: "1px solid rgba(217, 119, 6, 0.24)",
|
|
48
|
+
background: "linear-gradient(180deg, rgba(255,251,235,0.98) 0%, rgba(254,243,199,0.98) 100%)",
|
|
49
|
+
color: "#b45309",
|
|
50
|
+
shadow: "0 6px 14px rgba(180, 83, 9, 0.16)",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
14
53
|
density: {
|
|
15
54
|
zone: {
|
|
16
55
|
detail: 200,
|
|
@@ -32,6 +71,34 @@ export function resolveTheme(theme) {
|
|
|
32
71
|
return {
|
|
33
72
|
...defaultTheme,
|
|
34
73
|
...theme,
|
|
74
|
+
surface: {
|
|
75
|
+
chrome: {
|
|
76
|
+
...defaultTheme.surface.chrome,
|
|
77
|
+
...theme.surface?.chrome,
|
|
78
|
+
},
|
|
79
|
+
zone: {
|
|
80
|
+
...defaultTheme.surface.zone,
|
|
81
|
+
...theme.surface?.zone,
|
|
82
|
+
},
|
|
83
|
+
path: {
|
|
84
|
+
...defaultTheme.surface.path,
|
|
85
|
+
...theme.surface?.path,
|
|
86
|
+
},
|
|
87
|
+
anchor: {
|
|
88
|
+
...defaultTheme.surface.anchor,
|
|
89
|
+
...theme.surface?.anchor,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
status: {
|
|
93
|
+
info: {
|
|
94
|
+
...defaultTheme.status.info,
|
|
95
|
+
...theme.status?.info,
|
|
96
|
+
},
|
|
97
|
+
warning: {
|
|
98
|
+
...defaultTheme.status.warning,
|
|
99
|
+
...theme.status?.warning,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
35
102
|
density: {
|
|
36
103
|
zone: {
|
|
37
104
|
...defaultTheme.density.zone,
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zoneflow/renderer-dom",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
|
+
"description": "Low-level DOM renderer engines for Zoneflow.",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"homepage": "https://github.com/groobee/zoneflow",
|
|
7
9
|
"repository": {
|
|
8
10
|
"type": "git",
|
|
9
11
|
"url": "https://github.com/groobee/zoneflow.git",
|
|
@@ -16,7 +18,7 @@
|
|
|
16
18
|
"dist"
|
|
17
19
|
],
|
|
18
20
|
"dependencies": {
|
|
19
|
-
"@zoneflow/core": "0.0.
|
|
21
|
+
"@zoneflow/core": "0.0.4"
|
|
20
22
|
},
|
|
21
23
|
"scripts": {
|
|
22
24
|
"build": "tsc -p tsconfig.json",
|