@witchcraft/layout 0.4.3 → 0.5.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/dist/module.json +1 -1
- package/dist/runtime/components/FrameDragHandle.vue +3 -3
- package/dist/runtime/components/LayoutDragEdgeGrabbed.vue +3 -3
- package/dist/runtime/components/LayoutDragEdgeHandle.d.vue.ts +2 -2
- package/dist/runtime/components/LayoutDragEdgeHandle.vue +2 -2
- package/dist/runtime/components/LayoutDragEdgeHandle.vue.d.ts +2 -2
- package/dist/runtime/components/LayoutDragEdges.vue +5 -5
- package/dist/runtime/components/LayoutEdgesActiveFrame.vue +2 -2
- package/dist/runtime/components/LayoutFrame.vue +2 -2
- package/dist/runtime/components/LayoutIntersectionHandle.d.vue.ts +2 -2
- package/dist/runtime/components/LayoutIntersectionHandle.vue +2 -2
- package/dist/runtime/components/LayoutIntersectionHandle.vue.d.ts +2 -2
- package/dist/runtime/components/LayoutIntersections.vue +4 -4
- package/dist/runtime/components/LayoutWindow.d.vue.ts +54 -45
- package/dist/runtime/components/LayoutWindow.vue +29 -29
- package/dist/runtime/components/LayoutWindow.vue.d.ts +54 -45
- package/dist/runtime/composables/useFrames.d.ts +142 -129
- package/dist/runtime/composables/useFrames.js +122 -94
- package/dist/runtime/demo/App.vue +16 -16
- package/dist/runtime/demo/DemoControls.d.vue.ts +2 -2
- package/dist/runtime/demo/DemoControls.vue +4 -4
- package/dist/runtime/demo/DemoControls.vue.d.ts +2 -2
- package/dist/runtime/demo/tailwind.css +1 -1
- package/dist/runtime/helpers/createZoneSideClipPath.d.ts +2 -2
- package/dist/runtime/helpers/findEdgesTouchingWindow.d.ts +7 -0
- package/dist/runtime/helpers/findEdgesTouchingWindow.js +20 -0
- package/dist/runtime/helpers/{findFrameDraggableEdges.d.ts → findFrameMoveableEdges.d.ts} +2 -2
- package/dist/runtime/helpers/{findFrameDraggableEdges.js → findFrameMoveableEdges.js} +3 -3
- package/dist/runtime/helpers/{findDraggableEdge.d.ts → findMoveableEdge.d.ts} +1 -1
- package/dist/runtime/helpers/{findDraggableEdge.js → findMoveableEdge.js} +1 -1
- package/dist/runtime/helpers/index.d.ts +10 -3
- package/dist/runtime/helpers/index.js +10 -3
- package/dist/runtime/layout/createSplitDecoFromDrag.d.ts +1 -1
- package/dist/runtime/layout/createSplitDecoFromDrag.js +2 -2
- package/dist/runtime/layout/debugFrame.js +5 -1
- package/dist/runtime/layout/findSafeSplitEdge.d.ts +1 -1
- package/dist/runtime/layout/findSafeSplitEdge.js +2 -2
- package/dist/runtime/layout/getCloseFrameInfo.js +4 -4
- package/dist/runtime/layout/getFrameCollapseInfo.d.ts +1 -1
- package/dist/runtime/layout/getFrameCollapseInfo.js +9 -29
- package/dist/runtime/layout/getFrameDockInfo.d.ts +1 -1
- package/dist/runtime/layout/getFrameDockInfo.js +61 -44
- package/dist/runtime/layout/getFrameExpandInfo.d.ts +11 -0
- package/dist/runtime/layout/getFrameExpandInfo.js +49 -0
- package/dist/runtime/layout/getFrameRearrangeInfo.d.ts +2 -2
- package/dist/runtime/layout/getFrameRearrangeInfo.js +8 -8
- package/dist/runtime/layout/getFrameShrinkInfo.d.ts +14 -0
- package/dist/runtime/layout/getFrameShrinkInfo.js +48 -0
- package/dist/runtime/layout/getFrameSplitInfo.d.ts +1 -1
- package/dist/runtime/layout/getFrameSplitInfo.js +2 -2
- package/dist/runtime/layout/getFrameUncollapseInfo.d.ts +1 -1
- package/dist/runtime/layout/getFrameUncollapseInfo.js +6 -20
- package/dist/runtime/layout/{getFrameDragZones.d.ts → getFrameZones.d.ts} +3 -3
- package/dist/runtime/layout/{getFrameDragZones.js → getFrameZones.js} +1 -1
- package/dist/runtime/layout/getUpdateWindowSizeInfo.js +10 -12
- package/dist/runtime/layout/{getWindowDragZones.d.ts → getWindowZones.d.ts} +1 -1
- package/dist/runtime/layout/{getWindowDragZones.js → getWindowZones.js} +1 -1
- package/dist/runtime/layout/getZones.d.ts +8 -0
- package/dist/runtime/layout/getZones.js +32 -0
- package/dist/runtime/layout/index.d.ts +6 -3
- package/dist/runtime/layout/index.js +6 -3
- package/dist/runtime/{drag/DragActionHandler.d.ts → move/ActionHandler.d.ts} +18 -18
- package/dist/runtime/{drag/DragActionHandler.js → move/ActionHandler.js} +19 -19
- package/dist/runtime/{drag → move}/CloseAction.d.ts +8 -8
- package/dist/runtime/{drag → move}/CloseAction.js +29 -26
- package/dist/runtime/{drag → move}/FrameDragAction.d.ts +8 -8
- package/dist/runtime/{drag → move}/FrameDragAction.js +23 -21
- package/dist/runtime/{drag/DragDirectionStore.d.ts → move/MoveDirectionStore.d.ts} +4 -4
- package/dist/runtime/{drag/DragDirectionStore.js → move/MoveDirectionStore.js} +9 -9
- package/dist/runtime/{drag → move}/SplitAction.d.ts +9 -9
- package/dist/runtime/{drag → move}/SplitAction.js +46 -43
- package/dist/runtime/{drag → move}/createDefaultHandlers.d.ts +2 -2
- package/dist/runtime/types/index.d.ts +242 -158
- package/dist/runtime/types/index.js +4 -3
- package/dist/runtime/types/vue.d.ts +25 -18
- package/dist/runtime/types/vue.js +1 -1
- package/package.json +3 -2
- package/src/runtime/components/FrameDragHandle.vue +3 -3
- package/src/runtime/components/LayoutDragEdgeGrabbed.vue +3 -3
- package/src/runtime/components/LayoutDragEdgeHandle.vue +4 -4
- package/src/runtime/components/LayoutDragEdges.vue +5 -5
- package/src/runtime/components/LayoutEdgesActiveFrame.vue +2 -2
- package/src/runtime/components/LayoutFrame.vue +2 -2
- package/src/runtime/components/LayoutIntersectionHandle.vue +4 -4
- package/src/runtime/components/LayoutIntersections.vue +4 -4
- package/src/runtime/components/LayoutWindow.vue +35 -35
- package/src/runtime/composables/useFrames.ts +141 -121
- package/src/runtime/demo/App.vue +18 -18
- package/src/runtime/demo/DemoControls.vue +5 -5
- package/src/runtime/demo/tailwind.css +4 -0
- package/src/runtime/helpers/createZoneSideClipPath.ts +2 -2
- package/src/runtime/helpers/findEdgesTouchingWindow.ts +30 -0
- package/src/runtime/helpers/{findFrameDraggableEdges.ts → findFrameMoveableEdges.ts} +4 -4
- package/src/runtime/helpers/{findDraggableEdge.ts → findMoveableEdge.ts} +1 -1
- package/src/runtime/helpers/index.ts +10 -3
- package/src/runtime/layout/createSplitDecoFromDrag.ts +2 -2
- package/src/runtime/layout/debugFrame.ts +6 -1
- package/src/runtime/layout/findSafeSplitEdge.ts +4 -4
- package/src/runtime/layout/getCloseFrameInfo.ts +4 -4
- package/src/runtime/layout/getFrameCollapseInfo.ts +12 -41
- package/src/runtime/layout/getFrameDockInfo.ts +77 -57
- package/src/runtime/layout/getFrameExpandInfo.ts +84 -0
- package/src/runtime/layout/getFrameRearrangeInfo.ts +10 -10
- package/src/runtime/layout/getFrameShrinkInfo.ts +87 -0
- package/src/runtime/layout/getFrameSplitInfo.ts +5 -5
- package/src/runtime/layout/getFrameUncollapseInfo.ts +8 -30
- package/src/runtime/layout/{getFrameDragZones.ts → getFrameZones.ts} +4 -4
- package/src/runtime/layout/getUpdateWindowSizeInfo.ts +16 -22
- package/src/runtime/layout/{getWindowDragZones.ts → getWindowZones.ts} +1 -1
- package/src/runtime/layout/getZones.ts +48 -0
- package/src/runtime/layout/index.ts +6 -3
- package/src/runtime/{drag/DragActionHandler.ts → move/ActionHandler.ts} +44 -42
- package/src/runtime/{drag → move}/CloseAction.ts +39 -36
- package/src/runtime/{drag → move}/FrameDragAction.ts +36 -35
- package/src/runtime/{drag/DragDirectionStore.ts → move/MoveDirectionStore.ts} +10 -10
- package/src/runtime/{drag → move}/SplitAction.ts +56 -53
- package/src/runtime/{drag/defaultDragActions.ts → move/createDefaultHandlers.ts} +2 -2
- package/src/runtime/types/index.ts +104 -55
- package/src/runtime/types/vue.ts +18 -16
- package/dist/runtime/drag/defaultDragActions.d.ts +0 -9
- package/dist/runtime/drag/defaultDragActions.js +0 -10
- package/dist/runtime/layout/getDragZones.d.ts +0 -8
- package/dist/runtime/layout/getDragZones.js +0 -32
- package/src/runtime/drag/createDefaultHandlers.ts +0 -20
- package/src/runtime/layout/getDragZones.ts +0 -48
- /package/dist/runtime/{drag → move}/createDefaultHandlers.js +0 -0
|
@@ -3,7 +3,6 @@ import { keys } from "@alanscodelog/utils/keys"
|
|
|
3
3
|
import type { Ref } from "vue"
|
|
4
4
|
import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue"
|
|
5
5
|
|
|
6
|
-
import { DragDirectionStore } from "../drag/DragDirectionStore.js"
|
|
7
6
|
import { cloneFrame } from "../helpers/cloneFrame.js"
|
|
8
7
|
import { getIntersections } from "../helpers/getIntersections.js"
|
|
9
8
|
import { getVisualEdges } from "../helpers/getVisualEdges.js"
|
|
@@ -12,32 +11,14 @@ import { moveEdge } from "../helpers/moveEdge.js"
|
|
|
12
11
|
import { toWindowCoord } from "../helpers/toWindowCoord.js"
|
|
13
12
|
import { findFramesTouchingEdge } from "../layout/findFramesTouchingEdge.js"
|
|
14
13
|
import { isPointInRect } from "../layout/isPointInRect.js"
|
|
15
|
-
import
|
|
14
|
+
import { MoveDirectionStore } from "../move/MoveDirectionStore.js"
|
|
15
|
+
import type { ActionHandler, ActionHandlerApplyResult, Direction, Edge, EdgeMoveStartData, FrameId, FrameMoveStartData, IntersectionEntry, LayoutFrame, LayoutWindow, MoveState, Orientation, Point } from "../types/index.js"
|
|
16
16
|
|
|
17
17
|
export function useFrames(
|
|
18
18
|
win: Ref<LayoutWindow>,
|
|
19
|
-
handler:
|
|
20
|
-
eventHandler: (e: KeyboardEvent, state: DragState, forceRecalculateEdges: () => void) => void
|
|
21
|
-
/**
|
|
22
|
-
* Called when the drag coordinates change (during any event). Should return true to allow the edges to be updated/moved, or false to prevent it.
|
|
23
|
-
*
|
|
24
|
-
* Can return anything for the end event as it's ignored.
|
|
25
|
-
*
|
|
26
|
-
* Can be used to save some context/info to later apply safely during onDragApply.
|
|
27
|
-
*/
|
|
28
|
-
onDragChange: (...args: Parameters<DragChangeHandler>) => DragChangeResult
|
|
29
|
-
/**
|
|
30
|
-
* Called when drag will be applied. If dragEnd was called with apply false, it will not be called.
|
|
31
|
-
* Return false to not apply the regular drag end changes (i.e. return false to reset to the position before dragging).
|
|
32
|
-
*/
|
|
33
|
-
onDragApply: ((state: DragState, forceRecalculateEdges: () => void) => boolean)
|
|
34
|
-
/**
|
|
35
|
-
* Called after visual edges are recalculated. Action handlers can annotate edges with error info.
|
|
36
|
-
*/
|
|
37
|
-
annotateEdges?: (edges: Edge[], frames: LayoutFrame[]) => void
|
|
38
|
-
}
|
|
19
|
+
handler: ActionHandler
|
|
39
20
|
) {
|
|
40
|
-
const
|
|
21
|
+
const movingEdges = ref<Edge[]>([])
|
|
41
22
|
|
|
42
23
|
// each entry corresponds to the frames touching the corresponding dragging edge
|
|
43
24
|
const touchingFrames = ref<Record<string, LayoutFrame>[]>([])
|
|
@@ -53,30 +34,31 @@ export function useFrames(
|
|
|
53
34
|
|
|
54
35
|
const touchingFramesArrays = computed(() => touchingFrames.value.map(entry => Object.values(entry)))
|
|
55
36
|
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const
|
|
37
|
+
const isMoving = ref<false | "frame" | "edge">(false)
|
|
38
|
+
const showMoving = ref(false)
|
|
39
|
+
const movePoint = ref<Point | undefined>()
|
|
59
40
|
|
|
60
|
-
const
|
|
41
|
+
const moveDirections = ref<Record<Orientation, Direction | undefined>>({} as any)
|
|
61
42
|
|
|
62
|
-
let
|
|
63
|
-
const
|
|
43
|
+
let moveStartPoint: Point | undefined
|
|
44
|
+
const moveDistance = ref(-1)
|
|
64
45
|
|
|
65
|
-
const
|
|
66
|
-
const
|
|
46
|
+
const movingIntersection = ref<IntersectionEntry | undefined>(undefined)
|
|
47
|
+
const isMovingFromWindowEdge = ref<boolean>(false)
|
|
67
48
|
|
|
68
|
-
const
|
|
49
|
+
const movingFrameId = ref<FrameId | undefined>()
|
|
50
|
+
const eventContext = ref<Record<string, unknown> | undefined>()
|
|
69
51
|
|
|
70
52
|
const frames = computed(() =>
|
|
71
|
-
|
|
53
|
+
isMoving.value && showMoving.value
|
|
72
54
|
? { ...win.value.frames, ...allTouchingFrames.value }
|
|
73
55
|
: win.value.frames
|
|
74
56
|
)
|
|
75
57
|
|
|
76
|
-
const
|
|
77
|
-
if (
|
|
58
|
+
const moveHoveredFrame = computed(() => {
|
|
59
|
+
if (isMoving.value) {
|
|
78
60
|
for (const id of keys(frames.value)) {
|
|
79
|
-
if (isPointInRect(frames.value[id],
|
|
61
|
+
if (isPointInRect(frames.value[id], movePoint.value!)) {
|
|
80
62
|
return frames.value[id]
|
|
81
63
|
}
|
|
82
64
|
}
|
|
@@ -88,100 +70,127 @@ export function useFrames(
|
|
|
88
70
|
const intersections = computed(() => getIntersections(visualEdges.value))
|
|
89
71
|
// avoid expensive calculation of edges when lots of frames are added/removed
|
|
90
72
|
// all at once (e.g. by clicking on some command many times in a row)
|
|
91
|
-
const
|
|
73
|
+
const debounceGetMoveableEdges = debounce((f: LayoutFrame[]) => {
|
|
92
74
|
visualEdges.value = getVisualEdges(f, { includeWindowEdges: true })
|
|
93
75
|
handler.annotateEdges?.(visualEdges.value, f)
|
|
94
76
|
}, 50, {}) as any
|
|
95
77
|
|
|
96
|
-
watch([
|
|
78
|
+
watch([isMoving, frames], () => {
|
|
97
79
|
// let request animation force recalc
|
|
98
|
-
if (
|
|
80
|
+
if (isMoving.value) return
|
|
99
81
|
// otherwise use more performant debounced version
|
|
100
|
-
|
|
82
|
+
debounceGetMoveableEdges(Object.values(frames.value))
|
|
101
83
|
}, { deep: true })
|
|
102
|
-
|
|
84
|
+
debounceGetMoveableEdges(Object.values(frames.value))
|
|
103
85
|
|
|
104
86
|
function forceRecalculateEdges(): void {
|
|
105
87
|
visualEdges.value = getVisualEdges(Object.values(frames.value), { includeWindowEdges: true })
|
|
106
88
|
handler.annotateEdges?.(visualEdges.value, Object.values(frames.value))
|
|
107
89
|
}
|
|
108
90
|
|
|
109
|
-
const
|
|
110
|
-
onUpdate: dir =>
|
|
91
|
+
const moveDirStore = new MoveDirectionStore({
|
|
92
|
+
onUpdate: dir => moveDirections.value = dir
|
|
111
93
|
})
|
|
112
94
|
|
|
113
95
|
const state = computed(() => ({
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
96
|
+
moveDirections: moveDirections.value,
|
|
97
|
+
movePoint: movePoint.value,
|
|
98
|
+
moveDistance: moveDistance.value,
|
|
99
|
+
isMoving: isMoving.value,
|
|
100
|
+
showMoving: showMoving.value,
|
|
101
|
+
movingFrameId: movingFrameId.value,
|
|
102
|
+
movingEdges: movingEdges.value,
|
|
103
|
+
movingIntersection: movingIntersection.value,
|
|
122
104
|
visualEdges: visualEdges.value,
|
|
123
105
|
touchingFrames: touchingFrames.value,
|
|
124
106
|
touchingFramesArrays: touchingFramesArrays.value,
|
|
125
107
|
frames: frames.value,
|
|
126
|
-
|
|
108
|
+
moveHoveredFrame: moveHoveredFrame.value,
|
|
127
109
|
intersections: intersections.value,
|
|
128
|
-
|
|
110
|
+
isMovingFromWindowEdge: isMovingFromWindowEdge.value,
|
|
111
|
+
eventContext: eventContext.value,
|
|
129
112
|
win: win.value
|
|
130
|
-
} satisfies
|
|
113
|
+
} satisfies MoveState))
|
|
131
114
|
|
|
132
115
|
let controller: AbortController
|
|
116
|
+
let moveResolve: ((result: any) => void) | undefined
|
|
117
|
+
let moveResult: any
|
|
133
118
|
|
|
134
119
|
function resetState(): void {
|
|
135
|
-
|
|
120
|
+
moveStartPoint = undefined
|
|
121
|
+
|
|
122
|
+
moveResolve?.(moveResult)
|
|
123
|
+
moveResolve = undefined
|
|
124
|
+
moveResult = undefined
|
|
136
125
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
126
|
+
movingEdges.value = []
|
|
127
|
+
movingIntersection.value = undefined
|
|
128
|
+
isMoving.value = false
|
|
129
|
+
movePoint.value = undefined
|
|
130
|
+
moveDistance.value = -1
|
|
142
131
|
touchingFrames.value = []
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
132
|
+
movingFrameId.value = undefined
|
|
133
|
+
eventContext.value = undefined
|
|
134
|
+
moveDirStore.reset()
|
|
135
|
+
showMoving.value = false
|
|
146
136
|
forceRecalculateEdges()
|
|
147
137
|
}
|
|
148
138
|
|
|
149
|
-
function
|
|
139
|
+
function moveStart<T extends "edge" | "frame">(
|
|
150
140
|
e: PointerEvent,
|
|
151
141
|
type: T,
|
|
152
142
|
// someday this will work
|
|
153
|
-
data: T extends "edge" ?
|
|
154
|
-
|
|
143
|
+
data: T extends "edge" ? EdgeMoveStartData : FrameMoveStartData,
|
|
144
|
+
{
|
|
145
|
+
moveEvent = "pointermove",
|
|
146
|
+
endEvent = "pointerup",
|
|
147
|
+
context
|
|
148
|
+
}: {
|
|
149
|
+
moveEvent?: string
|
|
150
|
+
endEvent?: string
|
|
151
|
+
context?: Record<string, unknown>
|
|
152
|
+
} = {}
|
|
153
|
+
): Promise<any> {
|
|
154
|
+
// if already dragging, abort previous (resetState resolves the old promise)
|
|
155
|
+
if (controller) {
|
|
156
|
+
controller.abort()
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const promise = new Promise<any>(resolve => {
|
|
160
|
+
moveResolve = resolve
|
|
161
|
+
})
|
|
162
|
+
|
|
155
163
|
controller = new AbortController()
|
|
156
164
|
controller.signal.addEventListener("abort", () => resetState())
|
|
157
165
|
|
|
158
166
|
e.preventDefault()
|
|
159
|
-
window.addEventListener(
|
|
160
|
-
window.addEventListener(
|
|
167
|
+
window.addEventListener(moveEvent, onMove as EventListener, { signal: controller.signal })
|
|
168
|
+
window.addEventListener(endEvent, moveEnd as EventListener, { signal: controller.signal })
|
|
161
169
|
|
|
162
170
|
const point = toWindowCoord(win.value, e)
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
171
|
+
movePoint.value = point
|
|
172
|
+
moveStartPoint = { ...point }
|
|
173
|
+
moveDirStore.update(point)
|
|
174
|
+
eventContext.value = context
|
|
166
175
|
|
|
167
|
-
|
|
168
|
-
|
|
176
|
+
isMoving.value = type
|
|
177
|
+
showMoving.value = true
|
|
169
178
|
|
|
170
179
|
if (type === "frame") {
|
|
171
|
-
|
|
180
|
+
movingFrameId.value = (data as FrameMoveStartData).frameId
|
|
172
181
|
} else {
|
|
173
|
-
const { edge, intersection } = data as
|
|
174
|
-
|
|
182
|
+
const { edge, intersection } = data as EdgeMoveStartData
|
|
183
|
+
movingIntersection.value = intersection
|
|
175
184
|
|
|
176
|
-
|
|
177
|
-
? [edge]
|
|
185
|
+
movingEdges.value = edge
|
|
186
|
+
? [edge!]
|
|
178
187
|
: [
|
|
179
|
-
...(
|
|
180
|
-
...(
|
|
188
|
+
...(movingIntersection.value?.sharedEdges.horizontal ?? []),
|
|
189
|
+
...(movingIntersection.value?.sharedEdges.vertical ?? [])
|
|
181
190
|
]
|
|
182
191
|
|
|
183
192
|
|
|
184
|
-
|
|
193
|
+
isMovingFromWindowEdge.value = movingEdges.value.some(_ => isWindowEdge(_))
|
|
185
194
|
|
|
186
195
|
const framesArray = Object.values(win.value.frames)
|
|
187
196
|
|
|
@@ -189,10 +198,10 @@ export function useFrames(
|
|
|
189
198
|
// all frames in touchingFrames must be clones
|
|
190
199
|
// BUT they must be the same clone even if they are referenced by multiple dragging edges
|
|
191
200
|
const clones = new Map<string, LayoutFrame>()
|
|
192
|
-
for (let i = 0; i <
|
|
193
|
-
const
|
|
201
|
+
for (let i = 0; i < movingEdges.value.length; i++) {
|
|
202
|
+
const movingEdge = movingEdges.value[i]
|
|
194
203
|
touchingFrames.value[i] = {}
|
|
195
|
-
for (const { frame } of findFramesTouchingEdge(
|
|
204
|
+
for (const { frame } of findFramesTouchingEdge(movingEdge, framesArray)) {
|
|
196
205
|
if (!clones.has(frame.id)) {
|
|
197
206
|
const clone = cloneFrame(frame)
|
|
198
207
|
touchingFrames.value[i][frame.id] = clone
|
|
@@ -204,35 +213,36 @@ export function useFrames(
|
|
|
204
213
|
}
|
|
205
214
|
}
|
|
206
215
|
|
|
207
|
-
const res = handler.
|
|
208
|
-
|
|
216
|
+
const res = handler.onMoveChange("start", e, state.value, forceRecalculateEdges, cancel, resolve)
|
|
217
|
+
showMoving.value = res.showMoving ?? true
|
|
218
|
+
return promise
|
|
209
219
|
}
|
|
210
220
|
|
|
211
|
-
function
|
|
221
|
+
function onMove(e: PointerEvent): void {
|
|
212
222
|
e.preventDefault()
|
|
213
223
|
const point = toWindowCoord(win.value, e)
|
|
214
|
-
const didChange =
|
|
215
|
-
|
|
224
|
+
const didChange = moveDirStore.update(point)
|
|
225
|
+
movePoint.value = point
|
|
216
226
|
// at least one drag move has happened
|
|
217
|
-
|
|
218
|
-
if (
|
|
219
|
-
|
|
220
|
-
Math.pow(point.x -
|
|
227
|
+
moveDistance.value = 0
|
|
228
|
+
if (moveStartPoint) {
|
|
229
|
+
moveDistance.value = Math.sqrt(
|
|
230
|
+
Math.pow(point.x - moveStartPoint.x, 2) + Math.pow(point.y - moveStartPoint.y, 2)
|
|
221
231
|
)
|
|
222
232
|
}
|
|
223
233
|
if (!didChange) return
|
|
224
|
-
const res = handler.
|
|
234
|
+
const res = handler.onMoveChange("move", e, state.value, forceRecalculateEdges, cancel, resolve)
|
|
225
235
|
|
|
226
236
|
|
|
227
|
-
|
|
237
|
+
showMoving.value = res.showMoving ?? true
|
|
228
238
|
if (!res.updateEdges) return
|
|
229
239
|
|
|
230
|
-
if (
|
|
240
|
+
if (isMoving.value === "edge") {
|
|
231
241
|
requestAnimationFrame(() => {
|
|
232
|
-
for (let i = 0; i <
|
|
233
|
-
const
|
|
234
|
-
if (
|
|
235
|
-
moveEdge(touchingFramesArrays.value[i],
|
|
242
|
+
for (let i = 0; i < movingEdges.value.length; i++) {
|
|
243
|
+
const movingEdge = movingEdges.value[i]
|
|
244
|
+
if (movingEdge) {
|
|
245
|
+
moveEdge(touchingFramesArrays.value[i], movingEdge, point)
|
|
236
246
|
}
|
|
237
247
|
}
|
|
238
248
|
forceRecalculateEdges()
|
|
@@ -240,28 +250,37 @@ export function useFrames(
|
|
|
240
250
|
}
|
|
241
251
|
}
|
|
242
252
|
|
|
243
|
-
function
|
|
253
|
+
function moveEnd(e?: PointerEvent, { apply = true }: Partial<Pick<ActionHandlerApplyResult, "apply">> = {}): void {
|
|
244
254
|
if (e) {
|
|
245
255
|
const point = toWindowCoord(win.value, e)
|
|
246
|
-
|
|
256
|
+
movePoint.value = point
|
|
247
257
|
}
|
|
248
258
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
259
|
+
if (apply) {
|
|
260
|
+
const applyResult = handler.onMoveApply(state.value, forceRecalculateEdges)
|
|
261
|
+
moveResult = applyResult.result
|
|
262
|
+
if (applyResult.apply) {
|
|
263
|
+
for (const frame of touchingFramesArrays.value.flat()) {
|
|
264
|
+
win!.value.frames[frame.id] = frame
|
|
265
|
+
}
|
|
253
266
|
}
|
|
254
267
|
}
|
|
255
268
|
|
|
256
|
-
handler.
|
|
269
|
+
handler.onMoveChange("end", e, state.value, forceRecalculateEdges, undefined, undefined)
|
|
257
270
|
|
|
258
271
|
// this can get called from elsewhere
|
|
259
|
-
// also takes care of cleanup
|
|
272
|
+
// also takes care of cleanup nd resolving promise
|
|
260
273
|
controller?.abort()
|
|
261
274
|
}
|
|
275
|
+
|
|
262
276
|
function cancel(): void {
|
|
263
|
-
|
|
277
|
+
moveEnd(undefined, { apply: false })
|
|
264
278
|
}
|
|
279
|
+
function resolve({ apply, result: value }: ActionHandlerApplyResult): void {
|
|
280
|
+
moveResult = value
|
|
281
|
+
moveEnd(undefined, { apply })
|
|
282
|
+
}
|
|
283
|
+
|
|
265
284
|
const keydownController = new AbortController()
|
|
266
285
|
const wrappedKeydownHandler = (e: KeyboardEvent): void => {
|
|
267
286
|
handler.eventHandler(e, state.value, forceRecalculateEdges)
|
|
@@ -277,25 +296,26 @@ export function useFrames(
|
|
|
277
296
|
})
|
|
278
297
|
|
|
279
298
|
return {
|
|
280
|
-
|
|
281
|
-
|
|
299
|
+
moveStart,
|
|
300
|
+
moveEnd,
|
|
282
301
|
cancel,
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
302
|
+
onMove,
|
|
303
|
+
moveDirections,
|
|
304
|
+
movePoint,
|
|
305
|
+
isMoving,
|
|
306
|
+
movingEdges,
|
|
307
|
+
movingIntersection,
|
|
289
308
|
visualEdges,
|
|
290
309
|
touchingFrames,
|
|
291
310
|
touchingFramesArrays,
|
|
292
311
|
frames,
|
|
293
|
-
|
|
312
|
+
moveHoveredFrame,
|
|
294
313
|
intersections,
|
|
295
|
-
|
|
314
|
+
isMovingFromWindowEdge,
|
|
296
315
|
forceRecalculateEdges,
|
|
297
316
|
state,
|
|
298
|
-
|
|
299
|
-
|
|
317
|
+
showMoving,
|
|
318
|
+
movingFrameId,
|
|
319
|
+
actionHandler: handler
|
|
300
320
|
}
|
|
301
321
|
}
|
package/src/runtime/demo/App.vue
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
v-if="win"
|
|
11
11
|
:frames="frames!"
|
|
12
12
|
:win="win"
|
|
13
|
-
:
|
|
13
|
+
:actionHandler="actionHandler"
|
|
14
14
|
/>
|
|
15
15
|
|
|
16
16
|
<LayoutWindow
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
:textHints="textHints"
|
|
34
34
|
textHintsTeleportTo="#status-bar"
|
|
35
35
|
v-model:win="win"
|
|
36
|
-
@is-showing-
|
|
37
|
-
@
|
|
36
|
+
@is-showing-move="isShowingMove = $event"
|
|
37
|
+
@move-state="moveState = $event"
|
|
38
38
|
>
|
|
39
39
|
<LayoutFrame class="
|
|
40
40
|
flex
|
|
@@ -163,7 +163,7 @@ import LayoutIntersections from "../components/LayoutIntersections.vue"
|
|
|
163
163
|
import LayoutEdgesActiveFrame from "../components/LayoutEdgesActiveFrame.vue"
|
|
164
164
|
import LayoutDragEdges from "../components/LayoutDragEdges.vue"
|
|
165
165
|
import LayoutIntersectionHandle from "../components/LayoutIntersectionHandle.vue"
|
|
166
|
-
import type {
|
|
166
|
+
import type { MoveState } from "../types/index.js"
|
|
167
167
|
import { applyFrameChanges } from "../layout/applyFrameChanges.js"
|
|
168
168
|
import { debugFrame } from "../layout/debugFrame.js"
|
|
169
169
|
import { getFrameCollapseInfo } from "../layout/getFrameCollapseInfo.js"
|
|
@@ -179,7 +179,7 @@ import { settings } from "../settings.js"
|
|
|
179
179
|
import type { EdgeSide, Layout, Pos, Size } from "../types/index.js"
|
|
180
180
|
import { throwIfError } from "@alanscodelog/utils/throwIfError"
|
|
181
181
|
import { useTemplateRef } from "vue"
|
|
182
|
-
import {
|
|
182
|
+
import { ActionHandler } from "../move/ActionHandler.js"
|
|
183
183
|
import LayoutDragEdgeHandle from "../components/LayoutDragEdgeHandle.vue"
|
|
184
184
|
import LayoutDragEdgeVisible from "../components/LayoutDragEdgeVisible.vue"
|
|
185
185
|
import LayoutIntersectionVisible from "../components/LayoutIntersectionVisible.vue"
|
|
@@ -228,30 +228,30 @@ function layoutInitialize(layout: Layout, { defaultPos, defaultSize }: {
|
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
// whether layout window is showing the edge beneath the mouse
|
|
231
|
-
// as determined by the
|
|
232
|
-
const
|
|
231
|
+
// as determined by the ActionHandler in LayoutWindow
|
|
232
|
+
const isShowingMove = ref(false)
|
|
233
233
|
// drag state as returned by useFrames in LayoutWindow
|
|
234
|
-
const
|
|
234
|
+
const moveState = ref<MoveState | undefined>(undefined)
|
|
235
235
|
|
|
236
236
|
function handleUndock(frameId: string) {
|
|
237
237
|
const changes = throwIfError(getFrameUndockInfo(win.value!, frameId))
|
|
238
|
-
|
|
238
|
+
ActionHandler.debugState("undock", "before", moveState.value!, {}, undefined)
|
|
239
239
|
applyFrameChanges(win.value!, changes)
|
|
240
|
-
|
|
240
|
+
ActionHandler.debugState("undock", "after", moveState.value!, {}, undefined)
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
function handleCollapse(frameId: string) {
|
|
244
244
|
const changes = throwIfError(getFrameCollapseInfo(win.value!, frameId))
|
|
245
|
-
|
|
245
|
+
ActionHandler.debugState("collapse", "before", moveState.value!, {}, undefined)
|
|
246
246
|
applyFrameChanges(win.value!, changes)
|
|
247
|
-
|
|
247
|
+
ActionHandler.debugState("collapse", "after", moveState.value!, {}, undefined)
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
function handleUncollapse(frameId: string) {
|
|
251
251
|
const changes = throwIfError(getFrameUncollapseInfo(win.value!, frameId))
|
|
252
|
-
|
|
252
|
+
ActionHandler.debugState("uncollapse", "before", moveState.value!, {}, undefined)
|
|
253
253
|
applyFrameChanges(win.value!, changes)
|
|
254
|
-
|
|
254
|
+
ActionHandler.debugState("uncollapse", "after", moveState.value!, {}, undefined)
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
const collapsedDocks = computed(() => {
|
|
@@ -264,13 +264,13 @@ const collapsedDocks = computed(() => {
|
|
|
264
264
|
return sides
|
|
265
265
|
})
|
|
266
266
|
|
|
267
|
-
const
|
|
267
|
+
const actionHandler = computed(() => layoutComponent.value?.actionHandler)
|
|
268
268
|
|
|
269
269
|
const textHints = computed(() => {
|
|
270
|
-
const
|
|
271
|
-
const textHints =
|
|
270
|
+
const isMoving = moveState.value?.isMoving
|
|
271
|
+
const textHints = actionHandler.value?.textHints ?? {actions:[], errors:[]}
|
|
272
272
|
return [
|
|
273
|
-
...(!
|
|
273
|
+
...(!isMoving ? [{classes:"", text:"Drag from an edge to create a new frame."}] : []),
|
|
274
274
|
...textHints.errors.map((_: string) => ({classes:"text-red-500", text:_})),
|
|
275
275
|
...textHints.actions.map((_: string) => ({ text:_})),
|
|
276
276
|
]
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
<WButton class="flex-1" @click="copyState">Copy State</WButton>
|
|
26
26
|
<WCheckbox v-model="renameFrames">Easy Ids</WCheckbox>
|
|
27
27
|
</div>
|
|
28
|
-
<label class="flex flex-col gap-2 text-sm" v-if="
|
|
28
|
+
<label class="flex flex-col gap-2 text-sm" v-if="actionHandler">
|
|
29
29
|
<span>Plugin Debug Logs (true | debug path):</span>
|
|
30
30
|
<WSimpleInput v-model="debugKey" />
|
|
31
31
|
</label>
|
|
@@ -50,13 +50,13 @@ import { settings } from "../settings.js"
|
|
|
50
50
|
import type { LayoutFrame, LayoutWindow } from "../types/index.js"
|
|
51
51
|
import { throwIfError } from "@alanscodelog/utils/throwIfError";
|
|
52
52
|
import { walk } from "@alanscodelog/utils/walk"
|
|
53
|
-
import type {
|
|
53
|
+
import type { ActionHandler } from "../move/ActionHandler.js"
|
|
54
54
|
import { rotateLayout } from "../helpers/rotateFrames.js"
|
|
55
55
|
|
|
56
56
|
const props = defineProps<{
|
|
57
57
|
win: LayoutWindow
|
|
58
58
|
frames: LayoutFrame[],
|
|
59
|
-
|
|
59
|
+
actionHandler?: ActionHandler<any>
|
|
60
60
|
}>()
|
|
61
61
|
|
|
62
62
|
const showDevActions = ref(false)
|
|
@@ -112,14 +112,14 @@ function copyState() {
|
|
|
112
112
|
|
|
113
113
|
const debugKey = ref(import.meta.dev ? "state.win.frames" : "false")
|
|
114
114
|
watch(debugKey, () => {
|
|
115
|
-
if (!props.
|
|
115
|
+
if (!props.actionHandler) return
|
|
116
116
|
const value = debugKey.value === "true"
|
|
117
117
|
? true
|
|
118
118
|
: debugKey.value === "false"
|
|
119
119
|
? false
|
|
120
120
|
: debugKey.value
|
|
121
121
|
|
|
122
|
-
for (const plugin of Object.values(props.
|
|
122
|
+
for (const plugin of Object.values(props.actionHandler.actions)) {
|
|
123
123
|
plugin.debug = value
|
|
124
124
|
}
|
|
125
125
|
})
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* Accepts scaled coordinates.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type {
|
|
9
|
+
import type { Zone } from "../types/index.js"
|
|
10
10
|
|
|
11
11
|
export function createZoneSideClipPath(
|
|
12
|
-
zone:
|
|
12
|
+
zone: Zone,
|
|
13
13
|
{ frameEdgePx, windowEdgePx }: { frameEdgePx: number, windowEdgePx: number }
|
|
14
14
|
): string | undefined {
|
|
15
15
|
const side = zone.side
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { settings } from "../settings.js"
|
|
2
|
+
import type { EdgeSide, LayoutFrame } from "../types/index.js"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Checks which window edges a frame touches and how.
|
|
6
|
+
*
|
|
7
|
+
* @returns Record mapping touched sides to `"full"` (spans the entire edge) or `"partial"` (touches but doesn't span fully). Only touched sides are included as keys.
|
|
8
|
+
*/
|
|
9
|
+
export function findEdgesTouchingWindow(frame: LayoutFrame): Partial<Record<EdgeSide, "full" | "partial">> {
|
|
10
|
+
const maxInt = settings.maxInt
|
|
11
|
+
const result: Partial<Record<EdgeSide, "full" | "partial">> = {}
|
|
12
|
+
|
|
13
|
+
const spansVertical = frame.y === 0 && frame.y + frame.height === maxInt
|
|
14
|
+
const spansHorizontal = frame.x === 0 && frame.x + frame.width === maxInt
|
|
15
|
+
|
|
16
|
+
if (frame.x === 0) {
|
|
17
|
+
result.left = spansVertical ? "full" : "partial"
|
|
18
|
+
}
|
|
19
|
+
if (frame.x + frame.width === maxInt) {
|
|
20
|
+
result.right = spansVertical ? "full" : "partial"
|
|
21
|
+
}
|
|
22
|
+
if (frame.y === 0) {
|
|
23
|
+
result.top = spansHorizontal ? "full" : "partial"
|
|
24
|
+
}
|
|
25
|
+
if (frame.y + frame.height === maxInt) {
|
|
26
|
+
result.bottom = spansHorizontal ? "full" : "partial"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return result
|
|
30
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { findMoveableEdge } from "./findMoveableEdge.js"
|
|
2
2
|
import { frameToEdges } from "./frameToEdges.js"
|
|
3
3
|
import { isWindowEdge } from "./isWindowEdge.js"
|
|
4
4
|
import { sideToOrientation } from "./sideToOrientation.js"
|
|
@@ -8,10 +8,10 @@ import type {
|
|
|
8
8
|
EdgeSide, LayoutFrame
|
|
9
9
|
} from "../types/index.js"
|
|
10
10
|
|
|
11
|
-
export function
|
|
11
|
+
export function findFrameMoveableEdges(
|
|
12
12
|
frame: LayoutFrame,
|
|
13
13
|
edges: Edge[],
|
|
14
|
-
/** See {@link
|
|
14
|
+
/** See {@link findMoveableEdge} */
|
|
15
15
|
exact: boolean = true,
|
|
16
16
|
sides: EdgeSide[] = ["left", "right", "top", "bottom"]
|
|
17
17
|
): { edge: Edge, side: EdgeSide }[] | undefined {
|
|
@@ -23,7 +23,7 @@ export function findFrameDraggableEdges(
|
|
|
23
23
|
const isWinEdge = isWindowEdge(edge, edgeDirection)
|
|
24
24
|
|
|
25
25
|
if (isWinEdge) continue
|
|
26
|
-
const maybeEdge =
|
|
26
|
+
const maybeEdge = findMoveableEdge(edge, edges, exact, edgeDirection)
|
|
27
27
|
|
|
28
28
|
if (maybeEdge) res.push({ edge: maybeEdge, side })
|
|
29
29
|
}
|
|
@@ -4,7 +4,7 @@ import { isEdgeEqual } from "./isEdgeEqual.js"
|
|
|
4
4
|
|
|
5
5
|
import type { Edge, Orientation } from "../types/index.js"
|
|
6
6
|
|
|
7
|
-
export function
|
|
7
|
+
export function findMoveableEdge(
|
|
8
8
|
edge: Edge,
|
|
9
9
|
edges: Edge[],
|
|
10
10
|
/** Whether the edge must match exactly and does not "contain/touch" another frame. */
|