@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.
- package/README.md +125 -60
- package/dist/animate.d.ts +20 -2
- package/dist/animate.d.ts.map +1 -1
- package/dist/animate.js +76 -13
- package/dist/animate.js.map +1 -1
- package/dist/css-vars.d.ts +3 -3
- package/dist/css-vars.d.ts.map +1 -1
- package/dist/css-vars.js +47 -4
- package/dist/css-vars.js.map +1 -1
- package/dist/history-adapter.d.ts +0 -1
- package/dist/history-adapter.d.ts.map +1 -1
- package/dist/history-adapter.js +1 -5
- package/dist/history-adapter.js.map +1 -1
- package/dist/index.d.ts +27 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -15
- package/dist/index.js.map +1 -1
- package/dist/native-transition.d.ts +93 -0
- package/dist/native-transition.d.ts.map +1 -0
- package/dist/{ios-transition.js → native-transition.js} +48 -29
- package/dist/native-transition.js.map +1 -0
- package/dist/navigation-stack.d.ts +3 -2
- package/dist/navigation-stack.d.ts.map +1 -1
- package/dist/navigation-stack.js +45 -10
- package/dist/navigation-stack.js.map +1 -1
- package/dist/platform.d.ts +6 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +22 -0
- package/dist/platform.js.map +1 -0
- package/dist/stacknav.css +10 -13
- package/dist/styles.d.ts +10 -11
- package/dist/styles.d.ts.map +1 -1
- package/dist/styles.js +14 -16
- package/dist/styles.js.map +1 -1
- package/dist/swipe-back.d.ts +4 -0
- package/dist/swipe-back.d.ts.map +1 -0
- package/dist/swipe-back.js +31 -0
- package/dist/swipe-back.js.map +1 -0
- package/package.json +3 -2
- package/src/animate.ts +74 -15
- package/src/css-vars.ts +43 -4
- package/src/history-adapter.ts +1 -5
- package/src/index.ts +49 -24
- package/src/{ios-transition.ts → native-transition.ts} +72 -39
- package/src/navigation-stack.ts +33 -10
- package/src/platform.ts +23 -0
- package/src/styles.ts +14 -16
- package/src/swipe-back.ts +35 -0
- package/dist/edge-pan-gesture.d.ts +0 -36
- package/dist/edge-pan-gesture.d.ts.map +0 -1
- package/dist/edge-pan-gesture.js +0 -155
- package/dist/edge-pan-gesture.js.map +0 -1
- package/dist/ios-transition.d.ts +0 -61
- package/dist/ios-transition.d.ts.map +0 -1
- package/dist/ios-transition.js.map +0 -1
- package/src/edge-pan-gesture.ts +0 -190
package/README.md
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
# @stacknav/core
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
with a
|
|
3
|
+
A native push/pop navigation transition for any web app: the iOS one, or
|
|
4
|
+
Android's own on an Android browser. Pages, headers and styling stay the
|
|
5
|
+
app's own; the engine only moves them.
|
|
6
|
+
|
|
7
|
+
- **Push / pop** the way the platform does it. On iOS: the UIKit curve, parallax
|
|
8
|
+
and dim on the page beneath, and a shadow on the leading edge of the incoming
|
|
9
|
+
page. On Android: the framework's activity transition, a short slide of both
|
|
10
|
+
pages with a fade, on its own interpolator. The platform is detected from the
|
|
11
|
+
browser and falls back to iOS; either look can be forced.
|
|
12
|
+
- **Interactive pop**: `beginInteractivePop()` sets the transition from a number
|
|
13
|
+
you supply, so a pointer of your own can drag the page and release into a
|
|
14
|
+
settle. The library ships no recognizer: in a browser tab the browser owns the
|
|
15
|
+
edge (see [Swipe-back policy](#swipe-back-policy)).
|
|
11
16
|
- **Scroll and state are preserved.** Pages beneath the top stay mounted and
|
|
12
17
|
hidden. Only `transform` is written, so scroll offsets, form state and focus
|
|
13
18
|
survive.
|
|
@@ -31,10 +36,10 @@ styling stay the app's own; the engine only moves them.
|
|
|
31
36
|
```html
|
|
32
37
|
<div id="app"></div>
|
|
33
38
|
<script type="module">
|
|
34
|
-
import {
|
|
39
|
+
import { createNativeStack, attachBrowserHistory, injectStyles } from '@stacknav/core';
|
|
35
40
|
|
|
36
41
|
injectStyles(); // or <link rel="stylesheet" href="@stacknav/core/stacknav.css">
|
|
37
|
-
const nav =
|
|
42
|
+
const nav = createNativeStack({ container: document.getElementById('app') });
|
|
38
43
|
await nav.push(homePage(), { animated: false });
|
|
39
44
|
attachBrowserHistory(nav);
|
|
40
45
|
|
|
@@ -62,11 +67,45 @@ So a 500 ms push costs about a dozen style writes in total rather than one per p
|
|
|
62
67
|
|
|
63
68
|
## API
|
|
64
69
|
|
|
65
|
-
###
|
|
70
|
+
### Swipe-back policy
|
|
66
71
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
72
|
+
```js
|
|
73
|
+
const stack = createNativeStack({ container, swipeBack: 'browser' });
|
|
74
|
+
stack.setSwipeBack('disabled'); // request browser swipe suppression
|
|
75
|
+
stack.setSwipeBack('browser'); // release our suppression request (the default)
|
|
76
|
+
console.log(stack.swipeBack);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`SwipeBackMode` is `'browser' | 'disabled'` and is exported as a type. Mode changes
|
|
80
|
+
preserve the stack and history. Destroying the stack releases its policy.
|
|
81
|
+
Destruction is terminal: queued and subsequent navigation promises reject with
|
|
82
|
+
`AbortError`, so callers should handle cancellation when tearing down a stack.
|
|
83
|
+
|
|
84
|
+
**There is no gesture recognizer here, on purpose.** Browser suppression uses
|
|
85
|
+
`overscroll-behavior-x: contain` on the document root, which Safari ignores for
|
|
86
|
+
its edge swipe ([WebKit #240183](https://bugs.webkit.org/show_bug.cgi?id=240183)).
|
|
87
|
+
In a browser tab the browser keeps the edge whatever we ask for, so a recognizer
|
|
88
|
+
of ours next to it reads as two backs at once. An app that owns the edge -- an
|
|
89
|
+
installed PWA, a native webview -- can drive [`beginInteractivePop()`](#navigationstack)
|
|
90
|
+
from its own pointer handling instead, which is the same API the recognizer used.
|
|
91
|
+
|
|
92
|
+
**Migration:** `swipeBack: 'custom'`, the `gesture` option and `createEdgePanGesture()`
|
|
93
|
+
were removed. `'custom'` now throws; `'disabled'` keeps the suppression half of it.
|
|
94
|
+
|
|
95
|
+
Suppression is **best effort and document-wide**. OS gestures may still navigate.
|
|
96
|
+
Back/Forward buttons and keyboard navigation continue to work. Multiple stacks
|
|
97
|
+
share suppression; `browser` releases only that stack's request. The original
|
|
98
|
+
inline value and priority are restored when the last request ends, unless the
|
|
99
|
+
application has replaced our declaration in the meantime.
|
|
100
|
+
See [CSS overscroll behavior](https://drafts.csswg.org/css-overscroll/).
|
|
101
|
+
Page retention and transition effects work in every mode.
|
|
102
|
+
|
|
103
|
+
### `createNativeStack({ container, transition?, swipeBack? })`
|
|
104
|
+
|
|
105
|
+
Builds a `NavigationStack` with the platform's native transition. Set
|
|
106
|
+
`swipeBack: 'disabled'` to request browser swipe suppression; the default is
|
|
107
|
+
`browser`. `transition` is the option object for the factory below.
|
|
108
|
+
`stack.destroy()` releases the policy.
|
|
70
109
|
|
|
71
110
|
### `NavigationStack`
|
|
72
111
|
|
|
@@ -80,7 +119,7 @@ below. The gesture is exposed as `stack.gesture`; `stack.destroy()` detaches it.
|
|
|
80
119
|
| `present(el, direction, opts)` | `push`, `pop` (via `popWith`) or `replace`, for callers that already resolved the direction. |
|
|
81
120
|
| `remove(el)` | Drops a page wherever it sits, no animation. |
|
|
82
121
|
| `reset(elements)` | Replaces the whole stack, no animation. |
|
|
83
|
-
| `beginInteractivePop()` | Returns `{ update(p), finish({ complete, velocity }) }` or `null`.
|
|
122
|
+
| `beginInteractivePop()` | Returns `{ update(p), finish({ complete, velocity }) }` or `null`. Drive it from your own pointer handling: `update(p)` while the finger is down, `finish({ complete, velocity })` on release. Pops it emits carry `source: 'gesture'`. |
|
|
84
123
|
| `depth`, `top`, `entries`, `busy`, `canPop()`, `entryOf(el \| key)` | State. |
|
|
85
124
|
| `on(event, fn)` | Events: `push`, `pop`, `replace`, `reset`, `transitionstart`, `progress`, `transitionend`. Returns an unsubscribe function. |
|
|
86
125
|
| `destroy()` | Unmounts everything. |
|
|
@@ -114,22 +153,44 @@ A strategy is
|
|
|
114
153
|
numbers or unrelated siblings mean (default `replace`). `segmentsOf(url)` splits
|
|
115
154
|
a path into `segments`.
|
|
116
155
|
|
|
117
|
-
### `
|
|
118
|
-
|
|
119
|
-
| Option | CSS variable |
|
|
120
|
-
| --- | --- | --- | --- |
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
156
|
+
### `createNativeTransition(options)`
|
|
157
|
+
|
|
158
|
+
| Option | CSS variable | iOS | Android | Description |
|
|
159
|
+
| --- | --- | --- | --- | --- |
|
|
160
|
+
| `platform` | | `'auto'` | | `'ios'`, `'android'`, or `'auto'` to ask the browser (see below) |
|
|
161
|
+
| `duration` | `--sn-duration` | `500` | `450` | ms for a programmatic push/pop |
|
|
162
|
+
| `ease` | `--sn-easing` | `cubic-bezier(0.32, 0.72, 0, 1)` | `fast_out_extra_slow_in` as `linear()` | the curve push/pop runs on |
|
|
163
|
+
| `travel` | `--sn-travel` | `1` | `0.25` | fraction of the width the upper page travels |
|
|
164
|
+
| `parallax` | `--sn-parallax` | `0.3` | `0.25` | fraction of the width the lower page travels |
|
|
165
|
+
| `fade` | `--sn-fade` | `1` | `0` | opacity of the upper page when closed (`1` = no fade) |
|
|
166
|
+
| `dimColor`, `dimMax` | `--sn-dim-color`, `--sn-dim-max` | `"#000"`, `0.1` | `"#000"`, `0` | overlay on the lower page at full open (`0.35` suits dark UIs) |
|
|
167
|
+
| `shadow` | `--sn-shadow` | `-3px 0 14px rgba(0,0,0,0.16)` | `none` | box-shadow on the incoming page |
|
|
168
|
+
| `settleMin`, `settleMax` | `--sn-settle-min`, `--sn-settle-max` | `120`, `400` | same | ms bounds when finishing an interactive pop |
|
|
169
|
+
| `settleEase` | `--sn-settle-easing` | `cubic-bezier(0.2, 0.8, 0.2, 1)` | `cubic-bezier(0, 0, 0, 1)` | the curve a released pop finishes on |
|
|
170
|
+
| `settleVelocityFloor` | `--sn-settle-velocity-floor` | `900` | same | px/s assumed when the pointer was slower |
|
|
171
|
+
| `timeScale` | `--sn-time-scale` | `1` | same | multiplies every duration (slow motion, tests) |
|
|
130
172
|
|
|
131
173
|
`prefers-reduced-motion` sets every duration to 0.
|
|
132
174
|
|
|
175
|
+
#### Platforms
|
|
176
|
+
|
|
177
|
+
The two columns are the two presets, `nativeTransitionPreset('ios' | 'android')`.
|
|
178
|
+
They are what each system animates: UIKit's navigation push, and the Android
|
|
179
|
+
framework's activity open/close (`activity_open_enter.xml` and friends since
|
|
180
|
+
Android 13: both pages slide 96 dp, about a quarter of a phone, over 450 ms on
|
|
181
|
+
`fast_out_extra_slow_in`, while the incoming page fades in). The Android curve
|
|
182
|
+
is a path of two cubics, so it reaches CSS as `linear()`; a browser without
|
|
183
|
+
`linear()` (Chrome < 113, Safari < 17.2, Firefox < 112) runs `ease` instead.
|
|
184
|
+
|
|
185
|
+
`platform: 'auto'`, the default, calls `detectPlatform()`: `android` when the
|
|
186
|
+
browser says it is one (client hints first, then the user agent), otherwise
|
|
187
|
+
`ios`, which is also what a desktop browser gets. `isIOSBrowser()` and
|
|
188
|
+
`isAndroidBrowser()` are exported too. The choice is made once, when the
|
|
189
|
+
transition is created; an option you pass wins over the preset, and a CSS
|
|
190
|
+
variable wins over both, so `{ platform: 'android', duration: 300 }` is the
|
|
191
|
+
Android look at your speed. `transition.options.platform` and
|
|
192
|
+
`transition.resolved.platform` tell you which one is in force.
|
|
193
|
+
|
|
133
194
|
#### Tuning from CSS
|
|
134
195
|
|
|
135
196
|
Every option is also a custom property, read off the container when a transition
|
|
@@ -138,7 +199,7 @@ the container, under a theme class, or inside a media query.
|
|
|
138
199
|
|
|
139
200
|
```css
|
|
140
201
|
:root {
|
|
141
|
-
--sn-duration: 340ms; /* snappier than
|
|
202
|
+
--sn-duration: 340ms; /* snappier than either platform */
|
|
142
203
|
--sn-easing: cubic-bezier(0.4, 0, 0.2, 1);
|
|
143
204
|
}
|
|
144
205
|
.theme-flat {
|
|
@@ -154,8 +215,9 @@ the container, under a theme class, or inside a media query.
|
|
|
154
215
|
Durations accept `ms`, `s` or a bare number of milliseconds. Fractions accept
|
|
155
216
|
`0.3` or `30%`. Easings accept `linear`, `ease`, `ease-in`, `ease-out`,
|
|
156
217
|
`ease-in-out`, `cubic-bezier(…)` with x coordinates within `[0, 1]` as CSS
|
|
157
|
-
requires, or `ios
|
|
158
|
-
|
|
218
|
+
requires, `linear(…)` with percentage stops, or `ios`, `ios-settle`, `android`
|
|
219
|
+
and `android-settle` for the presets' curves. The `steps()` timing function is
|
|
220
|
+
not supported.
|
|
159
221
|
|
|
160
222
|
Precedence:
|
|
161
223
|
|
|
@@ -173,7 +235,7 @@ Precedence:
|
|
|
173
235
|
Values are re-read at the start of every transition, which covers media queries
|
|
174
236
|
and class changes. `transition.refresh()` re-reads them on demand, for example
|
|
175
237
|
after changing `transition.options` mid-animation. `transition.resolved` is what
|
|
176
|
-
is currently in force, and `
|
|
238
|
+
is currently in force, and `NATIVE_TRANSITION_CSS_VARS` maps each option to its
|
|
177
239
|
variable name.
|
|
178
240
|
|
|
179
241
|
A transition is just
|
|
@@ -183,7 +245,7 @@ can write a different one (a fade, a vertical sheet) and pass it to
|
|
|
183
245
|
write, not a frame: the stack calls it once at each end of a phase and lets CSS
|
|
184
246
|
interpolate between them, so keep it to `transform` and `opacity` and the
|
|
185
247
|
browser keeps it off the main thread. `ease` is sampled to report `progress`,
|
|
186
|
-
but its `css` property is what drives the pixels — `cubicBezier()` and
|
|
248
|
+
but its `css` property is what drives the pixels — `cubicBezier()`, `linearEasing()` and
|
|
187
249
|
`parseEasing()` set one, and a bare `(t) => number` of your own does not, so
|
|
188
250
|
such a curve runs `linear` on screen unless you give it a `css` property too. `cssVars()` and the
|
|
189
251
|
`parseTime` / `parseNumber` / `parseRatio` / `parseEasing` helpers are exported
|
|
@@ -191,26 +253,6 @@ so a custom transition can read variables the same way. Each returns `undefined`
|
|
|
191
253
|
rather than `NaN` for anything it cannot parse, so `?? yourDefault` is all the
|
|
192
254
|
handling a value needs.
|
|
193
255
|
|
|
194
|
-
### `createEdgePanGesture(options)`
|
|
195
|
-
|
|
196
|
-
| Option | Default | Description |
|
|
197
|
-
| --- | --- | --- |
|
|
198
|
-
| `edgeWidth` | `28` | px strip on the leading edge that starts the gesture |
|
|
199
|
-
| `anywhere` | `false` | recognize the drag from anywhere on the page |
|
|
200
|
-
| `startSlop` | `6` | px of horizontal travel before the drag begins |
|
|
201
|
-
| `verticalCancelSlop` | `10` | px of vertical travel that hands the touch to scrolling |
|
|
202
|
-
| `completeThreshold` | `0.5` | fraction of the width that completes on a slow release |
|
|
203
|
-
| `completeVelocity` | `500` | px/s toward the trailing edge that completes regardless |
|
|
204
|
-
| `cancelVelocity` | `-500` | px/s back toward the leading edge that cancels regardless |
|
|
205
|
-
|
|
206
|
-
Call `gesture.refresh()` after changing options at runtime.
|
|
207
|
-
|
|
208
|
-
The leading edge is whichever edge the container reads from, so in a
|
|
209
|
-
right-to-left container the strip sits on the right and back is a drag to the
|
|
210
|
-
left. The recognizer and the transition both take that from `--sn-dir`, which
|
|
211
|
-
the stylesheet sets under `:dir(rtl)`, falling back to the container's computed
|
|
212
|
-
`direction`; set `--sn-dir: -1` yourself to flip both without an RTL document.
|
|
213
|
-
|
|
214
256
|
### `attachBrowserHistory(stack, { key = "snDepth", animateHistoryPop, onForward })`
|
|
215
257
|
|
|
216
258
|
For apps without a router. Mirrors stack depth into `history.state`. Returns a
|
|
@@ -242,23 +284,29 @@ on the container, `--sn-t` and `--sn-e`, and marks the two pages taking part
|
|
|
242
284
|
`sn-page-upper` and `sn-page-lower`. Anything of yours that should move with
|
|
243
285
|
them can transition off the same four.
|
|
244
286
|
|
|
287
|
+
Reading direction is a CSS question, not a JS one: the stylesheet sets
|
|
288
|
+
`--sn-dir: -1` on a right-to-left container and the transform the engine writes
|
|
289
|
+
is signed by it, so back is a drag to the left there. Pointer handling of your
|
|
290
|
+
own should read the same variable, so it and the transition cannot disagree
|
|
291
|
+
about which edge is the back edge.
|
|
292
|
+
|
|
245
293
|
## Footprint
|
|
246
294
|
|
|
247
295
|
Plain ES modules, no dependencies, no work at module load: a bundler keeps only what you import, whether or not it honours the package's `sideEffects` flag (the tests bundle each entry point with that flag switched off and check what survives). Minified and gzipped, as measured by `pnpm size`:
|
|
248
296
|
|
|
249
297
|
| You import | Costs |
|
|
250
298
|
| --- | --- |
|
|
251
|
-
| `
|
|
252
|
-
| `NavigationStack` with your own transition | ~2.
|
|
299
|
+
| `createNativeStack` (stack, native look, swipe policy) | ~5.1 kB |
|
|
300
|
+
| `NavigationStack` with your own transition | ~2.2 kB |
|
|
253
301
|
| the direction strategies | ~0.6 kB |
|
|
254
302
|
| `attachBrowserHistory` | ~0.5 kB |
|
|
255
303
|
| `injectStyles` | ~0.5 kB |
|
|
256
|
-
| everything | ~5
|
|
304
|
+
| everything | ~6.5 kB |
|
|
257
305
|
|
|
258
306
|
## Develop
|
|
259
307
|
|
|
260
308
|
```sh
|
|
261
|
-
pnpm test # node:test with a
|
|
309
|
+
pnpm test # node:test with a small DOM stub, no browser; includes the tree-shaking checks
|
|
262
310
|
pnpm build # tsc → dist/, plus dist/stacknav.css
|
|
263
311
|
pnpm size # what each entry point costs, minified + gzipped (after a build)
|
|
264
312
|
```
|
|
@@ -268,10 +316,27 @@ src/
|
|
|
268
316
|
animate.ts curves CSS and JS can both read, the tween, the waits
|
|
269
317
|
css-vars.ts reading and parsing the engine's custom properties
|
|
270
318
|
navigation-stack.ts the stack: mounting, ordering, transition lifecycle, queueing
|
|
271
|
-
|
|
272
|
-
|
|
319
|
+
native-transition.ts the look: the presets, the endpoints, the settle timing, the variables
|
|
320
|
+
platform.ts which platform the browser is, for the default preset
|
|
273
321
|
direction.ts push / pop / replace strategies and the resolver
|
|
274
322
|
history-adapter.ts history.state mirroring for apps without a router
|
|
275
323
|
styles.ts the CSS the engine needs, motion included, and injectStyles()
|
|
276
|
-
index.ts exports +
|
|
324
|
+
index.ts exports + createNativeStack()
|
|
277
325
|
```
|
|
326
|
+
|
|
327
|
+
### Animation implementation notes
|
|
328
|
+
|
|
329
|
+
Timed transforms and opacity run as CSS transitions. Pointer handling, velocity
|
|
330
|
+
sampling, distance-dependent settle timing, and completion promises remain in
|
|
331
|
+
JavaScript. A `progress` subscriber also needs a JavaScript frame loop; omit
|
|
332
|
+
that subscription when CSS can drive your page chrome.
|
|
333
|
+
|
|
334
|
+
The browser resolves `--sn-dim-color` and `--sn-shadow` directly, so theme changes
|
|
335
|
+
to these variables take effect during a transition without `refresh()`. The JS
|
|
336
|
+
options remain their fallbacks. `resolved` remains a snapshot taken by
|
|
337
|
+
`begin()` or `refresh()`; numeric options still use that snapshot, including
|
|
338
|
+
support for percentage ratios and bare millisecond values.
|
|
339
|
+
|
|
340
|
+
The iOS look writes only `transform`; the Android look writes `opacity` on the
|
|
341
|
+
upper page as well, and clears it when the transition ends, so a page keeps
|
|
342
|
+
whatever opacity of its own it had.
|
package/dist/animate.d.ts
CHANGED
|
@@ -9,10 +9,19 @@ export interface Easing {
|
|
|
9
9
|
readonly css?: string;
|
|
10
10
|
}
|
|
11
11
|
export declare function cubicBezier(x1: number, y1: number, x2: number, y2: number): Easing;
|
|
12
|
+
/**
|
|
13
|
+
* Straight lines through `points` (`[x, y]` pairs, x from 0 to 1 and never
|
|
14
|
+
* decreasing), the curve CSS `linear()` draws. Spelled for CSS the same way
|
|
15
|
+
* unless `css` says otherwise. A browser without `linear()` (Chrome < 113,
|
|
16
|
+
* Safari < 17.2, Firefox < 112) runs its default `ease` instead.
|
|
17
|
+
*/
|
|
18
|
+
export declare function linearEasing(points: ReadonlyArray<readonly [number, number]>, css?: string): Easing;
|
|
12
19
|
export declare const easings: {
|
|
13
20
|
linear: Easing;
|
|
14
21
|
ios: Easing;
|
|
15
22
|
easeOut: Easing;
|
|
23
|
+
android: Easing;
|
|
24
|
+
androidSettle: Easing;
|
|
16
25
|
};
|
|
17
26
|
/** How an easing should be spelled for CSS. A curve with no spelling runs linearly. */
|
|
18
27
|
export declare const cssEasing: (ease: Easing | undefined) => string;
|
|
@@ -31,8 +40,8 @@ export type CancellableTween = Promise<void> & {
|
|
|
31
40
|
/**
|
|
32
41
|
* Animates a number from `from` to `to` over `duration` ms, calling `onUpdate`
|
|
33
42
|
* every frame. Returns a promise that resolves when the tween is done;
|
|
34
|
-
* `promise.cancel()` stops it early. A duration of
|
|
35
|
-
* to `to`.
|
|
43
|
+
* `promise.cancel()` stops it early and resolves the promise. A duration of
|
|
44
|
+
* 0 or less jumps straight to `to`.
|
|
36
45
|
*
|
|
37
46
|
* The engine does not use this to move pages, CSS does that. It uses it to
|
|
38
47
|
* report `progress` to listeners, and only while someone is listening.
|
|
@@ -55,5 +64,14 @@ export declare function commitStyles(el: HTMLElement): void;
|
|
|
55
64
|
* animation running on a page without stalling the stack.
|
|
56
65
|
*/
|
|
57
66
|
export declare function animationsFinished(els: Array<HTMLElement | null | undefined>, properties?: readonly string[]): Promise<void>;
|
|
67
|
+
/** `matchMedia`, and false where there is none: a server, a test. */
|
|
68
|
+
export declare const matchesMedia: (query: string) => boolean;
|
|
58
69
|
export declare const prefersReducedMotion: () => boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Whether the primary pointer is coarse: a phone or a tablet, not a mouse. The
|
|
72
|
+
* test to reach for when a behaviour is meant for handhelds only. It is a media
|
|
73
|
+
* query, so it answers again after the device changes — a tablet docked to a
|
|
74
|
+
* trackpad stops being touch-primary.
|
|
75
|
+
*/
|
|
76
|
+
export declare const isTouchPrimary: () => boolean;
|
|
59
77
|
//# sourceMappingURL=animate.d.ts.map
|
package/dist/animate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"animate.d.ts","sourceRoot":"","sources":["../src/animate.ts"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,MAAM,WAAW,MAAM;IACrB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"animate.d.ts","sourceRoot":"","sources":["../src/animate.ts"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,MAAM,WAAW,MAAM;IACrB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CA8BlF;AAKD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CASnG;AAED,eAAO,MAAM,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAiB3G,CAAC;AAEF,uFAAuF;AACvF,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,GAAG,SAAS,KAAG,MAA+B,CAAC;AAErF,gDAAgD;AAChD,eAAO,MAAM,WAAW,GAAI,IAAI,MAAM,KAAG,MAAqC,CAAC;AAE/E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG;IAAE,MAAM,IAAI,IAAI,CAAA;CAAE,CAAC;AAElE;;;;;;;;GAQG;AAGH,wBAAgB,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAe,EAAE,QAAQ,EAAE,EAAE,YAAY,GAAG,gBAAgB,CA+BvG;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,WAAW,GAAG,IAAI,CAElD;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,KAAK,CAAC,WAAW,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,UAAU,GAAE,SAAS,MAAM,EAA6B,GAAG,OAAO,CAAC,IAAI,CAAC,CAUtJ;AAED,qEAAqE;AACrE,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,OACkB,CAAC;AAEhE,eAAO,MAAM,oBAAoB,QAAO,OAA2D,CAAC;AAEpG;;;;;GAKG;AACH,eAAO,MAAM,cAAc,QAAO,OAA4C,CAAC"}
|
package/dist/animate.js
CHANGED
|
@@ -4,34 +4,81 @@
|
|
|
4
4
|
// a cancellable tween used only to report progress, and the two helpers that
|
|
5
5
|
// hand a run over to the browser.
|
|
6
6
|
export function cubicBezier(x1, y1, x2, y2) {
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
7
|
+
// The coefficients are constant for the lifetime of a curve.
|
|
8
|
+
const ax = 1 - 3 * x2 + 3 * x1, bx = 3 * x2 - 6 * x1, cx = 3 * x1;
|
|
9
|
+
const ay = 1 - 3 * y2 + 3 * y1, by = 3 * y2 - 6 * y1, cy = 3 * y1;
|
|
10
|
+
const sampleX = (t) => ((ax * t + bx) * t + cx) * t;
|
|
11
|
+
const sampleY = (t) => ((ay * t + by) * t + cy) * t;
|
|
12
12
|
const f = (x) => {
|
|
13
13
|
if (x <= 0)
|
|
14
14
|
return 0;
|
|
15
15
|
if (x >= 1)
|
|
16
16
|
return 1;
|
|
17
17
|
let t = x;
|
|
18
|
+
// Newton converges quickly for ordinary curves. Flat slopes can send it
|
|
19
|
+
// outside [0, 1], so fall back to a bounded search when it fails.
|
|
18
20
|
for (let i = 0; i < 8; i++) {
|
|
19
|
-
const
|
|
20
|
-
if (
|
|
21
|
+
const error = sampleX(t) - x;
|
|
22
|
+
if (Math.abs(error) < 1e-8)
|
|
23
|
+
return sampleY(t);
|
|
24
|
+
const slope = (3 * ax * t + 2 * bx) * t + cx;
|
|
25
|
+
if (Math.abs(slope) < 1e-8)
|
|
21
26
|
break;
|
|
22
|
-
|
|
27
|
+
const next = t - error / slope;
|
|
28
|
+
if (next < 0 || next > 1)
|
|
29
|
+
break;
|
|
30
|
+
t = next;
|
|
31
|
+
}
|
|
32
|
+
let lo = 0, hi = 1;
|
|
33
|
+
for (let i = 0; i < 30; i++) {
|
|
34
|
+
t = (lo + hi) / 2;
|
|
35
|
+
if (sampleX(t) < x)
|
|
36
|
+
lo = t;
|
|
37
|
+
else
|
|
38
|
+
hi = t;
|
|
23
39
|
}
|
|
24
|
-
return
|
|
40
|
+
return sampleY(t);
|
|
25
41
|
};
|
|
26
42
|
return Object.assign(f, { css: `cubic-bezier(${x1}, ${y1}, ${x2}, ${y2})` });
|
|
27
43
|
}
|
|
28
44
|
// `#__PURE__` marks the module-load calls as droppable, so a bundler that does
|
|
29
45
|
// not honour the package's `sideEffects` flag can still leave this module out
|
|
30
46
|
// when nothing here is imported.
|
|
47
|
+
/**
|
|
48
|
+
* Straight lines through `points` (`[x, y]` pairs, x from 0 to 1 and never
|
|
49
|
+
* decreasing), the curve CSS `linear()` draws. Spelled for CSS the same way
|
|
50
|
+
* unless `css` says otherwise. A browser without `linear()` (Chrome < 113,
|
|
51
|
+
* Safari < 17.2, Firefox < 112) runs its default `ease` instead.
|
|
52
|
+
*/
|
|
53
|
+
export function linearEasing(points, css) {
|
|
54
|
+
const f = (t) => {
|
|
55
|
+
if (t <= points[0][0])
|
|
56
|
+
return points[0][1];
|
|
57
|
+
let i = 1;
|
|
58
|
+
while (i < points.length - 1 && points[i][0] < t)
|
|
59
|
+
i++;
|
|
60
|
+
const [x0, y0] = points[i - 1], [x1, y1] = points[i];
|
|
61
|
+
return x1 > x0 ? y0 + (y1 - y0) * Math.min(1, (t - x0) / (x1 - x0)) : y1;
|
|
62
|
+
};
|
|
63
|
+
return Object.assign(f, { css: css ?? `linear(${points.map(([x, y]) => `${y} ${x * 100}%`).join(', ')})` });
|
|
64
|
+
}
|
|
31
65
|
export const easings = {
|
|
32
66
|
linear: /*#__PURE__*/ Object.assign((t) => t, { css: 'linear' }),
|
|
33
67
|
ios: /*#__PURE__*/ cubicBezier(0.32, 0.72, 0, 1), // the common approximation of UIKit's navigation curve
|
|
34
68
|
easeOut: /*#__PURE__*/ cubicBezier(0.2, 0.8, 0.2, 1),
|
|
69
|
+
// AOSP's `fast_out_extra_slow_in`, the interpolator behind activity open and
|
|
70
|
+
// close since Android 13 (frameworks/base, core/res/res/interpolator). It is
|
|
71
|
+
// a path of two cubics, `M0,0 C0.05,0 0.133,0.06 0.167,0.4 C0.208,0.82 0.25,1
|
|
72
|
+
// 1,1`, which one `cubic-bezier()` cannot bend into; these stops follow it
|
|
73
|
+
// to within 0.006 and are what CSS gets.
|
|
74
|
+
android: /*#__PURE__*/ linearEasing([
|
|
75
|
+
[0, 0], [0.03125, 0.008], [0.0625, 0.033], [0.09375, 0.08], [0.125, 0.162], [0.140625, 0.225], [0.15625, 0.313],
|
|
76
|
+
[0.1640625, 0.375], [0.171875, 0.451], [0.1796875, 0.517], [0.1875, 0.571], [0.203125, 0.649], [0.21875, 0.702],
|
|
77
|
+
[0.25, 0.773], [0.28125, 0.819], [0.3125, 0.853], [0.375, 0.899], [0.5, 0.951], [0.625, 0.977], [0.75, 0.991], [1, 1],
|
|
78
|
+
]),
|
|
79
|
+
// The "standard decelerate" Android's predictive-back guidelines ask for
|
|
80
|
+
// when a released gesture runs out.
|
|
81
|
+
androidSettle: /*#__PURE__*/ cubicBezier(0, 0, 0, 1),
|
|
35
82
|
};
|
|
36
83
|
/** How an easing should be spelled for CSS. A curve with no spelling runs linearly. */
|
|
37
84
|
export const cssEasing = (ease) => ease?.css ?? 'linear';
|
|
@@ -40,16 +87,20 @@ export const cssDuration = (ms) => (ms > 0 ? `${ms}ms` : '0s');
|
|
|
40
87
|
/**
|
|
41
88
|
* Animates a number from `from` to `to` over `duration` ms, calling `onUpdate`
|
|
42
89
|
* every frame. Returns a promise that resolves when the tween is done;
|
|
43
|
-
* `promise.cancel()` stops it early. A duration of
|
|
44
|
-
* to `to`.
|
|
90
|
+
* `promise.cancel()` stops it early and resolves the promise. A duration of
|
|
91
|
+
* 0 or less jumps straight to `to`.
|
|
45
92
|
*
|
|
46
93
|
* The engine does not use this to move pages, CSS does that. It uses it to
|
|
47
94
|
* report `progress` to listeners, and only while someone is listening.
|
|
48
95
|
*/
|
|
49
|
-
|
|
96
|
+
// The default is spelled out rather than taken from `easings`, so a bundle that
|
|
97
|
+
// only needs the tween does not carry the curves.
|
|
98
|
+
export function tween({ from, to, duration, ease = (t) => t, onUpdate }) {
|
|
50
99
|
let raf = 0;
|
|
51
100
|
let done = false;
|
|
101
|
+
let finish;
|
|
52
102
|
const promise = new Promise((resolve) => {
|
|
103
|
+
finish = resolve;
|
|
53
104
|
if (duration <= 0) {
|
|
54
105
|
onUpdate(to);
|
|
55
106
|
done = true;
|
|
@@ -61,6 +112,8 @@ export function tween({ from, to, duration, ease = easings.linear, onUpdate }) {
|
|
|
61
112
|
return;
|
|
62
113
|
const k = Math.min(1, (now - t0) / duration);
|
|
63
114
|
onUpdate(from + (to - from) * ease(k));
|
|
115
|
+
if (done)
|
|
116
|
+
return;
|
|
64
117
|
if (k < 1)
|
|
65
118
|
raf = requestAnimationFrame(step);
|
|
66
119
|
else {
|
|
@@ -73,6 +126,7 @@ export function tween({ from, to, duration, ease = easings.linear, onUpdate }) {
|
|
|
73
126
|
promise.cancel = () => {
|
|
74
127
|
done = true;
|
|
75
128
|
cancelAnimationFrame(raf);
|
|
129
|
+
finish();
|
|
76
130
|
};
|
|
77
131
|
return promise;
|
|
78
132
|
}
|
|
@@ -107,5 +161,14 @@ export function animationsFinished(els, properties = ['transform', 'opacity']) {
|
|
|
107
161
|
}
|
|
108
162
|
return running.length ? Promise.all(running).then(() => { }) : Promise.resolve();
|
|
109
163
|
}
|
|
110
|
-
|
|
164
|
+
/** `matchMedia`, and false where there is none: a server, a test. */
|
|
165
|
+
export const matchesMedia = (query) => typeof matchMedia === 'function' && matchMedia(query).matches;
|
|
166
|
+
export const prefersReducedMotion = () => matchesMedia('(prefers-reduced-motion: reduce)');
|
|
167
|
+
/**
|
|
168
|
+
* Whether the primary pointer is coarse: a phone or a tablet, not a mouse. The
|
|
169
|
+
* test to reach for when a behaviour is meant for handhelds only. It is a media
|
|
170
|
+
* query, so it answers again after the device changes — a tablet docked to a
|
|
171
|
+
* trackpad stops being touch-primary.
|
|
172
|
+
*/
|
|
173
|
+
export const isTouchPrimary = () => matchesMedia('(pointer: coarse)');
|
|
111
174
|
//# sourceMappingURL=animate.js.map
|
package/dist/animate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"animate.js","sourceRoot":"","sources":["../src/animate.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,8EAA8E;AAC9E,6EAA6E;AAC7E,6EAA6E;AAC7E,kCAAkC;AAalC,MAAM,UAAU,WAAW,CAAC,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU;IACxE,MAAM,CAAC,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"animate.js","sourceRoot":"","sources":["../src/animate.ts"],"names":[],"mappings":"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"}
|
package/dist/css-vars.d.ts
CHANGED
|
@@ -14,9 +14,9 @@ export declare function parseNumber(v: string | undefined): number | undefined;
|
|
|
14
14
|
/** A fraction. `0.3` and `30%` are equivalent. */
|
|
15
15
|
export declare function parseRatio(v: string | undefined): number | undefined;
|
|
16
16
|
/**
|
|
17
|
-
* A timing keyword
|
|
18
|
-
* within [0, 1], as CSS requires. Outside that range the
|
|
19
|
-
* function of time and the solver would not converge.
|
|
17
|
+
* A timing keyword, `cubic-bezier(x1, y1, x2, y2)` or `linear(…)`. The bezier's
|
|
18
|
+
* x coordinates must be within [0, 1], as CSS requires. Outside that range the
|
|
19
|
+
* curve is not a function of time and the solver would not converge.
|
|
20
20
|
*/
|
|
21
21
|
export declare function parseEasing(v: string | undefined): Easing | undefined;
|
|
22
22
|
//# sourceMappingURL=css-vars.d.ts.map
|
package/dist/css-vars.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"css-vars.d.ts","sourceRoot":"","sources":["../src/css-vars.ts"],"names":[],"mappings":"AASA,OAAO,
|
|
1
|
+
{"version":3,"file":"css-vars.d.ts","sourceRoot":"","sources":["../src/css-vars.ts"],"names":[],"mappings":"AASA,OAAO,EAAsC,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAE/E,gFAAgF;AAChF,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;AAEhE;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,YAAY,CAOpE;AAID,yDAAyD;AACzD,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAInE;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAGrE;AAED,kDAAkD;AAClD,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAMpE;AA4DD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAcrE"}
|
package/dist/css-vars.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// Every parser returns undefined for anything it does not understand, never
|
|
7
7
|
// NaN, so an unreadable value falls through to its JS option instead of
|
|
8
8
|
// producing an invalid transform.
|
|
9
|
-
import { cubicBezier, easings } from "./animate.js";
|
|
9
|
+
import { cubicBezier, easings, linearEasing } from "./animate.js";
|
|
10
10
|
/**
|
|
11
11
|
* Returns a reader over `el`'s computed custom properties. Custom properties
|
|
12
12
|
* inherit, so a variable set on `:root` or any ancestor is visible here. When
|
|
@@ -62,11 +62,51 @@ const easingKeywordsOf = () => (easingKeywords ??= Object.assign(Object.create(n
|
|
|
62
62
|
'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
|
|
63
63
|
ios: easings.ios,
|
|
64
64
|
'ios-settle': easings.easeOut,
|
|
65
|
+
android: easings.android,
|
|
66
|
+
'android-settle': easings.androidSettle,
|
|
65
67
|
}));
|
|
66
68
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
69
|
+
* `linear(y [x%] [x%], …)` as CSS defines it: a stop without a position sits
|
|
70
|
+
* evenly between its positioned neighbours, the first and last default to 0%
|
|
71
|
+
* and 100%, positions never go backwards, and two positions make two stops.
|
|
72
|
+
*/
|
|
73
|
+
function parseLinear(body) {
|
|
74
|
+
const xs = [], ys = [];
|
|
75
|
+
for (const stop of body.split(',')) {
|
|
76
|
+
const [y, ...positions] = stop.trim().split(/\s+/);
|
|
77
|
+
const yn = parseNumber(y);
|
|
78
|
+
if (yn === undefined || positions.length > 2)
|
|
79
|
+
return undefined;
|
|
80
|
+
for (const pos of positions.length ? positions : [undefined]) {
|
|
81
|
+
const xn = pos === undefined ? undefined : pos.endsWith('%') ? parseNumber(pos.slice(0, -1)) : NaN;
|
|
82
|
+
if (Number.isNaN(xn))
|
|
83
|
+
return undefined;
|
|
84
|
+
xs.push(xn === undefined ? undefined : xn / 100);
|
|
85
|
+
ys.push(yn);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (ys.length < 2)
|
|
89
|
+
return undefined;
|
|
90
|
+
xs[0] ??= 0;
|
|
91
|
+
xs[xs.length - 1] ??= 1;
|
|
92
|
+
for (let i = 1; i < xs.length; i++) {
|
|
93
|
+
if (xs[i] !== undefined)
|
|
94
|
+
continue;
|
|
95
|
+
let j = i;
|
|
96
|
+
while (xs[j] === undefined)
|
|
97
|
+
j++;
|
|
98
|
+
for (let k = i; k < j; k++)
|
|
99
|
+
xs[k] = xs[i - 1] + ((xs[j] - xs[i - 1]) * (k - i + 1)) / (j - i + 1);
|
|
100
|
+
}
|
|
101
|
+
const x = xs;
|
|
102
|
+
for (let i = 1; i < x.length; i++)
|
|
103
|
+
x[i] = Math.max(x[i], x[i - 1]);
|
|
104
|
+
return linearEasing(x.map((xi, i) => [xi, ys[i]]), `linear(${body.trim()})`);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* A timing keyword, `cubic-bezier(x1, y1, x2, y2)` or `linear(…)`. The bezier's
|
|
108
|
+
* x coordinates must be within [0, 1], as CSS requires. Outside that range the
|
|
109
|
+
* curve is not a function of time and the solver would not converge.
|
|
70
110
|
*/
|
|
71
111
|
export function parseEasing(v) {
|
|
72
112
|
if (v === undefined)
|
|
@@ -75,6 +115,9 @@ export function parseEasing(v) {
|
|
|
75
115
|
const keyword = easingKeywordsOf()[s];
|
|
76
116
|
if (keyword)
|
|
77
117
|
return keyword;
|
|
118
|
+
const l = /^linear\(([^)]*)\)$/.exec(s);
|
|
119
|
+
if (l)
|
|
120
|
+
return parseLinear(l[1]);
|
|
78
121
|
const m = /^cubic-bezier\(([^)]*)\)$/.exec(s);
|
|
79
122
|
if (!m)
|
|
80
123
|
return undefined;
|