@zoneflow/editor-dom 0.0.5 → 0.0.7
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/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/moveEditorShared.d.ts +32 -0
- package/dist/moveEditorShared.js +79 -0
- package/dist/zOrderEditor.d.ts +14 -0
- package/dist/zOrderEditor.js +126 -0
- package/dist/zoneMoveEditor.d.ts +21 -3
- package/dist/zoneMoveEditor.js +149 -11
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./zoneMoveEditor";
|
|
2
2
|
export * from "./pathCreateEditor";
|
|
3
|
+
export * from "./zOrderEditor";
|
|
3
4
|
export { alignPathsByMode, alignZonesByMode, commitZoneGroupReparentAtCurrentPosition, commitZoneReparentAtCurrentPosition, distributePathsByMode, distributeZonesByMode, resolveGroupPathDragOrigin, resolveGroupZoneDragOrigin, resolveZonePlacementAtWorldRect, resolvePathResizeOrigin, resizePathNodeByScreenDelta, } from "./zoneMoveEditor";
|
|
4
5
|
export type { PathResizeOrigin } from "./zoneMoveEditor";
|
|
5
6
|
export { resolvePathOutputAnchorScreenRect, retargetPathFromOutputAnchorDrag, } from "./pathCreateEditor";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from "./zoneMoveEditor";
|
|
2
2
|
export * from "./pathCreateEditor";
|
|
3
|
+
export * from "./zOrderEditor";
|
|
3
4
|
export { alignPathsByMode, alignZonesByMode, commitZoneGroupReparentAtCurrentPosition, commitZoneReparentAtCurrentPosition, distributePathsByMode, distributeZonesByMode, resolveGroupPathDragOrigin, resolveGroupZoneDragOrigin, resolveZonePlacementAtWorldRect, resolvePathResizeOrigin, resizePathNodeByScreenDelta, } from "./zoneMoveEditor";
|
|
4
5
|
export { resolvePathOutputAnchorScreenRect, retargetPathFromOutputAnchorDrag, } from "./pathCreateEditor";
|
|
@@ -26,11 +26,23 @@ export type PathMoveOriginSnapshot = {
|
|
|
26
26
|
componentId: "body" | "label" | null;
|
|
27
27
|
coordinateSpace: PathMoveCoordinateSpace;
|
|
28
28
|
};
|
|
29
|
+
export type ObjectSnapGuides = {
|
|
30
|
+
x: number[];
|
|
31
|
+
y: number[];
|
|
32
|
+
};
|
|
33
|
+
export type ObjectSnapAxisMatch = {
|
|
34
|
+
guide: number;
|
|
35
|
+
align: "start" | "center" | "end";
|
|
36
|
+
snappedStart: number;
|
|
37
|
+
};
|
|
29
38
|
export type MoveEditorDragOrigin = {
|
|
30
39
|
kind: "zone";
|
|
31
40
|
zoneId: ZoneId;
|
|
32
41
|
originX: number;
|
|
33
42
|
originY: number;
|
|
43
|
+
width: number;
|
|
44
|
+
height: number;
|
|
45
|
+
objectSnapGuides?: ObjectSnapGuides;
|
|
34
46
|
} | {
|
|
35
47
|
kind: "zone-group";
|
|
36
48
|
primaryZoneId: ZoneId;
|
|
@@ -39,6 +51,7 @@ export type MoveEditorDragOrigin = {
|
|
|
39
51
|
kind: "path";
|
|
40
52
|
pathId: PathId;
|
|
41
53
|
origin: PathMoveOriginSnapshot;
|
|
54
|
+
objectSnapGuides?: ObjectSnapGuides;
|
|
42
55
|
} | {
|
|
43
56
|
kind: "path-group";
|
|
44
57
|
primaryPathId: PathId;
|
|
@@ -61,6 +74,10 @@ export type GridSnapOptions = {
|
|
|
61
74
|
enabled?: boolean;
|
|
62
75
|
size?: number;
|
|
63
76
|
};
|
|
77
|
+
export type ObjectSnapOptions = {
|
|
78
|
+
enabled?: boolean;
|
|
79
|
+
threshold?: number;
|
|
80
|
+
};
|
|
64
81
|
export type ZoneAlignMode = "left" | "right" | "top" | "bottom" | "center-horizontal" | "center-vertical";
|
|
65
82
|
export type ZoneDistributeMode = "horizontal" | "vertical";
|
|
66
83
|
export type PathAlignMode = ZoneAlignMode;
|
|
@@ -83,5 +100,20 @@ export declare function resolveSnappedMove(params: {
|
|
|
83
100
|
effectiveDeltaX: number;
|
|
84
101
|
effectiveDeltaY: number;
|
|
85
102
|
};
|
|
103
|
+
export declare function collectRectObjectSnapGuides(rects: Rect[]): ObjectSnapGuides;
|
|
104
|
+
export declare function resolveObjectSnappedRectPosition(params: {
|
|
105
|
+
x: number;
|
|
106
|
+
y: number;
|
|
107
|
+
width: number;
|
|
108
|
+
height: number;
|
|
109
|
+
camera: CameraState;
|
|
110
|
+
guides?: ObjectSnapGuides;
|
|
111
|
+
objectSnap?: ObjectSnapOptions;
|
|
112
|
+
}): {
|
|
113
|
+
x: number;
|
|
114
|
+
y: number;
|
|
115
|
+
guideX: number | undefined;
|
|
116
|
+
guideY: number | undefined;
|
|
117
|
+
};
|
|
86
118
|
export declare function containsPoint(rect: Rect, point: Point): boolean;
|
|
87
119
|
export declare function getRectArea(rect: Rect): number;
|
package/dist/moveEditorShared.js
CHANGED
|
@@ -37,6 +37,85 @@ export function resolveSnappedMove(params) {
|
|
|
37
37
|
effectiveDeltaY: nextY - originY,
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
|
+
export function collectRectObjectSnapGuides(rects) {
|
|
41
|
+
const x = new Set();
|
|
42
|
+
const y = new Set();
|
|
43
|
+
for (const rect of rects) {
|
|
44
|
+
x.add(roundCoordinate(rect.x));
|
|
45
|
+
x.add(roundCoordinate(rect.x + rect.width / 2));
|
|
46
|
+
x.add(roundCoordinate(rect.x + rect.width));
|
|
47
|
+
y.add(roundCoordinate(rect.y));
|
|
48
|
+
y.add(roundCoordinate(rect.y + rect.height / 2));
|
|
49
|
+
y.add(roundCoordinate(rect.y + rect.height));
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
x: Array.from(x).sort((a, b) => a - b),
|
|
53
|
+
y: Array.from(y).sort((a, b) => a - b),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function resolveAxisObjectSnap(params) {
|
|
57
|
+
const { start, size, guides, threshold } = params;
|
|
58
|
+
const candidates = [
|
|
59
|
+
{ coordinate: start, align: "start" },
|
|
60
|
+
{ coordinate: start + size / 2, align: "center" },
|
|
61
|
+
{ coordinate: start + size, align: "end" },
|
|
62
|
+
];
|
|
63
|
+
let best;
|
|
64
|
+
for (const guide of guides) {
|
|
65
|
+
for (const candidate of candidates) {
|
|
66
|
+
const distance = Math.abs(guide - candidate.coordinate);
|
|
67
|
+
if (distance > threshold)
|
|
68
|
+
continue;
|
|
69
|
+
const snappedStart = candidate.align === "start"
|
|
70
|
+
? guide
|
|
71
|
+
: candidate.align === "center"
|
|
72
|
+
? guide - size / 2
|
|
73
|
+
: guide - size;
|
|
74
|
+
if (!best || distance < best.distance) {
|
|
75
|
+
best = {
|
|
76
|
+
distance,
|
|
77
|
+
match: {
|
|
78
|
+
guide: roundCoordinate(guide),
|
|
79
|
+
align: candidate.align,
|
|
80
|
+
snappedStart: roundCoordinate(snappedStart),
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return best?.match;
|
|
87
|
+
}
|
|
88
|
+
export function resolveObjectSnappedRectPosition(params) {
|
|
89
|
+
const { x, y, width, height, camera, guides, objectSnap } = params;
|
|
90
|
+
if (!guides || !objectSnap?.enabled) {
|
|
91
|
+
return {
|
|
92
|
+
x: roundCoordinate(x),
|
|
93
|
+
y: roundCoordinate(y),
|
|
94
|
+
guideX: undefined,
|
|
95
|
+
guideY: undefined,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const threshold = objectSnap.threshold ?? 8;
|
|
99
|
+
const worldThreshold = threshold / camera.zoom;
|
|
100
|
+
const xMatch = resolveAxisObjectSnap({
|
|
101
|
+
start: x,
|
|
102
|
+
size: width,
|
|
103
|
+
guides: guides.x,
|
|
104
|
+
threshold: worldThreshold,
|
|
105
|
+
});
|
|
106
|
+
const yMatch = resolveAxisObjectSnap({
|
|
107
|
+
start: y,
|
|
108
|
+
size: height,
|
|
109
|
+
guides: guides.y,
|
|
110
|
+
threshold: worldThreshold,
|
|
111
|
+
});
|
|
112
|
+
return {
|
|
113
|
+
x: xMatch?.snappedStart ?? roundCoordinate(x),
|
|
114
|
+
y: yMatch?.snappedStart ?? roundCoordinate(y),
|
|
115
|
+
guideX: xMatch?.guide,
|
|
116
|
+
guideY: yMatch?.guide,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
40
119
|
export function containsPoint(rect, point) {
|
|
41
120
|
return (point.x >= rect.x &&
|
|
42
121
|
point.x <= rect.x + rect.width &&
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type PathId, type UniverseLayoutModel, type UniverseModel, type ZoneId } from "@zoneflow/core";
|
|
2
|
+
export type ZOrderMode = "send-to-back" | "send-backward" | "bring-forward" | "bring-to-front";
|
|
3
|
+
export declare function reorderZonesByZOrderMode(params: {
|
|
4
|
+
model: UniverseModel;
|
|
5
|
+
layoutModel: UniverseLayoutModel;
|
|
6
|
+
zoneIds: ZoneId[];
|
|
7
|
+
mode: ZOrderMode;
|
|
8
|
+
}): UniverseLayoutModel;
|
|
9
|
+
export declare function reorderPathsByZOrderMode(params: {
|
|
10
|
+
model: UniverseModel;
|
|
11
|
+
layoutModel: UniverseLayoutModel;
|
|
12
|
+
pathIds: PathId[];
|
|
13
|
+
mode: ZOrderMode;
|
|
14
|
+
}): UniverseLayoutModel;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { updatePathLayout, updateZoneLayout, } from "@zoneflow/core";
|
|
2
|
+
function reorderIds(params) {
|
|
3
|
+
const { orderedIds, selectedIds, mode } = params;
|
|
4
|
+
if (orderedIds.length < 2 || selectedIds.size === 0)
|
|
5
|
+
return orderedIds;
|
|
6
|
+
if (mode === "send-to-back") {
|
|
7
|
+
return [
|
|
8
|
+
...orderedIds.filter((id) => selectedIds.has(id)),
|
|
9
|
+
...orderedIds.filter((id) => !selectedIds.has(id)),
|
|
10
|
+
];
|
|
11
|
+
}
|
|
12
|
+
if (mode === "bring-to-front") {
|
|
13
|
+
return [
|
|
14
|
+
...orderedIds.filter((id) => !selectedIds.has(id)),
|
|
15
|
+
...orderedIds.filter((id) => selectedIds.has(id)),
|
|
16
|
+
];
|
|
17
|
+
}
|
|
18
|
+
const nextIds = [...orderedIds];
|
|
19
|
+
if (mode === "send-backward") {
|
|
20
|
+
for (let index = 1; index < nextIds.length; index += 1) {
|
|
21
|
+
const currentId = nextIds[index];
|
|
22
|
+
const previousId = nextIds[index - 1];
|
|
23
|
+
if (!currentId || !previousId)
|
|
24
|
+
continue;
|
|
25
|
+
if (!selectedIds.has(currentId) || selectedIds.has(previousId))
|
|
26
|
+
continue;
|
|
27
|
+
nextIds[index - 1] = currentId;
|
|
28
|
+
nextIds[index] = previousId;
|
|
29
|
+
}
|
|
30
|
+
return nextIds;
|
|
31
|
+
}
|
|
32
|
+
for (let index = nextIds.length - 2; index >= 0; index -= 1) {
|
|
33
|
+
const currentId = nextIds[index];
|
|
34
|
+
const nextId = nextIds[index + 1];
|
|
35
|
+
if (!currentId || !nextId)
|
|
36
|
+
continue;
|
|
37
|
+
if (!selectedIds.has(currentId) || selectedIds.has(nextId))
|
|
38
|
+
continue;
|
|
39
|
+
nextIds[index] = nextId;
|
|
40
|
+
nextIds[index + 1] = currentId;
|
|
41
|
+
}
|
|
42
|
+
return nextIds;
|
|
43
|
+
}
|
|
44
|
+
function sortByCurrentZOrder(params) {
|
|
45
|
+
const { ids, getZOrder } = params;
|
|
46
|
+
return [...ids]
|
|
47
|
+
.map((id, index) => ({
|
|
48
|
+
id,
|
|
49
|
+
index,
|
|
50
|
+
zOrder: getZOrder(id) ?? index,
|
|
51
|
+
}))
|
|
52
|
+
.sort((a, b) => a.zOrder - b.zOrder || a.index - b.index)
|
|
53
|
+
.map((entry) => entry.id);
|
|
54
|
+
}
|
|
55
|
+
export function reorderZonesByZOrderMode(params) {
|
|
56
|
+
const { model, layoutModel, zoneIds, mode } = params;
|
|
57
|
+
const selectedIds = new Set(zoneIds);
|
|
58
|
+
if (selectedIds.size === 0)
|
|
59
|
+
return layoutModel;
|
|
60
|
+
const selectedIdsByParent = new Map();
|
|
61
|
+
for (const zoneId of selectedIds) {
|
|
62
|
+
const zone = model.zonesById[zoneId];
|
|
63
|
+
if (!zone)
|
|
64
|
+
continue;
|
|
65
|
+
const parentZoneId = zone.parentZoneId;
|
|
66
|
+
const current = selectedIdsByParent.get(parentZoneId) ?? new Set();
|
|
67
|
+
current.add(zoneId);
|
|
68
|
+
selectedIdsByParent.set(parentZoneId, current);
|
|
69
|
+
}
|
|
70
|
+
let nextLayoutModel = layoutModel;
|
|
71
|
+
for (const [parentZoneId, selectedSiblingIds] of selectedIdsByParent) {
|
|
72
|
+
const siblingIds = parentZoneId === null
|
|
73
|
+
? model.rootZoneIds
|
|
74
|
+
: model.zonesById[parentZoneId]?.childZoneIds ?? [];
|
|
75
|
+
const orderedSiblingIds = sortByCurrentZOrder({
|
|
76
|
+
ids: siblingIds.filter((zoneId) => Boolean(model.zonesById[zoneId])),
|
|
77
|
+
getZOrder: (zoneId) => layoutModel.zoneLayoutsById[zoneId]?.zOrder,
|
|
78
|
+
});
|
|
79
|
+
const reorderedSiblingIds = reorderIds({
|
|
80
|
+
orderedIds: orderedSiblingIds,
|
|
81
|
+
selectedIds: selectedSiblingIds,
|
|
82
|
+
mode,
|
|
83
|
+
});
|
|
84
|
+
reorderedSiblingIds.forEach((zoneId, zOrder) => {
|
|
85
|
+
if (!nextLayoutModel.zoneLayoutsById[zoneId])
|
|
86
|
+
return;
|
|
87
|
+
nextLayoutModel = updateZoneLayout(nextLayoutModel, zoneId, {
|
|
88
|
+
zOrder,
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return nextLayoutModel;
|
|
93
|
+
}
|
|
94
|
+
function getAllPathIds(model) {
|
|
95
|
+
const pathIds = [];
|
|
96
|
+
const zoneIds = Object.keys(model.zonesById);
|
|
97
|
+
for (const zoneId of zoneIds) {
|
|
98
|
+
const zone = model.zonesById[zoneId];
|
|
99
|
+
if (!zone)
|
|
100
|
+
continue;
|
|
101
|
+
pathIds.push(...zone.pathIds.filter((pathId) => Boolean(zone.pathsById[pathId])));
|
|
102
|
+
}
|
|
103
|
+
return pathIds;
|
|
104
|
+
}
|
|
105
|
+
export function reorderPathsByZOrderMode(params) {
|
|
106
|
+
const { model, layoutModel, pathIds, mode } = params;
|
|
107
|
+
const selectedIds = new Set(pathIds);
|
|
108
|
+
if (selectedIds.size === 0)
|
|
109
|
+
return layoutModel;
|
|
110
|
+
const orderedPathIds = sortByCurrentZOrder({
|
|
111
|
+
ids: getAllPathIds(model),
|
|
112
|
+
getZOrder: (pathId) => layoutModel.pathLayoutsById[pathId]?.zOrder,
|
|
113
|
+
});
|
|
114
|
+
const reorderedPathIds = reorderIds({
|
|
115
|
+
orderedIds: orderedPathIds,
|
|
116
|
+
selectedIds,
|
|
117
|
+
mode,
|
|
118
|
+
});
|
|
119
|
+
let nextLayoutModel = layoutModel;
|
|
120
|
+
reorderedPathIds.forEach((pathId, zOrder) => {
|
|
121
|
+
nextLayoutModel = updatePathLayout(nextLayoutModel, pathId, {
|
|
122
|
+
zOrder,
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
return nextLayoutModel;
|
|
126
|
+
}
|
package/dist/zoneMoveEditor.d.ts
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
import { type UniverseLayoutModel, type UniverseModel, type ZoneId } from "@zoneflow/core";
|
|
2
2
|
import type { CameraState, RendererFrame } from "@zoneflow/renderer-dom";
|
|
3
|
-
import { type GridSnapOptions, type MoveEditorDragOrigin, type MoveEditorTarget, type MoveEditorTargetOptions, type ZoneAlignMode, type ZoneDistributeMode, type ZoneResizeOrigin } from "./moveEditorShared";
|
|
4
|
-
export type { GridSnapOptions, MoveEditorDragOrigin, MoveEditorTarget, MoveEditorTargetOptions, PathAlignMode, PathDistributeMode, PathResizeOrigin, ZoneAlignMode, ZoneDistributeMode, ZoneResizeOrigin, } from "./moveEditorShared";
|
|
3
|
+
import { type GridSnapOptions, type ObjectSnapOptions, type MoveEditorDragOrigin, type MoveEditorTarget, type MoveEditorTargetOptions, type ZoneAlignMode, type ZoneDistributeMode, type ZoneResizeOrigin } from "./moveEditorShared";
|
|
4
|
+
export type { GridSnapOptions, MoveEditorDragOrigin, MoveEditorTarget, MoveEditorTargetOptions, ObjectSnapOptions, PathAlignMode, PathDistributeMode, PathResizeOrigin, ZoneAlignMode, ZoneDistributeMode, ZoneResizeOrigin, } from "./moveEditorShared";
|
|
5
5
|
export { alignPathsByMode, distributePathsByMode, resolveGroupPathDragOrigin, resolvePathResizeOrigin, resizePathNodeByScreenDelta, } from "./pathMoveEditor";
|
|
6
6
|
export { commitZoneGroupReparentAtCurrentPosition, commitZoneReparentAtCurrentPosition, reparentZoneAtCurrentPosition, resolveZonePlacementAtWorldRect, resolveZoneReparentCandidate, } from "./zoneReparent";
|
|
7
7
|
export declare function getMoveEditorTargets(params: {
|
|
8
8
|
model: UniverseModel;
|
|
9
|
+
layoutModel?: UniverseLayoutModel;
|
|
9
10
|
frame: RendererFrame;
|
|
10
11
|
camera: CameraState;
|
|
11
12
|
options?: MoveEditorTargetOptions;
|
|
12
13
|
}): MoveEditorTarget[];
|
|
13
|
-
export declare function resolveMoveEditorDragOrigin(
|
|
14
|
+
export declare function resolveMoveEditorDragOrigin(params: {
|
|
15
|
+
model: UniverseModel;
|
|
16
|
+
layoutModel: UniverseLayoutModel;
|
|
17
|
+
target: MoveEditorTarget;
|
|
18
|
+
frame?: RendererFrame;
|
|
19
|
+
}): MoveEditorDragOrigin | undefined;
|
|
14
20
|
export declare function resolveGroupZoneDragOrigin(params: {
|
|
15
21
|
model: UniverseModel;
|
|
16
22
|
layoutModel: UniverseLayoutModel;
|
|
@@ -24,7 +30,19 @@ export declare function moveEditorTargetByScreenDelta(params: {
|
|
|
24
30
|
deltaX: number;
|
|
25
31
|
deltaY: number;
|
|
26
32
|
gridSnap?: GridSnapOptions;
|
|
33
|
+
objectSnap?: ObjectSnapOptions;
|
|
27
34
|
}): UniverseLayoutModel;
|
|
35
|
+
export declare function resolveMoveEditorObjectSnapGuides(params: {
|
|
36
|
+
camera: CameraState;
|
|
37
|
+
origin: MoveEditorDragOrigin;
|
|
38
|
+
deltaX: number;
|
|
39
|
+
deltaY: number;
|
|
40
|
+
gridSnap?: GridSnapOptions;
|
|
41
|
+
objectSnap?: ObjectSnapOptions;
|
|
42
|
+
}): {
|
|
43
|
+
guideX: number | undefined;
|
|
44
|
+
guideY: number | undefined;
|
|
45
|
+
};
|
|
28
46
|
export declare function resolveZoneResizeOrigin(layoutModel: UniverseLayoutModel, zoneId: ZoneId): ZoneResizeOrigin | undefined;
|
|
29
47
|
export declare function resizeZoneByScreenDelta(params: {
|
|
30
48
|
layoutModel: UniverseLayoutModel;
|
package/dist/zoneMoveEditor.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getZoneDepth, getZoneLayout, updateZoneLayout, } from "@zoneflow/core";
|
|
2
|
-
import { projectWorldRectToScreenRect, resolveSnappedMove, roundCoordinate, snapCoordinate, typedValues, } from "./moveEditorShared";
|
|
2
|
+
import { collectRectObjectSnapGuides, projectWorldRectToScreenRect, resolveObjectSnappedRectPosition, resolveSnappedMove, roundCoordinate, snapCoordinate, typedValues, } from "./moveEditorShared";
|
|
3
3
|
import { applyPathMovePosition, resolvePathMoveOriginSnapshot, } from "./pathMoveEditor";
|
|
4
4
|
import { applyZoneOriginsDelta, resolveZoneGroupOrigins, } from "./zoneGeometry";
|
|
5
5
|
export { alignPathsByMode, distributePathsByMode, resolveGroupPathDragOrigin, resolvePathResizeOrigin, resizePathNodeByScreenDelta, } from "./pathMoveEditor";
|
|
@@ -7,6 +7,48 @@ export { commitZoneGroupReparentAtCurrentPosition, commitZoneReparentAtCurrentPo
|
|
|
7
7
|
const DEFAULT_MIN_VISIBLE_SIZE = 18;
|
|
8
8
|
const DEFAULT_MIN_ZONE_WIDTH = 140;
|
|
9
9
|
const DEFAULT_MIN_ZONE_HEIGHT = 96;
|
|
10
|
+
function collectDescendantZoneIds(model, zoneId) {
|
|
11
|
+
const descendants = new Set();
|
|
12
|
+
const queue = [...(model.zonesById[zoneId]?.childZoneIds ?? [])];
|
|
13
|
+
while (queue.length > 0) {
|
|
14
|
+
const current = queue.shift();
|
|
15
|
+
if (!current || descendants.has(current))
|
|
16
|
+
continue;
|
|
17
|
+
descendants.add(current);
|
|
18
|
+
queue.push(...(model.zonesById[current]?.childZoneIds ?? []));
|
|
19
|
+
}
|
|
20
|
+
return descendants;
|
|
21
|
+
}
|
|
22
|
+
function resolveObjectSnapGuides(params) {
|
|
23
|
+
const { model, frame, target } = params;
|
|
24
|
+
if (!frame)
|
|
25
|
+
return undefined;
|
|
26
|
+
const excludedZoneIds = target.kind === "zone"
|
|
27
|
+
? new Set([target.zoneId, ...collectDescendantZoneIds(model, target.zoneId)])
|
|
28
|
+
: new Set();
|
|
29
|
+
const candidateRects = [];
|
|
30
|
+
for (const zoneVisual of typedValues(frame.pipeline.graphLayout.zonesById)) {
|
|
31
|
+
const visibility = frame.pipeline.visibility.zoneVisibilityById[zoneVisual.zoneId];
|
|
32
|
+
if (!visibility?.isVisible)
|
|
33
|
+
continue;
|
|
34
|
+
if (excludedZoneIds.has(zoneVisual.zoneId))
|
|
35
|
+
continue;
|
|
36
|
+
candidateRects.push(zoneVisual.rect);
|
|
37
|
+
}
|
|
38
|
+
for (const pathVisual of typedValues(frame.pipeline.graphLayout.pathsById)) {
|
|
39
|
+
const visibility = frame.pipeline.visibility.pathVisibilityById[pathVisual.pathId];
|
|
40
|
+
if (!visibility?.shouldRenderNode || !pathVisual.rect)
|
|
41
|
+
continue;
|
|
42
|
+
if (target.kind === "path" && pathVisual.pathId === target.pathId)
|
|
43
|
+
continue;
|
|
44
|
+
if (target.kind === "zone" && excludedZoneIds.has(pathVisual.sourceZoneId))
|
|
45
|
+
continue;
|
|
46
|
+
candidateRects.push(pathVisual.rect);
|
|
47
|
+
}
|
|
48
|
+
return candidateRects.length > 0
|
|
49
|
+
? collectRectObjectSnapGuides(candidateRects)
|
|
50
|
+
: undefined;
|
|
51
|
+
}
|
|
10
52
|
function resolveResizedAnchor(params) {
|
|
11
53
|
const { kind, width, height, current } = params;
|
|
12
54
|
const rectWidth = current?.rect?.width;
|
|
@@ -34,7 +76,7 @@ function resolveResizedAnchor(params) {
|
|
|
34
76
|
};
|
|
35
77
|
}
|
|
36
78
|
export function getMoveEditorTargets(params) {
|
|
37
|
-
const { model, frame, camera, options, } = params;
|
|
79
|
+
const { model, layoutModel, frame, camera, options, } = params;
|
|
38
80
|
const includeRoot = options?.includeRoot ?? true;
|
|
39
81
|
const minVisibleSize = options?.minVisibleSize ?? DEFAULT_MIN_VISIBLE_SIZE;
|
|
40
82
|
const zoneTargets = [];
|
|
@@ -57,9 +99,10 @@ export function getMoveEditorTargets(params) {
|
|
|
57
99
|
label: zone.name,
|
|
58
100
|
rect,
|
|
59
101
|
depth: getZoneDepth(model, zoneVisual.zoneId),
|
|
102
|
+
zOrder: layoutModel?.zoneLayoutsById[zoneVisual.zoneId]?.zOrder,
|
|
60
103
|
});
|
|
61
104
|
}
|
|
62
|
-
for (const pathVisual of typedValues(frame.pipeline.graphLayout.pathsById)) {
|
|
105
|
+
for (const [index, pathVisual] of typedValues(frame.pipeline.graphLayout.pathsById).entries()) {
|
|
63
106
|
const visibility = frame.pipeline.visibility.pathVisibilityById[pathVisual.pathId];
|
|
64
107
|
if (!visibility?.shouldRenderNode || !pathVisual.rect)
|
|
65
108
|
continue;
|
|
@@ -73,25 +116,44 @@ export function getMoveEditorTargets(params) {
|
|
|
73
116
|
pathId: pathVisual.pathId,
|
|
74
117
|
label: pathVisual.path.name,
|
|
75
118
|
rect,
|
|
119
|
+
zOrder: layoutModel?.pathLayoutsById[pathVisual.pathId]?.zOrder ?? index,
|
|
76
120
|
});
|
|
77
121
|
}
|
|
78
122
|
return [
|
|
79
123
|
...zoneTargets
|
|
80
|
-
.sort((a, b) =>
|
|
81
|
-
|
|
82
|
-
|
|
124
|
+
.sort((a, b) => {
|
|
125
|
+
const aOrder = a.zOrder ?? 0;
|
|
126
|
+
const bOrder = b.zOrder ?? 0;
|
|
127
|
+
return a.depth - b.depth || aOrder - bOrder;
|
|
128
|
+
})
|
|
129
|
+
.map(({ depth: _depth, zOrder: _zOrder, ...target }) => target),
|
|
130
|
+
...pathTargets
|
|
131
|
+
.sort((a, b) => (a.zOrder ?? 0) - (b.zOrder ?? 0))
|
|
132
|
+
.map(({ zOrder: _zOrder, ...target }) => target),
|
|
83
133
|
];
|
|
84
134
|
}
|
|
85
|
-
export function resolveMoveEditorDragOrigin(
|
|
135
|
+
export function resolveMoveEditorDragOrigin(params) {
|
|
136
|
+
const { model, layoutModel, target, frame } = params;
|
|
86
137
|
if (target.kind === "zone") {
|
|
87
138
|
const zoneLayout = getZoneLayout(layoutModel, target.zoneId);
|
|
88
139
|
if (!zoneLayout)
|
|
89
140
|
return undefined;
|
|
141
|
+
const zoneRect = frame?.pipeline.graphLayout.zonesById[target.zoneId]?.rect ??
|
|
142
|
+
getZoneLayout(layoutModel, target.zoneId);
|
|
143
|
+
const width = zoneRect?.width ?? 0;
|
|
144
|
+
const height = zoneRect?.height ?? 0;
|
|
90
145
|
return {
|
|
91
146
|
kind: "zone",
|
|
92
147
|
zoneId: target.zoneId,
|
|
93
148
|
originX: zoneLayout.x,
|
|
94
149
|
originY: zoneLayout.y,
|
|
150
|
+
width,
|
|
151
|
+
height,
|
|
152
|
+
objectSnapGuides: resolveObjectSnapGuides({
|
|
153
|
+
model,
|
|
154
|
+
frame,
|
|
155
|
+
target,
|
|
156
|
+
}),
|
|
95
157
|
};
|
|
96
158
|
}
|
|
97
159
|
return {
|
|
@@ -102,6 +164,11 @@ export function resolveMoveEditorDragOrigin(layoutModel, target, frame) {
|
|
|
102
164
|
layoutModel,
|
|
103
165
|
pathId: target.pathId,
|
|
104
166
|
}),
|
|
167
|
+
objectSnapGuides: resolveObjectSnapGuides({
|
|
168
|
+
model,
|
|
169
|
+
frame,
|
|
170
|
+
target,
|
|
171
|
+
}),
|
|
105
172
|
};
|
|
106
173
|
}
|
|
107
174
|
export function resolveGroupZoneDragOrigin(params) {
|
|
@@ -115,7 +182,7 @@ export function resolveGroupZoneDragOrigin(params) {
|
|
|
115
182
|
};
|
|
116
183
|
}
|
|
117
184
|
export function moveEditorTargetByScreenDelta(params) {
|
|
118
|
-
const { layoutModel, camera, origin, deltaX, deltaY, gridSnap, } = params;
|
|
185
|
+
const { layoutModel, camera, origin, deltaX, deltaY, gridSnap, objectSnap, } = params;
|
|
119
186
|
if (origin.kind === "zone") {
|
|
120
187
|
const { nextX, nextY } = resolveSnappedMove({
|
|
121
188
|
originX: origin.originX,
|
|
@@ -125,9 +192,18 @@ export function moveEditorTargetByScreenDelta(params) {
|
|
|
125
192
|
camera,
|
|
126
193
|
gridSnap,
|
|
127
194
|
});
|
|
128
|
-
|
|
195
|
+
const snapped = resolveObjectSnappedRectPosition({
|
|
129
196
|
x: nextX,
|
|
130
197
|
y: nextY,
|
|
198
|
+
width: origin.width,
|
|
199
|
+
height: origin.height,
|
|
200
|
+
camera,
|
|
201
|
+
guides: origin.objectSnapGuides,
|
|
202
|
+
objectSnap,
|
|
203
|
+
});
|
|
204
|
+
return updateZoneLayout(layoutModel, origin.zoneId, {
|
|
205
|
+
x: snapped.x,
|
|
206
|
+
y: snapped.y,
|
|
131
207
|
});
|
|
132
208
|
}
|
|
133
209
|
if (origin.kind === "zone-group") {
|
|
@@ -181,14 +257,76 @@ export function moveEditorTargetByScreenDelta(params) {
|
|
|
181
257
|
camera,
|
|
182
258
|
gridSnap,
|
|
183
259
|
});
|
|
260
|
+
const snapped = resolveObjectSnappedRectPosition({
|
|
261
|
+
x: nextX,
|
|
262
|
+
y: nextY,
|
|
263
|
+
width: origin.origin.width,
|
|
264
|
+
height: origin.origin.height,
|
|
265
|
+
camera,
|
|
266
|
+
guides: origin.objectSnapGuides,
|
|
267
|
+
objectSnap,
|
|
268
|
+
});
|
|
184
269
|
return applyPathMovePosition({
|
|
185
270
|
layoutModel,
|
|
186
271
|
pathId: origin.pathId,
|
|
187
272
|
origin: origin.origin,
|
|
188
|
-
x:
|
|
189
|
-
y:
|
|
273
|
+
x: snapped.x,
|
|
274
|
+
y: snapped.y,
|
|
190
275
|
});
|
|
191
276
|
}
|
|
277
|
+
export function resolveMoveEditorObjectSnapGuides(params) {
|
|
278
|
+
const { camera, origin, deltaX, deltaY, gridSnap, objectSnap } = params;
|
|
279
|
+
if (origin.kind === "zone") {
|
|
280
|
+
const { nextX, nextY } = resolveSnappedMove({
|
|
281
|
+
originX: origin.originX,
|
|
282
|
+
originY: origin.originY,
|
|
283
|
+
deltaX,
|
|
284
|
+
deltaY,
|
|
285
|
+
camera,
|
|
286
|
+
gridSnap,
|
|
287
|
+
});
|
|
288
|
+
const snapped = resolveObjectSnappedRectPosition({
|
|
289
|
+
x: nextX,
|
|
290
|
+
y: nextY,
|
|
291
|
+
width: origin.width,
|
|
292
|
+
height: origin.height,
|
|
293
|
+
camera,
|
|
294
|
+
guides: origin.objectSnapGuides,
|
|
295
|
+
objectSnap,
|
|
296
|
+
});
|
|
297
|
+
return {
|
|
298
|
+
guideX: snapped.guideX,
|
|
299
|
+
guideY: snapped.guideY,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
if (origin.kind === "path") {
|
|
303
|
+
const { nextX, nextY } = resolveSnappedMove({
|
|
304
|
+
originX: origin.origin.x,
|
|
305
|
+
originY: origin.origin.y,
|
|
306
|
+
deltaX,
|
|
307
|
+
deltaY,
|
|
308
|
+
camera,
|
|
309
|
+
gridSnap,
|
|
310
|
+
});
|
|
311
|
+
const snapped = resolveObjectSnappedRectPosition({
|
|
312
|
+
x: nextX,
|
|
313
|
+
y: nextY,
|
|
314
|
+
width: origin.origin.width,
|
|
315
|
+
height: origin.origin.height,
|
|
316
|
+
camera,
|
|
317
|
+
guides: origin.objectSnapGuides,
|
|
318
|
+
objectSnap,
|
|
319
|
+
});
|
|
320
|
+
return {
|
|
321
|
+
guideX: snapped.guideX,
|
|
322
|
+
guideY: snapped.guideY,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
return {
|
|
326
|
+
guideX: undefined,
|
|
327
|
+
guideY: undefined,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
192
330
|
export function resolveZoneResizeOrigin(layoutModel, zoneId) {
|
|
193
331
|
const zoneLayout = getZoneLayout(layoutModel, zoneId);
|
|
194
332
|
if (!zoneLayout)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zoneflow/editor-dom",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Low-level editor geometry and interaction helpers for Zoneflow.",
|
|
6
6
|
"type": "module",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"dist"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@zoneflow/core": "0.0.
|
|
23
|
-
"@zoneflow/renderer-dom": "0.0.
|
|
22
|
+
"@zoneflow/core": "0.0.7",
|
|
23
|
+
"@zoneflow/renderer-dom": "0.0.7"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "tsc -p tsconfig.json",
|