@weasel-js/routing 1.9.3 → 1.9.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.
- package/README.md +6 -5
- package/dist/{chunk-LOP2JTTA.js → chunk-Y5RURZBH.js} +235 -183
- package/dist/chunk-Y5RURZBH.js.map +1 -0
- package/dist/{index-CrbDNXT_.d.ts → index-gArM2tbt.d.ts} +363 -187
- package/dist/index.d.ts +3 -2
- package/dist/index.js +23 -20
- package/dist/index.js.map +1 -1
- package/dist/{internal-B1B6gfOS.d.ts → internal-B0SsznMG.d.ts} +9 -12
- package/dist/internal.d.ts +4 -3
- package/dist/react.d.ts +30 -73
- package/dist/react.js +410 -299
- package/dist/react.js.map +1 -1
- package/package.json +8 -7
- package/dist/chunk-LOP2JTTA.js.map +0 -1
package/dist/react.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { createDepRegistry, createDispatcher, isTool,
|
|
1
|
+
import { createDepRegistry, createDispatcher, isTool, assembleEntries, liveScope, isDev, reportRouteConflicts, offhandKeyFor, makeToolOffhandAction, buildToolOffhandBindings, dlog, openPointerSession, LONG_PRESS_MS, publishLiveDispatch, pointerGestureId, ScopeNode, pushOwner, validateActionId, validateActionDefaultBinding, depNodeOf, DepNode, clientToCanvasRect, buildDepsFromRequires, isEditableTarget, activatesFocusedControl, itemsFromClipboardData, itemsFromDataTransfer } from './chunk-Y5RURZBH.js';
|
|
2
2
|
import { createContext, useState, useContext, useEffect, useRef, useCallback, useMemo, useSyncExternalStore } from 'react';
|
|
3
3
|
import { useLatest, useStableByContent, sameList } from '@weasel-js/react';
|
|
4
4
|
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
5
|
-
import { isInControlWithin, pastDragThreshold } from '@weasel-js/gestures';
|
|
5
|
+
import { isApplePlatform, isInControlWithin, pastDragThreshold } from '@weasel-js/gestures';
|
|
6
6
|
import { resolveCursorTier } from '@weasel-js/cursor';
|
|
7
7
|
import { activeModeOf } from '@weasel-js/modes';
|
|
8
8
|
|
|
@@ -115,19 +115,8 @@ function useDepSource(name, source) {
|
|
|
115
115
|
return registry.register(name, () => sourceRef.current());
|
|
116
116
|
}, [name, registry, sourceRef]);
|
|
117
117
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
...typeof e.pressure === "number" ? { pressure: e.pressure } : {},
|
|
122
|
-
...typeof e.tiltX === "number" ? { tiltX: e.tiltX } : {},
|
|
123
|
-
...typeof e.tiltY === "number" ? { tiltY: e.tiltY } : {}
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
var IS_MAC = typeof navigator !== "undefined" && /mac/i.test(
|
|
127
|
-
// `||`, not `??`: jsdom (and some embedders) report an empty-string
|
|
128
|
-
// platform, which is not nullish and would swallow the UA fallback.
|
|
129
|
-
navigator.platform || navigator.userAgent
|
|
130
|
-
);
|
|
118
|
+
|
|
119
|
+
// src/interactions/dispatcher/dispatchCore.ts
|
|
131
120
|
function withViewDeps(base, overlay) {
|
|
132
121
|
return {
|
|
133
122
|
register: (name, source) => base().register(name, source),
|
|
@@ -138,6 +127,297 @@ function withViewDeps(base, overlay) {
|
|
|
138
127
|
}
|
|
139
128
|
};
|
|
140
129
|
}
|
|
130
|
+
|
|
131
|
+
// src/interactions/dispatcher/channels/contextMenu.ts
|
|
132
|
+
function contextMenuInput(core) {
|
|
133
|
+
const { routeAt, toWorld, target, dispatch } = core;
|
|
134
|
+
const onContextMenu = (e) => {
|
|
135
|
+
e.preventDefault();
|
|
136
|
+
routeAt(null, e.clientX, e.clientY);
|
|
137
|
+
const w = toWorld(e.clientX, e.clientY);
|
|
138
|
+
const affordance = target().affordanceAt?.(w) ?? void 0;
|
|
139
|
+
const menuBody = target().classifyTarget?.(w);
|
|
140
|
+
const ev = {
|
|
141
|
+
kind: "contextmenu",
|
|
142
|
+
target: e.target,
|
|
143
|
+
altKey: e.altKey,
|
|
144
|
+
ctrlKey: e.ctrlKey,
|
|
145
|
+
metaKey: e.metaKey,
|
|
146
|
+
shiftKey: e.shiftKey,
|
|
147
|
+
x: w.x,
|
|
148
|
+
y: w.y,
|
|
149
|
+
clientX: e.clientX,
|
|
150
|
+
clientY: e.clientY,
|
|
151
|
+
...affordance !== void 0 ? { affordance } : {},
|
|
152
|
+
...menuBody?.body !== void 0 ? { bodyTarget: menuBody.body } : {},
|
|
153
|
+
...menuBody?.kind !== void 0 ? { bodyKind: menuBody.kind } : {}
|
|
154
|
+
};
|
|
155
|
+
dispatch(ev);
|
|
156
|
+
};
|
|
157
|
+
return onContextMenu;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/interactions/dispatcher/channels/ingest.ts
|
|
161
|
+
var DROPOVER_CLASS = "weasel-dropover";
|
|
162
|
+
function ingestInput(core) {
|
|
163
|
+
const { canvas, routeAt, toWorld, dispatch } = core;
|
|
164
|
+
const onDragOver = (e) => {
|
|
165
|
+
if (!e.dataTransfer) return;
|
|
166
|
+
e.preventDefault();
|
|
167
|
+
e.dataTransfer.dropEffect = "copy";
|
|
168
|
+
canvas?.classList.add(DROPOVER_CLASS);
|
|
169
|
+
};
|
|
170
|
+
const onDragLeave = () => {
|
|
171
|
+
canvas?.classList.remove(DROPOVER_CLASS);
|
|
172
|
+
};
|
|
173
|
+
const onDrop = (e) => {
|
|
174
|
+
canvas?.classList.remove(DROPOVER_CLASS);
|
|
175
|
+
const dt = e.dataTransfer;
|
|
176
|
+
if (!dt) return;
|
|
177
|
+
e.preventDefault();
|
|
178
|
+
routeAt(null, e.clientX, e.clientY);
|
|
179
|
+
const dropView = core.routedView();
|
|
180
|
+
const w = toWorld(e.clientX, e.clientY);
|
|
181
|
+
const base = {
|
|
182
|
+
altKey: e.altKey,
|
|
183
|
+
ctrlKey: e.ctrlKey,
|
|
184
|
+
metaKey: e.metaKey,
|
|
185
|
+
shiftKey: e.shiftKey
|
|
186
|
+
};
|
|
187
|
+
void itemsFromDataTransfer(dt).then((items) => {
|
|
188
|
+
if (core.disposed()) return;
|
|
189
|
+
if (items.length === 0) return;
|
|
190
|
+
const routedNow = core.routedView();
|
|
191
|
+
core.setRoutedView(dropView);
|
|
192
|
+
try {
|
|
193
|
+
dispatch({
|
|
194
|
+
kind: "drop",
|
|
195
|
+
items,
|
|
196
|
+
x: w.x,
|
|
197
|
+
y: w.y,
|
|
198
|
+
clientX: e.clientX,
|
|
199
|
+
clientY: e.clientY,
|
|
200
|
+
...base
|
|
201
|
+
});
|
|
202
|
+
} finally {
|
|
203
|
+
core.setRoutedView(routedNow);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
const onPaste = (e) => {
|
|
208
|
+
if (e.defaultPrevented || isEditableTarget(e.target)) return;
|
|
209
|
+
if (!core.holdsKeys()) return;
|
|
210
|
+
const cd = e.clipboardData;
|
|
211
|
+
if (!cd) return;
|
|
212
|
+
const items = itemsFromClipboardData(cd);
|
|
213
|
+
if (items.length === 0) return;
|
|
214
|
+
const result = dispatch({
|
|
215
|
+
kind: "paste",
|
|
216
|
+
items,
|
|
217
|
+
// ClipboardEvent carries no modifier state.
|
|
218
|
+
altKey: false,
|
|
219
|
+
ctrlKey: false,
|
|
220
|
+
metaKey: false,
|
|
221
|
+
shiftKey: false
|
|
222
|
+
});
|
|
223
|
+
if (result === "handled") e.preventDefault();
|
|
224
|
+
};
|
|
225
|
+
return { onDragOver, onDragLeave, onDrop, onPaste };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/interactions/dispatcher/channels/keyboard.ts
|
|
229
|
+
function keyboardInput(core) {
|
|
230
|
+
const { dispatch, dispatcherNow } = core;
|
|
231
|
+
const heldKeys = /* @__PURE__ */ new Map();
|
|
232
|
+
const codeOf = (e) => e.code ? { code: e.code } : {};
|
|
233
|
+
const physicalKey = (e) => e.code || e.key.toLowerCase();
|
|
234
|
+
const onKeyDown = (e) => {
|
|
235
|
+
if (e.defaultPrevented || isEditableTarget(e.target) || activatesFocusedControl(e)) return;
|
|
236
|
+
if (!core.holdsKeys()) return;
|
|
237
|
+
if (e.repeat && heldKeys.has(physicalKey(e))) {
|
|
238
|
+
e.preventDefault();
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
const keyEv = {
|
|
242
|
+
kind: "key",
|
|
243
|
+
key: e.key,
|
|
244
|
+
...codeOf(e),
|
|
245
|
+
altKey: e.altKey,
|
|
246
|
+
ctrlKey: e.ctrlKey,
|
|
247
|
+
metaKey: e.metaKey,
|
|
248
|
+
shiftKey: e.shiftKey,
|
|
249
|
+
repeat: e.repeat
|
|
250
|
+
};
|
|
251
|
+
const keyResult = dispatch(keyEv);
|
|
252
|
+
const heldEv = {
|
|
253
|
+
kind: "key-held",
|
|
254
|
+
key: e.key,
|
|
255
|
+
...codeOf(e),
|
|
256
|
+
phase: "down",
|
|
257
|
+
altKey: e.altKey,
|
|
258
|
+
ctrlKey: e.ctrlKey,
|
|
259
|
+
metaKey: e.metaKey,
|
|
260
|
+
shiftKey: e.shiftKey
|
|
261
|
+
};
|
|
262
|
+
const heldOn = dispatcherNow();
|
|
263
|
+
const heldResult = dispatch(heldEv, heldOn);
|
|
264
|
+
if (heldResult === "handled") {
|
|
265
|
+
heldKeys.set(physicalKey(e), { key: e.key, ...codeOf(e), on: heldOn });
|
|
266
|
+
}
|
|
267
|
+
if (keyResult === "handled" || heldResult === "handled") {
|
|
268
|
+
e.preventDefault();
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
const onKeyUp = (e) => {
|
|
272
|
+
const physical = physicalKey(e);
|
|
273
|
+
const down = heldKeys.get(physical);
|
|
274
|
+
if (down === void 0) return;
|
|
275
|
+
const ev = {
|
|
276
|
+
kind: "key-held",
|
|
277
|
+
key: down.key,
|
|
278
|
+
...down.code !== void 0 ? { code: down.code } : {},
|
|
279
|
+
phase: "up",
|
|
280
|
+
altKey: e.altKey,
|
|
281
|
+
ctrlKey: e.ctrlKey,
|
|
282
|
+
metaKey: e.metaKey,
|
|
283
|
+
shiftKey: e.shiftKey
|
|
284
|
+
};
|
|
285
|
+
heldKeys.delete(physical);
|
|
286
|
+
dispatch(ev, down.on);
|
|
287
|
+
};
|
|
288
|
+
const releaseAll = () => {
|
|
289
|
+
const releasing = [...heldKeys.values()];
|
|
290
|
+
heldKeys.clear();
|
|
291
|
+
for (const { key, code, on } of releasing) {
|
|
292
|
+
const ev = {
|
|
293
|
+
kind: "key-held",
|
|
294
|
+
key,
|
|
295
|
+
...code !== void 0 ? { code } : {},
|
|
296
|
+
phase: "up",
|
|
297
|
+
altKey: false,
|
|
298
|
+
ctrlKey: false,
|
|
299
|
+
metaKey: false,
|
|
300
|
+
shiftKey: false
|
|
301
|
+
};
|
|
302
|
+
dispatch(ev, on);
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
return { onKeyDown, onKeyUp, releaseAll };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// src/interactions/dispatcher/channels/webkitPinch.ts
|
|
309
|
+
function webkitPinchInput(core) {
|
|
310
|
+
const { canvas, routeAt, toWorld, target, dispatch, dispatcherNow, ctxNow } = core;
|
|
311
|
+
let pinchLive = false;
|
|
312
|
+
let pinchScale = 1;
|
|
313
|
+
let pinchRotation = 0;
|
|
314
|
+
const endPinch = () => {
|
|
315
|
+
pinchLive = false;
|
|
316
|
+
pinchScale = 1;
|
|
317
|
+
pinchRotation = 0;
|
|
318
|
+
};
|
|
319
|
+
const pinchEventOf = (e, scale, rotation) => {
|
|
320
|
+
const g = e;
|
|
321
|
+
const rect = canvas?.getBoundingClientRect();
|
|
322
|
+
const cx = typeof g.clientX === "number" ? g.clientX : rect ? rect.left + rect.width / 2 : 0;
|
|
323
|
+
const cy = typeof g.clientY === "number" ? g.clientY : rect ? rect.top + rect.height / 2 : 0;
|
|
324
|
+
routeAt(null, cx, cy);
|
|
325
|
+
const [localX, localY] = rect ? clientToCanvasRect(rect, cx, cy) : [cx, cy];
|
|
326
|
+
const hitPoint = toWorld(cx, cy);
|
|
327
|
+
const affordance = target().affordanceAt?.(hitPoint) ?? void 0;
|
|
328
|
+
const body = target().classifyTarget?.(hitPoint);
|
|
329
|
+
return {
|
|
330
|
+
kind: "pinch",
|
|
331
|
+
altKey: g.altKey === true,
|
|
332
|
+
ctrlKey: g.ctrlKey === true,
|
|
333
|
+
metaKey: g.metaKey === true,
|
|
334
|
+
shiftKey: g.shiftKey === true,
|
|
335
|
+
scale,
|
|
336
|
+
rotation,
|
|
337
|
+
clientX: localX,
|
|
338
|
+
clientY: localY,
|
|
339
|
+
...affordance !== void 0 ? { affordance } : {},
|
|
340
|
+
...body?.body !== void 0 ? { bodyTarget: body.body } : {},
|
|
341
|
+
...body?.kind !== void 0 ? { bodyKind: body.kind } : {}
|
|
342
|
+
};
|
|
343
|
+
};
|
|
344
|
+
const onGestureStart = (e) => {
|
|
345
|
+
endPinch();
|
|
346
|
+
const probe = pinchEventOf(e, 1, 0);
|
|
347
|
+
if (dispatcherNow().resolveOnly(probe, ctxNow()) === null) return;
|
|
348
|
+
pinchLive = true;
|
|
349
|
+
e.preventDefault();
|
|
350
|
+
};
|
|
351
|
+
const onGestureChange = (e) => {
|
|
352
|
+
const g = e;
|
|
353
|
+
const scale = typeof g.scale === "number" && g.scale > 0 ? g.scale : pinchScale;
|
|
354
|
+
const rotation = typeof g.rotation === "number" ? g.rotation : pinchRotation;
|
|
355
|
+
const step = scale / pinchScale;
|
|
356
|
+
const turn = rotation - pinchRotation;
|
|
357
|
+
pinchScale = scale;
|
|
358
|
+
pinchRotation = rotation;
|
|
359
|
+
if (dispatch(pinchEventOf(e, step, turn)) === "handled") {
|
|
360
|
+
pinchLive = true;
|
|
361
|
+
e.preventDefault();
|
|
362
|
+
} else if (pinchLive) {
|
|
363
|
+
e.preventDefault();
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
const onGestureEnd = (e) => {
|
|
367
|
+
if (pinchLive) e.preventDefault();
|
|
368
|
+
endPinch();
|
|
369
|
+
};
|
|
370
|
+
return { onGestureStart, onGestureChange, onGestureEnd, live: () => pinchLive, end: endPinch };
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// src/interactions/dispatcher/channels/wheel.ts
|
|
374
|
+
function wheelInput(core, pinchLive) {
|
|
375
|
+
const { canvas, routeAt, toWorld, target, dispatch } = core;
|
|
376
|
+
const onWheel = (e) => {
|
|
377
|
+
core.activate();
|
|
378
|
+
if (pinchLive() && e.ctrlKey) {
|
|
379
|
+
e.preventDefault();
|
|
380
|
+
e.stopPropagation();
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
routeAt(null, e.clientX, e.clientY);
|
|
384
|
+
const rect = canvas?.getBoundingClientRect();
|
|
385
|
+
const [localX, localY] = rect ? clientToCanvasRect(rect, e.clientX, e.clientY) : [e.clientX, e.clientY];
|
|
386
|
+
const hitPoint = toWorld(e.clientX, e.clientY);
|
|
387
|
+
const affordance = target().affordanceAt?.(hitPoint) ?? void 0;
|
|
388
|
+
const body = target().classifyTarget?.(hitPoint);
|
|
389
|
+
const ev = {
|
|
390
|
+
kind: "wheel",
|
|
391
|
+
altKey: e.altKey,
|
|
392
|
+
ctrlKey: e.ctrlKey,
|
|
393
|
+
metaKey: e.metaKey,
|
|
394
|
+
shiftKey: e.shiftKey,
|
|
395
|
+
deltaX: e.deltaX,
|
|
396
|
+
deltaY: e.deltaY,
|
|
397
|
+
clientX: localX,
|
|
398
|
+
clientY: localY,
|
|
399
|
+
...affordance !== void 0 ? { affordance } : {},
|
|
400
|
+
...body?.body !== void 0 ? { bodyTarget: body.body } : {},
|
|
401
|
+
...body?.kind !== void 0 ? { bodyKind: body.kind } : {}
|
|
402
|
+
};
|
|
403
|
+
const result = dispatch(ev);
|
|
404
|
+
if (result === "handled") {
|
|
405
|
+
e.preventDefault();
|
|
406
|
+
e.stopImmediatePropagation();
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
return onWheel;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// src/interactions/dispatcher/useGestureDispatcher.tsx
|
|
413
|
+
function stylusOf(e) {
|
|
414
|
+
return {
|
|
415
|
+
...typeof e.pressure === "number" ? { pressure: e.pressure } : {},
|
|
416
|
+
...typeof e.tiltX === "number" ? { tiltX: e.tiltX } : {},
|
|
417
|
+
...typeof e.tiltY === "number" ? { tiltY: e.tiltY } : {}
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
var IS_MAC = isApplePlatform();
|
|
141
421
|
function computeMultiTouchGeometry(positions) {
|
|
142
422
|
const pts = [...positions];
|
|
143
423
|
if (pts.length < 2) {
|
|
@@ -156,7 +436,7 @@ function computeMultiTouchGeometry(positions) {
|
|
|
156
436
|
return { centroid, spread };
|
|
157
437
|
}
|
|
158
438
|
function useGestureDispatcher(opts) {
|
|
159
|
-
const { canvasRef, actions, entriesById, enabled = true, keyboard = true, affordanceAt, classifyTarget, dispatcher: dispatcherOpt, clientToWorld, requestRedraw, paintedCursor, getRuleCtx, onClick, onDoubleClick, views, channels, longPress } = opts;
|
|
439
|
+
const { canvasRef, actions, entriesById, enabled = true, keyboard = true, affordanceAt, classifyTarget, dispatcher: dispatcherOpt, clientToWorld, requestRedraw, paintedCursor, getRuleCtx, onClick, onDoubleClick, views, channels, longPress, claimKeys = false } = opts;
|
|
160
440
|
const pointerChannel = channels?.pointer !== false;
|
|
161
441
|
const wheelChannel = channels?.wheel !== false;
|
|
162
442
|
const pinchChannel = channels?.pinch !== false;
|
|
@@ -199,13 +479,21 @@ function useGestureDispatcher(opts) {
|
|
|
199
479
|
const liveToolsRef = useRef(/* @__PURE__ */ new Set());
|
|
200
480
|
const prevActiveRef = useRef(activeTool.active);
|
|
201
481
|
const entriesByIdRef = useLatest(entriesById);
|
|
482
|
+
const lifecycleScratchRef = useRef(/* @__PURE__ */ new Map());
|
|
202
483
|
const fireLifecycle = useCallback((id, hook) => {
|
|
484
|
+
const scratches = lifecycleScratchRef.current;
|
|
203
485
|
const tool = entriesByIdRef.current.get(id);
|
|
204
|
-
if (!tool || !isTool(tool))
|
|
486
|
+
if (!tool || !isTool(tool)) {
|
|
487
|
+
scratches.delete(id);
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
if (hook === "onActivate" || !scratches.has(id)) scratches.set(id, tool.initScratch?.());
|
|
491
|
+
const scratch = scratches.get(id);
|
|
492
|
+
if (hook === "onDeactivate") scratches.delete(id);
|
|
205
493
|
const fn = tool[hook];
|
|
206
494
|
if (!fn) return;
|
|
207
495
|
try {
|
|
208
|
-
fn({ scratch
|
|
496
|
+
fn({ scratch });
|
|
209
497
|
} catch (err) {
|
|
210
498
|
console.error(`weasel: tool "${id}".${hook} threw`, err);
|
|
211
499
|
}
|
|
@@ -228,6 +516,9 @@ function useGestureDispatcher(opts) {
|
|
|
228
516
|
liveToolsRef.current = /* @__PURE__ */ new Set();
|
|
229
517
|
for (const id of live) fireLifecycle(id, "onDeactivate");
|
|
230
518
|
}, [fireLifecycle]);
|
|
519
|
+
useEffect(() => {
|
|
520
|
+
if (enabled && claimKeys) registryRef.current.activate();
|
|
521
|
+
}, [enabled, claimKeys, registryRef]);
|
|
231
522
|
useEffect(() => {
|
|
232
523
|
if (!enabled) return;
|
|
233
524
|
let disposed = false;
|
|
@@ -267,8 +558,29 @@ function useGestureDispatcher(opts) {
|
|
|
267
558
|
if (before > 0 || after > 0) requestRedrawRef.current?.();
|
|
268
559
|
return result;
|
|
269
560
|
};
|
|
270
|
-
const
|
|
271
|
-
|
|
561
|
+
const core = {
|
|
562
|
+
canvas,
|
|
563
|
+
target,
|
|
564
|
+
routeAt,
|
|
565
|
+
routedView: () => routedId,
|
|
566
|
+
setRoutedView: (id) => {
|
|
567
|
+
routedId = id;
|
|
568
|
+
},
|
|
569
|
+
dispatcherNow,
|
|
570
|
+
ctxNow,
|
|
571
|
+
toWorld,
|
|
572
|
+
dispatch,
|
|
573
|
+
holdsKeys: () => canvas ? registryRef.current.holdsKeys() : registryRef.current.isActive(),
|
|
574
|
+
activate: () => registryRef.current.activate(),
|
|
575
|
+
disposed: () => disposed
|
|
576
|
+
};
|
|
577
|
+
const keys = keyboardInput(core);
|
|
578
|
+
const { onKeyDown, onKeyUp } = keys;
|
|
579
|
+
const pinch = webkitPinchInput(core);
|
|
580
|
+
const { onGestureStart, onGestureChange, onGestureEnd } = pinch;
|
|
581
|
+
const onWheel = wheelInput(core, pinch.live);
|
|
582
|
+
const onContextMenu = contextMenuInput(core);
|
|
583
|
+
const { onDragOver, onDragLeave, onDrop, onPaste } = ingestInput(core);
|
|
272
584
|
const held = /* @__PURE__ */ new Map();
|
|
273
585
|
const toCanvasLocal = (p) => {
|
|
274
586
|
const rect = canvas?.getBoundingClientRect();
|
|
@@ -282,14 +594,6 @@ function useGestureDispatcher(opts) {
|
|
|
282
594
|
);
|
|
283
595
|
return { centroid: toCanvasLocal(centroid), spread };
|
|
284
596
|
};
|
|
285
|
-
let pinchLive = false;
|
|
286
|
-
let pinchScale = 1;
|
|
287
|
-
let pinchRotation = 0;
|
|
288
|
-
const endPinch = () => {
|
|
289
|
-
pinchLive = false;
|
|
290
|
-
pinchScale = 1;
|
|
291
|
-
pinchRotation = 0;
|
|
292
|
-
};
|
|
293
597
|
let multiTouchStart = null;
|
|
294
598
|
const TAP_THRESHOLD_PX = 8;
|
|
295
599
|
const longPressTimers = /* @__PURE__ */ new Map();
|
|
@@ -335,168 +639,23 @@ function useGestureDispatcher(opts) {
|
|
|
335
639
|
return d.resolveOnly(evs.longpress, ctxNow()) !== null || d.resolveOnly(evs.contextmenu, ctxNow()) !== null;
|
|
336
640
|
};
|
|
337
641
|
const fireLongPress = (pointerId, pointerType) => {
|
|
338
|
-
const
|
|
642
|
+
const rec = held.get(pointerId);
|
|
339
643
|
clearPending(pointerId);
|
|
340
|
-
if (!
|
|
341
|
-
const evs = longPressEvents(down);
|
|
644
|
+
if (!rec) return;
|
|
645
|
+
const evs = longPressEvents(rec.down);
|
|
342
646
|
let result = dispatch(evs.longpress);
|
|
343
647
|
if (result === "unhandled") result = dispatch(evs.contextmenu);
|
|
648
|
+
if (result === "handled") rec.buffered = null;
|
|
344
649
|
if (result === "handled" && longPressRef.current?.haptics !== false && (pointerType === "touch" || pointerType === "pen") && typeof navigator !== "undefined" && typeof navigator.vibrate === "function") {
|
|
345
650
|
navigator.vibrate(HAPTIC_MS);
|
|
346
651
|
}
|
|
347
652
|
};
|
|
348
653
|
const DOUBLE_CLICK_MAX_MS = 600;
|
|
349
654
|
const DOUBLE_CLICK_MAX_PX = 8;
|
|
350
|
-
const onKeyDown = (e) => {
|
|
351
|
-
if (e.defaultPrevented || isEditableTarget(e.target) || activatesFocusedControl(e)) return;
|
|
352
|
-
if (!registryRef.current.isActive()) return;
|
|
353
|
-
if (e.repeat && heldKeys.has(physicalKey(e))) {
|
|
354
|
-
e.preventDefault();
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
const keyEv = {
|
|
358
|
-
kind: "key",
|
|
359
|
-
key: e.key,
|
|
360
|
-
altKey: e.altKey,
|
|
361
|
-
ctrlKey: e.ctrlKey,
|
|
362
|
-
metaKey: e.metaKey,
|
|
363
|
-
shiftKey: e.shiftKey,
|
|
364
|
-
repeat: e.repeat
|
|
365
|
-
};
|
|
366
|
-
const keyResult = dispatch(keyEv);
|
|
367
|
-
const heldEv = {
|
|
368
|
-
kind: "key-held",
|
|
369
|
-
key: e.key,
|
|
370
|
-
phase: "down",
|
|
371
|
-
altKey: e.altKey,
|
|
372
|
-
ctrlKey: e.ctrlKey,
|
|
373
|
-
metaKey: e.metaKey,
|
|
374
|
-
shiftKey: e.shiftKey
|
|
375
|
-
};
|
|
376
|
-
const heldResult = dispatch(heldEv);
|
|
377
|
-
if (heldResult === "handled") {
|
|
378
|
-
heldKeys.set(physicalKey(e), e.key);
|
|
379
|
-
}
|
|
380
|
-
if (keyResult === "handled" || heldResult === "handled") {
|
|
381
|
-
e.preventDefault();
|
|
382
|
-
}
|
|
383
|
-
};
|
|
384
|
-
const onKeyUp = (e) => {
|
|
385
|
-
const physical = physicalKey(e);
|
|
386
|
-
const downKey = heldKeys.get(physical);
|
|
387
|
-
if (downKey === void 0) return;
|
|
388
|
-
const ev = {
|
|
389
|
-
kind: "key-held",
|
|
390
|
-
key: downKey,
|
|
391
|
-
phase: "up",
|
|
392
|
-
altKey: e.altKey,
|
|
393
|
-
ctrlKey: e.ctrlKey,
|
|
394
|
-
metaKey: e.metaKey,
|
|
395
|
-
shiftKey: e.shiftKey
|
|
396
|
-
};
|
|
397
|
-
dispatch(ev);
|
|
398
|
-
heldKeys.delete(physical);
|
|
399
|
-
};
|
|
400
655
|
const onWindowBlur = () => {
|
|
401
|
-
|
|
656
|
+
pinch.end();
|
|
402
657
|
for (const p of [...held.values()]) p.session.cancel();
|
|
403
|
-
|
|
404
|
-
const ev = {
|
|
405
|
-
kind: "key-held",
|
|
406
|
-
key,
|
|
407
|
-
phase: "up",
|
|
408
|
-
altKey: false,
|
|
409
|
-
ctrlKey: false,
|
|
410
|
-
metaKey: false,
|
|
411
|
-
shiftKey: false
|
|
412
|
-
};
|
|
413
|
-
dispatch(ev);
|
|
414
|
-
}
|
|
415
|
-
heldKeys.clear();
|
|
416
|
-
};
|
|
417
|
-
const onWheel = (e) => {
|
|
418
|
-
registryRef.current.activate();
|
|
419
|
-
if (pinchLive && e.ctrlKey) {
|
|
420
|
-
e.preventDefault();
|
|
421
|
-
e.stopPropagation();
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
routeAt(null, e.clientX, e.clientY);
|
|
425
|
-
const rect = canvas?.getBoundingClientRect();
|
|
426
|
-
const [localX, localY] = rect ? clientToCanvasRect(rect, e.clientX, e.clientY) : [e.clientX, e.clientY];
|
|
427
|
-
const hitPoint = toWorld(e.clientX, e.clientY);
|
|
428
|
-
const affordance = target().affordanceAt?.(hitPoint) ?? void 0;
|
|
429
|
-
const body = target().classifyTarget?.(hitPoint);
|
|
430
|
-
const ev = {
|
|
431
|
-
kind: "wheel",
|
|
432
|
-
altKey: e.altKey,
|
|
433
|
-
ctrlKey: e.ctrlKey,
|
|
434
|
-
metaKey: e.metaKey,
|
|
435
|
-
shiftKey: e.shiftKey,
|
|
436
|
-
deltaX: e.deltaX,
|
|
437
|
-
deltaY: e.deltaY,
|
|
438
|
-
clientX: localX,
|
|
439
|
-
clientY: localY,
|
|
440
|
-
...affordance !== void 0 ? { affordance } : {},
|
|
441
|
-
...body?.body !== void 0 ? { bodyTarget: body.body } : {},
|
|
442
|
-
...body?.kind !== void 0 ? { bodyKind: body.kind } : {}
|
|
443
|
-
};
|
|
444
|
-
const result = dispatch(ev);
|
|
445
|
-
if (result === "handled") {
|
|
446
|
-
e.preventDefault();
|
|
447
|
-
e.stopImmediatePropagation();
|
|
448
|
-
}
|
|
449
|
-
};
|
|
450
|
-
const pinchEventOf = (e, scale, rotation) => {
|
|
451
|
-
const g = e;
|
|
452
|
-
const rect = canvas?.getBoundingClientRect();
|
|
453
|
-
const cx = typeof g.clientX === "number" ? g.clientX : rect ? rect.left + rect.width / 2 : 0;
|
|
454
|
-
const cy = typeof g.clientY === "number" ? g.clientY : rect ? rect.top + rect.height / 2 : 0;
|
|
455
|
-
routeAt(null, cx, cy);
|
|
456
|
-
const [localX, localY] = rect ? clientToCanvasRect(rect, cx, cy) : [cx, cy];
|
|
457
|
-
const hitPoint = toWorld(cx, cy);
|
|
458
|
-
const affordance = target().affordanceAt?.(hitPoint) ?? void 0;
|
|
459
|
-
const body = target().classifyTarget?.(hitPoint);
|
|
460
|
-
return {
|
|
461
|
-
kind: "pinch",
|
|
462
|
-
altKey: g.altKey === true,
|
|
463
|
-
ctrlKey: g.ctrlKey === true,
|
|
464
|
-
metaKey: g.metaKey === true,
|
|
465
|
-
shiftKey: g.shiftKey === true,
|
|
466
|
-
scale,
|
|
467
|
-
rotation,
|
|
468
|
-
clientX: localX,
|
|
469
|
-
clientY: localY,
|
|
470
|
-
...affordance !== void 0 ? { affordance } : {},
|
|
471
|
-
...body?.body !== void 0 ? { bodyTarget: body.body } : {},
|
|
472
|
-
...body?.kind !== void 0 ? { bodyKind: body.kind } : {}
|
|
473
|
-
};
|
|
474
|
-
};
|
|
475
|
-
const onGestureStart = (e) => {
|
|
476
|
-
endPinch();
|
|
477
|
-
const probe = pinchEventOf(e, 1, 0);
|
|
478
|
-
if (dispatcherNow().resolveOnly(probe, ctxNow()) === null) return;
|
|
479
|
-
pinchLive = true;
|
|
480
|
-
e.preventDefault();
|
|
481
|
-
};
|
|
482
|
-
const onGestureChange = (e) => {
|
|
483
|
-
const g = e;
|
|
484
|
-
const scale = typeof g.scale === "number" && g.scale > 0 ? g.scale : pinchScale;
|
|
485
|
-
const rotation = typeof g.rotation === "number" ? g.rotation : pinchRotation;
|
|
486
|
-
const step = scale / pinchScale;
|
|
487
|
-
const turn = rotation - pinchRotation;
|
|
488
|
-
pinchScale = scale;
|
|
489
|
-
pinchRotation = rotation;
|
|
490
|
-
if (dispatch(pinchEventOf(e, step, turn)) === "handled") {
|
|
491
|
-
pinchLive = true;
|
|
492
|
-
e.preventDefault();
|
|
493
|
-
} else if (pinchLive) {
|
|
494
|
-
e.preventDefault();
|
|
495
|
-
}
|
|
496
|
-
};
|
|
497
|
-
const onGestureEnd = (e) => {
|
|
498
|
-
if (pinchLive) e.preventDefault();
|
|
499
|
-
endPinch();
|
|
658
|
+
keys.releaseAll();
|
|
500
659
|
};
|
|
501
660
|
let claimedDoubleClick = false;
|
|
502
661
|
const onNativeDoubleClick = (e) => {
|
|
@@ -506,7 +665,6 @@ function useGestureDispatcher(opts) {
|
|
|
506
665
|
};
|
|
507
666
|
const onPointerDown = (e) => {
|
|
508
667
|
claimedDoubleClick = false;
|
|
509
|
-
registryRef.current.activate();
|
|
510
668
|
if ((e.button ?? 0) !== 0) return;
|
|
511
669
|
if (!canvas) return;
|
|
512
670
|
if (isInControlWithin(e.target, canvas)) return;
|
|
@@ -593,10 +751,19 @@ function useGestureDispatcher(opts) {
|
|
|
593
751
|
for (const p of held.values()) p.buffered = null;
|
|
594
752
|
const { centroid, spread } = heldGeometry();
|
|
595
753
|
if (!multiTouchStart) {
|
|
596
|
-
multiTouchStart = {
|
|
754
|
+
multiTouchStart = {
|
|
755
|
+
fingers: held.size,
|
|
756
|
+
centroid: { ...centroid },
|
|
757
|
+
at: /* @__PURE__ */ new Map(),
|
|
758
|
+
moved: false,
|
|
759
|
+
startedAt: performance.now()
|
|
760
|
+
};
|
|
597
761
|
} else {
|
|
598
762
|
multiTouchStart.fingers = held.size;
|
|
599
763
|
}
|
|
764
|
+
for (const [id, p] of held) {
|
|
765
|
+
if (!multiTouchStart.at.has(id)) multiTouchStart.at.set(id, { ...p.pos });
|
|
766
|
+
}
|
|
600
767
|
const mt = {
|
|
601
768
|
kind: "multitouch",
|
|
602
769
|
fingers: held.size,
|
|
@@ -610,10 +777,25 @@ function useGestureDispatcher(opts) {
|
|
|
610
777
|
dispatch(mt);
|
|
611
778
|
}
|
|
612
779
|
};
|
|
780
|
+
const endMultiTouch = (end, fingers, mods) => {
|
|
781
|
+
dispatch({
|
|
782
|
+
kind: "multitouch",
|
|
783
|
+
fingers,
|
|
784
|
+
end,
|
|
785
|
+
altKey: mods.altKey,
|
|
786
|
+
ctrlKey: mods.ctrlKey,
|
|
787
|
+
metaKey: mods.metaKey,
|
|
788
|
+
shiftKey: mods.shiftKey
|
|
789
|
+
});
|
|
790
|
+
};
|
|
613
791
|
const onPointerMove = (e) => {
|
|
614
792
|
routeAt(e.pointerId, e.clientX, e.clientY);
|
|
615
793
|
const rec = held.get(e.pointerId);
|
|
616
794
|
if (rec) rec.pos = { x: e.clientX, y: e.clientY };
|
|
795
|
+
const joinedAt = multiTouchStart?.at.get(e.pointerId);
|
|
796
|
+
if (multiTouchStart && joinedAt && Math.hypot(e.clientX - joinedAt.x, e.clientY - joinedAt.y) > TAP_THRESHOLD_PX) {
|
|
797
|
+
multiTouchStart.moved = true;
|
|
798
|
+
}
|
|
617
799
|
const buffered = rec?.buffered;
|
|
618
800
|
if (rec && buffered) {
|
|
619
801
|
if (pastDragThreshold(buffered, e)) {
|
|
@@ -677,13 +859,12 @@ function useGestureDispatcher(opts) {
|
|
|
677
859
|
clearHoverCursor();
|
|
678
860
|
return;
|
|
679
861
|
}
|
|
680
|
-
const painted = tier.kind === "painted" ? tier : null;
|
|
681
|
-
paintedCursorRef.current?.()?.setOverride(painted);
|
|
682
862
|
const cursor = tier.kind === "css" ? tier.css : "none";
|
|
683
863
|
if (!cursor) {
|
|
684
864
|
clearHoverCursor();
|
|
685
865
|
return;
|
|
686
866
|
}
|
|
867
|
+
paintedCursorRef.current?.()?.setOverride(tier.kind === "painted" ? tier : "none");
|
|
687
868
|
canvas.style.cursor = cursor;
|
|
688
869
|
cursorOverridden = true;
|
|
689
870
|
};
|
|
@@ -771,11 +952,8 @@ function useGestureDispatcher(opts) {
|
|
|
771
952
|
const rec = held.get(e.pointerId);
|
|
772
953
|
held.delete(e.pointerId);
|
|
773
954
|
const staysUnderThreshold = rec?.buffered != null;
|
|
955
|
+
if (prevSize >= 2) endMultiTouch("commit", prevSize, e);
|
|
774
956
|
if (prevSize >= 2 && held.size < 2) {
|
|
775
|
-
const mtGestureId = `multitouch-${prevSize}`;
|
|
776
|
-
if (dispatcherNow().inFlight().has(mtGestureId)) {
|
|
777
|
-
dispatcherNow().cancelAll("commit");
|
|
778
|
-
}
|
|
779
957
|
const start = multiTouchStart;
|
|
780
958
|
if (start) {
|
|
781
959
|
const centroid = toCanvasLocal(
|
|
@@ -786,7 +964,8 @@ function useGestureDispatcher(opts) {
|
|
|
786
964
|
);
|
|
787
965
|
const dx = centroid.x - start.centroid.x;
|
|
788
966
|
const dy = centroid.y - start.centroid.y;
|
|
789
|
-
|
|
967
|
+
const brief = performance.now() - start.startedAt < (longPressRef.current?.duration ?? LONG_PRESS_MS);
|
|
968
|
+
if (brief && !start.moved && Math.hypot(dx, dy) <= TAP_THRESHOLD_PX) {
|
|
790
969
|
const tap = {
|
|
791
970
|
kind: "multitouchtap",
|
|
792
971
|
fingers: start.fingers,
|
|
@@ -883,9 +1062,11 @@ function useGestureDispatcher(opts) {
|
|
|
883
1062
|
if (disposed) return;
|
|
884
1063
|
const rec = held.get(pointerId);
|
|
885
1064
|
if (!rec) return;
|
|
1065
|
+
const prevSize = held.size;
|
|
886
1066
|
held.delete(pointerId);
|
|
887
1067
|
routeAt(pointerId, rec.pos.x, rec.pos.y);
|
|
888
1068
|
cancelLongPress(pointerId);
|
|
1069
|
+
if (prevSize >= 2) endMultiTouch("cancel", prevSize, rec.down);
|
|
889
1070
|
multiTouchStart = null;
|
|
890
1071
|
const ev = {
|
|
891
1072
|
kind: "pointercancel",
|
|
@@ -898,82 +1079,8 @@ function useGestureDispatcher(opts) {
|
|
|
898
1079
|
dispatch(ev);
|
|
899
1080
|
routeRelease(pointerId);
|
|
900
1081
|
};
|
|
901
|
-
const
|
|
902
|
-
|
|
903
|
-
routeAt(null, e.clientX, e.clientY);
|
|
904
|
-
const w = toWorld(e.clientX, e.clientY);
|
|
905
|
-
const affordance = target().affordanceAt?.(w) ?? void 0;
|
|
906
|
-
const menuBody = target().classifyTarget?.(w);
|
|
907
|
-
const ev = {
|
|
908
|
-
kind: "contextmenu",
|
|
909
|
-
target: e.target,
|
|
910
|
-
altKey: e.altKey,
|
|
911
|
-
ctrlKey: e.ctrlKey,
|
|
912
|
-
metaKey: e.metaKey,
|
|
913
|
-
shiftKey: e.shiftKey,
|
|
914
|
-
x: w.x,
|
|
915
|
-
y: w.y,
|
|
916
|
-
clientX: e.clientX,
|
|
917
|
-
clientY: e.clientY,
|
|
918
|
-
...affordance !== void 0 ? { affordance } : {},
|
|
919
|
-
...menuBody?.body !== void 0 ? { bodyTarget: menuBody.body } : {},
|
|
920
|
-
...menuBody?.kind !== void 0 ? { bodyKind: menuBody.kind } : {}
|
|
921
|
-
};
|
|
922
|
-
dispatch(ev);
|
|
923
|
-
};
|
|
924
|
-
const onDragOver = (e) => {
|
|
925
|
-
if (!e.dataTransfer) return;
|
|
926
|
-
e.preventDefault();
|
|
927
|
-
e.dataTransfer.dropEffect = "copy";
|
|
928
|
-
canvas?.classList.add(DROPOVER_CLASS);
|
|
929
|
-
};
|
|
930
|
-
const onDragLeave = () => {
|
|
931
|
-
canvas?.classList.remove(DROPOVER_CLASS);
|
|
932
|
-
};
|
|
933
|
-
const onDrop = (e) => {
|
|
934
|
-
canvas?.classList.remove(DROPOVER_CLASS);
|
|
935
|
-
const dt = e.dataTransfer;
|
|
936
|
-
if (!dt) return;
|
|
937
|
-
e.preventDefault();
|
|
938
|
-
routeAt(null, e.clientX, e.clientY);
|
|
939
|
-
const dropOn = target().dispatcher;
|
|
940
|
-
const w = toWorld(e.clientX, e.clientY);
|
|
941
|
-
const base = {
|
|
942
|
-
altKey: e.altKey,
|
|
943
|
-
ctrlKey: e.ctrlKey,
|
|
944
|
-
metaKey: e.metaKey,
|
|
945
|
-
shiftKey: e.shiftKey
|
|
946
|
-
};
|
|
947
|
-
void itemsFromDataTransfer(dt).then((items) => {
|
|
948
|
-
if (disposed) return;
|
|
949
|
-
if (items.length === 0) return;
|
|
950
|
-
dispatch({
|
|
951
|
-
kind: "drop",
|
|
952
|
-
items,
|
|
953
|
-
x: w.x,
|
|
954
|
-
y: w.y,
|
|
955
|
-
clientX: e.clientX,
|
|
956
|
-
clientY: e.clientY,
|
|
957
|
-
...base
|
|
958
|
-
}, dropOn);
|
|
959
|
-
});
|
|
960
|
-
};
|
|
961
|
-
const onPaste = (e) => {
|
|
962
|
-
if (isEditableTarget(e.target)) return;
|
|
963
|
-
const cd = e.clipboardData;
|
|
964
|
-
if (!cd) return;
|
|
965
|
-
const items = itemsFromClipboardData(cd);
|
|
966
|
-
if (items.length === 0) return;
|
|
967
|
-
const result = dispatch({
|
|
968
|
-
kind: "paste",
|
|
969
|
-
items,
|
|
970
|
-
// ClipboardEvent carries no modifier state.
|
|
971
|
-
altKey: false,
|
|
972
|
-
ctrlKey: false,
|
|
973
|
-
metaKey: false,
|
|
974
|
-
shiftKey: false
|
|
975
|
-
});
|
|
976
|
-
if (result === "handled") e.preventDefault();
|
|
1082
|
+
const onPressActivate = () => {
|
|
1083
|
+
registryRef.current.activate();
|
|
977
1084
|
};
|
|
978
1085
|
const keyCapture = keyboard === "first";
|
|
979
1086
|
if (keyboard) {
|
|
@@ -981,6 +1088,7 @@ function useGestureDispatcher(opts) {
|
|
|
981
1088
|
window.addEventListener("keyup", onKeyUp, keyCapture);
|
|
982
1089
|
}
|
|
983
1090
|
window.addEventListener("blur", onWindowBlur);
|
|
1091
|
+
canvas?.addEventListener("pointerdown", onPressActivate);
|
|
984
1092
|
window.addEventListener("keydown", scheduleHoverCursorRefresh);
|
|
985
1093
|
window.addEventListener("keyup", scheduleHoverCursorRefresh);
|
|
986
1094
|
if (pointerChannel) {
|
|
@@ -1009,6 +1117,7 @@ function useGestureDispatcher(opts) {
|
|
|
1009
1117
|
window.removeEventListener("keyup", onKeyUp, keyCapture);
|
|
1010
1118
|
}
|
|
1011
1119
|
window.removeEventListener("blur", onWindowBlur);
|
|
1120
|
+
canvas?.removeEventListener("pointerdown", onPressActivate);
|
|
1012
1121
|
window.removeEventListener("keydown", scheduleHoverCursorRefresh);
|
|
1013
1122
|
window.removeEventListener("keyup", scheduleHoverCursorRefresh);
|
|
1014
1123
|
canvas?.removeEventListener("pointerleave", onHoverPointerLeave);
|
|
@@ -1026,6 +1135,7 @@ function useGestureDispatcher(opts) {
|
|
|
1026
1135
|
canvas?.removeEventListener("drop", onDrop);
|
|
1027
1136
|
window.removeEventListener("paste", onPaste);
|
|
1028
1137
|
canvas?.classList.remove(DROPOVER_CLASS);
|
|
1138
|
+
registryRef.current.dropKeys();
|
|
1029
1139
|
clearHoverCursor();
|
|
1030
1140
|
cancelAllLongPress();
|
|
1031
1141
|
disposed = true;
|
|
@@ -1118,6 +1228,7 @@ var ActionsNode = class extends ScopeNode {
|
|
|
1118
1228
|
return null;
|
|
1119
1229
|
}
|
|
1120
1230
|
activate() {
|
|
1231
|
+
keyHolder = this;
|
|
1121
1232
|
super.activate();
|
|
1122
1233
|
for (const f of this.onActivate) f();
|
|
1123
1234
|
}
|
|
@@ -1138,7 +1249,11 @@ var ActionsNode = class extends ScopeNode {
|
|
|
1138
1249
|
const i = cur.lastIndexOf(action);
|
|
1139
1250
|
if (i === -1) return;
|
|
1140
1251
|
cur.splice(i, 1);
|
|
1141
|
-
if (cur.length === 0)
|
|
1252
|
+
if (cur.length === 0) {
|
|
1253
|
+
queueMicrotask(() => {
|
|
1254
|
+
if (cur.length === 0 && this.actions.get(action.id) === cur) this.actions.delete(action.id);
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1142
1257
|
this.changed();
|
|
1143
1258
|
};
|
|
1144
1259
|
},
|
|
@@ -1163,11 +1278,9 @@ var ActionsNode = class extends ScopeNode {
|
|
|
1163
1278
|
trigger: (id, params) => {
|
|
1164
1279
|
const leaf = this.start();
|
|
1165
1280
|
const a = leaf.resolve(id);
|
|
1166
|
-
if (!a) return false;
|
|
1281
|
+
if (!a?.invoker || a.invoker.timing !== "immediate") return false;
|
|
1167
1282
|
try {
|
|
1168
|
-
|
|
1169
|
-
a.invoker.run(depsFor(a, leaf.deps()), params);
|
|
1170
|
-
}
|
|
1283
|
+
a.invoker.run(depsFor(a, leaf.deps()), params);
|
|
1171
1284
|
} catch (err) {
|
|
1172
1285
|
console.error(`weasel ActionsRegistry: action "${id}" threw`, err);
|
|
1173
1286
|
}
|
|
@@ -1188,9 +1301,14 @@ var ActionsNode = class extends ScopeNode {
|
|
|
1188
1301
|
return disp.beginUiOngoing(id, depsFor(a, leaf.deps()), params);
|
|
1189
1302
|
},
|
|
1190
1303
|
activate: () => this.activate(),
|
|
1191
|
-
isActive: () => this.isActive()
|
|
1304
|
+
isActive: () => this.isActive(),
|
|
1305
|
+
holdsKeys: () => keyHolder === this,
|
|
1306
|
+
dropKeys: () => {
|
|
1307
|
+
if (keyHolder === this) keyHolder = null;
|
|
1308
|
+
}
|
|
1192
1309
|
};
|
|
1193
1310
|
};
|
|
1311
|
+
var keyHolder = null;
|
|
1194
1312
|
var NODES = /* @__PURE__ */ new WeakMap();
|
|
1195
1313
|
function actionsNodeOf(registry) {
|
|
1196
1314
|
return registry ? NODES.get(registry) ?? null : null;
|
|
@@ -1271,16 +1389,14 @@ function useOngoingAction(actionId) {
|
|
|
1271
1389
|
ctrlRef.current = null;
|
|
1272
1390
|
ctrl?.end(reason);
|
|
1273
1391
|
};
|
|
1392
|
+
const drive = (params) => {
|
|
1393
|
+
if (ctrlRef.current?.active) ctrlRef.current.update(params);
|
|
1394
|
+
else ctrlRef.current = regRef.current?.begin(idRef.current, params) ?? null;
|
|
1395
|
+
};
|
|
1274
1396
|
return {
|
|
1275
|
-
input
|
|
1276
|
-
if (ctrlRef.current) ctrlRef.current.update(params);
|
|
1277
|
-
else ctrlRef.current = regRef.current?.begin(idRef.current, params) ?? null;
|
|
1278
|
-
},
|
|
1397
|
+
input: drive,
|
|
1279
1398
|
commit(params) {
|
|
1280
|
-
if (params !== void 0)
|
|
1281
|
-
if (ctrlRef.current) ctrlRef.current.update(params);
|
|
1282
|
-
else ctrlRef.current = regRef.current?.begin(idRef.current, params) ?? null;
|
|
1283
|
-
}
|
|
1399
|
+
if (params !== void 0) drive(params);
|
|
1284
1400
|
end("commit");
|
|
1285
1401
|
},
|
|
1286
1402
|
cancel() {
|
|
@@ -1291,6 +1407,8 @@ function useOngoingAction(actionId) {
|
|
|
1291
1407
|
useEffect(() => () => edit.commit(), [edit, actionId]);
|
|
1292
1408
|
return edit;
|
|
1293
1409
|
}
|
|
1410
|
+
var NO_MODES = () => () => {
|
|
1411
|
+
};
|
|
1294
1412
|
function useContributions(opts) {
|
|
1295
1413
|
const ctx = useActiveToolContext();
|
|
1296
1414
|
const actionsRegistry = useActionsRegistry();
|
|
@@ -1309,25 +1427,18 @@ function useContributions(opts) {
|
|
|
1309
1427
|
const setFocused = useCallback((id) => {
|
|
1310
1428
|
ctx.setActive(id);
|
|
1311
1429
|
}, [ctx]);
|
|
1312
|
-
const
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
state: { focusedId: focused, engagedIds }
|
|
1325
|
-
};
|
|
1326
|
-
}, [entries, focused, engaged]);
|
|
1327
|
-
const scopedBindings = useCallback(() => {
|
|
1328
|
-
const { ordered, state } = snapshot();
|
|
1329
|
-
return scopeBindings(ordered, state);
|
|
1330
|
-
}, [snapshot]);
|
|
1430
|
+
const modes = opts.modes;
|
|
1431
|
+
const modeVersion = useSyncExternalStore(
|
|
1432
|
+
modes ? modes.subscribe : NO_MODES,
|
|
1433
|
+
() => modes?.getVersion() ?? 0
|
|
1434
|
+
);
|
|
1435
|
+
const snapshot = useCallback(() => assembleEntries(entries, {
|
|
1436
|
+
focusedId: focused,
|
|
1437
|
+
hotkeyStack: engaged,
|
|
1438
|
+
...modes ? { allowedCapabilities: activeModeOf(modes.current()).allowedCapabilities } : {}
|
|
1439
|
+
// The version is what changes; `modes` is one object for the registry's life.
|
|
1440
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1441
|
+
}), [entries, focused, engaged, modes, modeVersion]);
|
|
1331
1442
|
const overlays = useCallback((position = "top") => {
|
|
1332
1443
|
const { ordered, state } = snapshot();
|
|
1333
1444
|
const active = [];
|
|
@@ -1367,8 +1478,8 @@ function useContributions(opts) {
|
|
|
1367
1478
|
});
|
|
1368
1479
|
}, [entrySig, entries, actionsRegistry, modesRegistry]);
|
|
1369
1480
|
return useMemo(
|
|
1370
|
-
() => ({ entries, focused, setFocused,
|
|
1371
|
-
[entries, focused, setFocused,
|
|
1481
|
+
() => ({ entries, focused, setFocused, overlays }),
|
|
1482
|
+
[entries, focused, setFocused, overlays]
|
|
1372
1483
|
);
|
|
1373
1484
|
}
|
|
1374
1485
|
function useOffhandAction(entries) {
|