@witchcraft/layout 0.1.3 → 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 (189) 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 +8 -8
  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 +0 -1
  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.d.ts +3 -1
  17. package/dist/runtime/components/LayoutWindow.d.vue.ts +26 -12
  18. package/dist/runtime/components/LayoutWindow.vue +95 -84
  19. package/dist/runtime/components/LayoutWindow.vue.d.ts +26 -12
  20. package/dist/runtime/composables/useFrames.d.ts +15 -13
  21. package/dist/runtime/composables/useFrames.js +59 -39
  22. package/dist/runtime/demo/App.vue +116 -30
  23. package/dist/runtime/demo/DemoControls.d.vue.ts +4 -1
  24. package/dist/runtime/demo/DemoControls.vue +98 -4
  25. package/dist/runtime/demo/DemoControls.vue.d.ts +4 -1
  26. package/dist/runtime/drag/CloseAction.d.ts +26 -5
  27. package/dist/runtime/drag/CloseAction.js +87 -40
  28. package/dist/runtime/drag/DragActionHandler.d.ts +20 -8
  29. package/dist/runtime/drag/DragActionHandler.js +47 -12
  30. package/dist/runtime/drag/FrameDragAction.d.ts +45 -0
  31. package/dist/runtime/drag/FrameDragAction.js +143 -0
  32. package/dist/runtime/drag/SplitAction.d.ts +32 -11
  33. package/dist/runtime/drag/SplitAction.js +82 -24
  34. package/dist/runtime/drag/createDefaultHandlers.d.ts +9 -0
  35. package/dist/runtime/drag/createDefaultHandlers.js +10 -0
  36. package/dist/runtime/drag/defaultDragActions.d.ts +9 -0
  37. package/dist/runtime/drag/defaultDragActions.js +10 -0
  38. package/dist/runtime/drag/types.d.ts +82 -13
  39. package/dist/runtime/drag/types.js +1 -0
  40. package/dist/runtime/helpers/createZoneSideClipPath.d.ts +12 -0
  41. package/dist/runtime/helpers/createZoneSideClipPath.js +17 -0
  42. package/dist/runtime/helpers/doEdgesOverlap.d.ts +3 -1
  43. package/dist/runtime/helpers/doEdgesOverlap.js +5 -5
  44. package/dist/runtime/helpers/getDockBoundaries.d.ts +19 -0
  45. package/dist/runtime/helpers/getDockBoundaries.js +14 -0
  46. package/dist/runtime/helpers/getEdgeLength.d.ts +2 -0
  47. package/dist/runtime/helpers/getEdgeLength.js +5 -0
  48. package/dist/runtime/helpers/getIntersections.js +2 -2
  49. package/dist/runtime/helpers/getIntersectionsCss.js +2 -2
  50. package/dist/runtime/helpers/getMoveEdgeInfo.js +2 -2
  51. package/dist/runtime/helpers/getResizeLimit.js +2 -2
  52. package/dist/runtime/helpers/getShapeSquareCss.js +2 -2
  53. package/dist/runtime/helpers/getVisualEdgeCss.js +2 -2
  54. package/dist/runtime/helpers/getVisualEdges.d.ts +1 -1
  55. package/dist/runtime/helpers/getVisualEdges.js +4 -3
  56. package/dist/runtime/helpers/index.d.ts +4 -0
  57. package/dist/runtime/helpers/index.js +4 -0
  58. package/dist/runtime/helpers/isEdgeEqual.js +2 -4
  59. package/dist/runtime/helpers/isWindowEdge.js +2 -2
  60. package/dist/runtime/helpers/isWindowEdgePoint.js +2 -2
  61. package/dist/runtime/helpers/moveEdge.js +2 -2
  62. package/dist/runtime/helpers/numberToScaledPercent.d.ts +1 -1
  63. package/dist/runtime/helpers/numberToScaledPercent.js +2 -2
  64. package/dist/runtime/helpers/numberToScaledSize.js +2 -2
  65. package/dist/runtime/helpers/rotateFrames.d.ts +7 -0
  66. package/dist/runtime/helpers/rotateFrames.js +36 -0
  67. package/dist/runtime/helpers/scaledPointToPx.d.ts +13 -0
  68. package/dist/runtime/helpers/scaledPointToPx.js +7 -0
  69. package/dist/runtime/helpers/toWindowCoord.js +2 -2
  70. package/dist/runtime/layout/applyFrameChanges.d.ts +10 -0
  71. package/dist/runtime/layout/applyFrameChanges.js +29 -0
  72. package/dist/runtime/layout/createSplitDecoFromDrag.d.ts +6 -1
  73. package/dist/runtime/layout/createSplitDecoFromDrag.js +4 -4
  74. package/dist/runtime/layout/createSplitDecoShapes.d.ts +7 -0
  75. package/dist/runtime/layout/{createSplitDecoEdge.js → createSplitDecoShapes.js} +6 -3
  76. package/dist/runtime/layout/debugFrame.js +2 -1
  77. package/dist/runtime/layout/findSafeSplitEdge.js +2 -2
  78. package/dist/runtime/layout/frameCreate.js +2 -2
  79. package/dist/runtime/layout/getCloseFrameInfo.d.ts +7 -6
  80. package/dist/runtime/layout/getCloseFrameInfo.js +10 -3
  81. package/dist/runtime/layout/getDragZones.d.ts +8 -0
  82. package/dist/runtime/layout/getDragZones.js +32 -0
  83. package/dist/runtime/layout/getFillEmptySpaceInfo.d.ts +65 -0
  84. package/dist/runtime/layout/getFillEmptySpaceInfo.js +69 -0
  85. package/dist/runtime/layout/getFrameCollapseInfo.d.ts +13 -0
  86. package/dist/runtime/layout/getFrameCollapseInfo.js +93 -0
  87. package/dist/runtime/layout/getFrameDockInfo.d.ts +9 -0
  88. package/dist/runtime/layout/getFrameDockInfo.js +82 -0
  89. package/dist/runtime/layout/getFrameDragZones.d.ts +16 -0
  90. package/dist/runtime/layout/getFrameDragZones.js +74 -0
  91. package/dist/runtime/layout/getFrameRearrangeInfo.d.ts +139 -0
  92. package/dist/runtime/layout/getFrameRearrangeInfo.js +87 -0
  93. package/dist/runtime/layout/getFrameSplitInfo.d.ts +7 -5
  94. package/dist/runtime/layout/getFrameSplitInfo.js +10 -3
  95. package/dist/runtime/layout/getFrameSwapInfo.d.ts +9 -0
  96. package/dist/runtime/layout/getFrameSwapInfo.js +27 -0
  97. package/dist/runtime/layout/getFrameTo.js +2 -2
  98. package/dist/runtime/layout/getFrameUncollapseInfo.d.ts +12 -0
  99. package/dist/runtime/layout/getFrameUncollapseInfo.js +88 -0
  100. package/dist/runtime/layout/getFrameUndockInfo.d.ts +13 -0
  101. package/dist/runtime/layout/getFrameUndockInfo.js +51 -0
  102. package/dist/runtime/layout/getFramesRedistributeInfo.d.ts +29 -0
  103. package/dist/runtime/layout/getFramesRedistributeInfo.js +53 -0
  104. package/dist/runtime/layout/getWindowDragZones.d.ts +6 -0
  105. package/dist/runtime/layout/getWindowDragZones.js +49 -0
  106. package/dist/runtime/layout/index.d.ts +14 -5
  107. package/dist/runtime/layout/index.js +14 -5
  108. package/dist/runtime/layout/isPointInRect.d.ts +7 -0
  109. package/dist/runtime/layout/{isPointInFrame.js → isPointInRect.js} +1 -1
  110. package/dist/runtime/layout/resizeFrame.js +2 -2
  111. package/dist/runtime/settings.d.ts +41 -16
  112. package/dist/runtime/settings.js +95 -53
  113. package/dist/runtime/types/index.d.ts +324 -54
  114. package/dist/runtime/types/index.js +54 -20
  115. package/package.json +28 -29
  116. package/src/runtime/components/FrameDragHandle.vue +30 -0
  117. package/src/runtime/components/LayoutDecos.vue +12 -36
  118. package/src/runtime/components/LayoutEdges.vue +27 -23
  119. package/src/runtime/components/LayoutFrame.vue +6 -5
  120. package/src/runtime/components/LayoutShapeSquare.vue +9 -3
  121. package/src/runtime/components/LayoutWindow.vue +110 -101
  122. package/src/runtime/composables/useFrames.ts +80 -50
  123. package/src/runtime/demo/App.vue +126 -36
  124. package/src/runtime/demo/DemoControls.vue +115 -6
  125. package/src/runtime/drag/CloseAction.ts +106 -44
  126. package/src/runtime/drag/DragActionHandler.ts +71 -20
  127. package/src/runtime/drag/FrameDragAction.ts +202 -0
  128. package/src/runtime/drag/SplitAction.ts +106 -34
  129. package/src/runtime/drag/createDefaultHandlers.ts +19 -0
  130. package/src/runtime/drag/defaultDragActions.ts +19 -0
  131. package/src/runtime/drag/types.ts +90 -20
  132. package/src/runtime/helpers/createZoneSideClipPath.ts +41 -0
  133. package/src/runtime/helpers/doEdgesOverlap.ts +11 -5
  134. package/src/runtime/helpers/getDockBoundaries.ts +36 -0
  135. package/src/runtime/helpers/getEdgeLength.ts +10 -0
  136. package/src/runtime/helpers/getIntersections.ts +2 -2
  137. package/src/runtime/helpers/getIntersectionsCss.ts +2 -2
  138. package/src/runtime/helpers/getMoveEdgeInfo.ts +2 -2
  139. package/src/runtime/helpers/getResizeLimit.ts +2 -2
  140. package/src/runtime/helpers/getShapeSquareCss.ts +2 -2
  141. package/src/runtime/helpers/getVisualEdgeCss.ts +2 -2
  142. package/src/runtime/helpers/getVisualEdges.ts +5 -4
  143. package/src/runtime/helpers/index.ts +4 -0
  144. package/src/runtime/helpers/isEdgeEqual.ts +2 -4
  145. package/src/runtime/helpers/isWindowEdge.ts +2 -2
  146. package/src/runtime/helpers/isWindowEdgePoint.ts +2 -2
  147. package/src/runtime/helpers/moveEdge.ts +2 -2
  148. package/src/runtime/helpers/numberToScaledPercent.ts +3 -3
  149. package/src/runtime/helpers/numberToScaledSize.ts +2 -2
  150. package/src/runtime/helpers/rotateFrames.ts +45 -0
  151. package/src/runtime/helpers/scaledPointToPx.ts +13 -0
  152. package/src/runtime/helpers/toWindowCoord.ts +2 -2
  153. package/src/runtime/layout/applyFrameChanges.ts +39 -0
  154. package/src/runtime/layout/createSplitDecoFromDrag.ts +12 -6
  155. package/src/runtime/layout/{createSplitDecoEdge.ts → createSplitDecoShapes.ts} +17 -7
  156. package/src/runtime/layout/debugFrame.ts +1 -1
  157. package/src/runtime/layout/findSafeSplitEdge.ts +3 -3
  158. package/src/runtime/layout/frameCreate.ts +2 -2
  159. package/src/runtime/layout/getCloseFrameInfo.ts +21 -8
  160. package/src/runtime/layout/getDragZones.ts +48 -0
  161. package/src/runtime/layout/getFillEmptySpaceInfo.ts +177 -0
  162. package/src/runtime/layout/getFrameCollapseInfo.ts +164 -0
  163. package/src/runtime/layout/getFrameDockInfo.ts +126 -0
  164. package/src/runtime/layout/getFrameDragZones.ts +100 -0
  165. package/src/runtime/layout/getFrameRearrangeInfo.ts +261 -0
  166. package/src/runtime/layout/getFrameSplitInfo.ts +21 -8
  167. package/src/runtime/layout/getFrameSwapInfo.ts +45 -0
  168. package/src/runtime/layout/getFrameTo.ts +2 -2
  169. package/src/runtime/layout/getFrameUncollapseInfo.ts +160 -0
  170. package/src/runtime/layout/getFrameUndockInfo.ts +97 -0
  171. package/src/runtime/layout/getFramesRedistributeInfo.ts +98 -0
  172. package/src/runtime/layout/getWindowDragZones.ts +59 -0
  173. package/src/runtime/layout/index.ts +14 -5
  174. package/src/runtime/layout/isPointInRect.ts +7 -0
  175. package/src/runtime/layout/resizeFrame.ts +2 -2
  176. package/src/runtime/settings.ts +69 -49
  177. package/src/runtime/types/index.ts +143 -28
  178. package/dist/runtime/layout/closeFrame.d.ts +0 -5
  179. package/dist/runtime/layout/closeFrame.js +0 -13
  180. package/dist/runtime/layout/closeFrames.d.ts +0 -2
  181. package/dist/runtime/layout/closeFrames.js +0 -8
  182. package/dist/runtime/layout/createSplitDecoEdge.d.ts +0 -2
  183. package/dist/runtime/layout/frameSplit.d.ts +0 -16
  184. package/dist/runtime/layout/frameSplit.js +0 -9
  185. package/dist/runtime/layout/isPointInFrame.d.ts +0 -2
  186. package/src/runtime/layout/closeFrame.ts +0 -33
  187. package/src/runtime/layout/closeFrames.ts +0 -14
  188. package/src/runtime/layout/frameSplit.ts +0 -31
  189. package/src/runtime/layout/isPointInFrame.ts +0 -7
@@ -1,12 +1,20 @@
1
+ import { get } from "@alanscodelog/utils";
1
2
  export class DragActionHandler {
2
3
  activeAction;
3
4
  actions;
4
5
  eventCanceller = void 0;
5
6
  boundCancel;
6
- defaultOnDragChange;
7
+ defaultOnDragChange = (type, _e, state, _forceRecalculateEdges, _cancel) => ({
8
+ updateEdges: type === "move" ? !state.isDraggingFromWindowEdge : true,
9
+ shapes: []
10
+ });
7
11
  hooks;
8
- constructor(defaultOnDragChange, actions, hooks = {}) {
9
- this.defaultOnDragChange = defaultOnDragChange;
12
+ /** All action shapes merged into a single array. If using vue you can set this to a reactive array for reactivity. */
13
+ shapes = [];
14
+ /** All hint/error text from all actions, updated on every onDragChange. If using vue you can set this to a reactive object for reactivity. */
15
+ textHints = { actions: [], errors: [] };
16
+ constructor(actions, hooks = {}, defaultOnDragChange) {
17
+ if (defaultOnDragChange) this.defaultOnDragChange = defaultOnDragChange;
10
18
  this.hooks = hooks;
11
19
  this.actions = {};
12
20
  for (const action of actions) {
@@ -21,7 +29,7 @@ export class DragActionHandler {
21
29
  let cancelled = false;
22
30
  this.hooks.onEvent?.(e, () => {
23
31
  cancelled = true;
24
- this.cancel();
32
+ this.cancel(e, state);
25
33
  });
26
34
  if (cancelled) return;
27
35
  let found = false;
@@ -38,7 +46,7 @@ export class DragActionHandler {
38
46
  }
39
47
  if (oldActiveAction !== this.activeAction) {
40
48
  if (oldActiveAction) {
41
- this.actions[oldActiveAction].cancel();
49
+ this.actions[oldActiveAction].cancel(e, state);
42
50
  }
43
51
  this.hooks.onRequestChange?.(this.activeAction);
44
52
  }
@@ -51,17 +59,30 @@ export class DragActionHandler {
51
59
  onDragChange(type, e, state, forceRecalculateEdges, cancel) {
52
60
  if (type === "start") {
53
61
  this.eventCanceller = cancel;
54
- state.isDragging = true;
55
62
  this.eventHandler(e, state, forceRecalculateEdges);
56
63
  }
57
64
  if (type === "end") {
58
- state.isDragging = false;
59
65
  this.activeAction = void 0;
60
66
  }
61
67
  if (this.activeAction) {
62
- return this.actions[this.activeAction].onDragChange(type, e, state, forceRecalculateEdges, this.boundCancel);
68
+ const res2 = this.actions[this.activeAction].onDragChange(type, e, state, forceRecalculateEdges, this.boundCancel);
69
+ this.shapes.splice(0, this.shapes.length, ...res2.shapes);
70
+ this.setTextHints(type);
71
+ return res2;
72
+ }
73
+ this.setTextHints(type);
74
+ const res = this.defaultOnDragChange(type, e, state, forceRecalculateEdges, this.boundCancel);
75
+ this.shapes.splice(0, this.shapes.length, ...res.shapes);
76
+ return res;
77
+ }
78
+ setTextHints(type) {
79
+ this.textHints.actions = [];
80
+ this.textHints.errors = [];
81
+ for (const action of Object.values(this.actions)) {
82
+ const res = action.getTextHints?.(type);
83
+ this.textHints.actions.push(...res?.actions ?? []);
84
+ this.textHints.errors.push(...res?.errors ?? []);
63
85
  }
64
- return this.defaultOnDragChange(type, e, state, forceRecalculateEdges, this.boundCancel);
65
86
  }
66
87
  onDragApply(state, forceRecalculateEdges) {
67
88
  if (this.activeAction) {
@@ -72,12 +93,26 @@ export class DragActionHandler {
72
93
  this.hooks.onEnd?.({ cancelled: false, applied: false });
73
94
  return true;
74
95
  }
75
- cancel() {
96
+ cancel(e, state) {
76
97
  if (this.activeAction) {
77
- this.actions[this.activeAction].cancel();
98
+ this.actions[this.activeAction].cancel(e, state);
78
99
  }
79
100
  this.activeAction = void 0;
80
- this.eventCanceller?.();
101
+ this.eventCanceller?.(e, state);
81
102
  this.hooks.onEnd?.({ cancelled: true, applied: false });
82
103
  }
104
+ static debugState(pluginName, type, state, pluginState = {}, key) {
105
+ let res = { state, pluginState };
106
+ let pickedRes = {};
107
+ if (typeof key === "string" && key !== "") {
108
+ pickedRes = {};
109
+ const paths = key.split(",").map((_) => _.trim()).filter((_) => _);
110
+ for (const path of paths) {
111
+ pickedRes[path] = get(res, path.split("."));
112
+ }
113
+ res = pickedRes;
114
+ }
115
+ const resString = JSON.stringify(res, null, 2);
116
+ console.log(`[Drag Action Debug - ${pluginName}]:`, type, resString);
117
+ }
83
118
  }
@@ -0,0 +1,45 @@
1
+ import type { ActionDragChangeResult, DragState, DragZone, FrameDragDeco, IDragAction, LayoutChange } from "../types/index.js";
2
+ import type { KnownError } from "../utils/KnownError.js";
3
+ export declare class FrameDragAction implements IDragAction {
4
+ name: "frameDrag";
5
+ state: {
6
+ lastReturn?: LayoutChange<"split" | "swap" | "rearrange" | "dock"> | KnownError;
7
+ };
8
+ debug: boolean | string;
9
+ textHints: {
10
+ actions: string[];
11
+ errors: string[];
12
+ };
13
+ handleEvent: (e: PointerEvent | KeyboardEvent, state: DragState) => boolean;
14
+ dragHints: {
15
+ actions: {
16
+ split: string;
17
+ swap: string;
18
+ rearrange: string;
19
+ dock: string;
20
+ };
21
+ transformError: (e: KnownError) => string;
22
+ };
23
+ modifyDecos: (shapes: FrameDragDeco[]) => void;
24
+ hooks: {
25
+ onStart?: () => void;
26
+ onCancel?: () => void;
27
+ onError?: (e: KnownError) => void;
28
+ };
29
+ constructor(handleEvent?: FrameDragAction["handleEvent"],
30
+ /** Modify the created decos before they are rendered. */
31
+ modifyDecos?: FrameDragAction["modifyDecos"], hooks?: FrameDragAction["hooks"], config?: {
32
+ debug?: boolean | string;
33
+ });
34
+ reset(): void;
35
+ setTextHints(result: LayoutChange<"split" | "swap" | "rearrange" | "dock"> | KnownError | undefined): void;
36
+ getTextHints(type: "start" | "move" | "end"): {
37
+ actions: string[];
38
+ errors: string[];
39
+ };
40
+ getDecos(matchedZone: DragZone | undefined, state: DragState, result: LayoutChange<"split" | "swap" | "rearrange" | "dock"> | KnownError | undefined): FrameDragDeco[];
41
+ canHandleRequest(e: PointerEvent | KeyboardEvent, state: DragState): boolean;
42
+ onDragChange<T extends "start" | "move" | "end">(_type: T, _e: PointerEvent | undefined, state: DragState): ActionDragChangeResult;
43
+ onDragApply(state: DragState): boolean;
44
+ cancel(): void;
45
+ }
@@ -0,0 +1,143 @@
1
+ import { DragActionHandler } from "./DragActionHandler.js";
2
+ import { createZoneSideClipPath } from "../helpers/createZoneSideClipPath.js";
3
+ import { applyFrameChanges } from "../layout/applyFrameChanges.js";
4
+ import { getDragZones } from "../layout/getDragZones.js";
5
+ import { getFrameDockInfo } from "../layout/getFrameDockInfo.js";
6
+ import { getFrameRearrangeInfo } from "../layout/getFrameRearrangeInfo.js";
7
+ import { getFrameSwapInfo } from "../layout/getFrameSwapInfo.js";
8
+ import { settings } from "../settings.js";
9
+ export class FrameDragAction {
10
+ name = "frameDrag";
11
+ state = {
12
+ lastReturn: void 0
13
+ };
14
+ debug = false;
15
+ textHints = { actions: [], errors: [] };
16
+ handleEvent = (_e, state) => state.isDragging === "frame";
17
+ dragHints = {
18
+ actions: {
19
+ split: "Drop to Split the current frame.",
20
+ swap: "Drop to Swap the frames.",
21
+ rearrange: "Drop to Move the frame.",
22
+ dock: "Drop to Dock the frame."
23
+ },
24
+ transformError: (e) => e.message
25
+ };
26
+ modifyDecos = () => {
27
+ };
28
+ hooks = {};
29
+ constructor(handleEvent, modifyDecos, hooks = {}, config) {
30
+ if (handleEvent !== void 0) this.handleEvent = handleEvent;
31
+ if (modifyDecos !== void 0) this.modifyDecos = modifyDecos;
32
+ this.hooks = hooks;
33
+ if (config?.debug) this.debug = true;
34
+ this.reset();
35
+ }
36
+ reset() {
37
+ this.state = {
38
+ lastReturn: void 0
39
+ };
40
+ }
41
+ setTextHints(result) {
42
+ if (result === void 0 || !result.info) {
43
+ this.textHints.actions = [];
44
+ this.textHints.errors = [];
45
+ } else if (result instanceof Error) {
46
+ this.textHints.actions = [];
47
+ this.textHints.errors = [this.dragHints.transformError(result)];
48
+ } else {
49
+ const text = this.dragHints.actions[result.info];
50
+ this.textHints.actions = [text];
51
+ this.textHints.errors = [];
52
+ }
53
+ }
54
+ getTextHints(type) {
55
+ if (type === "end") {
56
+ this.setTextHints(void 0);
57
+ }
58
+ return this.textHints;
59
+ }
60
+ getDecos(matchedZone, state, result) {
61
+ let decos = [];
62
+ const isError = result instanceof Error;
63
+ const frame = state.dragHoveredFrame;
64
+ if (!matchedZone || !frame) {
65
+ decos = [];
66
+ this.modifyDecos(decos);
67
+ return decos;
68
+ }
69
+ const clipPath = createZoneSideClipPath(matchedZone, settings.zoneSizes);
70
+ 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 = [{
72
+ id: frame.id,
73
+ type: "drop",
74
+ position: matchedZone.side ?? "center",
75
+ shapes: [
76
+ {
77
+ type: "square",
78
+ data: matchedZone,
79
+ attrs: clipPath ? { class: classes, style: `clip-path:${clipPath}` } : { class: classes }
80
+ }
81
+ ]
82
+ }];
83
+ this.modifyDecos(decos);
84
+ return decos;
85
+ }
86
+ canHandleRequest(e, state) {
87
+ const res = this.handleEvent(e, state);
88
+ this.setTextHints(void 0);
89
+ if (res) {
90
+ this.hooks.onStart?.();
91
+ return true;
92
+ }
93
+ this.reset();
94
+ return false;
95
+ }
96
+ onDragChange(_type, _e, state) {
97
+ const { win, draggingFrameId, dragHoveredFrame } = state;
98
+ const matchedZone = getDragZones(state, settings.zoneSizes);
99
+ if (!matchedZone) {
100
+ this.state.lastReturn = void 0;
101
+ } else if (dragHoveredFrame && matchedZone.type === "frame" && matchedZone.side) {
102
+ if (matchedZone.side !== "center") {
103
+ this.state.lastReturn = getFrameRearrangeInfo(win, draggingFrameId, dragHoveredFrame.id, matchedZone.side);
104
+ } else {
105
+ this.state.lastReturn = getFrameSwapInfo(win, draggingFrameId, dragHoveredFrame.id);
106
+ if (!(this.state.lastReturn instanceof Error)) this.state.lastReturn.info = "swap";
107
+ }
108
+ } else if (matchedZone.type === "window") {
109
+ this.state.lastReturn = getFrameDockInfo(win, draggingFrameId, matchedZone.side);
110
+ if (!(this.state.lastReturn instanceof Error)) this.state.lastReturn.info = "dock";
111
+ }
112
+ this.setTextHints(this.state.lastReturn);
113
+ const decos = this.getDecos(matchedZone, state, this.state.lastReturn);
114
+ return {
115
+ shapes: decos.flatMap((_) => _.shapes),
116
+ updateEdges: false,
117
+ showDragging: false
118
+ };
119
+ }
120
+ onDragApply(state) {
121
+ const result = this.state.lastReturn;
122
+ if (!result || !state.dragHoveredFrame || !state.draggingFrameId) {
123
+ this.reset();
124
+ return true;
125
+ }
126
+ if (result instanceof Error) {
127
+ this.hooks.onError?.(result);
128
+ } else {
129
+ if (this.debug) {
130
+ DragActionHandler.debugState(this.name, "before", state, this.state, this.debug);
131
+ }
132
+ applyFrameChanges(state.win, result);
133
+ if (this.debug) {
134
+ DragActionHandler.debugState(this.name, "after", state, this.state, this.debug);
135
+ }
136
+ }
137
+ this.reset();
138
+ return true;
139
+ }
140
+ cancel() {
141
+ this.reset();
142
+ }
143
+ }
@@ -1,32 +1,53 @@
1
- import type { DragState, IDragAction } from "./types.js";
1
+ import type { ActionDragChangeResult, DragState, IDragAction } from "./types.js";
2
2
  import { getFrameSplitInfo } from "../layout/getFrameSplitInfo.js";
3
- import type { SplitDeco } from "../types/index.js";
3
+ import type { Point, SplitDeco } from "../types/index.js";
4
4
  import type { KnownError } from "../utils/KnownError.js";
5
5
  export type SplitInfo = Exclude<ReturnType<typeof getFrameSplitInfo>, KnownError>;
6
6
  export declare class SplitAction implements IDragAction {
7
7
  name: "split";
8
8
  state: {
9
- allowed: false;
10
- res: SplitInfo | undefined;
11
- cacheKey: string | undefined;
12
- } | {
13
9
  allowed: true;
14
10
  res: SplitInfo;
15
- cacheKey: string | undefined;
11
+ lastPoint: Point | undefined;
12
+ lastReturn: undefined;
13
+ } | {
14
+ allowed: false;
15
+ res: SplitInfo | undefined;
16
+ lastPoint: Point | undefined;
17
+ lastReturn: ActionDragChangeResult | undefined;
18
+ };
19
+ debug: boolean | string;
20
+ textHints: {
21
+ actions: string[];
22
+ errors: string[];
23
+ };
24
+ splitHints: {
25
+ action: string;
26
+ transformError: (e: KnownError) => string;
16
27
  };
17
28
  handleEvent: (e: PointerEvent | KeyboardEvent, state: DragState) => boolean;
18
- updateSplitDecos: (decos: SplitDeco[]) => void;
29
+ modifyDecos: (shapes: SplitDeco[]) => void;
19
30
  hooks: {
20
31
  onStart?: () => void;
21
32
  onCancel?: () => void;
22
33
  onError?: (e: KnownError) => void;
23
34
  };
24
- constructor(handleEvent: SplitAction["handleEvent"], updateSplitDecos: SplitAction["updateSplitDecos"], hooks?: SplitAction["hooks"]);
35
+ constructor(handleEvent?: SplitAction["handleEvent"],
36
+ /** Modify the created decos before they are rendered. */
37
+ modifyDecos?: SplitAction["modifyDecos"], hooks?: SplitAction["hooks"], config?: {
38
+ debug?: boolean | string;
39
+ splitHints?: Partial<SplitAction["splitHints"]>;
40
+ });
25
41
  reset(): void;
26
- updateDecos(state: DragState): void;
42
+ getDecos(state: DragState): SplitDeco[];
27
43
  canHandleRequest(e: PointerEvent | KeyboardEvent, state: DragState): boolean;
44
+ setTextHints(result: true | SplitInfo | KnownError | undefined): void;
45
+ getTextHints(type: "start" | "move" | "end"): {
46
+ actions: string[];
47
+ errors: string[];
48
+ };
28
49
  calculateSplitRequest(state: DragState): boolean;
29
- onDragChange(_type: "start" | "end" | "move", _e: PointerEvent | undefined, state: DragState): true;
50
+ onDragChange(_type: "start" | "end" | "move", _e: PointerEvent | undefined, state: DragState): ActionDragChangeResult;
30
51
  onDragApply(state: DragState): boolean;
31
52
  cancel(): void;
32
53
  }
@@ -1,30 +1,43 @@
1
+ import { DragActionHandler } from "./DragActionHandler.js";
1
2
  import { getEdgeOrientation } from "../helpers/getEdgeOrientation.js";
2
3
  import { oppositeSide } from "../helpers/oppositeSide.js";
4
+ import { applyFrameChanges } from "../layout/applyFrameChanges.js";
3
5
  import { createSplitDecoFromDrag } from "../layout/createSplitDecoFromDrag.js";
4
- import { frameSplit } from "../layout/frameSplit.js";
5
6
  import { getFrameSplitInfo } from "../layout/getFrameSplitInfo.js";
6
7
  export class SplitAction {
7
8
  name = "split";
8
9
  state = {};
9
10
  // this is initialized by `this.reset()`
10
- handleEvent;
11
- updateSplitDecos;
11
+ debug = false;
12
+ textHints = { actions: [], errors: [] };
13
+ splitHints = {
14
+ action: "Hold Alt to Split",
15
+ transformError: (e) => e.message
16
+ };
17
+ handleEvent = (e, state) => e.altKey || state.isDraggingFromWindowEdge;
18
+ modifyDecos = () => {
19
+ };
12
20
  hooks;
13
- constructor(handleEvent, updateSplitDecos, hooks = {}) {
14
- this.handleEvent = handleEvent;
15
- this.updateSplitDecos = updateSplitDecos;
21
+ constructor(handleEvent, modifyDecos, hooks = {}, config) {
22
+ if (handleEvent !== void 0) this.handleEvent = handleEvent;
23
+ if (modifyDecos !== void 0) this.modifyDecos = modifyDecos;
16
24
  this.hooks = hooks;
17
25
  this.reset();
26
+ if (config?.debug) this.debug = true;
27
+ if (config?.splitHints?.action) this.splitHints.action = config.splitHints.action;
28
+ if (config?.splitHints?.transformError) this.splitHints.transformError = config.splitHints.transformError;
18
29
  }
19
30
  reset() {
20
31
  this.state = {
21
32
  allowed: false,
22
33
  res: void 0,
23
- cacheKey: void 0
34
+ lastPoint: void 0,
35
+ lastReturn: void 0
24
36
  };
25
- this.updateSplitDecos([]);
37
+ this.modifyDecos([]);
26
38
  }
27
- updateDecos(state) {
39
+ getDecos(state) {
40
+ let decos = [];
28
41
  const {
29
42
  win,
30
43
  isDragging,
@@ -41,14 +54,15 @@ export class SplitAction {
41
54
  dragDirections[oppositeOrientation],
42
55
  dragPoint
43
56
  );
44
- this.updateSplitDecos([deco]);
45
- } else {
46
- this.updateSplitDecos([]);
57
+ decos = [deco];
47
58
  }
59
+ this.modifyDecos(decos);
60
+ return decos;
48
61
  }
49
62
  canHandleRequest(e, state) {
50
63
  const { draggingEdges } = state;
51
64
  if (draggingEdges.length !== 1) return false;
65
+ this.setTextHints(state.isDragging === "edge" ? true : void 0);
52
66
  if (this.handleEvent(e, state)) {
53
67
  this.hooks.onStart?.();
54
68
  return true;
@@ -56,26 +70,49 @@ export class SplitAction {
56
70
  this.reset();
57
71
  return false;
58
72
  }
73
+ setTextHints(result) {
74
+ if (result === void 0) {
75
+ this.textHints.actions = [];
76
+ this.textHints.errors = [];
77
+ } else if (result instanceof Error) {
78
+ this.textHints.actions = [];
79
+ this.textHints.errors = [this.splitHints.transformError(result)];
80
+ } else {
81
+ this.textHints.actions = [this.splitHints.action];
82
+ this.textHints.errors = [];
83
+ }
84
+ }
85
+ getTextHints(type) {
86
+ if (type === "end") {
87
+ this.setTextHints(void 0);
88
+ }
89
+ return this.textHints;
90
+ }
59
91
  calculateSplitRequest(state) {
60
92
  const {
61
93
  dragHoveredFrame,
62
94
  dragDirections,
63
95
  draggingEdges,
64
- dragPoint
96
+ dragPoint,
97
+ win
65
98
  } = state;
66
99
  const oppositeOrientation = oppositeSide(getEdgeOrientation(draggingEdges[0]));
100
+ const originalDragHoveredFrame = win.frames[dragHoveredFrame.id];
67
101
  const canSplit = getFrameSplitInfo(
68
- dragHoveredFrame,
102
+ originalDragHoveredFrame,
69
103
  dragDirections[oppositeOrientation],
70
104
  dragPoint
71
105
  );
106
+ this.setTextHints(canSplit);
72
107
  if (!(canSplit instanceof Error)) {
73
108
  this.state.allowed = true;
74
109
  this.state.res = canSplit;
75
- this.state.cacheKey = dragHoveredFrame?.id;
110
+ this.state.lastPoint = dragPoint ? { ...dragPoint } : void 0;
76
111
  return true;
77
112
  } else {
78
113
  this.hooks.onError?.(canSplit);
114
+ this.state.lastPoint = void 0;
115
+ this.state.allowed = false;
79
116
  return false;
80
117
  }
81
118
  }
@@ -83,22 +120,43 @@ export class SplitAction {
83
120
  const { dragHoveredFrame } = state;
84
121
  let ok = false;
85
122
  if (dragHoveredFrame) {
86
- if (this.state.cacheKey === dragHoveredFrame.id) {
87
- this.updateDecos(state);
88
- return true;
123
+ if (this.state.lastPoint?.x === state.dragPoint?.x && this.state.lastPoint?.y === state.dragPoint?.y && this.state.lastReturn) {
124
+ return this.state.lastReturn;
89
125
  }
90
126
  ok = this.calculateSplitRequest(state);
91
127
  }
92
- this.updateDecos(state);
128
+ const decos = this.getDecos(state);
93
129
  if (!ok) {
94
- this.state.allowed = false;
130
+ if (dragHoveredFrame && dragHoveredFrame.docked) {
131
+ const errorDeco = {
132
+ type: "square",
133
+ data: {
134
+ x: dragHoveredFrame.x,
135
+ y: dragHoveredFrame.y,
136
+ width: dragHoveredFrame.width,
137
+ height: dragHoveredFrame.height
138
+ },
139
+ attrs: { class: "deco-split-error bg-red-500/50" }
140
+ };
141
+ this.state.lastReturn = { updateEdges: true, shapes: [errorDeco], showDragging: false };
142
+ return this.state.lastReturn;
143
+ }
95
144
  }
96
- return true;
145
+ this.state.lastReturn = { updateEdges: false, shapes: decos.flatMap((_) => _.shapes), showDragging: false };
146
+ return this.state.lastReturn;
97
147
  }
98
148
  onDragApply(state) {
99
- if (this.state.res) {
100
- this.calculateSplitRequest(state);
101
- frameSplit(state.win, this.state.res);
149
+ if (this.state.res && state.dragHoveredFrame) {
150
+ const ok = this.calculateSplitRequest(state);
151
+ if (ok) {
152
+ if (this.debug) {
153
+ DragActionHandler.debugState(this.name, "before", state, this.state, this.debug);
154
+ }
155
+ applyFrameChanges(state.win, this.state.res);
156
+ if (this.debug) {
157
+ DragActionHandler.debugState(this.name, "after", state, this.state, this.debug);
158
+ }
159
+ }
102
160
  }
103
161
  this.reset();
104
162
  return true;
@@ -0,0 +1,9 @@
1
+ import type { IDragAction } from "./types.js";
2
+ /**
3
+ * Creates the default drag actions (Split, Close, FrameDrag).
4
+ */
5
+ export declare function createDefaultHandlers(config?: {
6
+ debugSplit?: boolean;
7
+ debugClose?: boolean;
8
+ debugFrameDrag?: boolean;
9
+ }): IDragAction[];
@@ -0,0 +1,10 @@
1
+ import { CloseAction } from "./CloseAction.js";
2
+ import { FrameDragAction } from "./FrameDragAction.js";
3
+ import { SplitAction } from "./SplitAction.js";
4
+ export function createDefaultHandlers(config = {}) {
5
+ return [
6
+ new SplitAction(void 0, void 0, void 0, { debug: config.debugSplit }),
7
+ new CloseAction(void 0, void 0, void 0, { debug: config.debugClose }),
8
+ new FrameDragAction(void 0, void 0, void 0, { debug: config.debugFrameDrag })
9
+ ];
10
+ }
@@ -0,0 +1,9 @@
1
+ import type { IDragAction } from "./types.js";
2
+ /**
3
+ * Creates the default drag actions (Split, Close, FrameDrag).
4
+ */
5
+ export declare function createDefaultHandlers(config: {
6
+ debugSplit?: boolean;
7
+ debugClose?: boolean;
8
+ debugFrameDrag?: boolean;
9
+ }): IDragAction[];
@@ -0,0 +1,10 @@
1
+ import { CloseAction } from "./CloseAction.js";
2
+ import { FrameDragAction } from "./FrameDragAction.js";
3
+ import { SplitAction } from "./SplitAction.js";
4
+ export function createDefaultHandlers(config) {
5
+ return [
6
+ new SplitAction(void 0, void 0, void 0, { debug: config.debugSplit }),
7
+ new CloseAction(void 0, void 0, void 0, { debug: config.debugClose }),
8
+ new FrameDragAction(void 0, void 0, void 0, { debug: config.debugFrameDrag })
9
+ ];
10
+ }