@witchcraft/layout 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@witchcraft/layout",
3
3
  "configKey": "witchcraftLayout",
4
- "version": "0.5.0",
4
+ "version": "0.5.1",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
@@ -102,6 +102,7 @@ export function useFrames(win, handler) {
102
102
  moveDirStore.reset();
103
103
  showMoving.value = false;
104
104
  forceRecalculateEdges();
105
+ handler.onMoveEnded();
105
106
  }
106
107
  function moveStart(e, type, data, {
107
108
  moveEvent = "pointermove",
@@ -169,10 +170,13 @@ export function useFrames(win, handler) {
169
170
  );
170
171
  }
171
172
  if (!didChange) return;
172
- const res = handler.onMoveChange("move", e, state.value, forceRecalculateEdges, cancel, resolve);
173
+ onMoveChange(e, "move", point);
174
+ }
175
+ function onMoveChange(e, type, point) {
176
+ const res = handler.onMoveChange(type, e, state.value, forceRecalculateEdges, cancel, type === "end" ? void 0 : resolve);
173
177
  showMoving.value = res.showMoving ?? true;
174
178
  if (!res.updateEdges) return;
175
- if (isMoving.value === "edge") {
179
+ if (isMoving.value === "edge" && point) {
176
180
  requestAnimationFrame(() => {
177
181
  for (let i = 0; i < movingEdges.value.length; i++) {
178
182
  const movingEdge = movingEdges.value[i];
@@ -189,6 +193,7 @@ export function useFrames(win, handler) {
189
193
  const point = toWindowCoord(win.value, e);
190
194
  movePoint.value = point;
191
195
  }
196
+ onMoveChange(e, "end", movePoint.value);
192
197
  if (apply) {
193
198
  const applyResult = handler.onMoveApply(state.value, forceRecalculateEdges);
194
199
  moveResult = applyResult.result;
@@ -198,7 +203,6 @@ export function useFrames(win, handler) {
198
203
  }
199
204
  }
200
205
  }
201
- handler.onMoveChange("end", e, state.value, forceRecalculateEdges, void 0, void 0);
202
206
  controller?.abort();
203
207
  }
204
208
  function cancel() {
@@ -45,7 +45,8 @@ export declare class ActionHandler<TRawActions extends IAction[], TActions exten
45
45
  */
46
46
  defaultOnMoveChange?: MoveChangeHandler);
47
47
  eventHandler(e: KeyboardEvent | PointerEvent, state: MoveState, forceRecalculateEdges: () => void): undefined;
48
- onMoveChange<T extends "start" | "move" | "end">(type: T, e: T extends "end" ? PointerEvent | undefined : PointerEvent, state: MoveState, forceRecalculateEdges: () => void, cancel: T extends "end" ? undefined : (e: PointerEvent | KeyboardEvent | undefined, state: MoveState) => void, resolve: T extends "end" ? undefined : (opts: ActionHandlerApplyResult) => void): MoveChangeResult;
48
+ onMoveChange<T extends "start" | "move" | "end">(type: T, e: T extends "end" ? PointerEvent | undefined : PointerEvent, state: MoveState, forceRecalculateEdges: () => void, cancel: (e: PointerEvent | KeyboardEvent | undefined, state: MoveState) => void, resolve: T extends "end" ? undefined : (opts: ActionHandlerApplyResult) => void): MoveChangeResult;
49
+ onMoveEnded(): void;
49
50
  setTextHints(type: "start" | "move" | "end"): void;
50
51
  annotateEdges(edges: Edge[], frames: LayoutFrame[]): void;
51
52
  onMoveApply(state: MoveState, forceRecalculateEdges: () => void): ActionHandlerApplyResult;
@@ -82,11 +82,14 @@ export class ActionHandler {
82
82
  this.setTextHints(type);
83
83
  const res = this.defaultOnMoveChange(type, e, state, forceRecalculateEdges, this.boundCancel, resolve);
84
84
  this.shapes.splice(0, this.shapes.length, ...res.shapes);
85
- if (type === "end") {
86
- this.activeAction = void 0;
87
- }
88
85
  return res;
89
86
  }
87
+ onMoveEnded() {
88
+ if (this.activeAction) {
89
+ this.actions[this.activeAction].onMoveEnded();
90
+ }
91
+ this.activeAction = void 0;
92
+ }
90
93
  setTextHints(type) {
91
94
  this.textHints.actions = [];
92
95
  this.textHints.errors = [];
@@ -122,7 +125,7 @@ export class ActionHandler {
122
125
  if (this.activeAction) {
123
126
  const res = this.actions[this.activeAction].onMoveApply(state, forceRecalculateEdges);
124
127
  this.hooks.onEnd?.({ cancelled: false, applied: res });
125
- return { apply: res, result: void 0 };
128
+ return { apply: !res, result: void 0 };
126
129
  }
127
130
  this.hooks.onEnd?.({ cancelled: false, applied: false });
128
131
  return { apply: true, result: void 0 };
@@ -55,4 +55,5 @@ export declare class CloseAction implements IAction {
55
55
  onMoveChange(type: "start" | "end" | "move", _e: PointerEvent | undefined, state: MoveState): ActionChangeResult;
56
56
  onMoveApply(state: MoveState): boolean;
57
57
  cancel(): void;
58
+ onMoveEnded(): void;
58
59
  }
@@ -99,10 +99,6 @@ export class CloseAction {
99
99
  return false;
100
100
  }
101
101
  onMoveChange(type, _e, state) {
102
- if (type === "end") {
103
- this.reset();
104
- return { shapes: [] };
105
- }
106
102
  if (state.moveDistance <= this.minDragDistance) {
107
103
  return { updateEdges: false, shapes: [] };
108
104
  }
@@ -152,7 +148,10 @@ export class CloseAction {
152
148
  }
153
149
  }
154
150
  const decos = this._getDecos(state);
155
- this.state.lastReturn = { updateEdges: !isMovingFromWindowEdge, shapes: decos.flatMap((_) => _.shapes) };
151
+ this.state.lastReturn = {
152
+ updateEdges: !isMovingFromWindowEdge,
153
+ shapes: type === "end" ? [] : decos.flatMap((_) => _.shapes)
154
+ };
156
155
  return this.state.lastReturn;
157
156
  }
158
157
  onMoveApply(state) {
@@ -170,7 +169,9 @@ export class CloseAction {
170
169
  return false;
171
170
  }
172
171
  cancel() {
173
- this.reset();
174
172
  this.hooks.onCancel?.();
175
173
  }
174
+ onMoveEnded() {
175
+ this.reset();
176
+ }
176
177
  }
@@ -46,5 +46,6 @@ export declare class FrameDragAction implements IAction {
46
46
  canHandleRequest(e: PointerEvent | KeyboardEvent, state: MoveState): boolean;
47
47
  onMoveChange<T extends "start" | "move" | "end">(type: T, _e: PointerEvent | undefined, state: MoveState): ActionChangeResult;
48
48
  onMoveApply(state: MoveState): boolean;
49
+ onMoveEnded(): void;
49
50
  cancel(): void;
50
51
  }
@@ -94,10 +94,6 @@ export class FrameDragAction {
94
94
  return false;
95
95
  }
96
96
  onMoveChange(type, _e, state) {
97
- if (type === "end") {
98
- this.reset();
99
- return { shapes: [] };
100
- }
101
97
  if (state.moveDistance <= this.minDragDistance) {
102
98
  return { updateEdges: false, shapes: [], showMoving: false };
103
99
  }
@@ -119,7 +115,7 @@ export class FrameDragAction {
119
115
  this.setTextHints(this.state.lastReturn);
120
116
  const decos = this.getDecos(matchedZone, state, this.state.lastReturn);
121
117
  return {
122
- shapes: decos.flatMap((_) => _.shapes),
118
+ shapes: type === "end" ? [] : decos.flatMap((_) => _.shapes),
123
119
  updateEdges: false,
124
120
  showMoving: false
125
121
  };
@@ -142,6 +138,9 @@ export class FrameDragAction {
142
138
  }
143
139
  return true;
144
140
  }
141
+ onMoveEnded() {
142
+ this.reset();
143
+ }
145
144
  cancel() {
146
145
  this.reset();
147
146
  }
@@ -52,5 +52,6 @@ export declare class SplitAction implements IAction {
52
52
  calculateSplitRequest(state: MoveState): boolean;
53
53
  onMoveChange(type: "start" | "end" | "move", _e: PointerEvent | undefined, state: MoveState): ActionChangeResult;
54
54
  onMoveApply(state: MoveState): boolean;
55
+ onMoveEnded(): void;
55
56
  cancel(): void;
56
57
  }
@@ -119,10 +119,6 @@ export class SplitAction {
119
119
  }
120
120
  }
121
121
  onMoveChange(type, _e, state) {
122
- if (type === "end") {
123
- this.reset();
124
- return { shapes: [] };
125
- }
126
122
  const { moveHoveredFrame, moveDistance } = state;
127
123
  if (moveDistance <= this.minDragDistance) {
128
124
  return { updateEdges: false, shapes: [], showMoving: false };
@@ -147,11 +143,19 @@ export class SplitAction {
147
143
  },
148
144
  attrs: { class: "deco-split-error bg-red-500/50" }
149
145
  };
150
- this.state.lastReturn = { updateEdges: true, shapes: [errorDeco], showMoving: false };
146
+ this.state.lastReturn = {
147
+ updateEdges: true,
148
+ shapes: type === "end" ? [] : [errorDeco],
149
+ showMoving: false
150
+ };
151
151
  return this.state.lastReturn;
152
152
  }
153
153
  }
154
- this.state.lastReturn = { updateEdges: false, shapes: decos.flatMap((_) => _.shapes), showMoving: false };
154
+ this.state.lastReturn = {
155
+ updateEdges: false,
156
+ shapes: type === "end" ? [] : decos.flatMap((_) => _.shapes),
157
+ showMoving: false
158
+ };
155
159
  return this.state.lastReturn;
156
160
  }
157
161
  onMoveApply(state) {
@@ -169,8 +173,10 @@ export class SplitAction {
169
173
  }
170
174
  return true;
171
175
  }
172
- cancel() {
176
+ onMoveEnded() {
173
177
  this.reset();
178
+ }
179
+ cancel() {
174
180
  this.hooks.onCancel?.();
175
181
  }
176
182
  }
@@ -1917,9 +1917,9 @@ export type MoveChangeResult = Omit<ActionChangeResult, "shapes">;
1917
1917
  * See also {@link ActionHandler.onMoveChange} to understand it's lifecycle as it is the extended version of.
1918
1918
  */
1919
1919
  export type MoveChangeHandler = <T extends "start" | "move" | "end">(type: T, e: T extends "end" ? PointerEvent | undefined : PointerEvent, state: MoveState, forceRecalculateEdges: () => void,
1920
- /** Calls moveEnd with apply: false */
1921
- cancel: T extends "end" ? undefined : (e: PointerEvent | KeyboardEvent | undefined, state: MoveState) => void,
1922
- /** Saves result to resolve moveStart promise with then calls moveEnd with given apply. */
1920
+ /** Calls moveEnd with apply: false. This can technically be called from "end", it should still work. */
1921
+ cancel: (e: PointerEvent | KeyboardEvent | undefined, state: MoveState) => void,
1922
+ /** Saves result to resolve moveStart promise with then calls moveEnd with given apply. Not available during "end" event. It's designed for resolving from other external evente (e.g. key events). */
1923
1923
  resolve: T extends "end" ? undefined : ({ apply, result }: {
1924
1924
  apply: boolean;
1925
1925
  result: any;
@@ -1977,6 +1977,10 @@ export interface IAction {
1977
1977
  /** Error texts/hints to display when the action produces an error. */
1978
1978
  errors?: string[];
1979
1979
  };
1980
+ /**
1981
+ * Called when an action is cancelled. Call action specific onCancel hooks here.
1982
+ */
1983
+ cancel(e: PointerEvent | KeyboardEvent | undefined, state: MoveState): void;
1980
1984
  /**
1981
1985
  * Called after visual edges are recalculated, once per edge. Action handlers can annotate edges with error info here ({@link Edge.error}).
1982
1986
  *
@@ -2017,24 +2021,27 @@ export type ActionHandlerApplyResult = {
2017
2021
  export interface ActionHandler {
2018
2022
  eventHandler: (e: KeyboardEvent, state: MoveState, forceRecalculateEdges: () => void) => void;
2019
2023
  /**
2020
- * 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. Note that the return only affects the move event but it's typed like this for ease of use. See also {@link MoveChangeHandler}.
2024
+ * 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. See also {@link MoveChangeHandler} for the built in action handler.
2021
2025
  *
2022
2026
  * Can be used to save some context/info to later apply safely during onMoveApply.
2023
2027
  *
2024
2028
  * The call order is:
2025
2029
  * - onMoveChange("start", ...)
2026
2030
  * - onMoveChange("move", ...)
2031
+ * - onMoveChange("end", ...)
2027
2032
  * - onMoveApply(...) (IF moveEnd was called with apply: true, otherwise this is skipped)
2028
- * - onMoveChange("end", ...) // always called last, do cleanup here
2033
+ * - onMoveEnded() // do cleanup here
2029
2034
  */
2030
2035
  onMoveChange: (...args: Parameters<MoveChangeHandler>) => MoveChangeResult;
2031
2036
  /**
2032
2037
  * Called when drag will be applied. If moveEnd was called with apply false, it will not be called.
2033
2038
  * Return false to not apply the regular drag end changes (i.e. return false to reset to the position before dragging).
2034
2039
  *
2035
- * Do not use for resetting handler state, use onMoveChange("end", ...) for that.
2040
+ * Do not use for resetting handler state, use onMoveEnded for that.
2036
2041
  */
2037
2042
  onMoveApply: (state: MoveState, forceRecalculateEdges: () => void) => ActionHandlerApplyResult;
2043
+ /** For doing cleanup */
2044
+ onMoveEnded: () => void;
2038
2045
  /**
2039
2046
  * Called after visual edges are recalculated. Action handlers can annotate edges with error info.
2040
2047
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@witchcraft/layout",
3
3
  "description": "Headless layout manager.",
4
- "version": "0.5.0",
4
+ "version": "0.5.1",
5
5
  "main": "./dist/runtime/index.js",
6
6
  "type": "module",
7
7
  "sideEffects": false,
@@ -134,6 +134,7 @@ export function useFrames(
134
134
  moveDirStore.reset()
135
135
  showMoving.value = false
136
136
  forceRecalculateEdges()
137
+ handler.onMoveEnded()
137
138
  }
138
139
 
139
140
  function moveStart<T extends "edge" | "frame">(
@@ -231,13 +232,17 @@ export function useFrames(
231
232
  )
232
233
  }
233
234
  if (!didChange) return
234
- const res = handler.onMoveChange("move", e, state.value, forceRecalculateEdges, cancel, resolve)
235
+ onMoveChange(e, "move", point)
236
+ }
237
+
238
+ function onMoveChange(e: PointerEvent | undefined, type: "move" | "end", point: Point | undefined) {
239
+ const res = handler.onMoveChange(type, e, state.value, forceRecalculateEdges, cancel, type === "end" ? undefined : resolve)
235
240
 
236
241
 
237
242
  showMoving.value = res.showMoving ?? true
238
243
  if (!res.updateEdges) return
239
244
 
240
- if (isMoving.value === "edge") {
245
+ if (isMoving.value === "edge" && point) {
241
246
  requestAnimationFrame(() => {
242
247
  for (let i = 0; i < movingEdges.value.length; i++) {
243
248
  const movingEdge = movingEdges.value[i]
@@ -256,6 +261,8 @@ export function useFrames(
256
261
  movePoint.value = point
257
262
  }
258
263
 
264
+ onMoveChange(e, "end", movePoint.value)
265
+
259
266
  if (apply) {
260
267
  const applyResult = handler.onMoveApply(state.value, forceRecalculateEdges)
261
268
  moveResult = applyResult.result
@@ -266,10 +273,8 @@ export function useFrames(
266
273
  }
267
274
  }
268
275
 
269
- handler.onMoveChange("end", e, state.value, forceRecalculateEdges, undefined, undefined)
270
-
271
276
  // this can get called from elsewhere
272
- // also takes care of cleanup nd resolving promise
277
+ // also takes care of cleanup, resolving promise, and calling onMoveEnded
273
278
  controller?.abort()
274
279
  }
275
280
 
@@ -128,7 +128,7 @@ export class ActionHandler<
128
128
  e: T extends "end" ? PointerEvent | undefined : PointerEvent,
129
129
  state: MoveState,
130
130
  forceRecalculateEdges: () => void,
131
- cancel: T extends "end" ? undefined : (e: PointerEvent | KeyboardEvent | undefined, state: MoveState) => void,
131
+ cancel: (e: PointerEvent | KeyboardEvent | undefined, state: MoveState) => void,
132
132
  resolve: T extends "end" ? undefined : (opts: ActionHandlerApplyResult) => void
133
133
  ): MoveChangeResult {
134
134
  if (type === "start") {
@@ -137,7 +137,7 @@ export class ActionHandler<
137
137
  }
138
138
 
139
139
  if (this.activeAction) {
140
- const res = this.actions[this.activeAction]!.onMoveChange(type, e, state, forceRecalculateEdges, this.boundCancel as any, resolve as any)
140
+ const res = this.actions[this.activeAction]!.onMoveChange(type, e, state, forceRecalculateEdges, this.boundCancel as any, resolve)
141
141
  // in case it's a vue reactive array
142
142
  this.shapes.splice(0, this.shapes.length, ...res.shapes)
143
143
  this.setTextHints(type)
@@ -147,12 +147,17 @@ export class ActionHandler<
147
147
  const res = this.defaultOnMoveChange(type, e, state, forceRecalculateEdges, this.boundCancel as any, resolve as any)
148
148
  this.shapes.splice(0, this.shapes.length, ...res.shapes)
149
149
 
150
- if (type === "end") {
151
- this.activeAction = undefined
152
- }
153
150
  return res
154
151
  }
155
152
 
153
+ onMoveEnded() {
154
+ if (this.activeAction) {
155
+ this.actions[this.activeAction]!.onMoveEnded()
156
+ }
157
+
158
+ this.activeAction = undefined
159
+ }
160
+
156
161
  setTextHints(type: "start" | "move" | "end") {
157
162
  // again in case it's a vue reactive object
158
163
  this.textHints.actions = []
@@ -197,7 +202,7 @@ export class ActionHandler<
197
202
  if (this.activeAction) {
198
203
  const res = this.actions[this.activeAction]!.onMoveApply(state, forceRecalculateEdges)
199
204
  this.hooks.onEnd?.({ cancelled: false, applied: res })
200
- return { apply: res, result: undefined }
205
+ return { apply: !res, result: undefined }
201
206
  }
202
207
  this.hooks.onEnd?.({ cancelled: false, applied: false })
203
208
  return { apply: true, result: undefined }
@@ -155,10 +155,6 @@ export class CloseAction implements IAction {
155
155
  _e: PointerEvent | undefined,
156
156
  state: MoveState
157
157
  ): ActionChangeResult {
158
- if (type === "end") {
159
- this.reset()
160
- return { shapes: [] }
161
- }
162
158
  if (state.moveDistance <= this.minDragDistance) {
163
159
  return { updateEdges: false, shapes: [] }
164
160
  }
@@ -209,7 +205,10 @@ export class CloseAction implements IAction {
209
205
  }
210
206
  }
211
207
  const decos = this._getDecos(state)
212
- this.state.lastReturn = { updateEdges: !isMovingFromWindowEdge, shapes: decos.flatMap(_ => _.shapes) }
208
+ this.state.lastReturn = {
209
+ updateEdges: !isMovingFromWindowEdge,
210
+ shapes: type === "end" ? [] : decos.flatMap(_ => _.shapes)
211
+ }
213
212
  return this.state.lastReturn
214
213
  }
215
214
 
@@ -225,7 +224,10 @@ export class CloseAction implements IAction {
225
224
  }
226
225
 
227
226
  cancel(): void {
228
- this.reset()
229
227
  this.hooks.onCancel?.()
230
228
  }
229
+
230
+ onMoveEnded() {
231
+ this.reset()
232
+ }
231
233
  }
@@ -155,10 +155,6 @@ export class FrameDragAction implements IAction {
155
155
  _e: PointerEvent | undefined,
156
156
  state: MoveState
157
157
  ): ActionChangeResult {
158
- if (type === "end") {
159
- this.reset()
160
- return { shapes: [] }
161
- }
162
158
  if (state.moveDistance <= this.minDragDistance) {
163
159
  return { updateEdges: false, shapes: [], showMoving: false }
164
160
  }
@@ -185,7 +181,7 @@ export class FrameDragAction implements IAction {
185
181
  const decos = this.getDecos(matchedZone, state, this.state.lastReturn)
186
182
 
187
183
  return {
188
- shapes: decos.flatMap(_ => _.shapes),
184
+ shapes: type === "end" ? [] : decos.flatMap(_ => _.shapes),
189
185
  updateEdges: false,
190
186
  showMoving: false
191
187
  }
@@ -208,6 +204,10 @@ export class FrameDragAction implements IAction {
208
204
  return true
209
205
  }
210
206
 
207
+ onMoveEnded() {
208
+ this.reset()
209
+ }
210
+
211
211
  cancel(): void {
212
212
  this.reset()
213
213
  }
@@ -178,10 +178,6 @@ export class SplitAction implements IAction {
178
178
  _e: PointerEvent | undefined,
179
179
  state: MoveState
180
180
  ): ActionChangeResult {
181
- if (type === "end") {
182
- this.reset()
183
- return { shapes: [] }
184
- }
185
181
  const { moveHoveredFrame, moveDistance } = state
186
182
  if (moveDistance <= this.minDragDistance) {
187
183
  return { updateEdges: false, shapes: [], showMoving: false }
@@ -207,11 +203,19 @@ export class SplitAction implements IAction {
207
203
  },
208
204
  attrs: { class: "deco-split-error bg-red-500/50" }
209
205
  }
210
- this.state.lastReturn = { updateEdges: true, shapes: [errorDeco], showMoving: false }
206
+ this.state.lastReturn = {
207
+ updateEdges: true,
208
+ shapes: type === "end" ? [] : [errorDeco],
209
+ showMoving: false
210
+ }
211
211
  return this.state.lastReturn
212
212
  }
213
213
  }
214
- this.state.lastReturn = { updateEdges: false, shapes: decos.flatMap(_ => _.shapes), showMoving: false }
214
+ this.state.lastReturn = {
215
+ updateEdges: false,
216
+ shapes: type === "end" ? [] : decos.flatMap(_ => _.shapes),
217
+ showMoving: false
218
+ }
215
219
  return this.state.lastReturn
216
220
  }
217
221
 
@@ -229,8 +233,11 @@ export class SplitAction implements IAction {
229
233
  return true
230
234
  }
231
235
 
232
- cancel(): void {
236
+ onMoveEnded() {
233
237
  this.reset()
238
+ }
239
+
240
+ cancel(): void {
234
241
  this.hooks.onCancel?.()
235
242
  }
236
243
  }
@@ -522,9 +522,9 @@ export type MoveChangeHandler = <T extends "start" | "move" | "end">(
522
522
  e: T extends "end" ? PointerEvent | undefined : PointerEvent,
523
523
  state: MoveState,
524
524
  forceRecalculateEdges: () => void,
525
- /** Calls moveEnd with apply: false */
526
- cancel: T extends "end" ? undefined : (e: PointerEvent | KeyboardEvent | undefined, state: MoveState) => void,
527
- /** Saves result to resolve moveStart promise with then calls moveEnd with given apply. */
525
+ /** Calls moveEnd with apply: false. This can technically be called from "end", it should still work. */
526
+ cancel: (e: PointerEvent | KeyboardEvent | undefined, state: MoveState) => void,
527
+ /** Saves result to resolve moveStart promise with then calls moveEnd with given apply. Not available during "end" event. It's designed for resolving from other external evente (e.g. key events). */
528
528
  resolve: T extends "end" ? undefined : ({ apply, result }: { apply: boolean, result: any }) => void
529
529
  ) => ActionChangeResult
530
530
 
@@ -587,6 +587,11 @@ export interface IAction {
587
587
  /** Error texts/hints to display when the action produces an error. */
588
588
  errors?: string[]
589
589
  }
590
+ /**
591
+ * Called when an action is cancelled. Call action specific onCancel hooks here.
592
+ */
593
+ // note: internally this is done by the actionhandler wrapping the given cancel function for an event
594
+ cancel(e: PointerEvent | KeyboardEvent | undefined, state: MoveState): void
590
595
  /**
591
596
  * Called after visual edges are recalculated, once per edge. Action handlers can annotate edges with error info here ({@link Edge.error}).
592
597
  *
@@ -624,27 +629,32 @@ export type ActionHandlerApplyResult = {
624
629
  export interface ActionHandler {
625
630
  eventHandler: (e: KeyboardEvent, state: MoveState, forceRecalculateEdges: () => void) => void
626
631
  /**
627
- * 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. Note that the return only affects the move event but it's typed like this for ease of use. See also {@link MoveChangeHandler}.
632
+ * 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. See also {@link MoveChangeHandler} for the built in action handler.
628
633
  *
629
634
  * Can be used to save some context/info to later apply safely during onMoveApply.
630
635
  *
631
636
  * The call order is:
632
637
  * - onMoveChange("start", ...)
633
638
  * - onMoveChange("move", ...)
639
+ * - onMoveChange("end", ...)
634
640
  * - onMoveApply(...) (IF moveEnd was called with apply: true, otherwise this is skipped)
635
- * - onMoveChange("end", ...) // always called last, do cleanup here
641
+ * - onMoveEnded() // do cleanup here
636
642
  */
637
643
  onMoveChange: (...args: Parameters<MoveChangeHandler>) => MoveChangeResult
638
644
  /**
639
645
  * Called when drag will be applied. If moveEnd was called with apply false, it will not be called.
640
646
  * Return false to not apply the regular drag end changes (i.e. return false to reset to the position before dragging).
641
647
  *
642
- * Do not use for resetting handler state, use onMoveChange("end", ...) for that.
648
+ * Do not use for resetting handler state, use onMoveEnded for that.
643
649
  */
644
650
  onMoveApply: (
645
651
  state: MoveState,
646
652
  forceRecalculateEdges: () => void
647
653
  ) => ActionHandlerApplyResult
654
+
655
+ /** For doing cleanup */
656
+ onMoveEnded: () => void
657
+
648
658
  /**
649
659
  * Called after visual edges are recalculated. Action handlers can annotate edges with error info.
650
660
  */