@studiometa/js-toolkit 4.0.0-alpha.1 → 4.0.0-alpha.2
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 +10 -0
- package/package.json +5 -2
- package/skills/studiometa-js-toolkit/SKILL.md +87 -0
- package/skills/studiometa-js-toolkit/references/api/configuration.md +169 -0
- package/skills/studiometa-js-toolkit/references/api/context/createContext.md +46 -0
- package/skills/studiometa-js-toolkit/references/api/context/createGroup.md +67 -0
- package/skills/studiometa-js-toolkit/references/api/context/index.md +69 -0
- package/skills/studiometa-js-toolkit/references/api/context/injectContext.md +58 -0
- package/skills/studiometa-js-toolkit/references/api/context/injectContextSync.md +44 -0
- package/skills/studiometa-js-toolkit/references/api/context/provideContext.md +61 -0
- package/skills/studiometa-js-toolkit/references/api/context/provideRootContext.md +42 -0
- package/skills/studiometa-js-toolkit/references/api/context/signal.md +85 -0
- package/skills/studiometa-js-toolkit/references/api/context/subscribeContext.md +71 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/children.md +92 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/component.md +75 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/index.md +41 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/inject.md +73 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/on.md +149 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/provide.md +77 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/read-write.md +93 -0
- package/skills/studiometa-js-toolkit/references/api/diagnostics/DIAGNOSTICS.md +137 -0
- package/skills/studiometa-js-toolkit/references/api/diagnostics/EVENTS.md +45 -0
- package/skills/studiometa-js-toolkit/references/api/diagnostics/index.md +58 -0
- package/skills/studiometa-js-toolkit/references/api/diagnostics/reportDiagnostic.md +67 -0
- package/skills/studiometa-js-toolkit/references/api/diagnostics/warn.md +54 -0
- package/skills/studiometa-js-toolkit/references/api/dom/breakpoints.md +81 -0
- package/skills/studiometa-js-toolkit/references/api/dom/domUpdate.md +70 -0
- package/skills/studiometa-js-toolkit/references/api/dom/emitExtendable.md +68 -0
- package/skills/studiometa-js-toolkit/references/api/dom/index.md +26 -0
- package/skills/studiometa-js-toolkit/references/api/dom/swap.md +105 -0
- package/skills/studiometa-js-toolkit/references/api/dom/watchAttributeNamespace.md +96 -0
- package/skills/studiometa-js-toolkit/references/api/dom/watchAttributes.md +56 -0
- package/skills/studiometa-js-toolkit/references/api/dom/whenDOMSettled.md +41 -0
- package/skills/studiometa-js-toolkit/references/api/html/data-component.md +105 -0
- package/skills/studiometa-js-toolkit/references/api/html/data-mount.md +69 -0
- package/skills/studiometa-js-toolkit/references/api/html/data-option.md +106 -0
- package/skills/studiometa-js-toolkit/references/api/html/data-ref.md +63 -0
- package/skills/studiometa-js-toolkit/references/api/html/index.md +32 -0
- package/skills/studiometa-js-toolkit/references/api/index.md +73 -0
- package/skills/studiometa-js-toolkit/references/api/instance-events.md +75 -0
- package/skills/studiometa-js-toolkit/references/api/instance-methods.md +267 -0
- package/skills/studiometa-js-toolkit/references/api/instance-properties.md +90 -0
- package/skills/studiometa-js-toolkit/references/api/methods-hooks-events.md +149 -0
- package/skills/studiometa-js-toolkit/references/api/methods-hooks-lifecycle.md +90 -0
- package/skills/studiometa-js-toolkit/references/api/methods-hooks-options.md +96 -0
- package/skills/studiometa-js-toolkit/references/api/methods-hooks-services.md +164 -0
- package/skills/studiometa-js-toolkit/references/api/registry/defineManifest.md +57 -0
- package/skills/studiometa-js-toolkit/references/api/registry/fromMetaGlob.md +33 -0
- package/skills/studiometa-js-toolkit/references/api/registry/fromWebpackContext.md +37 -0
- package/skills/studiometa-js-toolkit/references/api/registry/getInstance.md +52 -0
- package/skills/studiometa-js-toolkit/references/api/registry/getInstances.md +48 -0
- package/skills/studiometa-js-toolkit/references/api/registry/getMountedInstances.md +39 -0
- package/skills/studiometa-js-toolkit/references/api/registry/getUnmountedInstances.md +46 -0
- package/skills/studiometa-js-toolkit/references/api/registry/index.md +49 -0
- package/skills/studiometa-js-toolkit/references/api/registry/registerComponent.md +93 -0
- package/skills/studiometa-js-toolkit/references/api/registry/registerComponents.md +37 -0
- package/skills/studiometa-js-toolkit/references/api/registry/registerManifest.md +72 -0
- package/skills/studiometa-js-toolkit/references/api/scheduler/defaultScheduler.md +104 -0
- package/skills/studiometa-js-toolkit/references/api/scheduler/index.md +27 -0
- package/skills/studiometa-js-toolkit/references/api/scheduler/nextFrame.md +31 -0
- package/skills/studiometa-js-toolkit/references/api/scheduler/viewTransition.md +53 -0
- package/skills/studiometa-js-toolkit/references/api/services/createService.md +95 -0
- package/skills/studiometa-js-toolkit/references/api/services/createServiceMixin.md +81 -0
- package/skills/studiometa-js-toolkit/references/api/services/index.md +94 -0
- package/skills/studiometa-js-toolkit/references/api/services/mixins.md +148 -0
- package/skills/studiometa-js-toolkit/references/api/services/perTarget.md +65 -0
- package/skills/studiometa-js-toolkit/references/api/services/toggle.md +80 -0
- package/skills/studiometa-js-toolkit/references/api/services/until.md +44 -0
- package/skills/studiometa-js-toolkit/references/api/services/useBreakpoint.md +42 -0
- package/skills/studiometa-js-toolkit/references/api/services/useDrag.md +119 -0
- package/skills/studiometa-js-toolkit/references/api/services/useInView.md +64 -0
- package/skills/studiometa-js-toolkit/references/api/services/useKey.md +105 -0
- package/skills/studiometa-js-toolkit/references/api/services/useMediaQuery.md +45 -0
- package/skills/studiometa-js-toolkit/references/api/services/useMutation.md +69 -0
- package/skills/studiometa-js-toolkit/references/api/services/usePointer.md +91 -0
- package/skills/studiometa-js-toolkit/references/api/services/usePrefersReducedMotion.md +42 -0
- package/skills/studiometa-js-toolkit/references/api/services/useRaf.md +91 -0
- package/skills/studiometa-js-toolkit/references/api/services/useResize.md +60 -0
- package/skills/studiometa-js-toolkit/references/api/services/useScroll.md +88 -0
- package/skills/studiometa-js-toolkit/references/api/services/useScrollProgress.md +88 -0
- package/skills/studiometa-js-toolkit/references/api/services/useWindowScroll.md +25 -0
- package/skills/studiometa-js-toolkit/references/api/services/useWindowSize.md +25 -0
- package/skills/studiometa-js-toolkit/references/api/storage/createStorage.md +126 -0
- package/skills/studiometa-js-toolkit/references/api/storage/index.md +33 -0
- package/skills/studiometa-js-toolkit/references/api/storage/presets.md +58 -0
- package/skills/studiometa-js-toolkit/references/api/storage/providers.md +109 -0
- package/skills/studiometa-js-toolkit/references/api/test/index.md +214 -0
- package/skills/studiometa-js-toolkit/references/guide/concepts/attribute-grammar.md +93 -0
- package/skills/studiometa-js-toolkit/references/guide/concepts/mutation-engine.md +71 -0
- package/skills/studiometa-js-toolkit/references/guide/concepts/philosophy.md +78 -0
- package/skills/studiometa-js-toolkit/references/guide/concepts/the-registry.md +110 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/autoloading.md +104 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/handling-diagnostics.md +104 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/mount-strategies.md +95 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/scheduling-work.md +124 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/sharing-state.md +218 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/swapping-content.md +92 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/testing-components.md +137 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/typing-components.md +190 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/using-decorators.md +168 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/using-storage.md +125 -0
- package/skills/studiometa-js-toolkit/references/guide/index.md +163 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/installation.md +144 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/lifecycle-hooks.md +174 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/managing-components.md +229 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/managing-options.md +235 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/managing-refs.md +174 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/using-services.md +218 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/working-with-events.md +251 -0
- package/skills/studiometa-js-toolkit/references/guide/migration/v3-to-v4.md +373 -0
- package/skills/studiometa-js-toolkit/references/utils/css.md +98 -0
- package/skills/studiometa-js-toolkit/references/utils/dom.md +54 -0
- package/skills/studiometa-js-toolkit/references/utils/easings.md +217 -0
- package/skills/studiometa-js-toolkit/references/utils/focus.md +84 -0
- package/skills/studiometa-js-toolkit/references/utils/history.md +69 -0
- package/skills/studiometa-js-toolkit/references/utils/index.md +53 -0
- package/skills/studiometa-js-toolkit/references/utils/is.md +84 -0
- package/skills/studiometa-js-toolkit/references/utils/load.md +59 -0
- package/skills/studiometa-js-toolkit/references/utils/math.md +129 -0
- package/skills/studiometa-js-toolkit/references/utils/motion.md +218 -0
- package/skills/studiometa-js-toolkit/references/utils/objects.md +68 -0
- package/skills/studiometa-js-toolkit/references/utils/scroll.md +128 -0
- package/skills/studiometa-js-toolkit/references/utils/strings.md +176 -0
- package/skills/studiometa-js-toolkit/references/utils/timing.md +111 -0
- package/skills/studiometa-js-toolkit/references/utils/transitions.md +122 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# Easings
|
|
2
|
+
|
|
3
|
+
```js
|
|
4
|
+
import { easeInOutCubic, easeOutQuad } from '@studiometa/js-toolkit/utils';
|
|
5
|
+
|
|
6
|
+
easeOutQuad(0.5); // 0.75
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
An `EasingFunction` takes a `0 → 1` progress and returns a shaped `0 → 1` value.
|
|
10
|
+
|
|
11
|
+
## Choosing one
|
|
12
|
+
|
|
13
|
+
Eight curves, three directions each. Every `out` and `in-out` is derived from its `in`.
|
|
14
|
+
|
|
15
|
+
| Curve | In | Out | In-out |
|
|
16
|
+
| ------ | ----------------------------- | ------------------------------- | ----------------------------------- |
|
|
17
|
+
| linear | [`easeLinear`](#easelinear) | — | — |
|
|
18
|
+
| quad | [`easeInQuad`](#easeinquad) | [`easeOutQuad`](#easeoutquad) | [`easeInOutQuad`](#easeinoutquad) |
|
|
19
|
+
| cubic | [`easeInCubic`](#easeincubic) | [`easeOutCubic`](#easeoutcubic) | [`easeInOutCubic`](#easeinoutcubic) |
|
|
20
|
+
| quart | [`easeInQuart`](#easeinquart) | [`easeOutQuart`](#easeoutquart) | [`easeInOutQuart`](#easeinoutquart) |
|
|
21
|
+
| quint | [`easeInQuint`](#easeinquint) | [`easeOutQuint`](#easeoutquint) | [`easeInOutQuint`](#easeinoutquint) |
|
|
22
|
+
| sine | [`easeInSine`](#easeinsine) | [`easeOutSine`](#easeoutsine) | [`easeInOutSine`](#easeinoutsine) |
|
|
23
|
+
| circ | [`easeInCirc`](#easeincirc) | [`easeOutCirc`](#easeoutcirc) | [`easeInOutCirc`](#easeinoutcirc) |
|
|
24
|
+
| expo | [`easeInExpo`](#easeinexpo) | [`easeOutExpo`](#easeoutexpo) | [`easeInOutExpo`](#easeinoutexpo) |
|
|
25
|
+
|
|
26
|
+
## Deriving one
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
createEaseOut(easeIn: EasingFunction): EasingFunction
|
|
30
|
+
createEaseInOut(easeIn: EasingFunction): EasingFunction
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Every `out` and `in-out` above is one of these applied to its `in`. Which means a custom curve gets its whole family for two lines:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { createEaseInOut, createEaseOut, type EasingFunction } from '@studiometa/js-toolkit/utils';
|
|
37
|
+
|
|
38
|
+
const easeInBack: EasingFunction = (progress) => progress * progress * (2.7 * progress - 1.7);
|
|
39
|
+
|
|
40
|
+
const easeOutBack = createEaseOut(easeInBack);
|
|
41
|
+
const easeInOutBack = createEaseInOut(easeInBack);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
That is why only the eight `in` functions are written out and the other sixteen are derived: an `out` is an `in` run backwards, and writing it twice is how the two drift apart.
|
|
45
|
+
|
|
46
|
+
### createEaseOut
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
createEaseOut(easeIn: EasingFunction): EasingFunction
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Mirror an `in` curve into its `out`.
|
|
53
|
+
|
|
54
|
+
### createEaseInOut
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
createEaseInOut(easeIn: EasingFunction): EasingFunction
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Compose an `in` curve into its symmetric `in-out`.
|
|
61
|
+
|
|
62
|
+
## Using one
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
import { easeOutCubic, lerp } from '@studiometa/js-toolkit/utils';
|
|
66
|
+
|
|
67
|
+
function positionAt(progress) {
|
|
68
|
+
return lerp(0, 400, easeOutCubic(progress));
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
An easing shapes a **progress**, so it pairs with a value that already runs `0 → 1`: a [`useScrollProgress()`](../api/services/useScrollProgress.md) subscriber, or a `map()` of anything else.
|
|
73
|
+
|
|
74
|
+
## They do not animate
|
|
75
|
+
|
|
76
|
+
There is no clock here — an easing is a pure function of a progress you already have.
|
|
77
|
+
|
|
78
|
+
- For a value chasing a target frame by frame, use [`damp()` or `smoothTo()`](motion.md).
|
|
79
|
+
- For time-based playback, stagger and sequencing, that is the separate `ui-animation` package. `tween` and `animate` are not shipped.
|
|
80
|
+
|
|
81
|
+
## The functions
|
|
82
|
+
|
|
83
|
+
Each takes a `0 → 1` progress and returns the shaped value.
|
|
84
|
+
|
|
85
|
+
### easeLinear
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
easeLinear(progress: number): number
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The identity. It exists so a call site can name "no easing" rather than branch on `undefined`.
|
|
92
|
+
|
|
93
|
+
### easeInQuad
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
easeInQuad(progress: number): number
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### easeOutQuad
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
easeOutQuad(progress: number): number
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### easeInOutQuad
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
easeInOutQuad(progress: number): number
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### easeInCubic
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
easeInCubic(progress: number): number
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### easeOutCubic
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
easeOutCubic(progress: number): number
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### easeInOutCubic
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
easeInOutCubic(progress: number): number
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### easeInQuart
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
easeInQuart(progress: number): number
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### easeOutQuart
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
easeOutQuart(progress: number): number
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### easeInOutQuart
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
easeInOutQuart(progress: number): number
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### easeInQuint
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
easeInQuint(progress: number): number
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### easeOutQuint
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
easeOutQuint(progress: number): number
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### easeInOutQuint
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
easeInOutQuint(progress: number): number
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### easeInSine
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
easeInSine(progress: number): number
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### easeOutSine
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
easeOutSine(progress: number): number
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### easeInOutSine
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
easeInOutSine(progress: number): number
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### easeInCirc
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
easeInCirc(progress: number): number
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### easeOutCirc
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
easeOutCirc(progress: number): number
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### easeInOutCirc
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
easeInOutCirc(progress: number): number
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### easeInExpo
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
easeInExpo(progress: number): number
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### easeOutExpo
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
easeOutExpo(progress: number): number
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### easeInOutExpo
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
easeInOutExpo(progress: number): number
|
|
217
|
+
```
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Focus
|
|
2
|
+
|
|
3
|
+
```js
|
|
4
|
+
import { saveActiveElement, trapFocus, untrapFocus } from '@studiometa/js-toolkit/utils';
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
The three calls a modal surface needs.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
`trapFocus()` takes **the keyboard event**, so it is called from a key handler rather than installing a listener of its own:
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
import { Base } from '@studiometa/js-toolkit';
|
|
15
|
+
import { saveActiveElement, trapFocus, untrapFocus } from '@studiometa/js-toolkit/utils';
|
|
16
|
+
|
|
17
|
+
class Dialog extends Base {
|
|
18
|
+
static config = { name: 'Dialog' };
|
|
19
|
+
|
|
20
|
+
open() {
|
|
21
|
+
saveActiveElement();
|
|
22
|
+
this.$el.removeAttribute('hidden');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
close() {
|
|
26
|
+
this.$el.setAttribute('hidden', '');
|
|
27
|
+
untrapFocus();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
onKeydown(event) {
|
|
31
|
+
trapFocus(this.$el, event);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
With [`useKey()`](../api/services/useKey.md) the event is in the props:
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
mounted() {
|
|
40
|
+
return useKey(this.$el).subscribe(({ event, TAB, isDown }) => {
|
|
41
|
+
if (TAB && isDown && event) trapFocus(this.$el, event);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**This is why the key listeners are not passive**
|
|
47
|
+
|
|
48
|
+
`trapFocus()` calls `preventDefault()` on the event it is handed, and a passive listener cannot. See [`useKey()`](../api/services/useKey.md#the-listeners-are-neither-passive-nor-capturing).
|
|
49
|
+
|
|
50
|
+
## The three
|
|
51
|
+
|
|
52
|
+
### saveActiveElement
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
saveActiveElement(): void
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Remembers what had focus, so `untrapFocus()` can give it back.
|
|
59
|
+
|
|
60
|
+
### trapFocus
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
trapFocus(el: HTMLElement, event: KeyboardEvent): void
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Keeps `Tab` and `Shift+Tab` inside `el`.
|
|
67
|
+
|
|
68
|
+
### untrapFocus
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
untrapFocus(): void
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Releases the trap and restores focus to the saved element.
|
|
75
|
+
|
|
76
|
+
## The saved element is shared across copies
|
|
77
|
+
|
|
78
|
+
Through the shared runtime, for the same reason the scroll lock counts: **there is one focus per document**. Two independently evaluated copies of the package must not each think they own it.
|
|
79
|
+
|
|
80
|
+
## `<dialog>` already does some of this
|
|
81
|
+
|
|
82
|
+
`showModal()` gives the top layer, the backdrop, a focus trap and `Escape`. What it does **not** do is stop the page behind it from scrolling — see [`lockScroll()`](scroll.md#lockscroll).
|
|
83
|
+
|
|
84
|
+
Reach for these three when the surface is not a native `<dialog>`.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# History
|
|
2
|
+
|
|
3
|
+
```js
|
|
4
|
+
import { historyPush, historyReplace, objectToURLSearchParams } from '@studiometa/js-toolkit/utils';
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
## Writing the URL
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
import { historyPush, historyReplace } from '@studiometa/js-toolkit/utils';
|
|
11
|
+
|
|
12
|
+
historyPush({ path: '/products', search: { page: 2, sort: 'price' } });
|
|
13
|
+
historyReplace({ search: { page: 3 } });
|
|
14
|
+
historyPush({ hash: 'section-2' });
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
interface HistoryOptions {
|
|
19
|
+
path?: string;
|
|
20
|
+
search?: URLSearchParams | Record<string, SearchParamInput>;
|
|
21
|
+
hash?: string;
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Each part is optional, and an omitted part is kept.** `historyReplace({ search })` keeps the path and the hash; `historyPush({ hash })` keeps the path and the query string.
|
|
26
|
+
|
|
27
|
+
That is the whole reason these exist over `history.pushState()`: rebuilding a URL from `location` by hand is where a filter update loses the hash, or a hash update drops the query string.
|
|
28
|
+
|
|
29
|
+
### historyPush
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
historyPush(options: HistoryOptions, data?: unknown, title?: string): void
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Adds a history entry. This is what makes a state navigable with the back button.
|
|
36
|
+
|
|
37
|
+
### historyReplace
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
historyReplace(options: HistoryOptions, data?: unknown, title?: string): void
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Replaces the current entry. This is what keeps a scroll position or a tab index out of the history.
|
|
44
|
+
|
|
45
|
+
## Building the query string
|
|
46
|
+
|
|
47
|
+
### objectToURLSearchParams
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
objectToURLSearchParams(
|
|
51
|
+
object: Record<string, SearchParamInput>,
|
|
52
|
+
defaultSearch?: string,
|
|
53
|
+
): URLSearchParams
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
import { objectToURLSearchParams } from '@studiometa/js-toolkit/utils';
|
|
58
|
+
|
|
59
|
+
objectToURLSearchParams({ page: 2, tags: ['a', 'b'], open: true });
|
|
60
|
+
objectToURLSearchParams({ page: 2 }, location.search);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`SearchParamInput` accepts a string, a number, a boolean, `null`, `undefined`, an array of those, or a nested object of those — so a filter state serializes without being flattened first.
|
|
64
|
+
|
|
65
|
+
`defaultSearch` is the starting point, so the result **merges into** an existing query string instead of replacing it.
|
|
66
|
+
|
|
67
|
+
## See also
|
|
68
|
+
|
|
69
|
+
For a URL that is also a store you can read, write and observe, use [`createUrlSearchParamsStorage()`](../api/storage/presets.md) — it is these functions behind a typed key-value surface, with `popstate` wired up.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Utils
|
|
2
|
+
|
|
3
|
+
```js
|
|
4
|
+
import { clamp, damp } from '@studiometa/js-toolkit/utils';
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
Every one of them also has a subpath of its own:
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
import { clamp } from '@studiometa/js-toolkit/utils/clamp';
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
With a bundler, prefer the barrel — tree-shaking removes what you do not import. From an ESM CDN with no build step, prefer the subpath: there is no bundler to shake the graph.
|
|
14
|
+
|
|
15
|
+
## The categories
|
|
16
|
+
|
|
17
|
+
| Category | Contents |
|
|
18
|
+
| ---------------------------------- | ---------------------------------------------------------------------- |
|
|
19
|
+
| [Type guards](is.md) | `isString`, `isNumber`, `isObject`, `isDefined`, … |
|
|
20
|
+
| [Strings](strings.md) | case conversion, leading and trailing characters |
|
|
21
|
+
| [Math](math.md) | `clamp`, `lerp`, `map`, `wrap`, `fold`, `round`, `mean`, `createRange` |
|
|
22
|
+
| [Easings](easings.md) | the 24 easing functions, `createEaseOut`, `createEaseInOut` |
|
|
23
|
+
| [Motion](motion.md) | `damp`, `spring`, `smoothTo`, the inertia family |
|
|
24
|
+
| [CSS](css.md) | `transform`, `matrix`, `getOffsetSizes`, `setClassesOrStyles` |
|
|
25
|
+
| [Transitions](transitions.md) | `transition`, `enterTransition`, `leaveTransition` |
|
|
26
|
+
| [DOM](dom.md) | `createElement`, `selectorFor` |
|
|
27
|
+
| [Focus](focus.md) | `trapFocus`, `untrapFocus`, `saveActiveElement` |
|
|
28
|
+
| [Scroll](scroll.md) | `scrollTo`, `scrollPosition`, `lockScroll` |
|
|
29
|
+
| [History](history.md) | `historyPush`, `historyReplace`, `objectToURLSearchParams` |
|
|
30
|
+
| [Loading](load.md) | `loadImage`, `loadScript`, `loadLink` |
|
|
31
|
+
| [Timing](timing.md) | `debounce`, `throttle`, `wait`, `memo`, `noop` |
|
|
32
|
+
| [Objects & random](objects.md) | `deepmerge`, `random`, `randomInt`, `randomItem` |
|
|
33
|
+
|
|
34
|
+
## What v3 shipped and v4 does not
|
|
35
|
+
|
|
36
|
+
| Removed | Because |
|
|
37
|
+
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
38
|
+
| `tween`, `animate` | time-based playback moves to a separate `ui-animation` package |
|
|
39
|
+
| `domScheduler`, `useScheduler` | [one scheduler](../api/scheduler/index.md) is the clock |
|
|
40
|
+
| `Queue`, `SmartQueue` | the scheduler's lanes replace them |
|
|
41
|
+
| the collision helpers | not ported |
|
|
42
|
+
| `keyCodes` | [`useKey()`](../api/services/useKey.md) delivers named booleans, so nothing is left to compare against |
|
|
43
|
+
| `nextTick`, `nextMicrotask` | `await Promise.resolve()` and `queueMicrotask()` say it |
|
|
44
|
+
| `isEmpty`, `isEmptyString`, `isArray`, `isDev` | the platform or a one-liner says it |
|
|
45
|
+
| `cache`, `memoize` | [`memo`](timing.md#memo) covers the one case core needed |
|
|
46
|
+
| `addClass`, `removeClass`, `toggleClass`, `addStyle`, `removeStyle` | `classList` and `style` say it |
|
|
47
|
+
| `loadIframe`, `loadElement` | not ported |
|
|
48
|
+
|
|
49
|
+
## How a utility earns its place
|
|
50
|
+
|
|
51
|
+
**It is judged by consumer need, not by core's own use.** Some of these are not called anywhere in core — they ship because a component author would otherwise write them again, and getting them wrong is easy. `deepmerge` is the clearest case.
|
|
52
|
+
|
|
53
|
+
The reverse also holds: a function core happens to use internally is not public for that reason alone.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Type guards
|
|
2
|
+
|
|
3
|
+
```js
|
|
4
|
+
import { isDefined, isNumber, isObject, isString } from '@studiometa/js-toolkit/utils';
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
Each one narrows the type.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { isDefined, isObject, isString } from '@studiometa/js-toolkit/utils';
|
|
13
|
+
|
|
14
|
+
function label(value: unknown): string {
|
|
15
|
+
if (isString(value)) return value; // string
|
|
16
|
+
if (isObject(value)) return JSON.stringify(value); // Record<string, unknown>
|
|
17
|
+
return '';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function first<T>(items: (T | undefined)[]): T[] {
|
|
21
|
+
return items.filter(isDefined); // T[]
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`isDefined` as a `filter` predicate is the case that earns the export: it is the one narrowing TypeScript will not do from a truthiness check.
|
|
26
|
+
|
|
27
|
+
## The guards
|
|
28
|
+
|
|
29
|
+
### isNull
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
isNull(value: unknown): value is null
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### isDefined
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
isDefined<T>(value: T | undefined): value is T
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Narrows `T | undefined` to `T`. Passing it by reference to `filter` is what it is for.
|
|
42
|
+
|
|
43
|
+
### isString
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
isString(value: unknown): value is string
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### isNumber
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
isNumber(value: unknown): value is number
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**It rejects `NaN`**, because a `NaN` that passes a number check is a bug that surfaces three functions later.
|
|
56
|
+
|
|
57
|
+
### isBoolean
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
isBoolean(value: unknown): value is boolean
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### isFunction
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
isFunction(value: unknown): value is (...args: unknown[]) => unknown
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### isObject
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
isObject(value: unknown): value is Record<string, unknown>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## What is not here
|
|
76
|
+
|
|
77
|
+
| Not shipped | Write |
|
|
78
|
+
| --------------- | ----------------------------------- |
|
|
79
|
+
| `isArray` | `Array.isArray(value)` |
|
|
80
|
+
| `isEmpty` | the check the caller actually means |
|
|
81
|
+
| `isEmptyString` | `value === ''` |
|
|
82
|
+
| `isDev` | your bundler's own flag |
|
|
83
|
+
|
|
84
|
+
A guard earns its place by narrowing something the platform does not, or by being a predicate you pass by reference.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Loading
|
|
2
|
+
|
|
3
|
+
```js
|
|
4
|
+
import { loadImage, loadLink, loadScript } from '@studiometa/js-toolkit/utils';
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
Three promises over three tags. Each resolves with the **element** it created, so a caller can read the loaded image's natural size, or keep the script tag to remove it later.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import { loadImage, loadLink, loadScript } from '@studiometa/js-toolkit/utils';
|
|
13
|
+
|
|
14
|
+
async function load() {
|
|
15
|
+
const img = await loadImage('/hero.webp');
|
|
16
|
+
|
|
17
|
+
await loadScript('https://example.com/widget.js', { async: '', crossorigin: 'anonymous' });
|
|
18
|
+
|
|
19
|
+
await loadLink('/print.css', { rel: 'stylesheet', media: 'print' });
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### loadImage
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
loadImage(src: string): Promise<HTMLImageElement>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### loadScript
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
loadScript(src: string, attributes?: Record<string, string>): Promise<HTMLScriptElement>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### loadLink
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
loadLink(href: string, attributes: { rel: string; [key: string]: string }): Promise<HTMLLinkElement>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Where they belong
|
|
42
|
+
|
|
43
|
+
A third-party widget, a heavy stylesheet or a decorative image is exactly what a deferred mount is for. Reach for these **inside** a component whose mount is already deferred, rather than as a substitute for deferring it:
|
|
44
|
+
|
|
45
|
+
```html
|
|
46
|
+
<div data-component="Widget" data-mount="interaction:page"></div>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
mounted() {
|
|
51
|
+
return loadScript('https://example.com/widget.js').then(() => this.init());
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The [mount strategy](../guide/going-further/mount-strategies.md) decides _when_, and these decide _what_.
|
|
56
|
+
|
|
57
|
+
## What is not here
|
|
58
|
+
|
|
59
|
+
`loadIframe` and `loadElement` are not ported. An iframe is one `createElement()` and one `load` listener, and a generic element loader had no caller that a specific one did not serve better.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Math
|
|
2
|
+
|
|
3
|
+
```js
|
|
4
|
+
import { clamp, lerp, map } from '@studiometa/js-toolkit/utils';
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
Values in, values out. For anything with a time dimension — damping, springs, inertia — see [Motion](motion.md).
|
|
8
|
+
|
|
9
|
+
## Ranges
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import { clamp, clamp01 } from '@studiometa/js-toolkit/utils';
|
|
13
|
+
|
|
14
|
+
clamp(15, 0, 10); // 10
|
|
15
|
+
clamp(-5, 0, 10); // 0
|
|
16
|
+
clamp01(1.5); // 1
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### clamp
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
clamp(value: number, min: number, max: number): number
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Keep a value inside a range.
|
|
26
|
+
|
|
27
|
+
### clamp01
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
clamp01(value: number): number
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Keep a value inside `0 → 1`. It is the case that comes up in every progress calculation, so it has its own name and no arguments to get the wrong way round.
|
|
34
|
+
|
|
35
|
+
## Interpolation
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
import { lerp, map } from '@studiometa/js-toolkit/utils';
|
|
39
|
+
|
|
40
|
+
lerp(0, 100, 0.5); // 50
|
|
41
|
+
map(5, 0, 10, 0, 100); // 50
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### lerp
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
lerp(min: number, max: number, ratio: number): number
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Interpolate between two values by a `0 → 1` ratio.
|
|
51
|
+
|
|
52
|
+
### map
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
map(value: number, inputMin: number, inputMax: number, outputMin: number, outputMax: number): number
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Re-scale one range onto another. It is `lerp` with the input range worked out for you.
|
|
59
|
+
|
|
60
|
+
## Wrapping and folding
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
import { fold, wrap } from '@studiometa/js-toolkit/utils';
|
|
64
|
+
|
|
65
|
+
wrap(11, 0, 10); // 1 — it comes back round
|
|
66
|
+
fold(11, 0, 10); // 9 — it bounces back
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Two behaviours, two names, no flag.
|
|
70
|
+
|
|
71
|
+
### wrap
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
wrap(value: number, min: number, max: number): number
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
A carousel that loops: past the end, it comes back round to the start.
|
|
78
|
+
|
|
79
|
+
### fold
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
fold(value: number, min: number, max: number): number
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
A value that reverses at the edges rather than looping.
|
|
86
|
+
|
|
87
|
+
## Rounding and averaging
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
import { mean, round } from '@studiometa/js-toolkit/utils';
|
|
91
|
+
|
|
92
|
+
round(1.2345, 2); // 1.23
|
|
93
|
+
mean([1, 2, 3, 4]); // 2.5
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### round
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
round(value: number, decimals?: number): number
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Round to a number of decimals.
|
|
103
|
+
|
|
104
|
+
### mean
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
mean(numbers: readonly number[]): number
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The arithmetic mean of a list.
|
|
111
|
+
|
|
112
|
+
## Series
|
|
113
|
+
|
|
114
|
+
### createRange
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
createRange(min: number, max: number, step: number): number[]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
import { createRange } from '@studiometa/js-toolkit/utils';
|
|
122
|
+
|
|
123
|
+
createRange(0, 1, 0.25); // [0, 0.25, 0.5, 0.75, 1]
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## See also
|
|
127
|
+
|
|
128
|
+
- [Easings](easings.md) — shaping a `0 → 1` progress
|
|
129
|
+
- [Motion](motion.md) — everything with an elapsed time
|