@vielzeug/codex 26.10.11 → 26.10.12

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/data/catalog.json CHANGED
@@ -35,7 +35,7 @@
35
35
  "rune"
36
36
  ],
37
37
  "slug": "archive",
38
- "version": "26.10.10"
38
+ "version": "26.10.11"
39
39
  },
40
40
  {
41
41
  "availableDocPages": [
@@ -108,7 +108,7 @@
108
108
  "coins"
109
109
  ],
110
110
  "slug": "arsenal",
111
- "version": "26.10.10"
111
+ "version": "26.10.11"
112
112
  },
113
113
  {
114
114
  "availableDocPages": [
@@ -160,7 +160,7 @@
160
160
  "refine"
161
161
  ],
162
162
  "slug": "assay",
163
- "version": "26.10.10"
163
+ "version": "26.10.11"
164
164
  },
165
165
  {
166
166
  "availableDocPages": [
@@ -202,7 +202,7 @@
202
202
  "ward"
203
203
  ],
204
204
  "slug": "clockwork",
205
- "version": "26.10.10"
205
+ "version": "26.10.11"
206
206
  },
207
207
  {
208
208
  "availableDocPages": [
@@ -234,7 +234,7 @@
234
234
  "refine"
235
235
  ],
236
236
  "slug": "codex",
237
- "version": "26.10.10"
237
+ "version": "26.10.11"
238
238
  },
239
239
  {
240
240
  "availableDocPages": [
@@ -304,7 +304,7 @@
304
304
  "spell"
305
305
  ],
306
306
  "slug": "coins",
307
- "version": "26.10.10"
307
+ "version": "26.10.11"
308
308
  },
309
309
  {
310
310
  "availableDocPages": [
@@ -345,7 +345,7 @@
345
345
  "rune"
346
346
  ],
347
347
  "slug": "conduit",
348
- "version": "26.10.10"
348
+ "version": "26.10.11"
349
349
  },
350
350
  {
351
351
  "availableDocPages": [
@@ -387,7 +387,7 @@
387
387
  "postmaster"
388
388
  ],
389
389
  "slug": "courier",
390
- "version": "26.10.10"
390
+ "version": "26.10.11"
391
391
  },
392
392
  {
393
393
  "availableDocPages": [
@@ -433,7 +433,7 @@
433
433
  "refine"
434
434
  ],
435
435
  "slug": "dnd",
436
- "version": "26.10.10"
436
+ "version": "26.10.11"
437
437
  },
438
438
  {
439
439
  "availableDocPages": [
@@ -475,7 +475,7 @@
475
475
  "herald"
476
476
  ],
477
477
  "slug": "familiar",
478
- "version": "26.10.10"
478
+ "version": "26.10.11"
479
479
  },
480
480
  {
481
481
  "availableDocPages": [
@@ -542,7 +542,7 @@
542
542
  "courier"
543
543
  ],
544
544
  "slug": "flux",
545
- "version": "26.10.10"
545
+ "version": "26.10.11"
546
546
  },
547
547
  {
548
548
  "availableDocPages": [
@@ -582,7 +582,7 @@
582
582
  "ore"
583
583
  ],
584
584
  "slug": "focus",
585
- "version": "26.10.10"
585
+ "version": "26.10.11"
586
586
  },
587
587
  {
588
588
  "availableDocPages": [
@@ -623,7 +623,7 @@
623
623
  "assay"
624
624
  ],
625
625
  "slug": "forge",
626
- "version": "26.10.10"
626
+ "version": "26.10.11"
627
627
  },
628
628
  {
629
629
  "availableDocPages": [
@@ -661,7 +661,7 @@
661
661
  "keymap"
662
662
  ],
663
663
  "slug": "gesture",
664
- "version": "26.10.10"
664
+ "version": "26.10.11"
665
665
  },
666
666
  {
667
667
  "availableDocPages": [
@@ -705,7 +705,7 @@
705
705
  "familiar"
706
706
  ],
707
707
  "slug": "herald",
708
- "version": "26.10.10"
708
+ "version": "26.10.11"
709
709
  },
710
710
  {
711
711
  "availableDocPages": [
@@ -748,7 +748,7 @@
748
748
  "tempo"
749
749
  ],
750
750
  "slug": "illusionist",
751
- "version": "26.10.10"
751
+ "version": "26.10.11"
752
752
  },
753
753
  {
754
754
  "availableDocPages": [
@@ -793,7 +793,7 @@
793
793
  "ore"
794
794
  ],
795
795
  "slug": "keymap",
796
- "version": "26.10.10"
796
+ "version": "26.10.11"
797
797
  },
798
798
  {
799
799
  "availableDocPages": [
@@ -840,7 +840,7 @@
840
840
  "vault"
841
841
  ],
842
842
  "slug": "ledger",
843
- "version": "26.10.10"
843
+ "version": "26.10.11"
844
844
  },
845
845
  {
846
846
  "availableDocPages": [
@@ -886,7 +886,7 @@
886
886
  "courier"
887
887
  ],
888
888
  "slug": "lingua",
889
- "version": "26.10.10"
889
+ "version": "26.10.11"
890
890
  },
891
891
  {
892
892
  "availableDocPages": [
@@ -924,7 +924,7 @@
924
924
  "herald"
925
925
  ],
926
926
  "slug": "mesh",
927
- "version": "26.10.10"
927
+ "version": "26.10.11"
928
928
  },
929
929
  {
930
930
  "availableDocPages": [
@@ -961,7 +961,7 @@
961
961
  "ore"
962
962
  ],
963
963
  "slug": "necromancer",
964
- "version": "26.10.10"
964
+ "version": "26.10.11"
965
965
  },
966
966
  {
967
967
  "availableDocPages": [
@@ -1001,7 +1001,7 @@
1001
1001
  "prism"
1002
1002
  ],
1003
1003
  "slug": "orbit",
1004
- "version": "26.10.10"
1004
+ "version": "26.10.11"
1005
1005
  },
1006
1006
  {
1007
1007
  "availableDocPages": [
@@ -1066,7 +1066,7 @@
1066
1066
  "orbit"
1067
1067
  ],
1068
1068
  "slug": "ore",
1069
- "version": "26.10.10"
1069
+ "version": "26.10.11"
1070
1070
  },
1071
1071
  {
1072
1072
  "availableDocPages": [
@@ -1110,7 +1110,7 @@
1110
1110
  "ripple"
1111
1111
  ],
1112
1112
  "slug": "postmaster",
1113
- "version": "26.10.10"
1113
+ "version": "26.10.11"
1114
1114
  },
1115
1115
  {
1116
1116
  "availableDocPages": [
@@ -1150,7 +1150,7 @@
1150
1150
  "orbit"
1151
1151
  ],
1152
1152
  "slug": "prism",
1153
- "version": "26.10.10"
1153
+ "version": "26.10.11"
1154
1154
  },
1155
1155
  {
1156
1156
  "availableDocPages": [
@@ -1214,7 +1214,7 @@
1214
1214
  "clockwork"
1215
1215
  ],
1216
1216
  "slug": "pulse",
1217
- "version": "26.10.10"
1217
+ "version": "26.10.11"
1218
1218
  },
1219
1219
  {
1220
1220
  "availableDocPages": [
@@ -1333,7 +1333,7 @@
1333
1333
  "keymap"
1334
1334
  ],
1335
1335
  "slug": "refine",
1336
- "version": "26.10.10"
1336
+ "version": "26.10.11"
1337
1337
  },
1338
1338
  {
1339
1339
  "availableDocPages": [
@@ -1378,7 +1378,7 @@
1378
1378
  "ledger"
1379
1379
  ],
1380
1380
  "slug": "ripple",
1381
- "version": "26.10.10"
1381
+ "version": "26.10.11"
1382
1382
  },
1383
1383
  {
1384
1384
  "availableDocPages": [
@@ -1432,7 +1432,7 @@
1432
1432
  "familiar"
1433
1433
  ],
1434
1434
  "slug": "rune",
1435
- "version": "26.10.10"
1435
+ "version": "26.10.11"
1436
1436
  },
1437
1437
  {
1438
1438
  "availableDocPages": [
@@ -1473,7 +1473,7 @@
1473
1473
  "refine"
1474
1474
  ],
1475
1475
  "slug": "sandbox",
1476
- "version": "26.10.10"
1476
+ "version": "26.10.11"
1477
1477
  },
1478
1478
  {
1479
1479
  "availableDocPages": [
@@ -1524,7 +1524,7 @@
1524
1524
  "ripple"
1525
1525
  ],
1526
1526
  "slug": "scout",
1527
- "version": "26.10.10"
1527
+ "version": "26.10.11"
1528
1528
  },
1529
1529
  {
1530
1530
  "availableDocPages": [
@@ -1577,7 +1577,7 @@
1577
1577
  "refine"
1578
1578
  ],
1579
1579
  "slug": "scroll",
1580
- "version": "26.10.10"
1580
+ "version": "26.10.11"
1581
1581
  },
1582
1582
  {
1583
1583
  "availableDocPages": [
@@ -1624,7 +1624,7 @@
1624
1624
  "gesture"
1625
1625
  ],
1626
1626
  "slug": "sentinel",
1627
- "version": "26.10.10"
1627
+ "version": "26.10.11"
1628
1628
  },
1629
1629
  {
1630
1630
  "availableDocPages": [
@@ -1664,7 +1664,7 @@
1664
1664
  "refine"
1665
1665
  ],
1666
1666
  "slug": "sigil",
1667
- "version": "26.10.10"
1667
+ "version": "26.10.11"
1668
1668
  },
1669
1669
  {
1670
1670
  "availableDocPages": [
@@ -1709,7 +1709,7 @@
1709
1709
  "wayfinder"
1710
1710
  ],
1711
1711
  "slug": "sourcerer",
1712
- "version": "26.10.10"
1712
+ "version": "26.10.11"
1713
1713
  },
1714
1714
  {
1715
1715
  "availableDocPages": [
@@ -1768,7 +1768,7 @@
1768
1768
  "vault"
1769
1769
  ],
1770
1770
  "slug": "spell",
1771
- "version": "26.10.10"
1771
+ "version": "26.10.11"
1772
1772
  },
1773
1773
  {
1774
1774
  "availableDocPages": [
@@ -1809,7 +1809,7 @@
1809
1809
  "ripple"
1810
1810
  ],
1811
1811
  "slug": "tandem",
1812
- "version": "26.10.10"
1812
+ "version": "26.10.11"
1813
1813
  },
1814
1814
  {
1815
1815
  "availableDocPages": [
@@ -1853,7 +1853,7 @@
1853
1853
  "herald"
1854
1854
  ],
1855
1855
  "slug": "tavern",
1856
- "version": "26.10.10"
1856
+ "version": "26.10.11"
1857
1857
  },
1858
1858
  {
1859
1859
  "availableDocPages": [
@@ -1921,7 +1921,7 @@
1921
1921
  "vault"
1922
1922
  ],
1923
1923
  "slug": "tempo",
1924
- "version": "26.10.10"
1924
+ "version": "26.10.11"
1925
1925
  },
1926
1926
  {
1927
1927
  "availableDocPages": [
@@ -1972,7 +1972,7 @@
1972
1972
  "ripple"
1973
1973
  ],
1974
1974
  "slug": "vault",
1975
- "version": "26.10.10"
1975
+ "version": "26.10.11"
1976
1976
  },
1977
1977
  {
1978
1978
  "availableDocPages": [
@@ -2018,7 +2018,7 @@
2018
2018
  "refine"
2019
2019
  ],
2020
2020
  "slug": "ward",
2021
- "version": "26.10.10"
2021
+ "version": "26.10.11"
2022
2022
  },
2023
2023
  {
2024
2024
  "availableDocPages": [
@@ -2072,8 +2072,8 @@
2072
2072
  "herald"
2073
2073
  ],
2074
2074
  "slug": "wayfinder",
2075
- "version": "26.10.10"
2075
+ "version": "26.10.11"
2076
2076
  }
2077
2077
  ],
2078
- "version": "26.10.10"
2078
+ "version": "26.10.11"
2079
2079
  }
@@ -1,6 +1,6 @@
1
1
  # Vielzeug: Full Documentation
2
2
 
3
- > Complete documentation for 42 packages. Version: 26.10.10
3
+ > Complete documentation for 42 packages. Version: 26.10.11
4
4
 
5
5
  ---
6
6
 
@@ -9715,7 +9715,7 @@ type PanGesture = DragGesture;
9715
9715
 
9716
9716
  Options and callback details are readonly. Fixed callbacks and capture policy are snapshotted at construction. Function-valued `axis` and `disabled` options remain dynamic.
9717
9717
 
9718
- Gesture tracks accepted movement with capture-phase listeners on the target's current owner document. Window blur, hidden-document transition, `pointercancel`, and `lostpointercapture` cancel active recognition.
9718
+ Gesture tracks accepted movement with capture-phase listeners on the target's current owner document. Window blur, hidden-document transition, `pointercancel`, and pointer capture loss on the gesture target cancel active recognition. A `lostpointercapture` event bubbling from a descendant does not cancel the target's gesture.
9719
9719
 
9720
9720
  ## Errors
9721
9721
 
@@ -9976,7 +9976,7 @@ Gesture owns pointer recognition: unrestricted two-dimensional drag and one-axis
9976
9976
  - **Use** `shouldStart` to exclude nested interactive controls.
9977
9977
  - **Disable** pointer capture when nested or newly revealed controls must keep native release targeting.
9978
9978
  - **Apply** completion thresholds and direction rules in `onEnd`; use `activationDistance` only for recognition slop.
9979
- - **Treat** `reason: 'cancel'` as a reset path, never a commit path.
9979
+ - **Treat** `reason: 'cancel'` as a reset path, never a commit path. Pointer capture loss on the gesture target cancels; capture loss bubbling from a descendant does not.
9980
9980
  - **Keep** `onMove` rendering lightweight.
9981
9981
  - **Dispose** the handle when its target leaves the UI.
9982
9982
 
package/data/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # Vielzeug
2
2
 
3
- > 42 focused TypeScript packages. Version: 26.10.10
3
+ > 42 focused TypeScript packages. Version: 26.10.11
4
4
 
5
5
  Install any package independently: `pnpm add @vielzeug/<name>`
6
6
 
@@ -4,5 +4,5 @@
4
4
  "refine": "refine.json",
5
5
  "schemaVersion": 1,
6
6
  "search": "search.json",
7
- "version": "26.10.10"
7
+ "version": "26.10.11"
8
8
  }
@@ -2,8 +2,8 @@
2
2
  "apiSource": "export {\n createDragGesture,\n type DragEndReason,\n type DragGesture,\n type DragGestureDetail,\n type DragGestureEndDetail,\n type DragGestureOptions,\n type DragPoint,\n} from './drag-gesture.js';\nexport { GestureConfigError, GestureError } from './errors.js';\nexport {\n createLongPress,\n type LongPress,\n type LongPressDetail,\n type LongPressOptions,\n} from './long-press.js';\nexport {\n createPanGesture,\n type PanAxis,\n type PanEndReason,\n type PanGesture,\n type PanGestureDetail,\n type PanGestureEndDetail,\n type PanGestureOptions,\n} from './pan-gesture.js';\n",
3
3
  "docs": {
4
4
  "index": "---\ntitle: 'Gesture: Pointer movement primitives'\ndescription: Framework-neutral two-dimensional pointer drag and one-axis pan recognition with lifecycle-owned handles.\npackage: gesture\ncategory: input\nkeywords: [pointer, pan, swipe, gesture, touch, drag, long press]\nexports: [createDragGesture, createPanGesture, createLongPress, GestureConfigError]\nrelated: [refine, dnd, keymap]\nenvironments: [browser]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"gesture\" />\n\n## Why Gesture?\n\nPointer-driven interfaces need reliable one-pointer movement tracking without coupling recognition to rendering, drag-and-drop semantics, or product-specific completion policy.\n\n```ts\n// Before\nelement.addEventListener('pointermove', (event) => {\n // Coordinate tracking, pointer identity, direction locking, and cleanup\n});\n\n// After\nimport { createPanGesture } from '@vielzeug/gesture';\n\nconst pan = createPanGesture(element, {\n axis: 'x',\n onMove: ({ distance }) => console.log('distance', distance),\n onEnd: ({ reason }) => console.log('ended', reason),\n});\n```\n\n| Feature | Gesture | Ad-hoc pointer handling |\n| --- | --- | --- |\n| Bundle size | <PackageInfo package=\"gesture\" type=\"size\" /> | n/a |\n| Zero dependencies | <ore-icon name=\"check\" size=\"16\"></ore-icon> | n/a |\n| Two-dimensional drag tracking | Built in | Manual |\n| Axis intent recognition | Built in | Manual |\n| Pointer ownership | Tracked across the document | Manual |\n| Lifecycle cleanup | `dispose()` + `disposalSignal` | Manual |\n\n<div class=\"decision-callout\">\n\n**Use Gesture when** UI surfaces need consistent free drag or axis-locked pan tracking while retaining their own rendering and completion rules.\n\n**Consider direct pointer handling when** the interaction is isolated and does not need reusable lifecycle or direction-lock behavior.\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/gesture\n```\n\n```sh [npm]\nnpm install @vielzeug/gesture\n```\n\n```sh [yarn]\nyarn add @vielzeug/gesture\n```\n\n:::\n\n## Quick Start\n\n```ts\nimport { createPanGesture } from '@vielzeug/gesture';\n\nconst element = document.querySelector<HTMLElement>('[data-swipe]');\nif (!element) throw new Error('Missing [data-swipe] element');\n\nconst pan = createPanGesture(element, {\n axis: 'x',\n onMove: ({ distance }) => {\n element.style.transform = `translateX(${distance}px)`;\n },\n onEnd: ({ distance, reason }) => {\n element.style.transform = '';\n if (reason === 'release' && Math.abs(distance) >= 48) {\n pan.dispose();\n element.remove();\n }\n },\n});\n\nwindow.addEventListener('pagehide', () => pan.dispose(), { once: true });\n```\n\n## Features\n\n<div class=\"features-grid\">\n\n- `createDragGesture()`: unrestricted two-dimensional pointer movement tracking\n- `createPanGesture()`: one-axis pointer movement with direction intent recognition\n- `createLongPress()`: hold recognition with slop cancellation and click swallowing\n- Direction locking: activates only when movement favors the configured axis\n- Configurable pointer capture: own the pointer by default or preserve native targeting\n- Configurable `activationDistance`: tune slop for touch density and component needs\n- Consumer-owned policy: completion thresholds, snapping, and outcomes stay in application code\n- Stable completion: one `onEnd` callback for release and cancellation\n- Lifecycle ownership: `dispose()`, `disposed`, `disposalSignal`, optional owner `signal`, and `[Symbol.dispose]()`\n\n</div>\n\n## Documentation\n\n<div class=\"doc-links\">\n\n- [Usage Guide](./usage.md)\n- [API Reference](./api.md)\n- [Examples](./examples.md)\n- [Migration Guide](./migration.md)\n\n</div>\n\n## See Also\n\n<div class=\"see-also\">\n\n- [Refine](/refine/): components that use pan recognition for carousel, drawer, toast, and list interactions.\n- [Dnd](/dnd/): builds touch sorting on `createDragGesture()` and adds files, previews, drop targets, keyboard reordering, and connected-list transactions.\n- [Keymap](/keymap/): keyboard interaction primitives for complementary input paths.\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
5
- "api": "---\ntitle: 'Gesture: API Reference'\ndescription: API reference for two-dimensional drag and one-axis pan recognition.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| --- | --- | --- | --- |\n| `createDragGesture()` | Track unrestricted two-dimensional pointer movement | Sync | Does not provide DOM movement or drop semantics |\n| `createPanGesture()` | Track pointer movement projected onto one axis | Sync | Cross-axis intent ends a pending interaction |\n| `createLongPress()` | Recognize a primary pointer held in place | Sync | The click that follows a fired hold is swallowed |\n| `DragGesture` / `PanGesture` / `LongPress` | Control recognition lifecycle | Sync | `dispose()` does not invoke `onEnd` |\n| `DragGestureOptions` | Configure free-drag recognition | Sync | `activationDistance` uses Euclidean distance |\n| `PanGestureOptions` | Configure axis-locked recognition | Sync | Completion thresholds belong in `onEnd` |\n| `LongPressOptions` | Configure hold recognition | Sync | `durationMs` and `slopPx` are validated at construction |\n\n## Package Entry Point\n\n| Import | Purpose |\n| --- | --- |\n| `@vielzeug/gesture` | Drag and pan factories, handles, callback details, options, axes, points, and end reasons |\n\n## Core Functions\n\n### `createDragGesture()`\n\n```ts\nfunction createDragGesture(target: Element, options?: DragGestureOptions): DragGesture;\n```\n\nTracks one primary pointer without restricting movement to an axis.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `target` | `Element` | Element that owns pointer admission and optional capture |\n| `options` | `DragGestureOptions` | Activation, admission, capture, callback, and lifecycle configuration |\n\n**Returns:** A lifecycle-owned `DragGesture` handle.\n\n**Example**\n\n```ts\nimport { createDragGesture } from '@vielzeug/gesture';\n\nconst drag = createDragGesture(element, {\n onMove: ({ delta }) => {\n element.style.translate = `${delta.x}px ${delta.y}px`;\n },\n onEnd: () => {\n element.style.translate = '';\n },\n});\n```\n\n---\n\n### `createPanGesture()`\n\n```ts\nfunction createPanGesture(target: Element, options?: PanGestureOptions): PanGesture;\n```\n\nTracks one primary pointer after movement establishes intent on the configured axis.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `target` | `Element` | Element that owns pointer admission and optional capture |\n| `options` | `PanGestureOptions` | Axis, activation, admission, capture, callback, and lifecycle configuration |\n\n**Returns:** A lifecycle-owned `PanGesture` handle.\n\n**Example**\n\n```ts\nimport { createPanGesture } from '@vielzeug/gesture';\n\nconst pan = createPanGesture(element, {\n axis: 'x',\n onEnd: ({ distance, reason }) => {\n if (reason === 'release' && Math.abs(distance) >= 48) dismiss();\n },\n});\n```\n\nBoth handles expose the same members:\n\n| Member | Type | Contract |\n| --- | --- | --- |\n| `active` | `boolean` | `true` after activation and before release or cancellation |\n| `cancel()` | `() => boolean` | Clears pending recognition or ends active recognition with `reason: 'cancel'`; returns whether a session existed |\n| `disposalSignal` | `AbortSignal` | Aborts when the handle is disposed |\n| `dispose()` | `() => void` | Aborts `disposalSignal`, clears recognition, releases capture, and detaches listeners; idempotent |\n| `disposed` | `boolean` | `true` after disposal |\n| `[Symbol.dispose]()` | `() => void` | Calls `dispose()` |\n\n---\n\n### `createLongPress()`\n\n```ts\nfunction createLongPress(target: Element, options?: LongPressOptions): LongPress;\n```\n\nRecognizes a primary pointer held in place: `onLongPress` fires with the originating pointerdown once the hold outlasts `durationMs`.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `target` | `Element` | Element that owns pointer admission, document tracking, and click swallowing |\n| `options` | `LongPressOptions` | Hold time, slop distance, admission, callback, and lifecycle configuration |\n\n**Returns:** A lifecycle-owned `LongPress` handle with the same members as the other gestures; `active` is `true` while a hold is pending.\n\n**Example**\n\n```ts\nimport { createLongPress } from '@vielzeug/gesture';\n\nconst hold = createLongPress(list, {\n shouldStart: (event) => event.pointerType !== 'mouse',\n onLongPress: (detail) => {\n const tile = (detail.event.target as HTMLElement).closest('.tile');\n openDetail(tile?.dataset.id);\n },\n});\n```\n\nMovement beyond `slopPx` cancels back to a normal press, and window blur or a hidden document cancels a pending hold. The click that follows a fired hold is swallowed through a capture-phase listener on `target`, so releasing never commits what the hold opened; bind `target` to a stable ancestor of the pressed controls so the swallow precedes their click handlers.\n\n## Types\n\n### Drag types\n\n```ts\ntype DragEndReason = 'cancel' | 'release';\n\ntype DragPoint = Readonly<{\n x: number;\n y: number;\n}>;\n\ntype DragGestureDetail = Readonly<{\n current: DragPoint;\n delta: DragPoint;\n event: PointerEvent;\n pointerId: number;\n pointerType: string;\n start: DragPoint;\n target: Element;\n}>;\n\ntype DragGestureEndDetail = DragGestureDetail &\n Readonly<{\n reason: DragEndReason;\n }>;\n\ntype DragGestureOptions = Readonly<{\n activationDistance?: number;\n disabled?: boolean | (() => boolean | undefined);\n onEnd?: (detail: DragGestureEndDetail) => void;\n onMove?: (detail: DragGestureDetail) => void;\n onStart?: (detail: DragGestureDetail) => void;\n pointerCapture?: boolean;\n shouldStart?: (event: PointerEvent) => boolean;\n signal?: AbortSignal;\n}>;\n```\n\n`start`, `current`, and `delta` use viewport `clientX` and `clientY` coordinates. Activation uses `Math.hypot(delta.x, delta.y)`.\n\n### Pan types\n\n```ts\ntype PanAxis = 'x' | 'y';\ntype PanEndReason = 'cancel' | 'release';\n\ntype PanGestureDetail = Readonly<{\n axis: PanAxis;\n current: number;\n distance: number;\n event: PointerEvent;\n pointerId: number;\n pointerType: string;\n start: number;\n target: Element;\n}>;\n\ntype PanGestureEndDetail = PanGestureDetail &\n Readonly<{\n reason: PanEndReason;\n }>;\n\ntype PanGestureOptions = Readonly<{\n activationDistance?: number;\n axis?: PanAxis | (() => PanAxis);\n disabled?: boolean | (() => boolean | undefined);\n onEnd?: (detail: PanGestureEndDetail) => void;\n onMove?: (detail: PanGestureDetail) => void;\n onStart?: (detail: PanGestureDetail) => void;\n pointerCapture?: boolean;\n shouldStart?: (event: PointerEvent) => boolean;\n signal?: AbortSignal;\n}>;\n```\n\nPan details project `start`, `current`, and `distance` onto `axis`. The default axis is `'x'`.\n\n### Long-press types\n\n```ts\ntype LongPressDetail = Readonly<{\n event: PointerEvent;\n pointerId: number;\n pointerType: string;\n target: Element;\n}>;\n\ntype LongPressOptions = Readonly<{\n durationMs?: number;\n disabled?: boolean | (() => boolean | undefined);\n onLongPress?: (detail: LongPressDetail) => void;\n shouldStart?: (event: PointerEvent) => boolean;\n slopPx?: number;\n signal?: AbortSignal;\n}>;\n```\n\n`durationMs` defaults to `500` and `slopPx` to `8`. `event` is the pointerdown that started the hold, so `event.target` is the pressed element.\n\n### Handle types\n\n```ts\ntype DragGesture = {\n readonly active: boolean;\n [Symbol.dispose](): void;\n cancel(): boolean;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n};\n\ntype PanGesture = DragGesture;\n```\n\n### Shared option behavior\n\n| Option | Default | Contract |\n| --- | --- | --- |\n| `activationDistance` | `6` | Non-negative finite CSS pixels; drag uses Euclidean distance, pan waits for dominant-axis intent |\n| `axis` | `'x'` | Pan only; fixed value or getter sampled when each interaction starts |\n| `disabled` | `false` | Fixed value or getter; blocks starts and cancels active recognition on the next matching pointer event |\n| `pointerCapture` | `true` | Best-effort capture after activation; document tracking continues when capture is disabled or fails |\n| `shouldStart` | N/A | Admission predicate evaluated for the primary pointer before tracking starts |\n| `signal` | N/A | Disposes the handle when the external owner aborts |\n| `onStart` | N/A | Runs once on the activating movement |\n| `onMove` | N/A | Runs on the activating movement and each later matching movement |\n| `onEnd` | N/A | Runs once for active release or cancellation; pending interactions end without callbacks |\n\nOptions and callback details are readonly. Fixed callbacks and capture policy are snapshotted at construction. Function-valued `axis` and `disabled` options remain dynamic.\n\nGesture tracks accepted movement with capture-phase listeners on the target's current owner document. Window blur, hidden-document transition, `pointercancel`, and `lostpointercapture` cancel active recognition.\n\n## Errors\n\n| Error | Trigger | Notable properties |\n| --- | --- | --- |\n| `GestureError` | Base class for every gesture-originated error | `instanceof GestureError` catches any gesture error |\n| `GestureConfigError` | Invalid option value | Extends `GestureError` |\n\n- Invalid `activationDistance` values throw `GestureConfigError` during construction.\n- Invalid fixed `axis` values throw `GestureConfigError` during construction.\n- Invalid values returned by a dynamic `axis` getter throw `GestureConfigError` when a pointer interaction starts.\n",
6
- "usage": "---\ntitle: 'Gesture: Usage Guide'\ndescription: Track unrestricted pointer dragging and axis-locked pans with application-owned rendering and completion.\n---\n\n[[toc]]\n\n## Basic Usage\n\nCreate one handle for the element that owns the interaction. This example creates its surface, applies the required touch policy, and disposes the handle when the page unloads.\n\n```ts\nimport { createPanGesture } from '@vielzeug/gesture';\n\nconst row = document.body.appendChild(document.createElement('div'));\nrow.textContent = 'Swipe to archive';\nrow.style.touchAction = 'pan-y';\n\nconst pan = createPanGesture(row, {\n axis: 'x',\n onMove: ({ distance }) => {\n row.style.transform = `translateX(${distance}px)`;\n },\n onEnd: ({ distance, reason }) => {\n row.style.transform = '';\n if (reason === 'release' && Math.abs(distance) >= 64) {\n pan.dispose();\n row.remove();\n }\n },\n});\n\nwindow.addEventListener('pagehide', () => pan.dispose(), { once: true });\n```\n\n## Track Two-Dimensional Dragging\n\nUse `createDragGesture()` when movement must remain unrestricted in both dimensions:\n\n```ts\nimport { createDragGesture } from '@vielzeug/gesture';\n\nconst card = document.querySelector<HTMLElement>('[data-draggable]');\nif (!card) throw new Error('Missing [data-draggable] element');\n\nconst drag = createDragGesture(card, {\n activationDistance: 6,\n onMove: ({ delta }) => {\n card.style.translate = `${delta.x}px ${delta.y}px`;\n },\n onEnd: () => {\n card.style.translate = '';\n },\n});\n\nwindow.addEventListener('pagehide', () => drag.dispose(), { once: true });\n```\n\n`start`, `current`, and `delta` are readonly `{ x, y }` points. Set `touch-action: none` when the surface owns movement in both dimensions. Drag recognition does not provide previews, drop targets, DOM movement, or `DataTransfer`; use Dnd for those semantics.\n\n## Recognize Long Presses\n\nUse `createLongPress()` for hold interactions: the touch counterpart of a keyboard preview key. `onLongPress` receives the originating pointerdown once the hold outlasts `durationMs`:\n\n```ts\nimport { createLongPress } from '@vielzeug/gesture';\n\nconst hold = createLongPress(grid, {\n durationMs: 500,\n shouldStart: (event) =>\n event.pointerType !== 'mouse' &&\n (event.target as HTMLElement).closest('.tile') !== null,\n onLongPress: (detail) => {\n const tile = (detail.event.target as HTMLElement).closest<HTMLElement>('.tile');\n openPreview(tile?.dataset.id);\n },\n});\n```\n\nMovement beyond `slopPx` cancels back to a normal press, so scrolling never triggers the hold. The click that follows a fired hold is swallowed through a capture-phase listener on `target`: bind `target` to a stable ancestor of the pressed controls so the swallow precedes their click handlers, and gate `shouldStart` to the elements the hold belongs to.\n\n## Completion Rules\n\nGesture reports movement and terminal state but does not decide what constitutes a swipe. Apply thresholds and allowed directions in `onEnd`.\n\n```ts\nconst pan = createPanGesture(panel, {\n axis: 'x',\n onEnd: ({ distance, reason }) => {\n if (reason === 'release' && distance <= -80) {\n openNext();\n } else {\n resetPanel();\n }\n },\n});\n```\n\n## Direction Recognition\n\nThe gesture remains pending during small movement. It activates only after movement favors the configured axis. Cross-axis movement ends the pending interaction without invoking callbacks.\n\nThe default `activationDistance` is 6 pixels. Tune direction-recognition slop for touch density or component-specific needs:\n\n```ts\nconst pan = createPanGesture(row, {\n axis: 'x',\n activationDistance: 12,\n onMove,\n onEnd,\n});\n```\n\nSet `activationDistance: 0` when the first pointer move with non-zero displacement should activate immediately. Zero-motion events remain pending.\n\nUse the corresponding `touch-action` value so the browser retains native scrolling on the other axis.\n\n```css\n.swipe-row {\n touch-action: pan-y;\n}\n```\n\n```ts\nconst pan = createPanGesture(row, { axis: 'x', onMove });\n```\n\n## Pointer Capture\n\nPointer capture is enabled by default. After axis intent is accepted, Gesture attempts to capture the pointer while continuing to track movement through document-level listeners. Capture failure (for example after target detachment or pointer termination) falls back to document tracking without stranding the session.\n\nDisable capture when nested or newly revealed controls must retain native pointer-up and click targeting:\n\n```ts\nconst pan = createPanGesture(row, {\n axis: 'x',\n pointerCapture: false,\n onMove: renderReveal,\n onEnd: settleReveal,\n});\n```\n\nDocument-level tracking still keeps the pan active outside the target. Disabling capture changes event targeting, not gesture tracking.\n\n## Interactive Descendants\n\nUse `shouldStart` when buttons, links, or form controls inside the surface must not start a pan.\n\n```ts\nconst pan = createPanGesture(notification, {\n axis: 'x',\n pointerCapture: false,\n shouldStart: (event) =>\n !event\n .composedPath()\n .some((node) => node instanceof Element && node.matches('button, a, input, select, textarea')),\n onMove,\n onEnd,\n});\n```\n\n`shouldStart` protects controls under the initial pointer. `pointerCapture: false` additionally protects controls that appear beneath the pointer during a reveal interaction.\n\n## Disabled State\n\nA boolean disables the recognizer permanently. A getter supports state that changes while the handle is alive.\n\n```ts\nconst pan = createPanGesture(row, {\n disabled: () => isLocked,\n onEnd: ({ reason }) => {\n if (reason === 'cancel') resetRow();\n },\n});\n```\n\nWhen the getter becomes `true`, the next pointer event cancels an active pan. Window blur or a hidden owner document also cancels active tracking so a lost release cannot block later gestures.\n\nGesture snapshots fixed callbacks and capture policy at construction. Use the `axis` and `disabled` functions for behavior that must remain dynamic instead of mutating the options object.\n\n## Lifecycle\n\nDispose the target-bound handle when its owning UI scope unmounts.\n\n```ts\nconst element = document.querySelector<HTMLElement>('[data-pan]');\nif (!element) throw new Error('Missing [data-pan] element');\n\nconst owner = new AbortController();\nconst request = new AbortController();\nconst pan = createPanGesture(element, {\n axis: 'x',\n signal: owner.signal,\n});\n\nconst childSignal = AbortSignal.any([pan.disposalSignal, request.signal]);\nwindow.addEventListener('pagehide', () => owner.abort(), { once: true });\n```\n\nPass `{ signal }` to dispose with an existing owner signal. `disposalSignal` always aborts when Gesture finishes teardown, so child work can compose with the handle regardless of how disposal starts.\n\nUse `cancel()` to stop a pending or active interaction without disposing the handle. An active interaction emits `onEnd` with `reason: 'cancel'`. Call `cancel()` before `dispose()` when a connected surface needs its `onEnd` reset path; disposal itself does not invoke callbacks.\n\n## Framework Integration\n\nCreate the handle after the target element exists and dispose it on unmount.\n\n::: code-group\n\n```tsx [React]\nimport { useEffect, useRef } from 'react';\nimport { createPanGesture } from '@vielzeug/gesture';\n\nfunction SwipeRow({ onDismiss }: { onDismiss: () => void }) {\n const rowRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const row = rowRef.current;\n if (!row) return;\n\n const pan = createPanGesture(row, {\n axis: 'x',\n onMove: ({ distance }) => {\n row.style.transform = `translateX(${distance}px)`;\n },\n onEnd: ({ distance, reason }) => {\n row.style.transform = '';\n if (reason === 'release' && Math.abs(distance) >= 64) onDismiss();\n },\n });\n\n return () => pan.dispose();\n }, [onDismiss]);\n\n return <div ref={rowRef}>Swipe me</div>;\n}\n```\n\n```vue [Vue 3]\n<script setup lang=\"ts\">\nimport { onMounted, onUnmounted, ref } from 'vue';\nimport { createPanGesture, type PanGesture } from '@vielzeug/gesture';\n\nconst emit = defineEmits<{ dismiss: [] }>();\nconst rowEl = ref<HTMLDivElement | null>(null);\nlet pan: PanGesture | undefined;\n\nonMounted(() => {\n const row = rowEl.value;\n if (!row) return;\n\n pan = createPanGesture(row, {\n axis: 'x',\n onEnd: ({ distance, reason }) => {\n if (reason === 'release' && Math.abs(distance) >= 64) emit('dismiss');\n },\n });\n});\n\nonUnmounted(() => pan?.dispose());\n</script>\n\n<template>\n <div ref=\"rowEl\">Swipe me</div>\n</template>\n```\n\n```svelte [Svelte]\n<script lang=\"ts\">\n import { onMount } from 'svelte';\n import { createPanGesture } from '@vielzeug/gesture';\n\n let { ondismiss = () => {} }: { ondismiss: () => void } = $props();\n let rowEl: HTMLDivElement;\n\n onMount(() => {\n const pan = createPanGesture(rowEl, {\n axis: 'x',\n onEnd: ({ distance, reason }) => {\n if (reason === 'release' && Math.abs(distance) >= 64) ondismiss();\n },\n });\n\n return () => pan.dispose();\n });\n</script>\n\n<div bind:this={rowEl}>Swipe me</div>\n```\n\n:::\n\n## Working with Other Vielzeug Libraries\n\n### Gesture + Refine\n\nRefine uses Gesture internally for carousel, drawer, toast, and list-item pointer interactions. Custom surfaces can use the same pan lifecycle while keeping visual state local.\n\n```ts\nimport { createPanGesture } from '@vielzeug/gesture';\n\nconst pan = createPanGesture(panel, {\n axis: 'x',\n onMove: ({ distance }) => {\n panel.style.transform = `translateX(${distance}px)`;\n },\n onEnd: ({ distance, reason }) => {\n panel.style.transform = '';\n if (reason === 'release' && Math.abs(distance) >= 80) revealActions();\n },\n});\n```\n\n### Gesture + Dnd\n\nGesture owns pointer recognition: unrestricted two-dimensional drag and one-axis pan. Dnd builds touch sorting on `createDragGesture()`, then owns `DataTransfer`, previews, hit-testing, file drop zones, keyboard sorting, DOM reordering, and connected-list transactions. Use Dnd when an item is picked up and dropped; use Gesture directly when application code owns rendering and completion.\n\n## Best Practices\n\n- **Set** `touch-action` for the axis the browser should continue scrolling.\n- **Use** `shouldStart` to exclude nested interactive controls.\n- **Disable** pointer capture when nested or newly revealed controls must keep native release targeting.\n- **Apply** completion thresholds and direction rules in `onEnd`; use `activationDistance` only for recognition slop.\n- **Treat** `reason: 'cancel'` as a reset path, never a commit path.\n- **Keep** `onMove` rendering lightweight.\n- **Dispose** the handle when its target leaves the UI.\n",
5
+ "api": "---\ntitle: 'Gesture: API Reference'\ndescription: API reference for two-dimensional drag and one-axis pan recognition.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| --- | --- | --- | --- |\n| `createDragGesture()` | Track unrestricted two-dimensional pointer movement | Sync | Does not provide DOM movement or drop semantics |\n| `createPanGesture()` | Track pointer movement projected onto one axis | Sync | Cross-axis intent ends a pending interaction |\n| `createLongPress()` | Recognize a primary pointer held in place | Sync | The click that follows a fired hold is swallowed |\n| `DragGesture` / `PanGesture` / `LongPress` | Control recognition lifecycle | Sync | `dispose()` does not invoke `onEnd` |\n| `DragGestureOptions` | Configure free-drag recognition | Sync | `activationDistance` uses Euclidean distance |\n| `PanGestureOptions` | Configure axis-locked recognition | Sync | Completion thresholds belong in `onEnd` |\n| `LongPressOptions` | Configure hold recognition | Sync | `durationMs` and `slopPx` are validated at construction |\n\n## Package Entry Point\n\n| Import | Purpose |\n| --- | --- |\n| `@vielzeug/gesture` | Drag and pan factories, handles, callback details, options, axes, points, and end reasons |\n\n## Core Functions\n\n### `createDragGesture()`\n\n```ts\nfunction createDragGesture(target: Element, options?: DragGestureOptions): DragGesture;\n```\n\nTracks one primary pointer without restricting movement to an axis.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `target` | `Element` | Element that owns pointer admission and optional capture |\n| `options` | `DragGestureOptions` | Activation, admission, capture, callback, and lifecycle configuration |\n\n**Returns:** A lifecycle-owned `DragGesture` handle.\n\n**Example**\n\n```ts\nimport { createDragGesture } from '@vielzeug/gesture';\n\nconst drag = createDragGesture(element, {\n onMove: ({ delta }) => {\n element.style.translate = `${delta.x}px ${delta.y}px`;\n },\n onEnd: () => {\n element.style.translate = '';\n },\n});\n```\n\n---\n\n### `createPanGesture()`\n\n```ts\nfunction createPanGesture(target: Element, options?: PanGestureOptions): PanGesture;\n```\n\nTracks one primary pointer after movement establishes intent on the configured axis.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `target` | `Element` | Element that owns pointer admission and optional capture |\n| `options` | `PanGestureOptions` | Axis, activation, admission, capture, callback, and lifecycle configuration |\n\n**Returns:** A lifecycle-owned `PanGesture` handle.\n\n**Example**\n\n```ts\nimport { createPanGesture } from '@vielzeug/gesture';\n\nconst pan = createPanGesture(element, {\n axis: 'x',\n onEnd: ({ distance, reason }) => {\n if (reason === 'release' && Math.abs(distance) >= 48) dismiss();\n },\n});\n```\n\nBoth handles expose the same members:\n\n| Member | Type | Contract |\n| --- | --- | --- |\n| `active` | `boolean` | `true` after activation and before release or cancellation |\n| `cancel()` | `() => boolean` | Clears pending recognition or ends active recognition with `reason: 'cancel'`; returns whether a session existed |\n| `disposalSignal` | `AbortSignal` | Aborts when the handle is disposed |\n| `dispose()` | `() => void` | Aborts `disposalSignal`, clears recognition, releases capture, and detaches listeners; idempotent |\n| `disposed` | `boolean` | `true` after disposal |\n| `[Symbol.dispose]()` | `() => void` | Calls `dispose()` |\n\n---\n\n### `createLongPress()`\n\n```ts\nfunction createLongPress(target: Element, options?: LongPressOptions): LongPress;\n```\n\nRecognizes a primary pointer held in place: `onLongPress` fires with the originating pointerdown once the hold outlasts `durationMs`.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `target` | `Element` | Element that owns pointer admission, document tracking, and click swallowing |\n| `options` | `LongPressOptions` | Hold time, slop distance, admission, callback, and lifecycle configuration |\n\n**Returns:** A lifecycle-owned `LongPress` handle with the same members as the other gestures; `active` is `true` while a hold is pending.\n\n**Example**\n\n```ts\nimport { createLongPress } from '@vielzeug/gesture';\n\nconst hold = createLongPress(list, {\n shouldStart: (event) => event.pointerType !== 'mouse',\n onLongPress: (detail) => {\n const tile = (detail.event.target as HTMLElement).closest('.tile');\n openDetail(tile?.dataset.id);\n },\n});\n```\n\nMovement beyond `slopPx` cancels back to a normal press, and window blur or a hidden document cancels a pending hold. The click that follows a fired hold is swallowed through a capture-phase listener on `target`, so releasing never commits what the hold opened; bind `target` to a stable ancestor of the pressed controls so the swallow precedes their click handlers.\n\n## Types\n\n### Drag types\n\n```ts\ntype DragEndReason = 'cancel' | 'release';\n\ntype DragPoint = Readonly<{\n x: number;\n y: number;\n}>;\n\ntype DragGestureDetail = Readonly<{\n current: DragPoint;\n delta: DragPoint;\n event: PointerEvent;\n pointerId: number;\n pointerType: string;\n start: DragPoint;\n target: Element;\n}>;\n\ntype DragGestureEndDetail = DragGestureDetail &\n Readonly<{\n reason: DragEndReason;\n }>;\n\ntype DragGestureOptions = Readonly<{\n activationDistance?: number;\n disabled?: boolean | (() => boolean | undefined);\n onEnd?: (detail: DragGestureEndDetail) => void;\n onMove?: (detail: DragGestureDetail) => void;\n onStart?: (detail: DragGestureDetail) => void;\n pointerCapture?: boolean;\n shouldStart?: (event: PointerEvent) => boolean;\n signal?: AbortSignal;\n}>;\n```\n\n`start`, `current`, and `delta` use viewport `clientX` and `clientY` coordinates. Activation uses `Math.hypot(delta.x, delta.y)`.\n\n### Pan types\n\n```ts\ntype PanAxis = 'x' | 'y';\ntype PanEndReason = 'cancel' | 'release';\n\ntype PanGestureDetail = Readonly<{\n axis: PanAxis;\n current: number;\n distance: number;\n event: PointerEvent;\n pointerId: number;\n pointerType: string;\n start: number;\n target: Element;\n}>;\n\ntype PanGestureEndDetail = PanGestureDetail &\n Readonly<{\n reason: PanEndReason;\n }>;\n\ntype PanGestureOptions = Readonly<{\n activationDistance?: number;\n axis?: PanAxis | (() => PanAxis);\n disabled?: boolean | (() => boolean | undefined);\n onEnd?: (detail: PanGestureEndDetail) => void;\n onMove?: (detail: PanGestureDetail) => void;\n onStart?: (detail: PanGestureDetail) => void;\n pointerCapture?: boolean;\n shouldStart?: (event: PointerEvent) => boolean;\n signal?: AbortSignal;\n}>;\n```\n\nPan details project `start`, `current`, and `distance` onto `axis`. The default axis is `'x'`.\n\n### Long-press types\n\n```ts\ntype LongPressDetail = Readonly<{\n event: PointerEvent;\n pointerId: number;\n pointerType: string;\n target: Element;\n}>;\n\ntype LongPressOptions = Readonly<{\n durationMs?: number;\n disabled?: boolean | (() => boolean | undefined);\n onLongPress?: (detail: LongPressDetail) => void;\n shouldStart?: (event: PointerEvent) => boolean;\n slopPx?: number;\n signal?: AbortSignal;\n}>;\n```\n\n`durationMs` defaults to `500` and `slopPx` to `8`. `event` is the pointerdown that started the hold, so `event.target` is the pressed element.\n\n### Handle types\n\n```ts\ntype DragGesture = {\n readonly active: boolean;\n [Symbol.dispose](): void;\n cancel(): boolean;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n};\n\ntype PanGesture = DragGesture;\n```\n\n### Shared option behavior\n\n| Option | Default | Contract |\n| --- | --- | --- |\n| `activationDistance` | `6` | Non-negative finite CSS pixels; drag uses Euclidean distance, pan waits for dominant-axis intent |\n| `axis` | `'x'` | Pan only; fixed value or getter sampled when each interaction starts |\n| `disabled` | `false` | Fixed value or getter; blocks starts and cancels active recognition on the next matching pointer event |\n| `pointerCapture` | `true` | Best-effort capture after activation; document tracking continues when capture is disabled or fails |\n| `shouldStart` | N/A | Admission predicate evaluated for the primary pointer before tracking starts |\n| `signal` | N/A | Disposes the handle when the external owner aborts |\n| `onStart` | N/A | Runs once on the activating movement |\n| `onMove` | N/A | Runs on the activating movement and each later matching movement |\n| `onEnd` | N/A | Runs once for active release or cancellation; pending interactions end without callbacks |\n\nOptions and callback details are readonly. Fixed callbacks and capture policy are snapshotted at construction. Function-valued `axis` and `disabled` options remain dynamic.\n\nGesture tracks accepted movement with capture-phase listeners on the target's current owner document. Window blur, hidden-document transition, `pointercancel`, and pointer capture loss on the gesture target cancel active recognition. A `lostpointercapture` event bubbling from a descendant does not cancel the target's gesture.\n\n## Errors\n\n| Error | Trigger | Notable properties |\n| --- | --- | --- |\n| `GestureError` | Base class for every gesture-originated error | `instanceof GestureError` catches any gesture error |\n| `GestureConfigError` | Invalid option value | Extends `GestureError` |\n\n- Invalid `activationDistance` values throw `GestureConfigError` during construction.\n- Invalid fixed `axis` values throw `GestureConfigError` during construction.\n- Invalid values returned by a dynamic `axis` getter throw `GestureConfigError` when a pointer interaction starts.\n",
6
+ "usage": "---\ntitle: 'Gesture: Usage Guide'\ndescription: Track unrestricted pointer dragging and axis-locked pans with application-owned rendering and completion.\n---\n\n[[toc]]\n\n## Basic Usage\n\nCreate one handle for the element that owns the interaction. This example creates its surface, applies the required touch policy, and disposes the handle when the page unloads.\n\n```ts\nimport { createPanGesture } from '@vielzeug/gesture';\n\nconst row = document.body.appendChild(document.createElement('div'));\nrow.textContent = 'Swipe to archive';\nrow.style.touchAction = 'pan-y';\n\nconst pan = createPanGesture(row, {\n axis: 'x',\n onMove: ({ distance }) => {\n row.style.transform = `translateX(${distance}px)`;\n },\n onEnd: ({ distance, reason }) => {\n row.style.transform = '';\n if (reason === 'release' && Math.abs(distance) >= 64) {\n pan.dispose();\n row.remove();\n }\n },\n});\n\nwindow.addEventListener('pagehide', () => pan.dispose(), { once: true });\n```\n\n## Track Two-Dimensional Dragging\n\nUse `createDragGesture()` when movement must remain unrestricted in both dimensions:\n\n```ts\nimport { createDragGesture } from '@vielzeug/gesture';\n\nconst card = document.querySelector<HTMLElement>('[data-draggable]');\nif (!card) throw new Error('Missing [data-draggable] element');\n\nconst drag = createDragGesture(card, {\n activationDistance: 6,\n onMove: ({ delta }) => {\n card.style.translate = `${delta.x}px ${delta.y}px`;\n },\n onEnd: () => {\n card.style.translate = '';\n },\n});\n\nwindow.addEventListener('pagehide', () => drag.dispose(), { once: true });\n```\n\n`start`, `current`, and `delta` are readonly `{ x, y }` points. Set `touch-action: none` when the surface owns movement in both dimensions. Drag recognition does not provide previews, drop targets, DOM movement, or `DataTransfer`; use Dnd for those semantics.\n\n## Recognize Long Presses\n\nUse `createLongPress()` for hold interactions: the touch counterpart of a keyboard preview key. `onLongPress` receives the originating pointerdown once the hold outlasts `durationMs`:\n\n```ts\nimport { createLongPress } from '@vielzeug/gesture';\n\nconst hold = createLongPress(grid, {\n durationMs: 500,\n shouldStart: (event) =>\n event.pointerType !== 'mouse' &&\n (event.target as HTMLElement).closest('.tile') !== null,\n onLongPress: (detail) => {\n const tile = (detail.event.target as HTMLElement).closest<HTMLElement>('.tile');\n openPreview(tile?.dataset.id);\n },\n});\n```\n\nMovement beyond `slopPx` cancels back to a normal press, so scrolling never triggers the hold. The click that follows a fired hold is swallowed through a capture-phase listener on `target`: bind `target` to a stable ancestor of the pressed controls so the swallow precedes their click handlers, and gate `shouldStart` to the elements the hold belongs to.\n\n## Completion Rules\n\nGesture reports movement and terminal state but does not decide what constitutes a swipe. Apply thresholds and allowed directions in `onEnd`.\n\n```ts\nconst pan = createPanGesture(panel, {\n axis: 'x',\n onEnd: ({ distance, reason }) => {\n if (reason === 'release' && distance <= -80) {\n openNext();\n } else {\n resetPanel();\n }\n },\n});\n```\n\n## Direction Recognition\n\nThe gesture remains pending during small movement. It activates only after movement favors the configured axis. Cross-axis movement ends the pending interaction without invoking callbacks.\n\nThe default `activationDistance` is 6 pixels. Tune direction-recognition slop for touch density or component-specific needs:\n\n```ts\nconst pan = createPanGesture(row, {\n axis: 'x',\n activationDistance: 12,\n onMove,\n onEnd,\n});\n```\n\nSet `activationDistance: 0` when the first pointer move with non-zero displacement should activate immediately. Zero-motion events remain pending.\n\nUse the corresponding `touch-action` value so the browser retains native scrolling on the other axis.\n\n```css\n.swipe-row {\n touch-action: pan-y;\n}\n```\n\n```ts\nconst pan = createPanGesture(row, { axis: 'x', onMove });\n```\n\n## Pointer Capture\n\nPointer capture is enabled by default. After axis intent is accepted, Gesture attempts to capture the pointer while continuing to track movement through document-level listeners. Capture failure (for example after target detachment or pointer termination) falls back to document tracking without stranding the session.\n\nDisable capture when nested or newly revealed controls must retain native pointer-up and click targeting:\n\n```ts\nconst pan = createPanGesture(row, {\n axis: 'x',\n pointerCapture: false,\n onMove: renderReveal,\n onEnd: settleReveal,\n});\n```\n\nDocument-level tracking still keeps the pan active outside the target. Disabling capture changes event targeting, not gesture tracking.\n\n## Interactive Descendants\n\nUse `shouldStart` when buttons, links, or form controls inside the surface must not start a pan.\n\n```ts\nconst pan = createPanGesture(notification, {\n axis: 'x',\n pointerCapture: false,\n shouldStart: (event) =>\n !event\n .composedPath()\n .some((node) => node instanceof Element && node.matches('button, a, input, select, textarea')),\n onMove,\n onEnd,\n});\n```\n\n`shouldStart` protects controls under the initial pointer. `pointerCapture: false` additionally protects controls that appear beneath the pointer during a reveal interaction.\n\n## Disabled State\n\nA boolean disables the recognizer permanently. A getter supports state that changes while the handle is alive.\n\n```ts\nconst pan = createPanGesture(row, {\n disabled: () => isLocked,\n onEnd: ({ reason }) => {\n if (reason === 'cancel') resetRow();\n },\n});\n```\n\nWhen the getter becomes `true`, the next pointer event cancels an active pan. Window blur or a hidden owner document also cancels active tracking so a lost release cannot block later gestures.\n\nGesture snapshots fixed callbacks and capture policy at construction. Use the `axis` and `disabled` functions for behavior that must remain dynamic instead of mutating the options object.\n\n## Lifecycle\n\nDispose the target-bound handle when its owning UI scope unmounts.\n\n```ts\nconst element = document.querySelector<HTMLElement>('[data-pan]');\nif (!element) throw new Error('Missing [data-pan] element');\n\nconst owner = new AbortController();\nconst request = new AbortController();\nconst pan = createPanGesture(element, {\n axis: 'x',\n signal: owner.signal,\n});\n\nconst childSignal = AbortSignal.any([pan.disposalSignal, request.signal]);\nwindow.addEventListener('pagehide', () => owner.abort(), { once: true });\n```\n\nPass `{ signal }` to dispose with an existing owner signal. `disposalSignal` always aborts when Gesture finishes teardown, so child work can compose with the handle regardless of how disposal starts.\n\nUse `cancel()` to stop a pending or active interaction without disposing the handle. An active interaction emits `onEnd` with `reason: 'cancel'`. Call `cancel()` before `dispose()` when a connected surface needs its `onEnd` reset path; disposal itself does not invoke callbacks.\n\n## Framework Integration\n\nCreate the handle after the target element exists and dispose it on unmount.\n\n::: code-group\n\n```tsx [React]\nimport { useEffect, useRef } from 'react';\nimport { createPanGesture } from '@vielzeug/gesture';\n\nfunction SwipeRow({ onDismiss }: { onDismiss: () => void }) {\n const rowRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const row = rowRef.current;\n if (!row) return;\n\n const pan = createPanGesture(row, {\n axis: 'x',\n onMove: ({ distance }) => {\n row.style.transform = `translateX(${distance}px)`;\n },\n onEnd: ({ distance, reason }) => {\n row.style.transform = '';\n if (reason === 'release' && Math.abs(distance) >= 64) onDismiss();\n },\n });\n\n return () => pan.dispose();\n }, [onDismiss]);\n\n return <div ref={rowRef}>Swipe me</div>;\n}\n```\n\n```vue [Vue 3]\n<script setup lang=\"ts\">\nimport { onMounted, onUnmounted, ref } from 'vue';\nimport { createPanGesture, type PanGesture } from '@vielzeug/gesture';\n\nconst emit = defineEmits<{ dismiss: [] }>();\nconst rowEl = ref<HTMLDivElement | null>(null);\nlet pan: PanGesture | undefined;\n\nonMounted(() => {\n const row = rowEl.value;\n if (!row) return;\n\n pan = createPanGesture(row, {\n axis: 'x',\n onEnd: ({ distance, reason }) => {\n if (reason === 'release' && Math.abs(distance) >= 64) emit('dismiss');\n },\n });\n});\n\nonUnmounted(() => pan?.dispose());\n</script>\n\n<template>\n <div ref=\"rowEl\">Swipe me</div>\n</template>\n```\n\n```svelte [Svelte]\n<script lang=\"ts\">\n import { onMount } from 'svelte';\n import { createPanGesture } from '@vielzeug/gesture';\n\n let { ondismiss = () => {} }: { ondismiss: () => void } = $props();\n let rowEl: HTMLDivElement;\n\n onMount(() => {\n const pan = createPanGesture(rowEl, {\n axis: 'x',\n onEnd: ({ distance, reason }) => {\n if (reason === 'release' && Math.abs(distance) >= 64) ondismiss();\n },\n });\n\n return () => pan.dispose();\n });\n</script>\n\n<div bind:this={rowEl}>Swipe me</div>\n```\n\n:::\n\n## Working with Other Vielzeug Libraries\n\n### Gesture + Refine\n\nRefine uses Gesture internally for carousel, drawer, toast, and list-item pointer interactions. Custom surfaces can use the same pan lifecycle while keeping visual state local.\n\n```ts\nimport { createPanGesture } from '@vielzeug/gesture';\n\nconst pan = createPanGesture(panel, {\n axis: 'x',\n onMove: ({ distance }) => {\n panel.style.transform = `translateX(${distance}px)`;\n },\n onEnd: ({ distance, reason }) => {\n panel.style.transform = '';\n if (reason === 'release' && Math.abs(distance) >= 80) revealActions();\n },\n});\n```\n\n### Gesture + Dnd\n\nGesture owns pointer recognition: unrestricted two-dimensional drag and one-axis pan. Dnd builds touch sorting on `createDragGesture()`, then owns `DataTransfer`, previews, hit-testing, file drop zones, keyboard sorting, DOM reordering, and connected-list transactions. Use Dnd when an item is picked up and dropped; use Gesture directly when application code owns rendering and completion.\n\n## Best Practices\n\n- **Set** `touch-action` for the axis the browser should continue scrolling.\n- **Use** `shouldStart` to exclude nested interactive controls.\n- **Disable** pointer capture when nested or newly revealed controls must keep native release targeting.\n- **Apply** completion thresholds and direction rules in `onEnd`; use `activationDistance` only for recognition slop.\n- **Treat** `reason: 'cancel'` as a reset path, never a commit path. Pointer capture loss on the gesture target cancels; capture loss bubbling from a descendant does not.\n- **Keep** `onMove` rendering lightweight.\n- **Dispose** the handle when its target leaves the UI.\n",
7
7
  "examples": "---\ntitle: 'Gesture: Examples'\ndescription: Worked examples for @vielzeug/gesture.\n---\n\n## Examples\n\n- [Carousel Pan Navigation](./examples/carousel-swipe-navigation.md)\n- [Swipe-to-Dismiss Notifications](./examples/swipe-dismiss-notifications.md)\n"
8
8
  },
9
9
  "examples": [