@witchcraft/layout 0.1.3 → 0.2.1
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 +27 -24
- package/dist/module.json +1 -1
- package/dist/runtime/components/FrameDragHandle.d.vue.ts +15 -0
- package/dist/runtime/components/FrameDragHandle.vue +28 -0
- package/dist/runtime/components/FrameDragHandle.vue.d.ts +15 -0
- package/dist/runtime/components/LayoutDecos.d.vue.ts +2 -4
- package/dist/runtime/components/LayoutDecos.vue +10 -29
- package/dist/runtime/components/LayoutDecos.vue.d.ts +2 -4
- package/dist/runtime/components/LayoutEdges.d.vue.ts +3 -3
- package/dist/runtime/components/LayoutEdges.vue +8 -8
- package/dist/runtime/components/LayoutEdges.vue.d.ts +3 -3
- package/dist/runtime/components/LayoutFrame.d.vue.ts +1 -1
- package/dist/runtime/components/LayoutFrame.vue +0 -1
- package/dist/runtime/components/LayoutFrame.vue.d.ts +1 -1
- package/dist/runtime/components/LayoutShapeSquare.d.vue.ts +3 -1
- package/dist/runtime/components/LayoutShapeSquare.vue.d.ts +3 -1
- package/dist/runtime/components/LayoutWindow.d.vue.ts +26 -12
- package/dist/runtime/components/LayoutWindow.vue +95 -84
- package/dist/runtime/components/LayoutWindow.vue.d.ts +26 -12
- package/dist/runtime/composables/useFrames.d.ts +15 -13
- package/dist/runtime/composables/useFrames.js +59 -39
- package/dist/runtime/demo/App.vue +116 -30
- package/dist/runtime/demo/DemoControls.d.vue.ts +4 -1
- package/dist/runtime/demo/DemoControls.vue +98 -4
- package/dist/runtime/demo/DemoControls.vue.d.ts +4 -1
- package/dist/runtime/drag/CloseAction.d.ts +26 -5
- package/dist/runtime/drag/CloseAction.js +87 -40
- package/dist/runtime/drag/DragActionHandler.d.ts +20 -8
- package/dist/runtime/drag/DragActionHandler.js +47 -12
- package/dist/runtime/drag/FrameDragAction.d.ts +45 -0
- package/dist/runtime/drag/FrameDragAction.js +143 -0
- package/dist/runtime/drag/SplitAction.d.ts +32 -11
- package/dist/runtime/drag/SplitAction.js +82 -24
- package/dist/runtime/drag/createDefaultHandlers.d.ts +9 -0
- package/dist/runtime/drag/createDefaultHandlers.js +10 -0
- package/dist/runtime/drag/defaultDragActions.d.ts +9 -0
- package/dist/runtime/drag/defaultDragActions.js +10 -0
- package/dist/runtime/drag/types.d.ts +82 -13
- package/dist/runtime/drag/types.js +1 -0
- package/dist/runtime/helpers/createZoneSideClipPath.d.ts +12 -0
- package/dist/runtime/helpers/createZoneSideClipPath.js +17 -0
- package/dist/runtime/helpers/doEdgesOverlap.d.ts +3 -1
- package/dist/runtime/helpers/doEdgesOverlap.js +5 -5
- package/dist/runtime/helpers/getDockBoundaries.d.ts +19 -0
- package/dist/runtime/helpers/getDockBoundaries.js +14 -0
- package/dist/runtime/helpers/getEdgeLength.d.ts +2 -0
- package/dist/runtime/helpers/getEdgeLength.js +5 -0
- package/dist/runtime/helpers/getIntersections.js +2 -2
- package/dist/runtime/helpers/getIntersectionsCss.js +2 -2
- package/dist/runtime/helpers/getMoveEdgeInfo.js +2 -2
- package/dist/runtime/helpers/getResizeLimit.js +2 -2
- package/dist/runtime/helpers/getShapeSquareCss.js +2 -2
- package/dist/runtime/helpers/getVisualEdgeCss.js +2 -2
- package/dist/runtime/helpers/getVisualEdges.d.ts +1 -1
- package/dist/runtime/helpers/getVisualEdges.js +4 -3
- package/dist/runtime/helpers/index.d.ts +4 -0
- package/dist/runtime/helpers/index.js +4 -0
- package/dist/runtime/helpers/isEdgeEqual.js +2 -4
- package/dist/runtime/helpers/isWindowEdge.js +2 -2
- package/dist/runtime/helpers/isWindowEdgePoint.js +2 -2
- package/dist/runtime/helpers/moveEdge.js +2 -2
- package/dist/runtime/helpers/numberToScaledPercent.d.ts +1 -1
- package/dist/runtime/helpers/numberToScaledPercent.js +2 -2
- package/dist/runtime/helpers/numberToScaledSize.js +2 -2
- package/dist/runtime/helpers/rotateFrames.d.ts +7 -0
- package/dist/runtime/helpers/rotateFrames.js +36 -0
- package/dist/runtime/helpers/scaledPointToPx.d.ts +13 -0
- package/dist/runtime/helpers/scaledPointToPx.js +7 -0
- package/dist/runtime/helpers/toWindowCoord.js +2 -2
- package/dist/runtime/layout/applyFrameChanges.d.ts +10 -0
- package/dist/runtime/layout/applyFrameChanges.js +29 -0
- package/dist/runtime/layout/createSplitDecoFromDrag.d.ts +6 -1
- package/dist/runtime/layout/createSplitDecoFromDrag.js +4 -4
- package/dist/runtime/layout/createSplitDecoShapes.d.ts +7 -0
- package/dist/runtime/layout/{createSplitDecoEdge.js → createSplitDecoShapes.js} +6 -3
- package/dist/runtime/layout/debugFrame.js +2 -1
- package/dist/runtime/layout/findSafeSplitEdge.js +2 -2
- package/dist/runtime/layout/frameCreate.js +2 -2
- package/dist/runtime/layout/getCloseFrameInfo.d.ts +7 -6
- package/dist/runtime/layout/getCloseFrameInfo.js +10 -3
- package/dist/runtime/layout/getDragZones.d.ts +8 -0
- package/dist/runtime/layout/getDragZones.js +32 -0
- package/dist/runtime/layout/getFillEmptySpaceInfo.d.ts +65 -0
- package/dist/runtime/layout/getFillEmptySpaceInfo.js +69 -0
- package/dist/runtime/layout/getFrameCollapseInfo.d.ts +13 -0
- package/dist/runtime/layout/getFrameCollapseInfo.js +93 -0
- package/dist/runtime/layout/getFrameDockInfo.d.ts +9 -0
- package/dist/runtime/layout/getFrameDockInfo.js +82 -0
- package/dist/runtime/layout/getFrameDragZones.d.ts +16 -0
- package/dist/runtime/layout/getFrameDragZones.js +74 -0
- package/dist/runtime/layout/getFrameRearrangeInfo.d.ts +139 -0
- package/dist/runtime/layout/getFrameRearrangeInfo.js +87 -0
- package/dist/runtime/layout/getFrameSplitInfo.d.ts +7 -5
- package/dist/runtime/layout/getFrameSplitInfo.js +10 -3
- package/dist/runtime/layout/getFrameSwapInfo.d.ts +9 -0
- package/dist/runtime/layout/getFrameSwapInfo.js +27 -0
- package/dist/runtime/layout/getFrameTo.js +2 -2
- package/dist/runtime/layout/getFrameUncollapseInfo.d.ts +12 -0
- package/dist/runtime/layout/getFrameUncollapseInfo.js +88 -0
- package/dist/runtime/layout/getFrameUndockInfo.d.ts +13 -0
- package/dist/runtime/layout/getFrameUndockInfo.js +51 -0
- package/dist/runtime/layout/getFramesRedistributeInfo.d.ts +29 -0
- package/dist/runtime/layout/getFramesRedistributeInfo.js +53 -0
- package/dist/runtime/layout/getWindowDragZones.d.ts +6 -0
- package/dist/runtime/layout/getWindowDragZones.js +49 -0
- package/dist/runtime/layout/index.d.ts +14 -5
- package/dist/runtime/layout/index.js +14 -5
- package/dist/runtime/layout/isPointInRect.d.ts +7 -0
- package/dist/runtime/layout/{isPointInFrame.js → isPointInRect.js} +1 -1
- package/dist/runtime/layout/resizeFrame.js +2 -2
- package/dist/runtime/settings.d.ts +41 -16
- package/dist/runtime/settings.js +95 -53
- package/dist/runtime/types/index.d.ts +324 -54
- package/dist/runtime/types/index.js +54 -20
- package/package.json +28 -29
- package/src/runtime/components/FrameDragHandle.vue +30 -0
- package/src/runtime/components/LayoutDecos.vue +12 -36
- package/src/runtime/components/LayoutEdges.vue +27 -23
- package/src/runtime/components/LayoutFrame.vue +6 -5
- package/src/runtime/components/LayoutShapeSquare.vue +9 -3
- package/src/runtime/components/LayoutWindow.vue +110 -101
- package/src/runtime/composables/useFrames.ts +80 -50
- package/src/runtime/demo/App.vue +126 -36
- package/src/runtime/demo/DemoControls.vue +115 -6
- package/src/runtime/drag/CloseAction.ts +106 -44
- package/src/runtime/drag/DragActionHandler.ts +71 -20
- package/src/runtime/drag/FrameDragAction.ts +202 -0
- package/src/runtime/drag/SplitAction.ts +106 -34
- package/src/runtime/drag/createDefaultHandlers.ts +19 -0
- package/src/runtime/drag/defaultDragActions.ts +19 -0
- package/src/runtime/drag/types.ts +90 -20
- package/src/runtime/helpers/createZoneSideClipPath.ts +41 -0
- package/src/runtime/helpers/doEdgesOverlap.ts +11 -5
- package/src/runtime/helpers/getDockBoundaries.ts +36 -0
- package/src/runtime/helpers/getEdgeLength.ts +10 -0
- package/src/runtime/helpers/getIntersections.ts +2 -2
- package/src/runtime/helpers/getIntersectionsCss.ts +2 -2
- package/src/runtime/helpers/getMoveEdgeInfo.ts +2 -2
- package/src/runtime/helpers/getResizeLimit.ts +2 -2
- package/src/runtime/helpers/getShapeSquareCss.ts +2 -2
- package/src/runtime/helpers/getVisualEdgeCss.ts +2 -2
- package/src/runtime/helpers/getVisualEdges.ts +5 -4
- package/src/runtime/helpers/index.ts +4 -0
- package/src/runtime/helpers/isEdgeEqual.ts +2 -4
- package/src/runtime/helpers/isWindowEdge.ts +2 -2
- package/src/runtime/helpers/isWindowEdgePoint.ts +2 -2
- package/src/runtime/helpers/moveEdge.ts +2 -2
- package/src/runtime/helpers/numberToScaledPercent.ts +3 -3
- package/src/runtime/helpers/numberToScaledSize.ts +2 -2
- package/src/runtime/helpers/rotateFrames.ts +45 -0
- package/src/runtime/helpers/scaledPointToPx.ts +13 -0
- package/src/runtime/helpers/toWindowCoord.ts +2 -2
- package/src/runtime/layout/applyFrameChanges.ts +39 -0
- package/src/runtime/layout/createSplitDecoFromDrag.ts +12 -6
- package/src/runtime/layout/{createSplitDecoEdge.ts → createSplitDecoShapes.ts} +17 -7
- package/src/runtime/layout/debugFrame.ts +1 -1
- package/src/runtime/layout/findSafeSplitEdge.ts +3 -3
- package/src/runtime/layout/frameCreate.ts +2 -2
- package/src/runtime/layout/getCloseFrameInfo.ts +21 -8
- package/src/runtime/layout/getDragZones.ts +48 -0
- package/src/runtime/layout/getFillEmptySpaceInfo.ts +177 -0
- package/src/runtime/layout/getFrameCollapseInfo.ts +164 -0
- package/src/runtime/layout/getFrameDockInfo.ts +126 -0
- package/src/runtime/layout/getFrameDragZones.ts +100 -0
- package/src/runtime/layout/getFrameRearrangeInfo.ts +261 -0
- package/src/runtime/layout/getFrameSplitInfo.ts +21 -8
- package/src/runtime/layout/getFrameSwapInfo.ts +45 -0
- package/src/runtime/layout/getFrameTo.ts +2 -2
- package/src/runtime/layout/getFrameUncollapseInfo.ts +160 -0
- package/src/runtime/layout/getFrameUndockInfo.ts +97 -0
- package/src/runtime/layout/getFramesRedistributeInfo.ts +98 -0
- package/src/runtime/layout/getWindowDragZones.ts +59 -0
- package/src/runtime/layout/index.ts +14 -5
- package/src/runtime/layout/isPointInRect.ts +7 -0
- package/src/runtime/layout/resizeFrame.ts +2 -2
- package/src/runtime/settings.ts +69 -49
- package/src/runtime/types/index.ts +143 -28
- package/dist/runtime/layout/closeFrame.d.ts +0 -5
- package/dist/runtime/layout/closeFrame.js +0 -13
- package/dist/runtime/layout/closeFrames.d.ts +0 -2
- package/dist/runtime/layout/closeFrames.js +0 -8
- package/dist/runtime/layout/createSplitDecoEdge.d.ts +0 -2
- package/dist/runtime/layout/frameSplit.d.ts +0 -16
- package/dist/runtime/layout/frameSplit.js +0 -9
- package/dist/runtime/layout/isPointInFrame.d.ts +0 -2
- package/src/runtime/layout/closeFrame.ts +0 -33
- package/src/runtime/layout/closeFrames.ts +0 -14
- package/src/runtime/layout/frameSplit.ts +0 -31
- package/src/runtime/layout/isPointInFrame.ts +0 -7
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { get, type RecordFromArray } from "@alanscodelog/utils"
|
|
2
2
|
|
|
3
|
-
import type { DragChangeHandler, DragState, IDragAction } from "./types.js"
|
|
3
|
+
import type { DragChangeHandler, DragChangeResult, DragState, IDragAction } from "./types.js"
|
|
4
|
+
|
|
5
|
+
import type { LayoutShape } from "../types/index.js"
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* Handles the lifecycle of a drag actions {@link IDragAction} and provides additional hooks.
|
|
@@ -15,11 +17,15 @@ export class DragActionHandler<
|
|
|
15
17
|
|
|
16
18
|
actions: TDragActions
|
|
17
19
|
|
|
18
|
-
eventCanceller: (() => void) | undefined = undefined
|
|
20
|
+
eventCanceller: ((e: PointerEvent | KeyboardEvent | undefined, state: DragState) => void) | undefined = undefined
|
|
21
|
+
|
|
22
|
+
boundCancel: (e: PointerEvent | KeyboardEvent | undefined, state: DragState) => void
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
defaultOnDragChange: DragChangeHandler = (type, _e, state, _forceRecalculateEdges, _cancel) => ({
|
|
25
|
+
updateEdges: type === "move" ? !state.isDraggingFromWindowEdge : true,
|
|
26
|
+
shapes: []
|
|
27
|
+
})
|
|
21
28
|
|
|
22
|
-
defaultOnDragChange: DragChangeHandler
|
|
23
29
|
hooks: {
|
|
24
30
|
/** Called while dragging during dragChange events. You can use this to update the dragging edges. */
|
|
25
31
|
onRecalculate?: () => void
|
|
@@ -35,17 +41,23 @@ export class DragActionHandler<
|
|
|
35
41
|
onEnd?: (context: { cancelled: boolean, applied: boolean }) => void
|
|
36
42
|
}
|
|
37
43
|
|
|
44
|
+
/** All action shapes merged into a single array. If using vue you can set this to a reactive array for reactivity. */
|
|
45
|
+
shapes: LayoutShape[] = []
|
|
46
|
+
|
|
47
|
+
/** All hint/error text from all actions, updated on every onDragChange. If using vue you can set this to a reactive object for reactivity. */
|
|
48
|
+
textHints: { actions: string[], errors: string[] } = { actions: [], errors: [] }
|
|
49
|
+
|
|
38
50
|
constructor(
|
|
51
|
+
actions: TRawDragActions,
|
|
52
|
+
hooks: DragActionHandler<TRawDragActions, TDragActions>["hooks"] = {},
|
|
39
53
|
/**
|
|
40
54
|
* Default onDragChange handler for when no action can handle the request.
|
|
41
55
|
*
|
|
42
56
|
* Should return true to allow the edges to be moved, or false to prevent it.
|
|
43
57
|
*/
|
|
44
|
-
defaultOnDragChange
|
|
45
|
-
actions: TRawDragActions,
|
|
46
|
-
hooks: DragActionHandler<TRawDragActions, TDragActions>["hooks"] = {}
|
|
58
|
+
defaultOnDragChange?: DragChangeHandler
|
|
47
59
|
) {
|
|
48
|
-
this.defaultOnDragChange = defaultOnDragChange
|
|
60
|
+
if (defaultOnDragChange) this.defaultOnDragChange = defaultOnDragChange
|
|
49
61
|
this.hooks = hooks
|
|
50
62
|
this.actions = {} as any
|
|
51
63
|
for (const action of actions) {
|
|
@@ -65,7 +77,7 @@ export class DragActionHandler<
|
|
|
65
77
|
let cancelled = false
|
|
66
78
|
this.hooks.onEvent?.(e, () => {
|
|
67
79
|
cancelled = true
|
|
68
|
-
this.cancel()
|
|
80
|
+
this.cancel(e, state)
|
|
69
81
|
})
|
|
70
82
|
if (cancelled) return
|
|
71
83
|
|
|
@@ -87,7 +99,7 @@ export class DragActionHandler<
|
|
|
87
99
|
|
|
88
100
|
if (oldActiveAction !== this.activeAction) {
|
|
89
101
|
if (oldActiveAction) {
|
|
90
|
-
this.actions[oldActiveAction]!.cancel()
|
|
102
|
+
this.actions[oldActiveAction]!.cancel(e, state)
|
|
91
103
|
}
|
|
92
104
|
this.hooks.onRequestChange?.(this.activeAction)
|
|
93
105
|
}
|
|
@@ -104,22 +116,38 @@ export class DragActionHandler<
|
|
|
104
116
|
e: T extends "end" ? PointerEvent | undefined : PointerEvent,
|
|
105
117
|
state: DragState,
|
|
106
118
|
forceRecalculateEdges: () => void,
|
|
107
|
-
cancel
|
|
108
|
-
):
|
|
119
|
+
cancel: (e: PointerEvent | KeyboardEvent | undefined, state: DragState) => void
|
|
120
|
+
): DragChangeResult {
|
|
109
121
|
if (type === "start") {
|
|
110
122
|
this.eventCanceller = cancel
|
|
111
|
-
state.isDragging = true
|
|
112
123
|
this.eventHandler(e!, state, forceRecalculateEdges)
|
|
113
124
|
}
|
|
114
125
|
if (type === "end") {
|
|
115
|
-
state.isDragging = false
|
|
116
126
|
this.activeAction = undefined
|
|
117
127
|
}
|
|
118
128
|
|
|
119
129
|
if (this.activeAction) {
|
|
120
|
-
|
|
130
|
+
const res = this.actions[this.activeAction]!.onDragChange(type, e, state, forceRecalculateEdges, this.boundCancel)
|
|
131
|
+
// in case it's a vue reactive array
|
|
132
|
+
this.shapes.splice(0, this.shapes.length, ...res.shapes)
|
|
133
|
+
this.setTextHints(type)
|
|
134
|
+
return res
|
|
135
|
+
}
|
|
136
|
+
this.setTextHints(type)
|
|
137
|
+
const res = this.defaultOnDragChange(type, e, state, forceRecalculateEdges, this.boundCancel)
|
|
138
|
+
this.shapes.splice(0, this.shapes.length, ...res.shapes)
|
|
139
|
+
return res
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
setTextHints(type: "start" | "move" | "end") {
|
|
143
|
+
// again in case it's a vue reactive object
|
|
144
|
+
this.textHints.actions = []
|
|
145
|
+
this.textHints.errors = []
|
|
146
|
+
for (const action of Object.values<IDragAction>(this.actions)) {
|
|
147
|
+
const res = action.getTextHints?.(type)
|
|
148
|
+
this.textHints.actions.push(...(res?.actions ?? []))
|
|
149
|
+
this.textHints.errors.push(...(res?.errors ?? []))
|
|
121
150
|
}
|
|
122
|
-
return this.defaultOnDragChange(type, e, state, forceRecalculateEdges, this.boundCancel)
|
|
123
151
|
}
|
|
124
152
|
|
|
125
153
|
onDragApply(
|
|
@@ -135,12 +163,35 @@ export class DragActionHandler<
|
|
|
135
163
|
return true
|
|
136
164
|
}
|
|
137
165
|
|
|
138
|
-
cancel(): void {
|
|
166
|
+
cancel(e: PointerEvent | KeyboardEvent | undefined, state: DragState): void {
|
|
139
167
|
if (this.activeAction) {
|
|
140
|
-
this.actions[this.activeAction].cancel()
|
|
168
|
+
this.actions[this.activeAction].cancel(e, state)
|
|
141
169
|
}
|
|
142
170
|
this.activeAction = undefined
|
|
143
|
-
this.eventCanceller?.()
|
|
171
|
+
this.eventCanceller?.(e, state)
|
|
144
172
|
this.hooks.onEnd?.({ cancelled: true, applied: false })
|
|
145
173
|
}
|
|
174
|
+
|
|
175
|
+
static debugState(
|
|
176
|
+
pluginName: string,
|
|
177
|
+
type: "before" | "after" | string,
|
|
178
|
+
state: DragState,
|
|
179
|
+
pluginState: Record<string, any> = {},
|
|
180
|
+
/** Object key to filter the state by, e.g. state.win.frames. If boolean is ignored. The idea is you pass this.debug and users can set this.debug to a string to filter. */
|
|
181
|
+
key?: string | boolean
|
|
182
|
+
): void {
|
|
183
|
+
let res = { state, pluginState }
|
|
184
|
+
let pickedRes: any = {}
|
|
185
|
+
if (typeof key === "string" && key !== "") {
|
|
186
|
+
pickedRes = {} as any
|
|
187
|
+
const paths = key.split(",").map(_ => _.trim()).filter(_ => _)
|
|
188
|
+
for (const path of paths) {
|
|
189
|
+
pickedRes[path] = get(res, path.split("."))
|
|
190
|
+
}
|
|
191
|
+
res = pickedRes as any
|
|
192
|
+
}
|
|
193
|
+
const resString = JSON.stringify(res, null, 2)
|
|
194
|
+
// eslint-disable-next-line no-console
|
|
195
|
+
console.log(`[Drag Action Debug - ${pluginName}]:`, type, resString)
|
|
196
|
+
}
|
|
146
197
|
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { DragActionHandler } from "./DragActionHandler.js"
|
|
2
|
+
|
|
3
|
+
import { createZoneSideClipPath } from "../helpers/createZoneSideClipPath.js"
|
|
4
|
+
import { applyFrameChanges } from "../layout/applyFrameChanges.js"
|
|
5
|
+
import { getDragZones } from "../layout/getDragZones.js"
|
|
6
|
+
import { getFrameDockInfo } from "../layout/getFrameDockInfo.js"
|
|
7
|
+
import { getFrameRearrangeInfo } from "../layout/getFrameRearrangeInfo.js"
|
|
8
|
+
import { getFrameSwapInfo } from "../layout/getFrameSwapInfo.js"
|
|
9
|
+
import { settings } from "../settings.js"
|
|
10
|
+
import type { ActionDragChangeResult, DragState, DragZone, FrameDragDeco, IDragAction, LayoutChange } from "../types/index.js"
|
|
11
|
+
import type { KnownError } from "../utils/KnownError.js"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
export class FrameDragAction implements IDragAction {
|
|
15
|
+
name = "frameDrag" as const
|
|
16
|
+
state: {
|
|
17
|
+
lastReturn?: LayoutChange<"split" | "swap" | "rearrange" | "dock"> | KnownError
|
|
18
|
+
} = {
|
|
19
|
+
lastReturn: undefined
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
debug: boolean | string = false
|
|
23
|
+
textHints: { actions: string[], errors: string[] } = { actions: [], errors: [] }
|
|
24
|
+
|
|
25
|
+
handleEvent: (e: PointerEvent | KeyboardEvent, state: DragState) => boolean
|
|
26
|
+
= (_e: PointerEvent | KeyboardEvent, state: DragState) => state.isDragging === "frame"
|
|
27
|
+
|
|
28
|
+
dragHints: {
|
|
29
|
+
actions: { split: string, swap: string, rearrange: string, dock: string }
|
|
30
|
+
transformError: (e: KnownError) => string
|
|
31
|
+
} = {
|
|
32
|
+
actions: {
|
|
33
|
+
split: "Drop to Split the current frame.",
|
|
34
|
+
swap: "Drop to Swap the frames.",
|
|
35
|
+
rearrange: "Drop to Move the frame.",
|
|
36
|
+
dock: "Drop to Dock the frame."
|
|
37
|
+
},
|
|
38
|
+
transformError: e => e.message
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
modifyDecos: (shapes: FrameDragDeco[]) => void = () => { }
|
|
42
|
+
hooks: {
|
|
43
|
+
onStart?: () => void
|
|
44
|
+
onCancel?: () => void
|
|
45
|
+
onError?: (e: KnownError) => void
|
|
46
|
+
} = {}
|
|
47
|
+
|
|
48
|
+
constructor(
|
|
49
|
+
handleEvent?: FrameDragAction["handleEvent"],
|
|
50
|
+
/** Modify the created decos before they are rendered. */
|
|
51
|
+
modifyDecos?: FrameDragAction["modifyDecos"],
|
|
52
|
+
hooks: FrameDragAction["hooks"] = {},
|
|
53
|
+
config?: {
|
|
54
|
+
debug?: boolean | string
|
|
55
|
+
}
|
|
56
|
+
) {
|
|
57
|
+
if (handleEvent !== undefined) this.handleEvent = handleEvent
|
|
58
|
+
if (modifyDecos !== undefined) this.modifyDecos = modifyDecos
|
|
59
|
+
this.hooks = hooks
|
|
60
|
+
if (config?.debug) this.debug = true
|
|
61
|
+
|
|
62
|
+
this.reset()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
reset(): void {
|
|
66
|
+
this.state = {
|
|
67
|
+
lastReturn: undefined
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
setTextHints(result: LayoutChange<"split" | "swap" | "rearrange" | "dock"> | KnownError | undefined): void {
|
|
72
|
+
if (result === undefined || !result.info) {
|
|
73
|
+
this.textHints.actions = []
|
|
74
|
+
this.textHints.errors = []
|
|
75
|
+
} else if (result instanceof Error) {
|
|
76
|
+
this.textHints.actions = []
|
|
77
|
+
this.textHints.errors = [this.dragHints.transformError(result)]
|
|
78
|
+
} else {
|
|
79
|
+
const text = this.dragHints.actions[result.info]
|
|
80
|
+
this.textHints.actions = [text]
|
|
81
|
+
this.textHints.errors = []
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
getTextHints(type: "start" | "move" | "end"): {
|
|
86
|
+
actions: string[]
|
|
87
|
+
errors: string[]
|
|
88
|
+
} {
|
|
89
|
+
if (type === "end") { this.setTextHints(undefined) }
|
|
90
|
+
return this.textHints
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
getDecos(
|
|
95
|
+
matchedZone: DragZone | undefined,
|
|
96
|
+
state: DragState,
|
|
97
|
+
result: LayoutChange<"split" | "swap" | "rearrange" | "dock"> | KnownError | undefined
|
|
98
|
+
): FrameDragDeco[] {
|
|
99
|
+
let decos: FrameDragDeco[] = []
|
|
100
|
+
const isError = result instanceof Error
|
|
101
|
+
const frame = state.dragHoveredFrame
|
|
102
|
+
|
|
103
|
+
if (!matchedZone || !frame) {
|
|
104
|
+
decos = []
|
|
105
|
+
this.modifyDecos(decos)
|
|
106
|
+
return decos
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const clipPath = createZoneSideClipPath(matchedZone, settings.zoneSizes)
|
|
110
|
+
|
|
111
|
+
const classes = isError
|
|
112
|
+
? `deco-frame-drag deco-frame-drag-error deco-frame-drag-${matchedZone.type}-${matchedZone.side} bg-red-500/50`
|
|
113
|
+
: `deco-frame-drag deco-frame-drag-${matchedZone.type}-${matchedZone.side} bg-blue-500/50`
|
|
114
|
+
|
|
115
|
+
decos = [{
|
|
116
|
+
id: frame.id,
|
|
117
|
+
type: "drop",
|
|
118
|
+
position: matchedZone.side ?? "center",
|
|
119
|
+
shapes: [
|
|
120
|
+
{
|
|
121
|
+
type: "square",
|
|
122
|
+
data: matchedZone,
|
|
123
|
+
attrs: clipPath
|
|
124
|
+
? { class: classes, style: `clip-path:${clipPath}` }
|
|
125
|
+
: { class: classes }
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
}]
|
|
129
|
+
|
|
130
|
+
this.modifyDecos(decos)
|
|
131
|
+
return decos
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
canHandleRequest(e: PointerEvent | KeyboardEvent, state: DragState): boolean {
|
|
135
|
+
const res = this.handleEvent(e, state)
|
|
136
|
+
this.setTextHints(undefined)
|
|
137
|
+
if (res) {
|
|
138
|
+
this.hooks.onStart?.()
|
|
139
|
+
return true
|
|
140
|
+
}
|
|
141
|
+
this.reset()
|
|
142
|
+
return false
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
onDragChange<T extends "start" | "move" | "end">(
|
|
146
|
+
_type: T,
|
|
147
|
+
_e: PointerEvent | undefined,
|
|
148
|
+
state: DragState
|
|
149
|
+
): ActionDragChangeResult {
|
|
150
|
+
const { win, draggingFrameId, dragHoveredFrame } = state
|
|
151
|
+
const matchedZone = getDragZones(state, settings.zoneSizes)
|
|
152
|
+
|
|
153
|
+
if (!matchedZone) {
|
|
154
|
+
this.state.lastReturn = undefined
|
|
155
|
+
} else if (dragHoveredFrame && matchedZone.type === "frame" && matchedZone.side) {
|
|
156
|
+
if (matchedZone.side !== "center") {
|
|
157
|
+
this.state.lastReturn = getFrameRearrangeInfo(win, draggingFrameId!, dragHoveredFrame.id!, matchedZone.side)
|
|
158
|
+
} else {
|
|
159
|
+
this.state.lastReturn = getFrameSwapInfo(win, draggingFrameId!, dragHoveredFrame.id!)
|
|
160
|
+
// for the hints
|
|
161
|
+
if (!(this.state.lastReturn instanceof Error)) this.state.lastReturn.info = "swap"
|
|
162
|
+
}
|
|
163
|
+
} else if (matchedZone.type === "window") {
|
|
164
|
+
this.state.lastReturn = getFrameDockInfo(win, draggingFrameId!, matchedZone.side)
|
|
165
|
+
// for the hints
|
|
166
|
+
if (!(this.state.lastReturn instanceof Error)) this.state.lastReturn.info = "dock"
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
this.setTextHints(this.state.lastReturn)
|
|
170
|
+
const decos = this.getDecos(matchedZone, state, this.state.lastReturn)
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
shapes: decos.flatMap(_ => _.shapes),
|
|
174
|
+
updateEdges: false,
|
|
175
|
+
showDragging: false
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
onDragApply(state: DragState): boolean {
|
|
180
|
+
const result = this.state.lastReturn
|
|
181
|
+
if (!result || !state.dragHoveredFrame || !state.draggingFrameId) {
|
|
182
|
+
this.reset()
|
|
183
|
+
return true
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
if (result instanceof Error) {
|
|
188
|
+
this.hooks.onError?.(result)
|
|
189
|
+
} else {
|
|
190
|
+
if (this.debug) { DragActionHandler.debugState(this.name, "before", state, this.state, this.debug) }
|
|
191
|
+
applyFrameChanges(state.win, result)
|
|
192
|
+
if (this.debug) { DragActionHandler.debugState(this.name, "after", state, this.state, this.debug) }
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
this.reset()
|
|
196
|
+
return true
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
cancel(): void {
|
|
200
|
+
this.reset()
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -1,29 +1,45 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { DragActionHandler } from "./DragActionHandler.js"
|
|
2
|
+
import type { ActionDragChangeResult, DragState, IDragAction } from "./types.js"
|
|
2
3
|
|
|
3
4
|
import { getEdgeOrientation } from "../helpers/getEdgeOrientation.js"
|
|
4
5
|
import { oppositeSide } from "../helpers/oppositeSide.js"
|
|
6
|
+
import { applyFrameChanges } from "../layout/applyFrameChanges.js"
|
|
5
7
|
import { createSplitDecoFromDrag } from "../layout/createSplitDecoFromDrag.js"
|
|
6
|
-
import { frameSplit } from "../layout/frameSplit.js"
|
|
7
8
|
import { getFrameSplitInfo } from "../layout/getFrameSplitInfo.js"
|
|
8
|
-
import type { SplitDeco } from "../types/index.js"
|
|
9
|
+
import type { LayoutShape, Point, SplitDeco } from "../types/index.js"
|
|
9
10
|
import type { KnownError } from "../utils/KnownError.js"
|
|
10
11
|
|
|
11
12
|
export type SplitInfo = Exclude<ReturnType<typeof getFrameSplitInfo>, KnownError>
|
|
13
|
+
|
|
12
14
|
export class SplitAction implements IDragAction {
|
|
13
15
|
name = "split" as const
|
|
14
16
|
|
|
15
17
|
state: {
|
|
16
|
-
allowed: false
|
|
17
|
-
res: SplitInfo | undefined
|
|
18
|
-
cacheKey: string | undefined
|
|
19
|
-
} | {
|
|
20
18
|
allowed: true
|
|
21
19
|
res: SplitInfo
|
|
22
|
-
|
|
20
|
+
lastPoint: Point | undefined
|
|
21
|
+
lastReturn: undefined
|
|
22
|
+
} | {
|
|
23
|
+
allowed: false
|
|
24
|
+
res: SplitInfo | undefined
|
|
25
|
+
lastPoint: Point | undefined
|
|
26
|
+
lastReturn: ActionDragChangeResult | undefined
|
|
23
27
|
} = {} as any // this is initialized by `this.reset()`
|
|
24
28
|
|
|
29
|
+
debug: boolean | string = false
|
|
30
|
+
textHints: { actions: string[], errors: string[] } = { actions: [], errors: [] }
|
|
31
|
+
splitHints: {
|
|
32
|
+
action: string
|
|
33
|
+
transformError: (e: KnownError) => string
|
|
34
|
+
} = {
|
|
35
|
+
action: "Hold Alt to Split",
|
|
36
|
+
transformError: e => e.message
|
|
37
|
+
}
|
|
38
|
+
|
|
25
39
|
handleEvent: (e: PointerEvent | KeyboardEvent, state: DragState) => boolean
|
|
26
|
-
|
|
40
|
+
= (e: PointerEvent | KeyboardEvent, state: DragState) => e.altKey || state.isDraggingFromWindowEdge
|
|
41
|
+
|
|
42
|
+
modifyDecos: (shapes: SplitDeco[]) => void = () => { }
|
|
27
43
|
hooks: {
|
|
28
44
|
onStart?: () => void
|
|
29
45
|
onCancel?: () => void
|
|
@@ -31,26 +47,37 @@ export class SplitAction implements IDragAction {
|
|
|
31
47
|
}
|
|
32
48
|
|
|
33
49
|
constructor(
|
|
34
|
-
handleEvent
|
|
35
|
-
|
|
36
|
-
|
|
50
|
+
handleEvent?: SplitAction["handleEvent"],
|
|
51
|
+
/** Modify the created decos before they are rendered. */
|
|
52
|
+
modifyDecos?: SplitAction["modifyDecos"],
|
|
53
|
+
hooks: SplitAction["hooks"] = {},
|
|
54
|
+
config?: {
|
|
55
|
+
debug?: boolean | string
|
|
56
|
+
splitHints?: Partial<SplitAction["splitHints"]>
|
|
57
|
+
}
|
|
37
58
|
) {
|
|
38
|
-
this.handleEvent = handleEvent
|
|
39
|
-
this.
|
|
59
|
+
if (handleEvent !== undefined) this.handleEvent = handleEvent
|
|
60
|
+
if (modifyDecos !== undefined) this.modifyDecos = modifyDecos
|
|
61
|
+
|
|
40
62
|
this.hooks = hooks
|
|
41
63
|
this.reset()
|
|
64
|
+
if (config?.debug) this.debug = true
|
|
65
|
+
if (config?.splitHints?.action) this.splitHints.action = config.splitHints.action
|
|
66
|
+
if (config?.splitHints?.transformError) this.splitHints.transformError = config.splitHints.transformError
|
|
42
67
|
}
|
|
43
68
|
|
|
44
69
|
reset(): void {
|
|
45
70
|
this.state = {
|
|
46
71
|
allowed: false,
|
|
47
72
|
res: undefined,
|
|
48
|
-
|
|
73
|
+
lastPoint: undefined,
|
|
74
|
+
lastReturn: undefined
|
|
49
75
|
}
|
|
50
|
-
this.
|
|
76
|
+
this.modifyDecos([])
|
|
51
77
|
}
|
|
52
78
|
|
|
53
|
-
|
|
79
|
+
getDecos(state: DragState): SplitDeco[] {
|
|
80
|
+
let decos: SplitDeco[] = []
|
|
54
81
|
const {
|
|
55
82
|
win,
|
|
56
83
|
isDragging,
|
|
@@ -67,15 +94,16 @@ export class SplitAction implements IDragAction {
|
|
|
67
94
|
dragDirections[oppositeOrientation]!,
|
|
68
95
|
dragPoint!
|
|
69
96
|
)
|
|
70
|
-
|
|
71
|
-
} else {
|
|
72
|
-
this.updateSplitDecos([])
|
|
97
|
+
decos = [deco]
|
|
73
98
|
}
|
|
99
|
+
this.modifyDecos(decos)
|
|
100
|
+
return decos
|
|
74
101
|
}
|
|
75
102
|
|
|
76
103
|
canHandleRequest(e: PointerEvent | KeyboardEvent, state: DragState): boolean {
|
|
77
104
|
const { draggingEdges } = state
|
|
78
105
|
if (draggingEdges.length !== 1) return false
|
|
106
|
+
this.setTextHints(state.isDragging === "edge" ? true : undefined)
|
|
79
107
|
if (this.handleEvent(e, state)) {
|
|
80
108
|
this.hooks.onStart?.()
|
|
81
109
|
return true
|
|
@@ -84,26 +112,52 @@ export class SplitAction implements IDragAction {
|
|
|
84
112
|
return false
|
|
85
113
|
}
|
|
86
114
|
|
|
115
|
+
setTextHints(result: true | SplitInfo | KnownError | undefined): void {
|
|
116
|
+
if (result === undefined) {
|
|
117
|
+
this.textHints.actions = []
|
|
118
|
+
this.textHints.errors = []
|
|
119
|
+
} else if (result instanceof Error) {
|
|
120
|
+
this.textHints.actions = []
|
|
121
|
+
this.textHints.errors = [this.splitHints.transformError(result)]
|
|
122
|
+
} else {
|
|
123
|
+
this.textHints.actions = [this.splitHints.action]
|
|
124
|
+
this.textHints.errors = []
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getTextHints(type: "start" | "move" | "end"): {
|
|
129
|
+
actions: string[]
|
|
130
|
+
errors: string[]
|
|
131
|
+
} {
|
|
132
|
+
if (type === "end") { this.setTextHints(undefined) }
|
|
133
|
+
return this.textHints
|
|
134
|
+
}
|
|
135
|
+
|
|
87
136
|
calculateSplitRequest(state: DragState): boolean {
|
|
88
137
|
const {
|
|
89
138
|
dragHoveredFrame,
|
|
90
139
|
dragDirections,
|
|
91
140
|
draggingEdges,
|
|
92
|
-
dragPoint
|
|
141
|
+
dragPoint,
|
|
142
|
+
win
|
|
93
143
|
} = state
|
|
94
144
|
const oppositeOrientation = oppositeSide(getEdgeOrientation(draggingEdges[0]))
|
|
145
|
+
const originalDragHoveredFrame = win.frames[dragHoveredFrame!.id]
|
|
95
146
|
const canSplit = getFrameSplitInfo(
|
|
96
|
-
|
|
147
|
+
originalDragHoveredFrame!,
|
|
97
148
|
dragDirections[oppositeOrientation]!,
|
|
98
149
|
dragPoint!
|
|
99
150
|
)
|
|
151
|
+
this.setTextHints(canSplit)
|
|
100
152
|
if (!(canSplit instanceof Error)) {
|
|
101
153
|
this.state.allowed = true
|
|
102
154
|
this.state.res = canSplit
|
|
103
|
-
this.state.
|
|
155
|
+
this.state.lastPoint = dragPoint ? { ...dragPoint } : undefined
|
|
104
156
|
return true
|
|
105
157
|
} else {
|
|
106
158
|
this.hooks.onError?.(canSplit)
|
|
159
|
+
this.state.lastPoint = undefined
|
|
160
|
+
this.state.allowed = false
|
|
107
161
|
return false
|
|
108
162
|
}
|
|
109
163
|
}
|
|
@@ -112,29 +166,47 @@ export class SplitAction implements IDragAction {
|
|
|
112
166
|
_type: "start" | "end" | "move",
|
|
113
167
|
_e: PointerEvent | undefined,
|
|
114
168
|
state: DragState
|
|
115
|
-
):
|
|
169
|
+
): ActionDragChangeResult {
|
|
116
170
|
const { dragHoveredFrame } = state
|
|
117
171
|
let ok = false
|
|
118
172
|
if (dragHoveredFrame) {
|
|
119
|
-
if (this.state.
|
|
120
|
-
this.
|
|
121
|
-
return true
|
|
173
|
+
if (this.state.lastPoint?.x === state.dragPoint?.x && this.state.lastPoint?.y === state.dragPoint?.y && this.state.lastReturn) {
|
|
174
|
+
return this.state.lastReturn!
|
|
122
175
|
}
|
|
123
176
|
ok = this.calculateSplitRequest(state)
|
|
124
177
|
}
|
|
125
|
-
this.
|
|
178
|
+
const decos = this.getDecos(state)
|
|
179
|
+
|
|
126
180
|
if (!ok) {
|
|
127
|
-
|
|
181
|
+
if (dragHoveredFrame && dragHoveredFrame.docked) {
|
|
182
|
+
const errorDeco: LayoutShape = {
|
|
183
|
+
type: "square",
|
|
184
|
+
data: {
|
|
185
|
+
x: dragHoveredFrame.x,
|
|
186
|
+
y: dragHoveredFrame.y,
|
|
187
|
+
width: dragHoveredFrame.width,
|
|
188
|
+
height: dragHoveredFrame.height
|
|
189
|
+
},
|
|
190
|
+
attrs: { class: "deco-split-error bg-red-500/50" }
|
|
191
|
+
}
|
|
192
|
+
this.state.lastReturn = { updateEdges: true, shapes: [errorDeco], showDragging: false }
|
|
193
|
+
return this.state.lastReturn
|
|
194
|
+
}
|
|
128
195
|
}
|
|
129
|
-
|
|
196
|
+
this.state.lastReturn = { updateEdges: false, shapes: decos.flatMap(_ => _.shapes), showDragging: false }
|
|
197
|
+
return this.state.lastReturn
|
|
130
198
|
}
|
|
131
199
|
|
|
132
200
|
onDragApply(state: DragState): boolean {
|
|
133
|
-
if (this.state.res) {
|
|
201
|
+
if (this.state.res && state.dragHoveredFrame) {
|
|
134
202
|
// this only caches once per frame hovered over
|
|
135
|
-
// so the drag position is outdated
|
|
136
|
-
this.calculateSplitRequest(state)
|
|
137
|
-
|
|
203
|
+
// so the drag position is outdated, we must recalculate
|
|
204
|
+
const ok = this.calculateSplitRequest(state)
|
|
205
|
+
if (ok) {
|
|
206
|
+
if (this.debug) { DragActionHandler.debugState(this.name, "before", state, this.state, this.debug) }
|
|
207
|
+
applyFrameChanges(state.win, this.state.res!)
|
|
208
|
+
if (this.debug) { DragActionHandler.debugState(this.name, "after", state, this.state, this.debug) }
|
|
209
|
+
}
|
|
138
210
|
}
|
|
139
211
|
this.reset()
|
|
140
212
|
return true
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CloseAction } from "./CloseAction"
|
|
2
|
+
import { FrameDragAction } from "./FrameDragAction.js"
|
|
3
|
+
import { SplitAction } from "./SplitAction.js"
|
|
4
|
+
import type { IDragAction } from "./types.js"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates the default drag actions (Split, Close, FrameDrag).
|
|
8
|
+
*/
|
|
9
|
+
export function createDefaultHandlers(config: {
|
|
10
|
+
debugSplit?: boolean
|
|
11
|
+
debugClose?: boolean
|
|
12
|
+
debugFrameDrag?: boolean
|
|
13
|
+
} = {}): IDragAction[] {
|
|
14
|
+
return [
|
|
15
|
+
new SplitAction(undefined, undefined, undefined, { debug: config.debugSplit }),
|
|
16
|
+
new CloseAction(undefined, undefined, undefined, { debug: config.debugClose }),
|
|
17
|
+
new FrameDragAction(undefined, undefined, undefined, { debug: config.debugFrameDrag })
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CloseAction } from "./CloseAction"
|
|
2
|
+
import { FrameDragAction } from "./FrameDragAction.js"
|
|
3
|
+
import { SplitAction } from "./SplitAction.js"
|
|
4
|
+
import type { IDragAction } from "./types.js"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates the default drag actions (Split, Close, FrameDrag).
|
|
8
|
+
*/
|
|
9
|
+
export function createDefaultHandlers(config: {
|
|
10
|
+
debugSplit?: boolean
|
|
11
|
+
debugClose?: boolean
|
|
12
|
+
debugFrameDrag?: boolean
|
|
13
|
+
}): IDragAction[] {
|
|
14
|
+
return [
|
|
15
|
+
new SplitAction(undefined, undefined, undefined, { debug: config.debugSplit }),
|
|
16
|
+
new CloseAction(undefined, undefined, undefined, { debug: config.debugClose }),
|
|
17
|
+
new FrameDragAction(undefined, undefined, undefined, { debug: config.debugFrameDrag })
|
|
18
|
+
]
|
|
19
|
+
}
|