@yahoo/uds-create-config 5.19.1 → 5.21.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/dist/configs/documentation.js +1 -1
- package/dist/configs/system.d.ts +607 -2
- package/dist/configs/system.js +8 -0
- package/dist/entities/documentation/Documentation.d.ts +1 -1
- package/dist/entities/system/Component.js +1 -2
- package/dist/entities/system/LinkedSystem.d.ts +45 -5
- package/dist/entities/system/LinkedSystem.js +55 -4
- package/dist/entities/system/Modifier.d.ts +5 -2
- package/dist/entities/system/Modifier.js +5 -2
- package/dist/entities/system/Motion.d.ts +578 -4
- package/dist/entities/system/Motion.js +233 -70
- package/dist/entities/system/css-motion-reach.d.ts +62 -0
- package/dist/entities/system/css-motion-reach.js +195 -0
- package/dist/entities/system/css-motion.d.ts +55 -0
- package/dist/entities/system/css-motion.js +445 -0
- package/dist/entities/system/css-timing.d.ts +40 -0
- package/dist/entities/system/css-timing.js +247 -0
- package/dist/entities/system/easings.d.ts +29 -0
- package/dist/entities/system/easings.js +99 -0
- package/dist/entities/system/motion-convert.d.ts +42 -0
- package/dist/entities/system/motion-convert.js +517 -0
- package/dist/entities/system/motion-presets.d.ts +9 -1
- package/dist/entities/system/motion-presets.js +27 -38
- package/dist/entities/system/motion-variants.js +66 -49
- package/dist/entities/system/reduced-motion.d.ts +20 -0
- package/dist/entities/system/reduced-motion.js +42 -0
- package/dist/framework/Config.d.ts +32 -2
- package/dist/framework/Config.js +117 -7
- package/dist/framework/class-names.d.ts +2 -1
- package/dist/framework/class-names.js +31 -13
- package/dist/framework/defineEntity.d.ts +3 -1
- package/dist/framework/defineEntity.js +2 -0
- package/dist/framework/registered.d.ts +8 -1
- package/dist/framework/registry.d.ts +38 -1
- package/dist/framework/registry.js +3 -1
- package/dist/framework/render-spec.js +7 -2
- package/dist/framework/schema-version.d.ts +1 -1
- package/dist/framework/schema-version.js +5 -5
- package/dist/framework/signature.js +4 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/migrations/2.0.0/v1-artifact.d.ts +606 -2
- package/dist/migrations/20261009000514_css_motion_states.d.ts +11 -0
- package/dist/migrations/20261009000514_css_motion_states.js +10 -0
- package/dist/migrations/20261009022722_starting_style_modifier.d.ts +9 -0
- package/dist/migrations/20261009022722_starting_style_modifier.js +10 -0
- package/dist/migrations/20261009181635_css_motion_strict.d.ts +23 -0
- package/dist/migrations/20261009181635_css_motion_strict.js +152 -0
- package/dist/migrations/20261009193004_link_override_ref.d.ts +9 -0
- package/dist/migrations/20261009193004_link_override_ref.js +10 -0
- package/dist/spec/links.d.ts +2 -0
- package/dist/spec/links.js +2 -0
- package/dist/spec/motion.d.ts +7 -0
- package/dist/spec/motion.js +7 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -2
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import { isPlainObject } from "../../framework/utils/field-path.js";
|
|
2
|
+
import { createSliceMemo } from "../../framework/memo.js";
|
|
3
|
+
import { cssEasing, easingCurve, linearEasing, springTiming } from "./css-timing.js";
|
|
4
|
+
import { stateVariantNames } from "./motion-variants.js";
|
|
5
|
+
import { resolvePresetValue } from "./motion-presets.js";
|
|
6
|
+
//#region src/entities/system/css-motion.ts
|
|
7
|
+
/** The states a CSS preset changes to. `initial` is where it starts, so nothing transitions to it. */
|
|
8
|
+
const CSS_MOTION_STATES = [
|
|
9
|
+
"animate",
|
|
10
|
+
"exit",
|
|
11
|
+
"whileHover",
|
|
12
|
+
"whileTap",
|
|
13
|
+
"whileFocus"
|
|
14
|
+
];
|
|
15
|
+
/** `motion/react`'s transform values, which move on a spring by default. */
|
|
16
|
+
const TRANSFORM_VALUES = new Set([
|
|
17
|
+
"transformPerspective",
|
|
18
|
+
"x",
|
|
19
|
+
"y",
|
|
20
|
+
"z",
|
|
21
|
+
"translateX",
|
|
22
|
+
"translateY",
|
|
23
|
+
"translateZ",
|
|
24
|
+
"scale",
|
|
25
|
+
"scaleX",
|
|
26
|
+
"scaleY",
|
|
27
|
+
"rotate",
|
|
28
|
+
"rotateX",
|
|
29
|
+
"rotateY",
|
|
30
|
+
"rotateZ",
|
|
31
|
+
"skew",
|
|
32
|
+
"skewX",
|
|
33
|
+
"skewY"
|
|
34
|
+
]);
|
|
35
|
+
/** Keys of a transition that don't say how a value moves, so one holding only these takes the default. */
|
|
36
|
+
const ORCHESTRATION_KEYS = new Set([
|
|
37
|
+
"when",
|
|
38
|
+
"delay",
|
|
39
|
+
"delayChildren",
|
|
40
|
+
"staggerChildren",
|
|
41
|
+
"staggerDirection",
|
|
42
|
+
"repeat",
|
|
43
|
+
"repeatType",
|
|
44
|
+
"repeatDelay",
|
|
45
|
+
"from",
|
|
46
|
+
"elapsed"
|
|
47
|
+
]);
|
|
48
|
+
/** Keys of a state's values that are not values. */
|
|
49
|
+
const STATE_META_KEYS = new Set(["transition", "transitionEnd"]);
|
|
50
|
+
const CSS_TIME = /^(-?(?:\d+(?:\.\d*)?|\.\d+))(ms|s)$/;
|
|
51
|
+
/** A time in milliseconds: a number is seconds, as `motion/react` reads it, and a CSS time is read as
|
|
52
|
+
* written. `undefined` for a string that is no plain time, such as a `var()`. */
|
|
53
|
+
function milliseconds(value) {
|
|
54
|
+
if (typeof value === "number") return value * 1e3;
|
|
55
|
+
if (typeof value !== "string") return void 0;
|
|
56
|
+
const time = CSS_TIME.exec(value.trim());
|
|
57
|
+
if (!time) return void 0;
|
|
58
|
+
return time[2] === "ms" ? Number(time[1]) : Number(time[1]) * 1e3;
|
|
59
|
+
}
|
|
60
|
+
/** A time as CSS: whole milliseconds, or the string as written when it is no plain time. */
|
|
61
|
+
function cssTime(value) {
|
|
62
|
+
const ms = milliseconds(value);
|
|
63
|
+
if (ms !== void 0) return `${Math.round(ms)}ms`;
|
|
64
|
+
return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
|
|
65
|
+
}
|
|
66
|
+
const numberOf = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
67
|
+
/** `motion/react`'s default for a value with no transition of its own (`getDefaultTransition`). */
|
|
68
|
+
function defaultTransition({ key, value }) {
|
|
69
|
+
if (Array.isArray(value) && value.length > 2) return {
|
|
70
|
+
type: "keyframes",
|
|
71
|
+
duration: .8
|
|
72
|
+
};
|
|
73
|
+
if (TRANSFORM_VALUES.has(key)) {
|
|
74
|
+
if (!key.startsWith("scale")) return {
|
|
75
|
+
type: "spring",
|
|
76
|
+
stiffness: 500,
|
|
77
|
+
damping: 25
|
|
78
|
+
};
|
|
79
|
+
const target = Array.isArray(value) ? value.at(-1) : value;
|
|
80
|
+
return {
|
|
81
|
+
type: "spring",
|
|
82
|
+
stiffness: 550,
|
|
83
|
+
damping: Number(target) === 0 ? 2 * Math.sqrt(550) : 30
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
type: "keyframes",
|
|
88
|
+
ease: [
|
|
89
|
+
.25,
|
|
90
|
+
.1,
|
|
91
|
+
.35,
|
|
92
|
+
1
|
|
93
|
+
],
|
|
94
|
+
duration: .3
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The transition one value moves on, as `motion/react`'s `getValueTransition` picks it, and the path
|
|
99
|
+
* it was written at.
|
|
100
|
+
*/
|
|
101
|
+
function valueTransition({ transition, at, key, value }) {
|
|
102
|
+
let picked = transition;
|
|
103
|
+
let pickedAt = at;
|
|
104
|
+
for (const field of [key, "default"]) {
|
|
105
|
+
const own = transition?.[field];
|
|
106
|
+
if (!isPlainObject(own)) continue;
|
|
107
|
+
picked = own;
|
|
108
|
+
pickedAt = `${at}.${field}`;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
if (Object.keys(picked ?? {}).some((field) => !ORCHESTRATION_KEYS.has(field))) return {
|
|
112
|
+
transition: picked,
|
|
113
|
+
at: pickedAt
|
|
114
|
+
};
|
|
115
|
+
return {
|
|
116
|
+
transition: {
|
|
117
|
+
...defaultTransition({
|
|
118
|
+
key,
|
|
119
|
+
value
|
|
120
|
+
}),
|
|
121
|
+
...picked?.delay !== void 0 ? { delay: picked.delay } : {}
|
|
122
|
+
},
|
|
123
|
+
at: pickedAt
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/** One value's resolved transition as CSS, reporting an easing it can't spell. */
|
|
127
|
+
function timingOf({ transition, at, report }) {
|
|
128
|
+
const delay = cssTime(transition.delay);
|
|
129
|
+
const withDelay = (timing) => delay !== void 0 && delay !== "0ms" ? {
|
|
130
|
+
...timing,
|
|
131
|
+
delay
|
|
132
|
+
} : timing;
|
|
133
|
+
if (transition.type === "spring") {
|
|
134
|
+
const spring = springTiming({
|
|
135
|
+
stiffness: numberOf(transition.stiffness),
|
|
136
|
+
damping: numberOf(transition.damping),
|
|
137
|
+
mass: numberOf(transition.mass),
|
|
138
|
+
bounce: numberOf(transition.bounce),
|
|
139
|
+
duration: numberOf(transition.duration),
|
|
140
|
+
visualDuration: numberOf(transition.visualDuration)
|
|
141
|
+
});
|
|
142
|
+
return withDelay({
|
|
143
|
+
duration: cssTime(transition.playFor) ?? `${spring.duration}ms`,
|
|
144
|
+
easing: spring.easing
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
const duration = cssTime(transition.duration ?? .3) ?? "300ms";
|
|
148
|
+
const durationMs = milliseconds(duration) ?? 300;
|
|
149
|
+
let easing;
|
|
150
|
+
if (typeof transition.easing === "string") easing = transition.easing.trim();
|
|
151
|
+
else if (transition.ease !== void 0) {
|
|
152
|
+
easing = cssEasing({
|
|
153
|
+
ease: transition.ease,
|
|
154
|
+
duration: durationMs
|
|
155
|
+
});
|
|
156
|
+
if (easing === void 0) report({
|
|
157
|
+
at: `${at}.ease`,
|
|
158
|
+
problem: "easing"
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return withDelay({
|
|
162
|
+
duration,
|
|
163
|
+
easing: easing ?? "cubic-bezier(0, 0, 0.58, 1)"
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/** The timing of each value a destination sets. */
|
|
167
|
+
function timingsFor({ target, transition, at, report }) {
|
|
168
|
+
const out = {};
|
|
169
|
+
for (const [key, value] of Object.entries(target)) {
|
|
170
|
+
if (STATE_META_KEYS.has(key)) continue;
|
|
171
|
+
const picked = valueTransition({
|
|
172
|
+
transition,
|
|
173
|
+
at,
|
|
174
|
+
key,
|
|
175
|
+
value
|
|
176
|
+
});
|
|
177
|
+
const timing = timingOf({
|
|
178
|
+
...picked,
|
|
179
|
+
report
|
|
180
|
+
});
|
|
181
|
+
const hold = heldFor({
|
|
182
|
+
value,
|
|
183
|
+
times: picked.transition.times
|
|
184
|
+
});
|
|
185
|
+
if (hold === void 0) out[key] = timing;
|
|
186
|
+
else if (ACCELERATED_VALUES.has(key)) out[key] = heldWhole({
|
|
187
|
+
timing,
|
|
188
|
+
hold
|
|
189
|
+
});
|
|
190
|
+
else out[key] = held({
|
|
191
|
+
timing,
|
|
192
|
+
hold
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* The share of a keyframe list's time it holds its first value before it moves, for a list that
|
|
199
|
+
* holds and then moves to its last (`[0.7, 0.7, 1]`), which a transition plays as a delay. Its
|
|
200
|
+
* `times` say where the move starts; without them the keyframes are evenly spaced. `undefined` for
|
|
201
|
+
* any other value.
|
|
202
|
+
*/
|
|
203
|
+
function heldFor({ value, times }) {
|
|
204
|
+
if (!holdsThenMoves(value)) return void 0;
|
|
205
|
+
const start = value.length - 2;
|
|
206
|
+
if (Array.isArray(times) && times.length === value.length && typeof times[start] === "number") return times[start];
|
|
207
|
+
return start / (value.length - 1);
|
|
208
|
+
}
|
|
209
|
+
/** Whether a value is a keyframe list that holds its first value and then moves to its last. */
|
|
210
|
+
function holdsThenMoves(value) {
|
|
211
|
+
if (!Array.isArray(value) || value.length < 3) return false;
|
|
212
|
+
const first = JSON.stringify(value[0]);
|
|
213
|
+
return value.slice(1, -1).every((step) => JSON.stringify(step) === first);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* The values `motion/react` hands to the browser's own animations (`acceleratedValues`), which ease a
|
|
217
|
+
* keyframe list as a whole: the keyframes sit at their `times`, and the easing runs over all of them.
|
|
218
|
+
* Every other value it animates itself, easing each keyframe to the next on its own.
|
|
219
|
+
*/
|
|
220
|
+
const ACCELERATED_VALUES = new Set([
|
|
221
|
+
"opacity",
|
|
222
|
+
"clipPath",
|
|
223
|
+
"filter",
|
|
224
|
+
"transform"
|
|
225
|
+
]);
|
|
226
|
+
/**
|
|
227
|
+
* A hold eased as a whole, as the browser plays an accelerated value's keyframes: the full duration,
|
|
228
|
+
* on a curve that stays at the start until the easing passes the hold and then moves in a line to
|
|
229
|
+
* the end. An easing that is no curve, such as a `linear()` from a spring, waits out the hold instead.
|
|
230
|
+
*/
|
|
231
|
+
function heldWhole({ timing, hold }) {
|
|
232
|
+
const curve = easingCurve(timing.easing);
|
|
233
|
+
const duration = milliseconds(timing.duration);
|
|
234
|
+
if (!curve || duration === void 0) return held({
|
|
235
|
+
timing,
|
|
236
|
+
hold
|
|
237
|
+
});
|
|
238
|
+
return {
|
|
239
|
+
...timing,
|
|
240
|
+
easing: linearEasing({
|
|
241
|
+
curve: (progress) => Math.max(0, (curve(progress) - hold) / (1 - hold)),
|
|
242
|
+
duration
|
|
243
|
+
})
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/** A timing that waits out the hold: its delay grows by the held share and its duration shrinks. */
|
|
247
|
+
function held({ timing, hold }) {
|
|
248
|
+
const duration = milliseconds(timing.duration);
|
|
249
|
+
if (duration === void 0) return timing;
|
|
250
|
+
const delay = (milliseconds(timing.delay) ?? 0) + duration * hold;
|
|
251
|
+
return {
|
|
252
|
+
...timing,
|
|
253
|
+
duration: `${Math.round(duration * (1 - hold))}ms`,
|
|
254
|
+
delay: `${Math.round(delay)}ms`
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
/** Whether a CSS preset animates states, rather than only playing keyframes. */
|
|
258
|
+
function hasStates(body) {
|
|
259
|
+
return body.variants !== void 0 || body.initial !== void 0 || CSS_MOTION_STATES.some((state) => body[state] !== void 0);
|
|
260
|
+
}
|
|
261
|
+
/** A spring's time as the spring reads it, in seconds: a token's value has been read to seconds
|
|
262
|
+
* already, and a CSS time is converted. */
|
|
263
|
+
function springSeconds(value) {
|
|
264
|
+
if (typeof value === "number") return value;
|
|
265
|
+
const ms = milliseconds(value);
|
|
266
|
+
return ms === void 0 ? void 0 : ms / 1e3;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* A CSS timing as the reader below takes it, which is how `motion/react` writes one: a spring easing
|
|
270
|
+
* as `type: 'spring'` with its fields, and a value's own timing under its name. A `duration` beside a
|
|
271
|
+
* spring is how long the curve plays, `playFor`, since a spring's own `duration` is its settle time.
|
|
272
|
+
*/
|
|
273
|
+
function readable(timing) {
|
|
274
|
+
const { properties, easing, duration, ...rest } = timing;
|
|
275
|
+
const out = { ...rest };
|
|
276
|
+
if (isPlainObject(easing) && isPlainObject(easing.spring)) {
|
|
277
|
+
const spring = easing.spring;
|
|
278
|
+
out.type = "spring";
|
|
279
|
+
for (const key of [
|
|
280
|
+
"stiffness",
|
|
281
|
+
"damping",
|
|
282
|
+
"mass",
|
|
283
|
+
"bounce"
|
|
284
|
+
]) if (spring[key] !== void 0) out[key] = spring[key];
|
|
285
|
+
for (const key of ["duration", "visualDuration"]) {
|
|
286
|
+
const seconds = springSeconds(spring[key]);
|
|
287
|
+
if (seconds !== void 0) out[key] = seconds;
|
|
288
|
+
}
|
|
289
|
+
if (duration !== void 0) out.playFor = duration;
|
|
290
|
+
} else {
|
|
291
|
+
if (easing !== void 0) out.easing = easing;
|
|
292
|
+
if (duration !== void 0) out.duration = duration;
|
|
293
|
+
}
|
|
294
|
+
for (const [key, value] of Object.entries(isPlainObject(properties) ? properties : {})) if (isPlainObject(value)) out[key] = readable(value);
|
|
295
|
+
return out;
|
|
296
|
+
}
|
|
297
|
+
/** A preset's own transition, or a destination's, with its tokens read to values. */
|
|
298
|
+
function resolvedTransition({ config, value, at, report }) {
|
|
299
|
+
if (!isPlainObject(value)) return void 0;
|
|
300
|
+
const resolved = resolvePresetValue({
|
|
301
|
+
config,
|
|
302
|
+
value,
|
|
303
|
+
at,
|
|
304
|
+
report
|
|
305
|
+
});
|
|
306
|
+
return isPlainObject(resolved?.value) ? readable(resolved.value) : void 0;
|
|
307
|
+
}
|
|
308
|
+
function transitionsOf({ config, body, report }) {
|
|
309
|
+
const shared = resolvedTransition({
|
|
310
|
+
config,
|
|
311
|
+
value: body.transition,
|
|
312
|
+
at: "transition",
|
|
313
|
+
report
|
|
314
|
+
});
|
|
315
|
+
const destination = ({ target, at }) => {
|
|
316
|
+
const own = resolvedTransition({
|
|
317
|
+
config,
|
|
318
|
+
value: target.transition,
|
|
319
|
+
at: `${at}.transition`,
|
|
320
|
+
report
|
|
321
|
+
});
|
|
322
|
+
return timingsFor({
|
|
323
|
+
target,
|
|
324
|
+
transition: own ?? shared,
|
|
325
|
+
at: own ? `${at}.transition` : "transition",
|
|
326
|
+
report
|
|
327
|
+
});
|
|
328
|
+
};
|
|
329
|
+
const every = {};
|
|
330
|
+
const variants = {};
|
|
331
|
+
for (const [name, target] of Object.entries(isPlainObject(body.variants) ? body.variants : {})) {
|
|
332
|
+
if (!isPlainObject(target)) continue;
|
|
333
|
+
Object.assign(every, target);
|
|
334
|
+
variants[name] = destination({
|
|
335
|
+
target,
|
|
336
|
+
at: `variants.${name}`
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
const states = {};
|
|
340
|
+
for (const state of CSS_MOTION_STATES) {
|
|
341
|
+
const target = body[state];
|
|
342
|
+
if (!isPlainObject(target)) continue;
|
|
343
|
+
Object.assign(every, target);
|
|
344
|
+
states[state] = destination({
|
|
345
|
+
target,
|
|
346
|
+
at: state
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
values: timingsFor({
|
|
351
|
+
target: every,
|
|
352
|
+
transition: shared,
|
|
353
|
+
at: "transition",
|
|
354
|
+
report
|
|
355
|
+
}),
|
|
356
|
+
variants,
|
|
357
|
+
states
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
/** Every value in a preset's states written as a keyframe list. */
|
|
361
|
+
function keyframeLists({ preset, body }) {
|
|
362
|
+
const targets = [...Object.entries(isPlainObject(body.variants) ? body.variants : {}).map(([name, target]) => [`variants.${name}`, target]), ...CSS_MOTION_STATES.map((state) => [state, body[state]])];
|
|
363
|
+
const out = [];
|
|
364
|
+
for (const [at, target] of targets) {
|
|
365
|
+
if (!isPlainObject(target)) continue;
|
|
366
|
+
for (const [key, value] of Object.entries(target)) if (Array.isArray(value) && value.length > 1 && !holdsThenMoves(value)) out.push({
|
|
367
|
+
preset,
|
|
368
|
+
at: `${at}.${key}`
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
return out;
|
|
372
|
+
}
|
|
373
|
+
const resolveCssMotionPresets = createSliceMemo({
|
|
374
|
+
kinds: [
|
|
375
|
+
"motion",
|
|
376
|
+
"token",
|
|
377
|
+
"settings",
|
|
378
|
+
"font"
|
|
379
|
+
],
|
|
380
|
+
compute: (config) => {
|
|
381
|
+
const transitions = {};
|
|
382
|
+
const lists = [];
|
|
383
|
+
const tokens = [];
|
|
384
|
+
const reported = /* @__PURE__ */ new Set();
|
|
385
|
+
const report = (entry) => {
|
|
386
|
+
const key = JSON.stringify([
|
|
387
|
+
entry.preset,
|
|
388
|
+
entry.at,
|
|
389
|
+
entry.token,
|
|
390
|
+
entry.problem
|
|
391
|
+
]);
|
|
392
|
+
if (reported.has(key)) return;
|
|
393
|
+
reported.add(key);
|
|
394
|
+
tokens.push(entry);
|
|
395
|
+
};
|
|
396
|
+
for (const preset of config.list("motion")) {
|
|
397
|
+
const body = preset.toJSON();
|
|
398
|
+
if (body.runtime !== "css") continue;
|
|
399
|
+
if (!hasStates(body)) continue;
|
|
400
|
+
lists.push(...keyframeLists({
|
|
401
|
+
preset: preset.path,
|
|
402
|
+
body
|
|
403
|
+
}));
|
|
404
|
+
transitions[preset.path] = transitionsOf({
|
|
405
|
+
config,
|
|
406
|
+
body,
|
|
407
|
+
report: (problem) => report({
|
|
408
|
+
preset: preset.path,
|
|
409
|
+
...problem
|
|
410
|
+
})
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
return {
|
|
414
|
+
transitions,
|
|
415
|
+
keyframeLists: lists,
|
|
416
|
+
tokens
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
/** Every CSS preset that animates states, by path, with the timing of each value per destination. */
|
|
421
|
+
function cssMotionTransitions(config) {
|
|
422
|
+
return resolveCssMotionPresets(config).transitions;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* How long a CSS preset's `exit` takes, in milliseconds: the longest of its values' durations and
|
|
426
|
+
* delays as the element changes to its exit state. `0` for a preset with no exit, and a timing that is
|
|
427
|
+
* no plain time (a `var()`) counts as none.
|
|
428
|
+
*/
|
|
429
|
+
function cssMotionExitDuration({ config, preset }) {
|
|
430
|
+
const transitions = cssMotionTransitions(config)[preset];
|
|
431
|
+
const exit = (config.resolve("motion", preset)?.toJSON())?.exit;
|
|
432
|
+
if (!transitions || exit === void 0) return 0;
|
|
433
|
+
const timings = isPlainObject(exit) ? [transitions.states.exit ?? {}] : stateVariantNames(exit).map((name) => transitions.variants[name] ?? {});
|
|
434
|
+
return Math.max(0, ...timings.flatMap((group) => Object.values(group).map((timing) => (milliseconds(timing.duration) ?? 0) + (milliseconds(timing.delay) ?? 0))));
|
|
435
|
+
}
|
|
436
|
+
/** Every value a CSS preset's states write as a keyframe list, for the build to report. */
|
|
437
|
+
function cssMotionKeyframeLists(config) {
|
|
438
|
+
return resolveCssMotionPresets(config).keyframeLists;
|
|
439
|
+
}
|
|
440
|
+
/** The tokens CSS presets' transitions name that come to no value, and the easings CSS can't spell. */
|
|
441
|
+
function cssMotionTokens(config) {
|
|
442
|
+
return resolveCssMotionPresets(config).tokens;
|
|
443
|
+
}
|
|
444
|
+
//#endregion
|
|
445
|
+
export { CSS_MOTION_STATES, cssMotionExitDuration, cssMotionKeyframeLists, cssMotionTokens, cssMotionTransitions };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region src/entities/system/css-timing.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* A `motion/react` transition as the CSS timing that plays closest to it: a spring as a duration and
|
|
4
|
+
* a `linear()` easing sampled from the same equations `motion/react` runs, and a tween's named or
|
|
5
|
+
* bezier easing as its CSS spelling.
|
|
6
|
+
*
|
|
7
|
+
* The spring is a port of `motion-dom`'s generator (`animation/generators/spring.mjs`), with the
|
|
8
|
+
* rest thresholds it uses for a value that moves less than 5: a CSS transition plays one curve over
|
|
9
|
+
* a property's whole change, so the curve runs over progress from 0 to 1. Its duration is found and
|
|
10
|
+
* its curve sampled as `spring().toString()` does.
|
|
11
|
+
*/
|
|
12
|
+
/** The spring fields `motion/react` reads. Times are in seconds, as a transition writes them. */
|
|
13
|
+
interface SpringOptions {
|
|
14
|
+
readonly stiffness?: number;
|
|
15
|
+
readonly damping?: number;
|
|
16
|
+
readonly mass?: number;
|
|
17
|
+
readonly bounce?: number;
|
|
18
|
+
readonly duration?: number;
|
|
19
|
+
readonly visualDuration?: number;
|
|
20
|
+
}
|
|
21
|
+
/** A CSS timing: the duration in milliseconds and the easing function. */
|
|
22
|
+
interface SpringTiming {
|
|
23
|
+
readonly duration: number;
|
|
24
|
+
readonly easing: string;
|
|
25
|
+
}
|
|
26
|
+
/** A spring as the CSS duration and `linear()` easing that play it. */
|
|
27
|
+
declare function springTiming(options: SpringOptions): SpringTiming;
|
|
28
|
+
/**
|
|
29
|
+
* A tween's `ease` as CSS: a name `motion/react` gives, a curve of four numbers, or a CSS easing as it
|
|
30
|
+
* stands. `undefined` for anything else, such as a list of easings, one per keyframe.
|
|
31
|
+
*/
|
|
32
|
+
declare function cssEasing({
|
|
33
|
+
ease,
|
|
34
|
+
duration
|
|
35
|
+
}: {
|
|
36
|
+
ease: unknown; /** The tween's duration in milliseconds, which sets how many points a sampled easing gets. */
|
|
37
|
+
duration: number;
|
|
38
|
+
}): string | undefined;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { SpringOptions, SpringTiming, cssEasing, springTiming };
|