@weasel-js/gestures 1.9.4 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +386 -206
- package/dist/index.js +401 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
var GESTURE_DESCRIPTORS = [
|
|
3
3
|
{ name: "click", hasTarget: true },
|
|
4
4
|
{ name: "pointerDown", hasTarget: true },
|
|
5
|
+
{ name: "hover", hasTarget: true },
|
|
5
6
|
{ name: "dblTap", hasTarget: true },
|
|
6
7
|
{ name: "drag", hasTarget: true },
|
|
7
8
|
{ name: "wheel", hasTarget: true, arg: { name: "direction", values: ["up", "down", "*"], default: "*" } },
|
|
@@ -271,6 +272,20 @@ function keyRouteToSpec(r) {
|
|
|
271
272
|
return spec;
|
|
272
273
|
}
|
|
273
274
|
|
|
275
|
+
// src/ui/ingestTypes.ts
|
|
276
|
+
function mimeMatchesGlob(mime, glob) {
|
|
277
|
+
const m = mime.toLowerCase();
|
|
278
|
+
const g = glob.toLowerCase();
|
|
279
|
+
if (g === "*" || g === "*/*") return true;
|
|
280
|
+
if (g.endsWith("/*")) return m.startsWith(g.slice(0, -1));
|
|
281
|
+
return m === g;
|
|
282
|
+
}
|
|
283
|
+
var UNKNOWN_MIME = "*/*";
|
|
284
|
+
function matchIngestTypes(items, types) {
|
|
285
|
+
if (!types || types.length === 0) return true;
|
|
286
|
+
return items.some((it) => it.mime === UNKNOWN_MIME || types.some((g) => mimeMatchesGlob(it.mime, g)));
|
|
287
|
+
}
|
|
288
|
+
|
|
274
289
|
// src/ui/match.ts
|
|
275
290
|
function resolveSpecValue(value) {
|
|
276
291
|
if (value === true) return "required";
|
|
@@ -391,17 +406,6 @@ function matchPhaseAtom(a, ctx) {
|
|
|
391
406
|
const isEngaged = ctx.engagedChannels.has(id);
|
|
392
407
|
return a.phase === "engaged" ? isEngaged : !isEngaged;
|
|
393
408
|
}
|
|
394
|
-
function mimeMatchesGlob(mime, glob) {
|
|
395
|
-
const m = mime.toLowerCase();
|
|
396
|
-
const g = glob.toLowerCase();
|
|
397
|
-
if (g === "*" || g === "*/*") return true;
|
|
398
|
-
if (g.endsWith("/*")) return m.startsWith(g.slice(0, -1));
|
|
399
|
-
return m === g;
|
|
400
|
-
}
|
|
401
|
-
function matchIngestTypes(items, types) {
|
|
402
|
-
if (!types || types.length === 0) return true;
|
|
403
|
-
return items.some((it) => types.some((g) => mimeMatchesGlob(it.mime, g)));
|
|
404
|
-
}
|
|
405
409
|
function matchSpec(e, spec, isMac, phaseCtx) {
|
|
406
410
|
if (spec.phase !== void 0) {
|
|
407
411
|
const ctx = phaseCtx ?? { selfChannel: null, engagedChannels: EMPTY_ENGAGED };
|
|
@@ -445,13 +449,13 @@ function matchSpec(e, spec, isMac, phaseCtx) {
|
|
|
445
449
|
if (!matchModifiers(e, spec.mods, isMac)) return false;
|
|
446
450
|
return matchTarget(e.affordance, spec.target, e.bodyTarget, e.bodyKind);
|
|
447
451
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
if (e.kind !==
|
|
452
|
+
// Each happens at a point, so its target is what the event classified
|
|
453
|
+
// there: for a long-press, the affordance its press landed on.
|
|
454
|
+
case "contextMenu":
|
|
455
|
+
case "longPress":
|
|
456
|
+
case "hover": {
|
|
457
|
+
if (e.kind !== "contextmenu" && e.kind !== "longpress" && e.kind !== "hover") return false;
|
|
458
|
+
if (e.kind !== spec.kind.toLowerCase()) return false;
|
|
455
459
|
if (!matchModifiers(e, spec.mods, isMac)) return false;
|
|
456
460
|
return matchTarget(e.affordance, spec.target, e.bodyTarget, e.bodyKind);
|
|
457
461
|
}
|
|
@@ -505,6 +509,7 @@ var SPEC_KIND_TO_GESTURE = {
|
|
|
505
509
|
longPress: "longPress",
|
|
506
510
|
drag: "drag",
|
|
507
511
|
pointerDown: "pointerDown",
|
|
512
|
+
hover: "hover",
|
|
508
513
|
multiTouch: "multiTouch",
|
|
509
514
|
multiTouchTap: "multiTouchTap",
|
|
510
515
|
drop: "drop",
|
|
@@ -554,6 +559,7 @@ function routeToSpec(parsed) {
|
|
|
554
559
|
return fail(`${parsed.gesture} has no GestureSpec kind`);
|
|
555
560
|
case "click":
|
|
556
561
|
case "pointerDown":
|
|
562
|
+
case "hover":
|
|
557
563
|
case "doubleClick":
|
|
558
564
|
case "drag":
|
|
559
565
|
case "contextMenu":
|
|
@@ -681,6 +687,8 @@ function actionClause(parsed, required) {
|
|
|
681
687
|
return userDoes(required, `clicks${target}`);
|
|
682
688
|
case "pointerDown":
|
|
683
689
|
return userDoes(required, `presses${target}`);
|
|
690
|
+
case "hover":
|
|
691
|
+
return userDoes(required, `moves the pointer${target}`);
|
|
684
692
|
case "dblTap":
|
|
685
693
|
return userDoes(required, `double-taps${target}`);
|
|
686
694
|
case "drag":
|
|
@@ -730,6 +738,15 @@ function canonicalModifiers(mods2) {
|
|
|
730
738
|
return Object.entries(mods2).sort(([a], [b]) => a.localeCompare(b)).map(([name, req]) => `${name}=${req}`).join("&");
|
|
731
739
|
}
|
|
732
740
|
|
|
741
|
+
// src/ui/stylus.ts
|
|
742
|
+
function stylusOf(src) {
|
|
743
|
+
return {
|
|
744
|
+
...typeof src.pressure === "number" ? { pressure: src.pressure } : {},
|
|
745
|
+
...typeof src.tiltX === "number" ? { tiltX: src.tiltX } : {},
|
|
746
|
+
...typeof src.tiltY === "number" ? { tiltY: src.tiltY } : {}
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
|
|
733
750
|
// src/ui/spec.ts
|
|
734
751
|
function hitPredicate(fn) {
|
|
735
752
|
return Object.assign(
|
|
@@ -752,6 +769,23 @@ function isApplePlatform() {
|
|
|
752
769
|
return /mac|iphone|ipad|ipod/i.test(raw);
|
|
753
770
|
}
|
|
754
771
|
|
|
772
|
+
// src/ui/fixedSeat.ts
|
|
773
|
+
var HOST_SELECTOR = "[data-wzl-portal-host],[data-wzl-theme],[data-wzl-mode]";
|
|
774
|
+
function nearestPortalHost(from) {
|
|
775
|
+
return from?.closest(HOST_SELECTOR) ?? null;
|
|
776
|
+
}
|
|
777
|
+
function containsFixed(el) {
|
|
778
|
+
const cs = getComputedStyle(el);
|
|
779
|
+
const set = (value) => value !== void 0 && value !== "" && value !== "none";
|
|
780
|
+
return set(cs.transform) || set(cs.filter) || set(cs.backdropFilter) || set(cs.perspective) || /paint|layout|strict|content/.test(cs.contain ?? "");
|
|
781
|
+
}
|
|
782
|
+
function fixedSeat(from) {
|
|
783
|
+
const host = nearestPortalHost(from) ?? document.body;
|
|
784
|
+
if (host === document.body || !containsFixed(host)) return { host, left: 0, top: 0 };
|
|
785
|
+
const rect = host.getBoundingClientRect();
|
|
786
|
+
return { host, left: rect.left, top: rect.top };
|
|
787
|
+
}
|
|
788
|
+
|
|
755
789
|
// src/ui/specificity.ts
|
|
756
790
|
function modsCount(mods2) {
|
|
757
791
|
if (!mods2) return 0;
|
|
@@ -810,6 +844,313 @@ function pastDragThreshold(from, to, threshold = DRAG_THRESHOLD_PX) {
|
|
|
810
844
|
return Math.hypot(to.clientX - from.clientX, to.clientY - from.clientY) >= threshold;
|
|
811
845
|
}
|
|
812
846
|
|
|
847
|
+
// src/recognizer/thresholds.ts
|
|
848
|
+
var LONG_PRESS_MS = 500;
|
|
849
|
+
var DOUBLE_CLICK_MAX_MS = 600;
|
|
850
|
+
var DOUBLE_CLICK_MAX_PX = 8;
|
|
851
|
+
var TAP_THRESHOLD_PX = 8;
|
|
852
|
+
|
|
853
|
+
// src/recognizer/pressFacts.ts
|
|
854
|
+
function pressedOn(down) {
|
|
855
|
+
return {
|
|
856
|
+
...down.affordance !== void 0 ? { affordance: down.affordance } : {},
|
|
857
|
+
...down.bodyTarget !== void 0 ? { bodyTarget: down.bodyTarget } : {},
|
|
858
|
+
...down.bodyKind !== void 0 ? { bodyKind: down.bodyKind } : {}
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
function clickEvents(down, up) {
|
|
862
|
+
const shared = {
|
|
863
|
+
target: up.target,
|
|
864
|
+
altKey: up.altKey,
|
|
865
|
+
ctrlKey: up.ctrlKey,
|
|
866
|
+
metaKey: up.metaKey,
|
|
867
|
+
shiftKey: up.shiftKey,
|
|
868
|
+
x: up.worldX,
|
|
869
|
+
y: up.worldY,
|
|
870
|
+
clientX: up.clientX,
|
|
871
|
+
clientY: up.clientY,
|
|
872
|
+
...pressedOn(down)
|
|
873
|
+
};
|
|
874
|
+
return {
|
|
875
|
+
click: { kind: "click", ...shared, pressX: down.worldX, pressY: down.worldY },
|
|
876
|
+
doubleclick: { kind: "doubleclick", ...shared }
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
function longPressEvents(down) {
|
|
880
|
+
const shared = {
|
|
881
|
+
target: down.target,
|
|
882
|
+
altKey: down.altKey,
|
|
883
|
+
ctrlKey: down.ctrlKey,
|
|
884
|
+
metaKey: down.metaKey,
|
|
885
|
+
shiftKey: down.shiftKey,
|
|
886
|
+
...pressedOn(down)
|
|
887
|
+
};
|
|
888
|
+
const at2 = { x: down.worldX, y: down.worldY, clientX: down.clientX, clientY: down.clientY };
|
|
889
|
+
return {
|
|
890
|
+
longpress: { kind: "longpress", ...at2, ...shared },
|
|
891
|
+
contextmenu: { kind: "contextmenu", ...at2, ...shared }
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// src/recognizer/click.ts
|
|
896
|
+
function composeClick(down, up, clicks, now, dispatch, observer) {
|
|
897
|
+
const evs = clickEvents(down, up);
|
|
898
|
+
const world = { x: up.worldX, y: up.worldY };
|
|
899
|
+
dispatch(evs.click);
|
|
900
|
+
observer.click?.(world);
|
|
901
|
+
const t = now();
|
|
902
|
+
const prev = clicks.last;
|
|
903
|
+
const follows = prev !== null && t - prev.t < DOUBLE_CLICK_MAX_MS && Math.hypot(up.clientX - prev.clientX, up.clientY - prev.clientY) < DOUBLE_CLICK_MAX_PX;
|
|
904
|
+
if (!follows) {
|
|
905
|
+
clicks.last = { t, clientX: up.clientX, clientY: up.clientY };
|
|
906
|
+
return {};
|
|
907
|
+
}
|
|
908
|
+
observer.doubleClick?.(world);
|
|
909
|
+
const doubleClick = dispatch(evs.doubleclick);
|
|
910
|
+
clicks.last = null;
|
|
911
|
+
return { doubleClick };
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// src/recognizer/longPressHold.ts
|
|
915
|
+
function createLongPressHold(clock, onChange) {
|
|
916
|
+
let pending = null;
|
|
917
|
+
const cancel = (pointerId) => {
|
|
918
|
+
if (!pending || pointerId !== void 0 && pending.pointerId !== pointerId) return;
|
|
919
|
+
clock.clearTimer(pending.timer);
|
|
920
|
+
pending = null;
|
|
921
|
+
onChange(null);
|
|
922
|
+
};
|
|
923
|
+
return {
|
|
924
|
+
arm(press, fire) {
|
|
925
|
+
cancel();
|
|
926
|
+
const timer = clock.setTimer(() => {
|
|
927
|
+
pending = null;
|
|
928
|
+
onChange(null);
|
|
929
|
+
fire();
|
|
930
|
+
}, press.duration);
|
|
931
|
+
pending = { pointerId: press.pointerId, timer };
|
|
932
|
+
onChange(press);
|
|
933
|
+
},
|
|
934
|
+
pendingFor: (pointerId) => pending?.pointerId === pointerId,
|
|
935
|
+
cancel
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
// src/recognizer/multiTouch.ts
|
|
940
|
+
function multiTouchGeometry(positions) {
|
|
941
|
+
const pts = [...positions];
|
|
942
|
+
if (pts.length < 2) return { centroid: { x: 0, y: 0 }, spread: 0, angle: 0 };
|
|
943
|
+
let sumX = 0;
|
|
944
|
+
let sumY = 0;
|
|
945
|
+
for (const p of pts) {
|
|
946
|
+
sumX += p.x;
|
|
947
|
+
sumY += p.y;
|
|
948
|
+
}
|
|
949
|
+
const dx = pts[1].x - pts[0].x;
|
|
950
|
+
const dy = pts[1].y - pts[0].y;
|
|
951
|
+
return {
|
|
952
|
+
centroid: { x: sumX / pts.length, y: sumY / pts.length },
|
|
953
|
+
spread: Math.sqrt(dx * dx + dy * dy),
|
|
954
|
+
angle: Math.atan2(dy, dx) * 180 / Math.PI
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
function createMultiTouchEpisode() {
|
|
958
|
+
let episode = null;
|
|
959
|
+
return {
|
|
960
|
+
join(positions, centroid, now) {
|
|
961
|
+
if (!episode) {
|
|
962
|
+
episode = {
|
|
963
|
+
fingers: positions.size,
|
|
964
|
+
centroid: { ...centroid },
|
|
965
|
+
joinedAt: /* @__PURE__ */ new Map(),
|
|
966
|
+
moved: false,
|
|
967
|
+
startedAt: now
|
|
968
|
+
};
|
|
969
|
+
} else {
|
|
970
|
+
episode.fingers = positions.size;
|
|
971
|
+
}
|
|
972
|
+
for (const [id, p] of positions) {
|
|
973
|
+
if (!episode.joinedAt.has(id)) episode.joinedAt.set(id, { ...p });
|
|
974
|
+
}
|
|
975
|
+
},
|
|
976
|
+
moved(pointerId, to) {
|
|
977
|
+
const from = episode?.joinedAt.get(pointerId);
|
|
978
|
+
if (episode && from && Math.hypot(to.x - from.x, to.y - from.y) > TAP_THRESHOLD_PX) episode.moved = true;
|
|
979
|
+
},
|
|
980
|
+
finish(centroid, now, maxHoldMs) {
|
|
981
|
+
const e = episode;
|
|
982
|
+
episode = null;
|
|
983
|
+
if (!e) return null;
|
|
984
|
+
const brief = now - e.startedAt < maxHoldMs;
|
|
985
|
+
const still = Math.hypot(centroid.x - e.centroid.x, centroid.y - e.centroid.y) <= TAP_THRESHOLD_PX;
|
|
986
|
+
return brief && !e.moved && still ? e.fingers : null;
|
|
987
|
+
},
|
|
988
|
+
abandon() {
|
|
989
|
+
episode = null;
|
|
990
|
+
}
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
// src/recognizer/samples.ts
|
|
995
|
+
function modifiersOf(e) {
|
|
996
|
+
return { altKey: e.altKey, ctrlKey: e.ctrlKey, metaKey: e.metaKey, shiftKey: e.shiftKey };
|
|
997
|
+
}
|
|
998
|
+
function pressFactsOf(s) {
|
|
999
|
+
return {
|
|
1000
|
+
target: s.target,
|
|
1001
|
+
clientX: s.clientX,
|
|
1002
|
+
clientY: s.clientY,
|
|
1003
|
+
worldX: s.x,
|
|
1004
|
+
worldY: s.y,
|
|
1005
|
+
...modifiersOf(s),
|
|
1006
|
+
...pressedOn(s)
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
var at = (s, world) => ({
|
|
1010
|
+
pointerId: s.pointerId,
|
|
1011
|
+
x: world.x,
|
|
1012
|
+
y: world.y,
|
|
1013
|
+
clientX: s.clientX,
|
|
1014
|
+
clientY: s.clientY,
|
|
1015
|
+
...modifiersOf(s)
|
|
1016
|
+
});
|
|
1017
|
+
function pointerDownEvent(s) {
|
|
1018
|
+
return { kind: "pointerdown", target: s.target, ...at(s, s), ...stylusOf(s), ...pressedOn(s) };
|
|
1019
|
+
}
|
|
1020
|
+
function pointerMoveEvent(s, world) {
|
|
1021
|
+
return { kind: "pointermove", ...at(s, world), ...stylusOf(s) };
|
|
1022
|
+
}
|
|
1023
|
+
function pointerUpEvent(s, world) {
|
|
1024
|
+
return { kind: "pointerup", ...at(s, world) };
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// src/recognizer/pointerRecognizer.ts
|
|
1028
|
+
function multiTouchEvent(fingers, mods2, more) {
|
|
1029
|
+
return { kind: "multitouch", fingers, ...modifiersOf(mods2), ...more };
|
|
1030
|
+
}
|
|
1031
|
+
var positionsOf = (held) => new Map([...held].map(([id, p]) => [id, p.pos]));
|
|
1032
|
+
function heldGeometry(held, toSurface, extra) {
|
|
1033
|
+
const g = multiTouchGeometry([...positionsOf(held).values(), ...extra]);
|
|
1034
|
+
return { ...g, centroid: toSurface(g.centroid) };
|
|
1035
|
+
}
|
|
1036
|
+
function fireLongPress(rec, sink, observer) {
|
|
1037
|
+
if (!rec) return;
|
|
1038
|
+
const evs = longPressEvents(rec.down);
|
|
1039
|
+
let result = sink.dispatch(evs.longpress);
|
|
1040
|
+
if (result === "unhandled") result = sink.dispatch(evs.contextmenu);
|
|
1041
|
+
if (result === "unhandled") return;
|
|
1042
|
+
rec.buffered = null;
|
|
1043
|
+
if (rec.holdType) observer.longPress?.(rec.holdType);
|
|
1044
|
+
}
|
|
1045
|
+
function createPointerRecognizer(opts) {
|
|
1046
|
+
const { sink, clock, observer = {}, toSurface = (p) => p } = opts;
|
|
1047
|
+
const world = (s) => opts.toWorld?.(s.clientX, s.clientY) ?? { x: s.clientX, y: s.clientY };
|
|
1048
|
+
const clicks = opts.clicks ?? { last: null };
|
|
1049
|
+
const held = /* @__PURE__ */ new Map();
|
|
1050
|
+
const touches = createMultiTouchEpisode();
|
|
1051
|
+
const hold = createLongPressHold(clock, (pending) => {
|
|
1052
|
+
observer.holdChanged?.(pending);
|
|
1053
|
+
});
|
|
1054
|
+
const longPressMs = () => opts.longPressMs?.() ?? LONG_PRESS_MS;
|
|
1055
|
+
const geometry = (extra = []) => heldGeometry(held, toSurface, extra);
|
|
1056
|
+
const claimForMultiTouch = (mods2) => {
|
|
1057
|
+
hold.cancel();
|
|
1058
|
+
for (const p of held.values()) p.buffered = null;
|
|
1059
|
+
const g = geometry();
|
|
1060
|
+
touches.join(positionsOf(held), g.centroid, clock.now());
|
|
1061
|
+
sink.dispatch(multiTouchEvent(held.size, mods2, g));
|
|
1062
|
+
};
|
|
1063
|
+
return {
|
|
1064
|
+
press(s) {
|
|
1065
|
+
const ev = pointerDownEvent(s);
|
|
1066
|
+
const rec = { pos: { x: s.clientX, y: s.clientY }, buffered: ev, down: pressFactsOf(s) };
|
|
1067
|
+
held.set(s.pointerId, rec);
|
|
1068
|
+
if (held.size < 2) sink.dispatch({ ...ev, stage: "press" });
|
|
1069
|
+
if ((s.pointerType === "touch" || s.pointerType === "pen") && held.size === 1) {
|
|
1070
|
+
rec.holdType = s.pointerType;
|
|
1071
|
+
hold.arm(
|
|
1072
|
+
{ pointerId: s.pointerId, pointerType: s.pointerType, press: rec.down, startedAt: clock.now(), duration: longPressMs() },
|
|
1073
|
+
() => {
|
|
1074
|
+
fireLongPress(held.get(s.pointerId), sink, observer);
|
|
1075
|
+
}
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
if (held.size >= 2) claimForMultiTouch(s);
|
|
1079
|
+
},
|
|
1080
|
+
move(s) {
|
|
1081
|
+
const rec = held.get(s.pointerId);
|
|
1082
|
+
const to = { x: s.clientX, y: s.clientY };
|
|
1083
|
+
if (rec) rec.pos = to;
|
|
1084
|
+
touches.moved(s.pointerId, to);
|
|
1085
|
+
if (rec?.buffered && pastDragThreshold(rec.down, s)) {
|
|
1086
|
+
const press = rec.buffered;
|
|
1087
|
+
rec.buffered = null;
|
|
1088
|
+
hold.cancel(s.pointerId);
|
|
1089
|
+
sink.dispatch(press);
|
|
1090
|
+
}
|
|
1091
|
+
if (held.size >= 2 && sink.multiTouchInFlight(held.size)) {
|
|
1092
|
+
sink.dispatch(multiTouchEvent(held.size, s, geometry()));
|
|
1093
|
+
}
|
|
1094
|
+
sink.dispatch(pointerMoveEvent(s, world(s)));
|
|
1095
|
+
},
|
|
1096
|
+
release(s) {
|
|
1097
|
+
hold.cancel(s.pointerId);
|
|
1098
|
+
const before = held.size;
|
|
1099
|
+
const rec = held.get(s.pointerId);
|
|
1100
|
+
held.delete(s.pointerId);
|
|
1101
|
+
if (before >= 2) sink.dispatch(multiTouchEvent(before, s, { end: "commit" }));
|
|
1102
|
+
if (before >= 2 && held.size < 2) {
|
|
1103
|
+
const fingers = touches.finish(
|
|
1104
|
+
geometry([{ x: s.clientX, y: s.clientY }]).centroid,
|
|
1105
|
+
clock.now(),
|
|
1106
|
+
longPressMs()
|
|
1107
|
+
);
|
|
1108
|
+
if (fingers !== null) sink.dispatch({ kind: "multitouchtap", fingers, ...modifiersOf(s) });
|
|
1109
|
+
}
|
|
1110
|
+
sink.dispatch(pointerUpEvent(s, world(s)));
|
|
1111
|
+
if (!rec?.buffered) return {};
|
|
1112
|
+
const at2 = world(s);
|
|
1113
|
+
const up = {
|
|
1114
|
+
target: s.target,
|
|
1115
|
+
clientX: s.clientX,
|
|
1116
|
+
clientY: s.clientY,
|
|
1117
|
+
worldX: at2.x,
|
|
1118
|
+
worldY: at2.y,
|
|
1119
|
+
...modifiersOf(s)
|
|
1120
|
+
};
|
|
1121
|
+
return composeClick(rec.down, up, clicks, () => clock.now(), (ev) => sink.dispatch(ev), observer);
|
|
1122
|
+
},
|
|
1123
|
+
cancel(pointerId) {
|
|
1124
|
+
const rec = held.get(pointerId);
|
|
1125
|
+
if (!rec) return;
|
|
1126
|
+
const before = held.size;
|
|
1127
|
+
held.delete(pointerId);
|
|
1128
|
+
hold.cancel(pointerId);
|
|
1129
|
+
if (before >= 2) sink.dispatch(multiTouchEvent(before, rec.down, { end: "cancel" }));
|
|
1130
|
+
touches.abandon();
|
|
1131
|
+
sink.dispatch({ kind: "pointercancel", pointerId, ...modifiersOf(rec.down) });
|
|
1132
|
+
},
|
|
1133
|
+
takeHold() {
|
|
1134
|
+
let holding = false;
|
|
1135
|
+
for (const [id, rec] of held) {
|
|
1136
|
+
if (!rec.holdType) continue;
|
|
1137
|
+
holding = true;
|
|
1138
|
+
if (!hold.pendingFor(id)) continue;
|
|
1139
|
+
hold.cancel(id);
|
|
1140
|
+
fireLongPress(rec, sink, observer);
|
|
1141
|
+
}
|
|
1142
|
+
return holding;
|
|
1143
|
+
},
|
|
1144
|
+
heldCount: () => held.size,
|
|
1145
|
+
positionOf: (pointerId) => held.get(pointerId)?.pos,
|
|
1146
|
+
reset() {
|
|
1147
|
+
hold.cancel();
|
|
1148
|
+
held.clear();
|
|
1149
|
+
touches.abandon();
|
|
1150
|
+
}
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
|
|
813
1154
|
// src/ui/controlTarget.ts
|
|
814
1155
|
var CONTROL_SELECTOR = [
|
|
815
1156
|
"button",
|
|
@@ -831,6 +1172,47 @@ function isInControlWithin(target, host) {
|
|
|
831
1172
|
return control !== null && control !== host && host.contains(control);
|
|
832
1173
|
}
|
|
833
1174
|
|
|
834
|
-
|
|
1175
|
+
// src/ui/keyOwner.ts
|
|
1176
|
+
function isEditableTarget(target) {
|
|
1177
|
+
if (!target || !(target instanceof Element)) return false;
|
|
1178
|
+
const tag = target.tagName;
|
|
1179
|
+
if (tag === "INPUT" || tag === "TEXTAREA") return true;
|
|
1180
|
+
if (target.isContentEditable) return true;
|
|
1181
|
+
const ce = target.getAttribute("contenteditable");
|
|
1182
|
+
return ce === "true" || ce === "";
|
|
1183
|
+
}
|
|
1184
|
+
var ACTIVATED_BY_KEY = [
|
|
1185
|
+
"button",
|
|
1186
|
+
"a[href]",
|
|
1187
|
+
"summary",
|
|
1188
|
+
"select",
|
|
1189
|
+
'input[type="button"]',
|
|
1190
|
+
'input[type="submit"]',
|
|
1191
|
+
'input[type="reset"]',
|
|
1192
|
+
'input[type="checkbox"]',
|
|
1193
|
+
'input[type="radio"]',
|
|
1194
|
+
'[role="button"]',
|
|
1195
|
+
'[role="link"]',
|
|
1196
|
+
'[role="checkbox"]',
|
|
1197
|
+
'[role="switch"]',
|
|
1198
|
+
'[role="menuitem"]',
|
|
1199
|
+
'[role="tab"]',
|
|
1200
|
+
'[role="option"]'
|
|
1201
|
+
].join(", ");
|
|
1202
|
+
function activatesFocusedControl(e) {
|
|
1203
|
+
if (e.key !== " " && e.key !== "Enter") return false;
|
|
1204
|
+
return e.target instanceof Element && e.target.matches(ACTIVATED_BY_KEY);
|
|
1205
|
+
}
|
|
1206
|
+
var SELECT_NAVIGATION = /* @__PURE__ */ new Set(["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Home", "End", "PageUp", "PageDown"]);
|
|
1207
|
+
function selectOwnsKey(e) {
|
|
1208
|
+
if (!(e.target instanceof Element) || e.target.tagName !== "SELECT") return false;
|
|
1209
|
+
if (e.metaKey || e.ctrlKey || e.altKey) return false;
|
|
1210
|
+
return SELECT_NAVIGATION.has(e.key) || e.key.length === 1;
|
|
1211
|
+
}
|
|
1212
|
+
function focusedControlOwnsKey(e) {
|
|
1213
|
+
return activatesFocusedControl(e) || selectOwnsKey(e);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
export { ACTIVE_SIGILS, CONTROL_SELECTOR, DOUBLE_CLICK_MAX_MS, DOUBLE_CLICK_MAX_PX, DRAG_THRESHOLD_PX, GESTURE_DESCRIPTORS, LONG_PRESS_MS, MOD_NAME_SET, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, RESERVED_SIGILS, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, TAP_THRESHOLD_PX, UNKNOWN_MIME, VALID_MOD_NAMES, activatesFocusedControl, bodyPredicate, canonicalModifiers, clickEvents, collapseShiftPairs, createPointerRecognizer, describeRoute, describeRouteParts, fixedSeat, focusedControlOwnsKey, formatKeyRoute, formatPhaseAtom, formatRoute, getGestureDescriptor, hitPredicate, isApplePlatform, isEditableTarget, isInControlWithin, isKnownGestureName, keyRouteToSpec, longPressEvents, matchIngestTypes, matchKey, matchModifiers, matchPhase, matchSpec, matchTarget, mimeMatchesGlob, modifierComboToParsed, mods, multiTouchGeometry, nearestPortalHost, parseKeyRoute, parseRoute, parseTargetSpec, pastDragThreshold, routeGestureForSpecKind, routeToSpec, specKindForRouteGesture, specificity, stylusOf };
|
|
835
1217
|
//# sourceMappingURL=index.js.map
|
|
836
1218
|
//# sourceMappingURL=index.js.map
|