@zoneflow/renderer-dom 0.0.15 → 0.0.16
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/engines/drawEngine.js +175 -59
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/renderer.js +3 -1
- package/dist/types.d.ts +5 -0
- package/dist/zoneShape.d.ts +62 -0
- package/dist/zoneShape.js +39 -0
- package/package.json +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { resolveZoneAnchorRect } from "../anchors";
|
|
2
|
+
import { normalizeZoneShape } from "../zoneShape";
|
|
2
3
|
import { getZoneDepth, isZoneInputEnabled, isZoneOutputEnabled, } from "@zoneflow/core";
|
|
3
4
|
import { appendEdgeFlowStyle, resolveCollapsedEdgeStroke, resolveDrawableEdgeSegments, resolveEdgeFlowMotion, } from "./edgeFlow";
|
|
4
5
|
const SCENE_PADDING = 64;
|
|
@@ -552,11 +553,13 @@ function drawEdges(params) {
|
|
|
552
553
|
}
|
|
553
554
|
}
|
|
554
555
|
}
|
|
556
|
+
// Depth for clipped shapes: box-shadow is cut away by clip-path, so a
|
|
557
|
+
// polygon-following drop-shadow filter is used instead. Tuned to match the
|
|
558
|
+
// zone surface box-shadow tokens (rgba(15, 23, 42, …)).
|
|
559
|
+
const ZONE_CLIP_SHADOW = "drop-shadow(0 14px 22px rgba(15, 23, 42, 0.12)) drop-shadow(0 3px 6px rgba(15, 23, 42, 0.08))";
|
|
555
560
|
function createSurfaceChrome(params) {
|
|
556
|
-
const { owner, accent, radius, theme, topBandOpacity = 0.64 } = params;
|
|
561
|
+
const { owner, accent, radius, theme, header = true, topBandOpacity = 0.64 } = params;
|
|
557
562
|
const chrome = document.createElement("div");
|
|
558
|
-
const topBand = document.createElement("div");
|
|
559
|
-
const cornerGlow = document.createElement("div");
|
|
560
563
|
applyStyles(chrome, {
|
|
561
564
|
position: "absolute",
|
|
562
565
|
inset: "0",
|
|
@@ -564,43 +567,108 @@ function createSurfaceChrome(params) {
|
|
|
564
567
|
pointerEvents: "none",
|
|
565
568
|
background: theme.surface.chrome.overlay,
|
|
566
569
|
});
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
570
|
+
if (header) {
|
|
571
|
+
const topBand = document.createElement("div");
|
|
572
|
+
const cornerGlow = document.createElement("div");
|
|
573
|
+
applyStyles(topBand, {
|
|
574
|
+
position: "absolute",
|
|
575
|
+
left: "0",
|
|
576
|
+
top: "0",
|
|
577
|
+
right: "0",
|
|
578
|
+
height: "44px",
|
|
579
|
+
borderTopLeftRadius: radius,
|
|
580
|
+
borderTopRightRadius: radius,
|
|
581
|
+
background: `linear-gradient(90deg, ${accent} 0%, ${theme.surface.chrome.accentFade} 72%)`,
|
|
582
|
+
opacity: topBandOpacity,
|
|
583
|
+
pointerEvents: "none",
|
|
584
|
+
});
|
|
585
|
+
applyStyles(cornerGlow, {
|
|
586
|
+
position: "absolute",
|
|
587
|
+
right: "-20px",
|
|
588
|
+
top: "-24px",
|
|
589
|
+
width: "116px",
|
|
590
|
+
height: "116px",
|
|
591
|
+
borderRadius: "999px",
|
|
592
|
+
background: theme.surface.chrome.glow,
|
|
593
|
+
pointerEvents: "none",
|
|
594
|
+
});
|
|
595
|
+
chrome.appendChild(topBand);
|
|
596
|
+
chrome.appendChild(cornerGlow);
|
|
597
|
+
}
|
|
598
|
+
else {
|
|
599
|
+
// Header-less shapes (circle/pill/diamond/…) keep their accent identity
|
|
600
|
+
// via a soft top-centered wash instead of the rectangular band.
|
|
601
|
+
const accentWash = document.createElement("div");
|
|
602
|
+
applyStyles(accentWash, {
|
|
603
|
+
position: "absolute",
|
|
604
|
+
inset: "0",
|
|
605
|
+
borderRadius: radius,
|
|
606
|
+
background: `radial-gradient(135% 100% at 50% 0%, ${accent} 0%, ${theme.surface.chrome.accentFade} 70%)`,
|
|
607
|
+
opacity: 0.85,
|
|
608
|
+
pointerEvents: "none",
|
|
609
|
+
});
|
|
610
|
+
chrome.appendChild(accentWash);
|
|
611
|
+
}
|
|
591
612
|
owner.appendChild(chrome);
|
|
592
613
|
}
|
|
593
614
|
function drawZoneAnchors(params) {
|
|
594
|
-
const { owner, zone, input } = params;
|
|
595
|
-
const
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
615
|
+
const { owner, zone, input, mode = "edge" } = params;
|
|
616
|
+
const zoneColor = input.resolveZoneColor?.(zone.zone) ?? undefined;
|
|
617
|
+
const zoneBorderColor = zoneColor ??
|
|
618
|
+
(zone.zone.zoneType === "action"
|
|
619
|
+
? input.theme.zoneActionBorder
|
|
620
|
+
: input.theme.zoneContainerBorder);
|
|
621
|
+
const anchorAccentColor = zoneColor ??
|
|
622
|
+
(zone.zone.zoneType === "action"
|
|
623
|
+
? input.theme.surface.anchor.actionAccent
|
|
624
|
+
: input.theme.surface.anchor.containerAccent);
|
|
625
|
+
const anchorGlowColor = zoneColor
|
|
626
|
+
? `color-mix(in srgb, ${zoneColor} 12%, transparent)`
|
|
627
|
+
: anchorAccentColor.replace("0.96", "0.12");
|
|
601
628
|
const shouldRenderAnchor = (kind) => kind === "inlet"
|
|
602
629
|
? isZoneInputEnabled(zone.zone)
|
|
603
630
|
: isZoneOutputEnabled(zone.zone);
|
|
631
|
+
// Vertex mode: a compact dot centered on the left/right edge midpoint,
|
|
632
|
+
// sitting exactly on a round/diamond node's side. The interactive anchor
|
|
633
|
+
// geometry is unchanged — this only swaps the visual indicator.
|
|
634
|
+
if (mode === "vertex") {
|
|
635
|
+
const dotSize = 14;
|
|
636
|
+
for (const kind of ["inlet", "outlet"]) {
|
|
637
|
+
if (!shouldRenderAnchor(kind))
|
|
638
|
+
continue;
|
|
639
|
+
const dot = document.createElement("div");
|
|
640
|
+
const accentDot = document.createElement("div");
|
|
641
|
+
applyStyles(dot, {
|
|
642
|
+
position: "absolute",
|
|
643
|
+
top: "50%",
|
|
644
|
+
left: kind === "inlet" ? "0" : "auto",
|
|
645
|
+
right: kind === "outlet" ? "0" : "auto",
|
|
646
|
+
width: `${dotSize}px`,
|
|
647
|
+
height: `${dotSize}px`,
|
|
648
|
+
transform: kind === "inlet"
|
|
649
|
+
? "translate(-50%, -50%)"
|
|
650
|
+
: "translate(50%, -50%)",
|
|
651
|
+
borderRadius: "999px",
|
|
652
|
+
background: input.theme.surface.anchor.background,
|
|
653
|
+
border: `1px solid ${zoneBorderColor}`,
|
|
654
|
+
boxShadow: input.theme.surface.anchor.shadow,
|
|
655
|
+
boxSizing: "border-box",
|
|
656
|
+
display: "flex",
|
|
657
|
+
alignItems: "center",
|
|
658
|
+
justifyContent: "center",
|
|
659
|
+
pointerEvents: "none",
|
|
660
|
+
});
|
|
661
|
+
applyStyles(accentDot, {
|
|
662
|
+
width: "6px",
|
|
663
|
+
height: "6px",
|
|
664
|
+
borderRadius: "999px",
|
|
665
|
+
background: anchorAccentColor,
|
|
666
|
+
});
|
|
667
|
+
dot.appendChild(accentDot);
|
|
668
|
+
owner.appendChild(dot);
|
|
669
|
+
}
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
604
672
|
for (const kind of ["inlet", "outlet"]) {
|
|
605
673
|
if (!shouldRenderAnchor(kind))
|
|
606
674
|
continue;
|
|
@@ -648,7 +716,7 @@ function drawZoneAnchors(params) {
|
|
|
648
716
|
background: anchorAccentColor,
|
|
649
717
|
left: kind === "inlet" ? "8px" : "auto",
|
|
650
718
|
right: kind === "outlet" ? "8px" : "auto",
|
|
651
|
-
boxShadow: `0 0 0 4px ${
|
|
719
|
+
boxShadow: `0 0 0 4px ${anchorGlowColor}`,
|
|
652
720
|
});
|
|
653
721
|
el.appendChild(seam);
|
|
654
722
|
el.appendChild(accent);
|
|
@@ -795,7 +863,6 @@ export const domDrawEngine = {
|
|
|
795
863
|
const zoneDepth = getZoneDepth(input.model, zoneVisual.zoneId);
|
|
796
864
|
const zoneEl = document.createElement("div");
|
|
797
865
|
const zoneBodyEl = document.createElement("div");
|
|
798
|
-
const zoneChromeEl = document.createElement("div");
|
|
799
866
|
zoneEl.dataset.zoneflowZoneId = zoneVisual.zoneId;
|
|
800
867
|
zoneBodyEl.dataset.zoneflowZoneBody = zoneVisual.zoneId;
|
|
801
868
|
applyStyles(zoneEl, {
|
|
@@ -808,34 +875,82 @@ export const domDrawEngine = {
|
|
|
808
875
|
overflow: "visible",
|
|
809
876
|
zIndex: zoneDepth + RENDER_Z_INDEX.zoneBase,
|
|
810
877
|
});
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
borderRadius: "0",
|
|
818
|
-
border: `1px solid ${zoneVisual.zone.zoneType === "action"
|
|
878
|
+
const shape = normalizeZoneShape(input.resolveZoneShape?.(zoneVisual.zone));
|
|
879
|
+
// Consumer-resolved per-zone color overrides the theme's border + accent
|
|
880
|
+
// (body background and text stay theme-driven to preserve contrast).
|
|
881
|
+
const zoneColor = input.resolveZoneColor?.(zoneVisual.zone) ?? undefined;
|
|
882
|
+
const zoneBorderColor = zoneColor ??
|
|
883
|
+
(zoneVisual.zone.zoneType === "action"
|
|
819
884
|
? theme.zoneActionBorder
|
|
820
|
-
: theme.zoneContainerBorder
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
885
|
+
: theme.zoneContainerBorder);
|
|
886
|
+
const zoneAccentColor = zoneColor
|
|
887
|
+
? `color-mix(in srgb, ${zoneColor} 18%, transparent)`
|
|
888
|
+
: zoneVisual.zone.zoneType === "action"
|
|
889
|
+
? theme.surface.zone.actionAccent
|
|
890
|
+
: theme.surface.zone.containerAccent;
|
|
891
|
+
if (shape.clipPath) {
|
|
892
|
+
// Clipped polygon (diamond/hexagon/custom). A CSS border would be
|
|
893
|
+
// cut by clip-path, so the outline is synthesized: a border-colored
|
|
894
|
+
// base layer with a 1px-inset fill layer on top. Depth comes from a
|
|
895
|
+
// drop-shadow filter since box-shadow is clipped away.
|
|
896
|
+
applyStyles(zoneBodyEl, {
|
|
897
|
+
position: "absolute",
|
|
898
|
+
left: "0",
|
|
899
|
+
top: "0",
|
|
900
|
+
width: "100%",
|
|
901
|
+
height: "100%",
|
|
902
|
+
background: zoneBorderColor,
|
|
903
|
+
clipPath: shape.clipPath,
|
|
904
|
+
boxSizing: "border-box",
|
|
905
|
+
overflow: "hidden",
|
|
906
|
+
filter: ZONE_CLIP_SHADOW,
|
|
907
|
+
});
|
|
908
|
+
const zoneFillEl = document.createElement("div");
|
|
909
|
+
applyStyles(zoneFillEl, {
|
|
910
|
+
position: "absolute",
|
|
911
|
+
inset: "1px",
|
|
912
|
+
background: theme.surface.zone.background,
|
|
913
|
+
clipPath: shape.clipPath,
|
|
914
|
+
boxSizing: "border-box",
|
|
915
|
+
overflow: "hidden",
|
|
916
|
+
});
|
|
917
|
+
zoneBodyEl.appendChild(zoneFillEl);
|
|
918
|
+
createSurfaceChrome({
|
|
919
|
+
owner: zoneFillEl,
|
|
920
|
+
accent: zoneAccentColor,
|
|
921
|
+
radius: "0",
|
|
922
|
+
theme,
|
|
923
|
+
header: shape.header,
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
else {
|
|
927
|
+
applyStyles(zoneBodyEl, {
|
|
928
|
+
position: "absolute",
|
|
929
|
+
left: "0",
|
|
930
|
+
top: "0",
|
|
931
|
+
width: "100%",
|
|
932
|
+
height: "100%",
|
|
933
|
+
borderRadius: shape.borderRadius,
|
|
934
|
+
border: `1px solid ${zoneBorderColor}`,
|
|
935
|
+
background: theme.surface.zone.background,
|
|
936
|
+
boxSizing: "border-box",
|
|
937
|
+
boxShadow: theme.surface.zone.shadow,
|
|
938
|
+
overflow: "hidden",
|
|
939
|
+
});
|
|
940
|
+
const zoneChromeEl = document.createElement("div");
|
|
941
|
+
createSurfaceChrome({
|
|
942
|
+
owner: zoneChromeEl,
|
|
943
|
+
accent: zoneAccentColor,
|
|
944
|
+
radius: shape.borderRadius,
|
|
945
|
+
theme,
|
|
946
|
+
header: shape.header,
|
|
947
|
+
});
|
|
948
|
+
zoneBodyEl.appendChild(zoneChromeEl);
|
|
949
|
+
}
|
|
826
950
|
zoneEl.addEventListener("click", (event) => {
|
|
827
951
|
event.stopPropagation();
|
|
828
952
|
interactionHandlers?.onZoneClick?.(zoneVisual.zoneId);
|
|
829
953
|
});
|
|
830
|
-
createSurfaceChrome({
|
|
831
|
-
owner: zoneChromeEl,
|
|
832
|
-
accent: zoneVisual.zone.zoneType === "action"
|
|
833
|
-
? theme.surface.zone.actionAccent
|
|
834
|
-
: theme.surface.zone.containerAccent,
|
|
835
|
-
radius: "0",
|
|
836
|
-
theme,
|
|
837
|
-
});
|
|
838
|
-
zoneBodyEl.appendChild(zoneChromeEl);
|
|
839
954
|
zoneEl.appendChild(zoneBodyEl);
|
|
840
955
|
for (const slot of Object.keys(componentLayout?.slots ?? {})) {
|
|
841
956
|
createZoneSlotHost({
|
|
@@ -851,6 +966,7 @@ export const domDrawEngine = {
|
|
|
851
966
|
owner: zoneEl,
|
|
852
967
|
zone: zoneVisual,
|
|
853
968
|
input,
|
|
969
|
+
mode: shape.anchors,
|
|
854
970
|
});
|
|
855
971
|
zoneLayer.appendChild(zoneEl);
|
|
856
972
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/renderer.js
CHANGED
|
@@ -69,7 +69,7 @@ export function createRenderer() {
|
|
|
69
69
|
update(input) {
|
|
70
70
|
if (!host)
|
|
71
71
|
return;
|
|
72
|
-
const { model, layoutModel, theme, textScale = "md", camera = DEFAULT_CAMERA, graphLayoutEngine = defaultGraphLayoutEngine, densityEngine = defaultDensityEngine, visibilityEngine = defaultVisibilityEngine, componentLayoutEngine = defaultComponentLayoutEngine, drawEngine = domDrawEngine, zoneComponentRenderers, pathComponentRenderers, backgroundRenderer, gridOptions, interactionHandlers, exclusionState, debug, } = input;
|
|
72
|
+
const { model, layoutModel, theme, textScale = "md", camera = DEFAULT_CAMERA, graphLayoutEngine = defaultGraphLayoutEngine, densityEngine = defaultDensityEngine, visibilityEngine = defaultVisibilityEngine, componentLayoutEngine = defaultComponentLayoutEngine, drawEngine = domDrawEngine, zoneComponentRenderers, pathComponentRenderers, resolveZoneShape, resolveZoneColor, backgroundRenderer, gridOptions, interactionHandlers, exclusionState, debug, } = input;
|
|
73
73
|
const mergedTheme = resolveTheme(theme);
|
|
74
74
|
const viewportInfo = resolveViewportInfo(host, camera, input);
|
|
75
75
|
const pipeline = runRenderPipeline({
|
|
@@ -119,6 +119,8 @@ export function createRenderer() {
|
|
|
119
119
|
pipeline,
|
|
120
120
|
zoneComponentRenderers,
|
|
121
121
|
pathComponentRenderers,
|
|
122
|
+
resolveZoneShape,
|
|
123
|
+
resolveZoneColor,
|
|
122
124
|
backgroundRenderer,
|
|
123
125
|
gridOptions,
|
|
124
126
|
interactionHandlers,
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AnchorRect, Path, PathId, Point, UniverseId, UniverseLayoutModel, UniverseModel, Zone, ZoneId } from "@zoneflow/core";
|
|
2
2
|
import type { TextScaleLevel, ZoneflowTheme } from "./theme";
|
|
3
|
+
import type { ResolveZoneColor, ResolveZoneShape } from "./zoneShape";
|
|
3
4
|
export type CameraState = {
|
|
4
5
|
x: number;
|
|
5
6
|
y: number;
|
|
@@ -203,6 +204,8 @@ export type RendererDrawInput = {
|
|
|
203
204
|
pipeline: RenderPipelineResult;
|
|
204
205
|
zoneComponentRenderers?: ZoneComponentRendererMap;
|
|
205
206
|
pathComponentRenderers?: PathComponentRendererMap;
|
|
207
|
+
resolveZoneShape?: ResolveZoneShape;
|
|
208
|
+
resolveZoneColor?: ResolveZoneColor;
|
|
206
209
|
backgroundRenderer?: BackgroundRenderer;
|
|
207
210
|
gridOptions?: GridOptions;
|
|
208
211
|
interactionHandlers?: RendererInteractionHandlers;
|
|
@@ -266,6 +269,8 @@ export type RendererInput = {
|
|
|
266
269
|
drawEngine?: DrawEngine;
|
|
267
270
|
zoneComponentRenderers?: ZoneComponentRendererMap;
|
|
268
271
|
pathComponentRenderers?: PathComponentRendererMap;
|
|
272
|
+
resolveZoneShape?: ResolveZoneShape;
|
|
273
|
+
resolveZoneColor?: ResolveZoneColor;
|
|
269
274
|
backgroundRenderer?: BackgroundRenderer;
|
|
270
275
|
gridOptions?: GridOptions;
|
|
271
276
|
interactionHandlers?: RendererInteractionHandlers;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Zone } from "@zoneflow/core";
|
|
2
|
+
/**
|
|
3
|
+
* Where a zone's connection anchors are drawn.
|
|
4
|
+
* - `edge`: the default full-height side tabs (good for rectangular cards).
|
|
5
|
+
* - `vertex`: a compact dot centered on the left/right edge midpoint
|
|
6
|
+
* (good for round/diamond nodes whose sides are not flat).
|
|
7
|
+
*/
|
|
8
|
+
export type ZoneAnchorRenderMode = "edge" | "vertex";
|
|
9
|
+
/** Built-in shape presets. */
|
|
10
|
+
export type ZoneShapeName = "rect" | "rounded" | "pill" | "circle" | "diamond" | "hexagon";
|
|
11
|
+
/**
|
|
12
|
+
* Fully custom shape spec — the escape hatch for arbitrary geometry.
|
|
13
|
+
*
|
|
14
|
+
* Provide either `borderRadius` (rendered with a real CSS border) or
|
|
15
|
+
* `clipPath` (rendered as a clipped polygon with a synthesized outline).
|
|
16
|
+
* When `clipPath` is set, `borderRadius` is ignored.
|
|
17
|
+
*/
|
|
18
|
+
export type ZoneShapeSpec = {
|
|
19
|
+
/** CSS `border-radius` value, e.g. `"14px"`, `"50%"`, `"999px"`. */
|
|
20
|
+
borderRadius?: string;
|
|
21
|
+
/**
|
|
22
|
+
* CSS `clip-path` value, e.g. `"polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)"`.
|
|
23
|
+
* When set the zone is drawn as a clipped polygon.
|
|
24
|
+
*/
|
|
25
|
+
clipPath?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Whether to draw the rectangular accent header band at the top.
|
|
28
|
+
* Defaults to `true` for radius-based shapes and `false` for clipped /
|
|
29
|
+
* fully-rounded shapes (where a rectangular band looks wrong).
|
|
30
|
+
*/
|
|
31
|
+
header?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* How connection anchors attach. Defaults to `"vertex"` for clipped or
|
|
34
|
+
* circular shapes and `"edge"` otherwise.
|
|
35
|
+
*/
|
|
36
|
+
anchors?: ZoneAnchorRenderMode;
|
|
37
|
+
};
|
|
38
|
+
export type ZoneShape = ZoneShapeName | ZoneShapeSpec;
|
|
39
|
+
/**
|
|
40
|
+
* Resolver invoked once per zone to decide how it is drawn. Return
|
|
41
|
+
* `undefined`/`null` to fall back to the default rectangle. Purely a
|
|
42
|
+
* presentation concern — the zone's geometry, hit-testing, and anchor
|
|
43
|
+
* points are unaffected, so paths still connect exactly as before.
|
|
44
|
+
*/
|
|
45
|
+
export type ResolveZoneShape = (zone: Zone) => ZoneShape | null | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Resolver invoked once per zone to decide its accent color. Return a CSS
|
|
48
|
+
* color to override the zone's border + accent (and matching anchor) for that
|
|
49
|
+
* zone; return `undefined`/`null` to fall back to the theme's zoneType-based
|
|
50
|
+
* colors. Like {@link ResolveZoneShape}, a purely presentational hook decided
|
|
51
|
+
* by the consumer (e.g. from `zone.meta.color`, `zone.action`, …). The body
|
|
52
|
+
* background and text stay theme-driven so contrast is preserved.
|
|
53
|
+
*/
|
|
54
|
+
export type ResolveZoneColor = (zone: Zone) => string | null | undefined;
|
|
55
|
+
/** Normalized geometry consumed by the draw engine. */
|
|
56
|
+
export type ResolvedZoneShape = {
|
|
57
|
+
borderRadius: string;
|
|
58
|
+
clipPath: string | null;
|
|
59
|
+
header: boolean;
|
|
60
|
+
anchors: ZoneAnchorRenderMode;
|
|
61
|
+
};
|
|
62
|
+
export declare function normalizeZoneShape(shape: ZoneShape | null | undefined): ResolvedZoneShape;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const DIAMOND_CLIP = "polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)";
|
|
2
|
+
const HEXAGON_CLIP = "polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%)";
|
|
3
|
+
const RECT_SHAPE = {
|
|
4
|
+
borderRadius: "0",
|
|
5
|
+
clipPath: null,
|
|
6
|
+
header: true,
|
|
7
|
+
anchors: "edge",
|
|
8
|
+
};
|
|
9
|
+
function specFromName(name) {
|
|
10
|
+
switch (name) {
|
|
11
|
+
case "rounded":
|
|
12
|
+
return { borderRadius: "14px" };
|
|
13
|
+
case "pill":
|
|
14
|
+
return { borderRadius: "999px", header: false, anchors: "vertex" };
|
|
15
|
+
case "circle":
|
|
16
|
+
return { borderRadius: "50%", header: false, anchors: "vertex" };
|
|
17
|
+
case "diamond":
|
|
18
|
+
return { clipPath: DIAMOND_CLIP };
|
|
19
|
+
case "hexagon":
|
|
20
|
+
return { clipPath: HEXAGON_CLIP };
|
|
21
|
+
case "rect":
|
|
22
|
+
default:
|
|
23
|
+
return { borderRadius: "0" };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function normalizeZoneShape(shape) {
|
|
27
|
+
if (!shape)
|
|
28
|
+
return RECT_SHAPE;
|
|
29
|
+
const spec = typeof shape === "string" ? specFromName(shape) : shape;
|
|
30
|
+
const clipPath = spec.clipPath ?? null;
|
|
31
|
+
const borderRadius = clipPath ? "0" : spec.borderRadius ?? "0";
|
|
32
|
+
const isRound = !clipPath && borderRadius === "50%";
|
|
33
|
+
return {
|
|
34
|
+
borderRadius,
|
|
35
|
+
clipPath,
|
|
36
|
+
header: spec.header ?? !clipPath,
|
|
37
|
+
anchors: spec.anchors ?? (clipPath || isRound ? "vertex" : "edge"),
|
|
38
|
+
};
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zoneflow/renderer-dom",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Low-level DOM renderer engines for Zoneflow.",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"dist"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@zoneflow/core": "0.0.
|
|
22
|
+
"@zoneflow/core": "0.0.16"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsc -p tsconfig.json",
|