@witchcraft/layout 0.4.4 → 0.5.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/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 +44 -44
- package/dist/runtime/components/LayoutWindow.vue +27 -27
- package/dist/runtime/components/LayoutWindow.vue.d.ts +44 -44
- package/dist/runtime/composables/useFrames.d.ts +107 -109
- package/dist/runtime/composables/useFrames.js +107 -99
- 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/helpers/createZoneSideClipPath.d.ts +2 -2
- 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 +2 -2
- package/dist/runtime/helpers/index.js +2 -2
- package/dist/runtime/layout/createSplitDecoFromDrag.d.ts +1 -1
- package/dist/runtime/layout/createSplitDecoFromDrag.js +2 -2
- 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/getFrameRearrangeInfo.d.ts +2 -2
- package/dist/runtime/layout/getFrameRearrangeInfo.js +8 -8
- package/dist/runtime/layout/getFrameSplitInfo.d.ts +1 -1
- package/dist/runtime/layout/getFrameSplitInfo.js +2 -2
- 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/{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 +3 -3
- package/dist/runtime/layout/index.js +3 -3
- package/dist/runtime/{drag/DragActionHandler.d.ts → move/ActionHandler.d.ts} +19 -21
- package/dist/runtime/{drag/DragActionHandler.js → move/ActionHandler.js} +21 -18
- package/dist/runtime/{drag → move}/CloseAction.d.ts +9 -8
- package/dist/runtime/{drag → move}/CloseAction.js +31 -27
- package/dist/runtime/{drag → move}/FrameDragAction.d.ts +9 -8
- package/dist/runtime/{drag → move}/FrameDragAction.js +23 -22
- 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 +10 -9
- package/dist/runtime/{drag → move}/SplitAction.js +53 -44
- package/dist/runtime/{drag → move}/createDefaultHandlers.d.ts +2 -2
- package/dist/runtime/types/index.d.ts +192 -171
- package/dist/runtime/types/index.js +2 -2
- package/dist/runtime/types/vue.d.ts +14 -16
- package/dist/runtime/types/vue.js +1 -1
- package/package.json +1 -1
- 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 +32 -32
- package/src/runtime/composables/useFrames.ts +115 -104
- package/src/runtime/demo/App.vue +18 -18
- package/src/runtime/demo/DemoControls.vue +5 -5
- package/src/runtime/helpers/createZoneSideClipPath.ts +2 -2
- 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 +2 -2
- package/src/runtime/layout/createSplitDecoFromDrag.ts +2 -2
- package/src/runtime/layout/findSafeSplitEdge.ts +4 -4
- package/src/runtime/layout/getCloseFrameInfo.ts +4 -4
- package/src/runtime/layout/getFrameRearrangeInfo.ts +10 -10
- package/src/runtime/layout/getFrameSplitInfo.ts +5 -5
- package/src/runtime/layout/{getFrameDragZones.ts → getFrameZones.ts} +4 -4
- package/src/runtime/layout/{getWindowDragZones.ts → getWindowZones.ts} +1 -1
- package/src/runtime/layout/getZones.ts +48 -0
- package/src/runtime/layout/index.ts +3 -3
- package/src/runtime/{drag/DragActionHandler.ts → move/ActionHandler.ts} +48 -41
- package/src/runtime/{drag → move}/CloseAction.ts +42 -37
- package/src/runtime/{drag → move}/FrameDragAction.ts +37 -36
- package/src/runtime/{drag/DragDirectionStore.ts → move/MoveDirectionStore.ts} +10 -10
- package/src/runtime/{drag → move}/SplitAction.ts +64 -54
- package/src/runtime/{drag → move}/createDefaultHandlers.ts +2 -2
- package/src/runtime/types/index.ts +90 -69
- package/src/runtime/types/vue.ts +14 -14
- package/dist/runtime/layout/getDragZones.d.ts +0 -8
- package/dist/runtime/layout/getDragZones.js +0 -32
- package/src/runtime/layout/getDragZones.ts +0 -48
- /package/dist/runtime/{drag → move}/createDefaultHandlers.js +0 -0
|
@@ -7,7 +7,7 @@ import { getSideTouching } from "../helpers/getSideTouching.js"
|
|
|
7
7
|
import { isEdgeEqual } from "../helpers/isEdgeEqual.js"
|
|
8
8
|
import { oppositeSide } from "../helpers/oppositeSide.js"
|
|
9
9
|
import { sideToDirection } from "../helpers/sideToDirection.js"
|
|
10
|
-
import type {
|
|
10
|
+
import type { LayoutChange, LayoutWindow, Zone } from "../types/index.js"
|
|
11
11
|
import { LAYOUT_ERROR } from "../types/index.js"
|
|
12
12
|
import { KnownError } from "../utils/KnownError.js"
|
|
13
13
|
|
|
@@ -150,9 +150,9 @@ import { KnownError } from "../utils/KnownError.js"
|
|
|
150
150
|
|
|
151
151
|
export function getFrameRearrangeInfo(
|
|
152
152
|
win: LayoutWindow,
|
|
153
|
-
|
|
153
|
+
movingFrameId: string,
|
|
154
154
|
hoveredFrameId: string,
|
|
155
|
-
zoneSide:
|
|
155
|
+
zoneSide: Zone["side"]
|
|
156
156
|
): LayoutChange<"split" | "swap" | "rearrange">
|
|
157
157
|
| KnownError<typeof LAYOUT_ERROR.CANT_SWAP_WITH_SELF>
|
|
158
158
|
| KnownError<typeof LAYOUT_ERROR.CANT_SPLIT_FRAME_TOO_SMALL>
|
|
@@ -161,10 +161,10 @@ export function getFrameRearrangeInfo(
|
|
|
161
161
|
| KnownError<typeof LAYOUT_ERROR.CANT_REARRANGE_DOCKED_WITH_NON_DOCKED>
|
|
162
162
|
| KnownError<typeof LAYOUT_ERROR.CANT_SPLIT_DOCKED_FRAME>
|
|
163
163
|
| KnownError<typeof LAYOUT_ERROR.NO_FILL_CANDIDATES> {
|
|
164
|
-
const draggingFrame = { ...win.frames[
|
|
164
|
+
const draggingFrame = { ...win.frames[movingFrameId] }
|
|
165
165
|
const hoveredFrame = { ...win.frames[hoveredFrameId] }
|
|
166
166
|
|
|
167
|
-
if (
|
|
167
|
+
if (movingFrameId === hoveredFrameId) {
|
|
168
168
|
if (zoneSide === "center") return new KnownError(LAYOUT_ERROR.CANT_SWAP_WITH_SELF, `Can't swap frame with self.`, { frame: hoveredFrame, zoneSide })
|
|
169
169
|
}
|
|
170
170
|
|
|
@@ -180,12 +180,12 @@ export function getFrameRearrangeInfo(
|
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
if (draggingFrame.docked && !hoveredFrame.docked) { // center swapping IS allowed above
|
|
183
|
-
return new KnownError(LAYOUT_ERROR.CANT_REARRANGE_DOCKED_WITH_NON_DOCKED, `Can't rearrange docked frame ${
|
|
183
|
+
return new KnownError(LAYOUT_ERROR.CANT_REARRANGE_DOCKED_WITH_NON_DOCKED, `Can't rearrange docked frame ${movingFrameId} with non-docked frame ${hoveredFrameId}, can only swap.`, { movingFrameId, hoveredFrameId, zoneSide })
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
// block edge rearrange on docked frames
|
|
187
187
|
if (hoveredFrame.docked) { // again center swapping docked to undocked and vice versa is allowed
|
|
188
|
-
return new KnownError(LAYOUT_ERROR.CANT_REARRANGE_WITH_DOCKED_EDGES, `Can't rearrange with docked frame ${hoveredFrameId} edge, can only swap.`, {
|
|
188
|
+
return new KnownError(LAYOUT_ERROR.CANT_REARRANGE_WITH_DOCKED_EDGES, `Can't rearrange with docked frame ${hoveredFrameId} edge, can only swap.`, { movingFrameId, hoveredFrameId, zoneSide })
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
const touchingSide = getSideTouching(draggingFrame, hoveredFrame)
|
|
@@ -206,7 +206,7 @@ export function getFrameRearrangeInfo(
|
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
if (hoverOppositeSide === zoneSide) {
|
|
209
|
-
return new KnownError(LAYOUT_ERROR.CANT_REARRANGE_TO_SAME_RELATIVE_POSITION, `Frame ${
|
|
209
|
+
return new KnownError(LAYOUT_ERROR.CANT_REARRANGE_TO_SAME_RELATIVE_POSITION, `Frame ${movingFrameId} is already on the ${zoneSide} of ${hoveredFrameId}`, { movingFrameId, hoveredFrameId, zoneSide })
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
}
|
|
@@ -224,7 +224,7 @@ export function getFrameRearrangeInfo(
|
|
|
224
224
|
const splitFrame = splitResult.modified[0]
|
|
225
225
|
const newFrame = splitResult.created[0]
|
|
226
226
|
|
|
227
|
-
if (
|
|
227
|
+
if (movingFrameId === hoveredFrameId) {
|
|
228
228
|
return { ...splitResult, deleted: [], info: "split" }
|
|
229
229
|
}
|
|
230
230
|
|
|
@@ -249,7 +249,7 @@ export function getFrameRearrangeInfo(
|
|
|
249
249
|
[draggingFrame.id]: draggingFrame
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
|
-
const changes = getFillEmptySpaceInfo(winCopy, emptySpace, [hoveredFrameId,
|
|
252
|
+
const changes = getFillEmptySpaceInfo(winCopy, emptySpace, [hoveredFrameId, movingFrameId])
|
|
253
253
|
if (changes instanceof KnownError) return changes
|
|
254
254
|
|
|
255
255
|
// it will also be missing said changes unless it further moved the frames which is not alway
|
|
@@ -21,7 +21,7 @@ import { KnownError } from "../utils/KnownError.js"
|
|
|
21
21
|
export function getFrameSplitInfo(
|
|
22
22
|
frame: LayoutFrame,
|
|
23
23
|
dir: Direction,
|
|
24
|
-
|
|
24
|
+
movePointOrPosition: Point | number | "midpoint" = "midpoint",
|
|
25
25
|
minSize: Size = settings.minSizeScaled,
|
|
26
26
|
snapAmount: Point = settings.snapPointScaled
|
|
27
27
|
): LayoutChange
|
|
@@ -42,11 +42,11 @@ export function getFrameSplitInfo(
|
|
|
42
42
|
const sizeKey: "width" | "height" = isHorz ? "width" : "height"
|
|
43
43
|
const posKey: "x" | "y" = isHorz ? "x" : "y"
|
|
44
44
|
|
|
45
|
-
const position =
|
|
45
|
+
const position = movePointOrPosition === "midpoint"
|
|
46
46
|
? (isHorz ? frame.x + (frame.width / 2) : frame.y + (frame.height / 2))
|
|
47
|
-
: typeof
|
|
48
|
-
?
|
|
49
|
-
:
|
|
47
|
+
: typeof movePointOrPosition === "number"
|
|
48
|
+
? movePointOrPosition
|
|
49
|
+
: movePointOrPosition[isHorz ? "x" : "y"]
|
|
50
50
|
|
|
51
51
|
const safePosition = findSafeSplitEdgeAndPosition(
|
|
52
52
|
frame,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { settings } from "../settings.js"
|
|
2
|
-
import type {
|
|
2
|
+
import type { Zone } from "../types/index.js"
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Returns frame drag zones (in **unrounded** scaled coordinate space).
|
|
@@ -10,12 +10,12 @@ import type { DragZone } from "../types/index.js"
|
|
|
10
10
|
*
|
|
11
11
|
* Unrounded because these are for display purposes only.
|
|
12
12
|
*/
|
|
13
|
-
export function
|
|
13
|
+
export function getFrameZones(
|
|
14
14
|
frame: { x: number, y: number, width: number, height: number },
|
|
15
15
|
thresholdPx: number,
|
|
16
16
|
windowPxWidth: number,
|
|
17
17
|
windowPxHeight: number
|
|
18
|
-
):
|
|
18
|
+
): Zone[] {
|
|
19
19
|
// we do not round as we might undo this transform and drag along errors
|
|
20
20
|
// plugs this is is for display purposes only
|
|
21
21
|
const thX = thresholdPx / windowPxWidth * settings.maxInt
|
|
@@ -36,7 +36,7 @@ export function getFrameDragZones(
|
|
|
36
36
|
}]
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
const zones:
|
|
39
|
+
const zones: Zone[] = []
|
|
40
40
|
|
|
41
41
|
zones.push({
|
|
42
42
|
type: "frame",
|
|
@@ -6,7 +6,7 @@ import type { LayoutWindow, WindowEdgeZone } from "../types/index.js"
|
|
|
6
6
|
* Returns window edge drag zones (in scaled coordinates)
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
9
|
-
export function
|
|
9
|
+
export function getWindowZones(
|
|
10
10
|
win: LayoutWindow,
|
|
11
11
|
thresholdPx: number
|
|
12
12
|
): WindowEdgeZone[] {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { getFrameZones } from "./getFrameZones.js"
|
|
2
|
+
import { getWindowZones } from "./getWindowZones.js"
|
|
3
|
+
import { isPointInRect } from "./isPointInRect.js"
|
|
4
|
+
|
|
5
|
+
import type { MoveState, WindowEdgeZone, Zone } from "../types/index.js"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Find the drag zone under the drag point.
|
|
9
|
+
*/
|
|
10
|
+
export function getZones(
|
|
11
|
+
state: Pick<MoveState, "movePoint" | "moveHoveredFrame" | "frames" | "win">,
|
|
12
|
+
{ frameEdgePx, windowEdgePx }: { frameEdgePx: number, windowEdgePx: number }
|
|
13
|
+
): Zone | undefined {
|
|
14
|
+
const { movePoint, win } = state
|
|
15
|
+
const frameId = state.moveHoveredFrame?.id
|
|
16
|
+
if (!frameId) return
|
|
17
|
+
|
|
18
|
+
const frame = state.frames[frameId]
|
|
19
|
+
if (!frame || !movePoint) return
|
|
20
|
+
|
|
21
|
+
const frameZones = getFrameZones(frame, frameEdgePx, win.pxWidth, win.pxHeight)
|
|
22
|
+
|
|
23
|
+
const windowZones = getWindowZones(win, windowEdgePx)
|
|
24
|
+
|
|
25
|
+
let matchedZone: Zone | undefined
|
|
26
|
+
for (const zone of windowZones) {
|
|
27
|
+
if (isPointInRect(zone, movePoint)) {
|
|
28
|
+
matchedZone = {
|
|
29
|
+
...zone,
|
|
30
|
+
type: "window"
|
|
31
|
+
} as WindowEdgeZone
|
|
32
|
+
break
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!matchedZone) {
|
|
37
|
+
for (const zone of frameZones) {
|
|
38
|
+
if (isPointInRect(zone, movePoint)) {
|
|
39
|
+
matchedZone = zone
|
|
40
|
+
break
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!matchedZone) return undefined
|
|
46
|
+
|
|
47
|
+
return matchedZone
|
|
48
|
+
}
|
|
@@ -9,11 +9,11 @@ export { findSafeSplitEdgeAndPosition } from "./findSafeSplitEdge.js"
|
|
|
9
9
|
export { findVisualEdge } from "./findVisualEdge.js"
|
|
10
10
|
export { frameCreate } from "./frameCreate.js"
|
|
11
11
|
export { getCloseFrameInfo } from "./getCloseFrameInfo.js"
|
|
12
|
-
export {
|
|
12
|
+
export { getZones } from "./getZones.js"
|
|
13
13
|
export { getFillEmptySpaceInfo } from "./getFillEmptySpaceInfo.js"
|
|
14
14
|
export { getFrameCollapseInfo } from "./getFrameCollapseInfo.js"
|
|
15
15
|
export { getFrameDockInfo } from "./getFrameDockInfo.js"
|
|
16
|
-
export {
|
|
16
|
+
export { getFrameZones } from "./getFrameZones.js"
|
|
17
17
|
export { getFrameExpandInfo } from "./getFrameExpandInfo.js"
|
|
18
18
|
export { getFrameRearrangeInfo } from "./getFrameRearrangeInfo.js"
|
|
19
19
|
export { getFrameShrinkInfo } from "./getFrameShrinkInfo.js"
|
|
@@ -25,7 +25,7 @@ export { getFrameUncollapseInfo } from "./getFrameUncollapseInfo.js"
|
|
|
25
25
|
export { getFrameUndockInfo } from "./getFrameUndockInfo.js"
|
|
26
26
|
export { getRelaxFramesInfo } from "./getRelaxFramesInfo.js"
|
|
27
27
|
export { getUpdateWindowSizeInfo } from "./getUpdateWindowSizeInfo.js"
|
|
28
|
-
export {
|
|
28
|
+
export { getWindowZones } from "./getWindowZones.js"
|
|
29
29
|
export { isPointInRect } from "./isPointInRect.js"
|
|
30
30
|
export { layoutAddWindow } from "./layoutAddWindow.js"
|
|
31
31
|
export { layoutCreate } from "./layoutCreate.js"
|
|
@@ -2,36 +2,36 @@ import { get, type RecordFromArray } from "@alanscodelog/utils"
|
|
|
2
2
|
|
|
3
3
|
import { isWindowEdge } from "../helpers/isWindowEdge.js"
|
|
4
4
|
import { findFramesTouchingEdge } from "../layout/findFramesTouchingEdge.js"
|
|
5
|
-
import type {
|
|
5
|
+
import type { ActionHandlerApplyResult, Edge, IAction, LayoutFrame, LayoutShape, MoveChangeHandler, MoveChangeResult, MoveState } from "../types/index.js"
|
|
6
6
|
import { LAYOUT_ERROR } from "../types/index.js"
|
|
7
7
|
import { KnownError } from "../utils/KnownError.js"
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Handles the lifecycle of a drag actions {@link
|
|
10
|
+
* Handles the lifecycle of a drag actions {@link IAction} and provides additional hooks.
|
|
11
11
|
*
|
|
12
12
|
* The first action instance that can handle the request is passed control of the event handlers until the request changes.
|
|
13
13
|
*/
|
|
14
|
-
export class
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
export class ActionHandler<
|
|
15
|
+
TRawActions extends IAction[],
|
|
16
|
+
TActions extends RecordFromArray<TRawActions, "name"> = RecordFromArray<TRawActions, "name">
|
|
17
17
|
> {
|
|
18
|
-
activeAction?: keyof
|
|
18
|
+
activeAction?: keyof TActions
|
|
19
19
|
|
|
20
|
-
actions:
|
|
20
|
+
actions: TActions
|
|
21
21
|
|
|
22
|
-
eventCanceller: ((e: PointerEvent | KeyboardEvent | undefined, state:
|
|
22
|
+
eventCanceller: ((e: PointerEvent | KeyboardEvent | undefined, state: MoveState) => void) | undefined = undefined
|
|
23
23
|
|
|
24
|
-
boundCancel: (e: PointerEvent | KeyboardEvent | undefined, state:
|
|
24
|
+
boundCancel: (e: PointerEvent | KeyboardEvent | undefined, state: MoveState) => void
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
defaultOnMoveChange: MoveChangeHandler = (type, _e, state, _forceRecalculateEdges, _cancel) => {
|
|
27
27
|
const isTouchingCollapsedFrameEdge = type === "move"
|
|
28
|
-
&& state.
|
|
28
|
+
&& state.isMoving === "edge"
|
|
29
29
|
&& state.touchingFramesArrays.some(frames =>
|
|
30
30
|
// we check with a falsy check on PURPOSE, frames collapsed to 0 aren't an issue, they are ignored anyways
|
|
31
31
|
Object.values(frames).some(f => f.collapsed)
|
|
32
32
|
)
|
|
33
33
|
return {
|
|
34
|
-
updateEdges: type === "move" ? !(state.
|
|
34
|
+
updateEdges: type === "move" ? !(state.isMovingFromWindowEdge || isTouchingCollapsedFrameEdge) : true,
|
|
35
35
|
shapes: []
|
|
36
36
|
}
|
|
37
37
|
}
|
|
@@ -46,7 +46,7 @@ export class DragActionHandler<
|
|
|
46
46
|
*/
|
|
47
47
|
onEvent?: (e: PointerEvent | KeyboardEvent | undefined, cancel: () => void) => void
|
|
48
48
|
/** Called when the action requested changes. */
|
|
49
|
-
onRequestChange?: (type: keyof
|
|
49
|
+
onRequestChange?: (type: keyof TActions | undefined) => void
|
|
50
50
|
/** Called when the drag action ends either because it was completed or cancelled. */
|
|
51
51
|
onEnd?: (context: { cancelled: boolean, applied: boolean }) => void
|
|
52
52
|
}
|
|
@@ -54,22 +54,22 @@ export class DragActionHandler<
|
|
|
54
54
|
/** All action shapes merged into a single array. If using vue you can set this to a reactive array for reactivity. */
|
|
55
55
|
shapes: LayoutShape[] = []
|
|
56
56
|
|
|
57
|
-
/** All hint/error text from all actions, updated on every
|
|
57
|
+
/** All hint/error text from all actions, updated on every onMoveChange. If using vue you can set this to a reactive object for reactivity. */
|
|
58
58
|
textHints: { actions: string[], errors: string[] } = { actions: [], errors: [] }
|
|
59
59
|
|
|
60
60
|
constructor(
|
|
61
|
-
actions:
|
|
62
|
-
hooks:
|
|
61
|
+
actions: TRawActions,
|
|
62
|
+
hooks: ActionHandler<TRawActions, TActions>["hooks"] = {},
|
|
63
63
|
/**
|
|
64
|
-
* Default
|
|
64
|
+
* Default onMoveChange handler for when no action can handle the request.
|
|
65
65
|
*
|
|
66
66
|
* Should return true to allow the edges to be moved, or false to prevent it.
|
|
67
67
|
*
|
|
68
68
|
* The default prevents movement when the edge is a window edge and when the edge is touching a collapsed frame.
|
|
69
69
|
*/
|
|
70
|
-
|
|
70
|
+
defaultOnMoveChange?: MoveChangeHandler
|
|
71
71
|
) {
|
|
72
|
-
if (
|
|
72
|
+
if (defaultOnMoveChange) this.defaultOnMoveChange = defaultOnMoveChange
|
|
73
73
|
this.hooks = hooks
|
|
74
74
|
this.actions = {} as any
|
|
75
75
|
for (const action of actions) {
|
|
@@ -80,10 +80,10 @@ export class DragActionHandler<
|
|
|
80
80
|
|
|
81
81
|
eventHandler(
|
|
82
82
|
e: KeyboardEvent | PointerEvent,
|
|
83
|
-
state:
|
|
83
|
+
state: MoveState,
|
|
84
84
|
forceRecalculateEdges: () => void
|
|
85
85
|
) {
|
|
86
|
-
if (state.
|
|
86
|
+
if (state.isMoving) {
|
|
87
87
|
e.preventDefault()
|
|
88
88
|
}
|
|
89
89
|
let cancelled = false
|
|
@@ -95,7 +95,7 @@ export class DragActionHandler<
|
|
|
95
95
|
|
|
96
96
|
let found = false
|
|
97
97
|
const oldActiveAction = this.activeAction
|
|
98
|
-
for (const action of Object.values<
|
|
98
|
+
for (const action of Object.values<TRawActions[number]>(this.actions)) {
|
|
99
99
|
if (action.canHandleRequest(e, state, forceRecalculateEdges)) {
|
|
100
100
|
// if (this.activeAction !== action.name) {
|
|
101
101
|
// }
|
|
@@ -116,46 +116,53 @@ export class DragActionHandler<
|
|
|
116
116
|
this.hooks.onRequestChange?.(this.activeAction)
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
if (state.
|
|
119
|
+
if (state.isMoving) {
|
|
120
120
|
forceRecalculateEdges()
|
|
121
121
|
this.hooks.onRecalculate?.()
|
|
122
122
|
}
|
|
123
123
|
return undefined
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
onMoveChange<T extends "start" | "move" | "end">(
|
|
127
127
|
type: T,
|
|
128
128
|
e: T extends "end" ? PointerEvent | undefined : PointerEvent,
|
|
129
|
-
state:
|
|
129
|
+
state: MoveState,
|
|
130
130
|
forceRecalculateEdges: () => void,
|
|
131
|
-
cancel: (e: PointerEvent | KeyboardEvent | undefined, state:
|
|
132
|
-
|
|
131
|
+
cancel: (e: PointerEvent | KeyboardEvent | undefined, state: MoveState) => void,
|
|
132
|
+
resolve: T extends "end" ? undefined : (opts: ActionHandlerApplyResult) => void
|
|
133
|
+
): MoveChangeResult {
|
|
133
134
|
if (type === "start") {
|
|
134
135
|
this.eventCanceller = cancel
|
|
135
136
|
this.eventHandler(e!, state, forceRecalculateEdges)
|
|
136
137
|
}
|
|
137
|
-
if (type === "end") {
|
|
138
|
-
this.activeAction = undefined
|
|
139
|
-
}
|
|
140
138
|
|
|
141
139
|
if (this.activeAction) {
|
|
142
|
-
const res = this.actions[this.activeAction]!.
|
|
140
|
+
const res = this.actions[this.activeAction]!.onMoveChange(type, e, state, forceRecalculateEdges, this.boundCancel as any, resolve)
|
|
143
141
|
// in case it's a vue reactive array
|
|
144
142
|
this.shapes.splice(0, this.shapes.length, ...res.shapes)
|
|
145
143
|
this.setTextHints(type)
|
|
146
144
|
return res
|
|
147
145
|
}
|
|
148
146
|
this.setTextHints(type)
|
|
149
|
-
const res = this.
|
|
147
|
+
const res = this.defaultOnMoveChange(type, e, state, forceRecalculateEdges, this.boundCancel as any, resolve as any)
|
|
150
148
|
this.shapes.splice(0, this.shapes.length, ...res.shapes)
|
|
149
|
+
|
|
151
150
|
return res
|
|
152
151
|
}
|
|
153
152
|
|
|
153
|
+
onMoveEnded() {
|
|
154
|
+
if (this.activeAction) {
|
|
155
|
+
this.actions[this.activeAction]!.onMoveEnded()
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
this.activeAction = undefined
|
|
159
|
+
}
|
|
160
|
+
|
|
154
161
|
setTextHints(type: "start" | "move" | "end") {
|
|
155
162
|
// again in case it's a vue reactive object
|
|
156
163
|
this.textHints.actions = []
|
|
157
164
|
this.textHints.errors = []
|
|
158
|
-
for (const action of Object.values<
|
|
165
|
+
for (const action of Object.values<IAction>(this.actions)) {
|
|
159
166
|
const res = action.getTextHints?.(type)
|
|
160
167
|
this.textHints.actions.push(...(res?.actions ?? []))
|
|
161
168
|
this.textHints.errors.push(...(res?.errors ?? []))
|
|
@@ -166,7 +173,7 @@ export class DragActionHandler<
|
|
|
166
173
|
for (const edge of edges) {
|
|
167
174
|
// edge already has an error
|
|
168
175
|
if (edge.error) continue
|
|
169
|
-
for (const action of Object.values<
|
|
176
|
+
for (const action of Object.values<IAction>(this.actions)) {
|
|
170
177
|
action.annotateEdge?.(edge, frames)
|
|
171
178
|
if (edge.error) break
|
|
172
179
|
}
|
|
@@ -188,20 +195,20 @@ export class DragActionHandler<
|
|
|
188
195
|
}
|
|
189
196
|
}
|
|
190
197
|
|
|
191
|
-
|
|
192
|
-
state:
|
|
198
|
+
onMoveApply(
|
|
199
|
+
state: MoveState,
|
|
193
200
|
forceRecalculateEdges: () => void
|
|
194
|
-
):
|
|
201
|
+
): ActionHandlerApplyResult {
|
|
195
202
|
if (this.activeAction) {
|
|
196
|
-
const res = this.actions[this.activeAction]!.
|
|
203
|
+
const res = this.actions[this.activeAction]!.onMoveApply(state, forceRecalculateEdges)
|
|
197
204
|
this.hooks.onEnd?.({ cancelled: false, applied: res })
|
|
198
|
-
return { apply: res, result: undefined }
|
|
205
|
+
return { apply: !res, result: undefined }
|
|
199
206
|
}
|
|
200
207
|
this.hooks.onEnd?.({ cancelled: false, applied: false })
|
|
201
208
|
return { apply: true, result: undefined }
|
|
202
209
|
}
|
|
203
210
|
|
|
204
|
-
cancel(e: PointerEvent | KeyboardEvent | undefined, state:
|
|
211
|
+
cancel(e: PointerEvent | KeyboardEvent | undefined, state: MoveState): void {
|
|
205
212
|
if (this.activeAction) {
|
|
206
213
|
this.actions[this.activeAction].cancel(e, state)
|
|
207
214
|
}
|
|
@@ -213,7 +220,7 @@ export class DragActionHandler<
|
|
|
213
220
|
static debugState(
|
|
214
221
|
pluginName: string,
|
|
215
222
|
type: "before" | "after" | string,
|
|
216
|
-
state:
|
|
223
|
+
state: MoveState,
|
|
217
224
|
pluginState: Record<string, any> = {},
|
|
218
225
|
/** Object key to filter the state by, e.g. state.win.frames. If false is ignored. The idea is you pass this.debug and users can set this.debug to a string to filter. */
|
|
219
226
|
key?: string | boolean
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ActionHandler } from "./ActionHandler.js"
|
|
2
2
|
|
|
3
3
|
import { dirToOrientation } from "../helpers/dirToOrientation.js"
|
|
4
4
|
import { getEdgeOrientation } from "../helpers/getEdgeOrientation.js"
|
|
@@ -6,12 +6,12 @@ import { oppositeSide } from "../helpers/oppositeSide.js"
|
|
|
6
6
|
import { applyFrameChanges } from "../layout/applyFrameChanges.js"
|
|
7
7
|
import { findFramesTouchingEdge } from "../layout/findFramesTouchingEdge.js"
|
|
8
8
|
import { getCloseFrameInfo } from "../layout/getCloseFrameInfo.js"
|
|
9
|
-
import type {
|
|
9
|
+
import type { ActionChangeResult, CloseDeco, IAction, MoveState } from "../types/index.js"
|
|
10
10
|
import type { KnownError } from "../utils/KnownError.js"
|
|
11
11
|
|
|
12
12
|
export type CloseInfo = Exclude<ReturnType<typeof getCloseFrameInfo>, KnownError>
|
|
13
13
|
|
|
14
|
-
export class CloseAction implements
|
|
14
|
+
export class CloseAction implements IAction {
|
|
15
15
|
name = "close" as const
|
|
16
16
|
minDragDistance = 5
|
|
17
17
|
|
|
@@ -20,13 +20,13 @@ export class CloseAction implements IDragAction {
|
|
|
20
20
|
force: boolean
|
|
21
21
|
res: CloseInfo
|
|
22
22
|
cacheKey: string | undefined
|
|
23
|
-
lastReturn:
|
|
23
|
+
lastReturn: ActionChangeResult
|
|
24
24
|
} | {
|
|
25
25
|
allowed: false
|
|
26
26
|
force: boolean
|
|
27
27
|
res: CloseInfo | undefined
|
|
28
28
|
cacheKey: string | undefined
|
|
29
|
-
lastReturn:
|
|
29
|
+
lastReturn: ActionChangeResult | undefined
|
|
30
30
|
} = {} as any // this is initialized by this.reset()
|
|
31
31
|
|
|
32
32
|
debug: boolean | string = false
|
|
@@ -40,7 +40,7 @@ export class CloseAction implements IDragAction {
|
|
|
40
40
|
transformError: e => e.message
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
handleEvent: (e: PointerEvent | KeyboardEvent, state:
|
|
43
|
+
handleEvent: (e: PointerEvent | KeyboardEvent, state: MoveState) => boolean | "force" = (e: PointerEvent | KeyboardEvent) => {
|
|
44
44
|
if (e.ctrlKey && e.shiftKey) {
|
|
45
45
|
return "force"
|
|
46
46
|
}
|
|
@@ -86,10 +86,10 @@ export class CloseAction implements IDragAction {
|
|
|
86
86
|
this.modifyDecos([])
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
private _getDecos(state:
|
|
89
|
+
private _getDecos(state: MoveState): CloseDeco[] {
|
|
90
90
|
let decos: CloseDeco[] = []
|
|
91
|
-
const {
|
|
92
|
-
if (
|
|
91
|
+
const { isMoving } = state
|
|
92
|
+
if (isMoving && this.state.allowed && this.state.res) {
|
|
93
93
|
const { force } = this.state
|
|
94
94
|
decos = this.state.res.deleted.map(_ => ({
|
|
95
95
|
id: _.id,
|
|
@@ -136,12 +136,12 @@ export class CloseAction implements IDragAction {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
|
|
139
|
-
canHandleRequest(e: PointerEvent | KeyboardEvent, state:
|
|
140
|
-
const {
|
|
141
|
-
if (
|
|
139
|
+
canHandleRequest(e: PointerEvent | KeyboardEvent, state: MoveState): boolean {
|
|
140
|
+
const { movingEdges } = state
|
|
141
|
+
if (movingEdges.length !== 1) return false
|
|
142
142
|
const res = this.handleEvent(e, state)
|
|
143
143
|
this.state.force = res === "force"
|
|
144
|
-
this.setTextHints(state.
|
|
144
|
+
this.setTextHints(state.isMoving === "edge" ? true : undefined)
|
|
145
145
|
if (res) {
|
|
146
146
|
this.hooks.onStart?.(true)
|
|
147
147
|
return true
|
|
@@ -150,47 +150,47 @@ export class CloseAction implements IDragAction {
|
|
|
150
150
|
return false
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
onMoveChange(
|
|
154
|
+
type: "start" | "end" | "move",
|
|
155
155
|
_e: PointerEvent | undefined,
|
|
156
|
-
state:
|
|
157
|
-
):
|
|
158
|
-
if (state.
|
|
156
|
+
state: MoveState
|
|
157
|
+
): ActionChangeResult {
|
|
158
|
+
if (state.moveDistance <= this.minDragDistance) {
|
|
159
159
|
return { updateEdges: false, shapes: [] }
|
|
160
160
|
}
|
|
161
161
|
const {
|
|
162
162
|
touchingFramesArrays,
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
moveDirections,
|
|
164
|
+
isMoving,
|
|
165
|
+
movingEdges,
|
|
166
166
|
visualEdges,
|
|
167
167
|
frames,
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
isMovingFromWindowEdge,
|
|
169
|
+
movePoint
|
|
170
170
|
} = state
|
|
171
|
-
const oppositeOrientation = oppositeSide(getEdgeOrientation(
|
|
172
|
-
const cacheKey = `${
|
|
171
|
+
const oppositeOrientation = oppositeSide(getEdgeOrientation(movingEdges[0]))
|
|
172
|
+
const cacheKey = `${movePoint?.x}-${movePoint?.y}-${moveDirections[oppositeOrientation]!}-${this.state.force}`
|
|
173
173
|
if (this.state.allowed) {
|
|
174
174
|
if (this.state.cacheKey === cacheKey) {
|
|
175
175
|
return this.state.lastReturn
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
-
if (
|
|
178
|
+
if (isMoving && movingEdges.length === 1) {
|
|
179
179
|
const res = findFramesTouchingEdge(
|
|
180
|
-
|
|
180
|
+
movingEdges[0],
|
|
181
181
|
touchingFramesArrays[0],
|
|
182
182
|
{
|
|
183
|
-
// referencePoint:
|
|
184
|
-
searchDirections: [
|
|
183
|
+
// referencePoint: movePoint.value, // if force pick smallest frame?
|
|
184
|
+
searchDirections: [moveDirections[oppositeOrientation]!]
|
|
185
185
|
}
|
|
186
186
|
)
|
|
187
187
|
if (res.length > 0) {
|
|
188
|
-
const orientation = dirToOrientation(
|
|
188
|
+
const orientation = dirToOrientation(moveDirections[oppositeOrientation]!)
|
|
189
189
|
const sizeKey = orientation === "horizontal" ? "width" : "height"
|
|
190
190
|
const smallestFrameSize = Math.min(...res.map(_ => _.frame[sizeKey]))
|
|
191
191
|
const frame = res.find(_ => _.frame[sizeKey] === smallestFrameSize)!.frame!
|
|
192
192
|
|
|
193
|
-
const closeInfo = getCloseFrameInfo(Object.values(frames), visualEdges, frame,
|
|
193
|
+
const closeInfo = getCloseFrameInfo(Object.values(frames), visualEdges, frame, moveDirections[oppositeOrientation]!, "dir", this.state.force)
|
|
194
194
|
this.state.cacheKey = cacheKey
|
|
195
195
|
this.setTextHints(closeInfo)
|
|
196
196
|
if (!(closeInfo instanceof Error)) {
|
|
@@ -205,24 +205,29 @@ export class CloseAction implements IDragAction {
|
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
const decos = this._getDecos(state)
|
|
208
|
-
this.state.lastReturn = {
|
|
208
|
+
this.state.lastReturn = {
|
|
209
|
+
updateEdges: !isMovingFromWindowEdge,
|
|
210
|
+
shapes: type === "end" ? [] : decos.flatMap(_ => _.shapes)
|
|
211
|
+
}
|
|
209
212
|
return this.state.lastReturn
|
|
210
213
|
}
|
|
211
214
|
|
|
212
|
-
|
|
215
|
+
onMoveApply(state: MoveState): boolean {
|
|
213
216
|
if (this.state.res) {
|
|
214
217
|
const win = state.win
|
|
215
|
-
if (this.debug) {
|
|
218
|
+
if (this.debug) { ActionHandler.debugState(this.name, "before", state, this.state, this.debug) }
|
|
216
219
|
applyFrameChanges(win, this.state.res)
|
|
217
|
-
if (this.debug) {
|
|
218
|
-
this.reset()
|
|
220
|
+
if (this.debug) { ActionHandler.debugState(this.name, "after", state, this.state, this.debug) }
|
|
219
221
|
return true
|
|
220
222
|
}
|
|
221
223
|
return false
|
|
222
224
|
}
|
|
223
225
|
|
|
224
226
|
cancel(): void {
|
|
225
|
-
this.reset()
|
|
226
227
|
this.hooks.onCancel?.()
|
|
227
228
|
}
|
|
229
|
+
|
|
230
|
+
onMoveEnded() {
|
|
231
|
+
this.reset()
|
|
232
|
+
}
|
|
228
233
|
}
|