@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.
Files changed (190) hide show
  1. package/README.md +27 -24
  2. package/dist/module.json +1 -1
  3. package/dist/runtime/components/FrameDragHandle.d.vue.ts +15 -0
  4. package/dist/runtime/components/FrameDragHandle.vue +28 -0
  5. package/dist/runtime/components/FrameDragHandle.vue.d.ts +15 -0
  6. package/dist/runtime/components/LayoutDecos.d.vue.ts +2 -4
  7. package/dist/runtime/components/LayoutDecos.vue +10 -29
  8. package/dist/runtime/components/LayoutDecos.vue.d.ts +2 -4
  9. package/dist/runtime/components/LayoutEdges.d.vue.ts +3 -3
  10. package/dist/runtime/components/LayoutEdges.vue +9 -10
  11. package/dist/runtime/components/LayoutEdges.vue.d.ts +3 -3
  12. package/dist/runtime/components/LayoutFrame.d.vue.ts +1 -1
  13. package/dist/runtime/components/LayoutFrame.vue +1 -4
  14. package/dist/runtime/components/LayoutFrame.vue.d.ts +1 -1
  15. package/dist/runtime/components/LayoutShapeSquare.d.vue.ts +3 -1
  16. package/dist/runtime/components/LayoutShapeSquare.vue +3 -5
  17. package/dist/runtime/components/LayoutShapeSquare.vue.d.ts +3 -1
  18. package/dist/runtime/components/LayoutWindow.d.vue.ts +26 -12
  19. package/dist/runtime/components/LayoutWindow.vue +95 -84
  20. package/dist/runtime/components/LayoutWindow.vue.d.ts +26 -12
  21. package/dist/runtime/composables/useFrames.d.ts +15 -13
  22. package/dist/runtime/composables/useFrames.js +59 -39
  23. package/dist/runtime/demo/App.vue +116 -30
  24. package/dist/runtime/demo/DemoControls.d.vue.ts +4 -1
  25. package/dist/runtime/demo/DemoControls.vue +98 -4
  26. package/dist/runtime/demo/DemoControls.vue.d.ts +4 -1
  27. package/dist/runtime/drag/CloseAction.d.ts +26 -5
  28. package/dist/runtime/drag/CloseAction.js +87 -40
  29. package/dist/runtime/drag/DragActionHandler.d.ts +20 -8
  30. package/dist/runtime/drag/DragActionHandler.js +47 -12
  31. package/dist/runtime/drag/FrameDragAction.d.ts +45 -0
  32. package/dist/runtime/drag/FrameDragAction.js +143 -0
  33. package/dist/runtime/drag/SplitAction.d.ts +32 -11
  34. package/dist/runtime/drag/SplitAction.js +82 -24
  35. package/dist/runtime/drag/createDefaultHandlers.d.ts +9 -0
  36. package/dist/runtime/drag/createDefaultHandlers.js +10 -0
  37. package/dist/runtime/drag/defaultDragActions.d.ts +9 -0
  38. package/dist/runtime/drag/defaultDragActions.js +10 -0
  39. package/dist/runtime/drag/types.d.ts +82 -13
  40. package/dist/runtime/drag/types.js +1 -0
  41. package/dist/runtime/helpers/createZoneSideClipPath.d.ts +12 -0
  42. package/dist/runtime/helpers/createZoneSideClipPath.js +17 -0
  43. package/dist/runtime/helpers/doEdgesOverlap.d.ts +3 -1
  44. package/dist/runtime/helpers/doEdgesOverlap.js +5 -5
  45. package/dist/runtime/helpers/getDockBoundaries.d.ts +19 -0
  46. package/dist/runtime/helpers/getDockBoundaries.js +14 -0
  47. package/dist/runtime/helpers/getEdgeLength.d.ts +2 -0
  48. package/dist/runtime/helpers/getEdgeLength.js +5 -0
  49. package/dist/runtime/helpers/getIntersections.js +2 -2
  50. package/dist/runtime/helpers/getIntersectionsCss.js +2 -2
  51. package/dist/runtime/helpers/getMoveEdgeInfo.js +2 -2
  52. package/dist/runtime/helpers/getResizeLimit.js +2 -2
  53. package/dist/runtime/helpers/getShapeSquareCss.js +2 -2
  54. package/dist/runtime/helpers/getVisualEdgeCss.js +2 -2
  55. package/dist/runtime/helpers/getVisualEdges.d.ts +1 -1
  56. package/dist/runtime/helpers/getVisualEdges.js +4 -3
  57. package/dist/runtime/helpers/index.d.ts +4 -0
  58. package/dist/runtime/helpers/index.js +4 -0
  59. package/dist/runtime/helpers/isEdgeEqual.js +2 -4
  60. package/dist/runtime/helpers/isWindowEdge.js +2 -2
  61. package/dist/runtime/helpers/isWindowEdgePoint.js +2 -2
  62. package/dist/runtime/helpers/moveEdge.js +2 -2
  63. package/dist/runtime/helpers/numberToScaledPercent.d.ts +1 -1
  64. package/dist/runtime/helpers/numberToScaledPercent.js +2 -2
  65. package/dist/runtime/helpers/numberToScaledSize.js +2 -2
  66. package/dist/runtime/helpers/rotateFrames.d.ts +7 -0
  67. package/dist/runtime/helpers/rotateFrames.js +36 -0
  68. package/dist/runtime/helpers/scaledPointToPx.d.ts +13 -0
  69. package/dist/runtime/helpers/scaledPointToPx.js +7 -0
  70. package/dist/runtime/helpers/toWindowCoord.js +2 -2
  71. package/dist/runtime/layout/applyFrameChanges.d.ts +10 -0
  72. package/dist/runtime/layout/applyFrameChanges.js +29 -0
  73. package/dist/runtime/layout/createSplitDecoFromDrag.d.ts +6 -1
  74. package/dist/runtime/layout/createSplitDecoFromDrag.js +4 -4
  75. package/dist/runtime/layout/createSplitDecoShapes.d.ts +7 -0
  76. package/dist/runtime/layout/{createSplitDecoEdge.js → createSplitDecoShapes.js} +6 -3
  77. package/dist/runtime/layout/debugFrame.js +2 -1
  78. package/dist/runtime/layout/findSafeSplitEdge.js +2 -2
  79. package/dist/runtime/layout/frameCreate.js +2 -2
  80. package/dist/runtime/layout/getCloseFrameInfo.d.ts +7 -6
  81. package/dist/runtime/layout/getCloseFrameInfo.js +10 -3
  82. package/dist/runtime/layout/getDragZones.d.ts +8 -0
  83. package/dist/runtime/layout/getDragZones.js +32 -0
  84. package/dist/runtime/layout/getFillEmptySpaceInfo.d.ts +65 -0
  85. package/dist/runtime/layout/getFillEmptySpaceInfo.js +69 -0
  86. package/dist/runtime/layout/getFrameCollapseInfo.d.ts +13 -0
  87. package/dist/runtime/layout/getFrameCollapseInfo.js +93 -0
  88. package/dist/runtime/layout/getFrameDockInfo.d.ts +9 -0
  89. package/dist/runtime/layout/getFrameDockInfo.js +82 -0
  90. package/dist/runtime/layout/getFrameDragZones.d.ts +16 -0
  91. package/dist/runtime/layout/getFrameDragZones.js +74 -0
  92. package/dist/runtime/layout/getFrameRearrangeInfo.d.ts +139 -0
  93. package/dist/runtime/layout/getFrameRearrangeInfo.js +87 -0
  94. package/dist/runtime/layout/getFrameSplitInfo.d.ts +7 -5
  95. package/dist/runtime/layout/getFrameSplitInfo.js +10 -3
  96. package/dist/runtime/layout/getFrameSwapInfo.d.ts +9 -0
  97. package/dist/runtime/layout/getFrameSwapInfo.js +27 -0
  98. package/dist/runtime/layout/getFrameTo.js +2 -2
  99. package/dist/runtime/layout/getFrameUncollapseInfo.d.ts +12 -0
  100. package/dist/runtime/layout/getFrameUncollapseInfo.js +88 -0
  101. package/dist/runtime/layout/getFrameUndockInfo.d.ts +13 -0
  102. package/dist/runtime/layout/getFrameUndockInfo.js +51 -0
  103. package/dist/runtime/layout/getFramesRedistributeInfo.d.ts +29 -0
  104. package/dist/runtime/layout/getFramesRedistributeInfo.js +53 -0
  105. package/dist/runtime/layout/getWindowDragZones.d.ts +6 -0
  106. package/dist/runtime/layout/getWindowDragZones.js +49 -0
  107. package/dist/runtime/layout/index.d.ts +14 -5
  108. package/dist/runtime/layout/index.js +14 -5
  109. package/dist/runtime/layout/isPointInRect.d.ts +7 -0
  110. package/dist/runtime/layout/{isPointInFrame.js → isPointInRect.js} +1 -1
  111. package/dist/runtime/layout/resizeFrame.js +2 -2
  112. package/dist/runtime/settings.d.ts +41 -16
  113. package/dist/runtime/settings.js +95 -53
  114. package/dist/runtime/types/index.d.ts +324 -55
  115. package/dist/runtime/types/index.js +54 -20
  116. package/package.json +28 -29
  117. package/src/runtime/components/FrameDragHandle.vue +30 -0
  118. package/src/runtime/components/LayoutDecos.vue +12 -36
  119. package/src/runtime/components/LayoutEdges.vue +27 -22
  120. package/src/runtime/components/LayoutFrame.vue +6 -5
  121. package/src/runtime/components/LayoutShapeSquare.vue +11 -5
  122. package/src/runtime/components/LayoutWindow.vue +110 -101
  123. package/src/runtime/composables/useFrames.ts +80 -50
  124. package/src/runtime/demo/App.vue +126 -36
  125. package/src/runtime/demo/DemoControls.vue +115 -6
  126. package/src/runtime/drag/CloseAction.ts +106 -44
  127. package/src/runtime/drag/DragActionHandler.ts +71 -20
  128. package/src/runtime/drag/FrameDragAction.ts +202 -0
  129. package/src/runtime/drag/SplitAction.ts +106 -34
  130. package/src/runtime/drag/createDefaultHandlers.ts +19 -0
  131. package/src/runtime/drag/defaultDragActions.ts +19 -0
  132. package/src/runtime/drag/types.ts +90 -20
  133. package/src/runtime/helpers/createZoneSideClipPath.ts +41 -0
  134. package/src/runtime/helpers/doEdgesOverlap.ts +11 -5
  135. package/src/runtime/helpers/getDockBoundaries.ts +36 -0
  136. package/src/runtime/helpers/getEdgeLength.ts +10 -0
  137. package/src/runtime/helpers/getIntersections.ts +2 -2
  138. package/src/runtime/helpers/getIntersectionsCss.ts +2 -2
  139. package/src/runtime/helpers/getMoveEdgeInfo.ts +2 -2
  140. package/src/runtime/helpers/getResizeLimit.ts +2 -2
  141. package/src/runtime/helpers/getShapeSquareCss.ts +2 -2
  142. package/src/runtime/helpers/getVisualEdgeCss.ts +2 -2
  143. package/src/runtime/helpers/getVisualEdges.ts +5 -4
  144. package/src/runtime/helpers/index.ts +4 -0
  145. package/src/runtime/helpers/isEdgeEqual.ts +2 -4
  146. package/src/runtime/helpers/isWindowEdge.ts +2 -2
  147. package/src/runtime/helpers/isWindowEdgePoint.ts +2 -2
  148. package/src/runtime/helpers/moveEdge.ts +2 -2
  149. package/src/runtime/helpers/numberToScaledPercent.ts +3 -3
  150. package/src/runtime/helpers/numberToScaledSize.ts +2 -2
  151. package/src/runtime/helpers/rotateFrames.ts +45 -0
  152. package/src/runtime/helpers/scaledPointToPx.ts +13 -0
  153. package/src/runtime/helpers/toWindowCoord.ts +2 -2
  154. package/src/runtime/layout/applyFrameChanges.ts +39 -0
  155. package/src/runtime/layout/createSplitDecoFromDrag.ts +12 -6
  156. package/src/runtime/layout/{createSplitDecoEdge.ts → createSplitDecoShapes.ts} +17 -7
  157. package/src/runtime/layout/debugFrame.ts +1 -1
  158. package/src/runtime/layout/findSafeSplitEdge.ts +3 -3
  159. package/src/runtime/layout/frameCreate.ts +2 -2
  160. package/src/runtime/layout/getCloseFrameInfo.ts +21 -8
  161. package/src/runtime/layout/getDragZones.ts +48 -0
  162. package/src/runtime/layout/getFillEmptySpaceInfo.ts +177 -0
  163. package/src/runtime/layout/getFrameCollapseInfo.ts +164 -0
  164. package/src/runtime/layout/getFrameDockInfo.ts +126 -0
  165. package/src/runtime/layout/getFrameDragZones.ts +100 -0
  166. package/src/runtime/layout/getFrameRearrangeInfo.ts +261 -0
  167. package/src/runtime/layout/getFrameSplitInfo.ts +21 -8
  168. package/src/runtime/layout/getFrameSwapInfo.ts +45 -0
  169. package/src/runtime/layout/getFrameTo.ts +2 -2
  170. package/src/runtime/layout/getFrameUncollapseInfo.ts +160 -0
  171. package/src/runtime/layout/getFrameUndockInfo.ts +97 -0
  172. package/src/runtime/layout/getFramesRedistributeInfo.ts +98 -0
  173. package/src/runtime/layout/getWindowDragZones.ts +59 -0
  174. package/src/runtime/layout/index.ts +14 -5
  175. package/src/runtime/layout/isPointInRect.ts +7 -0
  176. package/src/runtime/layout/resizeFrame.ts +2 -2
  177. package/src/runtime/settings.ts +69 -49
  178. package/src/runtime/types/index.ts +143 -28
  179. package/dist/runtime/layout/closeFrame.d.ts +0 -5
  180. package/dist/runtime/layout/closeFrame.js +0 -13
  181. package/dist/runtime/layout/closeFrames.d.ts +0 -2
  182. package/dist/runtime/layout/closeFrames.js +0 -8
  183. package/dist/runtime/layout/createSplitDecoEdge.d.ts +0 -2
  184. package/dist/runtime/layout/frameSplit.d.ts +0 -16
  185. package/dist/runtime/layout/frameSplit.js +0 -9
  186. package/dist/runtime/layout/isPointInFrame.d.ts +0 -2
  187. package/src/runtime/layout/closeFrame.ts +0 -33
  188. package/src/runtime/layout/closeFrames.ts +0 -14
  189. package/src/runtime/layout/frameSplit.ts +0 -31
  190. package/src/runtime/layout/isPointInFrame.ts +0 -7
@@ -1,57 +1,92 @@
1
1
  <template>
2
2
  <!-- overflow hidden is because the borders inside will make it overflow -->
3
- <div :class="twMerge(
4
- `window
5
- relative
6
- overflow-hidden
7
- `,
3
+ <div
4
+ :class="twMerge(
5
+ `
6
+ layout-wrapper
7
+ flex
8
+ flex-col
9
+ `,
8
10
  isDragging && `dragging cursor-pointer`,
9
11
  requestType && `request-${requestType}`,
10
12
  ($attrs as any).class
11
13
  )"
12
- ref="windowEl"
13
- v-bind="{...$attrs, class: undefined}"
14
+ v-bind="{ ...$attrs, class: undefined }"
14
15
  >
15
- <template v-if="windowEl && win">
16
- <LayoutFrameComponent :frame="frame"
17
- :is-active-frame="frame.id === win.activeFrame"
18
- v-for="frame of frames"
19
- :key="frame.id"
20
- v-bind="frameProps"
21
- @focus="windowSetActiveFrame(win, frame.id)"
16
+ <!-- we need the size withot borders for correct px calculations -->
17
+ <div class="
18
+ layout-window
19
+ relative
20
+ overflow-hidden
21
+ flex-1
22
+ " ref="windowEl">
23
+ <template v-if="windowEl && win">
24
+ <LayoutFrameComponent
25
+ :frame="frame"
26
+ :is-active-frame="frame.id === win.activeFrame"
27
+ v-for="frame of frames"
28
+ :key="frame.id"
29
+ v-bind="frameProps"
30
+ @focus="windowSetActiveFrame(win, frame.id)"
31
+ >
32
+ <slot :name="`frame-${frame.id}`" v-bind="{frame}"/>
33
+ </LayoutFrameComponent>
34
+ <LayoutEdgesComponent
35
+ :win="win"
36
+ :active-frame="win.activeFrame ? frames[win.activeFrame] : undefined"
37
+ :edges="visualEdges"
38
+ :intersections="intersections"
39
+ :dragging-edge="draggingEdges.length === 1 ? draggingEdges[0] : undefined"
40
+ :dragging-intersection="draggingIntersection"
41
+ v-bind="edgesProps"
42
+ @drag-start="dragStart"
43
+ />
44
+ <LayoutDecosComponent
45
+ :shapes="shapes"
46
+ />
47
+ <slot name="extra-decos"/>
48
+ </template>
49
+ </div>
50
+ <Teleport
51
+ v-if="isDragging === 'frame' && frameDragFrameId && dragPoint"
52
+ :to="ghostTeleportTo"
53
+ defer
54
+ >
55
+ <div
56
+ class="fixed z-[9999] pointer-events-none"
57
+ :style="{
58
+ left: (dragPoint.x / settings.maxInt * win.pxWidth + win.pxX) + 'px',
59
+ top: (dragPoint.y / settings.maxInt * win.pxHeight + win.pxY) + 'px',
60
+ width: frames[frameDragFrameId]
61
+ ? (frames[frameDragFrameId].width / settings.maxInt * win.pxWidth) + 'px'
62
+ : undefined,
63
+ height: frames[frameDragFrameId]
64
+ ? (frames[frameDragFrameId].height / settings.maxInt * win.pxHeight) + 'px'
65
+ : undefined
66
+ }"
22
67
  >
23
- <slot :name="`frame-${frame.id}`" v-bind="{frame}"/>
24
- </LayoutFrameComponent>
25
- <LayoutEdgesComponent
26
- :win="win"
27
- :active-frame="win.activeFrame ? frames[win.activeFrame] : undefined"
28
- :edges="visualEdges"
29
- :intersections="intersections"
30
- :dragging-edge="draggingEdges.length === 1 ? draggingEdges[0] : undefined"
31
- :dragging-intersection="draggingIntersection"
32
- v-bind="edgesProps"
33
- @drag-start="dragStart"
34
- />
35
- <LayoutDecosComponent
36
- :frames="frames"
37
- :split-decos="splitDecos"
38
- :close-decos="closeDecos"
39
- />
40
- <slot name="extra-decos"/>
41
- </template>
42
- <Teleport v-if="instructionsTeleportTo && filteredUsageInstructions.length > 0" defer :to="instructionsTeleportTo">
68
+ <slot
69
+ :id="frameDragFrameId"
70
+ :name="`frame-drag-ghost`"
71
+ >
72
+ <div class="frame-drag-ghost border border-neutral-500 bg-white/50 w-10 h-10"/>
73
+ </slot>
74
+ </div>
75
+ </Teleport>
76
+ <Teleport v-if="textHintsTeleportTo && textHints.length > 0" :to="textHintsTeleportTo" defer>
43
77
  <span aria-live="polite">
44
78
  <span
45
- class="
79
+ :class="twMerge(`
80
+ text-hint
46
81
  after:content-['┃']
47
82
  last:after:content-none
48
83
  after:mx-1
49
- after:text-gray-500
50
- "
51
- v-for="instruction of filteredUsageInstructions"
52
- :key="instruction"
84
+ after:text-neutral-500
85
+ `, instruction.classes)"
86
+ v-for="instruction of textHints"
87
+ :key="instruction.text"
53
88
  >
54
- {{ instruction }}
89
+ {{ instruction.text }}
55
90
  </span>
56
91
  </span>
57
92
  </Teleport>
@@ -60,32 +95,31 @@
60
95
  <script lang="ts" setup>
61
96
  import { useGlobalResizeObserver } from "@witchcraft/ui/composables/useGlobalResizeObserver"
62
97
  import { twMerge } from "@witchcraft/ui/utils/twMerge"
63
- import { computed, ref,useAttrs,watch } from "vue"
64
- import { type HTMLAttributes } from "vue"
98
+ import { computed, provide, reactive, ref, useAttrs, watch } from "vue"
65
99
 
66
100
  import LayoutDecosComponent from "./LayoutDecos.vue"
67
101
  import LayoutEdgesComponent from "./LayoutEdges.vue"
68
102
  import LayoutFrameComponent from "./LayoutFrame.vue"
69
103
 
70
104
  import { useFrames } from "../composables/useFrames.js"
71
- import { CloseAction } from "../drag/CloseAction"
105
+ import { createDefaultHandlers } from "../drag/createDefaultHandlers.js"
72
106
  import { DragActionHandler } from "../drag/DragActionHandler"
73
- import { SplitAction } from "../drag/SplitAction.js"
74
- import { type DragState, type IDragAction } from "../drag/types.js"
107
+ import { dragContextInjectionKey, type DragState, type IDragAction } from "../drag/types.js"
75
108
  import { updateWindowWithEvent } from "../helpers/updateWindowSizeWithEvent.js"
76
109
  import { windowSetActiveFrame } from "../layout/windowSetActiveFrame.js"
77
- import { type CloseDeco, type LayoutEdgesProps, type LayoutFrameProps, type LayoutWindow, type SplitDeco } from "../types/index.js"
110
+ import type { LayoutEdgesProps, LayoutFrameProps, LayoutWindow } from "../types/index.js"
111
+ import { settings } from "../settings.js"
78
112
 
79
113
 
80
114
  const $attrs = useAttrs()
81
115
  const win = defineModel<LayoutWindow>("win", { required: true })
82
116
 
83
117
  const props = withDefaults(defineProps<{
84
- additionalDragActions?: IDragAction[]
85
- splitKeyHandler?: (e: PointerEvent | KeyboardEvent, state: DragState) => boolean
86
- closeKeyHandler?: (e: PointerEvent | KeyboardEvent, state: DragState) => boolean
87
- usageInstructions?: Record<string, string | undefined>
88
- instructionsTeleportTo: string | undefined
118
+ /** Custom drag action handlers. Falls back to default split/close/frame handlers if not provided. */
119
+ actionHandlers?: IDragAction[]
120
+ textHints?: {text:string, classes?:string}[]
121
+ textHintsTeleportTo: string | undefined
122
+ ghostTeleportTo?: string
89
123
  /**
90
124
  * You might need to temporarily disable updating the window size while transitioning, depending on your layout.
91
125
  *
@@ -94,61 +128,28 @@ const props = withDefaults(defineProps<{
94
128
  * When this is turned back on again, it will trigger an update. You can also trigger updates manually with the exposed updateWindowSize function.
95
129
  */
96
130
  allowWindowSizeUpdate?: boolean
97
- frameProps?: Partial<Omit<LayoutFrameProps, "frame" | "isActiveFrame" | "onFocus">>,
98
- edgesProps?: Partial<Omit<LayoutEdgesProps, "edges" | "intersections" | "win">>
131
+ frameProps?: Partial<Omit<LayoutFrameProps, "frame" | "isActiveFrame" | "onFocus">>
132
+ edgesProps?: Partial<Omit<LayoutEdgesProps, "edges" | "intersections" | "win">>,
99
133
  }>(), {
100
- additionalDragActions: () => ([]),
101
- splitKeyHandler: undefined,
102
- closeKeyHandler: undefined,
103
- usageInstructions: () => ({ }),
104
- instructionTeleportTo: undefined,
105
- allowWindowSizeUpdate: true,
134
+ actionHandlers: undefined,
135
+ textHints: () => [],
136
+ textHintsTeleportTo: undefined,
137
+ ghostTeleportTo: "#root",
138
+ allowWindowSizeUpdate: true
106
139
  })
107
140
  const emit = defineEmits<{
108
141
  (e: "isShowingDrag", value: boolean): void
109
142
  (e: "dragState", value: DragState): void
110
143
  }>()
111
144
 
112
- const filteredUsageInstructions = computed(() => Object.values(props.usageInstructions).filter(_ => _ !== undefined).map(_ => _))
113
-
114
- const splitKeyHandler = props.splitKeyHandler ?? ((e: PointerEvent | KeyboardEvent, state: DragState) =>
115
- e.altKey || state.isDraggingFromWindowEdge)
116
- const closeKeyHandler = props.closeKeyHandler ?? ((e: PointerEvent | KeyboardEvent) => {
117
- if (e.ctrlKey && e.shiftKey) {
118
- return "force"
119
- }
120
- if (e.shiftKey) return true
121
- return false
122
- })
123
-
124
145
 
125
146
  const windowEl = ref<HTMLElement | null>(null)
126
147
 
127
- const showDragging = ref(true)
128
-
129
- const closeDecos = ref<CloseDeco[]>([])
130
- const splitDecos = ref<SplitDeco[]>([])
131
- const requestType = ref<"split" | "close" | undefined | string>()
148
+ const requestType = ref<string | undefined | string>()
132
149
 
133
150
  const dragActionHandler = new DragActionHandler(
134
- (
135
- type: "start" | "move" | "end",
136
- _e: PointerEvent | KeyboardEvent | undefined,
137
- state: DragState
138
- ) => type === "move" ? !state.isDraggingFromWindowEdge : undefined,
139
151
  [
140
- new SplitAction(
141
- splitKeyHandler,
142
- ((decos: SplitDeco[]) => splitDecos.value = decos),
143
- {
144
- onStart: () => showDragging.value = false,
145
- onCancel: () => showDragging.value = true,
146
- }),
147
- new CloseAction(
148
- closeKeyHandler,
149
- ((decos: CloseDeco[]) => closeDecos.value = decos),
150
- ),
151
- ...props.additionalDragActions
152
+ ...(props.actionHandlers ?? createDefaultHandlers()),
152
153
  ],
153
154
  {
154
155
  onEvent: (e, cancel) => {
@@ -163,12 +164,21 @@ const dragActionHandler = new DragActionHandler(
163
164
  onEnd: () => {
164
165
  requestType.value = undefined
165
166
  showDragging.value = true
166
- }
167
+ },
167
168
  }
168
169
  )
170
+ dragActionHandler.shapes = reactive([])
171
+ dragActionHandler.textHints = reactive({ actions: [], errors: [] })
172
+ const shapes = dragActionHandler.shapes
173
+
174
+ const dragContext = useFrames(
175
+ win,
176
+ dragActionHandler
177
+ )
169
178
 
170
179
  const {
171
180
  dragStart,
181
+ dragPoint,
172
182
  visualEdges,
173
183
  isDragging,
174
184
  draggingEdges,
@@ -176,25 +186,24 @@ const {
176
186
  frames,
177
187
  intersections,
178
188
  state,
179
- } = useFrames(
180
- win,
189
+ frameDragFrameId,
181
190
  showDragging,
182
- dragActionHandler,
183
- )
191
+ } = dragContext
184
192
 
193
+ provide(dragContextInjectionKey, dragContext)
185
194
 
186
195
  function getWindowOffset() {
187
196
  const windowElRect = windowEl.value!.getBoundingClientRect()
188
197
  return {
189
198
  pxX: Math.floor(windowElRect.x),
190
- pxY: Math.floor(windowElRect.y),
199
+ pxY: Math.floor(windowElRect.y)
191
200
  }
192
201
  }
193
202
  function getWindowSize() {
194
203
  const windowElRect = windowEl.value!.getBoundingClientRect()
195
204
  return {
196
205
  pxWidth: Math.floor(windowElRect.width),
197
- pxHeight: Math.floor(windowElRect.height),
206
+ pxHeight: Math.floor(windowElRect.height)
198
207
  }
199
208
  }
200
209
  function updateWindowSize() {
@@ -217,7 +226,7 @@ defineExpose({
217
226
  state,
218
227
  win,
219
228
  updateWindowSize,
229
+ dragActionHandler
220
230
  })
221
-
222
231
  </script>
223
232
 
@@ -1,10 +1,10 @@
1
1
  import { debounce } from "@alanscodelog/utils/debounce"
2
2
  import { keys } from "@alanscodelog/utils/keys"
3
3
  import type { Ref } from "vue"
4
- import { computed, onBeforeUnmount, onMounted, ref, watchEffect } from "vue"
4
+ import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue"
5
5
 
6
6
  import { DragDirectionStore } from "../drag/DragDirectionStore.js"
7
- import type { DragChangeHandler, DragState } from "../drag/types.js"
7
+ import type { DragChangeHandler, DragChangeResult, DragState, EdgeDragStartData, FrameDragStartData } from "../drag/types.js"
8
8
  import { cloneFrame } from "../helpers/cloneFrame.js"
9
9
  import { getIntersections } from "../helpers/getIntersections.js"
10
10
  import { getVisualEdges } from "../helpers/getVisualEdges.js"
@@ -12,21 +12,21 @@ import { isWindowEdge } from "../helpers/isWindowEdge.js"
12
12
  import { moveEdge } from "../helpers/moveEdge.js"
13
13
  import { toWindowCoord } from "../helpers/toWindowCoord.js"
14
14
  import { findFramesTouchingEdge } from "../layout/findFramesTouchingEdge.js"
15
- import { isPointInFrame } from "../layout/isPointInFrame.js"
16
- import type { Direction, Edge, IntersectionEntry, LayoutFrame, LayoutWindow, Orientation, Point } from "../types/index.js"
15
+ import { isPointInRect } from "../layout/isPointInRect.js"
16
+ import type { Direction, Edge, FrameId, IntersectionEntry, LayoutFrame, LayoutWindow, Orientation, Point } from "../types/index.js"
17
17
 
18
18
  export function useFrames(
19
19
  win: Ref<LayoutWindow>,
20
- /** Whether to show merged the moved frames while dragging. */
21
- showDragging: Ref<boolean>,
22
20
  handler: {
23
21
  eventHandler: (e: KeyboardEvent, state: DragState, forceRecalculateEdges: () => void) => void
24
22
  /**
25
- * Called when the drag coordinates change (during any event). Should return true to allow the edges to be moved, or false to prevent it.
23
+ * Called when the drag coordinates change (during any event). Should return true to allow the edges to be updated/moved, or false to prevent it.
24
+ *
25
+ * Can return anything for the end event as it's ignored.
26
26
  *
27
27
  * Can be used to save some context/info to later apply safely during onDragApply.
28
28
  */
29
- onDragChange: DragChangeHandler
29
+ onDragChange: (...args: Parameters<DragChangeHandler>) => DragChangeResult
30
30
  /**
31
31
  * Called when drag will be applied. If dragEnd was called with apply false, it will not be called.
32
32
  * Return false to not apply the regular drag end changes (i.e. return false to reset to the position before dragging).
@@ -50,7 +50,8 @@ export function useFrames(
50
50
 
51
51
  const touchingFramesArrays = computed(() => touchingFrames.value.map(entry => Object.values(entry)))
52
52
 
53
- const isDragging = ref(false)
53
+ const isDragging = ref<false | "frame" | "edge">(false)
54
+ const showDragging = ref(false)
54
55
  const dragPoint = ref<Point | undefined>()
55
56
 
56
57
  const dragDirections = ref<Record<Orientation, Direction | undefined>>({} as any)
@@ -58,6 +59,8 @@ export function useFrames(
58
59
  const draggingIntersection = ref<IntersectionEntry | undefined>(undefined)
59
60
  const isDraggingFromWindowEdge = ref<boolean>(false)
60
61
 
62
+ const frameDragFrameId = ref<FrameId | undefined>()
63
+
61
64
  const frames = computed(() =>
62
65
  isDragging.value && showDragging.value
63
66
  ? { ...win.value.frames, ...allTouchingFrames.value }
@@ -67,7 +70,7 @@ export function useFrames(
67
70
  const dragHoveredFrame = computed(() => {
68
71
  if (isDragging.value) {
69
72
  for (const id of keys(frames.value)) {
70
- if (isPointInFrame(frames.value[id], dragPoint.value!)) {
73
+ if (isPointInRect(frames.value[id], dragPoint.value!)) {
71
74
  return frames.value[id]
72
75
  }
73
76
  }
@@ -83,12 +86,14 @@ export function useFrames(
83
86
  visualEdges.value = getVisualEdges(f, { includeWindowEdges: true })
84
87
  }, 50, {}) as any
85
88
 
86
- watchEffect(() => {
89
+ watch([isDragging, frames], () => {
87
90
  // let request animation force recalc
88
91
  if (isDragging.value) return
89
92
  // otherwise use more performant debounced version
90
93
  debounceGetDraggableEdges(Object.values(frames.value))
91
- })
94
+ }, { deep: true })
95
+ debounceGetDraggableEdges(Object.values(frames.value))
96
+
92
97
  function forceRecalculateEdges(): void {
93
98
  visualEdges.value = getVisualEdges(Object.values(frames.value), { includeWindowEdges: true })
94
99
  }
@@ -101,6 +106,8 @@ export function useFrames(
101
106
  dragDirections: dragDirections.value,
102
107
  dragPoint: dragPoint.value,
103
108
  isDragging: isDragging.value,
109
+ showDragging: showDragging.value,
110
+ draggingFrameId: frameDragFrameId.value,
104
111
  draggingEdges: draggingEdges.value,
105
112
  draggingIntersection: draggingIntersection.value,
106
113
  visualEdges: visualEdges.value,
@@ -121,11 +128,18 @@ export function useFrames(
121
128
  isDragging.value = false
122
129
  dragPoint.value = undefined
123
130
  touchingFrames.value = []
131
+ frameDragFrameId.value = undefined
124
132
  dragDirStore.reset()
133
+ showDragging.value = false
125
134
  forceRecalculateEdges()
126
135
  }
127
136
 
128
- function dragStart(e: PointerEvent, { edge, intersection }: { edge?: Edge, intersection?: IntersectionEntry }): void {
137
+ function dragStart<T extends "edge" | "frame">(
138
+ e: PointerEvent,
139
+ type: T,
140
+ // someday this will work
141
+ data: T extends "edge" ? EdgeDragStartData : FrameDragStartData
142
+ ): void {
129
143
  controller = new AbortController()
130
144
  controller.signal.addEventListener("abort", () => resetState())
131
145
 
@@ -135,42 +149,50 @@ export function useFrames(
135
149
 
136
150
  const point = toWindowCoord(win.value, e)
137
151
  dragPoint.value = point
152
+ dragDirStore.update(point)
138
153
 
139
- isDragging.value = true
154
+ isDragging.value = type
155
+ showDragging.value = true
140
156
 
141
- draggingIntersection.value = intersection
157
+ if (type === "frame") {
158
+ frameDragFrameId.value = (data as FrameDragStartData).frameId
159
+ } else {
160
+ const { edge, intersection } = data as EdgeDragStartData
161
+ draggingIntersection.value = intersection
142
162
 
143
- draggingEdges.value = edge
144
- ? [edge]
145
- : [
146
- ...(draggingIntersection.value?.sharedEdges.horizontal ?? []),
147
- ...(draggingIntersection.value?.sharedEdges.vertical ?? [])
148
- ]
163
+ draggingEdges.value = edge
164
+ ? [edge]
165
+ : [
166
+ ...(draggingIntersection.value?.sharedEdges.horizontal ?? []),
167
+ ...(draggingIntersection.value?.sharedEdges.vertical ?? [])
168
+ ]
149
169
 
150
- dragDirStore.update(point)
151
- isDraggingFromWindowEdge.value = draggingEdges.value.some(_ => isWindowEdge(_))
152
170
 
153
- const framesArray = Object.values(win.value.frames)
171
+ isDraggingFromWindowEdge.value = draggingEdges.value.some(_ => isWindowEdge(_))
154
172
 
155
- touchingFrames.value = []
156
- // all frames in touchingFrames must be clones
157
- // BUT they must be the same clone even if they are referenced by multiple dragging edges
158
- const clones = new Map<string, LayoutFrame>()
159
- for (let i = 0; i < draggingEdges.value.length; i++) {
160
- const draggingEdge = draggingEdges.value[i]
161
- touchingFrames.value[i] = {}
162
- for (const { frame } of findFramesTouchingEdge(draggingEdge, framesArray)) {
163
- if (!clones.has(frame.id)) {
164
- const clone = cloneFrame(frame)
165
- touchingFrames.value[i][frame.id] = clone
166
- clones.set(frame.id, clone)
167
- } else {
168
- touchingFrames.value[i][frame.id] = clones.get(frame.id)!
173
+ const framesArray = Object.values(win.value.frames)
174
+
175
+ touchingFrames.value = []
176
+ // all frames in touchingFrames must be clones
177
+ // BUT they must be the same clone even if they are referenced by multiple dragging edges
178
+ const clones = new Map<string, LayoutFrame>()
179
+ for (let i = 0; i < draggingEdges.value.length; i++) {
180
+ const draggingEdge = draggingEdges.value[i]
181
+ touchingFrames.value[i] = {}
182
+ for (const { frame } of findFramesTouchingEdge(draggingEdge, framesArray)) {
183
+ if (!clones.has(frame.id)) {
184
+ const clone = cloneFrame(frame)
185
+ touchingFrames.value[i][frame.id] = clone
186
+ clones.set(frame.id, clone)
187
+ } else {
188
+ touchingFrames.value[i][frame.id] = clones.get(frame.id)!
189
+ }
169
190
  }
170
191
  }
171
192
  }
172
193
 
173
- handler.onDragChange("start", e, state.value, forceRecalculateEdges, cancel)
194
+ const res = handler.onDragChange("start", e, state.value, forceRecalculateEdges, cancel)
195
+ showDragging.value = res.showDragging ?? true
174
196
  }
175
197
 
176
198
  function dragMove(e: PointerEvent): void {
@@ -179,18 +201,23 @@ export function useFrames(
179
201
  const didChange = dragDirStore.update(point)
180
202
  dragPoint.value = point
181
203
  if (!didChange) return
182
- const allowed = handler.onDragChange("move", e, state.value, forceRecalculateEdges, cancel)
204
+ const res = handler.onDragChange("move", e, state.value, forceRecalculateEdges, cancel)
183
205
 
184
- if (!allowed) return
185
- requestAnimationFrame(() => {
186
- for (let i = 0; i < draggingEdges.value.length; i++) {
187
- const draggingEdge = draggingEdges.value[i]
188
- if (draggingEdge) {
189
- moveEdge(touchingFramesArrays.value[i], draggingEdge, point)
206
+
207
+ showDragging.value = res.showDragging ?? true
208
+ if (!res.updateEdges) return
209
+
210
+ if (isDragging.value === "edge") {
211
+ requestAnimationFrame(() => {
212
+ for (let i = 0; i < draggingEdges.value.length; i++) {
213
+ const draggingEdge = draggingEdges.value[i]
214
+ if (draggingEdge) {
215
+ moveEdge(touchingFramesArrays.value[i], draggingEdge, point)
216
+ }
190
217
  }
191
- }
192
- forceRecalculateEdges()
193
- })
218
+ forceRecalculateEdges()
219
+ })
220
+ }
194
221
  }
195
222
 
196
223
  function dragEnd(e?: PointerEvent, { apply = true }: { apply?: boolean } = {}): void {
@@ -233,6 +260,7 @@ export function useFrames(
233
260
  dragStart,
234
261
  dragEnd,
235
262
  cancel,
263
+ dragMove,
236
264
  dragDirections,
237
265
  dragPoint,
238
266
  isDragging,
@@ -246,6 +274,8 @@ export function useFrames(
246
274
  intersections,
247
275
  isDraggingFromWindowEdge,
248
276
  forceRecalculateEdges,
249
- state
277
+ state,
278
+ showDragging,
279
+ frameDragFrameId
250
280
  }
251
281
  }