@weasel-js/gestures 1.11.0 → 1.12.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 +84 -12
- package/dist/index.js +205 -63
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -18,7 +18,7 @@ var GESTURE_DESCRIPTORS = [
|
|
|
18
18
|
// matches any payload, which is what an omitted arg slot means for a
|
|
19
19
|
// `'free'` arg.
|
|
20
20
|
{ name: "drop", hasTarget: false, arg: { name: "types", values: "free" } },
|
|
21
|
-
{ name: "carry", hasTarget:
|
|
21
|
+
{ name: "carry", hasTarget: true, arg: { name: "types", values: "free" } },
|
|
22
22
|
{ name: "paste", hasTarget: false, arg: { name: "types", values: "free" } }
|
|
23
23
|
];
|
|
24
24
|
var BY_NAME = new Map(
|
|
@@ -107,6 +107,7 @@ function parseRoute(input) {
|
|
|
107
107
|
target = "*";
|
|
108
108
|
}
|
|
109
109
|
const modifiers = {};
|
|
110
|
+
let held2;
|
|
110
111
|
while (rest.length > 0) {
|
|
111
112
|
const ch = rest[0];
|
|
112
113
|
if (RESERVED_SIGILS.has(ch)) {
|
|
@@ -120,6 +121,13 @@ function parseRoute(input) {
|
|
|
120
121
|
const nameMatch = /^([a-z]+)/.exec(rest);
|
|
121
122
|
if (!nameMatch) throw new Error(`invalid route (sigil "${sigil}" without modifier name): ${input}`);
|
|
122
123
|
const name = nameMatch[1];
|
|
124
|
+
if (name === HELD) {
|
|
125
|
+
if (gestureName !== "hover") throw new Error(`invalid route (${gestureName} takes no "${HELD}"): ${input}`);
|
|
126
|
+
if (held2 !== void 0) throw new Error(`invalid route (duplicate "${HELD}"): ${input}`);
|
|
127
|
+
held2 = sigil === "+" ? "required" : "optional";
|
|
128
|
+
rest = rest.slice(nameMatch[0].length).trimStart();
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
123
131
|
if (!MOD_NAME_SET.has(name)) {
|
|
124
132
|
throw new Error(`invalid route (unknown modifier "${name}"): ${input}`);
|
|
125
133
|
}
|
|
@@ -129,8 +137,9 @@ function parseRoute(input) {
|
|
|
129
137
|
modifiers[name] = sigil === "+" ? "required" : "optional";
|
|
130
138
|
rest = rest.slice(nameMatch[0].length).trimStart();
|
|
131
139
|
}
|
|
132
|
-
return { phases, gesture: gestureName, arg, target, modifiers };
|
|
140
|
+
return { phases, gesture: gestureName, arg, target, modifiers, ...held2 ? { held: held2 } : {} };
|
|
133
141
|
}
|
|
142
|
+
var HELD = "held";
|
|
134
143
|
function parsePhaseList(raw, input) {
|
|
135
144
|
if (raw === "") throw new Error(`invalid route (empty phase list): ${input}`);
|
|
136
145
|
const tokens = raw.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
|
|
@@ -204,6 +213,7 @@ function collapseShiftPairs(routes) {
|
|
|
204
213
|
}
|
|
205
214
|
function isShiftTwin(a, b) {
|
|
206
215
|
if (a.gesture !== b.gesture) return false;
|
|
216
|
+
if (a.held !== b.held) return false;
|
|
207
217
|
if (a.arg !== b.arg) return false;
|
|
208
218
|
if (a.target !== b.target) return false;
|
|
209
219
|
if (!samePhases(a.phases, b.phases)) return false;
|
|
@@ -246,21 +256,23 @@ function formatRoute(r) {
|
|
|
246
256
|
if (req === "required") out += ` +${name}`;
|
|
247
257
|
else if (req === "optional") out += ` ?${name}`;
|
|
248
258
|
}
|
|
259
|
+
if (r.held === "required") out += ` +${HELD}`;
|
|
260
|
+
else if (r.held === "optional") out += ` ?${HELD}`;
|
|
249
261
|
return out;
|
|
250
262
|
}
|
|
251
263
|
|
|
252
264
|
// src/grammar/keyRouteGrammar.ts
|
|
253
265
|
var OPTIONAL_SET = new Set(VALID_MOD_NAMES);
|
|
254
266
|
function parseKeyRoute(input) {
|
|
255
|
-
const [
|
|
256
|
-
if (!
|
|
267
|
+
const [key2, ...mods2] = input.split("?");
|
|
268
|
+
if (!key2) throw new Error(`invalid key route (empty key): ${input}`);
|
|
257
269
|
const seen = /* @__PURE__ */ new Set();
|
|
258
270
|
for (const m of mods2) {
|
|
259
271
|
if (!OPTIONAL_SET.has(m)) throw new Error(`unknown optional modifier "${m}" in ${input}`);
|
|
260
272
|
if (seen.has(m)) throw new Error(`duplicate optional modifier "${m}" in ${input}`);
|
|
261
273
|
seen.add(m);
|
|
262
274
|
}
|
|
263
|
-
return { key, optionalMods: mods2 };
|
|
275
|
+
return { key: key2, optionalMods: mods2 };
|
|
264
276
|
}
|
|
265
277
|
function formatKeyRoute(r) {
|
|
266
278
|
return r.optionalMods.length === 0 ? r.key : `${r.key}?${r.optionalMods.join("?")}`;
|
|
@@ -366,9 +378,9 @@ function matchModifiers(e, mods2, isMac) {
|
|
|
366
378
|
}
|
|
367
379
|
return checkKey(alt, e.altKey) && checkKey(ctrl, e.ctrlKey) && checkKey(meta, e.metaKey) && checkKey(shift, e.shiftKey);
|
|
368
380
|
}
|
|
369
|
-
function checkKey(req,
|
|
370
|
-
if (req === "required") return
|
|
371
|
-
if (req === "forbidden") return !
|
|
381
|
+
function checkKey(req, held2) {
|
|
382
|
+
if (req === "required") return held2;
|
|
383
|
+
if (req === "forbidden") return !held2;
|
|
372
384
|
return true;
|
|
373
385
|
}
|
|
374
386
|
function matchKey(eventKey, specKey) {
|
|
@@ -474,6 +486,7 @@ function matchSpec(e, spec, isMac, phaseCtx) {
|
|
|
474
486
|
case "hover": {
|
|
475
487
|
if (e.kind !== "hover") return false;
|
|
476
488
|
if (!matchModifiers(e, spec.mods, isMac)) return false;
|
|
489
|
+
if (!e.off && (e.held === true ? spec.held === void 0 : spec.held === true)) return false;
|
|
477
490
|
const on = !e.off && matchTarget(e.affordance, spec.target, e.bodyTarget, e.bodyKind);
|
|
478
491
|
if (spec.motion !== "leave") return on;
|
|
479
492
|
return !on && e.from !== void 0 && matchTarget(e.from.affordance, spec.target, e.from.bodyTarget, e.from.bodyKind);
|
|
@@ -503,8 +516,10 @@ function matchSpec(e, spec, isMac, phaseCtx) {
|
|
|
503
516
|
function matchArrival(e, spec, isMac) {
|
|
504
517
|
if (e.kind !== spec.kind) return false;
|
|
505
518
|
if (e.kind === "carry") {
|
|
506
|
-
|
|
519
|
+
const target = "target" in spec ? spec.target : void 0;
|
|
520
|
+
if (e.stage === "leave" || e.stage === "move" && target === void 0) return false;
|
|
507
521
|
if (!matchIngestTypes([{ mime: e.type }], spec.types)) return false;
|
|
522
|
+
if (!matchTarget(e.affordance, target, e.bodyTarget, e.bodyKind)) return false;
|
|
508
523
|
} else if (e.kind === "drop" || e.kind === "paste") {
|
|
509
524
|
if (!matchIngestTypes(e.items, spec.types)) return false;
|
|
510
525
|
}
|
|
@@ -574,7 +589,12 @@ function routeToSpec(parsed) {
|
|
|
574
589
|
case void 0:
|
|
575
590
|
return fail(`${parsed.gesture} has no GestureSpec kind`);
|
|
576
591
|
case "hover":
|
|
577
|
-
return withMods({
|
|
592
|
+
return withMods({
|
|
593
|
+
kind,
|
|
594
|
+
...arg === "leave" ? { motion: "leave" } : {},
|
|
595
|
+
...parsed.held ? { held: parsed.held === "required" ? true : "optional" } : {},
|
|
596
|
+
...t
|
|
597
|
+
});
|
|
578
598
|
case "click":
|
|
579
599
|
case "pointerDown":
|
|
580
600
|
case "doubleClick":
|
|
@@ -589,17 +609,18 @@ function routeToSpec(parsed) {
|
|
|
589
609
|
case "key":
|
|
590
610
|
case "key-held": {
|
|
591
611
|
if (arg === void 0) return fail("a key spec needs a concrete key");
|
|
592
|
-
const { key, optionalMods } = parseKeyRoute(arg);
|
|
612
|
+
const { key: key2, optionalMods } = parseKeyRoute(arg);
|
|
593
613
|
const optional = {};
|
|
594
614
|
for (const m of optionalMods) optional[m] = "optional";
|
|
595
|
-
return withMods({ kind, key, ...optionalMods.length > 0 ? { mods: optional } : {} });
|
|
615
|
+
return withMods({ kind, key: key2, ...optionalMods.length > 0 ? { mods: optional } : {} });
|
|
596
616
|
}
|
|
597
617
|
case "multiTouch":
|
|
598
618
|
case "multiTouchTap":
|
|
599
619
|
if (arg === void 0) return fail(`a ${kind} spec needs concrete fingers`);
|
|
600
620
|
return withMods({ kind, fingers: Number(arg) });
|
|
601
|
-
case "drop":
|
|
602
621
|
case "carry":
|
|
622
|
+
return withMods({ kind, ...arg ? { types: arg.split("|") } : {}, ...t });
|
|
623
|
+
case "drop":
|
|
603
624
|
case "paste":
|
|
604
625
|
return withMods({ kind, ...arg ? { types: arg.split("|") } : {} });
|
|
605
626
|
}
|
|
@@ -669,6 +690,11 @@ function targetClause(target, hasTarget) {
|
|
|
669
690
|
if (target === "empty") return " on empty canvas";
|
|
670
691
|
return ` on a ${target}`;
|
|
671
692
|
}
|
|
693
|
+
function carryOnto(target) {
|
|
694
|
+
if (target === void 0 || target === "*") return "onto the canvas";
|
|
695
|
+
if (target === "empty") return "onto empty canvas";
|
|
696
|
+
return `onto a ${target}`;
|
|
697
|
+
}
|
|
672
698
|
function concreteArg(parsed) {
|
|
673
699
|
return parsed.arg === "*" ? void 0 : parsed.arg;
|
|
674
700
|
}
|
|
@@ -686,8 +712,8 @@ function actionClause(parsed, required) {
|
|
|
686
712
|
case "keyUp":
|
|
687
713
|
case "keyHeld": {
|
|
688
714
|
const verb = parsed.gesture === "keyDown" ? "presses" : parsed.gesture === "keyUp" ? "releases" : "holds";
|
|
689
|
-
const
|
|
690
|
-
return required.length > 0 ? `the user holds ${joinAnd(required)} and ${verb} ${
|
|
715
|
+
const key2 = arg ?? "any key";
|
|
716
|
+
return required.length > 0 ? `the user holds ${joinAnd(required)} and ${verb} ${key2}` : `the user ${verb} ${key2}`;
|
|
691
717
|
}
|
|
692
718
|
case "wheel": {
|
|
693
719
|
const direction = arg === "up" ? " up" : arg === "down" ? " down" : "";
|
|
@@ -709,8 +735,11 @@ function actionClause(parsed, required) {
|
|
|
709
735
|
return userDoes(required, `clicks${target}`);
|
|
710
736
|
case "pointerDown":
|
|
711
737
|
return userDoes(required, `presses${target}`);
|
|
712
|
-
case "hover":
|
|
713
|
-
|
|
738
|
+
case "hover": {
|
|
739
|
+
const held2 = parsed.held === "required" ? " with a button held" : parsed.held === "optional" ? " whether or not a button is held" : "";
|
|
740
|
+
const where = arg === "leave" ? ` ${leftClause(parsed.target)}` : target;
|
|
741
|
+
return userDoes(required, `moves the pointer${where}${held2}`);
|
|
742
|
+
}
|
|
714
743
|
case "dblTap":
|
|
715
744
|
return userDoes(required, `double-taps${target}`);
|
|
716
745
|
case "drag":
|
|
@@ -718,7 +747,7 @@ function actionClause(parsed, required) {
|
|
|
718
747
|
case "drop":
|
|
719
748
|
return userDoes(required, `drops ${arg ?? "any"} content onto the canvas`);
|
|
720
749
|
case "carry":
|
|
721
|
-
return userDoes(required, `carries ${arg ?? "anything"}
|
|
750
|
+
return userDoes(required, `carries ${arg ?? "anything"} ${carryOnto(parsed.target)}`);
|
|
722
751
|
case "paste":
|
|
723
752
|
return userDoes(required, `pastes ${arg ?? "any"} content`);
|
|
724
753
|
}
|
|
@@ -750,10 +779,10 @@ function mergeAdjacentStrings(parts) {
|
|
|
750
779
|
}
|
|
751
780
|
|
|
752
781
|
// src/grammar/modifierComboToParsed.ts
|
|
753
|
-
function modifierComboToParsed(
|
|
754
|
-
if (
|
|
782
|
+
function modifierComboToParsed(key2) {
|
|
783
|
+
if (key2 === "default") return {};
|
|
755
784
|
const out = {};
|
|
756
|
-
for (const part of
|
|
785
|
+
for (const part of key2.split("+")) {
|
|
757
786
|
out[part] = "required";
|
|
758
787
|
}
|
|
759
788
|
return out;
|
|
@@ -887,19 +916,124 @@ function keySpecsFromShortcut(shortcut, opts = {}) {
|
|
|
887
916
|
if (shortcut.mod) mods2.push({ name: "mod" });
|
|
888
917
|
if (shortcut.shift) mods2.push(shortcut.shift === "optional" ? { name: "shift", optional: true } : { name: "shift" });
|
|
889
918
|
if (shortcut.alt) mods2.push({ name: "alt" });
|
|
890
|
-
const
|
|
919
|
+
const key2 = typeof shortcut.key === "string" ? shortcut.key : shortcut.key[0];
|
|
891
920
|
const keys = keySpecsFromMods(mods2, opts);
|
|
892
|
-
return
|
|
921
|
+
return key2 === void 0 ? keys : [...keys, keySpecFromKey(key2, opts)];
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// src/ui/keySpecFromCode.ts
|
|
925
|
+
var MOD_CODES = {
|
|
926
|
+
ShiftLeft: "shift",
|
|
927
|
+
ShiftRight: "shift",
|
|
928
|
+
ControlLeft: "ctrl",
|
|
929
|
+
ControlRight: "ctrl",
|
|
930
|
+
AltLeft: "alt",
|
|
931
|
+
AltRight: "alt",
|
|
932
|
+
MetaLeft: "meta",
|
|
933
|
+
MetaRight: "meta"
|
|
934
|
+
};
|
|
935
|
+
var PRINTED = {
|
|
936
|
+
Backquote: "`",
|
|
937
|
+
Minus: "-",
|
|
938
|
+
Equal: "=",
|
|
939
|
+
BracketLeft: "[",
|
|
940
|
+
BracketRight: "]",
|
|
941
|
+
Backslash: "\\",
|
|
942
|
+
Semicolon: ";",
|
|
943
|
+
Quote: "'",
|
|
944
|
+
Comma: ",",
|
|
945
|
+
Period: ".",
|
|
946
|
+
Slash: "/",
|
|
947
|
+
IntlBackslash: "\xA7",
|
|
948
|
+
CapsLock: "Caps",
|
|
949
|
+
Fn: "fn"
|
|
950
|
+
};
|
|
951
|
+
function keySpecFromCode(code, opts = {}) {
|
|
952
|
+
const own = opts.labels?.[code];
|
|
953
|
+
if (own !== void 0) return { label: own };
|
|
954
|
+
const mod = MOD_CODES[code];
|
|
955
|
+
if (mod) return keySpecsFromMods([{ name: mod }], opts)[0];
|
|
956
|
+
const char = /^Key([A-Z])$/.exec(code) ?? /^Digit(\d)$/.exec(code);
|
|
957
|
+
if (char) return keySpecFromKey(char[1], opts);
|
|
958
|
+
if (code === "NumpadEnter") return keySpecFromKey("Enter", opts);
|
|
959
|
+
const printed = PRINTED[code];
|
|
960
|
+
return printed !== void 0 ? { label: printed } : keySpecFromKey(code, opts);
|
|
893
961
|
}
|
|
894
962
|
|
|
895
963
|
// src/ui/formatShortcut.ts
|
|
896
|
-
var
|
|
964
|
+
var SHORTCUT_LEADER_SEPARATOR = " \u25B8 ";
|
|
897
965
|
function formatShortcutParts(b, opts = {}) {
|
|
898
|
-
|
|
966
|
+
const keys = keySpecsFromShortcut(b, opts);
|
|
967
|
+
if (!b || !keys) return void 0;
|
|
968
|
+
const leader = (b.leader ?? []).map((l) => ({ label: l.label, keys: keySpecsFromShortcut(l.shortcut, opts) ?? [] }));
|
|
969
|
+
return { leader, keys };
|
|
899
970
|
}
|
|
900
971
|
function formatShortcut(b, opts = {}) {
|
|
901
972
|
const platform = opts.platform ?? detectPlatform();
|
|
902
|
-
|
|
973
|
+
const mac = platform === "macos";
|
|
974
|
+
const parts = formatShortcutParts(b && { ...b, shift: b.shift === true }, mac ? { platform } : { platform, legend: "text" });
|
|
975
|
+
if (!parts) return void 0;
|
|
976
|
+
const keys = parts.keys.map((k) => k.label).join(mac ? "" : "+");
|
|
977
|
+
return [...parts.leader.map((l) => `${l.label}${SHORTCUT_LEADER_SEPARATOR}`), keys].join("");
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// src/ui/keySugar.ts
|
|
981
|
+
var MODIFIERS = {
|
|
982
|
+
meta: "meta",
|
|
983
|
+
cmd: "meta",
|
|
984
|
+
command: "meta",
|
|
985
|
+
ctrl: "ctrl",
|
|
986
|
+
control: "ctrl",
|
|
987
|
+
alt: "alt",
|
|
988
|
+
option: "alt",
|
|
989
|
+
shift: "shift",
|
|
990
|
+
mod: "mod"
|
|
991
|
+
};
|
|
992
|
+
var KEY_NAMES = { space: " ", esc: "Escape" };
|
|
993
|
+
function parse(shortcut) {
|
|
994
|
+
const parts = shortcut.endsWith("++") ? [...shortcut.slice(0, -2).split("+"), "+"] : shortcut.split("+");
|
|
995
|
+
const raw = parts.pop();
|
|
996
|
+
if (!raw) throw new Error(`shortcut "${shortcut}" names no key`);
|
|
997
|
+
const mods2 = {};
|
|
998
|
+
for (const part of parts) {
|
|
999
|
+
const mod = MODIFIERS[part.toLowerCase()];
|
|
1000
|
+
if (!mod) throw new Error(`shortcut "${shortcut}": unknown modifier "${part}"`);
|
|
1001
|
+
mods2[mod] = true;
|
|
1002
|
+
}
|
|
1003
|
+
return { key: KEY_NAMES[raw.toLowerCase()] ?? raw, mods: mods2 };
|
|
1004
|
+
}
|
|
1005
|
+
function key(shortcut) {
|
|
1006
|
+
const { key: k, mods: mods2 } = parse(shortcut);
|
|
1007
|
+
return Object.keys(mods2).length > 0 ? { kind: "key", key: k, mods: mods2 } : { kind: "key", key: k };
|
|
1008
|
+
}
|
|
1009
|
+
function held(shortcut) {
|
|
1010
|
+
const { key: k, mods: mods2 } = parse(shortcut);
|
|
1011
|
+
return Object.keys(mods2).length > 0 ? { kind: "key-held", key: k, mods: mods2 } : { kind: "key-held", key: k };
|
|
1012
|
+
}
|
|
1013
|
+
function unbound() {
|
|
1014
|
+
return null;
|
|
1015
|
+
}
|
|
1016
|
+
function bindingSpec(input) {
|
|
1017
|
+
if (typeof input === "string") return routeToSpec(parseRoute(input));
|
|
1018
|
+
return input;
|
|
1019
|
+
}
|
|
1020
|
+
var MOD_ORDER3 = ["mod", "shift", "alt", "ctrl", "meta"];
|
|
1021
|
+
function keySpecRoute(spec) {
|
|
1022
|
+
const k = Array.isArray(spec.key) ? spec.key[0] : spec.key;
|
|
1023
|
+
const optional = MOD_ORDER3.filter((m) => spec.mods?.[m] === "optional");
|
|
1024
|
+
const required = MOD_ORDER3.filter((m) => spec.mods?.[m] === true);
|
|
1025
|
+
const arg = [k, ...optional].join("?");
|
|
1026
|
+
const gesture = spec.kind === "key" ? "keyDown" : "keyHeld";
|
|
1027
|
+
const route = [`[*:*] ${gesture}(${arg})`, ...required.map((m) => `+${m}`)].join(" ");
|
|
1028
|
+
return spellsKey(route, k) ? route : null;
|
|
1029
|
+
}
|
|
1030
|
+
function spellsKey(route, k) {
|
|
1031
|
+
try {
|
|
1032
|
+
const back = bindingSpec(route);
|
|
1033
|
+
return back !== null && (back.kind === "key" || back.kind === "key-held") && back.key === k;
|
|
1034
|
+
} catch {
|
|
1035
|
+
return false;
|
|
1036
|
+
}
|
|
903
1037
|
}
|
|
904
1038
|
|
|
905
1039
|
// src/ui/fixedSeat.ts
|
|
@@ -1166,9 +1300,9 @@ function pointerUpEvent(s, world) {
|
|
|
1166
1300
|
function multiTouchEvent(fingers, mods2, more) {
|
|
1167
1301
|
return { kind: "multitouch", fingers, ...modifiersOf(mods2), ...more };
|
|
1168
1302
|
}
|
|
1169
|
-
var positionsOf = (
|
|
1170
|
-
function heldGeometry(
|
|
1171
|
-
const g = multiTouchGeometry([...positionsOf(
|
|
1303
|
+
var positionsOf = (held2) => new Map([...held2].map(([id, p]) => [id, p.pos]));
|
|
1304
|
+
function heldGeometry(held2, toSurface, extra) {
|
|
1305
|
+
const g = multiTouchGeometry([...positionsOf(held2).values(), ...extra]);
|
|
1172
1306
|
return { ...g, centroid: toSurface(g.centroid) };
|
|
1173
1307
|
}
|
|
1174
1308
|
function fireLongPress(rec, sink, observer) {
|
|
@@ -1191,43 +1325,51 @@ function thresholdsOf(opts) {
|
|
|
1191
1325
|
})
|
|
1192
1326
|
};
|
|
1193
1327
|
}
|
|
1328
|
+
function sampleTime(clock) {
|
|
1329
|
+
return (s) => {
|
|
1330
|
+
const now = clock.now();
|
|
1331
|
+
const t = s.timeStamp;
|
|
1332
|
+
return t !== void 0 && t > 0 && t <= now ? t : now;
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1194
1335
|
function createPointerRecognizer(opts) {
|
|
1195
1336
|
const { sink, clock, observer = {}, toSurface = (p) => p } = opts;
|
|
1196
1337
|
const world = (s) => opts.toWorld?.(s.clientX, s.clientY) ?? { x: s.clientX, y: s.clientY };
|
|
1197
1338
|
const clicks = opts.clicks ?? { last: null };
|
|
1198
|
-
const
|
|
1339
|
+
const held2 = /* @__PURE__ */ new Map();
|
|
1199
1340
|
const { longPressMs, tapPx, dragPx, doubleClick } = thresholdsOf(opts);
|
|
1200
1341
|
const touches = createMultiTouchEpisode(tapPx);
|
|
1201
1342
|
const hold = createLongPressHold(clock, (pending) => {
|
|
1202
1343
|
observer.holdChanged?.(pending);
|
|
1203
1344
|
});
|
|
1204
|
-
const geometry = (extra = []) => heldGeometry(
|
|
1205
|
-
const
|
|
1345
|
+
const geometry = (extra = []) => heldGeometry(held2, toSurface, extra);
|
|
1346
|
+
const happenedAt = sampleTime(clock);
|
|
1347
|
+
const claimForMultiTouch = (s) => {
|
|
1206
1348
|
hold.cancel();
|
|
1207
|
-
for (const p of
|
|
1349
|
+
for (const p of held2.values()) p.buffered = null;
|
|
1208
1350
|
const g = geometry();
|
|
1209
|
-
touches.join(positionsOf(
|
|
1210
|
-
sink.dispatch(multiTouchEvent(
|
|
1351
|
+
touches.join(positionsOf(held2), g.centroid, happenedAt(s));
|
|
1352
|
+
sink.dispatch(multiTouchEvent(held2.size, s, g));
|
|
1211
1353
|
};
|
|
1212
1354
|
return {
|
|
1213
1355
|
press(s) {
|
|
1214
1356
|
const ev = pointerDownEvent(s);
|
|
1215
1357
|
const rec = { pos: { x: s.clientX, y: s.clientY }, buffered: ev, down: pressFactsOf(s) };
|
|
1216
|
-
|
|
1217
|
-
if (
|
|
1218
|
-
if ((s.pointerType === "touch" || s.pointerType === "pen") &&
|
|
1358
|
+
held2.set(s.pointerId, rec);
|
|
1359
|
+
if (held2.size < 2) sink.dispatch({ ...ev, stage: "press" });
|
|
1360
|
+
if ((s.pointerType === "touch" || s.pointerType === "pen") && held2.size === 1) {
|
|
1219
1361
|
rec.holdType = s.pointerType;
|
|
1220
1362
|
hold.arm(
|
|
1221
1363
|
{ pointerId: s.pointerId, pointerType: s.pointerType, press: rec.down, startedAt: clock.now(), duration: longPressMs() },
|
|
1222
1364
|
() => {
|
|
1223
|
-
fireLongPress(
|
|
1365
|
+
fireLongPress(held2.get(s.pointerId), sink, observer);
|
|
1224
1366
|
}
|
|
1225
1367
|
);
|
|
1226
1368
|
}
|
|
1227
|
-
if (
|
|
1369
|
+
if (held2.size >= 2) claimForMultiTouch(s);
|
|
1228
1370
|
},
|
|
1229
1371
|
move(s) {
|
|
1230
|
-
const rec =
|
|
1372
|
+
const rec = held2.get(s.pointerId);
|
|
1231
1373
|
const to = { x: s.clientX, y: s.clientY };
|
|
1232
1374
|
if (rec) rec.pos = to;
|
|
1233
1375
|
touches.moved(s.pointerId, to);
|
|
@@ -1237,21 +1379,21 @@ function createPointerRecognizer(opts) {
|
|
|
1237
1379
|
hold.cancel(s.pointerId);
|
|
1238
1380
|
sink.dispatch(press);
|
|
1239
1381
|
}
|
|
1240
|
-
if (
|
|
1241
|
-
sink.dispatch(multiTouchEvent(
|
|
1382
|
+
if (held2.size >= 2 && sink.multiTouchInFlight(held2.size)) {
|
|
1383
|
+
sink.dispatch(multiTouchEvent(held2.size, s, geometry()));
|
|
1242
1384
|
}
|
|
1243
1385
|
sink.dispatch(pointerMoveEvent(s, world(s)));
|
|
1244
1386
|
},
|
|
1245
1387
|
release(s) {
|
|
1246
1388
|
hold.cancel(s.pointerId);
|
|
1247
|
-
const before =
|
|
1248
|
-
const rec =
|
|
1249
|
-
|
|
1389
|
+
const before = held2.size;
|
|
1390
|
+
const rec = held2.get(s.pointerId);
|
|
1391
|
+
held2.delete(s.pointerId);
|
|
1250
1392
|
if (before >= 2) sink.dispatch(multiTouchEvent(before, s, { end: "commit" }));
|
|
1251
|
-
if (before >= 2 &&
|
|
1393
|
+
if (before >= 2 && held2.size < 2) {
|
|
1252
1394
|
const fingers = touches.finish(
|
|
1253
1395
|
geometry([{ x: s.clientX, y: s.clientY }]).centroid,
|
|
1254
|
-
|
|
1396
|
+
happenedAt(s),
|
|
1255
1397
|
longPressMs()
|
|
1256
1398
|
);
|
|
1257
1399
|
if (fingers !== null) sink.dispatch({ kind: "multitouchtap", fingers, ...modifiersOf(s) });
|
|
@@ -1267,13 +1409,13 @@ function createPointerRecognizer(opts) {
|
|
|
1267
1409
|
worldY: at2.y,
|
|
1268
1410
|
...modifiersOf(s)
|
|
1269
1411
|
};
|
|
1270
|
-
return composeClick(rec.down, up, clicks, () =>
|
|
1412
|
+
return composeClick(rec.down, up, clicks, () => happenedAt(s), (ev) => sink.dispatch(ev), observer, doubleClick());
|
|
1271
1413
|
},
|
|
1272
1414
|
cancel(pointerId) {
|
|
1273
|
-
const rec =
|
|
1415
|
+
const rec = held2.get(pointerId);
|
|
1274
1416
|
if (!rec) return;
|
|
1275
|
-
const before =
|
|
1276
|
-
|
|
1417
|
+
const before = held2.size;
|
|
1418
|
+
held2.delete(pointerId);
|
|
1277
1419
|
hold.cancel(pointerId);
|
|
1278
1420
|
if (before >= 2) sink.dispatch(multiTouchEvent(before, rec.down, { end: "cancel" }));
|
|
1279
1421
|
touches.abandon();
|
|
@@ -1281,7 +1423,7 @@ function createPointerRecognizer(opts) {
|
|
|
1281
1423
|
},
|
|
1282
1424
|
takeHold() {
|
|
1283
1425
|
let holding = false;
|
|
1284
|
-
for (const [id, rec] of
|
|
1426
|
+
for (const [id, rec] of held2) {
|
|
1285
1427
|
if (!rec.holdType) continue;
|
|
1286
1428
|
holding = true;
|
|
1287
1429
|
if (!hold.pendingFor(id)) continue;
|
|
@@ -1290,11 +1432,11 @@ function createPointerRecognizer(opts) {
|
|
|
1290
1432
|
}
|
|
1291
1433
|
return holding;
|
|
1292
1434
|
},
|
|
1293
|
-
heldCount: () =>
|
|
1294
|
-
positionOf: (pointerId) =>
|
|
1435
|
+
heldCount: () => held2.size,
|
|
1436
|
+
positionOf: (pointerId) => held2.get(pointerId)?.pos,
|
|
1295
1437
|
reset() {
|
|
1296
1438
|
hold.cancel();
|
|
1297
|
-
|
|
1439
|
+
held2.clear();
|
|
1298
1440
|
touches.abandon();
|
|
1299
1441
|
}
|
|
1300
1442
|
};
|
|
@@ -1326,20 +1468,20 @@ var trackers = /* @__PURE__ */ new WeakMap();
|
|
|
1326
1468
|
function attach(element) {
|
|
1327
1469
|
const doc = element.ownerDocument;
|
|
1328
1470
|
const win = doc.defaultView;
|
|
1329
|
-
const
|
|
1471
|
+
const held2 = /* @__PURE__ */ new Map();
|
|
1330
1472
|
const down = (e) => {
|
|
1331
1473
|
const p = e;
|
|
1332
|
-
|
|
1474
|
+
held2.set(p.pointerId, !!p.buttons);
|
|
1333
1475
|
};
|
|
1334
1476
|
const up = (e) => {
|
|
1335
|
-
|
|
1477
|
+
held2.delete(e.pointerId);
|
|
1336
1478
|
};
|
|
1337
1479
|
const move = (e) => {
|
|
1338
1480
|
const p = e;
|
|
1339
|
-
if (
|
|
1481
|
+
if (held2.get(p.pointerId) && p.buttons === 0) held2.delete(p.pointerId);
|
|
1340
1482
|
};
|
|
1341
1483
|
const blur = () => {
|
|
1342
|
-
|
|
1484
|
+
held2.clear();
|
|
1343
1485
|
};
|
|
1344
1486
|
element.addEventListener("pointerdown", down, true);
|
|
1345
1487
|
doc.addEventListener("pointerup", up, true);
|
|
@@ -1348,7 +1490,7 @@ function attach(element) {
|
|
|
1348
1490
|
win?.addEventListener("blur", blur);
|
|
1349
1491
|
return {
|
|
1350
1492
|
readers: 0,
|
|
1351
|
-
held,
|
|
1493
|
+
held: held2,
|
|
1352
1494
|
detach() {
|
|
1353
1495
|
element.removeEventListener("pointerdown", down, true);
|
|
1354
1496
|
doc.removeEventListener("pointerup", up, true);
|
|
@@ -1421,6 +1563,6 @@ function focusedControlOwnsKey(e) {
|
|
|
1421
1563
|
return activatesFocusedControl(e) || selectOwnsKey(e);
|
|
1422
1564
|
}
|
|
1423
1565
|
|
|
1424
|
-
export { ACTIVE_SIGILS, CONTROL_SELECTOR, DOUBLE_CLICK_MAX_MS, DOUBLE_CLICK_MAX_PX, DRAG_THRESHOLD_PX, GESTURE_DESCRIPTORS, LONG_PRESS_MS, MODIFIER_LABELS, 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, detectPlatform, fixedSeat, focusedControlOwnsKey, formatKeyRoute, formatPhaseAtom, formatRoute, formatShortcut, formatShortcutParts, getGestureDescriptor, heldPointers, hitPredicate, isApplePlatform, isEditableTarget, isInControlWithin, isKnownGestureName, keyRouteToSpec, keySpecFromKey, keySpecsFromMods, keySpecsFromShortcut, longPressEvents, matchIngestTypes, matchKey, matchModifiers, matchPhase, matchSpec, matchTarget, mimeMatchesGlob, modifierComboToParsed, mods, multiTouchGeometry, nearestPortalHost, parseKeyRoute, parseRoute, parseTargetSpec, pastDragThreshold, routeGestureForSpecKind, routeToSpec, specKindForRouteGesture, specificity, stylusOf };
|
|
1566
|
+
export { ACTIVE_SIGILS, CONTROL_SELECTOR, DOUBLE_CLICK_MAX_MS, DOUBLE_CLICK_MAX_PX, DRAG_THRESHOLD_PX, GESTURE_DESCRIPTORS, LONG_PRESS_MS, MODIFIER_LABELS, MOD_NAME_SET, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, RESERVED_SIGILS, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, SHORTCUT_LEADER_SEPARATOR, TAP_THRESHOLD_PX, UNKNOWN_MIME, VALID_MOD_NAMES, activatesFocusedControl, bindingSpec, bodyPredicate, canonicalModifiers, clickEvents, collapseShiftPairs, createPointerRecognizer, describeRoute, describeRouteParts, detectPlatform, fixedSeat, focusedControlOwnsKey, formatKeyRoute, formatPhaseAtom, formatRoute, formatShortcut, formatShortcutParts, getGestureDescriptor, held, heldPointers, hitPredicate, isApplePlatform, isEditableTarget, isInControlWithin, isKnownGestureName, key, keyRouteToSpec, keySpecFromCode, keySpecFromKey, keySpecRoute, keySpecsFromMods, keySpecsFromShortcut, longPressEvents, matchIngestTypes, matchKey, matchModifiers, matchPhase, matchSpec, matchTarget, mimeMatchesGlob, modifierComboToParsed, mods, multiTouchGeometry, nearestPortalHost, parseKeyRoute, parseRoute, parseTargetSpec, pastDragThreshold, routeGestureForSpecKind, routeToSpec, specKindForRouteGesture, specificity, stylusOf, unbound };
|
|
1425
1567
|
//# sourceMappingURL=index.js.map
|
|
1426
1568
|
//# sourceMappingURL=index.js.map
|