@witchcraft/layout 0.3.1 → 0.4.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 (167) 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/getFramesRedistributeInfo.d.ts +20 -4
  86. package/dist/runtime/layout/getFramesRedistributeInfo.js +31 -2
  87. package/dist/runtime/layout/getRelaxFramesInfo.d.ts +7 -0
  88. package/dist/runtime/layout/getRelaxFramesInfo.js +91 -0
  89. package/dist/runtime/layout/getUpdateWindowSizeInfo.d.ts +13 -0
  90. package/dist/runtime/layout/getUpdateWindowSizeInfo.js +65 -0
  91. package/dist/runtime/layout/index.d.ts +1 -0
  92. package/dist/runtime/layout/index.js +1 -0
  93. package/dist/runtime/layout/resizeFrame.js +1 -3
  94. package/dist/runtime/settings.d.ts +26 -19
  95. package/dist/runtime/settings.js +57 -22
  96. package/dist/runtime/types/index.d.ts +1243 -30
  97. package/dist/runtime/types/index.js +22 -8
  98. package/dist/runtime/types/vue.d.ts +41 -0
  99. package/dist/runtime/{drag/types.js → types/vue.js} +1 -0
  100. package/dist/runtime/utils/cssToKey.d.ts +5 -0
  101. package/dist/runtime/utils/cssToKey.js +3 -0
  102. package/package.json +11 -12
  103. package/src/module.ts +6 -1
  104. package/src/runtime/components/FrameDragHandle.vue +1 -1
  105. package/src/runtime/components/LayoutDecosEdges.vue +38 -0
  106. package/src/runtime/components/LayoutDecosRects.vue +42 -0
  107. package/src/runtime/components/LayoutDragEdgeGrabbed.vue +50 -0
  108. package/src/runtime/components/LayoutDragEdgeHandle.vue +52 -0
  109. package/src/runtime/components/LayoutDragEdgeVisible.vue +45 -0
  110. package/src/runtime/components/LayoutDragEdges.vue +78 -0
  111. package/src/runtime/components/LayoutEdgesActiveFrame.vue +43 -0
  112. package/src/runtime/components/LayoutFrame.vue +22 -15
  113. package/src/runtime/components/LayoutIntersectionHandle.vue +45 -0
  114. package/src/runtime/components/LayoutIntersectionVisible.vue +41 -0
  115. package/src/runtime/components/LayoutIntersections.vue +64 -0
  116. package/src/runtime/components/{LayoutShapeSquare.vue → LayoutShapeRect.vue} +2 -2
  117. package/src/runtime/components/LayoutWindow.vue +29 -49
  118. package/src/runtime/composables/useFrames.ts +7 -2
  119. package/src/runtime/demo/App.vue +128 -78
  120. package/src/runtime/demo/DemoControls.vue +7 -5
  121. package/src/runtime/demo/index.html +12 -0
  122. package/src/runtime/demo/tailwind.css +3 -0
  123. package/src/runtime/drag/CloseAction.ts +2 -3
  124. package/src/runtime/drag/DragActionHandler.ts +48 -10
  125. package/src/runtime/drag/FrameDragAction.ts +4 -5
  126. package/src/runtime/drag/SplitAction.ts +4 -3
  127. package/src/runtime/drag/createDefaultHandlers.ts +2 -1
  128. package/src/runtime/drag/defaultDragActions.ts +2 -1
  129. package/src/runtime/helpers/consoleDebugWindow.ts +331 -0
  130. package/src/runtime/helpers/convertLayoutWindowToWorkspace.ts +2 -2
  131. package/src/runtime/helpers/framesRedistributeFix.ts +162 -0
  132. package/src/runtime/helpers/getMoveEdgeInfo.ts +22 -5
  133. package/src/runtime/helpers/getPinnedEdgesForCollapsedFrames.ts +29 -0
  134. package/src/runtime/helpers/{getShapeSquareCss.ts → getShapeRectCss.ts} +3 -3
  135. package/src/runtime/helpers/index.ts +4 -3
  136. package/src/runtime/helpers/moveEdge.ts +3 -1
  137. package/src/runtime/helpers/{numberToScaledSize.ts → pxSizeToScaledSize.ts} +2 -2
  138. package/src/runtime/helpers/updateWindowSizeWithEvent.ts +3 -0
  139. package/src/runtime/helpers/validateLayoutShape.ts +46 -0
  140. package/src/runtime/layout/applyFrameChanges.ts +5 -0
  141. package/src/runtime/layout/createSplitDecoShapes.ts +1 -1
  142. package/src/runtime/layout/getFrameCollapseInfo.ts +26 -85
  143. package/src/runtime/layout/getFrameDockInfo.ts +8 -1
  144. package/src/runtime/layout/getFrameUncollapseInfo.ts +29 -96
  145. package/src/runtime/layout/getFramesRedistributeInfo.ts +60 -5
  146. package/src/runtime/layout/getRelaxFramesInfo.ts +139 -0
  147. package/src/runtime/layout/getUpdateWindowSizeInfo.ts +105 -0
  148. package/src/runtime/layout/index.ts +1 -0
  149. package/src/runtime/layout/resizeFrame.ts +4 -3
  150. package/src/runtime/settings.ts +78 -25
  151. package/src/runtime/types/index.ts +205 -33
  152. package/src/runtime/types/vue.ts +41 -0
  153. package/src/runtime/utils/cssToKey.ts +8 -0
  154. package/dist/runtime/components/LayoutDecos.vue +0 -26
  155. package/dist/runtime/components/LayoutEdges.d.vue.ts +0 -17
  156. package/dist/runtime/components/LayoutEdges.vue +0 -144
  157. package/dist/runtime/components/LayoutEdges.vue.d.ts +0 -17
  158. package/dist/runtime/drag/types.d.ts +0 -161
  159. package/dist/runtime/helpers/clampNumber.d.ts +0 -1
  160. package/dist/runtime/helpers/clampNumber.js +0 -3
  161. package/dist/runtime/helpers/getShapeSquareCss.d.ts +0 -7
  162. package/dist/runtime/helpers/numberToScaledSize.d.ts +0 -2
  163. package/src/runtime/components/LayoutDecos.vue +0 -28
  164. package/src/runtime/components/LayoutEdges.vue +0 -170
  165. package/src/runtime/drag/types.ts +0 -177
  166. package/src/runtime/helpers/clampNumber.ts +0 -9
  167. /package/dist/runtime/components/{LayoutShapeSquare.vue → LayoutShapeRect.vue} +0 -0
@@ -47,6 +47,7 @@ export function useFrames(win, handler) {
47
47
  const intersections = computed(() => getIntersections(visualEdges.value));
48
48
  const debounceGetDraggableEdges = debounce((f) => {
49
49
  visualEdges.value = getVisualEdges(f, { includeWindowEdges: true });
50
+ handler.annotateEdges?.(visualEdges.value, f);
50
51
  }, 50, {});
51
52
  watch([isDragging, frames], () => {
52
53
  if (isDragging.value) return;
@@ -55,6 +56,7 @@ export function useFrames(win, handler) {
55
56
  debounceGetDraggableEdges(Object.values(frames.value));
56
57
  function forceRecalculateEdges() {
57
58
  visualEdges.value = getVisualEdges(Object.values(frames.value), { includeWindowEdges: true });
59
+ handler.annotateEdges?.(visualEdges.value, Object.values(frames.value));
58
60
  }
59
61
  const dragDirStore = new DragDirectionStore({
60
62
  onUpdate: (dir) => dragDirections.value = dir
@@ -12,6 +12,7 @@
12
12
  :win="win"
13
13
  :dragActionHandler="dragActionHandler"
14
14
  />
15
+
15
16
  <LayoutWindow
16
17
  ref="layoutComponent"
17
18
  v-if="win"
@@ -19,25 +20,11 @@
19
20
  `
20
21
  flex-1
21
22
  w-full
23
+ self-stretch
22
24
  border-1
23
25
  border-neutral-300
24
26
  dark:border-neutral-700
25
27
  rounded-md
26
- self-stretch
27
- [&_.frame]:flex
28
- [&_.frame]:flex-col
29
- [&_.frame]:outline-hidden
30
- [&_.active-frame-edge]:rounded-md
31
- [&_.active-frame-edge]:border-none
32
- [&_.drag-edge:hover+.edge]:bg-accent-500/20
33
- [&_.grabbed-edge]:bg-accent-500
34
- [&.request-split_.grabbed-edge]:hidden
35
- [&.request-split_.drag-edge]:hidden
36
- [&.deco-split-error]:rounded-md
37
- [&_.deco-split-new-frame]:rounded-md
38
- [&_.deco-close-frame]:rounded-md
39
- [&_.deco-close-frame-force]:rounded-md
40
- [&_.deco-frame-drag]:rounded-md
41
28
  `,
42
29
  collapsedDocks.top && `border-t-2 border-t-green-500`,
43
30
  collapsedDocks.bottom && `border-b-2 border-b-green-500`,
@@ -50,60 +37,110 @@
50
37
  @is-showing-drag="isShowingDrag = $event"
51
38
  @drag-state="dragState = $event"
52
39
  >
53
- <template #[`frame-${f.id}`] v-for="f in frames" :key="f.id" >
54
- <div
55
- v-if="!(f.collapsed && (f.width === 0 || f.height === 0))"
56
- :data-is-active="win.activeFrame === f.id"
57
- :class="twMerge(
40
+ <LayoutFrame class="
41
+ flex
42
+ flex-col
43
+ outline-hidden
44
+ ">
45
+ <template #[`frame-${f.id}`] v-for="f in frames" :key="f.id" >
46
+ <div
47
+ v-if="!(f.collapsed && (f.width === 0 || f.height === 0))"
48
+ :data-is-active="win.activeFrame === f.id"
49
+ :class="twMerge(
58
50
  `
59
- border-2
60
- border-neutral-500
61
- h-full
62
- rounded-md
63
- overflow-auto
64
- `,
51
+ border-2
52
+ border-neutral-500
53
+ h-full
54
+ rounded-md
55
+ overflow-auto
56
+ `,
65
57
  win.activeFrame === f.id && `border-blue-500`
66
58
  )"
67
- @click="win.activeFrame = f.id"
68
- >
69
- <!--
70
- Avoid placing the padding on the first div in the frame.
71
- Set it on the first child instead, so that the frame can shrink as small as possible.
72
- 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.
73
- -->
74
- <div class="p-2 flex flex-col">
75
- <FrameDragHandle :frame-id="f.id">
76
- <div
77
- class="cursor-grab bg-neutral-200 dark:bg-neutral-700 px-2 py-1 text-xs select-none"
78
- >
79
- ⠿ Drag to move frame
59
+ @click="win.activeFrame = f.id"
60
+ >
61
+ <!--
62
+ Avoid placing the padding on the first div in the frame.
63
+ Set it on the first child instead, so that the frame can shrink as small as possible.
64
+ 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.
65
+ -->
66
+ <div class="p-2 flex flex-col">
67
+ <FrameDragHandle :frame-id="f.id">
68
+ <div
69
+ class="cursor-grab bg-neutral-200 dark:bg-neutral-700 px-2 py-1 text-xs select-none"
70
+ >
71
+ ⠿ Drag to move frame
72
+ </div>
73
+ </FrameDragHandle>
74
+ <div class="flex gap-1 px-2 py-1 flex-wrap">
75
+ <button
76
+ v-if="f.docked && !f.collapsed"
77
+ class="bg-neutral-200 dark:bg-neutral-700 px-2 py-0.5 text-xs rounded"
78
+ @click="handleUndock(f.id)"
79
+ >Undock</button>
80
+ <button
81
+ v-if="f.docked && !f.collapsed"
82
+ class="bg-neutral-200 dark:bg-neutral-700 px-2 py-0.5 text-xs rounded"
83
+ @click="handleCollapse(f.id)"
84
+ >Collapse</button>
85
+ <button
86
+ v-if="f.docked && f.collapsed"
87
+ class="bg-neutral-200 dark:bg-neutral-700 px-2 py-0.5 text-xs rounded"
88
+ @click="handleUncollapse(f.id)"
89
+ >Uncollapse</button>
90
+ </div>
91
+ <div class="p-2 whitespace-pre-wrap">
92
+ {{ debugFrame(f) }}
93
+ </div>
80
94
  </div>
81
- </FrameDragHandle>
82
- <div class="flex gap-1 px-2 py-1 flex-wrap">
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="handleUndock(f.id)"
87
- >Undock</button>
88
- <button
89
- v-if="f.docked && !f.collapsed"
90
- class="bg-neutral-200 dark:bg-neutral-700 px-2 py-0.5 text-xs rounded"
91
- @click="handleCollapse(f.id)"
92
- >Collapse</button>
93
- <button
94
- v-if="f.docked && typeof f.collapsed === 'number'"
95
- class="bg-neutral-200 dark:bg-neutral-700 px-2 py-0.5 text-xs rounded"
96
- @click="handleUncollapse(f.id)"
97
- >Uncollapse</button>
98
95
  </div>
99
- <div class="p-2 whitespace-pre-wrap">
100
- {{ debugFrame(f) }}
96
+ <div v-else>
101
97
  </div>
102
- </div>
103
- </div>
104
- <div v-else>
105
- </div>
106
- </template>
98
+ </template>
99
+ </LayoutFrame>
100
+ <LayoutEdgesActiveFrame class="rounded-md border-none"/>
101
+ <!-- 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:
102
+ <LayoutIntersections/>
103
+ ...
104
+ <LayoutDragEdges/>
105
+ -->
106
+ <LayoutDragEdges>
107
+ <template #handle="slotProps">
108
+ <LayoutDragEdgeHandle
109
+ class="..."
110
+ v-bind="slotProps"
111
+ />
112
+ </template>
113
+ <template #indicator="slotProps">
114
+ <LayoutDragEdgeVisible
115
+ class="..."
116
+ v-bind="slotProps"
117
+ />
118
+ </template>
119
+ </LayoutDragEdges>
120
+ <LayoutDecosEdges/>
121
+ <!-- Decos add only the most basic color indicators (which you can override, we also pretty them up here with from rounded edges. -->
122
+ <LayoutDecosRects class="
123
+ [&.deco-split-error]:rounded-md
124
+ [&.deco-split-new-frame]:rounded-md
125
+ [&.deco-close-frame]:rounded-md
126
+ [&.deco-close-frame-force]:rounded-md
127
+ [&.deco-frame-drag]:rounded-md
128
+ "/>
129
+ <LayoutDragEdgeGrabbed class="bg-accent-500"/>
130
+ <LayoutIntersections>
131
+ <template #handler="slotProps">
132
+ <LayoutIntersectionHandle
133
+ class="..."
134
+ v-bind="slotProps"
135
+ />
136
+ </template>
137
+ <template #indicator="slotProps">
138
+ <LayoutIntersectionVisible
139
+ class="..."
140
+ v-bind="slotProps"
141
+ />
142
+ </template>
143
+ </LayoutIntersections>
107
144
  </LayoutWindow>
108
145
  </WRoot>
109
146
  </template>
@@ -116,7 +153,14 @@ import { computed, onBeforeMount, ref } from "vue";
116
153
  import DemoControls from "./DemoControls.vue";
117
154
  import { app } from "./sharedLayoutInstance.js";
118
155
  import FrameDragHandle from "../components/FrameDragHandle.vue";
119
- import LayoutWindow from "../components/LayoutWindow.vue";
156
+ import LayoutFrame from "../components/LayoutFrame.vue";
157
+ import LayoutDecosEdges from "../components/LayoutDecosEdges.vue";
158
+ import LayoutDecosRects from "../components/LayoutDecosRects.vue";
159
+ import LayoutDragEdgeGrabbed from "../components/LayoutDragEdgeGrabbed.vue";
160
+ import LayoutIntersections from "../components/LayoutIntersections.vue";
161
+ import LayoutEdgesActiveFrame from "../components/LayoutEdgesActiveFrame.vue";
162
+ import LayoutDragEdges from "../components/LayoutDragEdges.vue";
163
+ import LayoutIntersectionHandle from "../components/LayoutIntersectionHandle.vue";
120
164
  import { applyFrameChanges } from "../layout/applyFrameChanges.js";
121
165
  import { debugFrame } from "../layout/debugFrame.js";
122
166
  import { getFrameCollapseInfo } from "../layout/getFrameCollapseInfo.js";
@@ -132,9 +176,13 @@ import { settings } from "../settings.js";
132
176
  import { throwIfError } from "@alanscodelog/utils/throwIfError";
133
177
  import { useTemplateRef } from "vue";
134
178
  import { DragActionHandler } from "../drag/DragActionHandler.js";
179
+ import LayoutDragEdgeHandle from "../components/LayoutDragEdgeHandle.vue";
180
+ import LayoutDragEdgeVisible from "../components/LayoutDragEdgeVisible.vue";
181
+ import LayoutIntersectionVisible from "../components/LayoutIntersectionVisible.vue";
182
+ import LayoutWindow from "../components/LayoutWindow.vue";
135
183
  const winId = ref(void 0);
136
184
  const win = computed(() => winId.value !== void 0 ? app.layout.windows[winId.value] : void 0);
137
- const layoutComponent = useTemplateRef("layoutComponent");
185
+ const layoutComponent = ref(void 0);
138
186
  const frames = computed(() => {
139
187
  if (!win.value) return;
140
188
  return Object.values(win.value.frames);
@@ -185,15 +233,6 @@ function handleUncollapse(frameId) {
185
233
  applyFrameChanges(win.value, changes);
186
234
  DragActionHandler.debugState("uncollapse", "after", dragState.value, {}, void 0);
187
235
  }
188
- const textHints = computed(() => {
189
- const isDragging = dragState.value?.isDragging;
190
- const textHints2 = dragActionHandler.value?.textHints ?? { actions: [], errors: [] };
191
- return [
192
- ...!isDragging ? [{ classes: "", text: "Drag from an edge to create a new frame." }] : [],
193
- ...textHints2.errors.map((_) => ({ classes: "text-red-500", text: _ })),
194
- ...textHints2.actions.map((_) => ({ text: _ }))
195
- ];
196
- });
197
236
  const collapsedDocks = computed(() => {
198
237
  const sides = {};
199
238
  for (const frame of Object.values(win.value?.frames ?? {})) {
@@ -204,4 +243,13 @@ const collapsedDocks = computed(() => {
204
243
  return sides;
205
244
  });
206
245
  const dragActionHandler = computed(() => layoutComponent.value?.dragActionHandler);
246
+ const textHints = computed(() => {
247
+ const isDragging = dragState.value?.isDragging;
248
+ const textHints2 = dragActionHandler.value?.textHints ?? { actions: [], errors: [] };
249
+ return [
250
+ ...!isDragging ? [{ classes: "", text: "Drag from an edge to create a new frame." }] : [],
251
+ ...textHints2.errors.map((_) => ({ classes: "text-red-500", text: _ })),
252
+ ...textHints2.actions.map((_) => ({ text: _ }))
253
+ ];
254
+ });
207
255
  </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"
@@ -42,7 +42,9 @@
42
42
  import WDarkModeSwitcher from "@witchcraft/ui/components/WDarkModeSwitcher";
43
43
  import WButton from "@witchcraft/ui/components/WButton";
44
44
  import WPopover from "@witchcraft/ui/components/WPopover";
45
- import { copy } from "@witchcraft/ui/helpers";
45
+ import WCheckbox from "@witchcraft/ui/components/WCheckbox";
46
+ import WSimpleInput from "@witchcraft/ui/components/WSimpleInput";
47
+ import { copyToClipboard } from "@alanscodelog/utils/copyToClipboard";
46
48
  import { ref, watch } from "vue";
47
49
  import { applyFrameChanges } from "../layout/applyFrameChanges.js";
48
50
  import { getFrameUncollapseInfo } from "../layout/getFrameUncollapseInfo.js";
@@ -56,9 +58,9 @@ const props = defineProps({
56
58
  dragActionHandler: { type: Object, required: false }
57
59
  });
58
60
  const showDevActions = ref(false);
59
- const collapseSizeValue = ref(Math.round(settings.collapseSizeScaled.width / settings.maxInt * 100));
61
+ const collapseSizeValue = ref(settings.collapseSizePx.width);
60
62
  function handleCollapseSizeChange(value) {
61
- settings.collapseSize = value;
63
+ settings.collapseSizePx = value;
62
64
  collapseSizeValue.value = value;
63
65
  }
64
66
  function uncollapseAll() {
@@ -95,7 +97,7 @@ function copyState() {
95
97
  }
96
98
  }
97
99
  const state = JSON.stringify(clone, null, 2);
98
- copy(state);
100
+ copyToClipboard(state);
99
101
  }
100
102
  const debugKey = ref(import.meta.dev ? "state.win.frames" : "false");
101
103
  watch(debugKey, () => {
@@ -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>
@@ -1 +1 @@
1
- @import "tailwindcss";@import "../../../playground/.nuxt/witchcraft-ui.css";@import "../../../playground/.nuxt/witchcraft-layout.css";
1
+ @import "tailwindcss";@import "../../../playground/.nuxt/witchcraft-ui.css";@import "../../../playground/.nuxt/witchcraft-layout.css";@source "../../../src/runtime/demo";
@@ -1,6 +1,5 @@
1
- import type { ActionDragChangeResult, DragState, IDragAction } from "./types.js";
2
1
  import { getCloseFrameInfo } from "../layout/getCloseFrameInfo.js";
3
- import type { CloseDeco } from "../types/index.js";
2
+ import type { ActionDragChangeResult, CloseDeco, DragState, IDragAction } from "../types/index.js";
4
3
  import type { KnownError } from "../utils/KnownError.js";
5
4
  export type CloseInfo = Exclude<ReturnType<typeof getCloseFrameInfo>, KnownError>;
6
5
  export declare class CloseAction implements IDragAction {
@@ -53,7 +53,7 @@ export class CloseAction {
53
53
  force,
54
54
  shapes: [
55
55
  {
56
- type: "square",
56
+ type: "rect",
57
57
  data: { x: _.x, y: _.y, width: _.width, height: _.height },
58
58
  attrs: {
59
59
  class: force ? `deco-close-force-frame bg-orange-500/50` : `deco-close-frame bg-orange-500/20`
@@ -1,6 +1,5 @@
1
1
  import { type RecordFromArray } from "@alanscodelog/utils";
2
- import type { DragChangeHandler, DragChangeResult, DragState, IDragAction } from "./types.js";
3
- import type { LayoutShape } from "../types/index.js";
2
+ import type { DragChangeHandler, DragChangeResult, DragState, Edge, IDragAction, LayoutFrame, LayoutShape } from "../types/index.js";
4
3
  /**
5
4
  * Handles the lifecycle of a drag actions {@link IDragAction} and provides additional hooks.
6
5
  *
@@ -41,14 +40,17 @@ export declare class DragActionHandler<TRawDragActions extends IDragAction[], TD
41
40
  * Default onDragChange handler for when no action can handle the request.
42
41
  *
43
42
  * Should return true to allow the edges to be moved, or false to prevent it.
43
+ *
44
+ * The default prevents movement when the edge is a window edge and when the edge is touching a collapsed frame.
44
45
  */
45
46
  defaultOnDragChange?: DragChangeHandler);
46
47
  eventHandler(e: KeyboardEvent | PointerEvent, state: DragState, forceRecalculateEdges: () => void): undefined;
47
48
  onDragChange<T extends "start" | "move" | "end">(type: T, e: T extends "end" ? PointerEvent | undefined : PointerEvent, state: DragState, forceRecalculateEdges: () => void, cancel: (e: PointerEvent | KeyboardEvent | undefined, state: DragState) => void): DragChangeResult;
48
49
  setTextHints(type: "start" | "move" | "end"): void;
50
+ annotateEdges(edges: Edge[], frames: LayoutFrame[]): void;
49
51
  onDragApply(state: DragState, forceRecalculateEdges: () => void): boolean;
50
52
  cancel(e: PointerEvent | KeyboardEvent | undefined, state: DragState): void;
51
53
  static debugState(pluginName: string, type: "before" | "after" | string, state: DragState, pluginState?: Record<string, any>,
52
- /** Object key to filter the state by, e.g. state.win.frames. If boolean is ignored. The idea is you pass this.debug and users can set this.debug to a string to filter. */
54
+ /** Object key to filter the state by, e.g. state.win.frames. If false is ignored. The idea is you pass this.debug and users can set this.debug to a string to filter. */
53
55
  key?: string | boolean): void;
54
56
  }
@@ -1,13 +1,25 @@
1
1
  import { get } from "@alanscodelog/utils";
2
+ import { isWindowEdge } from "../helpers/isWindowEdge.js";
3
+ import { findFramesTouchingEdge } from "../layout/findFramesTouchingEdge.js";
4
+ import { LAYOUT_ERROR } from "../types/index.js";
5
+ import { KnownError } from "../utils/KnownError.js";
2
6
  export class DragActionHandler {
3
7
  activeAction;
4
8
  actions;
5
9
  eventCanceller = void 0;
6
10
  boundCancel;
7
- defaultOnDragChange = (type, _e, state, _forceRecalculateEdges, _cancel) => ({
8
- updateEdges: type === "move" ? !state.isDraggingFromWindowEdge : true,
9
- shapes: []
10
- });
11
+ defaultOnDragChange = (type, _e, state, _forceRecalculateEdges, _cancel) => {
12
+ const isTouchingCollapsedFrameEdge = type === "move" && state.isDragging === "edge" && state.touchingFramesArrays.some(
13
+ (frames) => (
14
+ // we check with a falsy check on PURPOSE, frames collapsed to 0 aren't an issue, they are ignored anyways
15
+ Object.values(frames).some((f) => f.collapsed)
16
+ )
17
+ );
18
+ return {
19
+ updateEdges: type === "move" ? !(state.isDraggingFromWindowEdge || isTouchingCollapsedFrameEdge) : true,
20
+ shapes: []
21
+ };
22
+ };
11
23
  hooks;
12
24
  /** All action shapes merged into a single array. If using vue you can set this to a reactive array for reactivity. */
13
25
  shapes = [];
@@ -84,6 +96,28 @@ export class DragActionHandler {
84
96
  this.textHints.errors.push(...res?.errors ?? []);
85
97
  }
86
98
  }
99
+ annotateEdges(edges, frames) {
100
+ for (const edge of edges) {
101
+ if (edge.error) continue;
102
+ for (const action of Object.values(this.actions)) {
103
+ action.annotateEdge?.(edge, frames);
104
+ if (edge.error) break;
105
+ }
106
+ if (!edge.error && !isWindowEdge(edge)) {
107
+ const touching = findFramesTouchingEdge(edge, frames);
108
+ if (touching) {
109
+ const collapsedFrame = touching.find((f) => f.frame.collapsed);
110
+ if (collapsedFrame) {
111
+ edge.error = new KnownError(
112
+ LAYOUT_ERROR.CANT_RESIZE_COLLAPSED_FRAME,
113
+ "Cannot Move: Can't Resize Collapsed Frame",
114
+ { frame: collapsedFrame.frame }
115
+ );
116
+ }
117
+ }
118
+ }
119
+ }
120
+ }
87
121
  onDragApply(state, forceRecalculateEdges) {
88
122
  if (this.activeAction) {
89
123
  const res = this.actions[this.activeAction].onDragApply(state, forceRecalculateEdges);
@@ -102,11 +136,11 @@ export class DragActionHandler {
102
136
  this.hooks.onEnd?.({ cancelled: true, applied: false });
103
137
  }
104
138
  static debugState(pluginName, type, state, pluginState = {}, key) {
139
+ if (key === false) return;
105
140
  let res = { state, pluginState };
106
- let pickedRes = {};
107
141
  if (typeof key === "string" && key !== "") {
108
- pickedRes = {};
109
142
  const paths = key.split(",").map((_) => _.trim()).filter((_) => _);
143
+ const pickedRes = {};
110
144
  for (const path of paths) {
111
145
  pickedRes[path] = get(res, path.split("."));
112
146
  }
@@ -58,23 +58,22 @@ export class FrameDragAction {
58
58
  return this.textHints;
59
59
  }
60
60
  getDecos(matchedZone, state, result) {
61
- let decos = [];
62
61
  const isError = result instanceof Error;
63
62
  const frame = state.dragHoveredFrame;
64
63
  if (!matchedZone || !frame) {
65
- decos = [];
66
- this.modifyDecos(decos);
67
- return decos;
64
+ const decos2 = [];
65
+ this.modifyDecos(decos2);
66
+ return decos2;
68
67
  }
69
68
  const clipPath = createZoneSideClipPath(matchedZone, settings.zoneSizes);
70
69
  const classes = isError ? `deco-frame-drag deco-frame-drag-error deco-frame-drag-${matchedZone.type}-${matchedZone.side} bg-red-500/50` : `deco-frame-drag deco-frame-drag-${matchedZone.type}-${matchedZone.side} bg-blue-500/50`;
71
- decos = [{
70
+ const decos = [{
72
71
  id: frame.id,
73
72
  type: "drop",
74
73
  position: matchedZone.side ?? "center",
75
74
  shapes: [
76
75
  {
77
- type: "square",
76
+ type: "rect",
78
77
  data: matchedZone,
79
78
  attrs: clipPath ? { class: classes, style: `clip-path:${clipPath}` } : { class: classes }
80
79
  }
@@ -105,7 +104,7 @@ export class FrameDragAction {
105
104
  this.state.lastReturn = getFrameSwapInfo(win, draggingFrameId, dragHoveredFrame.id);
106
105
  if (!(this.state.lastReturn instanceof Error)) this.state.lastReturn.info = "swap";
107
106
  }
108
- } else if (matchedZone.type === "window") {
107
+ } else if (matchedZone.type === "window" && dragHoveredFrame && !dragHoveredFrame.docked) {
109
108
  this.state.lastReturn = getFrameDockInfo(win, draggingFrameId, matchedZone.side);
110
109
  if (!(this.state.lastReturn instanceof Error)) this.state.lastReturn.info = "dock";
111
110
  }
@@ -1,6 +1,5 @@
1
- import type { ActionDragChangeResult, DragState, IDragAction } from "./types.js";
2
1
  import { getFrameSplitInfo } from "../layout/getFrameSplitInfo.js";
3
- import type { Point, SplitDeco } from "../types/index.js";
2
+ import type { ActionDragChangeResult, DragState, IDragAction, Point, SplitDeco } from "../types/index.js";
4
3
  import type { KnownError } from "../utils/KnownError.js";
5
4
  export type SplitInfo = Exclude<ReturnType<typeof getFrameSplitInfo>, KnownError>;
6
5
  export declare class SplitAction implements IDragAction {
@@ -129,7 +129,7 @@ export class SplitAction {
129
129
  if (!ok) {
130
130
  if (dragHoveredFrame && dragHoveredFrame.docked) {
131
131
  const errorDeco = {
132
- type: "square",
132
+ type: "rect",
133
133
  data: {
134
134
  x: dragHoveredFrame.x,
135
135
  y: dragHoveredFrame.y,
@@ -1,4 +1,4 @@
1
- import type { IDragAction } from "./types.js";
1
+ import type { IDragAction } from "../types/index.js";
2
2
  /**
3
3
  * Creates the default drag actions (Split, Close, FrameDrag).
4
4
  */
@@ -1,4 +1,4 @@
1
- import type { IDragAction } from "./types.js";
1
+ import type { IDragAction } from "../types/index.js";
2
2
  /**
3
3
  * Creates the default drag actions (Split, Close, FrameDrag).
4
4
  */
@@ -0,0 +1,43 @@
1
+ import type { LayoutWindow, Point, Size } from "../types/index.js";
2
+ /**
3
+ * See also {@link createDebugLabeler} for customizing the labeling function, for
4
+ * example to debug the pixel sizes instead of position/size:
5
+ *
6
+ * ```ts
7
+ * consoleDebugWindow(clone, 50, createDebugLabeler({
8
+ * showPxSize: true,
9
+ * showSize: false,
10
+ * showPos: false,
11
+ * getWin: () => clone
12
+ * }))
13
+ *
14
+ * @experimental
15
+ */
16
+ export declare function consoleDebugWindow(win: LayoutWindow, targetWidth?: number, labelFunc?: (f: LayoutWindow["frames"][string]) => string): void;
17
+ type Border = {
18
+ horizontal: string;
19
+ vertical: string;
20
+ topLeft: string;
21
+ topRight: string;
22
+ bottomLeft: string;
23
+ bottomRight: string;
24
+ cross: string;
25
+ fill: string;
26
+ labelBg: string;
27
+ };
28
+ export declare function boxesToText<T extends Size & Point & {
29
+ id?: string;
30
+ }>(boxes: T[], targetWidth?: number, { border, heightScale, labelFunc }?: {
31
+ border?: Partial<Border>;
32
+ heightScale?: number;
33
+ labelFunc?: (box: T) => string;
34
+ }): string | Error;
35
+ export declare function createDebugLabeler({ showPxSize, showCollapsed, showDocked, showSize, showPos, getWin }?: {
36
+ showPxSize?: boolean;
37
+ showCollapsed?: boolean;
38
+ showDocked?: boolean;
39
+ showSize?: boolean;
40
+ showPos?: boolean;
41
+ getWin?: (f: LayoutWindow["frames"][string]) => Pick<LayoutWindow, "pxWidth" | "pxHeight">;
42
+ }): (f: LayoutWindow["frames"][string]) => string;
43
+ export {};