@stacknav/core 0.2.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 (48) hide show
  1. package/README.md +235 -0
  2. package/dist/animate.d.ts +26 -0
  3. package/dist/animate.d.ts.map +1 -0
  4. package/dist/animate.js +69 -0
  5. package/dist/animate.js.map +1 -0
  6. package/dist/css-vars.d.ts +22 -0
  7. package/dist/css-vars.d.ts.map +1 -0
  8. package/dist/css-vars.js +89 -0
  9. package/dist/css-vars.js.map +1 -0
  10. package/dist/direction.d.ts +79 -0
  11. package/dist/direction.d.ts.map +1 -0
  12. package/dist/direction.js +98 -0
  13. package/dist/direction.js.map +1 -0
  14. package/dist/edge-pan-gesture.d.ts +33 -0
  15. package/dist/edge-pan-gesture.d.ts.map +1 -0
  16. package/dist/edge-pan-gesture.js +128 -0
  17. package/dist/edge-pan-gesture.js.map +1 -0
  18. package/dist/history-adapter.d.ts +17 -0
  19. package/dist/history-adapter.d.ts.map +1 -0
  20. package/dist/history-adapter.js +44 -0
  21. package/dist/history-adapter.js.map +1 -0
  22. package/dist/index.d.ts +34 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +30 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/ios-transition.d.ts +50 -0
  27. package/dist/ios-transition.d.ts.map +1 -0
  28. package/dist/ios-transition.js +122 -0
  29. package/dist/ios-transition.js.map +1 -0
  30. package/dist/navigation-stack.d.ts +177 -0
  31. package/dist/navigation-stack.d.ts.map +1 -0
  32. package/dist/navigation-stack.js +296 -0
  33. package/dist/navigation-stack.js.map +1 -0
  34. package/dist/stacknav.css +33 -0
  35. package/dist/styles.d.ts +29 -0
  36. package/dist/styles.d.ts.map +1 -0
  37. package/dist/styles.js +39 -0
  38. package/dist/styles.js.map +1 -0
  39. package/package.json +43 -0
  40. package/src/animate.ts +80 -0
  41. package/src/css-vars.ts +91 -0
  42. package/src/direction.ts +146 -0
  43. package/src/edge-pan-gesture.ts +162 -0
  44. package/src/history-adapter.ts +52 -0
  45. package/src/index.ts +86 -0
  46. package/src/ios-transition.ts +163 -0
  47. package/src/navigation-stack.ts +362 -0
  48. package/src/styles.ts +40 -0
@@ -0,0 +1,177 @@
1
+ import { type Easing } from './animate.ts';
2
+ /** One mounted page. `key` and `data` belong to the caller; the stack only carries them. */
3
+ export interface StackEntry<T = unknown> {
4
+ el: HTMLElement;
5
+ index: number;
6
+ key: string | null;
7
+ data: T | null;
8
+ }
9
+ export interface SettleInput {
10
+ remainingPx: number;
11
+ velocity: number;
12
+ }
13
+ /**
14
+ * Describes what the pages look like at any progress p (1 = upper page fully
15
+ * open, 0 = upper page fully off-screen). Push runs p from 0 to 1, pop from
16
+ * 1 to 0.
17
+ */
18
+ export interface Transition {
19
+ readonly duration: number;
20
+ readonly ease: Easing;
21
+ settle(input: SettleInput): {
22
+ duration: number;
23
+ ease: Easing;
24
+ };
25
+ begin?(lower: StackEntry | null, upper: StackEntry): void;
26
+ apply(lower: StackEntry | null, upper: StackEntry, p: number): void;
27
+ end?(lower: StackEntry | null, upper: StackEntry): void;
28
+ }
29
+ export type TransitionKind = 'push' | 'pop' | 'interactive';
30
+ /** Where an operation came from: `api`, `gesture`, `history`, or any value a port defines. */
31
+ export type NavigationSource = 'api' | 'gesture' | 'history' | (string & {});
32
+ export interface MountOptions<T = unknown> {
33
+ animated?: boolean;
34
+ data?: T | null;
35
+ key?: string | null;
36
+ source?: NavigationSource;
37
+ }
38
+ export interface PushEvent {
39
+ entry: StackEntry;
40
+ entries: StackEntry[];
41
+ source: NavigationSource;
42
+ }
43
+ export interface PopEvent {
44
+ entry: StackEntry;
45
+ removed: StackEntry[];
46
+ entries: StackEntry[];
47
+ source: NavigationSource;
48
+ }
49
+ export interface ReplaceEvent {
50
+ entry: StackEntry;
51
+ removed: StackEntry[];
52
+ entries: StackEntry[];
53
+ source: NavigationSource;
54
+ }
55
+ export interface ResetEvent {
56
+ entries: StackEntry[];
57
+ removed: StackEntry[];
58
+ source: NavigationSource;
59
+ }
60
+ export interface TransitionEvent {
61
+ lower: StackEntry | null;
62
+ upper: StackEntry;
63
+ kind: TransitionKind;
64
+ }
65
+ export interface ProgressEvent {
66
+ lower: StackEntry | null;
67
+ upper: StackEntry;
68
+ p: number;
69
+ }
70
+ export interface StackEvents {
71
+ push: PushEvent;
72
+ pop: PopEvent;
73
+ replace: ReplaceEvent;
74
+ reset: ResetEvent;
75
+ transitionstart: TransitionEvent;
76
+ progress: ProgressEvent;
77
+ transitionend: TransitionEvent;
78
+ }
79
+ export interface InteractivePopHandle {
80
+ /** p = 1 fully open … 0 fully popped */
81
+ update(p: number): void;
82
+ /** Resolves when the settle animation ends. */
83
+ finish(input: {
84
+ complete: boolean;
85
+ velocity?: number;
86
+ }): Promise<void>;
87
+ }
88
+ export interface NavigationStackOptions {
89
+ container: HTMLElement;
90
+ transition: Transition;
91
+ pageClass?: string;
92
+ }
93
+ type Listener<E> = (detail: E) => void;
94
+ /**
95
+ * A stack of page elements inside one container. The stack owns mounting,
96
+ * ordering, visibility and the transition lifecycle. A `Transition` decides
97
+ * what the pages look like at any progress p.
98
+ *
99
+ * Operations are serialized: one called during a transition waits its turn.
100
+ */
101
+ export declare class NavigationStack {
102
+ readonly container: HTMLElement;
103
+ transition: Transition;
104
+ readonly pageClass: string;
105
+ entries: StackEntry[];
106
+ busy: boolean;
107
+ private _queue;
108
+ private _listeners;
109
+ constructor({ container, transition, pageClass }: NavigationStackOptions);
110
+ get depth(): number;
111
+ get top(): StackEntry | null;
112
+ width(): number;
113
+ canPop(): boolean;
114
+ /** The entry holding `el`, or the entry with `key`, if either is mounted. */
115
+ entryOf(elOrKey: HTMLElement | string): StackEntry | null;
116
+ on<K extends keyof StackEvents>(event: K, fn: Listener<StackEvents[K]>): () => void;
117
+ private _emit;
118
+ /**
119
+ * Pushes an element, or a function returning one. Resolves with the entry
120
+ * once the transition has finished. An element already mounted lower in the
121
+ * stack is moved to the top.
122
+ */
123
+ push<T = unknown>(elOrFactory: HTMLElement | (() => HTMLElement), { animated, data, key, source }?: MountOptions<T>): Promise<StackEntry>;
124
+ /** Pops one level. Resolves with the removed entry; its element is detached, not destroyed. */
125
+ pop(opts?: {
126
+ animated?: boolean;
127
+ source?: NavigationSource;
128
+ }): Promise<StackEntry | null>;
129
+ /** Pops until `depth` entries remain (≥ 1). Intermediate pages are removed without animation. */
130
+ popTo(depth: number, { animated, source }?: {
131
+ animated?: boolean;
132
+ source?: NavigationSource;
133
+ }): Promise<StackEntry | null>;
134
+ /**
135
+ * Pops the top page, revealing `el`. If `el` is already mounted beneath the
136
+ * top, everything above it is removed, as in `popTo`. If it is not mounted,
137
+ * it is placed directly beneath the top first, so a page that no longer
138
+ * exists (for example a fresh instance after a deep link) still arrives with
139
+ * a pop.
140
+ */
141
+ popWith<T = unknown>(el: HTMLElement, { animated, data, key, source }?: MountOptions<T>): Promise<StackEntry | null>;
142
+ /** Swaps the top page for `el` without animation. Returns the removed entry. */
143
+ replace<T = unknown>(el: HTMLElement, { data, key, source }?: MountOptions<T>): Promise<StackEntry | null>;
144
+ /**
145
+ * Convenience for ports that already resolved the direction: `push`, `pop`
146
+ * (via `popWith`) or `replace`.
147
+ */
148
+ present<T = unknown>(el: HTMLElement, direction: 'push' | 'pop' | 'replace', opts?: MountOptions<T>): Promise<StackEntry | null>;
149
+ /** Removes a mounted page without animation, wherever it sits. Returns its entry, or null. */
150
+ remove(el: HTMLElement, { source }?: {
151
+ source?: NavigationSource;
152
+ }): Promise<StackEntry | null>;
153
+ /** Replaces the whole stack without animation. Returns the removed entries. */
154
+ reset(elements: HTMLElement[], { source }?: {
155
+ source?: NavigationSource;
156
+ }): Promise<StackEntry[]>;
157
+ /** Starts a pointer-driven pop. Returns null if the stack cannot pop right now. */
158
+ beginInteractivePop(): InteractivePopHandle | null;
159
+ destroy(): void;
160
+ private _setBusy;
161
+ /** Serializes operations: while a transition runs, later calls wait their turn. */
162
+ private _run;
163
+ private _drain;
164
+ private _popRevealing;
165
+ private _mount;
166
+ private _unmount;
167
+ /** Drops `el` from the entries if it is mounted. Returns its old entry. */
168
+ private _forget;
169
+ private _begin;
170
+ private _apply;
171
+ private _end;
172
+ private _transition;
173
+ /** Makes only the top page visible, resets every transform, renumbers the indexes. */
174
+ private _settle;
175
+ }
176
+ export {};
177
+ //# sourceMappingURL=navigation-stack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation-stack.d.ts","sourceRoot":"","sources":["../src/navigation-stack.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,296 @@
1
+ import { tween } from "./animate.js";
2
+ /**
3
+ * A stack of page elements inside one container. The stack owns mounting,
4
+ * ordering, visibility and the transition lifecycle. A `Transition` decides
5
+ * what the pages look like at any progress p.
6
+ *
7
+ * Operations are serialized: one called during a transition waits its turn.
8
+ */
9
+ export class NavigationStack {
10
+ container;
11
+ transition;
12
+ pageClass;
13
+ entries = [];
14
+ busy = false;
15
+ _queue = [];
16
+ _listeners = new Map();
17
+ constructor({ container, transition, pageClass = 'sn-page' }) {
18
+ if (!container || !transition)
19
+ throw new Error('NavigationStack needs { container, transition }');
20
+ this.container = container;
21
+ this.transition = transition;
22
+ this.pageClass = pageClass;
23
+ container.classList.add('sn-container');
24
+ }
25
+ // ---------------------------------------------------------------- state
26
+ get depth() {
27
+ return this.entries.length;
28
+ }
29
+ get top() {
30
+ return this.entries[this.entries.length - 1] || null;
31
+ }
32
+ width() {
33
+ return this.container.clientWidth;
34
+ }
35
+ canPop() {
36
+ return !this.busy && this.entries.length > 1;
37
+ }
38
+ /** The entry holding `el`, or the entry with `key`, if either is mounted. */
39
+ entryOf(elOrKey) {
40
+ return this.entries.find((e) => (typeof elOrKey === 'string' ? e.key === elOrKey : e.el === elOrKey)) || null;
41
+ }
42
+ on(event, fn) {
43
+ if (!this._listeners.has(event))
44
+ this._listeners.set(event, new Set());
45
+ const set = this._listeners.get(event);
46
+ set.add(fn);
47
+ return () => {
48
+ set.delete(fn);
49
+ };
50
+ }
51
+ _emit(event, detail) {
52
+ const set = this._listeners.get(event);
53
+ if (set)
54
+ set.forEach((fn) => fn(detail));
55
+ }
56
+ // ------------------------------------------------------------ operations
57
+ /**
58
+ * Pushes an element, or a function returning one. Resolves with the entry
59
+ * once the transition has finished. An element already mounted lower in the
60
+ * stack is moved to the top.
61
+ */
62
+ push(elOrFactory, { animated = true, data = null, key = null, source = 'api' } = {}) {
63
+ return this._run(async () => {
64
+ const el = typeof elOrFactory === 'function' ? elOrFactory() : elOrFactory;
65
+ this._forget(el);
66
+ const lower = this.top;
67
+ const upper = this._mount(el, this.entries.length, data, key);
68
+ this.entries.push(upper);
69
+ await this._transition(lower, upper, 0, 1, animated, 'push');
70
+ this._settle();
71
+ this._emit('push', { entry: upper, entries: this.entries.slice(), source });
72
+ return upper;
73
+ });
74
+ }
75
+ /** Pops one level. Resolves with the removed entry; its element is detached, not destroyed. */
76
+ pop(opts = {}) {
77
+ return this.popTo(this.entries.length - 1, opts);
78
+ }
79
+ /** Pops until `depth` entries remain (≥ 1). Intermediate pages are removed without animation. */
80
+ popTo(depth, { animated = true, source = 'api' } = {}) {
81
+ return this._run(async () => {
82
+ if (depth < 1 || this.entries.length <= depth)
83
+ return null;
84
+ return this._popRevealing(depth, animated, source);
85
+ });
86
+ }
87
+ /**
88
+ * Pops the top page, revealing `el`. If `el` is already mounted beneath the
89
+ * top, everything above it is removed, as in `popTo`. If it is not mounted,
90
+ * it is placed directly beneath the top first, so a page that no longer
91
+ * exists (for example a fresh instance after a deep link) still arrives with
92
+ * a pop.
93
+ */
94
+ popWith(el, { animated = true, data = null, key = null, source = 'api' } = {}) {
95
+ return this._run(async () => {
96
+ if (!this.entries.length) {
97
+ const entry = this._mount(el, 0, data, key);
98
+ this.entries.push(entry);
99
+ this._settle();
100
+ this._emit('push', { entry, entries: this.entries.slice(), source });
101
+ return null;
102
+ }
103
+ const top = this.top;
104
+ if (top.el === el)
105
+ return null;
106
+ const existing = this.entries.findIndex((e) => e.el === el);
107
+ if (existing >= 0)
108
+ return this._popRevealing(existing + 1, animated, source);
109
+ const lower = this._mount(el, this.entries.length - 1, data, key, top.el);
110
+ this.entries.splice(this.entries.length - 1, 0, lower);
111
+ return this._popRevealing(this.entries.length - 1, animated, source);
112
+ });
113
+ }
114
+ /** Swaps the top page for `el` without animation. Returns the removed entry. */
115
+ replace(el, { data = null, key = null, source = 'api' } = {}) {
116
+ return this._run(async () => {
117
+ const old = this.top;
118
+ if (old && old.el === el)
119
+ return null;
120
+ this._forget(el);
121
+ const removed = old ? [this._unmount(this.entries.pop())] : [];
122
+ const entry = this._mount(el, this.entries.length, data, key);
123
+ this.entries.push(entry);
124
+ this._settle();
125
+ this._emit('replace', { entry, removed, entries: this.entries.slice(), source });
126
+ return removed[0] || null;
127
+ });
128
+ }
129
+ /**
130
+ * Convenience for ports that already resolved the direction: `push`, `pop`
131
+ * (via `popWith`) or `replace`.
132
+ */
133
+ present(el, direction, opts = {}) {
134
+ if (direction === 'pop')
135
+ return this.popWith(el, opts);
136
+ if (direction === 'replace')
137
+ return this.replace(el, opts);
138
+ return this.push(el, opts);
139
+ }
140
+ /** Removes a mounted page without animation, wherever it sits. Returns its entry, or null. */
141
+ remove(el, { source = 'api' } = {}) {
142
+ return this._run(async () => {
143
+ const entry = this._forget(el);
144
+ if (!entry)
145
+ return null;
146
+ this._settle();
147
+ this._emit('pop', { entry, removed: [entry], entries: this.entries.slice(), source });
148
+ return entry;
149
+ });
150
+ }
151
+ /** Replaces the whole stack without animation. Returns the removed entries. */
152
+ reset(elements, { source = 'api' } = {}) {
153
+ return this._run(async () => {
154
+ const removed = [];
155
+ while (this.entries.length)
156
+ removed.push(this._unmount(this.entries.pop()));
157
+ elements.forEach((el, i) => this.entries.push(this._mount(el, i, null, null)));
158
+ this._settle();
159
+ this._emit('reset', { entries: this.entries.slice(), removed, source });
160
+ return removed;
161
+ });
162
+ }
163
+ /** Starts a pointer-driven pop. Returns null if the stack cannot pop right now. */
164
+ beginInteractivePop() {
165
+ if (!this.canPop())
166
+ return null;
167
+ this._setBusy(true);
168
+ const upper = this.top;
169
+ const lower = this.entries[this.entries.length - 2];
170
+ let p = 1;
171
+ this._begin(lower, upper, 'interactive');
172
+ return {
173
+ update: (v) => {
174
+ p = Math.min(1, Math.max(0, v));
175
+ this._apply(lower, upper, p);
176
+ },
177
+ finish: async ({ complete, velocity = 0 }) => {
178
+ const remainingPx = (complete ? p : 1 - p) * this.width();
179
+ const { duration, ease } = this.transition.settle({ remainingPx, velocity });
180
+ await tween({ from: p, to: complete ? 0 : 1, duration, ease, onUpdate: (v) => this._apply(lower, upper, v) });
181
+ this._end(lower, upper, 'interactive');
182
+ if (complete) {
183
+ this.entries.pop();
184
+ this._unmount(upper);
185
+ }
186
+ this._settle();
187
+ this._setBusy(false);
188
+ if (complete)
189
+ this._emit('pop', { entry: upper, removed: [upper], entries: this.entries.slice(), source: 'gesture' });
190
+ this._drain();
191
+ },
192
+ };
193
+ }
194
+ destroy() {
195
+ while (this.entries.length)
196
+ this._unmount(this.entries.pop());
197
+ this.container.classList.remove('sn-container');
198
+ }
199
+ // -------------------------------------------------------------- internals
200
+ _setBusy(v) {
201
+ this.busy = v;
202
+ this.container.classList.toggle('sn-busy', v);
203
+ }
204
+ /** Serializes operations: while a transition runs, later calls wait their turn. */
205
+ _run(fn) {
206
+ return new Promise((resolve, reject) => {
207
+ const task = async () => {
208
+ this._setBusy(true);
209
+ try {
210
+ resolve(await fn());
211
+ }
212
+ catch (e) {
213
+ reject(e);
214
+ }
215
+ finally {
216
+ this._setBusy(false);
217
+ this._drain();
218
+ }
219
+ };
220
+ if (this.busy)
221
+ this._queue.push(task);
222
+ else
223
+ task();
224
+ });
225
+ }
226
+ _drain() {
227
+ if (!this.busy && this._queue.length)
228
+ this._queue.shift()();
229
+ }
230
+ async _popRevealing(depth, animated, source) {
231
+ const upper = this.entries.pop(); // the visible page: it animates out
232
+ const removed = [];
233
+ while (this.entries.length > depth)
234
+ removed.push(this._unmount(this.entries.pop())); // intermediate pages: removed without animation
235
+ const lower = this.top;
236
+ await this._transition(lower, upper, 1, 0, animated, 'pop');
237
+ removed.push(this._unmount(upper));
238
+ this._settle();
239
+ this._emit('pop', { entry: upper, removed, entries: this.entries.slice(), source });
240
+ return upper;
241
+ }
242
+ _mount(el, index, data, key, before = null) {
243
+ el.classList.add(this.pageClass);
244
+ if (before)
245
+ this.container.insertBefore(el, before);
246
+ else if (el.parentElement !== this.container)
247
+ this.container.append(el);
248
+ return { el, index, key, data };
249
+ }
250
+ _unmount(entry) {
251
+ entry.el.classList.remove(this.pageClass, 'sn-page-visible');
252
+ entry.el.style.transform = '';
253
+ entry.el.remove();
254
+ return entry;
255
+ }
256
+ /** Drops `el` from the entries if it is mounted. Returns its old entry. */
257
+ _forget(el) {
258
+ const i = this.entries.findIndex((e) => e.el === el);
259
+ if (i < 0)
260
+ return null;
261
+ const [entry] = this.entries.splice(i, 1);
262
+ return this._unmount(entry);
263
+ }
264
+ _begin(lower, upper, kind) {
265
+ if (lower)
266
+ lower.el.classList.add('sn-page-visible');
267
+ upper.el.classList.add('sn-page-visible');
268
+ this.transition.begin?.(lower, upper);
269
+ this._emit('transitionstart', { lower, upper, kind });
270
+ }
271
+ _apply(lower, upper, p) {
272
+ this.transition.apply(lower, upper, p);
273
+ this._emit('progress', { lower, upper, p });
274
+ }
275
+ _end(lower, upper, kind) {
276
+ this.transition.end?.(lower, upper);
277
+ this._emit('transitionend', { lower, upper, kind });
278
+ }
279
+ async _transition(lower, upper, from, to, animated, kind) {
280
+ this._begin(lower, upper, kind);
281
+ this._apply(lower, upper, from);
282
+ const duration = animated ? this.transition.duration : 0;
283
+ await tween({ from, to, duration, ease: this.transition.ease, onUpdate: (p) => this._apply(lower, upper, p) });
284
+ this._end(lower, upper, kind);
285
+ }
286
+ /** Makes only the top page visible, resets every transform, renumbers the indexes. */
287
+ _settle() {
288
+ const top = this.top;
289
+ this.entries.forEach((e, i) => {
290
+ e.index = i;
291
+ e.el.classList.toggle('sn-page-visible', e === top);
292
+ e.el.style.transform = e === top ? 'translate3d(0,0,0)' : '';
293
+ });
294
+ }
295
+ }
296
+ //# sourceMappingURL=navigation-stack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation-stack.js","sourceRoot":"","sources":["../src/navigation-stack.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /* Tune the iOS transition by setting these on .sn-container or any ancestor:
2
+ --sn-duration: 500ms; push/pop length
3
+ --sn-easing: cubic-bezier(0.32, 0.72, 0, 1); its curve: a cubic-bezier, a keyword, or ios
4
+ --sn-parallax: 30%; how far the page beneath travels
5
+ --sn-dim-color: #000; overlay on the page beneath
6
+ --sn-dim-max: 10%; its opacity at full open
7
+ --sn-shadow: -3px 0 14px rgba(0,0,0,0.16); on the incoming page; none to remove it
8
+ --sn-settle-min: 120ms; bounds for finishing a swipe
9
+ --sn-settle-max: 400ms;
10
+ --sn-settle-easing: ios-settle; the curve a released swipe finishes on
11
+ --sn-settle-velocity-floor: 900; px/s assumed when the pointer was slower
12
+ --sn-time-scale: 1; multiplies every duration */
13
+ .sn-container {
14
+ position: relative;
15
+ overflow: hidden;
16
+ }
17
+ .sn-page {
18
+ position: absolute;
19
+ inset: 0;
20
+ overflow-y: auto;
21
+ overflow-x: hidden;
22
+ -webkit-overflow-scrolling: touch;
23
+ overscroll-behavior-y: contain;
24
+ touch-action: pan-y;
25
+ visibility: hidden;
26
+ will-change: transform;
27
+ }
28
+ .sn-page-visible {
29
+ visibility: visible;
30
+ }
31
+ .sn-busy .sn-page {
32
+ pointer-events: none;
33
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The only styles the engine needs. These cover layout, not appearance: the
3
+ * look lives in the transition and is tuned through the `--sn-*` custom
4
+ * properties (see `IOS_TRANSITION_CSS_VARS` and the README). Those properties
5
+ * are documented but deliberately not declared, because leaving one unset is
6
+ * what makes its JS default apply.
7
+ *
8
+ * The string is kept minified because it ships inside every consumer's JS
9
+ * bundle (`injectStyles()` is the default path); `scripts/write-css.mjs`
10
+ * expands it into the readable `dist/stacknav.css`. What each rule is for:
11
+ *
12
+ * - `.sn-container`: the stack's scroll-clipping frame.
13
+ * - `.sn-page`: absolutely fills the container and is its own scroll container.
14
+ * `touch-action: pan-y` keeps vertical scrolling native while horizontal
15
+ * drags reach the gesture; `visibility: hidden` keeps pages beneath the top
16
+ * mounted (scroll position, form state) but out of sight and out of the
17
+ * accessibility tree.
18
+ * - `.sn-page-visible`: the top page, and both pages during a transition.
19
+ * - `.sn-busy .sn-page`: no clicks land on a page that is mid-transition.
20
+ */
21
+ export declare const STACKNAV_CSS: string;
22
+ export declare const STACKNAV_STYLE_ID = "stacknav-styles";
23
+ /**
24
+ * Inserts the engine's stylesheet into `doc` once. Framework ports call this so
25
+ * apps need no stylesheet import. Apps that ship `stacknav.css` themselves can
26
+ * skip it.
27
+ */
28
+ export declare function injectStyles(doc?: Document | null): void;
29
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,YAAY,QAIiB,CAAC;AAE3C,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAEnD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,GAAE,QAAQ,GAAG,IAAwD,GAAG,IAAI,CAM3G"}
package/dist/styles.js ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The only styles the engine needs. These cover layout, not appearance: the
3
+ * look lives in the transition and is tuned through the `--sn-*` custom
4
+ * properties (see `IOS_TRANSITION_CSS_VARS` and the README). Those properties
5
+ * are documented but deliberately not declared, because leaving one unset is
6
+ * what makes its JS default apply.
7
+ *
8
+ * The string is kept minified because it ships inside every consumer's JS
9
+ * bundle (`injectStyles()` is the default path); `scripts/write-css.mjs`
10
+ * expands it into the readable `dist/stacknav.css`. What each rule is for:
11
+ *
12
+ * - `.sn-container`: the stack's scroll-clipping frame.
13
+ * - `.sn-page`: absolutely fills the container and is its own scroll container.
14
+ * `touch-action: pan-y` keeps vertical scrolling native while horizontal
15
+ * drags reach the gesture; `visibility: hidden` keeps pages beneath the top
16
+ * mounted (scroll position, form state) but out of sight and out of the
17
+ * accessibility tree.
18
+ * - `.sn-page-visible`: the top page, and both pages during a transition.
19
+ * - `.sn-busy .sn-page`: no clicks land on a page that is mid-transition.
20
+ */
21
+ export const STACKNAV_CSS = '.sn-container{position:relative;overflow:hidden}' +
22
+ '.sn-page{position:absolute;inset:0;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;overscroll-behavior-y:contain;touch-action:pan-y;visibility:hidden;will-change:transform}' +
23
+ '.sn-page-visible{visibility:visible}' +
24
+ '.sn-busy .sn-page{pointer-events:none}';
25
+ export const STACKNAV_STYLE_ID = 'stacknav-styles';
26
+ /**
27
+ * Inserts the engine's stylesheet into `doc` once. Framework ports call this so
28
+ * apps need no stylesheet import. Apps that ship `stacknav.css` themselves can
29
+ * skip it.
30
+ */
31
+ export function injectStyles(doc = typeof document === 'undefined' ? null : document) {
32
+ if (!doc || doc.getElementById(STACKNAV_STYLE_ID))
33
+ return;
34
+ const style = doc.createElement('style');
35
+ style.id = STACKNAV_STYLE_ID;
36
+ style.textContent = STACKNAV_CSS;
37
+ (doc.head ?? doc.documentElement).append(style);
38
+ }
39
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,YAAY,GACvB,kDAAkD;IAClD,iMAAiM;IACjM,sCAAsC;IACtC,wCAAwC,CAAC;AAE3C,MAAM,CAAC,MAAM,iBAAiB,GAAG,iBAAiB,CAAC;AAEnD;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,MAAuB,OAAO,QAAQ,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ;IACnG,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,iBAAiB,CAAC;QAAE,OAAO;IAC1D,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,GAAG,iBAAiB,CAAC;IAC7B,KAAK,CAAC,WAAW,GAAG,YAAY,CAAC;IACjC,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAClD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@stacknav/core",
3
+ "version": "0.2.0",
4
+ "description": "Framework-agnostic iOS-style push/pop navigation stack: transition, interactive swipe back, direction resolution, history mirroring.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "keywords": [
8
+ "navigation",
9
+ "transition",
10
+ "ios",
11
+ "swipe-back",
12
+ "stack",
13
+ "animation"
14
+ ],
15
+ "main": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "default": "./dist/index.js"
21
+ },
22
+ "./stacknav.css": "./dist/stacknav.css",
23
+ "./package.json": "./package.json"
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "src"
28
+ ],
29
+ "sideEffects": [
30
+ "*.css"
31
+ ],
32
+ "devDependencies": {
33
+ "@types/node": "^22.0.0",
34
+ "esbuild": "^0.28.0",
35
+ "typescript": "~6.0.0"
36
+ },
37
+ "scripts": {
38
+ "build": "tsc -p tsconfig.build.json && node scripts/write-css.mjs",
39
+ "test": "node --test \"test/**/*.test.ts\"",
40
+ "typecheck": "tsc -p tsconfig.json --noEmit",
41
+ "size": "node scripts/size.mjs"
42
+ }
43
+ }