@witchcraft/layout 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +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 +9 -10
- 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 +1 -4
- 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 +3 -5
- 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 -55
- 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 -22
- package/src/runtime/components/LayoutFrame.vue +6 -5
- package/src/runtime/components/LayoutShapeSquare.vue +11 -5
- 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
package/src/runtime/demo/App.vue
CHANGED
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
2
|
+
<!-- not sure why we're needed to specify flex-col, tailwind isn't detecting the class properly in wroot maybe? -->
|
|
3
|
+
<WRoot class="
|
|
4
|
+
gap-2
|
|
5
|
+
p-2
|
|
6
|
+
flex-col
|
|
7
|
+
[&_.frame-drag-ghost]:rounded-md
|
|
8
|
+
">
|
|
3
9
|
<DemoControls
|
|
10
|
+
v-if="win"
|
|
4
11
|
:frames="frames!"
|
|
12
|
+
:win="win"
|
|
13
|
+
:dragActionHandler="dragActionHandler"
|
|
5
14
|
/>
|
|
6
|
-
<LayoutWindow
|
|
7
|
-
|
|
8
|
-
|
|
15
|
+
<LayoutWindow
|
|
16
|
+
ref="layoutComponent"
|
|
17
|
+
v-if="win"
|
|
18
|
+
:class="twMerge(`
|
|
19
|
+
flex-1
|
|
20
|
+
w-full
|
|
9
21
|
border-1
|
|
10
22
|
border-neutral-300
|
|
11
23
|
dark:border-neutral-700
|
|
12
24
|
rounded-md
|
|
13
|
-
flex-1
|
|
14
25
|
self-stretch
|
|
15
26
|
[&_.frame]:flex
|
|
16
27
|
[&_.frame]:flex-col
|
|
@@ -21,26 +32,34 @@
|
|
|
21
32
|
[&_.grabbed-edge]:bg-accent-500
|
|
22
33
|
[&.request-split_.grabbed-edge]:hidden
|
|
23
34
|
[&.request-split_.drag-edge]:hidden
|
|
35
|
+
[&.deco-split-error]:rounded-md
|
|
24
36
|
[&_.deco-split-new-frame]:rounded-md
|
|
25
37
|
[&_.deco-close-frame]:rounded-md
|
|
26
|
-
[&_.deco-close-frame]:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
38
|
+
[&_.deco-close-frame-force]:rounded-md
|
|
39
|
+
[&_.deco-frame-drag]:rounded-md
|
|
40
|
+
`,
|
|
41
|
+
collapsedDocks.top && `border-t-2 border-t-green-500`,
|
|
42
|
+
collapsedDocks.bottom && `border-b-2 border-b-green-500`,
|
|
43
|
+
collapsedDocks.left && `border-l-2 border-l-green-500`,
|
|
44
|
+
collapsedDocks.right && `border-r-2 border-r-green-500`
|
|
45
|
+
)"
|
|
46
|
+
:textHints="textHints"
|
|
47
|
+
textHintsTeleportTo="#status-bar"
|
|
30
48
|
v-model:win="win"
|
|
31
49
|
@is-showing-drag="isShowingDrag = $event"
|
|
32
50
|
@drag-state="dragState = $event"
|
|
33
51
|
>
|
|
34
|
-
<template #[`frame-${f.id}`] v-for="f in frames" :key="f.id">
|
|
52
|
+
<template #[`frame-${f.id}`] v-for="f in frames" :key="f.id" >
|
|
35
53
|
<div
|
|
54
|
+
v-if="!(f.collapsed && (f.width === 0 || f.height === 0))"
|
|
36
55
|
:data-is-active="win.activeFrame === f.id"
|
|
37
56
|
:class="twMerge(`
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
57
|
+
border-2
|
|
58
|
+
border-neutral-500
|
|
59
|
+
h-full
|
|
60
|
+
rounded-md
|
|
61
|
+
overflow-auto
|
|
62
|
+
`,
|
|
44
63
|
win.activeFrame === f.id && `border-blue-500`
|
|
45
64
|
)"
|
|
46
65
|
@click="win.activeFrame=f.id"
|
|
@@ -50,12 +69,43 @@
|
|
|
50
69
|
Set it on the first child instead, so that the frame can shrink as small as possible.
|
|
51
70
|
Too big a border can also be a problem, but usually it's small enough that it's beneath the min frame width/height allowed.
|
|
52
71
|
-->
|
|
53
|
-
<div class="p-2
|
|
72
|
+
<div class="p-2 flex flex-col">
|
|
73
|
+
<FrameDragHandle :frame-id="f.id">
|
|
74
|
+
<div
|
|
75
|
+
class="cursor-grab bg-neutral-200 dark:bg-neutral-700 px-2 py-1 text-xs select-none"
|
|
76
|
+
>
|
|
77
|
+
⠿ Drag to move frame
|
|
78
|
+
</div>
|
|
79
|
+
</FrameDragHandle>
|
|
80
|
+
<div class="flex gap-1 px-2 py-1 flex-wrap">
|
|
81
|
+
<button
|
|
82
|
+
v-if="f.docked && f.collapsed === false"
|
|
83
|
+
class="bg-neutral-200 dark:bg-neutral-700 px-2 py-0.5 text-xs rounded"
|
|
84
|
+
@click="handleUndock(f.id)"
|
|
85
|
+
>Undock</button>
|
|
86
|
+
<button
|
|
87
|
+
v-if="f.docked && f.collapsed === false"
|
|
88
|
+
class="bg-neutral-200 dark:bg-neutral-700 px-2 py-0.5 text-xs rounded"
|
|
89
|
+
@click="handleCollapse(f.id)"
|
|
90
|
+
>Collapse</button>
|
|
91
|
+
<button
|
|
92
|
+
v-if="f.docked && typeof f.collapsed === 'number'"
|
|
93
|
+
class="bg-neutral-200 dark:bg-neutral-700 px-2 py-0.5 text-xs rounded"
|
|
94
|
+
@click="handleUncollapse(f.id)"
|
|
95
|
+
>Uncollapse</button>
|
|
96
|
+
</div>
|
|
97
|
+
<div class="p-2 whitespace-pre-wrap">
|
|
98
|
+
{{ debugFrame(f) }}
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
<div v-else>
|
|
54
103
|
</div>
|
|
55
104
|
</template>
|
|
56
105
|
</LayoutWindow>
|
|
57
|
-
</
|
|
106
|
+
</WRoot>
|
|
58
107
|
</template>
|
|
108
|
+
|
|
59
109
|
<script lang="ts" setup>
|
|
60
110
|
import { keys } from "@alanscodelog/utils/keys"
|
|
61
111
|
// playground not resolving???
|
|
@@ -67,21 +117,30 @@ import { computed, onBeforeMount, ref } from "vue"
|
|
|
67
117
|
import DemoControls from "./DemoControls.vue"
|
|
68
118
|
import { app } from "./sharedLayoutInstance.js"
|
|
69
119
|
|
|
120
|
+
import FrameDragHandle from "../components/FrameDragHandle.vue"
|
|
70
121
|
import LayoutWindow from "../components/LayoutWindow.vue"
|
|
71
122
|
import type { DragState } from "../drag/types.js"
|
|
123
|
+
import { applyFrameChanges } from "../layout/applyFrameChanges.js"
|
|
72
124
|
import { debugFrame } from "../layout/debugFrame.js"
|
|
125
|
+
import { getFrameCollapseInfo } from "../layout/getFrameCollapseInfo.js"
|
|
126
|
+
import { getFrameUncollapseInfo } from "../layout/getFrameUncollapseInfo.js"
|
|
127
|
+
import { getFrameUndockInfo } from "../layout/getFrameUndockInfo.js"
|
|
73
128
|
import {
|
|
74
129
|
frameCreate,
|
|
75
130
|
layoutAddWindow,
|
|
76
131
|
windowAddFrame,
|
|
77
|
-
windowCreate
|
|
132
|
+
windowCreate
|
|
78
133
|
} from "../layout/index.js"
|
|
79
|
-
import {
|
|
80
|
-
import {
|
|
134
|
+
import { settings } from "../settings.js"
|
|
135
|
+
import type { EdgeSide, Layout, Pos, Size } from "../types/index.js"
|
|
136
|
+
import { throwIfError } from "@alanscodelog/utils/throwIfError"
|
|
137
|
+
import { useTemplateRef } from "vue"
|
|
138
|
+
import { DragActionHandler } from "../drag/DragActionHandler.js"
|
|
81
139
|
|
|
82
140
|
|
|
83
141
|
const winId = ref<string | undefined>(undefined)
|
|
84
142
|
const win = computed(() => winId.value !== undefined ? app.layout.windows[winId.value] : undefined)
|
|
143
|
+
const layoutComponent = useTemplateRef("layoutComponent")
|
|
85
144
|
|
|
86
145
|
const frames = computed(() => {
|
|
87
146
|
if (!win.value) return
|
|
@@ -92,30 +151,29 @@ onBeforeMount(() => {
|
|
|
92
151
|
winId.value = keys(app.layout.windows)[0]
|
|
93
152
|
})
|
|
94
153
|
|
|
95
|
-
const isDragging = ref(false)
|
|
96
154
|
function layoutInitialize(layout: Layout, { defaultPos, defaultSize }: {
|
|
97
155
|
defaultPos: Pos
|
|
98
156
|
defaultSize: Size
|
|
99
157
|
} = {
|
|
100
158
|
defaultPos: { x: 0, y: 0 },
|
|
101
|
-
defaultSize: { width: 0, height: 0 }
|
|
159
|
+
defaultSize: { width: 0, height: 0 }
|
|
102
160
|
}) {
|
|
103
161
|
const w = layoutAddWindow(
|
|
104
162
|
layout,
|
|
105
163
|
windowCreate({
|
|
106
164
|
...defaultPos,
|
|
107
165
|
...defaultSize,
|
|
108
|
-
frames: {}
|
|
166
|
+
frames: {}
|
|
109
167
|
})
|
|
110
168
|
)
|
|
111
169
|
|
|
112
170
|
layout.activeWindow = w.id
|
|
113
|
-
const max =
|
|
171
|
+
const max = settings.maxInt
|
|
114
172
|
const frame = windowAddFrame(w, frameCreate({
|
|
115
173
|
x: 0,
|
|
116
174
|
y: 0,
|
|
117
175
|
width: max,
|
|
118
|
-
height: max
|
|
176
|
+
height: max
|
|
119
177
|
}))
|
|
120
178
|
w.activeFrame = frame.id
|
|
121
179
|
}
|
|
@@ -126,14 +184,46 @@ const isShowingDrag = ref(false)
|
|
|
126
184
|
// drag state as returned by useFrames in LayoutWindow
|
|
127
185
|
const dragState = ref<DragState | undefined>(undefined)
|
|
128
186
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
187
|
+
function handleUndock(frameId: string) {
|
|
188
|
+
const changes = throwIfError(getFrameUndockInfo(win.value!, frameId))
|
|
189
|
+
DragActionHandler.debugState("undock", "before", dragState.value!, {}, undefined)
|
|
190
|
+
applyFrameChanges(win.value!, changes)
|
|
191
|
+
DragActionHandler.debugState("undock", "after", dragState.value!, {}, undefined)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function handleCollapse(frameId: string) {
|
|
195
|
+
const changes = throwIfError(getFrameCollapseInfo(win.value!, frameId))
|
|
196
|
+
DragActionHandler.debugState("collapse", "before", dragState.value!, {}, undefined)
|
|
197
|
+
applyFrameChanges(win.value!, changes)
|
|
198
|
+
DragActionHandler.debugState("collapse", "after", dragState.value!, {}, undefined)
|
|
199
|
+
}
|
|
139
200
|
|
|
201
|
+
function handleUncollapse(frameId: string) {
|
|
202
|
+
const changes = throwIfError(getFrameUncollapseInfo(win.value!, frameId))
|
|
203
|
+
DragActionHandler.debugState("uncollapse", "before", dragState.value!, {}, undefined)
|
|
204
|
+
applyFrameChanges(win.value!, changes)
|
|
205
|
+
DragActionHandler.debugState("uncollapse", "after", dragState.value!, {}, undefined)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const textHints = computed(() => {
|
|
209
|
+
const isDragging = dragState.value?.isDragging
|
|
210
|
+
const textHints = dragActionHandler.value?.textHints ?? {actions:[], errors:[]}
|
|
211
|
+
return [
|
|
212
|
+
...(!isDragging ? [{classes:"", text:"Drag from an edge to create a new frame."}] : []),
|
|
213
|
+
...textHints.errors.map(_ => ({classes:"text-red-500", text:_})),
|
|
214
|
+
...textHints.actions.map(_ => ({ text:_})),
|
|
215
|
+
]
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
const collapsedDocks = computed(() => {
|
|
219
|
+
const sides: Partial<Record<EdgeSide, true>> = {}
|
|
220
|
+
for (const frame of Object.values(win.value?.frames ?? {})) {
|
|
221
|
+
if (frame.docked) {
|
|
222
|
+
sides[frame.docked] = true
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return sides
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
const dragActionHandler = computed(() => layoutComponent.value?.dragActionHandler)
|
|
229
|
+
</script>
|
|
@@ -1,17 +1,126 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="border-2 border-neutral-500 p-2 rounded-md flex">
|
|
2
|
+
<div class="border-2 border-neutral-500 p-2 rounded-md flex gap-2 items-center flex-wrap">
|
|
3
3
|
<div class="">Instruction/status bar:</div>
|
|
4
|
-
<div id="status-bar" class="px-2 flex-1 overflow-x-auto scrollbar-hidden"/>
|
|
4
|
+
<div id="status-bar" class="px-2 flex-1 overflow-x-auto scrollbar-hidden whitespace-nowrap"/>
|
|
5
|
+
<WButton class="" @click="uncollapseAll" >Uncollapse All</WButton>
|
|
6
|
+
<WPopover v-model="showDevActions">
|
|
7
|
+
<template #button>
|
|
8
|
+
<WButton @click="showDevActions = !showDevActions">Dev Actions</WButton>
|
|
9
|
+
</template>
|
|
10
|
+
<template #popover>
|
|
11
|
+
<div class="flex flex-col gap-2 rounded-md w-full`">
|
|
12
|
+
<label class="text-xs">Collapse Size (%):</label>
|
|
13
|
+
<input
|
|
14
|
+
type="number"
|
|
15
|
+
class="w-full min-w-0 border border-neutral-300 dark:border-neutral-700 rounded px-1 py-0.5 text-xs bg-neutral-100 dark:bg-neutral-800"
|
|
16
|
+
:min="0"
|
|
17
|
+
:max="100"
|
|
18
|
+
:value="collapseSizeValue"
|
|
19
|
+
@input="collapseSizeValue = Number(($event.target as HTMLInputElement).value); handleCollapseSizeChange(collapseSizeValue)"
|
|
20
|
+
/>
|
|
21
|
+
<WButton class="flex-1" @click="rotateLayoutAction">Rotate Layout</WButton>
|
|
22
|
+
<div class="p-2 flex flex-col border-neutral-500/50 border rounded-md gap-2">
|
|
23
|
+
<span>Debug:</span>
|
|
24
|
+
<div class="flex gap-2">
|
|
25
|
+
<WButton class="flex-1" @click="copyState">Copy State</WButton>
|
|
26
|
+
<WCheckbox v-model="renameFrames">Easy Ids</WCheckbox>
|
|
27
|
+
</div>
|
|
28
|
+
<label class="flex flex-col gap-2 text-sm" v-if="dragActionHandler">
|
|
29
|
+
<span>Plugin Debug Logs (true | debug path):</span>
|
|
30
|
+
<WSimpleInput v-model="debugKey" />
|
|
31
|
+
</label>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</template>
|
|
35
|
+
</WPopover>
|
|
5
36
|
<WDarkModeSwitcher :show-label="false"/>
|
|
6
37
|
</div>
|
|
7
38
|
</template>
|
|
8
39
|
<script lang="ts" setup>
|
|
9
40
|
import WDarkModeSwitcher from "@witchcraft/ui/components/WDarkModeSwitcher"
|
|
41
|
+
import WButton from "@witchcraft/ui/components/WButton"
|
|
42
|
+
import WPopover from "@witchcraft/ui/components/WPopover"
|
|
43
|
+
import { copy } from "@witchcraft/ui/helpers"
|
|
44
|
+
import { ref, watch } from "vue"
|
|
45
|
+
import { applyFrameChanges } from "../layout/applyFrameChanges.js"
|
|
46
|
+
import { getFrameUncollapseInfo } from "../layout/getFrameUncollapseInfo.js"
|
|
47
|
+
import { settings } from "../settings.js"
|
|
48
|
+
import type { LayoutFrame, LayoutWindow } from "../types/index.js"
|
|
49
|
+
import { throwIfError } from "@alanscodelog/utils/throwIfError";
|
|
50
|
+
import { walk } from "@alanscodelog/utils/walk"
|
|
51
|
+
import type { DragActionHandler } from "../drag/DragActionHandler.js"
|
|
52
|
+
import { rotateLayout } from "../helpers/rotateFrames.js"
|
|
10
53
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
54
|
+
const props = defineProps<{
|
|
55
|
+
win: LayoutWindow
|
|
56
|
+
frames: LayoutFrame[],
|
|
57
|
+
dragActionHandler?: DragActionHandler<any>
|
|
15
58
|
}>()
|
|
16
59
|
|
|
60
|
+
const showDevActions = ref(false)
|
|
61
|
+
|
|
62
|
+
const collapseSizeValue = ref(Math.round(settings.collapseSizeScaled.width / settings.maxInt * 100))
|
|
63
|
+
|
|
64
|
+
function handleCollapseSizeChange(value: number) {
|
|
65
|
+
settings.collapseSize = value
|
|
66
|
+
collapseSizeValue.value = value
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
function uncollapseAll() {
|
|
71
|
+
const win = props.win
|
|
72
|
+
if (!win) return
|
|
73
|
+
for (const frame of Object.values(win.frames)) {
|
|
74
|
+
if (frame.docked && typeof frame.collapsed === 'number') {
|
|
75
|
+
const changes = throwIfError(getFrameUncollapseInfo(win, frame.id))
|
|
76
|
+
applyFrameChanges(win, changes)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const renameFrames = ref(false)
|
|
82
|
+
|
|
83
|
+
const rotationAngle = ref(0)
|
|
84
|
+
|
|
85
|
+
function rotateLayoutAction() {
|
|
86
|
+
const angles = [90, 180, 270] as const
|
|
87
|
+
const currentIdx = angles.indexOf(rotationAngle.value as any)
|
|
88
|
+
const nextIdx = (currentIdx + 1) % angles.length
|
|
89
|
+
const nextAngle = angles[nextIdx]
|
|
90
|
+
rotationAngle.value = nextAngle
|
|
91
|
+
rotateLayout(Object.values(props.win.frames), nextAngle)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const alphabet = "abcdefghijklmnopqrstuvwxyz".split("")
|
|
95
|
+
function copyState() {
|
|
96
|
+
const clone = walk(props.win, undefined, { save: true })
|
|
97
|
+
if (renameFrames.value) {
|
|
98
|
+
const framesList = Object.values(clone.frames)
|
|
99
|
+
for (let i = 0; i < framesList.length; i++) {
|
|
100
|
+
const frame = framesList[i] as LayoutFrame
|
|
101
|
+
delete clone.frames[frame.id]
|
|
102
|
+
;(frame as any)._originalId = frame.id
|
|
103
|
+
frame.id = alphabet[i % alphabet.length].repeat(i == 0 ? 1 : Math.ceil(i / alphabet.length))
|
|
104
|
+
clone.frames[frame.id] = frame
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const state = JSON.stringify(clone, null, 2)
|
|
108
|
+
copy(state)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const debugKey = ref(import.meta.dev ? "state.win.frames" : "false")
|
|
112
|
+
watch(debugKey, () => {
|
|
113
|
+
if (!props.dragActionHandler) return
|
|
114
|
+
const value = debugKey.value === "true"
|
|
115
|
+
? true
|
|
116
|
+
: debugKey.value === "false"
|
|
117
|
+
? false
|
|
118
|
+
: debugKey.value
|
|
119
|
+
|
|
120
|
+
for (const plugin of Object.values(props.dragActionHandler.actions)) {
|
|
121
|
+
plugin.debug = value
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
|
|
17
126
|
</script>
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { DragActionHandler } from "./DragActionHandler.js"
|
|
2
|
+
import type { ActionDragChangeResult, DragState, IDragAction } from "./types.js"
|
|
2
3
|
|
|
3
4
|
import { dirToOrientation } from "../helpers/dirToOrientation.js"
|
|
4
5
|
import { getEdgeOrientation } from "../helpers/getEdgeOrientation.js"
|
|
5
6
|
import { oppositeSide } from "../helpers/oppositeSide.js"
|
|
6
|
-
import {
|
|
7
|
+
import { applyFrameChanges } from "../layout/applyFrameChanges.js"
|
|
7
8
|
import { findFramesTouchingEdge } from "../layout/findFramesTouchingEdge.js"
|
|
8
9
|
import { getCloseFrameInfo } from "../layout/getCloseFrameInfo.js"
|
|
9
10
|
import type { CloseDeco } from "../types/index.js"
|
|
@@ -19,29 +20,55 @@ export class CloseAction implements IDragAction {
|
|
|
19
20
|
force: boolean
|
|
20
21
|
res: CloseInfo
|
|
21
22
|
cacheKey: string | undefined
|
|
23
|
+
lastReturn: ActionDragChangeResult
|
|
22
24
|
} | {
|
|
23
25
|
allowed: false
|
|
24
26
|
force: boolean
|
|
25
27
|
res: CloseInfo | undefined
|
|
26
28
|
cacheKey: string | undefined
|
|
29
|
+
lastReturn: ActionDragChangeResult | undefined
|
|
27
30
|
} = {} as any // this is initialized by this.reset()
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
debug: boolean | string = false
|
|
33
|
+
textHints: { actions: string[], errors: string[] } = { actions: [], errors: [] }
|
|
34
|
+
|
|
35
|
+
closeHints: {
|
|
36
|
+
actions: string[]
|
|
37
|
+
transformError: (e: KnownError) => string
|
|
38
|
+
} = {
|
|
39
|
+
actions: ["Hold Shift to Close", "Hold Ctrl+Shift to Force Close"],
|
|
40
|
+
transformError: e => e.message
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
handleEvent: (e: PointerEvent | KeyboardEvent, state: DragState) => boolean | "force" = (e: PointerEvent | KeyboardEvent) => {
|
|
44
|
+
if (e.ctrlKey && e.shiftKey) {
|
|
45
|
+
return "force"
|
|
46
|
+
}
|
|
47
|
+
if (e.shiftKey) return true
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
modifyDecos: (shapes: CloseDeco[]) => void = () => { }
|
|
31
52
|
hooks: {
|
|
32
53
|
onStart?: (active: boolean) => void
|
|
33
54
|
onCancel?: () => void
|
|
34
55
|
onError?: (e: KnownError) => void
|
|
35
|
-
}
|
|
56
|
+
} = {}
|
|
36
57
|
|
|
37
58
|
constructor(
|
|
38
|
-
handleEvent
|
|
39
|
-
|
|
40
|
-
|
|
59
|
+
handleEvent?: CloseAction["handleEvent"],
|
|
60
|
+
/** Modify the created decos before they are rendered. */
|
|
61
|
+
modifyDecos?: CloseAction["modifyDecos"],
|
|
62
|
+
hooks: CloseAction["hooks"] = {},
|
|
63
|
+
config?: {
|
|
64
|
+
debug?: boolean | string
|
|
65
|
+
closeHints?: Partial<CloseAction["closeHints"]>
|
|
66
|
+
}
|
|
41
67
|
) {
|
|
42
|
-
this.handleEvent = handleEvent
|
|
43
|
-
this.
|
|
68
|
+
if (handleEvent !== undefined) this.handleEvent = handleEvent
|
|
69
|
+
if (modifyDecos !== undefined) this.modifyDecos = modifyDecos
|
|
44
70
|
this.hooks = hooks
|
|
71
|
+
if (config?.debug) this.debug = true
|
|
45
72
|
this.reset()
|
|
46
73
|
}
|
|
47
74
|
|
|
@@ -50,33 +77,66 @@ export class CloseAction implements IDragAction {
|
|
|
50
77
|
allowed: false,
|
|
51
78
|
force: false,
|
|
52
79
|
res: undefined,
|
|
53
|
-
cacheKey: undefined
|
|
80
|
+
cacheKey: undefined,
|
|
81
|
+
lastReturn: undefined
|
|
54
82
|
}
|
|
55
|
-
this.
|
|
83
|
+
this.modifyDecos([])
|
|
56
84
|
}
|
|
57
85
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
} = state
|
|
86
|
+
private _getDecos(state: DragState): CloseDeco[] {
|
|
87
|
+
let decos: CloseDeco[] = []
|
|
88
|
+
const { isDragging } = state
|
|
62
89
|
if (isDragging && this.state.allowed && this.state.res) {
|
|
63
90
|
const { force } = this.state
|
|
64
|
-
|
|
65
|
-
|
|
91
|
+
decos = this.state.res.deleted.map(_ => ({
|
|
92
|
+
id: _.id,
|
|
93
|
+
type: "close" as const,
|
|
94
|
+
force,
|
|
95
|
+
shapes: [
|
|
96
|
+
{
|
|
97
|
+
type: "square",
|
|
98
|
+
data: { x: _.x, y: _.y, width: _.width, height: _.height },
|
|
99
|
+
attrs: {
|
|
100
|
+
class: force
|
|
101
|
+
? `deco-close-force-frame bg-orange-500/50`
|
|
102
|
+
: `deco-close-frame bg-orange-500/20`
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
}))
|
|
107
|
+
}
|
|
108
|
+
this.modifyDecos(decos)
|
|
109
|
+
return decos
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
setTextHints(result: true | CloseInfo | KnownError | undefined): void {
|
|
113
|
+
if (result === undefined) {
|
|
114
|
+
this.textHints.actions = []
|
|
115
|
+
this.textHints.errors = []
|
|
116
|
+
} else if (result instanceof Error) {
|
|
117
|
+
this.textHints.actions = []
|
|
118
|
+
this.textHints.errors = [this.closeHints.transformError(result)]
|
|
66
119
|
} else {
|
|
67
|
-
this.
|
|
120
|
+
this.textHints.actions = this.closeHints.actions
|
|
121
|
+
this.textHints.errors = []
|
|
68
122
|
}
|
|
69
123
|
}
|
|
70
124
|
|
|
125
|
+
getTextHints(type: "start" | "move" | "end"): {
|
|
126
|
+
actions: string[]
|
|
127
|
+
errors: string[]
|
|
128
|
+
} {
|
|
129
|
+
if (type === "end") { this.setTextHints(undefined) }
|
|
130
|
+
return this.textHints
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
|
|
71
134
|
canHandleRequest(e: PointerEvent | KeyboardEvent, state: DragState): boolean {
|
|
72
135
|
const { draggingEdges } = state
|
|
73
136
|
if (draggingEdges.length !== 1) return false
|
|
74
137
|
const res = this.handleEvent(e, state)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
} else {
|
|
78
|
-
this.state.force = false
|
|
79
|
-
}
|
|
138
|
+
this.state.force = res === "force"
|
|
139
|
+
this.setTextHints(state.isDragging === "edge" ? true : undefined)
|
|
80
140
|
if (res) {
|
|
81
141
|
this.hooks.onStart?.(true)
|
|
82
142
|
return true
|
|
@@ -89,7 +149,7 @@ export class CloseAction implements IDragAction {
|
|
|
89
149
|
_type: "start" | "end" | "move",
|
|
90
150
|
_e: PointerEvent | undefined,
|
|
91
151
|
state: DragState
|
|
92
|
-
):
|
|
152
|
+
): ActionDragChangeResult {
|
|
93
153
|
const {
|
|
94
154
|
touchingFramesArrays,
|
|
95
155
|
dragDirections,
|
|
@@ -97,10 +157,16 @@ export class CloseAction implements IDragAction {
|
|
|
97
157
|
draggingEdges,
|
|
98
158
|
visualEdges,
|
|
99
159
|
frames,
|
|
100
|
-
isDraggingFromWindowEdge
|
|
160
|
+
isDraggingFromWindowEdge,
|
|
161
|
+
dragPoint
|
|
101
162
|
} = state
|
|
102
163
|
const oppositeOrientation = oppositeSide(getEdgeOrientation(draggingEdges[0]))
|
|
103
|
-
|
|
164
|
+
const cacheKey = `${dragPoint?.x}-${dragPoint?.y}-${dragDirections[oppositeOrientation]!}-${this.state.force}`
|
|
165
|
+
if (this.state.allowed) {
|
|
166
|
+
if (this.state.cacheKey === cacheKey) {
|
|
167
|
+
return this.state.lastReturn
|
|
168
|
+
}
|
|
169
|
+
}
|
|
104
170
|
if (isDragging && draggingEdges.length === 1) {
|
|
105
171
|
const res = findFramesTouchingEdge(
|
|
106
172
|
draggingEdges[0],
|
|
@@ -115,36 +181,32 @@ export class CloseAction implements IDragAction {
|
|
|
115
181
|
const sizeKey = orientation === "horizontal" ? "width" : "height"
|
|
116
182
|
const smallestFrameSize = Math.min(...res.map(_ => _.frame[sizeKey]))
|
|
117
183
|
const frame = res.find(_ => _.frame[sizeKey] === smallestFrameSize)!.frame!
|
|
118
|
-
|
|
119
|
-
if (this.state.allowed) {
|
|
120
|
-
if (this.state.cacheKey === cacheKey) {
|
|
121
|
-
this.updateDecos(state)
|
|
122
|
-
return true
|
|
123
|
-
}
|
|
124
|
-
}
|
|
184
|
+
|
|
125
185
|
const closeInfo = getCloseFrameInfo(Object.values(frames), visualEdges, frame, dragDirections[oppositeOrientation]!, "dir", this.state.force)
|
|
186
|
+
this.state.cacheKey = cacheKey
|
|
187
|
+
this.setTextHints(closeInfo)
|
|
126
188
|
if (!(closeInfo instanceof Error)) {
|
|
127
|
-
this.state.allowed = true
|
|
128
189
|
this.state.res = closeInfo
|
|
129
|
-
this.state.
|
|
130
|
-
ok = true
|
|
190
|
+
this.state.allowed = true
|
|
131
191
|
} else {
|
|
192
|
+
this.state.res = undefined
|
|
193
|
+
this.state.allowed = false
|
|
132
194
|
this.hooks.onError?.(closeInfo)
|
|
195
|
+
return { updateEdges: true, shapes: [] }
|
|
133
196
|
}
|
|
134
197
|
}
|
|
135
198
|
}
|
|
136
|
-
this.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
return !isDraggingFromWindowEdge
|
|
199
|
+
const decos = this._getDecos(state)
|
|
200
|
+
this.state.lastReturn = { updateEdges: !isDraggingFromWindowEdge, shapes: decos.flatMap(_ => _.shapes) }
|
|
201
|
+
return this.state.lastReturn
|
|
141
202
|
}
|
|
142
203
|
|
|
143
204
|
onDragApply(state: DragState): boolean {
|
|
144
205
|
if (this.state.res) {
|
|
145
|
-
const { deletedFrames, modifiedFrames } = this.state.res
|
|
146
206
|
const win = state.win
|
|
147
|
-
|
|
207
|
+
if (this.debug) { DragActionHandler.debugState(this.name, "before", state, this.state, this.debug) }
|
|
208
|
+
applyFrameChanges(win, this.state.res)
|
|
209
|
+
if (this.debug) { DragActionHandler.debugState(this.name, "after", state, this.state, this.debug) }
|
|
148
210
|
this.reset()
|
|
149
211
|
return true
|
|
150
212
|
}
|