@witchcraft/layout 0.3.1 → 0.4.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.
Files changed (169) hide show
  1. package/README.md +62 -70
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +6 -1
  4. package/dist/runtime/components/FrameDragHandle.vue +1 -1
  5. package/dist/runtime/components/LayoutDecosEdges.d.vue.ts +15 -0
  6. package/dist/runtime/components/LayoutDecosEdges.vue +34 -0
  7. package/dist/runtime/components/LayoutDecosEdges.vue.d.ts +15 -0
  8. package/dist/runtime/components/LayoutDecosRects.d.vue.ts +15 -0
  9. package/dist/runtime/components/LayoutDecosRects.vue +37 -0
  10. package/dist/runtime/components/LayoutDecosRects.vue.d.ts +15 -0
  11. package/dist/runtime/components/LayoutDragEdgeGrabbed.d.vue.ts +2 -0
  12. package/dist/runtime/components/LayoutDragEdgeGrabbed.vue +43 -0
  13. package/dist/runtime/components/LayoutDragEdgeGrabbed.vue.d.ts +2 -0
  14. package/dist/runtime/components/LayoutDragEdgeHandle.d.vue.ts +9 -0
  15. package/dist/runtime/components/LayoutDragEdgeHandle.vue +44 -0
  16. package/dist/runtime/components/LayoutDragEdgeHandle.vue.d.ts +9 -0
  17. package/dist/runtime/components/{LayoutDecos.d.vue.ts → LayoutDragEdgeVisible.d.vue.ts} +3 -2
  18. package/dist/runtime/components/LayoutDragEdgeVisible.vue +38 -0
  19. package/dist/runtime/components/{LayoutDecos.vue.d.ts → LayoutDragEdgeVisible.vue.d.ts} +3 -2
  20. package/dist/runtime/components/LayoutDragEdges.d.vue.ts +21 -0
  21. package/dist/runtime/components/LayoutDragEdges.vue +67 -0
  22. package/dist/runtime/components/LayoutDragEdges.vue.d.ts +21 -0
  23. package/dist/runtime/components/LayoutEdgesActiveFrame.d.vue.ts +2 -0
  24. package/dist/runtime/components/LayoutEdgesActiveFrame.vue +35 -0
  25. package/dist/runtime/components/LayoutEdgesActiveFrame.vue.d.ts +2 -0
  26. package/dist/runtime/components/LayoutFrame.d.vue.ts +7 -6
  27. package/dist/runtime/components/LayoutFrame.vue +18 -14
  28. package/dist/runtime/components/LayoutFrame.vue.d.ts +7 -6
  29. package/dist/runtime/components/LayoutIntersectionHandle.d.vue.ts +10 -0
  30. package/dist/runtime/components/LayoutIntersectionHandle.vue +36 -0
  31. package/dist/runtime/components/LayoutIntersectionHandle.vue.d.ts +10 -0
  32. package/dist/runtime/components/LayoutIntersectionVisible.d.vue.ts +6 -0
  33. package/dist/runtime/components/LayoutIntersectionVisible.vue +34 -0
  34. package/dist/runtime/components/LayoutIntersectionVisible.vue.d.ts +6 -0
  35. package/dist/runtime/components/LayoutIntersections.d.vue.ts +21 -0
  36. package/dist/runtime/components/LayoutIntersections.vue +52 -0
  37. package/dist/runtime/components/LayoutIntersections.vue.d.ts +21 -0
  38. package/dist/runtime/components/{LayoutShapeSquare.d.vue.ts → LayoutShapeRect.d.vue.ts} +2 -2
  39. package/dist/runtime/components/{LayoutShapeSquare.vue.d.ts → LayoutShapeRect.vue.d.ts} +2 -2
  40. package/dist/runtime/components/LayoutWindow.d.vue.ts +252 -14
  41. package/dist/runtime/components/LayoutWindow.vue +27 -48
  42. package/dist/runtime/components/LayoutWindow.vue.d.ts +252 -14
  43. package/dist/runtime/composables/useFrames.d.ts +859 -2
  44. package/dist/runtime/composables/useFrames.js +2 -0
  45. package/dist/runtime/demo/App.vue +122 -74
  46. package/dist/runtime/demo/DemoControls.vue +7 -5
  47. package/dist/runtime/demo/index.html +12 -0
  48. package/dist/runtime/demo/tailwind.css +1 -1
  49. package/dist/runtime/drag/CloseAction.d.ts +1 -2
  50. package/dist/runtime/drag/CloseAction.js +1 -1
  51. package/dist/runtime/drag/DragActionHandler.d.ts +5 -3
  52. package/dist/runtime/drag/DragActionHandler.js +40 -6
  53. package/dist/runtime/drag/FrameDragAction.js +6 -7
  54. package/dist/runtime/drag/SplitAction.d.ts +1 -2
  55. package/dist/runtime/drag/SplitAction.js +1 -1
  56. package/dist/runtime/drag/createDefaultHandlers.d.ts +1 -1
  57. package/dist/runtime/drag/defaultDragActions.d.ts +1 -1
  58. package/dist/runtime/helpers/consoleDebugWindow.d.ts +43 -0
  59. package/dist/runtime/helpers/consoleDebugWindow.js +222 -0
  60. package/dist/runtime/helpers/convertLayoutWindowToWorkspace.js +2 -2
  61. package/dist/runtime/helpers/framesRedistributeFix.d.ts +87 -0
  62. package/dist/runtime/helpers/framesRedistributeFix.js +54 -0
  63. package/dist/runtime/helpers/getMoveEdgeInfo.d.ts +6 -2
  64. package/dist/runtime/helpers/getMoveEdgeInfo.js +11 -2
  65. package/dist/runtime/helpers/getPinnedEdgesForCollapsedFrames.d.ts +5 -0
  66. package/dist/runtime/helpers/getPinnedEdgesForCollapsedFrames.js +18 -0
  67. package/dist/runtime/helpers/getShapeRectCss.d.ts +7 -0
  68. package/dist/runtime/helpers/{getShapeSquareCss.js → getShapeRectCss.js} +1 -1
  69. package/dist/runtime/helpers/index.d.ts +3 -3
  70. package/dist/runtime/helpers/index.js +3 -3
  71. package/dist/runtime/helpers/moveEdge.js +3 -1
  72. package/dist/runtime/helpers/pxSizeToScaledSize.d.ts +2 -0
  73. package/dist/runtime/helpers/{numberToScaledSize.js → pxSizeToScaledSize.js} +1 -1
  74. package/dist/runtime/helpers/updateWindowSizeWithEvent.d.ts +3 -0
  75. package/dist/runtime/helpers/validateLayoutShape.d.ts +5 -0
  76. package/dist/runtime/helpers/validateLayoutShape.js +28 -0
  77. package/dist/runtime/layout/applyFrameChanges.js +6 -0
  78. package/dist/runtime/layout/createSplitDecoShapes.js +1 -1
  79. package/dist/runtime/layout/getFrameCollapseInfo.d.ts +5 -2
  80. package/dist/runtime/layout/getFrameCollapseInfo.js +16 -46
  81. package/dist/runtime/layout/getFrameDockInfo.d.ts +1 -1
  82. package/dist/runtime/layout/getFrameDockInfo.js +5 -1
  83. package/dist/runtime/layout/getFrameUncollapseInfo.d.ts +5 -2
  84. package/dist/runtime/layout/getFrameUncollapseInfo.js +16 -50
  85. package/dist/runtime/layout/getFrameUndockInfo.js +4 -1
  86. package/dist/runtime/layout/getFramesRedistributeInfo.d.ts +20 -4
  87. package/dist/runtime/layout/getFramesRedistributeInfo.js +31 -2
  88. package/dist/runtime/layout/getRelaxFramesInfo.d.ts +7 -0
  89. package/dist/runtime/layout/getRelaxFramesInfo.js +91 -0
  90. package/dist/runtime/layout/getUpdateWindowSizeInfo.d.ts +13 -0
  91. package/dist/runtime/layout/getUpdateWindowSizeInfo.js +65 -0
  92. package/dist/runtime/layout/index.d.ts +1 -0
  93. package/dist/runtime/layout/index.js +1 -0
  94. package/dist/runtime/layout/resizeFrame.js +1 -3
  95. package/dist/runtime/settings.d.ts +26 -19
  96. package/dist/runtime/settings.js +57 -22
  97. package/dist/runtime/types/index.d.ts +1243 -30
  98. package/dist/runtime/types/index.js +22 -8
  99. package/dist/runtime/types/vue.d.ts +41 -0
  100. package/dist/runtime/{drag/types.js → types/vue.js} +1 -0
  101. package/dist/runtime/utils/cssToKey.d.ts +5 -0
  102. package/dist/runtime/utils/cssToKey.js +3 -0
  103. package/package.json +11 -12
  104. package/src/module.ts +6 -1
  105. package/src/runtime/components/FrameDragHandle.vue +1 -1
  106. package/src/runtime/components/LayoutDecosEdges.vue +38 -0
  107. package/src/runtime/components/LayoutDecosRects.vue +42 -0
  108. package/src/runtime/components/LayoutDragEdgeGrabbed.vue +50 -0
  109. package/src/runtime/components/LayoutDragEdgeHandle.vue +52 -0
  110. package/src/runtime/components/LayoutDragEdgeVisible.vue +45 -0
  111. package/src/runtime/components/LayoutDragEdges.vue +78 -0
  112. package/src/runtime/components/LayoutEdgesActiveFrame.vue +43 -0
  113. package/src/runtime/components/LayoutFrame.vue +22 -15
  114. package/src/runtime/components/LayoutIntersectionHandle.vue +45 -0
  115. package/src/runtime/components/LayoutIntersectionVisible.vue +41 -0
  116. package/src/runtime/components/LayoutIntersections.vue +64 -0
  117. package/src/runtime/components/{LayoutShapeSquare.vue → LayoutShapeRect.vue} +2 -2
  118. package/src/runtime/components/LayoutWindow.vue +29 -49
  119. package/src/runtime/composables/useFrames.ts +7 -2
  120. package/src/runtime/demo/App.vue +128 -78
  121. package/src/runtime/demo/DemoControls.vue +7 -5
  122. package/src/runtime/demo/index.html +12 -0
  123. package/src/runtime/demo/tailwind.css +3 -0
  124. package/src/runtime/drag/CloseAction.ts +2 -3
  125. package/src/runtime/drag/DragActionHandler.ts +48 -10
  126. package/src/runtime/drag/FrameDragAction.ts +4 -5
  127. package/src/runtime/drag/SplitAction.ts +4 -3
  128. package/src/runtime/drag/createDefaultHandlers.ts +2 -1
  129. package/src/runtime/drag/defaultDragActions.ts +2 -1
  130. package/src/runtime/helpers/consoleDebugWindow.ts +331 -0
  131. package/src/runtime/helpers/convertLayoutWindowToWorkspace.ts +2 -2
  132. package/src/runtime/helpers/framesRedistributeFix.ts +162 -0
  133. package/src/runtime/helpers/getMoveEdgeInfo.ts +22 -5
  134. package/src/runtime/helpers/getPinnedEdgesForCollapsedFrames.ts +29 -0
  135. package/src/runtime/helpers/{getShapeSquareCss.ts → getShapeRectCss.ts} +3 -3
  136. package/src/runtime/helpers/index.ts +4 -3
  137. package/src/runtime/helpers/moveEdge.ts +3 -1
  138. package/src/runtime/helpers/{numberToScaledSize.ts → pxSizeToScaledSize.ts} +2 -2
  139. package/src/runtime/helpers/updateWindowSizeWithEvent.ts +3 -0
  140. package/src/runtime/helpers/validateLayoutShape.ts +46 -0
  141. package/src/runtime/layout/applyFrameChanges.ts +5 -0
  142. package/src/runtime/layout/createSplitDecoShapes.ts +1 -1
  143. package/src/runtime/layout/getFrameCollapseInfo.ts +26 -85
  144. package/src/runtime/layout/getFrameDockInfo.ts +8 -1
  145. package/src/runtime/layout/getFrameUncollapseInfo.ts +29 -96
  146. package/src/runtime/layout/getFrameUndockInfo.ts +7 -1
  147. package/src/runtime/layout/getFramesRedistributeInfo.ts +60 -5
  148. package/src/runtime/layout/getRelaxFramesInfo.ts +139 -0
  149. package/src/runtime/layout/getUpdateWindowSizeInfo.ts +105 -0
  150. package/src/runtime/layout/index.ts +1 -0
  151. package/src/runtime/layout/resizeFrame.ts +4 -3
  152. package/src/runtime/settings.ts +78 -25
  153. package/src/runtime/types/index.ts +205 -33
  154. package/src/runtime/types/vue.ts +41 -0
  155. package/src/runtime/utils/cssToKey.ts +8 -0
  156. package/dist/runtime/components/LayoutDecos.vue +0 -26
  157. package/dist/runtime/components/LayoutEdges.d.vue.ts +0 -17
  158. package/dist/runtime/components/LayoutEdges.vue +0 -144
  159. package/dist/runtime/components/LayoutEdges.vue.d.ts +0 -17
  160. package/dist/runtime/drag/types.d.ts +0 -161
  161. package/dist/runtime/helpers/clampNumber.d.ts +0 -1
  162. package/dist/runtime/helpers/clampNumber.js +0 -3
  163. package/dist/runtime/helpers/getShapeSquareCss.d.ts +0 -7
  164. package/dist/runtime/helpers/numberToScaledSize.d.ts +0 -2
  165. package/src/runtime/components/LayoutDecos.vue +0 -28
  166. package/src/runtime/components/LayoutEdges.vue +0 -170
  167. package/src/runtime/drag/types.ts +0 -177
  168. package/src/runtime/helpers/clampNumber.ts +0 -9
  169. /package/dist/runtime/components/{LayoutShapeSquare.vue → LayoutShapeRect.vue} +0 -0
@@ -0,0 +1,45 @@
1
+ <template>
2
+ <LayoutShapeRect
3
+ :css="css"
4
+ :class="twMerge(`
5
+ drag-intersection
6
+ z-30
7
+ rounded-full
8
+ hover:cursor-pointer
9
+ `, ($attrs as any).class)"
10
+ v-bind="{ ...$attrs, class: undefined }"
11
+ @pointerdown="onPointerDown?.($event, 'edge', { intersection })"
12
+ />
13
+ </template>
14
+
15
+ <script lang="ts" setup>
16
+ import { layoutContextInjectionKey } from "../types/vue.js"
17
+ import { useAttrs, inject } from "vue"
18
+ import { twMerge } from "@witchcraft/ui/utils/twMerge"
19
+
20
+ import LayoutShapeRect from "./LayoutShapeRect.vue"
21
+
22
+ import { getIntersectionsCss } from "../helpers/getIntersectionsCss.js"
23
+ import { type EdgeDragStartData } from "../types/index.js"
24
+ import { dragContextInjectionKey } from "../types/vue.js"
25
+ import type { IntersectionEntry } from "../types/index.js"
26
+
27
+ const $attrs = useAttrs()
28
+
29
+ defineOptions({
30
+ inheritAttrs: false
31
+ })
32
+
33
+ const props = defineProps<{
34
+ css: ReturnType<typeof getIntersectionsCss>[number]
35
+ intersection: IntersectionEntry
36
+ onPointerDown?: (e: PointerEvent, type: "edge", data: EdgeDragStartData) => void
37
+ }>()
38
+
39
+ const ctx = inject(layoutContextInjectionKey, undefined)
40
+ if (!ctx) throw new Error("LayoutEdges must be used within a LayoutWindow")
41
+
42
+ const dragCtx = inject(dragContextInjectionKey, undefined)
43
+ if (!dragCtx) throw new Error("LayoutEdges must be used within a LayoutWindow")
44
+
45
+ </script>
@@ -0,0 +1,41 @@
1
+ <template>
2
+ <LayoutShapeRect
3
+ :css="css"
4
+ :class="twMerge(`
5
+ drag-intersection-visible
6
+ z-30
7
+ rounded-full
8
+ pointer-events-none
9
+ [.drag-intersection:hover+&]:bg-blue-500/50
10
+ `,
11
+ css._shifted && `w-[7px] h-[7px]`,
12
+ ($attrs as any).class
13
+ )"
14
+ v-bind="{ ...$attrs, class: undefined }"
15
+ />
16
+ </template>
17
+
18
+ <script lang="ts" setup>
19
+ import { twMerge } from "@witchcraft/ui/utils/twMerge"
20
+ import { inject, useAttrs } from "vue"
21
+
22
+ import LayoutShapeRect from "./LayoutShapeRect.vue"
23
+ import { layoutContextInjectionKey } from "../types/vue.js"
24
+
25
+ import { getIntersectionsCss } from "../helpers/getIntersectionsCss.js"
26
+
27
+ const $attrs = useAttrs()
28
+
29
+ defineOptions({
30
+ inheritAttrs: false
31
+ })
32
+
33
+ defineProps<{
34
+ css: ReturnType<typeof getIntersectionsCss>[number]
35
+ }>()
36
+
37
+
38
+ const ctx = inject(layoutContextInjectionKey, undefined)
39
+ if (!ctx) throw new Error("LayoutEdges must be used within a LayoutWindow")
40
+
41
+ </script>
@@ -0,0 +1,64 @@
1
+ <template>
2
+ <template
3
+ v-for="css, i of cssDragIntersections"
4
+ :key="cssToKey(css.handle)"
5
+ >
6
+ <slot name="handler" v-bind="{
7
+ css: css.handle,
8
+ onPointerDown: dragCtx.dragStart,
9
+ intersection: draggableIntersections[i]
10
+ }">
11
+ <LayoutIntersectionHandle
12
+ :css="css.handle"
13
+ :intersection="draggableIntersections[i]"
14
+ />
15
+ </slot>
16
+ <slot name="indicator" v-bind="{ css: css.visible, intersection: draggableIntersections[i] }">
17
+ <LayoutIntersectionVisible
18
+ :css="css.visible"
19
+ />
20
+ </slot>
21
+ </template>
22
+ </template>
23
+
24
+ <script lang="ts" setup>
25
+ import { dragContextInjectionKey, layoutContextInjectionKey } from "../types/vue.js"
26
+ import { computed, inject } from "vue"
27
+
28
+
29
+ import { getIntersectionsCss } from "../helpers/getIntersectionsCss.js"
30
+
31
+ import { cssToKey } from "../utils/cssToKey.js"
32
+ import LayoutIntersectionHandle from "./LayoutIntersectionHandle.vue"
33
+ import LayoutIntersectionVisible from "./LayoutIntersectionVisible.vue"
34
+
35
+ defineOptions({
36
+ inheritAttrs: false
37
+ })
38
+
39
+ const ctx = inject(layoutContextInjectionKey, undefined)
40
+ if (!ctx) throw new Error("LayoutEdges must be used within a LayoutWindow")
41
+
42
+ const dragCtx = inject(dragContextInjectionKey, undefined)
43
+ if (!dragCtx) throw new Error("LayoutEdges must be used within a LayoutWindow")
44
+
45
+ const draggableIntersections = computed(() => dragCtx.intersections.value
46
+ .filter(_ => !_.isWindowEdge && (_.count === 4 || (_.count === 1 && _.sharesEdge)))
47
+ )
48
+
49
+ const cssDragIntersections = computed(() => {
50
+ const intersections: {
51
+ handle: ReturnType<typeof getIntersectionsCss>[number]
52
+ visible: ReturnType<typeof getIntersectionsCss>[number]
53
+ }[] = []
54
+ const thick = getIntersectionsCss(draggableIntersections.value, {
55
+ intersectionWidth: `var(--layoutIntersectionWidth, 15px)`
56
+ })
57
+ const thin = getIntersectionsCss(draggableIntersections.value, {
58
+ })
59
+ for (let i = 0; i < thick.length; i++) {
60
+ intersections.push({ handle: thick[i], visible: thin[i] })
61
+ }
62
+ return intersections
63
+ })
64
+ </script>
@@ -28,7 +28,7 @@ import { twMerge } from "@witchcraft/ui/utils/twMerge"
28
28
  import { useAttrs } from "vue"
29
29
  import type { HTMLAttributes } from "vue"
30
30
 
31
- import type { LayoutShapeSquareProps } from "../types/index.js"
31
+ import type { LayoutShapeRectProps } from "../types/index.js"
32
32
 
33
33
  const $attrs = useAttrs()
34
34
 
@@ -37,7 +37,7 @@ defineOptions({
37
37
  })
38
38
 
39
39
  const props = defineProps<
40
- & LayoutShapeSquareProps
40
+ & LayoutShapeRectProps
41
41
  & /** @vue-ignore */ HTMLAttributes
42
42
  >()
43
43
  </script>
@@ -14,37 +14,12 @@
14
14
  v-bind="{ ...$attrs, class: undefined }"
15
15
  >
16
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">
17
+ <div
18
+ class="layout-window relative overflow-hidden flex-1"
19
+ ref="windowEl"
20
+ >
23
21
  <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"/>
22
+ <slot/>
48
23
  </template>
49
24
  </div>
50
25
  <Teleport
@@ -97,18 +72,18 @@ import { useGlobalResizeObserver } from "@witchcraft/ui/composables/useGlobalRes
97
72
  import { twMerge } from "@witchcraft/ui/utils/twMerge"
98
73
  import { computed, provide, reactive, ref, useAttrs, watch } from "vue"
99
74
 
100
- import LayoutDecosComponent from "./LayoutDecos.vue"
101
- import LayoutEdgesComponent from "./LayoutEdges.vue"
102
- import LayoutFrameComponent from "./LayoutFrame.vue"
103
75
 
104
76
  import { useFrames } from "../composables/useFrames.js"
105
77
  import { createDefaultHandlers } from "../drag/createDefaultHandlers.js"
106
78
  import { DragActionHandler } from "../drag/DragActionHandler"
107
- import { dragContextInjectionKey, type DragState, type IDragAction } from "../drag/types.js"
108
- import { updateWindowWithEvent } from "../helpers/updateWindowSizeWithEvent.js"
79
+ import { dragContextInjectionKey } from "../types/vue.js"
80
+ import { type DragState, type IDragAction } from "../types/index.js"
81
+ import { getUpdateWindowSizeInfo } from "../layout/getUpdateWindowSizeInfo.js"
109
82
  import { windowSetActiveFrame } from "../layout/windowSetActiveFrame.js"
110
- import type { LayoutEdgesProps, LayoutFrameProps, LayoutWindow } from "../types/index.js"
83
+ import { type LayoutWindow } from "../types/index.js"
84
+ import { layoutContextInjectionKey } from "../types/vue.js"
111
85
  import { settings } from "../settings.js"
86
+ import { applyFrameChanges } from "../layout/applyFrameChanges.js"
112
87
 
113
88
 
114
89
  const $attrs = useAttrs()
@@ -128,8 +103,6 @@ const props = withDefaults(defineProps<{
128
103
  * When this is turned back on again, it will trigger an update. You can also trigger updates manually with the exposed updateWindowSize function.
129
104
  */
130
105
  allowWindowSizeUpdate?: boolean
131
- frameProps?: Partial<Omit<LayoutFrameProps, "frame" | "isActiveFrame" | "onFocus">>
132
- edgesProps?: Partial<Omit<LayoutEdgesProps, "edges" | "intersections" | "win">>,
133
106
  }>(), {
134
107
  actionHandlers: undefined,
135
108
  textHints: () => [],
@@ -177,14 +150,9 @@ const dragContext = useFrames(
177
150
  )
178
151
 
179
152
  const {
180
- dragStart,
181
153
  dragPoint,
182
- visualEdges,
183
154
  isDragging,
184
- draggingEdges,
185
- draggingIntersection,
186
155
  frames,
187
- intersections,
188
156
  state,
189
157
  frameDragFrameId,
190
158
  showDragging,
@@ -206,15 +174,18 @@ function getWindowSize() {
206
174
  pxHeight: Math.floor(windowElRect.height)
207
175
  }
208
176
  }
209
- function updateWindowSize() {
177
+ function onWindowResize() {
210
178
  if (!props.allowWindowSizeUpdate) return
211
179
  const newSize = { ...getWindowSize(), ...getWindowOffset() }
212
- updateWindowWithEvent(win.value, newSize)
180
+ win.value.pxX = newSize.pxX
181
+ win.value.pxY = newSize.pxY
182
+ applyFrameChanges(win.value, getUpdateWindowSizeInfo(win.value, newSize))
213
183
  }
214
- useGlobalResizeObserver(windowEl, updateWindowSize)
184
+
185
+ useGlobalResizeObserver(windowEl, onWindowResize)
215
186
  watch(() => props.allowWindowSizeUpdate, (newval, oldval) => {
216
187
  if (oldval === false && newval === true) {
217
- updateWindowSize()
188
+ onWindowResize()
218
189
  }
219
190
  })
220
191
 
@@ -222,11 +193,20 @@ watch(() => props.allowWindowSizeUpdate, (newval, oldval) => {
222
193
  watch(state, () => emit("dragState", state.value))
223
194
  watch(showDragging, () => emit("isShowingDrag", showDragging.value))
224
195
 
196
+ const layoutContext = computed(() => {
197
+ return {
198
+ win: win.value!,
199
+ shapes,
200
+ onFocus: (frameId:string) => windowSetActiveFrame(win.value!, frameId),
201
+ }
202
+ })
203
+ provide(layoutContextInjectionKey, layoutContext)
204
+
225
205
  defineExpose({
226
206
  state,
227
207
  win,
228
- updateWindowSize,
229
- dragActionHandler
208
+ getUpdateWindowSizeInfo,
209
+ dragActionHandler: dragActionHandler as DragActionHandler<any, any>,
230
210
  })
231
211
  </script>
232
212
 
@@ -4,7 +4,6 @@ import type { Ref } from "vue"
4
4
  import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue"
5
5
 
6
6
  import { DragDirectionStore } from "../drag/DragDirectionStore.js"
7
- import type { DragChangeHandler, DragChangeResult, DragState, EdgeDragStartData, FrameDragStartData } from "../drag/types.js"
8
7
  import { cloneFrame } from "../helpers/cloneFrame.js"
9
8
  import { getIntersections } from "../helpers/getIntersections.js"
10
9
  import { getVisualEdges } from "../helpers/getVisualEdges.js"
@@ -13,7 +12,7 @@ import { moveEdge } from "../helpers/moveEdge.js"
13
12
  import { toWindowCoord } from "../helpers/toWindowCoord.js"
14
13
  import { findFramesTouchingEdge } from "../layout/findFramesTouchingEdge.js"
15
14
  import { isPointInRect } from "../layout/isPointInRect.js"
16
- import type { Direction, Edge, FrameId, IntersectionEntry, LayoutFrame, LayoutWindow, Orientation, Point } from "../types/index.js"
15
+ import type { Direction, DragChangeHandler, DragChangeResult, DragState, Edge, EdgeDragStartData, FrameDragStartData, FrameId, IntersectionEntry, LayoutFrame, LayoutWindow, Orientation, Point } from "../types/index.js"
17
16
 
18
17
  export function useFrames(
19
18
  win: Ref<LayoutWindow>,
@@ -32,6 +31,10 @@ export function useFrames(
32
31
  * Return false to not apply the regular drag end changes (i.e. return false to reset to the position before dragging).
33
32
  */
34
33
  onDragApply: ((state: DragState, forceRecalculateEdges: () => void) => boolean)
34
+ /**
35
+ * Called after visual edges are recalculated. Action handlers can annotate edges with error info.
36
+ */
37
+ annotateEdges?: (edges: Edge[], frames: LayoutFrame[]) => void
35
38
  }
36
39
  ) {
37
40
  const draggingEdges = ref<Edge[]>([])
@@ -84,6 +87,7 @@ export function useFrames(
84
87
  // all at once (e.g. by clicking on some command many times in a row)
85
88
  const debounceGetDraggableEdges = debounce((f: LayoutFrame[]) => {
86
89
  visualEdges.value = getVisualEdges(f, { includeWindowEdges: true })
90
+ handler.annotateEdges?.(visualEdges.value, f)
87
91
  }, 50, {}) as any
88
92
 
89
93
  watch([isDragging, frames], () => {
@@ -96,6 +100,7 @@ export function useFrames(
96
100
 
97
101
  function forceRecalculateEdges(): void {
98
102
  visualEdges.value = getVisualEdges(Object.values(frames.value), { includeWindowEdges: true })
103
+ handler.annotateEdges?.(visualEdges.value, Object.values(frames.value))
99
104
  }
100
105
 
101
106
  const dragDirStore = new DragDirectionStore({
@@ -12,31 +12,18 @@
12
12
  :win="win"
13
13
  :dragActionHandler="dragActionHandler"
14
14
  />
15
+
15
16
  <LayoutWindow
16
17
  ref="layoutComponent"
17
18
  v-if="win"
18
19
  :class="twMerge(`
19
20
  flex-1
20
21
  w-full
22
+ self-stretch
21
23
  border-1
22
24
  border-neutral-300
23
25
  dark:border-neutral-700
24
26
  rounded-md
25
- self-stretch
26
- [&_.frame]:flex
27
- [&_.frame]:flex-col
28
- [&_.frame]:outline-hidden
29
- [&_.active-frame-edge]:rounded-md
30
- [&_.active-frame-edge]:border-none
31
- [&_.drag-edge:hover+.edge]:bg-accent-500/20
32
- [&_.grabbed-edge]:bg-accent-500
33
- [&.request-split_.grabbed-edge]:hidden
34
- [&.request-split_.drag-edge]:hidden
35
- [&.deco-split-error]:rounded-md
36
- [&_.deco-split-new-frame]:rounded-md
37
- [&_.deco-close-frame]:rounded-md
38
- [&_.deco-close-frame-force]:rounded-md
39
- [&_.deco-frame-drag]:rounded-md
40
27
  `,
41
28
  collapsedDocks.top && `border-t-2 border-t-green-500`,
42
29
  collapsedDocks.bottom && `border-b-2 border-b-green-500`,
@@ -49,59 +36,109 @@
49
36
  @is-showing-drag="isShowingDrag = $event"
50
37
  @drag-state="dragState = $event"
51
38
  >
52
- <template #[`frame-${f.id}`] v-for="f in frames" :key="f.id" >
53
- <div
54
- v-if="!(f.collapsed && (f.width === 0 || f.height === 0))"
55
- :data-is-active="win.activeFrame === f.id"
56
- :class="twMerge(`
57
- border-2
58
- border-neutral-500
59
- h-full
60
- rounded-md
61
- overflow-auto
62
- `,
63
- win.activeFrame === f.id && `border-blue-500`
64
- )"
65
- @click="win.activeFrame=f.id"
66
- >
67
- <!--
68
- Avoid placing the padding on the first div in the frame.
69
- Set it on the first child instead, so that the frame can shrink as small as possible.
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.
71
- -->
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
39
+ <LayoutFrame class="
40
+ flex
41
+ flex-col
42
+ outline-hidden
43
+ ">
44
+ <template #[`frame-${f.id}`] v-for="f in frames" :key="f.id" >
45
+ <div
46
+ v-if="!(f.collapsed && (f.width === 0 || f.height === 0))"
47
+ :data-is-active="win.activeFrame === f.id"
48
+ :class="twMerge(`
49
+ border-2
50
+ border-neutral-500
51
+ h-full
52
+ rounded-md
53
+ overflow-auto
54
+ `,
55
+ win.activeFrame === f.id && `border-blue-500`
56
+ )"
57
+ @click="win.activeFrame=f.id"
58
+ >
59
+ <!--
60
+ Avoid placing the padding on the first div in the frame.
61
+ Set it on the first child instead, so that the frame can shrink as small as possible.
62
+ 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.
63
+ -->
64
+ <div class="p-2 flex flex-col">
65
+ <FrameDragHandle :frame-id="f.id">
66
+ <div
67
+ class="cursor-grab bg-neutral-200 dark:bg-neutral-700 px-2 py-1 text-xs select-none"
68
+ >
69
+ ⠿ Drag to move frame
70
+ </div>
71
+ </FrameDragHandle>
72
+ <div class="flex gap-1 px-2 py-1 flex-wrap">
73
+ <button
74
+ v-if="f.docked && !f.collapsed"
75
+ class="bg-neutral-200 dark:bg-neutral-700 px-2 py-0.5 text-xs rounded"
76
+ @click="handleUndock(f.id)"
77
+ >Undock</button>
78
+ <button
79
+ v-if="f.docked && !f.collapsed"
80
+ class="bg-neutral-200 dark:bg-neutral-700 px-2 py-0.5 text-xs rounded"
81
+ @click="handleCollapse(f.id)"
82
+ >Collapse</button>
83
+ <button
84
+ v-if="f.docked && f.collapsed"
85
+ class="bg-neutral-200 dark:bg-neutral-700 px-2 py-0.5 text-xs rounded"
86
+ @click="handleUncollapse(f.id)"
87
+ >Uncollapse</button>
88
+ </div>
89
+ <div class="p-2 whitespace-pre-wrap">
90
+ {{ debugFrame(f) }}
91
+ </div>
78
92
  </div>
79
- </FrameDragHandle>
80
- <div class="flex gap-1 px-2 py-1 flex-wrap">
81
- <button
82
- v-if="f.docked && !f.collapsed"
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"
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
93
  </div>
97
- <div class="p-2 whitespace-pre-wrap">
98
- {{ debugFrame(f) }}
94
+ <div v-else>
99
95
  </div>
100
- </div>
101
- </div>
102
- <div v-else>
103
- </div>
104
- </template>
96
+ </template>
97
+ </LayoutFrame>
98
+ <LayoutEdgesActiveFrame class="rounded-md border-none"/>
99
+ <!-- Following slot usage is just done as an example for how to override the slots and further customize the components. You could place classes on the parent LayoutWindow instead or if you don't need to customize anything you can just do:
100
+ <LayoutIntersections/>
101
+ ...
102
+ <LayoutDragEdges/>
103
+ -->
104
+ <LayoutDragEdges>
105
+ <template #handle="slotProps">
106
+ <LayoutDragEdgeHandle
107
+ class="..."
108
+ v-bind="slotProps"
109
+ />
110
+ </template>
111
+ <template #indicator="slotProps">
112
+ <LayoutDragEdgeVisible
113
+ class="..."
114
+ v-bind="slotProps"
115
+ />
116
+ </template>
117
+ </LayoutDragEdges>
118
+ <LayoutDecosEdges/>
119
+ <!-- Decos add only the most basic color indicators (which you can override, we also pretty them up here with from rounded edges. -->
120
+ <LayoutDecosRects class="
121
+ [&.deco-split-error]:rounded-md
122
+ [&.deco-split-new-frame]:rounded-md
123
+ [&.deco-close-frame]:rounded-md
124
+ [&.deco-close-frame-force]:rounded-md
125
+ [&.deco-frame-drag]:rounded-md
126
+ "/>
127
+ <LayoutDragEdgeGrabbed class="bg-accent-500"/>
128
+ <LayoutIntersections>
129
+ <template #handler="slotProps">
130
+ <LayoutIntersectionHandle
131
+ class="..."
132
+ v-bind="slotProps"
133
+ />
134
+ </template>
135
+ <template #indicator="slotProps">
136
+ <LayoutIntersectionVisible
137
+ class="..."
138
+ v-bind="slotProps"
139
+ />
140
+ </template>
141
+ </LayoutIntersections>
105
142
  </LayoutWindow>
106
143
  </WRoot>
107
144
  </template>
@@ -118,8 +155,15 @@ import DemoControls from "./DemoControls.vue"
118
155
  import { app } from "./sharedLayoutInstance.js"
119
156
 
120
157
  import FrameDragHandle from "../components/FrameDragHandle.vue"
121
- import LayoutWindow from "../components/LayoutWindow.vue"
122
- import type { DragState } from "../drag/types.js"
158
+ import LayoutFrame from "../components/LayoutFrame.vue"
159
+ import LayoutDecosEdges from "../components/LayoutDecosEdges.vue"
160
+ import LayoutDecosRects from "../components/LayoutDecosRects.vue"
161
+ import LayoutDragEdgeGrabbed from "../components/LayoutDragEdgeGrabbed.vue"
162
+ import LayoutIntersections from "../components/LayoutIntersections.vue"
163
+ import LayoutEdgesActiveFrame from "../components/LayoutEdgesActiveFrame.vue"
164
+ import LayoutDragEdges from "../components/LayoutDragEdges.vue"
165
+ import LayoutIntersectionHandle from "../components/LayoutIntersectionHandle.vue"
166
+ import type { DragState } from "../types/index.js"
123
167
  import { applyFrameChanges } from "../layout/applyFrameChanges.js"
124
168
  import { debugFrame } from "../layout/debugFrame.js"
125
169
  import { getFrameCollapseInfo } from "../layout/getFrameCollapseInfo.js"
@@ -136,11 +180,16 @@ import type { EdgeSide, Layout, Pos, Size } from "../types/index.js"
136
180
  import { throwIfError } from "@alanscodelog/utils/throwIfError"
137
181
  import { useTemplateRef } from "vue"
138
182
  import { DragActionHandler } from "../drag/DragActionHandler.js"
183
+ import LayoutDragEdgeHandle from "../components/LayoutDragEdgeHandle.vue"
184
+ import LayoutDragEdgeVisible from "../components/LayoutDragEdgeVisible.vue"
185
+ import LayoutIntersectionVisible from "../components/LayoutIntersectionVisible.vue"
186
+
139
187
 
188
+ import LayoutWindow from "../components/LayoutWindow.vue"
140
189
 
141
190
  const winId = ref<string | undefined>(undefined)
142
191
  const win = computed(() => winId.value !== undefined ? app.layout.windows[winId.value] : undefined)
143
- const layoutComponent = useTemplateRef("layoutComponent")
192
+ const layoutComponent = ref<InstanceType<typeof LayoutWindow> | undefined>(undefined)
144
193
 
145
194
  const frames = computed(() => {
146
195
  if (!win.value) return
@@ -205,16 +254,6 @@ function handleUncollapse(frameId: string) {
205
254
  DragActionHandler.debugState("uncollapse", "after", dragState.value!, {}, undefined)
206
255
  }
207
256
 
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
257
  const collapsedDocks = computed(() => {
219
258
  const sides: Partial<Record<EdgeSide, true>> = {}
220
259
  for (const frame of Object.values(win.value?.frames ?? {})) {
@@ -226,4 +265,15 @@ const collapsedDocks = computed(() => {
226
265
  })
227
266
 
228
267
  const dragActionHandler = computed(() => layoutComponent.value?.dragActionHandler)
268
+
269
+ const textHints = computed(() => {
270
+ const isDragging = dragState.value?.isDragging
271
+ const textHints = dragActionHandler.value?.textHints ?? {actions:[], errors:[]}
272
+ return [
273
+ ...(!isDragging ? [{classes:"", text:"Drag from an edge to create a new frame."}] : []),
274
+ ...textHints.errors.map((_: string) => ({classes:"text-red-500", text:_})),
275
+ ...textHints.actions.map((_: string) => ({ text:_})),
276
+ ]
277
+ })
278
+
229
279
  </script>
@@ -9,7 +9,7 @@
9
9
  </template>
10
10
  <template #popover>
11
11
  <div class="flex flex-col gap-2 rounded-md w-full`">
12
- <label class="text-xs">Collapse Size (%):</label>
12
+ <label class="text-xs">Collapse Size (px):</label>
13
13
  <input
14
14
  type="number"
15
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"
@@ -40,7 +40,9 @@
40
40
  import WDarkModeSwitcher from "@witchcraft/ui/components/WDarkModeSwitcher"
41
41
  import WButton from "@witchcraft/ui/components/WButton"
42
42
  import WPopover from "@witchcraft/ui/components/WPopover"
43
- import { copy } from "@witchcraft/ui/helpers"
43
+ import WCheckbox from "@witchcraft/ui/components/WCheckbox"
44
+ import WSimpleInput from "@witchcraft/ui/components/WSimpleInput"
45
+ import { copyToClipboard } from "@alanscodelog/utils/copyToClipboard"
44
46
  import { ref, watch } from "vue"
45
47
  import { applyFrameChanges } from "../layout/applyFrameChanges.js"
46
48
  import { getFrameUncollapseInfo } from "../layout/getFrameUncollapseInfo.js"
@@ -59,10 +61,10 @@ const props = defineProps<{
59
61
 
60
62
  const showDevActions = ref(false)
61
63
 
62
- const collapseSizeValue = ref(Math.round(settings.collapseSizeScaled.width / settings.maxInt * 100))
64
+ const collapseSizeValue = ref(settings.collapseSizePx.width)
63
65
 
64
66
  function handleCollapseSizeChange(value: number) {
65
- settings.collapseSize = value
67
+ settings.collapseSizePx = value
66
68
  collapseSizeValue.value = value
67
69
  }
68
70
 
@@ -105,7 +107,7 @@ function copyState() {
105
107
  }
106
108
  }
107
109
  const state = JSON.stringify(clone, null, 2)
108
- copy(state)
110
+ copyToClipboard(state)
109
111
  }
110
112
 
111
113
  const debugKey = ref(import.meta.dev ? "state.win.frames" : "false")
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>@witchcraft/layout Vue Demo</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="./main.ts"></script>
11
+ </body>
12
+ </html>
@@ -2,3 +2,6 @@
2
2
 
3
3
  @import "../../../playground/.nuxt/witchcraft-ui.css";
4
4
  @import "../../../playground/.nuxt/witchcraft-layout.css";
5
+
6
+ /* Only needed for the dev playground: the demo App.vue lives outside the Vite project root, so Tailwind's auto-scan doesn't find the [&.deco-...] classes it defines. */
7
+ @source "../../../src/runtime/demo";