@witchcraft/layout 0.4.2 → 0.4.4

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 (65) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/LayoutDragEdgeGrabbed.vue +1 -1
  3. package/dist/runtime/components/LayoutDragEdgeHandle.vue +1 -2
  4. package/dist/runtime/components/LayoutWindow.d.vue.ts +12 -0
  5. package/dist/runtime/components/LayoutWindow.vue +5 -4
  6. package/dist/runtime/components/LayoutWindow.vue.d.ts +12 -0
  7. package/dist/runtime/composables/useFrames.d.ts +38 -22
  8. package/dist/runtime/composables/useFrames.js +44 -8
  9. package/dist/runtime/demo/tailwind.css +1 -1
  10. package/dist/runtime/drag/CloseAction.d.ts +3 -0
  11. package/dist/runtime/drag/CloseAction.js +5 -0
  12. package/dist/runtime/drag/DragActionHandler.d.ts +4 -1
  13. package/dist/runtime/drag/DragActionHandler.js +2 -2
  14. package/dist/runtime/drag/FrameDragAction.d.ts +3 -0
  15. package/dist/runtime/drag/FrameDragAction.js +5 -0
  16. package/dist/runtime/drag/SplitAction.d.ts +3 -0
  17. package/dist/runtime/drag/SplitAction.js +8 -3
  18. package/dist/runtime/helpers/findEdgesTouchingWindow.d.ts +7 -0
  19. package/dist/runtime/helpers/findEdgesTouchingWindow.js +20 -0
  20. package/dist/runtime/helpers/index.d.ts +8 -1
  21. package/dist/runtime/helpers/index.js +8 -1
  22. package/dist/runtime/layout/debugFrame.js +5 -1
  23. package/dist/runtime/layout/getFrameCollapseInfo.d.ts +1 -1
  24. package/dist/runtime/layout/getFrameCollapseInfo.js +9 -29
  25. package/dist/runtime/layout/getFrameDockInfo.d.ts +1 -1
  26. package/dist/runtime/layout/getFrameDockInfo.js +61 -44
  27. package/dist/runtime/layout/getFrameExpandInfo.d.ts +11 -0
  28. package/dist/runtime/layout/getFrameExpandInfo.js +49 -0
  29. package/dist/runtime/layout/getFrameShrinkInfo.d.ts +14 -0
  30. package/dist/runtime/layout/getFrameShrinkInfo.js +48 -0
  31. package/dist/runtime/layout/getFrameUncollapseInfo.d.ts +1 -1
  32. package/dist/runtime/layout/getFrameUncollapseInfo.js +6 -20
  33. package/dist/runtime/layout/getUpdateWindowSizeInfo.js +10 -12
  34. package/dist/runtime/layout/index.d.ts +3 -0
  35. package/dist/runtime/layout/index.js +3 -0
  36. package/dist/runtime/settings.js +1 -1
  37. package/dist/runtime/types/index.d.ts +72 -0
  38. package/dist/runtime/types/index.js +2 -1
  39. package/dist/runtime/types/vue.d.ts +12 -3
  40. package/package.json +3 -2
  41. package/src/runtime/components/LayoutDragEdgeGrabbed.vue +1 -1
  42. package/src/runtime/components/LayoutDragEdgeHandle.vue +1 -2
  43. package/src/runtime/components/LayoutWindow.vue +8 -6
  44. package/src/runtime/composables/useFrames.ts +61 -32
  45. package/src/runtime/demo/tailwind.css +4 -0
  46. package/src/runtime/drag/CloseAction.ts +7 -0
  47. package/src/runtime/drag/DragActionHandler.ts +3 -3
  48. package/src/runtime/drag/FrameDragAction.ts +9 -0
  49. package/src/runtime/drag/SplitAction.ts +11 -3
  50. package/src/runtime/helpers/findEdgesTouchingWindow.ts +30 -0
  51. package/src/runtime/helpers/index.ts +8 -1
  52. package/src/runtime/layout/debugFrame.ts +6 -1
  53. package/src/runtime/layout/getFrameCollapseInfo.ts +12 -41
  54. package/src/runtime/layout/getFrameDockInfo.ts +77 -57
  55. package/src/runtime/layout/getFrameExpandInfo.ts +84 -0
  56. package/src/runtime/layout/getFrameShrinkInfo.ts +87 -0
  57. package/src/runtime/layout/getFrameUncollapseInfo.ts +8 -30
  58. package/src/runtime/layout/getUpdateWindowSizeInfo.ts +16 -22
  59. package/src/runtime/layout/index.ts +3 -0
  60. package/src/runtime/settings.ts +1 -1
  61. package/src/runtime/types/index.ts +41 -1
  62. package/src/runtime/types/vue.ts +5 -3
  63. package/dist/runtime/drag/defaultDragActions.d.ts +0 -9
  64. package/dist/runtime/drag/defaultDragActions.js +0 -10
  65. package/src/runtime/drag/defaultDragActions.ts +0 -20
@@ -12,13 +12,16 @@ export { getFillEmptySpaceInfo } from "./getFillEmptySpaceInfo.js";
12
12
  export { getFrameCollapseInfo } from "./getFrameCollapseInfo.js";
13
13
  export { getFrameDockInfo } from "./getFrameDockInfo.js";
14
14
  export { getFrameDragZones } from "./getFrameDragZones.js";
15
+ export { getFrameExpandInfo } from "./getFrameExpandInfo.js";
15
16
  export { getFrameRearrangeInfo } from "./getFrameRearrangeInfo.js";
17
+ export { getFrameShrinkInfo } from "./getFrameShrinkInfo.js";
16
18
  export { getFrameSplitInfo } from "./getFrameSplitInfo.js";
17
19
  export { getFramesRedistributeInfo } from "./getFramesRedistributeInfo.js";
18
20
  export { getFrameSwapInfo } from "./getFrameSwapInfo.js";
19
21
  export { getFrameTo } from "./getFrameTo.js";
20
22
  export { getFrameUncollapseInfo } from "./getFrameUncollapseInfo.js";
21
23
  export { getFrameUndockInfo } from "./getFrameUndockInfo.js";
24
+ export { getRelaxFramesInfo } from "./getRelaxFramesInfo.js";
22
25
  export { getUpdateWindowSizeInfo } from "./getUpdateWindowSizeInfo.js";
23
26
  export { getWindowDragZones } from "./getWindowDragZones.js";
24
27
  export { isPointInRect } from "./isPointInRect.js";
@@ -8,7 +8,7 @@ export class Settings {
8
8
  resetToDefaults() {
9
9
  this.scale = 3;
10
10
  this.snapPoint = 0.5;
11
- this.minSize = 5;
11
+ this.minSize = 3;
12
12
  this.collapseSizePx = 15;
13
13
  this.maxPerpendicularLength = 20;
14
14
  this.zoneSizes = { frameEdgePx: 40, windowEdgePx: 20 };
@@ -105,6 +105,8 @@ export declare const zEdge: z.ZodObject<{
105
105
  frame: LayoutFrame;
106
106
  } | {
107
107
  frame: LayoutFrame;
108
+ } | {
109
+ frame: LayoutFrame;
108
110
  }>, KnownError<LayoutError, {} | {
109
111
  id: string | undefined;
110
112
  } | {
@@ -162,6 +164,8 @@ export declare const zEdge: z.ZodObject<{
162
164
  frame: LayoutFrame;
163
165
  } | {
164
166
  frame: LayoutFrame;
167
+ } | {
168
+ frame: LayoutFrame;
165
169
  }>>, z.ZodObject<{
166
170
  code: z.ZodString;
167
171
  message: z.ZodString;
@@ -504,6 +508,8 @@ declare const zLayoutShape: z.ZodDiscriminatedUnion<[z.ZodObject<{
504
508
  frame: LayoutFrame;
505
509
  } | {
506
510
  frame: LayoutFrame;
511
+ } | {
512
+ frame: LayoutFrame;
507
513
  }>, KnownError<LayoutError, {} | {
508
514
  id: string | undefined;
509
515
  } | {
@@ -561,6 +567,8 @@ declare const zLayoutShape: z.ZodDiscriminatedUnion<[z.ZodObject<{
561
567
  frame: LayoutFrame;
562
568
  } | {
563
569
  frame: LayoutFrame;
570
+ } | {
571
+ frame: LayoutFrame;
564
572
  }>>, z.ZodObject<{
565
573
  code: z.ZodString;
566
574
  message: z.ZodString;
@@ -643,6 +651,8 @@ declare const zBaseDeco: z.ZodObject<{
643
651
  frame: LayoutFrame;
644
652
  } | {
645
653
  frame: LayoutFrame;
654
+ } | {
655
+ frame: LayoutFrame;
646
656
  }>, KnownError<LayoutError, {} | {
647
657
  id: string | undefined;
648
658
  } | {
@@ -700,6 +710,8 @@ declare const zBaseDeco: z.ZodObject<{
700
710
  frame: LayoutFrame;
701
711
  } | {
702
712
  frame: LayoutFrame;
713
+ } | {
714
+ frame: LayoutFrame;
703
715
  }>>, z.ZodObject<{
704
716
  code: z.ZodString;
705
717
  message: z.ZodString;
@@ -783,6 +795,8 @@ export declare const zSplitDeco: z.ZodObject<{
783
795
  frame: LayoutFrame;
784
796
  } | {
785
797
  frame: LayoutFrame;
798
+ } | {
799
+ frame: LayoutFrame;
786
800
  }>, KnownError<LayoutError, {} | {
787
801
  id: string | undefined;
788
802
  } | {
@@ -840,6 +854,8 @@ export declare const zSplitDeco: z.ZodObject<{
840
854
  frame: LayoutFrame;
841
855
  } | {
842
856
  frame: LayoutFrame;
857
+ } | {
858
+ frame: LayoutFrame;
843
859
  }>>, z.ZodObject<{
844
860
  code: z.ZodString;
845
861
  message: z.ZodString;
@@ -944,6 +960,8 @@ declare const zCloseDeco: z.ZodObject<{
944
960
  frame: LayoutFrame;
945
961
  } | {
946
962
  frame: LayoutFrame;
963
+ } | {
964
+ frame: LayoutFrame;
947
965
  }>, KnownError<LayoutError, {} | {
948
966
  id: string | undefined;
949
967
  } | {
@@ -1001,6 +1019,8 @@ declare const zCloseDeco: z.ZodObject<{
1001
1019
  frame: LayoutFrame;
1002
1020
  } | {
1003
1021
  frame: LayoutFrame;
1022
+ } | {
1023
+ frame: LayoutFrame;
1004
1024
  }>>, z.ZodObject<{
1005
1025
  code: z.ZodString;
1006
1026
  message: z.ZodString;
@@ -1087,6 +1107,8 @@ declare const zFrameDragDeco: z.ZodObject<{
1087
1107
  frame: LayoutFrame;
1088
1108
  } | {
1089
1109
  frame: LayoutFrame;
1110
+ } | {
1111
+ frame: LayoutFrame;
1090
1112
  }>, KnownError<LayoutError, {} | {
1091
1113
  id: string | undefined;
1092
1114
  } | {
@@ -1144,6 +1166,8 @@ declare const zFrameDragDeco: z.ZodObject<{
1144
1166
  frame: LayoutFrame;
1145
1167
  } | {
1146
1168
  frame: LayoutFrame;
1169
+ } | {
1170
+ frame: LayoutFrame;
1147
1171
  }>>, z.ZodObject<{
1148
1172
  code: z.ZodString;
1149
1173
  message: z.ZodString;
@@ -1237,6 +1261,8 @@ export declare const zDeco: z.ZodUnion<readonly [z.ZodObject<{
1237
1261
  frame: LayoutFrame;
1238
1262
  } | {
1239
1263
  frame: LayoutFrame;
1264
+ } | {
1265
+ frame: LayoutFrame;
1240
1266
  }>, KnownError<LayoutError, {} | {
1241
1267
  id: string | undefined;
1242
1268
  } | {
@@ -1294,6 +1320,8 @@ export declare const zDeco: z.ZodUnion<readonly [z.ZodObject<{
1294
1320
  frame: LayoutFrame;
1295
1321
  } | {
1296
1322
  frame: LayoutFrame;
1323
+ } | {
1324
+ frame: LayoutFrame;
1297
1325
  }>>, z.ZodObject<{
1298
1326
  code: z.ZodString;
1299
1327
  message: z.ZodString;
@@ -1384,6 +1412,8 @@ export declare const zDeco: z.ZodUnion<readonly [z.ZodObject<{
1384
1412
  frame: LayoutFrame;
1385
1413
  } | {
1386
1414
  frame: LayoutFrame;
1415
+ } | {
1416
+ frame: LayoutFrame;
1387
1417
  }>, KnownError<LayoutError, {} | {
1388
1418
  id: string | undefined;
1389
1419
  } | {
@@ -1441,6 +1471,8 @@ export declare const zDeco: z.ZodUnion<readonly [z.ZodObject<{
1441
1471
  frame: LayoutFrame;
1442
1472
  } | {
1443
1473
  frame: LayoutFrame;
1474
+ } | {
1475
+ frame: LayoutFrame;
1444
1476
  }>>, z.ZodObject<{
1445
1477
  code: z.ZodString;
1446
1478
  message: z.ZodString;
@@ -1525,6 +1557,8 @@ export declare const zDeco: z.ZodUnion<readonly [z.ZodObject<{
1525
1557
  frame: LayoutFrame;
1526
1558
  } | {
1527
1559
  frame: LayoutFrame;
1560
+ } | {
1561
+ frame: LayoutFrame;
1528
1562
  }>, KnownError<LayoutError, {} | {
1529
1563
  id: string | undefined;
1530
1564
  } | {
@@ -1582,6 +1616,8 @@ export declare const zDeco: z.ZodUnion<readonly [z.ZodObject<{
1582
1616
  frame: LayoutFrame;
1583
1617
  } | {
1584
1618
  frame: LayoutFrame;
1619
+ } | {
1620
+ frame: LayoutFrame;
1585
1621
  }>>, z.ZodObject<{
1586
1622
  code: z.ZodString;
1587
1623
  message: z.ZodString;
@@ -1627,6 +1663,7 @@ export declare const LAYOUT_ERROR: {
1627
1663
  CANT_UNCOLLAPSE_NOT_COLLAPSED: "CANT_UNCOLLAPSE_NOT_COLLAPSED";
1628
1664
  NO_FILL_CANDIDATES: "NO_FILL_CANDIDATES";
1629
1665
  CANT_RESIZE_COLLAPSED_FRAME: "CANT_RESIZE_COLLAPSED_FRAME";
1666
+ CANT_RESIZE_SINGLE_FRAME: "CANT_RESIZE_SINGLE_FRAME";
1630
1667
  };
1631
1668
  export type LayoutError = EnumLike<typeof LAYOUT_ERROR>;
1632
1669
  export type LayoutErrorInfo<T extends LayoutError> = LayoutErrorsInfo[T] extends undefined ? never : LayoutErrorsInfo[T];
@@ -1710,6 +1747,9 @@ export type LayoutErrorsInfo = {
1710
1747
  [LAYOUT_ERROR.CANT_RESIZE_COLLAPSED_FRAME]: {
1711
1748
  frame: LayoutFrame;
1712
1749
  };
1750
+ [LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME]: {
1751
+ frame: LayoutFrame;
1752
+ };
1713
1753
  [LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES]: {
1714
1754
  problemEdgeCoordinates: number[];
1715
1755
  };
@@ -1808,6 +1848,8 @@ export type DragState = {
1808
1848
  dragDirections: Record<Orientation, Direction | undefined>;
1809
1849
  /** The curren point (in scaled window coordinates) the user is dragging at. */
1810
1850
  dragPoint?: Point;
1851
+ /** Cumulative pixel distance the user has dragged from the initial click point. 0 on start, increases on each move. */
1852
+ dragDistance: number;
1811
1853
  /** Whether the user is currently dragging and what type of drag. Is truthy string during all drag events. */
1812
1854
  isDragging: boolean | "frame" | "edge";
1813
1855
  /** Whether to show the moved frames while dragging. */
@@ -1850,6 +1892,8 @@ export type DragState = {
1850
1892
  * Whether the drag was initiated from a point along the window edge.
1851
1893
  */
1852
1894
  isDraggingFromWindowEdge: boolean;
1895
+ /** Custom context passed to dragStart, available to action handlers via state.eventContext. */
1896
+ eventContext?: Record<string, unknown>;
1853
1897
  win: LayoutWindow;
1854
1898
  };
1855
1899
  export interface ActionDragChangeResult {
@@ -1954,6 +1998,34 @@ export type EdgeDragStartData = {
1954
1998
  export type FrameDragStartData = {
1955
1999
  frameId: FrameId;
1956
2000
  };
2001
+ /**
2002
+ * Handler interface for drag actions.
2003
+ */
2004
+ export interface ActionHandler {
2005
+ eventHandler: (e: KeyboardEvent, state: DragState, forceRecalculateEdges: () => void) => void;
2006
+ /**
2007
+ * Called when the drag coordinates change (during any event). Should return true to allow the edges to be updated/moved, or false to prevent it.
2008
+ *
2009
+ * Can return anything for the end event as it's ignored.
2010
+ *
2011
+ * Can be used to save some context/info to later apply safely during onDragApply.
2012
+ */
2013
+ onDragChange: (...args: Parameters<DragChangeHandler>) => DragChangeResult;
2014
+ /**
2015
+ * Called when drag will be applied. If dragEnd was called with apply false, it will not be called.
2016
+ * Return false to not apply the regular drag end changes (i.e. return false to reset to the position before dragging).
2017
+ */
2018
+ onDragApply: (state: DragState, forceRecalculateEdges: () => void) => {
2019
+ /** Whether to apply the regular drag end changes. Return false to reset to the position before dragging. */
2020
+ apply: boolean;
2021
+ /** Value to resolve the drag promise with. Ignored if `apply` is false. */
2022
+ result: any;
2023
+ };
2024
+ /**
2025
+ * Called after visual edges are recalculated. Action handlers can annotate edges with error info.
2026
+ */
2027
+ annotateEdges?: (edges: Edge[], frames: LayoutFrame[]) => void;
2028
+ }
1957
2029
  export type DragStartFn = {
1958
2030
  (e: PointerEvent, type: "edge", data: EdgeDragStartData): void;
1959
2031
  (e: PointerEvent, type: "frame", data: FrameDragStartData): void;
@@ -153,7 +153,8 @@ export const LAYOUT_ERROR = enumFromArray([
153
153
  "CANT_COLLAPSE_NOT_DOCKED",
154
154
  "CANT_UNCOLLAPSE_NOT_COLLAPSED",
155
155
  "NO_FILL_CANDIDATES",
156
- "CANT_RESIZE_COLLAPSED_FRAME"
156
+ "CANT_RESIZE_COLLAPSED_FRAME",
157
+ "CANT_RESIZE_SINGLE_FRAME"
157
158
  ]);
158
159
  export const zWindowCreate = zLayoutWindowLoose.partial({ id: true, pxWidth: true, pxHeight: true, pxX: true, pxY: true }).extend({ frames: zLayoutWindow.shape.frames.optional() });
159
160
  export const zLayoutCreate = baseLayout.loose().extend({
@@ -1,5 +1,5 @@
1
1
  import type { ComputedRef, InjectionKey, Ref } from "vue";
2
- import type { Direction, DragState, Edge, FrameId, IntersectionEntry, LayoutFrame, LayoutShape, LayoutWindow, Orientation, Point } from "./index.js";
2
+ import type { ActionHandler, Direction, DragState, Edge, FrameId, IntersectionEntry, LayoutFrame, LayoutShape, LayoutWindow, Orientation, Point } from "./index.js";
3
3
  export type LayoutContext = ComputedRef<{
4
4
  /** The owning window, needed so we can correctly scale coordinates. */
5
5
  win: LayoutWindow;
@@ -8,14 +8,23 @@ export type LayoutContext = ComputedRef<{
8
8
  }>;
9
9
  export declare const layoutContextInjectionKey: InjectionKey<LayoutContext>;
10
10
  export interface UseFramesContext {
11
+ actionHandler: ActionHandler;
11
12
  dragStart: {
12
13
  (e: PointerEvent, type: "edge", data: {
13
14
  edge?: Edge;
14
15
  intersection?: IntersectionEntry;
15
- }): void;
16
+ }, opts?: {
17
+ moveEvent?: string;
18
+ endEvent?: string;
19
+ context?: Record<string, unknown>;
20
+ }): Promise<any>;
16
21
  (e: PointerEvent, type: "frame", data: {
17
22
  frameId: FrameId;
18
- }): void;
23
+ }, opts?: {
24
+ moveEvent?: string;
25
+ endEvent?: string;
26
+ context?: Record<string, unknown>;
27
+ }): Promise<any>;
19
28
  };
20
29
  dragMove: (e: PointerEvent) => void;
21
30
  dragEnd: (e?: PointerEvent, options?: {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@witchcraft/layout",
3
3
  "description": "Headless layout manager.",
4
- "version": "0.4.2",
4
+ "version": "0.4.4",
5
5
  "main": "./dist/runtime/index.js",
6
6
  "type": "module",
7
7
  "sideEffects": false,
@@ -49,6 +49,7 @@
49
49
  "@alanscodelog/semantic-release-config": "^6.0.2",
50
50
  "@alanscodelog/tsconfigs": "^6.3.0",
51
51
  "@alanscodelog/vite-config": "^0.0.7",
52
+ "@babel/runtime": "^7.20.7",
52
53
  "@commitlint/cli": "^19.8.1",
53
54
  "@nuxt/eslint-config": "^1.15.2",
54
55
  "@nuxt/kit": "^4.4.6",
@@ -145,6 +146,6 @@
145
146
  "doc:dev": "concurrently \"pnpm doc:watch\" \"pnpm doc:serve\"",
146
147
  "doc:check-invalid": "typedoc --listInvalidSymbolLinks",
147
148
  "actions:debug": "act -r -v -j release",
148
- "gen:exports": "indexit update --ignore **/*.d.ts **.vue src/assets/** -o '${path}.js'"
149
+ "gen:exports": "indexit update --ignore **/*.d.ts **.vue src/assets/** **/demo/** **/dist/** -o '${path}.js'"
149
150
  }
150
151
  }
@@ -6,7 +6,7 @@
6
6
  z-20
7
7
  bg-accent-500
8
8
  cursor-pointer
9
- [&.request-split]:hidden
9
+ [.request-split_&]:hidden
10
10
  `,
11
11
  ($attrs as any).class
12
12
  )"
@@ -7,8 +7,7 @@
7
7
  drag-edge
8
8
  z-20
9
9
  hover:cursor-pointer
10
-
11
- [&.request-split]:hidden
10
+ [.request-split_&]:hidden
12
11
  `,
13
12
  edge.error && `
14
13
  drag-edge-errored
@@ -7,7 +7,7 @@
7
7
  flex
8
8
  flex-col
9
9
  `,
10
- isDragging && `dragging cursor-pointer`,
10
+ isDragging && `dragging cursor-pointer user-select-none pointer-events-none`,
11
11
  requestType && `request-${requestType}`,
12
12
  ($attrs as any).class
13
13
  )"
@@ -70,7 +70,7 @@
70
70
  <script lang="ts" setup>
71
71
  import { useGlobalResizeObserver } from "@witchcraft/ui/composables/useGlobalResizeObserver"
72
72
  import { twMerge } from "@witchcraft/ui/utils/twMerge"
73
- import { computed, provide, reactive, ref, useAttrs, watch } from "vue"
73
+ import { computed, markRaw, provide, reactive, ref, useAttrs, watch } from "vue"
74
74
 
75
75
 
76
76
  import { useFrames } from "../composables/useFrames.js"
@@ -90,6 +90,8 @@ const $attrs = useAttrs()
90
90
  const win = defineModel<LayoutWindow>("win", { required: true })
91
91
 
92
92
  const props = withDefaults(defineProps<{
93
+ /** If you really need it, you can provide your own drag action handler (remember to markRaw it), note the actionHandler's prop will no longer have any effect.*/
94
+ dragActionHandler?: DragActionHandler<any, any>
93
95
  /** Custom drag action handlers. Falls back to default split/close/frame handlers if not provided. */
94
96
  actionHandlers?: IDragAction[]
95
97
  textHints?: {text:string, classes?:string}[]
@@ -120,9 +122,9 @@ const windowEl = ref<HTMLElement | null>(null)
120
122
 
121
123
  const requestType = ref<string | undefined | string>()
122
124
 
123
- const dragActionHandler = new DragActionHandler(
125
+ const dragActionHandler = props.dragActionHandler ?? markRaw(new DragActionHandler(
124
126
  [
125
- ...(props.actionHandlers ?? createDefaultHandlers()),
127
+ ...(props.actionHandlers ?? createDefaultHandlers())
126
128
  ],
127
129
  {
128
130
  onEvent: (e, cancel) => {
@@ -132,14 +134,14 @@ const dragActionHandler = new DragActionHandler(
132
134
  }
133
135
  },
134
136
  onRequestChange: type => {
135
- requestType.value = type
137
+ requestType.value = type as any
136
138
  },
137
139
  onEnd: () => {
138
140
  requestType.value = undefined
139
141
  showDragging.value = true
140
142
  },
141
143
  }
142
- )
144
+ ))
143
145
  dragActionHandler.shapes = reactive([])
144
146
  dragActionHandler.textHints = reactive({ actions: [], errors: [] })
145
147
  const shapes = dragActionHandler.shapes
@@ -12,30 +12,11 @@ import { moveEdge } from "../helpers/moveEdge.js"
12
12
  import { toWindowCoord } from "../helpers/toWindowCoord.js"
13
13
  import { findFramesTouchingEdge } from "../layout/findFramesTouchingEdge.js"
14
14
  import { isPointInRect } from "../layout/isPointInRect.js"
15
- import type { Direction, DragChangeHandler, DragChangeResult, DragState, Edge, EdgeDragStartData, FrameDragStartData, FrameId, IntersectionEntry, LayoutFrame, LayoutWindow, Orientation, Point } from "../types/index.js"
15
+ import type { ActionHandler, Direction, DragState, Edge, EdgeDragStartData, FrameDragStartData, FrameId, IntersectionEntry, LayoutFrame, LayoutWindow, Orientation, Point } from "../types/index.js"
16
16
 
17
17
  export function useFrames(
18
18
  win: Ref<LayoutWindow>,
19
- handler: {
20
- eventHandler: (e: KeyboardEvent, state: DragState, forceRecalculateEdges: () => void) => void
21
- /**
22
- * Called when the drag coordinates change (during any event). Should return true to allow the edges to be updated/moved, or false to prevent it.
23
- *
24
- * Can return anything for the end event as it's ignored.
25
- *
26
- * Can be used to save some context/info to later apply safely during onDragApply.
27
- */
28
- onDragChange: (...args: Parameters<DragChangeHandler>) => DragChangeResult
29
- /**
30
- * Called when drag will be applied. If dragEnd was called with apply false, it will not be called.
31
- * Return false to not apply the regular drag end changes (i.e. return false to reset to the position before dragging).
32
- */
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
38
- }
19
+ handler: ActionHandler
39
20
  ) {
40
21
  const draggingEdges = ref<Edge[]>([])
41
22
 
@@ -59,10 +40,14 @@ export function useFrames(
59
40
 
60
41
  const dragDirections = ref<Record<Orientation, Direction | undefined>>({} as any)
61
42
 
43
+ let dragStartPoint: Point | undefined
44
+ const dragDistance = ref(-1)
45
+
62
46
  const draggingIntersection = ref<IntersectionEntry | undefined>(undefined)
63
47
  const isDraggingFromWindowEdge = ref<boolean>(false)
64
48
 
65
49
  const frameDragFrameId = ref<FrameId | undefined>()
50
+ const eventContext = ref<Record<string, unknown> | undefined>()
66
51
 
67
52
  const frames = computed(() =>
68
53
  isDragging.value && showDragging.value
@@ -110,6 +95,7 @@ export function useFrames(
110
95
  const state = computed(() => ({
111
96
  dragDirections: dragDirections.value,
112
97
  dragPoint: dragPoint.value,
98
+ dragDistance: dragDistance.value,
113
99
  isDragging: isDragging.value,
114
100
  showDragging: showDragging.value,
115
101
  draggingFrameId: frameDragFrameId.value,
@@ -122,18 +108,29 @@ export function useFrames(
122
108
  dragHoveredFrame: dragHoveredFrame.value,
123
109
  intersections: intersections.value,
124
110
  isDraggingFromWindowEdge: isDraggingFromWindowEdge.value,
111
+ eventContext: eventContext.value,
125
112
  win: win.value
126
113
  } satisfies DragState))
127
114
 
128
115
  let controller: AbortController
116
+ let dragResolve: ((result: any) => void) | undefined
117
+ let dragResult: any
129
118
 
130
119
  function resetState(): void {
120
+ dragStartPoint = undefined
121
+
122
+ dragResolve?.(dragResult)
123
+ dragResolve = undefined
124
+ dragResult = undefined
125
+
131
126
  draggingEdges.value = []
132
127
  draggingIntersection.value = undefined
133
128
  isDragging.value = false
134
129
  dragPoint.value = undefined
130
+ dragDistance.value = -1
135
131
  touchingFrames.value = []
136
132
  frameDragFrameId.value = undefined
133
+ eventContext.value = undefined
137
134
  dragDirStore.reset()
138
135
  showDragging.value = false
139
136
  forceRecalculateEdges()
@@ -143,18 +140,38 @@ export function useFrames(
143
140
  e: PointerEvent,
144
141
  type: T,
145
142
  // someday this will work
146
- data: T extends "edge" ? EdgeDragStartData : FrameDragStartData
147
- ): void {
143
+ data: T extends "edge" ? EdgeDragStartData : FrameDragStartData,
144
+ {
145
+ moveEvent = "pointermove",
146
+ endEvent = "pointerup",
147
+ context
148
+ }: {
149
+ moveEvent?: string
150
+ endEvent?: string
151
+ context?: Record<string, unknown>
152
+ } = {}
153
+ ): Promise<any> {
154
+ // if already dragging, abort previous (resetState resolves the old promise)
155
+ if (controller) {
156
+ controller.abort()
157
+ }
158
+
159
+ const promise = new Promise<any>(resolve => {
160
+ dragResolve = resolve
161
+ })
162
+
148
163
  controller = new AbortController()
149
164
  controller.signal.addEventListener("abort", () => resetState())
150
165
 
151
166
  e.preventDefault()
152
- window.addEventListener("pointermove", dragMove, { signal: controller.signal })
153
- window.addEventListener("pointerup", dragEnd, { signal: controller.signal })
167
+ window.addEventListener(moveEvent, dragMove as EventListener, { signal: controller.signal })
168
+ window.addEventListener(endEvent, dragEnd as EventListener, { signal: controller.signal })
154
169
 
155
170
  const point = toWindowCoord(win.value, e)
156
171
  dragPoint.value = point
172
+ dragStartPoint = { ...point }
157
173
  dragDirStore.update(point)
174
+ eventContext.value = context
158
175
 
159
176
  isDragging.value = type
160
177
  showDragging.value = true
@@ -166,7 +183,7 @@ export function useFrames(
166
183
  draggingIntersection.value = intersection
167
184
 
168
185
  draggingEdges.value = edge
169
- ? [edge]
186
+ ? [edge!]
170
187
  : [
171
188
  ...(draggingIntersection.value?.sharedEdges.horizontal ?? []),
172
189
  ...(draggingIntersection.value?.sharedEdges.vertical ?? [])
@@ -198,6 +215,7 @@ export function useFrames(
198
215
 
199
216
  const res = handler.onDragChange("start", e, state.value, forceRecalculateEdges, cancel)
200
217
  showDragging.value = res.showDragging ?? true
218
+ return promise
201
219
  }
202
220
 
203
221
  function dragMove(e: PointerEvent): void {
@@ -205,6 +223,13 @@ export function useFrames(
205
223
  const point = toWindowCoord(win.value, e)
206
224
  const didChange = dragDirStore.update(point)
207
225
  dragPoint.value = point
226
+ // at least one drag move has happened
227
+ dragDistance.value = 0
228
+ if (dragStartPoint) {
229
+ dragDistance.value = Math.sqrt(
230
+ Math.pow(point.x - dragStartPoint.x, 2) + Math.pow(point.y - dragStartPoint.y, 2)
231
+ )
232
+ }
208
233
  if (!didChange) return
209
234
  const res = handler.onDragChange("move", e, state.value, forceRecalculateEdges, cancel)
210
235
 
@@ -231,17 +256,20 @@ export function useFrames(
231
256
  dragPoint.value = point
232
257
  }
233
258
 
234
- const doApply = apply && handler.onDragApply(state.value, forceRecalculateEdges)
235
- if (doApply) {
236
- for (const frame of touchingFramesArrays.value.flat()) {
237
- win!.value.frames[frame.id] = frame
259
+ if (apply) {
260
+ const applyResult = handler.onDragApply(state.value, forceRecalculateEdges)
261
+ dragResult = applyResult.result
262
+ if (applyResult.apply) {
263
+ for (const frame of touchingFramesArrays.value.flat()) {
264
+ win!.value.frames[frame.id] = frame
265
+ }
238
266
  }
239
267
  }
240
268
 
241
269
  handler.onDragChange("end", e, state.value, forceRecalculateEdges, cancel)
242
270
 
243
271
  // this can get called from elsewhere
244
- // also takes care of cleanup
272
+ // also takes care of cleanup nd resolving promise
245
273
  controller?.abort()
246
274
  }
247
275
  function cancel(): void {
@@ -281,6 +309,7 @@ export function useFrames(
281
309
  forceRecalculateEdges,
282
310
  state,
283
311
  showDragging,
284
- frameDragFrameId
312
+ frameDragFrameId,
313
+ actionHandler: handler
285
314
  }
286
315
  }
@@ -5,3 +5,7 @@
5
5
 
6
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
7
  @source "../../../src/runtime/demo";
8
+
9
+ button {
10
+ cursor: pointer;
11
+ }
@@ -13,6 +13,7 @@ export type CloseInfo = Exclude<ReturnType<typeof getCloseFrameInfo>, KnownError
13
13
 
14
14
  export class CloseAction implements IDragAction {
15
15
  name = "close" as const
16
+ minDragDistance = 5
16
17
 
17
18
  state: {
18
19
  allowed: true
@@ -62,12 +63,15 @@ export class CloseAction implements IDragAction {
62
63
  config?: {
63
64
  debug?: boolean | string
64
65
  closeHints?: Partial<CloseAction["closeHints"]>
66
+ /** Minimum pixel distance the user must drag before the action is allowed (decos shown and action applied). Defaults to 5. */
67
+ minDragDistance?: number
65
68
  }
66
69
  ) {
67
70
  if (handleEvent !== undefined) this.handleEvent = handleEvent
68
71
  if (modifyDecos !== undefined) this.modifyDecos = modifyDecos
69
72
  this.hooks = hooks
70
73
  if (config?.debug) this.debug = true
74
+ if (config?.minDragDistance !== undefined) this.minDragDistance = config.minDragDistance
71
75
  this.reset()
72
76
  }
73
77
 
@@ -151,6 +155,9 @@ export class CloseAction implements IDragAction {
151
155
  _e: PointerEvent | undefined,
152
156
  state: DragState
153
157
  ): ActionDragChangeResult {
158
+ if (state.dragDistance <= this.minDragDistance) {
159
+ return { updateEdges: false, shapes: [] }
160
+ }
154
161
  const {
155
162
  touchingFramesArrays,
156
163
  dragDirections,
@@ -191,14 +191,14 @@ export class DragActionHandler<
191
191
  onDragApply(
192
192
  state: DragState,
193
193
  forceRecalculateEdges: () => void
194
- ): boolean {
194
+ ): { apply: boolean, result: any } {
195
195
  if (this.activeAction) {
196
196
  const res = this.actions[this.activeAction]!.onDragApply(state, forceRecalculateEdges)
197
197
  this.hooks.onEnd?.({ cancelled: false, applied: res })
198
- return false
198
+ return { apply: res, result: undefined }
199
199
  }
200
200
  this.hooks.onEnd?.({ cancelled: false, applied: false })
201
- return true
201
+ return { apply: true, result: undefined }
202
202
  }
203
203
 
204
204
  cancel(e: PointerEvent | KeyboardEvent | undefined, state: DragState): void {