@stacknav/core 0.3.0 → 0.4.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.
Files changed (56) hide show
  1. package/README.md +125 -60
  2. package/dist/animate.d.ts +20 -2
  3. package/dist/animate.d.ts.map +1 -1
  4. package/dist/animate.js +76 -13
  5. package/dist/animate.js.map +1 -1
  6. package/dist/css-vars.d.ts +3 -3
  7. package/dist/css-vars.d.ts.map +1 -1
  8. package/dist/css-vars.js +47 -4
  9. package/dist/css-vars.js.map +1 -1
  10. package/dist/history-adapter.d.ts +0 -1
  11. package/dist/history-adapter.d.ts.map +1 -1
  12. package/dist/history-adapter.js +1 -5
  13. package/dist/history-adapter.js.map +1 -1
  14. package/dist/index.d.ts +27 -18
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +38 -15
  17. package/dist/index.js.map +1 -1
  18. package/dist/native-transition.d.ts +93 -0
  19. package/dist/native-transition.d.ts.map +1 -0
  20. package/dist/{ios-transition.js → native-transition.js} +48 -29
  21. package/dist/native-transition.js.map +1 -0
  22. package/dist/navigation-stack.d.ts +3 -2
  23. package/dist/navigation-stack.d.ts.map +1 -1
  24. package/dist/navigation-stack.js +45 -10
  25. package/dist/navigation-stack.js.map +1 -1
  26. package/dist/platform.d.ts +6 -0
  27. package/dist/platform.d.ts.map +1 -0
  28. package/dist/platform.js +22 -0
  29. package/dist/platform.js.map +1 -0
  30. package/dist/stacknav.css +10 -13
  31. package/dist/styles.d.ts +10 -11
  32. package/dist/styles.d.ts.map +1 -1
  33. package/dist/styles.js +14 -16
  34. package/dist/styles.js.map +1 -1
  35. package/dist/swipe-back.d.ts +4 -0
  36. package/dist/swipe-back.d.ts.map +1 -0
  37. package/dist/swipe-back.js +31 -0
  38. package/dist/swipe-back.js.map +1 -0
  39. package/package.json +3 -2
  40. package/src/animate.ts +74 -15
  41. package/src/css-vars.ts +43 -4
  42. package/src/history-adapter.ts +1 -5
  43. package/src/index.ts +49 -24
  44. package/src/{ios-transition.ts → native-transition.ts} +72 -39
  45. package/src/navigation-stack.ts +33 -10
  46. package/src/platform.ts +23 -0
  47. package/src/styles.ts +14 -16
  48. package/src/swipe-back.ts +35 -0
  49. package/dist/edge-pan-gesture.d.ts +0 -36
  50. package/dist/edge-pan-gesture.d.ts.map +0 -1
  51. package/dist/edge-pan-gesture.js +0 -155
  52. package/dist/edge-pan-gesture.js.map +0 -1
  53. package/dist/ios-transition.d.ts +0 -61
  54. package/dist/ios-transition.d.ts.map +0 -1
  55. package/dist/ios-transition.js.map +0 -1
  56. package/src/edge-pan-gesture.ts +0 -190
@@ -0,0 +1,35 @@
1
+ /** Browser suppression is best effort; browser buttons and OS gestures remain available. */
2
+ export type SwipeBackMode = 'browser' | 'disabled';
3
+
4
+ interface Suppression {
5
+ count: number;
6
+ value: string;
7
+ priority: string;
8
+ }
9
+
10
+ // Several outlets can share one viewport. Restore it only after the last
11
+ // suppression request is released, including when stacks are destroyed.
12
+ const suppressions = /*#__PURE__*/ new WeakMap<HTMLElement, Suppression>();
13
+
14
+ export function suppressBrowserSwipe(container: HTMLElement): () => void {
15
+ const root = container.ownerDocument.documentElement;
16
+ const property = 'overscroll-behavior-x';
17
+ let state = suppressions.get(root);
18
+ if (!state) {
19
+ state = { count: 0, value: root.style.getPropertyValue(property), priority: root.style.getPropertyPriority(property) };
20
+ suppressions.set(root, state);
21
+ root.style.setProperty(property, 'contain', 'important');
22
+ }
23
+ state.count++;
24
+ let released = false;
25
+ return () => {
26
+ if (released) return;
27
+ released = true;
28
+ if (--state.count) return;
29
+ suppressions.delete(root);
30
+ // Do not overwrite a newer declaration installed by the application.
31
+ if (root.style.getPropertyValue(property) !== 'contain' || root.style.getPropertyPriority(property) !== 'important') return;
32
+ if (state.value) root.style.setProperty(property, state.value, state.priority);
33
+ else root.style.removeProperty(property);
34
+ };
35
+ }
@@ -1,36 +0,0 @@
1
- import type { NavigationStack } from './navigation-stack.ts';
2
- export interface EdgePanGestureOptions {
3
- /** px strip on the leading edge that starts the gesture */
4
- edgeWidth: number;
5
- /** recognize the drag from anywhere on the page */
6
- anywhere: boolean;
7
- /** px of horizontal movement before the drag begins */
8
- startSlop: number;
9
- /** px of vertical movement that hands the touch back to scrolling */
10
- verticalCancelSlop: number;
11
- /** fraction of the width dragged that completes without velocity */
12
- completeThreshold: number;
13
- /** px/s toward the trailing edge that completes the pop regardless of distance */
14
- completeVelocity: number;
15
- /** px/s back toward the leading edge that cancels the pop regardless of distance */
16
- cancelVelocity: number;
17
- velocitySamples: number;
18
- }
19
- export interface EdgePanGesture {
20
- readonly options: EdgePanGestureOptions;
21
- /** Re-reads options changed at runtime, such as `edgeWidth` and `anywhere`. */
22
- refresh(): void;
23
- attach(stack: NavigationStack): EdgePanGesture;
24
- detach(): void;
25
- }
26
- /**
27
- * Recognizes a horizontal drag from the leading edge and drives the stack's
28
- * interactive pop from it. Built on pointer events, so it handles both mouse
29
- * and touch. Vertical movement early in the gesture hands the touch back to
30
- * native scrolling.
31
- *
32
- * "Leading" is whichever edge the container reads from, so in a right-to-left
33
- * container the strip sits on the right and back is a drag to the left.
34
- */
35
- export declare function createEdgePanGesture(options?: Partial<EdgePanGestureOptions>): EdgePanGesture;
36
- //# sourceMappingURL=edge-pan-gesture.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"edge-pan-gesture.d.ts","sourceRoot":"","sources":["../src/edge-pan-gesture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAwB,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEnF,MAAM,WAAW,qBAAqB;IACpC,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,QAAQ,EAAE,OAAO,CAAC;IAClB,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oEAAoE;IACpE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kFAAkF;IAClF,gBAAgB,EAAE,MAAM,CAAC;IACzB,oFAAoF;IACpF,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,qBAAqB,CAAC;IACxC,+EAA+E;IAC/E,OAAO,IAAI,IAAI,CAAC;IAChB,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,cAAc,CAAC;IAC/C,MAAM,IAAI,IAAI,CAAC;CAChB;AAcD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,OAAO,CAAC,qBAAqB,CAAM,GAAG,cAAc,CA4IjG"}
@@ -1,155 +0,0 @@
1
- /**
2
- * Recognizes a horizontal drag from the leading edge and drives the stack's
3
- * interactive pop from it. Built on pointer events, so it handles both mouse
4
- * and touch. Vertical movement early in the gesture hands the touch back to
5
- * native scrolling.
6
- *
7
- * "Leading" is whichever edge the container reads from, so in a right-to-left
8
- * container the strip sits on the right and back is a drag to the left.
9
- */
10
- export function createEdgePanGesture(options = {}) {
11
- const o = {
12
- edgeWidth: 28,
13
- anywhere: false,
14
- startSlop: 6,
15
- verticalCancelSlop: 10,
16
- completeThreshold: 0.5,
17
- completeVelocity: 500,
18
- cancelVelocity: -500,
19
- velocitySamples: 6,
20
- ...options,
21
- };
22
- let stack;
23
- let strip = null;
24
- /**
25
- * Which way forward is, read the same way the pages read it: `--sn-dir` when
26
- * the stylesheet sets it, the container's reading direction otherwise. Taking
27
- * it from one place is what keeps the drag and the transition from disagreeing
28
- * about which edge is the back edge.
29
- */
30
- const direction = () => {
31
- const style = typeof getComputedStyle === 'function' ? getComputedStyle(stack.container) : null;
32
- const declared = Number(style?.getPropertyValue('--sn-dir'));
33
- if (declared < 0)
34
- return -1;
35
- if (declared > 0)
36
- return 1;
37
- return style?.direction === 'rtl' ? -1 : 1;
38
- };
39
- let drag = null;
40
- let suppressClick = false;
41
- let offs = [];
42
- const onDown = (ev) => {
43
- if (drag || !stack.canPop())
44
- return;
45
- if (ev.pointerType === 'mouse' && ev.button !== 0)
46
- return;
47
- if (ev.currentTarget === stack.container && !o.anywhere)
48
- return;
49
- drag = { id: ev.pointerId, target: ev.currentTarget, x0: ev.clientX, y0: ev.clientY, dir: direction(), handle: null, p: 1, samples: [[ev.clientX, performance.now()]] };
50
- };
51
- const onMove = (ev) => {
52
- if (!drag || ev.pointerId !== drag.id)
53
- return;
54
- // Signed so that "forward along the drag" is always positive, whichever
55
- // edge the container calls leading.
56
- const dx = (ev.clientX - drag.x0) * drag.dir;
57
- const dy = ev.clientY - drag.y0;
58
- if (!drag.handle) {
59
- if (Math.abs(dy) > o.verticalCancelSlop && Math.abs(dy) > Math.abs(dx)) {
60
- drag = null;
61
- return;
62
- }
63
- if (dx < o.startSlop)
64
- return;
65
- const handle = stack.beginInteractivePop();
66
- if (!handle) {
67
- drag = null;
68
- return;
69
- }
70
- drag.handle = handle;
71
- try {
72
- drag.target.setPointerCapture?.(ev.pointerId);
73
- }
74
- catch {
75
- /* capture is best-effort */
76
- }
77
- }
78
- drag.samples.push([ev.clientX, performance.now()]);
79
- if (drag.samples.length > o.velocitySamples)
80
- drag.samples.shift();
81
- drag.p = 1 - Math.min(1, Math.max(0, (dx - o.startSlop) / stack.width()));
82
- drag.handle.update(drag.p);
83
- };
84
- const onUp = (ev) => {
85
- if (!drag || ev.pointerId !== drag.id)
86
- return;
87
- const d = drag;
88
- drag = null;
89
- if (!d.handle)
90
- return;
91
- const s = d.samples;
92
- const [x1, t1] = s[0];
93
- const [x2, t2] = s[s.length - 1];
94
- const velocity = (t2 > t1 ? ((x2 - x1) / (t2 - t1)) * 1000 : 0) * d.dir;
95
- const cancelled = ev.type === 'pointercancel';
96
- const complete = !cancelled && (velocity > o.completeVelocity || (d.p < 1 - o.completeThreshold && velocity > o.cancelVelocity));
97
- // The click that follows a drag release must not activate whatever is under the pointer.
98
- suppressClick = true;
99
- setTimeout(() => {
100
- suppressClick = false;
101
- }, 0);
102
- void d.handle.finish({ complete, velocity });
103
- };
104
- const onClick = (ev) => {
105
- if (suppressClick) {
106
- ev.stopPropagation();
107
- ev.preventDefault();
108
- }
109
- };
110
- const EVENTS = { pointerdown: onDown, pointermove: onMove, pointerup: onUp, pointercancel: onUp };
111
- const listen = (el) => Object.entries(EVENTS).forEach(([k, f]) => el.addEventListener(k, f));
112
- const unlisten = (el) => Object.entries(EVENTS).forEach(([k, f]) => el.removeEventListener(k, f));
113
- /**
114
- * Whether the strip is shown at all is a CSS question — the stylesheet hides
115
- * it when there is nothing to go back to, or when the whole page is the
116
- * target — so this only has to say what is true.
117
- */
118
- const refresh = () => {
119
- if (!strip)
120
- return;
121
- strip.style.width = o.edgeWidth + 'px';
122
- stack.container.classList.toggle('sn-anywhere', o.anywhere);
123
- stack.container.classList.toggle('sn-can-pop', stack.entries.length > 1);
124
- };
125
- const gesture = {
126
- options: o,
127
- refresh,
128
- attach(s) {
129
- stack = s;
130
- strip = document.createElement('div');
131
- strip.className = 'sn-edge';
132
- strip.setAttribute('aria-hidden', 'true');
133
- stack.container.append(strip);
134
- listen(strip);
135
- listen(stack.container);
136
- stack.container.addEventListener('click', onClick, true);
137
- offs = ['push', 'pop', 'replace', 'reset'].map((e) => stack.on(e, refresh));
138
- refresh();
139
- return gesture;
140
- },
141
- detach() {
142
- if (!strip)
143
- return;
144
- offs.forEach((f) => f());
145
- unlisten(strip);
146
- unlisten(stack.container);
147
- stack.container.removeEventListener('click', onClick, true);
148
- stack.container.classList.remove('sn-anywhere', 'sn-can-pop');
149
- strip.remove();
150
- strip = null;
151
- },
152
- };
153
- return gesture;
154
- }
155
- //# sourceMappingURL=edge-pan-gesture.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"edge-pan-gesture.js","sourceRoot":"","sources":["../src/edge-pan-gesture.ts"],"names":[],"mappings":"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"}
@@ -1,61 +0,0 @@
1
- import { type Easing } from './animate.ts';
2
- import type { Transition } from './navigation-stack.ts';
3
- export interface IOSTransitionOptions {
4
- /** ms, programmatic push/pop */
5
- duration: number;
6
- /**
7
- * the curve a programmatic push/pop runs on. CSS runs it, so it has to be
8
- * one CSS can spell: everything `cubicBezier()` and `parseEasing()` build
9
- * carries a `css` property. A bare `(t) => number` of your own has none, so
10
- * the pages would run `linear` while `progress` reported your curve; give it
11
- * a `css` property, or write the whole transition yourself.
12
- */
13
- ease: Easing;
14
- /** fraction of the width the lower page travels */
15
- parallax: number;
16
- dimColor: string;
17
- /** lower-page overlay opacity at p = 1 (≈0.35 suits dark UIs) */
18
- dimMax: number;
19
- /** box-shadow on the incoming page */
20
- shadow: string;
21
- /** ms bounds for finishing an interactive pop */
22
- settleMin: number;
23
- settleMax: number;
24
- /** the curve the remaining distance of an interactive pop runs on; see `ease` */
25
- settleEase: Easing;
26
- /** px/s assumed when the pointer was slower than this */
27
- settleVelocityFloor: number;
28
- /** multiplies every duration, for slow motion and tests */
29
- timeScale: number;
30
- }
31
- /** The CSS custom property behind each option. */
32
- export declare const IOS_TRANSITION_CSS_VARS: Readonly<Record<keyof IOSTransitionOptions, string>>;
33
- export interface IOSTransition extends Transition {
34
- /** The JS options: the defaults with the caller's merged in. Mutable at runtime. */
35
- readonly options: IOSTransitionOptions;
36
- /** The values currently in force: `options` with the CSS variables applied over them. */
37
- readonly resolved: Readonly<IOSTransitionOptions>;
38
- /**
39
- * Re-reads the CSS variables, from `el` or from the container of the last
40
- * transition. Called at the start of every transition. Call it directly
41
- * after changing `options` or the variables mid-animation.
42
- */
43
- refresh(el?: Element | null): void;
44
- }
45
- /**
46
- * The iOS navigation transition: the upper page slides in from the trailing
47
- * edge with a shadow on its leading edge, while the lower page parallaxes
48
- * toward the leading edge and dims. Every value is a function of one number, p.
49
- *
50
- * Every option is also a CSS custom property on the container (see
51
- * `IOS_TRANSITION_CSS_VARS`), read when a transition starts. A variable that
52
- * is set wins over the JS option, so a stylesheet can slow the animation down
53
- * or restyle it per theme without the app rebuilding the transition.
54
- *
55
- * p is only ever written at the ends of a phase. The stack puts that phase's
56
- * duration and curve in `--sn-t` / `--sn-e` and CSS interpolates between the
57
- * two writes, so the animation costs a handful of style writes rather than one
58
- * per page per frame, and runs on the compositor rather than the main thread.
59
- */
60
- export declare function createIOSTransition(options?: Partial<IOSTransitionOptions>): IOSTransition;
61
- //# sourceMappingURL=ios-transition.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ios-transition.d.ts","sourceRoot":"","sources":["../src/ios-transition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAE1E,OAAO,KAAK,EAA2B,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEjF,MAAM,WAAW,oBAAoB;IACnC,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,kDAAkD;AAClD,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,oBAAoB,EAAE,MAAM,CAAC,CAYvF,CAAC;AAEH,MAAM,WAAW,aAAc,SAAQ,UAAU;IAC/C,oFAAoF;IACpF,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;IACvC,yFAAyF;IACzF,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,oBAAoB,CAAC,CAAC;IAClD;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAAC;CACpC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,OAAO,CAAC,oBAAoB,CAAM,GAAG,aAAa,CAsG9F"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ios-transition.js","sourceRoot":"","sources":["../src/ios-transition.ts"],"names":[],"mappings":"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"}
@@ -1,190 +0,0 @@
1
- import type { InteractivePopHandle, NavigationStack } from './navigation-stack.ts';
2
-
3
- export interface EdgePanGestureOptions {
4
- /** px strip on the leading edge that starts the gesture */
5
- edgeWidth: number;
6
- /** recognize the drag from anywhere on the page */
7
- anywhere: boolean;
8
- /** px of horizontal movement before the drag begins */
9
- startSlop: number;
10
- /** px of vertical movement that hands the touch back to scrolling */
11
- verticalCancelSlop: number;
12
- /** fraction of the width dragged that completes without velocity */
13
- completeThreshold: number;
14
- /** px/s toward the trailing edge that completes the pop regardless of distance */
15
- completeVelocity: number;
16
- /** px/s back toward the leading edge that cancels the pop regardless of distance */
17
- cancelVelocity: number;
18
- velocitySamples: number;
19
- }
20
-
21
- export interface EdgePanGesture {
22
- readonly options: EdgePanGestureOptions;
23
- /** Re-reads options changed at runtime, such as `edgeWidth` and `anywhere`. */
24
- refresh(): void;
25
- attach(stack: NavigationStack): EdgePanGesture;
26
- detach(): void;
27
- }
28
-
29
- interface Drag {
30
- id: number;
31
- target: EventTarget & { setPointerCapture?(id: number): void };
32
- x0: number;
33
- y0: number;
34
- /** +1 when back is a drag to the right, -1 when the container reads right-to-left */
35
- dir: number;
36
- handle: InteractivePopHandle | null;
37
- p: number;
38
- samples: Array<[number, number]>;
39
- }
40
-
41
- /**
42
- * Recognizes a horizontal drag from the leading edge and drives the stack's
43
- * interactive pop from it. Built on pointer events, so it handles both mouse
44
- * and touch. Vertical movement early in the gesture hands the touch back to
45
- * native scrolling.
46
- *
47
- * "Leading" is whichever edge the container reads from, so in a right-to-left
48
- * container the strip sits on the right and back is a drag to the left.
49
- */
50
- export function createEdgePanGesture(options: Partial<EdgePanGestureOptions> = {}): EdgePanGesture {
51
- const o: EdgePanGestureOptions = {
52
- edgeWidth: 28,
53
- anywhere: false,
54
- startSlop: 6,
55
- verticalCancelSlop: 10,
56
- completeThreshold: 0.5,
57
- completeVelocity: 500,
58
- cancelVelocity: -500,
59
- velocitySamples: 6,
60
- ...options,
61
- };
62
-
63
- let stack: NavigationStack;
64
- let strip: HTMLElement | null = null;
65
-
66
- /**
67
- * Which way forward is, read the same way the pages read it: `--sn-dir` when
68
- * the stylesheet sets it, the container's reading direction otherwise. Taking
69
- * it from one place is what keeps the drag and the transition from disagreeing
70
- * about which edge is the back edge.
71
- */
72
- const direction = (): number => {
73
- const style = typeof getComputedStyle === 'function' ? getComputedStyle(stack.container) : null;
74
- const declared = Number(style?.getPropertyValue('--sn-dir'));
75
- if (declared < 0) return -1;
76
- if (declared > 0) return 1;
77
- return style?.direction === 'rtl' ? -1 : 1;
78
- };
79
- let drag: Drag | null = null;
80
- let suppressClick = false;
81
- let offs: Array<() => void> = [];
82
-
83
- const onDown = (ev: PointerEvent) => {
84
- if (drag || !stack.canPop()) return;
85
- if (ev.pointerType === 'mouse' && ev.button !== 0) return;
86
- if (ev.currentTarget === stack.container && !o.anywhere) return;
87
- drag = { id: ev.pointerId, target: ev.currentTarget as Drag['target'], x0: ev.clientX, y0: ev.clientY, dir: direction(), handle: null, p: 1, samples: [[ev.clientX, performance.now()]] };
88
- };
89
-
90
- const onMove = (ev: PointerEvent) => {
91
- if (!drag || ev.pointerId !== drag.id) return;
92
- // Signed so that "forward along the drag" is always positive, whichever
93
- // edge the container calls leading.
94
- const dx = (ev.clientX - drag.x0) * drag.dir;
95
- const dy = ev.clientY - drag.y0;
96
- if (!drag.handle) {
97
- if (Math.abs(dy) > o.verticalCancelSlop && Math.abs(dy) > Math.abs(dx)) {
98
- drag = null;
99
- return;
100
- }
101
- if (dx < o.startSlop) return;
102
- const handle = stack.beginInteractivePop();
103
- if (!handle) {
104
- drag = null;
105
- return;
106
- }
107
- drag.handle = handle;
108
- try {
109
- drag.target.setPointerCapture?.(ev.pointerId);
110
- } catch {
111
- /* capture is best-effort */
112
- }
113
- }
114
- drag.samples.push([ev.clientX, performance.now()]);
115
- if (drag.samples.length > o.velocitySamples) drag.samples.shift();
116
- drag.p = 1 - Math.min(1, Math.max(0, (dx - o.startSlop) / stack.width()));
117
- drag.handle.update(drag.p);
118
- };
119
-
120
- const onUp = (ev: PointerEvent) => {
121
- if (!drag || ev.pointerId !== drag.id) return;
122
- const d = drag;
123
- drag = null;
124
- if (!d.handle) return;
125
- const s = d.samples;
126
- const [x1, t1] = s[0];
127
- const [x2, t2] = s[s.length - 1];
128
- const velocity = (t2 > t1 ? ((x2 - x1) / (t2 - t1)) * 1000 : 0) * d.dir;
129
- const cancelled = ev.type === 'pointercancel';
130
- const complete = !cancelled && (velocity > o.completeVelocity || (d.p < 1 - o.completeThreshold && velocity > o.cancelVelocity));
131
- // The click that follows a drag release must not activate whatever is under the pointer.
132
- suppressClick = true;
133
- setTimeout(() => {
134
- suppressClick = false;
135
- }, 0);
136
- void d.handle.finish({ complete, velocity });
137
- };
138
-
139
- const onClick = (ev: Event) => {
140
- if (suppressClick) {
141
- ev.stopPropagation();
142
- ev.preventDefault();
143
- }
144
- };
145
-
146
- const EVENTS: Record<string, (ev: PointerEvent) => void> = { pointerdown: onDown, pointermove: onMove, pointerup: onUp, pointercancel: onUp };
147
- const listen = (el: HTMLElement) => Object.entries(EVENTS).forEach(([k, f]) => el.addEventListener(k, f as EventListener));
148
- const unlisten = (el: HTMLElement) => Object.entries(EVENTS).forEach(([k, f]) => el.removeEventListener(k, f as EventListener));
149
-
150
- /**
151
- * Whether the strip is shown at all is a CSS question — the stylesheet hides
152
- * it when there is nothing to go back to, or when the whole page is the
153
- * target — so this only has to say what is true.
154
- */
155
- const refresh = () => {
156
- if (!strip) return;
157
- strip.style.width = o.edgeWidth + 'px';
158
- stack.container.classList.toggle('sn-anywhere', o.anywhere);
159
- stack.container.classList.toggle('sn-can-pop', stack.entries.length > 1);
160
- };
161
-
162
- const gesture: EdgePanGesture = {
163
- options: o,
164
- refresh,
165
- attach(s) {
166
- stack = s;
167
- strip = document.createElement('div');
168
- strip.className = 'sn-edge';
169
- strip.setAttribute('aria-hidden', 'true');
170
- stack.container.append(strip);
171
- listen(strip);
172
- listen(stack.container);
173
- stack.container.addEventListener('click', onClick, true);
174
- offs = (['push', 'pop', 'replace', 'reset'] as const).map((e) => stack.on(e, refresh));
175
- refresh();
176
- return gesture;
177
- },
178
- detach() {
179
- if (!strip) return;
180
- offs.forEach((f) => f());
181
- unlisten(strip);
182
- unlisten(stack.container);
183
- stack.container.removeEventListener('click', onClick, true);
184
- stack.container.classList.remove('sn-anywhere', 'sn-can-pop');
185
- strip.remove();
186
- strip = null;
187
- },
188
- };
189
- return gesture;
190
- }