@weasel-js/gestures 1.10.1 → 1.11.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 +195 -13
- package/dist/index.js +160 -26
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { MenuShortcut } from '@weasel-js/menu';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Declarative gesture taxonomy. Single source of truth for:
|
|
3
5
|
* - which gestures hit-test (have a `.target` slot in the route string)
|
|
@@ -7,7 +9,7 @@
|
|
|
7
9
|
* Reflection, matcher, and inspector UI all read this table. Adding a new
|
|
8
10
|
* gesture name in one place updates every consumer.
|
|
9
11
|
*/
|
|
10
|
-
type GestureName = 'click' | 'pointerDown' | 'hover' | 'dblTap' | 'drag' | 'wheel' | 'pinch' | 'keyDown' | 'keyUp' | 'keyHeld' | 'contextMenu' | 'longPress' | 'multiTouch' | 'multiTouchTap' | 'drop' | 'paste';
|
|
12
|
+
type GestureName = 'click' | 'pointerDown' | 'hover' | 'dblTap' | 'drag' | 'wheel' | 'pinch' | 'keyDown' | 'keyUp' | 'keyHeld' | 'contextMenu' | 'longPress' | 'multiTouch' | 'multiTouchTap' | 'drop' | 'carry' | 'paste';
|
|
11
13
|
/**
|
|
12
14
|
* Gesture kinds an affordance claim can bar, in the spec vocabulary bindings
|
|
13
15
|
* are written in. `'pointer'` is one token because `pointerDown` / `click` /
|
|
@@ -397,6 +399,24 @@ interface DropSpec {
|
|
|
397
399
|
mods?: ModSpec;
|
|
398
400
|
phase?: PhaseSpec;
|
|
399
401
|
}
|
|
402
|
+
/**
|
|
403
|
+
* A press that began elsewhere on the page, carried over the canvas: a palette
|
|
404
|
+
* item dragged in, a row dragged out of a list. The in-app sibling of
|
|
405
|
+
* {@link DropSpec}, which is the OS's drag. A carry is begun with `startCarry`.
|
|
406
|
+
* `types` filters by the payload's `type`, as globs; omitted or empty matches
|
|
407
|
+
* any carry.
|
|
408
|
+
*
|
|
409
|
+
* An ongoing action bound here starts when the carry comes over the surface:
|
|
410
|
+
* its moves update it, the release commits it, and leaving the surface or
|
|
411
|
+
* canceling the press cancels it. An immediate action fires once, on the
|
|
412
|
+
* release.
|
|
413
|
+
*/
|
|
414
|
+
interface CarrySpec {
|
|
415
|
+
kind: 'carry';
|
|
416
|
+
types?: string[];
|
|
417
|
+
mods?: ModSpec;
|
|
418
|
+
phase?: PhaseSpec;
|
|
419
|
+
}
|
|
400
420
|
/** System-clipboard paste of external content. Same `types` semantics as
|
|
401
421
|
* {@link DropSpec} — omitted or empty = matches any paste. */
|
|
402
422
|
interface PasteSpec {
|
|
@@ -406,7 +426,7 @@ interface PasteSpec {
|
|
|
406
426
|
phase?: PhaseSpec;
|
|
407
427
|
}
|
|
408
428
|
/** The full union of supported gesture spec kinds. */
|
|
409
|
-
type GestureSpec = KeySpec | KeyHeldSpec | WheelSpec | PinchSpec | ClickSpec | DoubleClickSpec | ContextMenuSpec | DragSpec | PointerDownSpec | HoverSpec | LongPressSpec | MultiTouchSpec | MultiTouchTapSpec | DropSpec | PasteSpec;
|
|
429
|
+
type GestureSpec = KeySpec | KeyHeldSpec | WheelSpec | PinchSpec | ClickSpec | DoubleClickSpec | ContextMenuSpec | DragSpec | PointerDownSpec | HoverSpec | LongPressSpec | MultiTouchSpec | MultiTouchTapSpec | DropSpec | CarrySpec | PasteSpec;
|
|
410
430
|
|
|
411
431
|
/**
|
|
412
432
|
* Mini-grammar for the `arg` slot of `keyDown` / `keyUp` routes.
|
|
@@ -447,7 +467,7 @@ declare function keyRouteToSpec(r: ParsedKeyRoute): KeySpec;
|
|
|
447
467
|
* Wildcards become omitted fields: a `*` target, direction or MIME list, and a
|
|
448
468
|
* `[*:*]` phase slot. `[*]` is `[&:*]`, which an ambient binding never
|
|
449
469
|
* matches, so it stays a phase atom. A `keyDown` / `keyHeld` arg is read as a
|
|
450
|
-
* key route, so `keyDown(z?shift)` yields `mods.shift: 'optional'`; a drop or
|
|
470
|
+
* key route, so `keyDown(z?shift)` yields `mods.shift: 'optional'`; a drop, carry, or
|
|
451
471
|
* paste arg is a `|`-separated MIME list.
|
|
452
472
|
*
|
|
453
473
|
* Throws on a route no spec can express: `keyUp`, a key or finger-count
|
|
@@ -519,13 +539,6 @@ declare function modifierComboToParsed(key: ModifierCombo): ParsedModifiers;
|
|
|
519
539
|
* `name=req` pairs joined by `&`. */
|
|
520
540
|
declare function canonicalModifiers(mods: ParsedModifiers): string;
|
|
521
541
|
|
|
522
|
-
/** Keyboard/pointer modifier flags carried by every {@link InputEvent} arm. */
|
|
523
|
-
interface EventModifiers {
|
|
524
|
-
altKey: boolean;
|
|
525
|
-
ctrlKey: boolean;
|
|
526
|
-
metaKey: boolean;
|
|
527
|
-
shiftKey: boolean;
|
|
528
|
-
}
|
|
529
542
|
/**
|
|
530
543
|
* Body-target classification from the scene hit-test. Populated by
|
|
531
544
|
* `useGestureDispatcher` when a `classifyTarget` thunk is supplied to its
|
|
@@ -552,6 +565,28 @@ interface BodyClassification {
|
|
|
552
565
|
/** Semantic kind of the hit body. Undefined on empty canvas, and undefined
|
|
553
566
|
* on a body whose kind the scene can't name — never guessed. */
|
|
554
567
|
kind?: BodyKind;
|
|
568
|
+
/** The node classified. Matching ignores it. */
|
|
569
|
+
id?: string;
|
|
570
|
+
/** What a double-click at this point names, where that is not the body hit: the group a hit
|
|
571
|
+
* member belongs to. A double click's targets match against it. Absent, the hit is named. */
|
|
572
|
+
doubleClick?: {
|
|
573
|
+
body: BodyTarget;
|
|
574
|
+
kind?: BodyKind;
|
|
575
|
+
id?: string;
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
/** The body a double-click names, as a press carries it to the double click it composes. */
|
|
579
|
+
interface DoubleClickBody {
|
|
580
|
+
bodyTarget: BodyTarget;
|
|
581
|
+
bodyKind?: BodyKind;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/** Keyboard/pointer modifier flags carried by every {@link InputEvent} arm. */
|
|
585
|
+
interface EventModifiers {
|
|
586
|
+
altKey: boolean;
|
|
587
|
+
ctrlKey: boolean;
|
|
588
|
+
metaKey: boolean;
|
|
589
|
+
shiftKey: boolean;
|
|
555
590
|
}
|
|
556
591
|
/** A keystroke. */
|
|
557
592
|
interface KeyEvent extends EventModifiers {
|
|
@@ -850,6 +885,21 @@ interface DropEvent extends EventModifiers {
|
|
|
850
885
|
clientX?: number;
|
|
851
886
|
clientY?: number;
|
|
852
887
|
}
|
|
888
|
+
/** A carry over the canvas (`startCarry`). `stage` is what it did here: came
|
|
889
|
+
* over, moved, was released, or left — off the surface, or canceled on it. */
|
|
890
|
+
interface CarryEvent extends EventModifiers {
|
|
891
|
+
kind: 'carry';
|
|
892
|
+
stage: 'enter' | 'move' | 'drop' | 'leave';
|
|
893
|
+
pointerId?: number;
|
|
894
|
+
/** The payload's type, which a spec's `types` globs filter, and the payload. */
|
|
895
|
+
type: string;
|
|
896
|
+
value: unknown;
|
|
897
|
+
/** World-space position (post view transform). */
|
|
898
|
+
x: number;
|
|
899
|
+
y: number;
|
|
900
|
+
clientX: number;
|
|
901
|
+
clientY: number;
|
|
902
|
+
}
|
|
853
903
|
/** External content pasted from the system clipboard. Carries no point. */
|
|
854
904
|
interface PasteEvent extends EventModifiers {
|
|
855
905
|
kind: 'paste';
|
|
@@ -864,7 +914,7 @@ interface PasteEvent extends EventModifiers {
|
|
|
864
914
|
* The `affordance?` field on {@link PointerDownEvent} is widened to `unknown`
|
|
865
915
|
* in this package — the kit narrows it back to `AffordanceHit` at consumption.
|
|
866
916
|
*/
|
|
867
|
-
type InputEvent = KeyEvent | KeyHeldEvent | WheelEvent | PinchEvent | PointerDownEvent | PointerMoveEvent | HoverEvent | PointerUpEvent | PointerCancelEvent | ClickEvent | DoubleClickEvent | ContextMenuEvent | LongPressEvent | MultiTouchEvent | MultiTouchTapEvent | DropEvent | PasteEvent;
|
|
917
|
+
type InputEvent = KeyEvent | KeyHeldEvent | WheelEvent | PinchEvent | PointerDownEvent | PointerMoveEvent | HoverEvent | PointerUpEvent | PointerCancelEvent | ClickEvent | DoubleClickEvent | ContextMenuEvent | LongPressEvent | MultiTouchEvent | MultiTouchTapEvent | DropEvent | CarryEvent | PasteEvent;
|
|
868
918
|
|
|
869
919
|
/**
|
|
870
920
|
* The stylus fields `src` carries, as a spread-able partial: a sample that
|
|
@@ -998,6 +1048,126 @@ declare function matchIngestTypes(items: readonly {
|
|
|
998
1048
|
*/
|
|
999
1049
|
declare function isApplePlatform(): boolean;
|
|
1000
1050
|
|
|
1051
|
+
/** One key of a shortcut as a keyboard prints it. */
|
|
1052
|
+
interface KeyLegend {
|
|
1053
|
+
/** Glyph drawn for the key (modifier or key). */
|
|
1054
|
+
label: string;
|
|
1055
|
+
/** When true, the key may be held but is not required to trigger the action
|
|
1056
|
+
* (e.g. an optional modifier). Defaults to false. */
|
|
1057
|
+
optional?: boolean;
|
|
1058
|
+
}
|
|
1059
|
+
/** Logical modifier name. Matches the kit's `ModifierKey` in shape — kept as a
|
|
1060
|
+
* local type so `weasel-ui` stays independent of `@weasel-js/core`. */
|
|
1061
|
+
type LogicalMod = 'mod' | 'shift' | 'alt' | 'ctrl' | 'meta';
|
|
1062
|
+
/** Detected (or overridden) platform. `'mod'` resolves to ⌘ on macOS,
|
|
1063
|
+
* Ctrl on Windows / Linux. */
|
|
1064
|
+
type Platform = 'macos' | 'windows' | 'linux';
|
|
1065
|
+
/** Visual form of a modifier or named-key label.
|
|
1066
|
+
*
|
|
1067
|
+
* - `'auto'` (default): the per-entry, per-platform default — whatever
|
|
1068
|
+
* label is actually printed on the physical key. macOS keys show the
|
|
1069
|
+
* Apple glyphs (⌘ ⌥ ⌃ ⇧ ⎋ ↵ ⇥). Windows keys mostly show text
|
|
1070
|
+
* (Ctrl, Alt, Shift) BUT the Win key shows its logo (⊞) and the
|
|
1071
|
+
* context-menu key shows ▤. Linux mirrors Windows with Super (⊞).
|
|
1072
|
+
* - `'symbol'` forces a glyph wherever the key has one: Apple's on macOS,
|
|
1073
|
+
* ISO/IEC 9995-7's elsewhere (⎈ Ctrl, ⎇ Alt, ⎋ Esc). A key with no
|
|
1074
|
+
* symbol still falls back to text.
|
|
1075
|
+
* - `'text'` always spells the label out (Cmd / Option / Esc / Enter). */
|
|
1076
|
+
type LegendStyle = 'auto' | 'symbol' | 'text';
|
|
1077
|
+
/** A modifier to render, named platform-independently. */
|
|
1078
|
+
interface LogicalModSpec {
|
|
1079
|
+
name: LogicalMod;
|
|
1080
|
+
/** Marks this modifier as optional (may be held but isn't required).
|
|
1081
|
+
* Forwarded as the resulting `KeyLegend.optional`. */
|
|
1082
|
+
optional?: boolean;
|
|
1083
|
+
}
|
|
1084
|
+
/** Options for {@link keySpecsFromMods}. */
|
|
1085
|
+
interface KeySpecsFromModsOptions {
|
|
1086
|
+
/** Override OS detection. Defaults to {@link detectPlatform}. */
|
|
1087
|
+
platform?: Platform;
|
|
1088
|
+
/** Defaults to `'auto'`. Pass `'symbol'` or `'text'` to force a specific
|
|
1089
|
+
* style regardless of platform. */
|
|
1090
|
+
legend?: LegendStyle;
|
|
1091
|
+
/** Labels that replace the platform and legend tables', by a modifier's
|
|
1092
|
+
* logical name (`mod`, `shift`, `alt`, `ctrl`, `meta`) or a key's
|
|
1093
|
+
* `KeyboardEvent.key` (`Escape`, `Home`, `z`): a whole keyboard's worth
|
|
1094
|
+
* for a layout, a locale, or a keyboard of one's own. */
|
|
1095
|
+
labels?: KeyLabels;
|
|
1096
|
+
}
|
|
1097
|
+
/** Key labels by modifier name or `KeyboardEvent.key`. See {@link KeySpecsFromModsOptions.labels}. */
|
|
1098
|
+
type KeyLabels = Readonly<Record<string, string>>;
|
|
1099
|
+
/** Every label {@link keySpecsFromMods} can produce, on any platform and legend. */
|
|
1100
|
+
declare const MODIFIER_LABELS: ReadonlySet<string>;
|
|
1101
|
+
/** The platform whose key legends to draw. Whether `mod` is ⌘ comes from
|
|
1102
|
+
* `isApplePlatform`, the same read the dispatcher
|
|
1103
|
+
* matches `mod` with, so a keycap names the key that fires. With no
|
|
1104
|
+
* `navigator` (a server render) it answers `'macos'`, the kit's docs
|
|
1105
|
+
* convention; an unrecognized browser reads as `'linux'`. */
|
|
1106
|
+
declare function detectPlatform(): Platform;
|
|
1107
|
+
/**
|
|
1108
|
+
* Map a list of **logical** modifiers (`'mod'`, `'shift'`, …) to **visual**
|
|
1109
|
+
* `KeyLegend`s appropriate for the current platform — `'mod'` means "Cmd on
|
|
1110
|
+
* Mac, Ctrl on Win/Linux".
|
|
1111
|
+
*
|
|
1112
|
+
* ```ts
|
|
1113
|
+
* <KeySequence
|
|
1114
|
+
* keys={keySpecsFromMods([{ name: 'mod' }, { name: 'shift', optional: true }])}
|
|
1115
|
+
* />
|
|
1116
|
+
* ```
|
|
1117
|
+
*/
|
|
1118
|
+
declare function keySpecsFromMods(mods: readonly LogicalModSpec[], opts?: KeySpecsFromModsOptions): readonly KeyLegend[];
|
|
1119
|
+
/**
|
|
1120
|
+
* Render a raw `KeyboardEvent.key` value as a platform-appropriate
|
|
1121
|
+
* `KeyLegend`. Named non-modifier keys (Esc, Enter, Tab, Space, Backspace,
|
|
1122
|
+
* Delete, arrows) get the platform's conventional form. Letter / digit /
|
|
1123
|
+
* other keys pass through upper-cased.
|
|
1124
|
+
*
|
|
1125
|
+
* ```ts
|
|
1126
|
+
* keySpecFromKey('Escape', { platform: 'macos' }) // { label: '⎋' }
|
|
1127
|
+
* keySpecFromKey('Escape', { platform: 'windows' }) // { label: 'Esc' }
|
|
1128
|
+
* keySpecFromKey('p') // { label: 'P' }
|
|
1129
|
+
* ```
|
|
1130
|
+
*
|
|
1131
|
+
* Symbol mode off macOS draws ISO/IEC 9995-7's symbols where the key
|
|
1132
|
+
* has one, though Windows and Linux keyboards print the text form.
|
|
1133
|
+
*/
|
|
1134
|
+
declare function keySpecFromKey(raw: string, opts?: KeySpecsFromModsOptions & {
|
|
1135
|
+
optional?: boolean;
|
|
1136
|
+
}): KeyLegend;
|
|
1137
|
+
|
|
1138
|
+
/** A keyboard shortcut as the formatters read it: `@weasel-js/menu`'s `MenuShortcut`. */
|
|
1139
|
+
type ShortcutInput = MenuShortcut;
|
|
1140
|
+
/** Options for {@link formatShortcut} and {@link formatShortcutParts}. */
|
|
1141
|
+
interface FormatShortcutOptions {
|
|
1142
|
+
/** Defaults to the detected platform. */
|
|
1143
|
+
platform?: Platform;
|
|
1144
|
+
}
|
|
1145
|
+
/** Format a shortcut as an array of display chips, one per key: the
|
|
1146
|
+
* modifiers, then the key, each as the platform's keyboard prints it, so
|
|
1147
|
+
* `mod` is `⌘` on macOS and `Ctrl` elsewhere. UIs that want a boxed per-key
|
|
1148
|
+
* look render each chip in its own element. `shift: 'optional'` is treated
|
|
1149
|
+
* as falsy. */
|
|
1150
|
+
declare function formatShortcutParts(b: ShortcutInput | undefined, opts?: FormatShortcutOptions): readonly string[] | undefined;
|
|
1151
|
+
/** Format a shortcut as a single display string. Order: mod, shift, alt,
|
|
1152
|
+
* key. On macOS the glyphs run together (`⌘⇧Z`); elsewhere the keys are
|
|
1153
|
+
* spelled out and joined with `+` (`Ctrl+Shift+Z`). Returns `undefined` for
|
|
1154
|
+
* `undefined` input so callers can `??` a fallback. When `key` is an array,
|
|
1155
|
+
* the first element is used. `shift: 'optional'` is treated as falsy. */
|
|
1156
|
+
declare function formatShortcut(b: ShortcutInput | undefined, opts?: FormatShortcutOptions): string | undefined;
|
|
1157
|
+
|
|
1158
|
+
/**
|
|
1159
|
+
* A shortcut as key legends, one per key, as `@weasel-js/ui`'s `KeySequence` draws them: mod, shift, alt, then the
|
|
1160
|
+
* key, each in the platform's own legend (`⌘` on macOS, `Ctrl` elsewhere).
|
|
1161
|
+
* `formatShortcutParts` is these labels as strings. `shift: 'optional'`
|
|
1162
|
+
* becomes an optional key; a key list
|
|
1163
|
+
* renders its first key. Returns `undefined` for no shortcut.
|
|
1164
|
+
*
|
|
1165
|
+
* ```tsx
|
|
1166
|
+
* <KeySequence keys={keySpecsFromShortcut({ key: 'z', mod: true, shift: true })} />
|
|
1167
|
+
* ```
|
|
1168
|
+
*/
|
|
1169
|
+
declare function keySpecsFromShortcut(shortcut: ShortcutInput | undefined, opts?: KeySpecsFromModsOptions): readonly KeyLegend[] | undefined;
|
|
1170
|
+
|
|
1001
1171
|
/**
|
|
1002
1172
|
* The nearest element `from` sits inside that can host a portalled overlay,
|
|
1003
1173
|
* or `null` when there is none and the body is the only answer.
|
|
@@ -1027,7 +1197,7 @@ declare function fixedSeat(from: Element | null | undefined): FixedSeat;
|
|
|
1027
1197
|
* [1] mods — count of required modifier keys (shift/alt/ctrl/meta/mod).
|
|
1028
1198
|
* `'optional'` does NOT count.
|
|
1029
1199
|
* [2] phase — how much the spec's `phase` narrows; see `phaseRank`.
|
|
1030
|
-
* [3] exact — per-kind tiebreak: 2 for a drop/paste spec with a
|
|
1200
|
+
* [3] exact — per-kind tiebreak: 2 for a drop/carry/paste spec with a
|
|
1031
1201
|
* non-empty `types` MIME filter, else 1.
|
|
1032
1202
|
*
|
|
1033
1203
|
* Identical tuples fall back to registration order in the matcher's
|
|
@@ -1102,6 +1272,8 @@ interface PressFacts extends EventModifiers {
|
|
|
1102
1272
|
affordance?: unknown;
|
|
1103
1273
|
bodyTarget?: BodyTarget;
|
|
1104
1274
|
bodyKind?: BodyKind;
|
|
1275
|
+
/** Replayed onto the double click in place of `bodyTarget` and `bodyKind`. */
|
|
1276
|
+
doubleClickBody?: DoubleClickBody;
|
|
1105
1277
|
}
|
|
1106
1278
|
/** Where and how a press was released, for the click it turned out to be. */
|
|
1107
1279
|
interface ReleaseFacts extends EventModifiers {
|
|
@@ -1195,6 +1367,8 @@ interface PressSample extends PointerSample {
|
|
|
1195
1367
|
affordance?: unknown;
|
|
1196
1368
|
bodyTarget?: BodyTarget;
|
|
1197
1369
|
bodyKind?: BodyKind;
|
|
1370
|
+
/** What a double click this press completes lands on, where not the body hit. */
|
|
1371
|
+
doubleClickBody?: DoubleClickBody;
|
|
1198
1372
|
}
|
|
1199
1373
|
|
|
1200
1374
|
/**
|
|
@@ -1233,6 +1407,14 @@ interface PointerRecognizerOptions {
|
|
|
1233
1407
|
toSurface?: (client: Point) => Point;
|
|
1234
1408
|
/** Long-press hold time in ms, read at each press. Default `LONG_PRESS_MS`. */
|
|
1235
1409
|
longPressMs?: () => number | undefined;
|
|
1410
|
+
/** Longest gap in ms between two clicks of a double click, read at each click. Default `DOUBLE_CLICK_MAX_MS`. */
|
|
1411
|
+
doubleClickMs?: () => number | undefined;
|
|
1412
|
+
/** Farthest two clicks of a double click may land apart, in CSS px. Default `DOUBLE_CLICK_MAX_PX`. */
|
|
1413
|
+
doubleClickPx?: () => number | undefined;
|
|
1414
|
+
/** Farthest a multitouch tap's fingers may travel, in CSS px. Default `TAP_THRESHOLD_PX`. */
|
|
1415
|
+
tapPx?: () => number | undefined;
|
|
1416
|
+
/** Travel in CSS px that makes a press a drag, read at each move. Default `DRAG_THRESHOLD_PX`. */
|
|
1417
|
+
dragPx?: () => number | undefined;
|
|
1236
1418
|
clicks?: ClickMemory;
|
|
1237
1419
|
observer?: RecognizerObserver;
|
|
1238
1420
|
}
|
|
@@ -1303,4 +1485,4 @@ declare function activatesFocusedControl(e: KeyboardEvent): boolean;
|
|
|
1303
1485
|
*/
|
|
1304
1486
|
declare function focusedControlOwnsKey(e: KeyboardEvent): boolean;
|
|
1305
1487
|
|
|
1306
|
-
export { ACTIVE_SIGILS, type BodyClassification, type BodyKind, type BodyTarget, CONTROL_SELECTOR, type ChannelRef, type ClaimableGesture, type ClickEvent, type ClickMemory, type ClickObserver, type ClickSpec, type ContextMenuEvent, type ContextMenuSpec, DOUBLE_CLICK_MAX_MS, DOUBLE_CLICK_MAX_PX, DRAG_THRESHOLD_PX, type DescribeRouteOptions, type DoubleClickEvent, type DoubleClickSpec, type DragSpec, type DropEvent, type DropSpec, type EventModifiers, type FixedSeat, GESTURE_DESCRIPTORS, type GestureArgSpec, type GestureDescriptor, type GestureName, type GestureSpec, type HeldPointers, type HeldPress, type HoverEvent, type HoverSpec, type HoverWhere, type IngestItem, type InputEvent, type KeyEvent, type KeyHeldEvent, type KeyHeldSpec, type KeySpec, LONG_PRESS_MS, type LongPressEvent, type LongPressSpec, MOD_NAME_SET, type ModRequirement, type ModSpec, type ModifierCombo, type ModifierKey, type ModifiersEvent, type MultiTouchEvent, type MultiTouchGeometry, type MultiTouchSpec, type MultiTouchTapEvent, type MultiTouchTapSpec, type OptionalMod, type ParsedKeyRoute, type ParsedModifiers, type ParsedRoute, type PasteEvent, type PasteSpec, type PhaseAtom, type PhaseContext, type PhaseSpec, type PinchEvent, type PinchSpec, type Point, type PointerCancelEvent, type PointerDownEvent, type PointerDownSpec, type PointerIdentity, type PointerMoveEvent, type PointerRecognizer, type PointerRecognizerOptions, type PointerSample, type PointerSampleStylus, type PointerUpEvent, type PressFacts, type PressSample, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, RESERVED_SIGILS, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, type RecognizerClock, type RecognizerObserver, type RecognizerSink, type ReleaseFacts, type ReleaseOutcome, type RouteDescriptionPart, type RouteFieldName, type RoutePhase, type RouteTermLabel, TAP_THRESHOLD_PX, type TargetPredicate, type TargetSpec, type TargetSpecForm, UNKNOWN_MIME, VALID_MOD_NAMES, type WheelEvent, type WheelSpec, activatesFocusedControl, bodyPredicate, canonicalModifiers, clickEvents, collapseShiftPairs, createPointerRecognizer, describeRoute, describeRouteParts, fixedSeat, focusedControlOwnsKey, formatKeyRoute, formatPhaseAtom, formatRoute, getGestureDescriptor, heldPointers, 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 };
|
|
1488
|
+
export { ACTIVE_SIGILS, type BodyClassification, type BodyKind, type BodyTarget, CONTROL_SELECTOR, type CarryEvent, type CarrySpec, type ChannelRef, type ClaimableGesture, type ClickEvent, type ClickMemory, type ClickObserver, type ClickSpec, type ContextMenuEvent, type ContextMenuSpec, DOUBLE_CLICK_MAX_MS, DOUBLE_CLICK_MAX_PX, DRAG_THRESHOLD_PX, type DescribeRouteOptions, type DoubleClickBody, type DoubleClickEvent, type DoubleClickSpec, type DragSpec, type DropEvent, type DropSpec, type EventModifiers, type FixedSeat, type FormatShortcutOptions, GESTURE_DESCRIPTORS, type GestureArgSpec, type GestureDescriptor, type GestureName, type GestureSpec, type HeldPointers, type HeldPress, type HoverEvent, type HoverSpec, type HoverWhere, type IngestItem, type InputEvent, type KeyEvent, type KeyHeldEvent, type KeyHeldSpec, type KeyLabels, type KeyLegend, type KeySpec, type KeySpecsFromModsOptions, LONG_PRESS_MS, type LegendStyle, type LogicalMod, type LogicalModSpec, type LongPressEvent, type LongPressSpec, MODIFIER_LABELS, MOD_NAME_SET, type ModRequirement, type ModSpec, type ModifierCombo, type ModifierKey, type ModifiersEvent, type MultiTouchEvent, type MultiTouchGeometry, type MultiTouchSpec, type MultiTouchTapEvent, type MultiTouchTapSpec, type OptionalMod, type ParsedKeyRoute, type ParsedModifiers, type ParsedRoute, type PasteEvent, type PasteSpec, type PhaseAtom, type PhaseContext, type PhaseSpec, type PinchEvent, type PinchSpec, type Platform, type Point, type PointerCancelEvent, type PointerDownEvent, type PointerDownSpec, type PointerIdentity, type PointerMoveEvent, type PointerRecognizer, type PointerRecognizerOptions, type PointerSample, type PointerSampleStylus, type PointerUpEvent, type PressFacts, type PressSample, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, RESERVED_SIGILS, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, type RecognizerClock, type RecognizerObserver, type RecognizerSink, type ReleaseFacts, type ReleaseOutcome, type RouteDescriptionPart, type RouteFieldName, type RoutePhase, type RouteTermLabel, type ShortcutInput, TAP_THRESHOLD_PX, type TargetPredicate, type TargetSpec, type TargetSpecForm, UNKNOWN_MIME, VALID_MOD_NAMES, type WheelEvent, type WheelSpec, 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 };
|
package/dist/index.js
CHANGED
|
@@ -18,6 +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: false, arg: { name: "types", values: "free" } },
|
|
21
22
|
{ name: "paste", hasTarget: false, arg: { name: "types", values: "free" } }
|
|
22
23
|
];
|
|
23
24
|
var BY_NAME = new Map(
|
|
@@ -490,21 +491,25 @@ function matchSpec(e, spec, isMac, phaseCtx) {
|
|
|
490
491
|
if (e.fingers !== spec.fingers) return false;
|
|
491
492
|
return matchModifiers(e, spec.mods, isMac);
|
|
492
493
|
}
|
|
493
|
-
case "drop":
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
return
|
|
497
|
-
}
|
|
498
|
-
case "paste": {
|
|
499
|
-
if (e.kind !== "paste") return false;
|
|
500
|
-
if (!matchIngestTypes(e.items, spec.types)) return false;
|
|
501
|
-
return matchModifiers(e, spec.mods, isMac);
|
|
502
|
-
}
|
|
494
|
+
case "drop":
|
|
495
|
+
case "carry":
|
|
496
|
+
case "paste":
|
|
497
|
+
return matchArrival(e, spec, isMac);
|
|
503
498
|
default: {
|
|
504
499
|
return false;
|
|
505
500
|
}
|
|
506
501
|
}
|
|
507
502
|
}
|
|
503
|
+
function matchArrival(e, spec, isMac) {
|
|
504
|
+
if (e.kind !== spec.kind) return false;
|
|
505
|
+
if (e.kind === "carry") {
|
|
506
|
+
if (e.stage !== "enter" && e.stage !== "drop") return false;
|
|
507
|
+
if (!matchIngestTypes([{ mime: e.type }], spec.types)) return false;
|
|
508
|
+
} else if (e.kind === "drop" || e.kind === "paste") {
|
|
509
|
+
if (!matchIngestTypes(e.items, spec.types)) return false;
|
|
510
|
+
}
|
|
511
|
+
return matchModifiers(e, spec.mods, isMac);
|
|
512
|
+
}
|
|
508
513
|
var EMPTY_ENGAGED = /* @__PURE__ */ new Set();
|
|
509
514
|
|
|
510
515
|
// src/grammar/specKinds.ts
|
|
@@ -523,6 +528,7 @@ var SPEC_KIND_TO_GESTURE = {
|
|
|
523
528
|
multiTouch: "multiTouch",
|
|
524
529
|
multiTouchTap: "multiTouchTap",
|
|
525
530
|
drop: "drop",
|
|
531
|
+
carry: "carry",
|
|
526
532
|
paste: "paste"
|
|
527
533
|
};
|
|
528
534
|
var GESTURE_TO_SPEC_KIND = new Map(
|
|
@@ -593,6 +599,7 @@ function routeToSpec(parsed) {
|
|
|
593
599
|
if (arg === void 0) return fail(`a ${kind} spec needs concrete fingers`);
|
|
594
600
|
return withMods({ kind, fingers: Number(arg) });
|
|
595
601
|
case "drop":
|
|
602
|
+
case "carry":
|
|
596
603
|
case "paste":
|
|
597
604
|
return withMods({ kind, ...arg ? { types: arg.split("|") } : {} });
|
|
598
605
|
}
|
|
@@ -710,6 +717,8 @@ function actionClause(parsed, required) {
|
|
|
710
717
|
return userDoes(required, `drags${target}`);
|
|
711
718
|
case "drop":
|
|
712
719
|
return userDoes(required, `drops ${arg ?? "any"} content onto the canvas`);
|
|
720
|
+
case "carry":
|
|
721
|
+
return userDoes(required, `carries ${arg ?? "anything"} onto the canvas`);
|
|
713
722
|
case "paste":
|
|
714
723
|
return userDoes(required, `pastes ${arg ?? "any"} content`);
|
|
715
724
|
}
|
|
@@ -784,6 +793,115 @@ function isApplePlatform() {
|
|
|
784
793
|
return /mac|iphone|ipad|ipod/i.test(raw);
|
|
785
794
|
}
|
|
786
795
|
|
|
796
|
+
// src/ui/keySpecsFromMods.ts
|
|
797
|
+
var MOD_BY_PLATFORM = {
|
|
798
|
+
mod: {
|
|
799
|
+
macos: { auto: "\u2318", symbol: "\u2318", text: "Cmd" },
|
|
800
|
+
windows: { auto: "Ctrl", symbol: "\u2388", text: "Ctrl" },
|
|
801
|
+
linux: { auto: "Ctrl", symbol: "\u2388", text: "Ctrl" }
|
|
802
|
+
},
|
|
803
|
+
shift: {
|
|
804
|
+
macos: { auto: "\u21E7", symbol: "\u21E7", text: "Shift" },
|
|
805
|
+
windows: { auto: "\u21E7", symbol: "\u21E7", text: "Shift" },
|
|
806
|
+
linux: { auto: "\u21E7", symbol: "\u21E7", text: "Shift" }
|
|
807
|
+
},
|
|
808
|
+
// Off macOS the symbols are ISO/IEC 9995-7's: ⎈ for Control, ⎇ for Alt.
|
|
809
|
+
alt: {
|
|
810
|
+
macos: { auto: "\u2325", symbol: "\u2325", text: "Option" },
|
|
811
|
+
windows: { auto: "Alt", symbol: "\u2387", text: "Alt" },
|
|
812
|
+
linux: { auto: "Alt", symbol: "\u2387", text: "Alt" }
|
|
813
|
+
},
|
|
814
|
+
ctrl: {
|
|
815
|
+
macos: { auto: "\u2303", symbol: "\u2303", text: "Control" },
|
|
816
|
+
windows: { auto: "Ctrl", symbol: "\u2388", text: "Ctrl" },
|
|
817
|
+
linux: { auto: "Ctrl", symbol: "\u2388", text: "Ctrl" }
|
|
818
|
+
},
|
|
819
|
+
// Windows/Linux Meta has a recognized symbol on the physical key (⊞ Win
|
|
820
|
+
// logo / Super logo), so `auto` shows it. Use `text` for Win / Super.
|
|
821
|
+
meta: {
|
|
822
|
+
macos: { auto: "\u2318", symbol: "\u2318", text: "Cmd" },
|
|
823
|
+
windows: { auto: "\u229E", symbol: "\u229E", text: "Win" },
|
|
824
|
+
linux: { auto: "\u229E", symbol: "\u229E", text: "Super" }
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
var MODIFIER_LABELS = new Set(
|
|
828
|
+
Object.values(MOD_BY_PLATFORM).flatMap(
|
|
829
|
+
(byPlatform) => Object.values(byPlatform).flatMap((byLegend) => Object.values(byLegend))
|
|
830
|
+
)
|
|
831
|
+
);
|
|
832
|
+
function detectPlatform() {
|
|
833
|
+
if (typeof navigator === "undefined" || !navigator) return "macos";
|
|
834
|
+
if (isApplePlatform()) return "macos";
|
|
835
|
+
const nav = navigator;
|
|
836
|
+
const raw = nav.userAgentData?.platform || nav.platform || nav.userAgent || "";
|
|
837
|
+
return /\bwin/i.test(raw) ? "windows" : "linux";
|
|
838
|
+
}
|
|
839
|
+
function keySpecsFromMods(mods2, opts = {}) {
|
|
840
|
+
const platform = opts.platform ?? detectPlatform();
|
|
841
|
+
const legend = opts.legend ?? "auto";
|
|
842
|
+
return mods2.map((m) => {
|
|
843
|
+
const label = opts.labels?.[m.name] ?? MOD_BY_PLATFORM[m.name][platform][legend];
|
|
844
|
+
return m.optional ? { label, optional: true } : { label };
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
var NAMED_KEY = {
|
|
848
|
+
Escape: { macos: { auto: "\u238B", symbol: "\u238B", text: "Esc" }, windows: { auto: "Esc", symbol: "\u238B", text: "Esc" }, linux: { auto: "Esc", symbol: "\u238B", text: "Esc" } },
|
|
849
|
+
Enter: { macos: { auto: "\u21B5", symbol: "\u21B5", text: "Return" }, windows: { auto: "Enter", symbol: "\u21B5", text: "Enter" }, linux: { auto: "Enter", symbol: "\u21B5", text: "Enter" } },
|
|
850
|
+
Return: { macos: { auto: "\u21B5", symbol: "\u21B5", text: "Return" }, windows: { auto: "Enter", symbol: "\u21B5", text: "Enter" }, linux: { auto: "Enter", symbol: "\u21B5", text: "Enter" } },
|
|
851
|
+
Tab: { macos: { auto: "\u21E5", symbol: "\u21E5", text: "Tab" }, windows: { auto: "Tab", symbol: "\u21E5", text: "Tab" }, linux: { auto: "Tab", symbol: "\u21E5", text: "Tab" } },
|
|
852
|
+
Space: { macos: { auto: "\u2423", symbol: "\u2423", text: "Space" }, windows: { auto: "Space", symbol: "\u2423", text: "Space" }, linux: { auto: "Space", symbol: "\u2423", text: "Space" } },
|
|
853
|
+
" ": { macos: { auto: "\u2423", symbol: "\u2423", text: "Space" }, windows: { auto: "Space", symbol: "\u2423", text: "Space" }, linux: { auto: "Space", symbol: "\u2423", text: "Space" } },
|
|
854
|
+
Backspace: { macos: { auto: "\u232B", symbol: "\u232B", text: "Backspace" }, windows: { auto: "Backspace", symbol: "\u27F5", text: "Backspace" }, linux: { auto: "Backspace", symbol: "\u27F5", text: "Backspace" } },
|
|
855
|
+
Delete: { macos: { auto: "\u2326", symbol: "\u2326", text: "Delete" }, windows: { auto: "Delete", symbol: "\u2326", text: "Delete" }, linux: { auto: "Delete", symbol: "\u2326", text: "Delete" } },
|
|
856
|
+
// Context-menu key (the right-Ctrl-area key on Windows / Linux keyboards
|
|
857
|
+
// with a ▤ icon). Mac has no equivalent; falls back to text 'Menu'.
|
|
858
|
+
ContextMenu: { macos: { auto: "Menu", symbol: "Menu", text: "Menu" }, windows: { auto: "\u25A4", symbol: "\u25A4", text: "Menu" }, linux: { auto: "\u25A4", symbol: "\u25A4", text: "Menu" } },
|
|
859
|
+
// Symbol mode only: Apple's arrows for Home, End, and the page keys, and
|
|
860
|
+
// ISO/IEC 9995-7's symbols for the rest.
|
|
861
|
+
Home: { macos: { auto: "Home", symbol: "\u2196", text: "Home" }, windows: { auto: "Home", symbol: "\u2196", text: "Home" }, linux: { auto: "Home", symbol: "\u2196", text: "Home" } },
|
|
862
|
+
End: { macos: { auto: "End", symbol: "\u2198", text: "End" }, windows: { auto: "End", symbol: "\u2198", text: "End" }, linux: { auto: "End", symbol: "\u2198", text: "End" } },
|
|
863
|
+
PageUp: { macos: { auto: "PgUp", symbol: "\u21DE", text: "PgUp" }, windows: { auto: "PgUp", symbol: "\u21DE", text: "PgUp" }, linux: { auto: "PgUp", symbol: "\u21DE", text: "PgUp" } },
|
|
864
|
+
PageDown: { macos: { auto: "PgDn", symbol: "\u21DF", text: "PgDn" }, windows: { auto: "PgDn", symbol: "\u21DF", text: "PgDn" }, linux: { auto: "PgDn", symbol: "\u21DF", text: "PgDn" } },
|
|
865
|
+
PrintScreen: { macos: { auto: "PrtSc", symbol: "\u2399", text: "PrtSc" }, windows: { auto: "PrtSc", symbol: "\u2399", text: "PrtSc" }, linux: { auto: "PrtSc", symbol: "\u2399", text: "PrtSc" } },
|
|
866
|
+
Insert: { macos: { auto: "Ins", symbol: "\u2380", text: "Insert" }, windows: { auto: "Ins", symbol: "\u2380", text: "Insert" }, linux: { auto: "Ins", symbol: "\u2380", text: "Insert" } },
|
|
867
|
+
ScrollLock: { macos: { auto: "ScrLk", symbol: "\u21F3", text: "ScrLk" }, windows: { auto: "ScrLk", symbol: "\u21F3", text: "ScrLk" }, linux: { auto: "ScrLk", symbol: "\u21F3", text: "ScrLk" } },
|
|
868
|
+
Pause: { macos: { auto: "Pause", symbol: "\u2389", text: "Pause" }, windows: { auto: "Pause", symbol: "\u2389", text: "Pause" }, linux: { auto: "Pause", symbol: "\u2389", text: "Pause" } },
|
|
869
|
+
ArrowUp: { macos: { auto: "\u2191", symbol: "\u2191", text: "\u2191" }, windows: { auto: "\u2191", symbol: "\u2191", text: "\u2191" }, linux: { auto: "\u2191", symbol: "\u2191", text: "\u2191" } },
|
|
870
|
+
ArrowDown: { macos: { auto: "\u2193", symbol: "\u2193", text: "\u2193" }, windows: { auto: "\u2193", symbol: "\u2193", text: "\u2193" }, linux: { auto: "\u2193", symbol: "\u2193", text: "\u2193" } },
|
|
871
|
+
ArrowLeft: { macos: { auto: "\u2190", symbol: "\u2190", text: "\u2190" }, windows: { auto: "\u2190", symbol: "\u2190", text: "\u2190" }, linux: { auto: "\u2190", symbol: "\u2190", text: "\u2190" } },
|
|
872
|
+
ArrowRight: { macos: { auto: "\u2192", symbol: "\u2192", text: "\u2192" }, windows: { auto: "\u2192", symbol: "\u2192", text: "\u2192" }, linux: { auto: "\u2192", symbol: "\u2192", text: "\u2192" } }
|
|
873
|
+
};
|
|
874
|
+
function keySpecFromKey(raw, opts = {}) {
|
|
875
|
+
const platform = opts.platform ?? detectPlatform();
|
|
876
|
+
const legend = opts.legend ?? "auto";
|
|
877
|
+
const entry = NAMED_KEY[raw];
|
|
878
|
+
const override = opts.labels?.[raw] ?? (raw.length === 1 ? opts.labels?.[raw.toUpperCase()] : void 0);
|
|
879
|
+
const label = override ?? (entry ? entry[platform][legend] : raw.length === 1 ? raw.toUpperCase() : raw);
|
|
880
|
+
return opts.optional ? { label, optional: true } : { label };
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// src/ui/keySpecsFromShortcut.ts
|
|
884
|
+
function keySpecsFromShortcut(shortcut, opts = {}) {
|
|
885
|
+
if (!shortcut) return void 0;
|
|
886
|
+
const mods2 = [];
|
|
887
|
+
if (shortcut.mod) mods2.push({ name: "mod" });
|
|
888
|
+
if (shortcut.shift) mods2.push(shortcut.shift === "optional" ? { name: "shift", optional: true } : { name: "shift" });
|
|
889
|
+
if (shortcut.alt) mods2.push({ name: "alt" });
|
|
890
|
+
const key = typeof shortcut.key === "string" ? shortcut.key : shortcut.key[0];
|
|
891
|
+
const keys = keySpecsFromMods(mods2, opts);
|
|
892
|
+
return key === void 0 ? keys : [...keys, keySpecFromKey(key, opts)];
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// src/ui/formatShortcut.ts
|
|
896
|
+
var labels = (b, opts) => b ? keySpecsFromShortcut({ ...b, shift: b.shift === true }, opts)?.map((k) => k.label) : void 0;
|
|
897
|
+
function formatShortcutParts(b, opts = {}) {
|
|
898
|
+
return labels(b, opts);
|
|
899
|
+
}
|
|
900
|
+
function formatShortcut(b, opts = {}) {
|
|
901
|
+
const platform = opts.platform ?? detectPlatform();
|
|
902
|
+
return platform === "macos" ? labels(b, { platform })?.join("") : labels(b, { platform, legend: "text" })?.join("+");
|
|
903
|
+
}
|
|
904
|
+
|
|
787
905
|
// src/ui/fixedSeat.ts
|
|
788
906
|
var HOST_SELECTOR = "[data-wzl-portal-host],[data-wzl-theme],[data-wzl-mode]";
|
|
789
907
|
function nearestPortalHost(from) {
|
|
@@ -849,7 +967,7 @@ function specificity(spec) {
|
|
|
849
967
|
const mods2 = "mods" in spec ? spec.mods : void 0;
|
|
850
968
|
const m = modsCount(mods2);
|
|
851
969
|
const p = phaseRank("phase" in spec ? spec.phase : void 0);
|
|
852
|
-
const typed = (spec.kind === "drop" || spec.kind === "paste") && spec.types !== void 0 && spec.types.length > 0 ? 2 : 1;
|
|
970
|
+
const typed = (spec.kind === "drop" || spec.kind === "carry" || spec.kind === "paste") && spec.types !== void 0 && spec.types.length > 0 ? 2 : 1;
|
|
853
971
|
return [t, m, p, typed];
|
|
854
972
|
}
|
|
855
973
|
|
|
@@ -883,12 +1001,16 @@ function clickEvents(down, up) {
|
|
|
883
1001
|
x: up.worldX,
|
|
884
1002
|
y: up.worldY,
|
|
885
1003
|
clientX: up.clientX,
|
|
886
|
-
clientY: up.clientY
|
|
887
|
-
...pressedOn(down)
|
|
1004
|
+
clientY: up.clientY
|
|
888
1005
|
};
|
|
1006
|
+
const named = down.doubleClickBody;
|
|
889
1007
|
return {
|
|
890
|
-
click: { kind: "click", ...shared, pressX: down.worldX, pressY: down.worldY },
|
|
891
|
-
doubleclick: {
|
|
1008
|
+
click: { kind: "click", ...shared, ...pressedOn(down), pressX: down.worldX, pressY: down.worldY },
|
|
1009
|
+
doubleclick: {
|
|
1010
|
+
kind: "doubleclick",
|
|
1011
|
+
...shared,
|
|
1012
|
+
...pressedOn(named ? { affordance: down.affordance, ...named } : down)
|
|
1013
|
+
}
|
|
892
1014
|
};
|
|
893
1015
|
}
|
|
894
1016
|
function longPressEvents(down) {
|
|
@@ -908,14 +1030,14 @@ function longPressEvents(down) {
|
|
|
908
1030
|
}
|
|
909
1031
|
|
|
910
1032
|
// src/recognizer/click.ts
|
|
911
|
-
function composeClick(down, up, clicks, now, dispatch, observer) {
|
|
1033
|
+
function composeClick(down, up, clicks, now, dispatch, observer, limits = { maxMs: DOUBLE_CLICK_MAX_MS, maxPx: DOUBLE_CLICK_MAX_PX }) {
|
|
912
1034
|
const evs = clickEvents(down, up);
|
|
913
1035
|
const world = { x: up.worldX, y: up.worldY };
|
|
914
1036
|
dispatch(evs.click);
|
|
915
1037
|
observer.click?.(world);
|
|
916
1038
|
const t = now();
|
|
917
1039
|
const prev = clicks.last;
|
|
918
|
-
const follows = prev !== null && t - prev.t <
|
|
1040
|
+
const follows = prev !== null && t - prev.t < limits.maxMs && Math.hypot(up.clientX - prev.clientX, up.clientY - prev.clientY) < limits.maxPx;
|
|
919
1041
|
if (!follows) {
|
|
920
1042
|
clicks.last = { t, clientX: up.clientX, clientY: up.clientY };
|
|
921
1043
|
return {};
|
|
@@ -969,7 +1091,7 @@ function multiTouchGeometry(positions) {
|
|
|
969
1091
|
angle: Math.atan2(dy, dx) * 180 / Math.PI
|
|
970
1092
|
};
|
|
971
1093
|
}
|
|
972
|
-
function createMultiTouchEpisode() {
|
|
1094
|
+
function createMultiTouchEpisode(tapPx = () => TAP_THRESHOLD_PX) {
|
|
973
1095
|
let episode = null;
|
|
974
1096
|
return {
|
|
975
1097
|
join(positions, centroid, now) {
|
|
@@ -990,14 +1112,14 @@ function createMultiTouchEpisode() {
|
|
|
990
1112
|
},
|
|
991
1113
|
moved(pointerId, to) {
|
|
992
1114
|
const from = episode?.joinedAt.get(pointerId);
|
|
993
|
-
if (episode && from && Math.hypot(to.x - from.x, to.y - from.y) >
|
|
1115
|
+
if (episode && from && Math.hypot(to.x - from.x, to.y - from.y) > tapPx()) episode.moved = true;
|
|
994
1116
|
},
|
|
995
1117
|
finish(centroid, now, maxHoldMs) {
|
|
996
1118
|
const e = episode;
|
|
997
1119
|
episode = null;
|
|
998
1120
|
if (!e) return null;
|
|
999
1121
|
const brief = now - e.startedAt < maxHoldMs;
|
|
1000
|
-
const still = Math.hypot(centroid.x - e.centroid.x, centroid.y - e.centroid.y) <=
|
|
1122
|
+
const still = Math.hypot(centroid.x - e.centroid.x, centroid.y - e.centroid.y) <= tapPx();
|
|
1001
1123
|
return brief && !e.moved && still ? e.fingers : null;
|
|
1002
1124
|
},
|
|
1003
1125
|
abandon() {
|
|
@@ -1018,7 +1140,8 @@ function pressFactsOf(s) {
|
|
|
1018
1140
|
worldX: s.x,
|
|
1019
1141
|
worldY: s.y,
|
|
1020
1142
|
...modifiersOf(s),
|
|
1021
|
-
...pressedOn(s)
|
|
1143
|
+
...pressedOn(s),
|
|
1144
|
+
...s.doubleClickBody !== void 0 ? { doubleClickBody: s.doubleClickBody } : {}
|
|
1022
1145
|
};
|
|
1023
1146
|
}
|
|
1024
1147
|
var at = (s, world) => ({
|
|
@@ -1057,16 +1180,27 @@ function fireLongPress(rec, sink, observer) {
|
|
|
1057
1180
|
rec.buffered = null;
|
|
1058
1181
|
if (rec.holdType) observer.longPress?.(rec.holdType);
|
|
1059
1182
|
}
|
|
1183
|
+
function thresholdsOf(opts) {
|
|
1184
|
+
return {
|
|
1185
|
+
longPressMs: () => opts.longPressMs?.() ?? LONG_PRESS_MS,
|
|
1186
|
+
tapPx: () => opts.tapPx?.() ?? TAP_THRESHOLD_PX,
|
|
1187
|
+
dragPx: () => opts.dragPx?.() ?? DRAG_THRESHOLD_PX,
|
|
1188
|
+
doubleClick: () => ({
|
|
1189
|
+
maxMs: opts.doubleClickMs?.() ?? DOUBLE_CLICK_MAX_MS,
|
|
1190
|
+
maxPx: opts.doubleClickPx?.() ?? DOUBLE_CLICK_MAX_PX
|
|
1191
|
+
})
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1060
1194
|
function createPointerRecognizer(opts) {
|
|
1061
1195
|
const { sink, clock, observer = {}, toSurface = (p) => p } = opts;
|
|
1062
1196
|
const world = (s) => opts.toWorld?.(s.clientX, s.clientY) ?? { x: s.clientX, y: s.clientY };
|
|
1063
1197
|
const clicks = opts.clicks ?? { last: null };
|
|
1064
1198
|
const held = /* @__PURE__ */ new Map();
|
|
1065
|
-
const
|
|
1199
|
+
const { longPressMs, tapPx, dragPx, doubleClick } = thresholdsOf(opts);
|
|
1200
|
+
const touches = createMultiTouchEpisode(tapPx);
|
|
1066
1201
|
const hold = createLongPressHold(clock, (pending) => {
|
|
1067
1202
|
observer.holdChanged?.(pending);
|
|
1068
1203
|
});
|
|
1069
|
-
const longPressMs = () => opts.longPressMs?.() ?? LONG_PRESS_MS;
|
|
1070
1204
|
const geometry = (extra = []) => heldGeometry(held, toSurface, extra);
|
|
1071
1205
|
const claimForMultiTouch = (mods2) => {
|
|
1072
1206
|
hold.cancel();
|
|
@@ -1097,7 +1231,7 @@ function createPointerRecognizer(opts) {
|
|
|
1097
1231
|
const to = { x: s.clientX, y: s.clientY };
|
|
1098
1232
|
if (rec) rec.pos = to;
|
|
1099
1233
|
touches.moved(s.pointerId, to);
|
|
1100
|
-
if (rec?.buffered && pastDragThreshold(rec.down, s)) {
|
|
1234
|
+
if (rec?.buffered && pastDragThreshold(rec.down, s, dragPx())) {
|
|
1101
1235
|
const press = rec.buffered;
|
|
1102
1236
|
rec.buffered = null;
|
|
1103
1237
|
hold.cancel(s.pointerId);
|
|
@@ -1133,7 +1267,7 @@ function createPointerRecognizer(opts) {
|
|
|
1133
1267
|
worldY: at2.y,
|
|
1134
1268
|
...modifiersOf(s)
|
|
1135
1269
|
};
|
|
1136
|
-
return composeClick(rec.down, up, clicks, () => clock.now(), (ev) => sink.dispatch(ev), observer);
|
|
1270
|
+
return composeClick(rec.down, up, clicks, () => clock.now(), (ev) => sink.dispatch(ev), observer, doubleClick());
|
|
1137
1271
|
},
|
|
1138
1272
|
cancel(pointerId) {
|
|
1139
1273
|
const rec = held.get(pointerId);
|
|
@@ -1287,6 +1421,6 @@ function focusedControlOwnsKey(e) {
|
|
|
1287
1421
|
return activatesFocusedControl(e) || selectOwnsKey(e);
|
|
1288
1422
|
}
|
|
1289
1423
|
|
|
1290
|
-
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, heldPointers, 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 };
|
|
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 };
|
|
1291
1425
|
//# sourceMappingURL=index.js.map
|
|
1292
1426
|
//# sourceMappingURL=index.js.map
|