@witchcraft/layout 0.3.1 → 0.4.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.
- package/README.md +62 -70
- package/dist/module.json +1 -1
- package/dist/module.mjs +6 -1
- package/dist/runtime/components/FrameDragHandle.vue +1 -1
- package/dist/runtime/components/LayoutDecosEdges.d.vue.ts +15 -0
- package/dist/runtime/components/LayoutDecosEdges.vue +34 -0
- package/dist/runtime/components/LayoutDecosEdges.vue.d.ts +15 -0
- package/dist/runtime/components/LayoutDecosRects.d.vue.ts +15 -0
- package/dist/runtime/components/LayoutDecosRects.vue +37 -0
- package/dist/runtime/components/LayoutDecosRects.vue.d.ts +15 -0
- package/dist/runtime/components/LayoutDragEdgeGrabbed.d.vue.ts +2 -0
- package/dist/runtime/components/LayoutDragEdgeGrabbed.vue +43 -0
- package/dist/runtime/components/LayoutDragEdgeGrabbed.vue.d.ts +2 -0
- package/dist/runtime/components/LayoutDragEdgeHandle.d.vue.ts +9 -0
- package/dist/runtime/components/LayoutDragEdgeHandle.vue +44 -0
- package/dist/runtime/components/LayoutDragEdgeHandle.vue.d.ts +9 -0
- package/dist/runtime/components/{LayoutDecos.d.vue.ts → LayoutDragEdgeVisible.d.vue.ts} +3 -2
- package/dist/runtime/components/LayoutDragEdgeVisible.vue +38 -0
- package/dist/runtime/components/{LayoutDecos.vue.d.ts → LayoutDragEdgeVisible.vue.d.ts} +3 -2
- package/dist/runtime/components/LayoutDragEdges.d.vue.ts +21 -0
- package/dist/runtime/components/LayoutDragEdges.vue +67 -0
- package/dist/runtime/components/LayoutDragEdges.vue.d.ts +21 -0
- package/dist/runtime/components/LayoutEdgesActiveFrame.d.vue.ts +2 -0
- package/dist/runtime/components/LayoutEdgesActiveFrame.vue +35 -0
- package/dist/runtime/components/LayoutEdgesActiveFrame.vue.d.ts +2 -0
- package/dist/runtime/components/LayoutFrame.d.vue.ts +7 -6
- package/dist/runtime/components/LayoutFrame.vue +18 -14
- package/dist/runtime/components/LayoutFrame.vue.d.ts +7 -6
- package/dist/runtime/components/LayoutIntersectionHandle.d.vue.ts +10 -0
- package/dist/runtime/components/LayoutIntersectionHandle.vue +36 -0
- package/dist/runtime/components/LayoutIntersectionHandle.vue.d.ts +10 -0
- package/dist/runtime/components/LayoutIntersectionVisible.d.vue.ts +6 -0
- package/dist/runtime/components/LayoutIntersectionVisible.vue +34 -0
- package/dist/runtime/components/LayoutIntersectionVisible.vue.d.ts +6 -0
- package/dist/runtime/components/LayoutIntersections.d.vue.ts +21 -0
- package/dist/runtime/components/LayoutIntersections.vue +52 -0
- package/dist/runtime/components/LayoutIntersections.vue.d.ts +21 -0
- package/dist/runtime/components/{LayoutShapeSquare.d.vue.ts → LayoutShapeRect.d.vue.ts} +2 -2
- package/dist/runtime/components/{LayoutShapeSquare.vue.d.ts → LayoutShapeRect.vue.d.ts} +2 -2
- package/dist/runtime/components/LayoutWindow.d.vue.ts +252 -14
- package/dist/runtime/components/LayoutWindow.vue +27 -48
- package/dist/runtime/components/LayoutWindow.vue.d.ts +252 -14
- package/dist/runtime/composables/useFrames.d.ts +859 -2
- package/dist/runtime/composables/useFrames.js +2 -0
- package/dist/runtime/demo/App.vue +122 -74
- package/dist/runtime/demo/DemoControls.vue +7 -5
- package/dist/runtime/demo/index.html +12 -0
- package/dist/runtime/demo/tailwind.css +1 -1
- package/dist/runtime/drag/CloseAction.d.ts +1 -2
- package/dist/runtime/drag/CloseAction.js +1 -1
- package/dist/runtime/drag/DragActionHandler.d.ts +5 -3
- package/dist/runtime/drag/DragActionHandler.js +40 -6
- package/dist/runtime/drag/FrameDragAction.js +6 -7
- package/dist/runtime/drag/SplitAction.d.ts +1 -2
- package/dist/runtime/drag/SplitAction.js +1 -1
- package/dist/runtime/drag/createDefaultHandlers.d.ts +1 -1
- package/dist/runtime/drag/defaultDragActions.d.ts +1 -1
- package/dist/runtime/helpers/consoleDebugWindow.d.ts +43 -0
- package/dist/runtime/helpers/consoleDebugWindow.js +222 -0
- package/dist/runtime/helpers/convertLayoutWindowToWorkspace.js +2 -2
- package/dist/runtime/helpers/framesRedistributeFix.d.ts +87 -0
- package/dist/runtime/helpers/framesRedistributeFix.js +54 -0
- package/dist/runtime/helpers/getMoveEdgeInfo.d.ts +6 -2
- package/dist/runtime/helpers/getMoveEdgeInfo.js +11 -2
- package/dist/runtime/helpers/getPinnedEdgesForCollapsedFrames.d.ts +5 -0
- package/dist/runtime/helpers/getPinnedEdgesForCollapsedFrames.js +18 -0
- package/dist/runtime/helpers/getShapeRectCss.d.ts +7 -0
- package/dist/runtime/helpers/{getShapeSquareCss.js → getShapeRectCss.js} +1 -1
- package/dist/runtime/helpers/index.d.ts +3 -3
- package/dist/runtime/helpers/index.js +3 -3
- package/dist/runtime/helpers/moveEdge.js +3 -1
- package/dist/runtime/helpers/pxSizeToScaledSize.d.ts +2 -0
- package/dist/runtime/helpers/{numberToScaledSize.js → pxSizeToScaledSize.js} +1 -1
- package/dist/runtime/helpers/updateWindowSizeWithEvent.d.ts +3 -0
- package/dist/runtime/helpers/validateLayoutShape.d.ts +5 -0
- package/dist/runtime/helpers/validateLayoutShape.js +28 -0
- package/dist/runtime/layout/applyFrameChanges.js +6 -0
- package/dist/runtime/layout/createSplitDecoShapes.js +1 -1
- package/dist/runtime/layout/getFrameCollapseInfo.d.ts +5 -2
- package/dist/runtime/layout/getFrameCollapseInfo.js +16 -46
- package/dist/runtime/layout/getFrameDockInfo.d.ts +1 -1
- package/dist/runtime/layout/getFrameDockInfo.js +5 -1
- package/dist/runtime/layout/getFrameUncollapseInfo.d.ts +5 -2
- package/dist/runtime/layout/getFrameUncollapseInfo.js +16 -50
- package/dist/runtime/layout/getFramesRedistributeInfo.d.ts +20 -4
- package/dist/runtime/layout/getFramesRedistributeInfo.js +31 -2
- package/dist/runtime/layout/getRelaxFramesInfo.d.ts +7 -0
- package/dist/runtime/layout/getRelaxFramesInfo.js +91 -0
- package/dist/runtime/layout/getUpdateWindowSizeInfo.d.ts +13 -0
- package/dist/runtime/layout/getUpdateWindowSizeInfo.js +65 -0
- package/dist/runtime/layout/index.d.ts +1 -0
- package/dist/runtime/layout/index.js +1 -0
- package/dist/runtime/layout/resizeFrame.js +1 -3
- package/dist/runtime/settings.d.ts +26 -19
- package/dist/runtime/settings.js +57 -22
- package/dist/runtime/types/index.d.ts +1243 -30
- package/dist/runtime/types/index.js +22 -8
- package/dist/runtime/types/vue.d.ts +41 -0
- package/dist/runtime/{drag/types.js → types/vue.js} +1 -0
- package/dist/runtime/utils/cssToKey.d.ts +5 -0
- package/dist/runtime/utils/cssToKey.js +3 -0
- package/package.json +11 -12
- package/src/module.ts +6 -1
- package/src/runtime/components/FrameDragHandle.vue +1 -1
- package/src/runtime/components/LayoutDecosEdges.vue +38 -0
- package/src/runtime/components/LayoutDecosRects.vue +42 -0
- package/src/runtime/components/LayoutDragEdgeGrabbed.vue +50 -0
- package/src/runtime/components/LayoutDragEdgeHandle.vue +52 -0
- package/src/runtime/components/LayoutDragEdgeVisible.vue +45 -0
- package/src/runtime/components/LayoutDragEdges.vue +78 -0
- package/src/runtime/components/LayoutEdgesActiveFrame.vue +43 -0
- package/src/runtime/components/LayoutFrame.vue +22 -15
- package/src/runtime/components/LayoutIntersectionHandle.vue +45 -0
- package/src/runtime/components/LayoutIntersectionVisible.vue +41 -0
- package/src/runtime/components/LayoutIntersections.vue +64 -0
- package/src/runtime/components/{LayoutShapeSquare.vue → LayoutShapeRect.vue} +2 -2
- package/src/runtime/components/LayoutWindow.vue +29 -49
- package/src/runtime/composables/useFrames.ts +7 -2
- package/src/runtime/demo/App.vue +128 -78
- package/src/runtime/demo/DemoControls.vue +7 -5
- package/src/runtime/demo/index.html +12 -0
- package/src/runtime/demo/tailwind.css +3 -0
- package/src/runtime/drag/CloseAction.ts +2 -3
- package/src/runtime/drag/DragActionHandler.ts +48 -10
- package/src/runtime/drag/FrameDragAction.ts +4 -5
- package/src/runtime/drag/SplitAction.ts +4 -3
- package/src/runtime/drag/createDefaultHandlers.ts +2 -1
- package/src/runtime/drag/defaultDragActions.ts +2 -1
- package/src/runtime/helpers/consoleDebugWindow.ts +331 -0
- package/src/runtime/helpers/convertLayoutWindowToWorkspace.ts +2 -2
- package/src/runtime/helpers/framesRedistributeFix.ts +162 -0
- package/src/runtime/helpers/getMoveEdgeInfo.ts +22 -5
- package/src/runtime/helpers/getPinnedEdgesForCollapsedFrames.ts +29 -0
- package/src/runtime/helpers/{getShapeSquareCss.ts → getShapeRectCss.ts} +3 -3
- package/src/runtime/helpers/index.ts +4 -3
- package/src/runtime/helpers/moveEdge.ts +3 -1
- package/src/runtime/helpers/{numberToScaledSize.ts → pxSizeToScaledSize.ts} +2 -2
- package/src/runtime/helpers/updateWindowSizeWithEvent.ts +3 -0
- package/src/runtime/helpers/validateLayoutShape.ts +46 -0
- package/src/runtime/layout/applyFrameChanges.ts +5 -0
- package/src/runtime/layout/createSplitDecoShapes.ts +1 -1
- package/src/runtime/layout/getFrameCollapseInfo.ts +26 -85
- package/src/runtime/layout/getFrameDockInfo.ts +8 -1
- package/src/runtime/layout/getFrameUncollapseInfo.ts +29 -96
- package/src/runtime/layout/getFramesRedistributeInfo.ts +60 -5
- package/src/runtime/layout/getRelaxFramesInfo.ts +139 -0
- package/src/runtime/layout/getUpdateWindowSizeInfo.ts +105 -0
- package/src/runtime/layout/index.ts +1 -0
- package/src/runtime/layout/resizeFrame.ts +4 -3
- package/src/runtime/settings.ts +78 -25
- package/src/runtime/types/index.ts +205 -33
- package/src/runtime/types/vue.ts +41 -0
- package/src/runtime/utils/cssToKey.ts +8 -0
- package/dist/runtime/components/LayoutDecos.vue +0 -26
- package/dist/runtime/components/LayoutEdges.d.vue.ts +0 -17
- package/dist/runtime/components/LayoutEdges.vue +0 -144
- package/dist/runtime/components/LayoutEdges.vue.d.ts +0 -17
- package/dist/runtime/drag/types.d.ts +0 -161
- package/dist/runtime/helpers/clampNumber.d.ts +0 -1
- package/dist/runtime/helpers/clampNumber.js +0 -3
- package/dist/runtime/helpers/getShapeSquareCss.d.ts +0 -7
- package/dist/runtime/helpers/numberToScaledSize.d.ts +0 -2
- package/src/runtime/components/LayoutDecos.vue +0 -28
- package/src/runtime/components/LayoutEdges.vue +0 -170
- package/src/runtime/drag/types.ts +0 -177
- package/src/runtime/helpers/clampNumber.ts +0 -9
- /package/dist/runtime/components/{LayoutShapeSquare.vue → LayoutShapeRect.vue} +0 -0
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
import { enumFromArray } from "@alanscodelog/utils/enumFromArray";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
|
|
4
|
-
import { settings } from "../settings.js";
|
|
3
|
+
import { KnownError } from "../utils/KnownError.js";
|
|
5
4
|
export const zUuid = z.uuid();
|
|
6
5
|
export const zWindowIdConstants = z.enum(["ACTIVE"]);
|
|
7
6
|
export const zWinId = z.uuid().or(zWindowIdConstants);
|
|
8
7
|
export const zFrameIdConstants = z.enum(["ACTIVE"]);
|
|
9
8
|
export const zFrameId = z.uuid().or(zFrameIdConstants);
|
|
10
|
-
|
|
9
|
+
import { settings } from "../settings.js";
|
|
10
|
+
export const zScaledIntPercentage = z.number().int().min(0).nonnegative().superRefine((val, ctx) => {
|
|
11
|
+
if (val > settings.maxInt) {
|
|
12
|
+
ctx.addIssue({
|
|
13
|
+
code: "too_big",
|
|
14
|
+
type: "number",
|
|
15
|
+
origin: "number",
|
|
16
|
+
maximum: settings.maxInt,
|
|
17
|
+
inclusive: true,
|
|
18
|
+
message: `Value ${val} must be less than or equal to ${settings.maxInt}`
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
});
|
|
11
22
|
const zPx = z.number().int().nonnegative();
|
|
12
23
|
export const zPos = z.object({
|
|
13
24
|
x: zScaledIntPercentage,
|
|
@@ -33,15 +44,16 @@ export const zEdge = z.object({
|
|
|
33
44
|
startX: zScaledIntPercentage,
|
|
34
45
|
startY: zScaledIntPercentage,
|
|
35
46
|
endX: zScaledIntPercentage,
|
|
36
|
-
endY: zScaledIntPercentage
|
|
47
|
+
endY: zScaledIntPercentage,
|
|
48
|
+
error: z.instanceof(KnownError).or(z.object({ code: z.string(), message: z.string() })).optional()
|
|
37
49
|
});
|
|
38
50
|
export const zDirection = z.enum(["up", "down", "left", "right"]);
|
|
39
51
|
export const zExtendedDirection = z.union([zDirection, z.enum(["horizontal", "vertical"])]);
|
|
40
52
|
export const zSide = z.enum(["top", "bottom", "left", "right"]);
|
|
41
53
|
export const zOrientation = z.enum(["horizontal", "vertical"]);
|
|
42
54
|
export const zExtendedSide = z.union([zSide, zOrientation]);
|
|
43
|
-
export const
|
|
44
|
-
export const zLayoutFrame =
|
|
55
|
+
export const zBaseRect = zSize.extend(zPos.shape);
|
|
56
|
+
export const zLayoutFrame = zBaseRect.extend({
|
|
45
57
|
id: z.uuid(),
|
|
46
58
|
docked: zSide.optional(),
|
|
47
59
|
collapsed: z.number().min(1).optional()
|
|
@@ -88,7 +100,7 @@ export const zInitializedLayout = zLayout.required({
|
|
|
88
100
|
return zLayoutWindow.safeParse(layout.windows[layout.activeWindow]).success;
|
|
89
101
|
}, { message: "An initialized layout's active window must exist and be valid." });
|
|
90
102
|
const zLayoutShape = z.discriminatedUnion("type", [
|
|
91
|
-
z.object({ type: z.literal("
|
|
103
|
+
z.object({ type: z.literal("rect"), data: zBaseRect, attrs: z.record(z.string(), z.string()).optional() }),
|
|
92
104
|
z.object({ type: z.literal("edge"), data: zEdge, attrs: z.record(z.string(), z.string()).optional() })
|
|
93
105
|
]);
|
|
94
106
|
const zBaseDeco = z.object({
|
|
@@ -134,12 +146,14 @@ export const LAYOUT_ERROR = enumFromArray([
|
|
|
134
146
|
"CANT_SPLIT_DOCKED_FRAME",
|
|
135
147
|
"NO_SPACE_TO_REDISTRIBUTE",
|
|
136
148
|
"REDISTRIBUTE_OUT_OF_BOUNDS",
|
|
149
|
+
"REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES",
|
|
137
150
|
"FRAME_ALREADY_DOCKED_ON_SIDE",
|
|
138
151
|
"CANT_LEAVE_NO_UNDOCKED_FRAMES",
|
|
139
152
|
"CANT_UNDOCK_COLLAPSED_FRAME",
|
|
140
153
|
"CANT_COLLAPSE_NOT_DOCKED",
|
|
141
154
|
"CANT_UNCOLLAPSE_NOT_COLLAPSED",
|
|
142
|
-
"NO_FILL_CANDIDATES"
|
|
155
|
+
"NO_FILL_CANDIDATES",
|
|
156
|
+
"CANT_RESIZE_COLLAPSED_FRAME"
|
|
143
157
|
]);
|
|
144
158
|
export const zWindowCreate = zLayoutWindowLoose.partial({ id: true, pxWidth: true, pxHeight: true, pxX: true, pxY: true }).extend({ frames: zLayoutWindow.shape.frames.optional() });
|
|
145
159
|
export const zLayoutCreate = baseLayout.loose().extend({
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ComputedRef, InjectionKey, Ref } from "vue";
|
|
2
|
+
import type { Direction, DragState, Edge, FrameId, IntersectionEntry, LayoutFrame, LayoutShape, LayoutWindow, Orientation, Point } from "./index.js";
|
|
3
|
+
export type LayoutContext = ComputedRef<{
|
|
4
|
+
/** The owning window, needed so we can correctly scale coordinates. */
|
|
5
|
+
win: LayoutWindow;
|
|
6
|
+
onFocus: (frameId: string) => void;
|
|
7
|
+
shapes: LayoutShape[];
|
|
8
|
+
}>;
|
|
9
|
+
export declare const layoutContextInjectionKey: InjectionKey<LayoutContext>;
|
|
10
|
+
export interface UseFramesContext {
|
|
11
|
+
dragStart: {
|
|
12
|
+
(e: PointerEvent, type: "edge", data: {
|
|
13
|
+
edge?: Edge;
|
|
14
|
+
intersection?: IntersectionEntry;
|
|
15
|
+
}): void;
|
|
16
|
+
(e: PointerEvent, type: "frame", data: {
|
|
17
|
+
frameId: FrameId;
|
|
18
|
+
}): void;
|
|
19
|
+
};
|
|
20
|
+
dragMove: (e: PointerEvent) => void;
|
|
21
|
+
dragEnd: (e?: PointerEvent, options?: {
|
|
22
|
+
apply?: boolean;
|
|
23
|
+
}) => void;
|
|
24
|
+
cancel: () => void;
|
|
25
|
+
dragDirections: Ref<Record<Orientation, Direction | undefined>>;
|
|
26
|
+
dragPoint: Ref<Point | undefined>;
|
|
27
|
+
isDragging: Ref<false | "frame" | "edge">;
|
|
28
|
+
draggingEdges: Ref<Edge[]>;
|
|
29
|
+
draggingIntersection: Ref<IntersectionEntry | undefined>;
|
|
30
|
+
visualEdges: Ref<Edge[]>;
|
|
31
|
+
touchingFrames: Ref<Record<string, LayoutFrame>[]>;
|
|
32
|
+
touchingFramesArrays: Ref<LayoutFrame[][]>;
|
|
33
|
+
frames: ComputedRef<Record<string, LayoutFrame>>;
|
|
34
|
+
dragHoveredFrame: ComputedRef<LayoutFrame | undefined>;
|
|
35
|
+
intersections: ComputedRef<IntersectionEntry[]>;
|
|
36
|
+
isDraggingFromWindowEdge: Ref<boolean>;
|
|
37
|
+
forceRecalculateEdges: () => void;
|
|
38
|
+
state: ComputedRef<DragState>;
|
|
39
|
+
frameDragFrameId: Ref<FrameId | undefined>;
|
|
40
|
+
}
|
|
41
|
+
export declare const dragContextInjectionKey: InjectionKey<UseFramesContext>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@witchcraft/layout",
|
|
3
3
|
"description": "Headless layout manager.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"main": "./dist/runtime/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -28,13 +28,12 @@
|
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@alanscodelog/utils": "^6.
|
|
32
|
-
"@iconify/json": "^2.2.474"
|
|
33
|
-
"es-toolkit": "^1.46.1"
|
|
31
|
+
"@alanscodelog/utils": "^6.3.0",
|
|
32
|
+
"@iconify/json": "^2.2.474"
|
|
34
33
|
},
|
|
35
34
|
"peerDependencies": {
|
|
36
|
-
"@witchcraft/nuxt-utils": "^0.3.
|
|
37
|
-
"@witchcraft/ui": "^0.5.
|
|
35
|
+
"@witchcraft/nuxt-utils": "^0.3.10",
|
|
36
|
+
"@witchcraft/ui": "^0.5.9"
|
|
38
37
|
},
|
|
39
38
|
"peerDependenciesMeta": {
|
|
40
39
|
"@witchcraft/ui": {
|
|
@@ -60,8 +59,8 @@
|
|
|
60
59
|
"@types/node": "^24.12.4",
|
|
61
60
|
"@vitejs/plugin-vue": "^6.0.7",
|
|
62
61
|
"@vitest/coverage-v8": "^4.1.0",
|
|
63
|
-
"@witchcraft/nuxt-utils": "^0.3.
|
|
64
|
-
"@witchcraft/ui": "^0.5.
|
|
62
|
+
"@witchcraft/nuxt-utils": "^0.3.10",
|
|
63
|
+
"@witchcraft/ui": "^0.5.9",
|
|
65
64
|
"concurrently": "^9.2.1",
|
|
66
65
|
"cross-env": "^10.1.0",
|
|
67
66
|
"fast-glob": "^3.3.3",
|
|
@@ -128,10 +127,10 @@
|
|
|
128
127
|
"build:only": "nuxt-module-build build",
|
|
129
128
|
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
|
|
130
129
|
"dev:playground": "nuxi dev playground",
|
|
131
|
-
"//dev:vite": "check everything is working when using vite",
|
|
132
|
-
"dev:vite": "vite --mode development --config vite.dev.config.ts",
|
|
130
|
+
"//dev:vite:vue": "check everything is working when using vite (Vue demo)",
|
|
131
|
+
"dev:vite:vue": "vite --mode development --config vite.dev.vue.config.ts",
|
|
133
132
|
"//dev": "The module needs to be build, not stubbed, for vite's dev to work.",
|
|
134
|
-
"dev": "pnpm dev:vite",
|
|
133
|
+
"dev": "pnpm dev:vite:vue",
|
|
135
134
|
"lint:eslint": "eslint \"{src,tests,bin}/**/*.{cjs,js,ts}\" \"*.{cjs,js,ts}\" --max-warnings=0 --report-unused-disable-directives",
|
|
136
135
|
"lint:commits": "commitlint --from-last-tag --to HEAD --verbose",
|
|
137
136
|
"lint:imports": "echo disabled madge --circular --extensions ts ./src",
|
|
@@ -139,7 +138,7 @@
|
|
|
139
138
|
"lint:types": "vue-tsc --noEmit",
|
|
140
139
|
"coverage": "vitest --coverage",
|
|
141
140
|
"coverage:dev": "vitest --watch --coverage",
|
|
142
|
-
"test": "vitest",
|
|
141
|
+
"test": "vitest --config vite.dev.vue.config.ts",
|
|
143
142
|
"doc": "typedoc",
|
|
144
143
|
"doc:watch": "onchange -i \"src/**/*.ts\" \"typedoc.config.js\" -- pnpm doc",
|
|
145
144
|
"doc:serve": "http-server docs --port=5001",
|
package/src/module.ts
CHANGED
|
@@ -35,7 +35,12 @@ export default defineNuxtModule<ModuleOptions>({
|
|
|
35
35
|
addTemplate({
|
|
36
36
|
filename: "witchcraft-layout.css",
|
|
37
37
|
write: true,
|
|
38
|
-
getContents: () =>
|
|
38
|
+
getContents: () => [
|
|
39
|
+
`@source "${resolve("runtime/components")}";`,
|
|
40
|
+
// the drag actions handlers and utils they use add some styles
|
|
41
|
+
`@source "${resolve("runtime/drag")}";`,
|
|
42
|
+
`@source "${resolve("runtime/utils/createSplitDecoShapes")}";`
|
|
43
|
+
].join("\n")
|
|
39
44
|
})
|
|
40
45
|
|
|
41
46
|
nuxt.options.alias["#witchcraft-layout"] = resolve("runtime")
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
</template>
|
|
9
9
|
<script lang="ts" setup>
|
|
10
10
|
import { inject, onBeforeUnmount } from "vue"
|
|
11
|
-
import { dragContextInjectionKey } from "../
|
|
11
|
+
import { dragContextInjectionKey } from "../types/vue.js"
|
|
12
12
|
|
|
13
13
|
const props = defineProps<{
|
|
14
14
|
frameId: string
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<template
|
|
3
|
+
v-for="(shape, i) of ctx.shapes.filter(_ => _.type === 'edge')"
|
|
4
|
+
:key="cssToKey(shape.data)"
|
|
5
|
+
>
|
|
6
|
+
|
|
7
|
+
<slot
|
|
8
|
+
name="edge"
|
|
9
|
+
v-bind="{
|
|
10
|
+
shape,
|
|
11
|
+
css: getVisualEdgeCss(shape.data, { padLongAxis: 'var(--layoutEdgeWidth,2px)' })
|
|
12
|
+
}"
|
|
13
|
+
>
|
|
14
|
+
<LayoutShapeRect
|
|
15
|
+
:css="getVisualEdgeCss(shape.data, { padLongAxis: 'var(--layoutEdgeWidth,2px)' })"
|
|
16
|
+
:class="twMerge(shape.attrs?.class, ($attrs as any).class)"
|
|
17
|
+
v-bind="{...shape.attrs, ...$attrs, class: undefined }"
|
|
18
|
+
|
|
19
|
+
/>
|
|
20
|
+
</slot>
|
|
21
|
+
</template>
|
|
22
|
+
</template>
|
|
23
|
+
<script lang="ts" setup>
|
|
24
|
+
import LayoutShapeRect from "./LayoutShapeRect.vue"
|
|
25
|
+
|
|
26
|
+
import { getVisualEdgeCss } from "../helpers/getVisualEdgeCss"
|
|
27
|
+
import { layoutContextInjectionKey } from "../types/vue.js"
|
|
28
|
+
import { useAttrs, inject } from "vue"
|
|
29
|
+
import {cssToKey} from "../utils/cssToKey.js"
|
|
30
|
+
import { twMerge } from "@witchcraft/ui/utils/twMerge"
|
|
31
|
+
|
|
32
|
+
const $attrs = useAttrs()
|
|
33
|
+
|
|
34
|
+
const ctx = inject(layoutContextInjectionKey, undefined)
|
|
35
|
+
if (!ctx) throw new Error("LayoutEdges must be used within a LayoutWindow")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
</script>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<template
|
|
3
|
+
v-for="(shape) of ctx.shapes.filter(_ => _.type === 'rect')"
|
|
4
|
+
:key="cssToKey(shape.data)"
|
|
5
|
+
>
|
|
6
|
+
<slot
|
|
7
|
+
name="rect"
|
|
8
|
+
v-bind="{
|
|
9
|
+
shape,
|
|
10
|
+
css: getShapeRectCss(shape.data, `var(--layoutEdgeWidth,2px)`)
|
|
11
|
+
}"
|
|
12
|
+
>
|
|
13
|
+
<LayoutShapeRect
|
|
14
|
+
v-if="shape.type === 'rect'"
|
|
15
|
+
:css="getShapeRectCss(shape.data, `var(--layoutEdgeWidth,2px)`)"
|
|
16
|
+
:class="twMerge(`bg-neutral-500/20`,shape.attrs?.class, ($attrs as any).class)"
|
|
17
|
+
v-bind="{...shape.attrs, ...$attrs, class: undefined }"
|
|
18
|
+
/>
|
|
19
|
+
</slot>
|
|
20
|
+
|
|
21
|
+
</template>
|
|
22
|
+
</template>
|
|
23
|
+
<script lang="ts" setup>
|
|
24
|
+
import LayoutShapeRect from "./LayoutShapeRect.vue"
|
|
25
|
+
|
|
26
|
+
import { getShapeRectCss } from "../helpers/getShapeRectCss"
|
|
27
|
+
import { layoutContextInjectionKey } from "../types/vue.js"
|
|
28
|
+
import { useAttrs, inject } from "vue"
|
|
29
|
+
import { cssToKey } from "../utils/cssToKey.js"
|
|
30
|
+
import { twMerge } from "@witchcraft/ui/utils/twMerge"
|
|
31
|
+
|
|
32
|
+
const $attrs = useAttrs()
|
|
33
|
+
|
|
34
|
+
defineOptions({
|
|
35
|
+
inheritAttrs: false
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
const ctx = inject(layoutContextInjectionKey, undefined)
|
|
40
|
+
if (!ctx) throw new Error("LayoutEdges must be used within a LayoutWindow")
|
|
41
|
+
|
|
42
|
+
</script>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<LayoutShapeRect
|
|
3
|
+
:css="css"
|
|
4
|
+
:class="twMerge(`
|
|
5
|
+
drag-edge-grabbed
|
|
6
|
+
z-20
|
|
7
|
+
bg-accent-500
|
|
8
|
+
cursor-pointer
|
|
9
|
+
[&.request-split]:hidden
|
|
10
|
+
`,
|
|
11
|
+
($attrs as any).class
|
|
12
|
+
)"
|
|
13
|
+
v-bind="{ ...$attrs, class: undefined }"
|
|
14
|
+
v-for="css, i of cssDragEdge"
|
|
15
|
+
:key="cssToKey(css)"
|
|
16
|
+
/>
|
|
17
|
+
</template>
|
|
18
|
+
<script lang="ts" setup>
|
|
19
|
+
import { dragContextInjectionKey, layoutContextInjectionKey } from "../types/vue.js"
|
|
20
|
+
import { twMerge } from "@witchcraft/ui/utils/twMerge"
|
|
21
|
+
import { computed, inject, useAttrs } from "vue"
|
|
22
|
+
|
|
23
|
+
import LayoutShapeRect from "./LayoutShapeRect.vue"
|
|
24
|
+
import { getVisualEdgesCss } from "../helpers/getVisualEdgesCss.js"
|
|
25
|
+
import { cssToKey } from "../utils/cssToKey.js"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
const $attrs = useAttrs()
|
|
29
|
+
|
|
30
|
+
defineOptions({
|
|
31
|
+
inheritAttrs: false
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const ctx = inject(layoutContextInjectionKey, undefined)
|
|
35
|
+
if (!ctx) throw new Error("LayoutEdges must be used within a LayoutWindow")
|
|
36
|
+
|
|
37
|
+
const dragCtx = inject(dragContextInjectionKey, undefined)
|
|
38
|
+
if (!dragCtx) throw new Error("LayoutEdges must be used within a LayoutWindow")
|
|
39
|
+
|
|
40
|
+
const draggingEdge = computed(() => dragCtx.draggingEdges.value.length === 1 ? dragCtx.draggingEdges.value[0] : undefined)
|
|
41
|
+
|
|
42
|
+
const cssDragEdge = computed(() => {
|
|
43
|
+
if (!draggingEdge.value) return []
|
|
44
|
+
return getVisualEdgesCss([draggingEdge.value], {
|
|
45
|
+
edgeWidth: `var(--layoutEdgeWidth, 2px)`,
|
|
46
|
+
padLongAxis: `(var(--layoutEdgeWidth, 2px) + var(--layoutExtraDragEdgePadding, 0px))`
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
</script>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<LayoutShapeRect
|
|
3
|
+
:css="css"
|
|
4
|
+
:title="edge.error?.message"
|
|
5
|
+
:class="twMerge(
|
|
6
|
+
`
|
|
7
|
+
drag-edge
|
|
8
|
+
z-20
|
|
9
|
+
hover:cursor-pointer
|
|
10
|
+
|
|
11
|
+
[&.request-split]:hidden
|
|
12
|
+
`,
|
|
13
|
+
edge.error && `
|
|
14
|
+
drag-edge-errored
|
|
15
|
+
hover:cursor-not-allowed
|
|
16
|
+
`,
|
|
17
|
+
($attrs as any).class
|
|
18
|
+
)"
|
|
19
|
+
v-bind="{ ...$attrs, class: undefined }"
|
|
20
|
+
@pointerdown="onPointerDown?.($event, 'edge', { edge })"
|
|
21
|
+
/>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script lang="ts" setup>
|
|
25
|
+
import { layoutContextInjectionKey } from "../types/vue.js"
|
|
26
|
+
import { twMerge } from "@witchcraft/ui/utils/twMerge"
|
|
27
|
+
import { useAttrs, inject } from "vue"
|
|
28
|
+
|
|
29
|
+
import LayoutShapeRect from "./LayoutShapeRect.vue"
|
|
30
|
+
import { type EdgeDragStartData } from "../types/index.js"
|
|
31
|
+
import { dragContextInjectionKey } from "../types/vue.js"
|
|
32
|
+
import type { Edge, EdgeCss } from "../types/index.js"
|
|
33
|
+
|
|
34
|
+
const $attrs = useAttrs()
|
|
35
|
+
|
|
36
|
+
defineOptions({
|
|
37
|
+
inheritAttrs: false
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const props = defineProps<{
|
|
41
|
+
edge: Edge
|
|
42
|
+
css: EdgeCss
|
|
43
|
+
onPointerDown?: (e: PointerEvent, type: "edge", data: EdgeDragStartData) => void
|
|
44
|
+
}>()
|
|
45
|
+
|
|
46
|
+
const ctx = inject(layoutContextInjectionKey, undefined)
|
|
47
|
+
if (!ctx) throw new Error("LayoutEdges must be used within a LayoutWindow")
|
|
48
|
+
|
|
49
|
+
const dragCtx = inject(dragContextInjectionKey, undefined)
|
|
50
|
+
if (!dragCtx) throw new Error("LayoutEdges must be used within a LayoutWindow")
|
|
51
|
+
|
|
52
|
+
</script>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<LayoutShapeRect
|
|
3
|
+
:class="twMerge(
|
|
4
|
+
`
|
|
5
|
+
visible-drag-edge
|
|
6
|
+
pointer-events-none
|
|
7
|
+
[.drag-edge:hover+&]:bg-accent-500/60
|
|
8
|
+
`,
|
|
9
|
+
edge.error && `
|
|
10
|
+
visible-drag-edge-errored
|
|
11
|
+
[.drag-edge:hover+&]:bg-red-500/50
|
|
12
|
+
[.drag-edge:hover+&]:cursor-not-allowed
|
|
13
|
+
|
|
14
|
+
`,
|
|
15
|
+
($attrs as any).class
|
|
16
|
+
)"
|
|
17
|
+
:css="css"
|
|
18
|
+
v-bind="{ ...$attrs, class: undefined }"
|
|
19
|
+
/>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script lang="ts" setup>
|
|
23
|
+
import { twMerge } from "@witchcraft/ui/utils/twMerge"
|
|
24
|
+
import { useAttrs, inject } from "vue"
|
|
25
|
+
|
|
26
|
+
import LayoutShapeRect from "./LayoutShapeRect.vue"
|
|
27
|
+
import { type Edge, type EdgeCss } from "../types/index.js"
|
|
28
|
+
import { layoutContextInjectionKey } from "../types/vue.js"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
const $attrs = useAttrs()
|
|
32
|
+
|
|
33
|
+
defineOptions({
|
|
34
|
+
inheritAttrs: false
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
defineProps<{
|
|
38
|
+
edge: Edge
|
|
39
|
+
css: EdgeCss
|
|
40
|
+
}>()
|
|
41
|
+
|
|
42
|
+
const ctx = inject(layoutContextInjectionKey, undefined)
|
|
43
|
+
if (!ctx) throw new Error("LayoutEdges must be used within a LayoutWindow")
|
|
44
|
+
|
|
45
|
+
</script>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<template
|
|
3
|
+
v-for="css, i in cssDragEdges"
|
|
4
|
+
:key="cssToKey(css.handle)"
|
|
5
|
+
>
|
|
6
|
+
<slot name="handle" v-bind="{
|
|
7
|
+
css: css.handle,
|
|
8
|
+
edge: visualEdges[i],
|
|
9
|
+
onPointerDown: dragCtx.dragStart
|
|
10
|
+
}">
|
|
11
|
+
<LayoutDragEdgeHandle
|
|
12
|
+
:css="css.handle"
|
|
13
|
+
:edge="visualEdges[i]"
|
|
14
|
+
@pointerdown="dragCtx.dragStart"
|
|
15
|
+
/>
|
|
16
|
+
</slot>
|
|
17
|
+
<slot name="indicator" v-bind="{
|
|
18
|
+
css: css.visible,
|
|
19
|
+
edge: visualEdges[i],
|
|
20
|
+
}">
|
|
21
|
+
<LayoutDragEdgeVisible
|
|
22
|
+
:edge="visualEdges[i]"
|
|
23
|
+
:css="css.visible"
|
|
24
|
+
/>
|
|
25
|
+
</slot>
|
|
26
|
+
</template>
|
|
27
|
+
</template>
|
|
28
|
+
<script lang="ts" setup>
|
|
29
|
+
import { layoutContextInjectionKey } from "../types/vue.js"
|
|
30
|
+
|
|
31
|
+
import { computed, inject } from "vue"
|
|
32
|
+
|
|
33
|
+
import LayoutDragEdgeHandle from "./LayoutDragEdgeHandle.vue"
|
|
34
|
+
import LayoutDragEdgeVisible from "./LayoutDragEdgeVisible.vue"
|
|
35
|
+
import { getVisualEdgesCss } from "../helpers/getVisualEdgesCss.js"
|
|
36
|
+
import { isEdgeEqual } from "../helpers/isEdgeEqual.js"
|
|
37
|
+
import { toRef } from "vue"
|
|
38
|
+
import {cssToKey} from "../utils/cssToKey.js"
|
|
39
|
+
import { type EdgeCss } from "../types/index.js"
|
|
40
|
+
import { dragContextInjectionKey } from "../types/vue.js"
|
|
41
|
+
|
|
42
|
+
defineOptions({
|
|
43
|
+
inheritAttrs: false
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const ctx = inject(layoutContextInjectionKey, undefined)
|
|
47
|
+
if (!ctx) throw new Error("LayoutEdges must be used wivisible a LayoutWindow")
|
|
48
|
+
|
|
49
|
+
const dragCtx = inject(dragContextInjectionKey, undefined)
|
|
50
|
+
if (!dragCtx) throw new Error("LayoutEdges must be used within a LayoutWindow")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
const visualEdges = toRef(dragCtx, "visualEdges")
|
|
54
|
+
|
|
55
|
+
const draggingEdge = computed(() => dragCtx.draggingEdges.value.length === 1 ? dragCtx.draggingEdges.value[0] : undefined)
|
|
56
|
+
|
|
57
|
+
const cssDragEdges = computed(() => {
|
|
58
|
+
const handleEdges = getVisualEdgesCss(
|
|
59
|
+
draggingEdge.value
|
|
60
|
+
? dragCtx.visualEdges.value.filter(_ => !isEdgeEqual(draggingEdge.value!, _))
|
|
61
|
+
: dragCtx.visualEdges.value,
|
|
62
|
+
{
|
|
63
|
+
edgeWidth: `var(--layoutHandleWidth, 8px)`,
|
|
64
|
+
padLongAxis: `var(--layoutEdgeWidth, 2px)`
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
const visibleEdges = getVisualEdgesCss(dragCtx.visualEdges.value, {
|
|
68
|
+
edgeWidth: `var(--layoutEdgeWidth, 2px)`,
|
|
69
|
+
padLongAxis: `(var(--layoutEdgeWidth, 2px) + var(--layoutExtraDragEdgePadding, 0px))`
|
|
70
|
+
})
|
|
71
|
+
const edges: { visible: EdgeCss, handle: EdgeCss }[] = []
|
|
72
|
+
for (let i = 0; i < handleEdges.length; i++) {
|
|
73
|
+
edges.push({ visible: visibleEdges[i], handle: handleEdges[i] })
|
|
74
|
+
}
|
|
75
|
+
return edges
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
</script>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<LayoutShapeRect
|
|
3
|
+
v-if="activeFrame"
|
|
4
|
+
:class="twMerge(`
|
|
5
|
+
active-frame-edge
|
|
6
|
+
pointer-events-none
|
|
7
|
+
z-0
|
|
8
|
+
border-blue-500
|
|
9
|
+
border
|
|
10
|
+
rounded-md
|
|
11
|
+
`,
|
|
12
|
+
($attrs as any).class
|
|
13
|
+
)"
|
|
14
|
+
v-bind="{ ...$attrs, class: undefined }"
|
|
15
|
+
:css="getShapeRectCss(activeFrame, `var(--layoutEdgeWidth,2px)`)"
|
|
16
|
+
/>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script lang="ts" setup>
|
|
20
|
+
import { dragContextInjectionKey, layoutContextInjectionKey } from "../types/vue.js"
|
|
21
|
+
import { computed, inject, useAttrs } from "vue"
|
|
22
|
+
import { twMerge } from "@witchcraft/ui/utils/twMerge"
|
|
23
|
+
|
|
24
|
+
import LayoutShapeRect from "./LayoutShapeRect.vue"
|
|
25
|
+
import { getShapeRectCss } from "../helpers/getShapeRectCss.js"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
const $attrs = useAttrs()
|
|
29
|
+
|
|
30
|
+
defineOptions({
|
|
31
|
+
inheritAttrs: false
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const ctx = inject(layoutContextInjectionKey, undefined)
|
|
35
|
+
if (!ctx) throw new Error("LayoutEdges must be used within a LayoutWindow")
|
|
36
|
+
|
|
37
|
+
const dragCtx = inject(dragContextInjectionKey, undefined)
|
|
38
|
+
if (!dragCtx) throw new Error("LayoutEdges must be used within a LayoutWindow")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
const activeFrame = computed(() => ctx.value.win.activeFrame ? dragCtx.frames.value[ctx.value.win.activeFrame] : undefined)
|
|
42
|
+
</script>
|
|
43
|
+
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<!-- overflow-hidden is just in case, if the frame's css is not properly set to h-full, overflow-auto, or the border/padding are too large, we can still get overflows -->
|
|
3
|
-
<
|
|
3
|
+
<LayoutShapeRect
|
|
4
4
|
tabindex="0"
|
|
5
|
-
:css="
|
|
5
|
+
:css="getShapeRectCss(frame)"
|
|
6
6
|
:class="twMerge(`
|
|
7
7
|
frame
|
|
8
8
|
p-[var(--layoutEdgeWidth,_2px)]
|
|
@@ -11,38 +11,45 @@
|
|
|
11
11
|
($attrs as any).class
|
|
12
12
|
)"
|
|
13
13
|
v-bind="{ ...$attrs, class: undefined }"
|
|
14
|
-
@focus="emit('focus')"
|
|
14
|
+
@focus="ctx.onFocus(frame.id); emit('focus');"
|
|
15
|
+
v-for="frame in frames"
|
|
16
|
+
:key="frame.id"
|
|
15
17
|
>
|
|
16
|
-
<slot>
|
|
18
|
+
<slot :name="`frame-${frame.id}`" v-bind="{frame}">
|
|
17
19
|
<div class="p-2 text-xs bg-neutral-500">
|
|
18
20
|
{{ debugFrame(frame) }}
|
|
19
21
|
</div>
|
|
20
22
|
<div>No slot found for `frame-{{ frame.id }}`. </div>
|
|
21
23
|
</slot>
|
|
22
|
-
</
|
|
24
|
+
</LayoutShapeRect>
|
|
23
25
|
</template>
|
|
24
26
|
|
|
25
27
|
<script lang="ts" setup>
|
|
28
|
+
import { dragContextInjectionKey, layoutContextInjectionKey } from "../types/vue.js"
|
|
26
29
|
import { twMerge } from "@witchcraft/ui/utils/twMerge"
|
|
27
|
-
import { useAttrs } from "vue"
|
|
30
|
+
import { useAttrs, inject, toRef } from "vue"
|
|
28
31
|
|
|
29
32
|
const $attrs = useAttrs()
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
inheritAttrs: false
|
|
33
|
-
})
|
|
34
|
+
import LayoutShapeRect from "./LayoutShapeRect.vue"
|
|
34
35
|
|
|
35
|
-
import
|
|
36
|
-
|
|
37
|
-
import { getShapeSquareCss } from "../helpers/getShapeSquareCss"
|
|
36
|
+
import { getShapeRectCss } from "../helpers/getShapeRectCss"
|
|
38
37
|
import { debugFrame } from "../layout/debugFrame.js"
|
|
39
|
-
import type { LayoutFrameProps } from "../types/index.js"
|
|
40
38
|
|
|
39
|
+
defineOptions({
|
|
40
|
+
inheritAttrs: false
|
|
41
|
+
})
|
|
41
42
|
|
|
42
43
|
const emit = defineEmits<{
|
|
43
|
-
/**
|
|
44
|
+
/** Focus event is always "emitted" to the provider (so it can't be cancelled), but the component also emits it in case you want to listen to it. */
|
|
44
45
|
(e: "focus"): void
|
|
45
46
|
}>()
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
const ctx = inject(layoutContextInjectionKey, undefined)
|
|
49
|
+
if (!ctx) throw new Error("LayoutEdges must be used within a LayoutWindow")
|
|
50
|
+
|
|
51
|
+
const dragCtx = inject(dragContextInjectionKey, undefined)
|
|
52
|
+
if (!dragCtx) throw new Error("LayoutEdges must be used within a LayoutWindow")
|
|
53
|
+
|
|
54
|
+
const frames = toRef(dragCtx, "frames")
|
|
48
55
|
</script>
|