@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.
Files changed (56) hide show
  1. package/dist/configs/documentation.js +1 -1
  2. package/dist/configs/system.d.ts +607 -2
  3. package/dist/configs/system.js +8 -0
  4. package/dist/entities/documentation/Documentation.d.ts +1 -1
  5. package/dist/entities/system/Component.js +1 -2
  6. package/dist/entities/system/LinkedSystem.d.ts +45 -5
  7. package/dist/entities/system/LinkedSystem.js +55 -4
  8. package/dist/entities/system/Modifier.d.ts +5 -2
  9. package/dist/entities/system/Modifier.js +5 -2
  10. package/dist/entities/system/Motion.d.ts +578 -4
  11. package/dist/entities/system/Motion.js +233 -70
  12. package/dist/entities/system/css-motion-reach.d.ts +62 -0
  13. package/dist/entities/system/css-motion-reach.js +195 -0
  14. package/dist/entities/system/css-motion.d.ts +55 -0
  15. package/dist/entities/system/css-motion.js +445 -0
  16. package/dist/entities/system/css-timing.d.ts +40 -0
  17. package/dist/entities/system/css-timing.js +247 -0
  18. package/dist/entities/system/easings.d.ts +29 -0
  19. package/dist/entities/system/easings.js +99 -0
  20. package/dist/entities/system/motion-convert.d.ts +42 -0
  21. package/dist/entities/system/motion-convert.js +517 -0
  22. package/dist/entities/system/motion-presets.d.ts +9 -1
  23. package/dist/entities/system/motion-presets.js +27 -38
  24. package/dist/entities/system/motion-variants.js +66 -49
  25. package/dist/entities/system/reduced-motion.d.ts +20 -0
  26. package/dist/entities/system/reduced-motion.js +42 -0
  27. package/dist/framework/Config.d.ts +32 -2
  28. package/dist/framework/Config.js +117 -7
  29. package/dist/framework/class-names.d.ts +2 -1
  30. package/dist/framework/class-names.js +31 -13
  31. package/dist/framework/defineEntity.d.ts +3 -1
  32. package/dist/framework/defineEntity.js +2 -0
  33. package/dist/framework/registered.d.ts +8 -1
  34. package/dist/framework/registry.d.ts +38 -1
  35. package/dist/framework/registry.js +3 -1
  36. package/dist/framework/render-spec.js +7 -2
  37. package/dist/framework/schema-version.d.ts +1 -1
  38. package/dist/framework/schema-version.js +5 -5
  39. package/dist/framework/signature.js +4 -2
  40. package/dist/index.d.ts +2 -2
  41. package/dist/index.js +1 -1
  42. package/dist/migrations/2.0.0/v1-artifact.d.ts +606 -2
  43. package/dist/migrations/20261009000514_css_motion_states.d.ts +11 -0
  44. package/dist/migrations/20261009000514_css_motion_states.js +10 -0
  45. package/dist/migrations/20261009022722_starting_style_modifier.d.ts +9 -0
  46. package/dist/migrations/20261009022722_starting_style_modifier.js +10 -0
  47. package/dist/migrations/20261009181635_css_motion_strict.d.ts +23 -0
  48. package/dist/migrations/20261009181635_css_motion_strict.js +152 -0
  49. package/dist/migrations/20261009193004_link_override_ref.d.ts +9 -0
  50. package/dist/migrations/20261009193004_link_override_ref.js +10 -0
  51. package/dist/spec/links.d.ts +2 -0
  52. package/dist/spec/links.js +2 -0
  53. package/dist/spec/motion.d.ts +7 -0
  54. package/dist/spec/motion.js +7 -0
  55. package/dist/tsconfig.tsbuildinfo +1 -1
  56. package/package.json +10 -2
@@ -0,0 +1,517 @@
1
+ import { reject } from "../../framework/rejection.js";
2
+ import { isRef } from "../../framework/utils/refs.js";
3
+ import { isPlainObject } from "../../framework/utils/field-path.js";
4
+ import { MOTION_EASING_NAMES, bezierPoints, motionOfCssKeyword } from "./easings.js";
5
+ import { cssEasing } from "./css-timing.js";
6
+ import { JS_ONLY_FIELDS } from "./Motion.js";
7
+ //#region src/entities/system/motion-convert.ts
8
+ /** The states both runtimes share. */
9
+ const STATES = [
10
+ "initial",
11
+ "animate",
12
+ "exit",
13
+ "whileHover",
14
+ "whileTap",
15
+ "whileFocus"
16
+ ];
17
+ /** A JS transition's keys that time a value, rather than name one. */
18
+ const JS_TIMING_KEYS = new Set([
19
+ "type",
20
+ "duration",
21
+ "delay",
22
+ "ease",
23
+ "times",
24
+ "stiffness",
25
+ "damping",
26
+ "mass",
27
+ "bounce",
28
+ "visualDuration"
29
+ ]);
30
+ /** A JS transition's keys that say how a value's animation sits among others, which CSS has none of. */
31
+ const JS_ORCHESTRATION_KEYS = new Set([
32
+ "when",
33
+ "delayChildren",
34
+ "staggerChildren",
35
+ "staggerDirection",
36
+ "from",
37
+ "elapsed",
38
+ "velocity",
39
+ "restSpeed",
40
+ "restDelta"
41
+ ]);
42
+ const JS_REPEAT_KEYS = [
43
+ "repeat",
44
+ "repeatType",
45
+ "repeatDelay"
46
+ ];
47
+ const CSS_TIME = /^(-?(?:\d+(?:\.\d*)?|\.\d+))(ms|s)$/;
48
+ var Notes = class {
49
+ dropped = [];
50
+ approximated = [];
51
+ refused = [];
52
+ };
53
+ /** `body` as the `to` runtime takes it, with what the conversion left out, changed or refused. */
54
+ function convertMotion({ body, to }) {
55
+ const notes = new Notes();
56
+ if (body.runtime === to) return {
57
+ body,
58
+ ...notes
59
+ };
60
+ const converted = to === "css" ? cssOf(body, notes) : jsOf(body, notes);
61
+ if (notes.refused.length > 0) return { ...notes };
62
+ return {
63
+ body: converted,
64
+ ...notes
65
+ };
66
+ }
67
+ function cssOf(body, notes) {
68
+ const out = { runtime: "css" };
69
+ for (const state of STATES) {
70
+ if (body[state] === void 0) continue;
71
+ out[state] = cssStateOf({
72
+ target: body[state],
73
+ at: state,
74
+ notes
75
+ });
76
+ }
77
+ if (isPlainObject(body.variants)) out.variants = Object.fromEntries(Object.entries(body.variants).map(([name, target]) => [name, cssStateOf({
78
+ target,
79
+ at: `variants.${name}`,
80
+ notes
81
+ })]));
82
+ if (isPlainObject(body.transition)) out.transition = cssTransitionOf({
83
+ transition: body.transition,
84
+ at: "transition",
85
+ notes
86
+ });
87
+ if (body.reducedMotion !== void 0) out.reducedMotion = body.reducedMotion;
88
+ for (const field of JS_ONLY_FIELDS) {
89
+ if (body[field] === void 0) continue;
90
+ notes.dropped.push({
91
+ at: field,
92
+ reason: `CSS has no \`${field}\`.`
93
+ });
94
+ }
95
+ return out;
96
+ }
97
+ function cssStateOf({ target, at, notes }) {
98
+ if (!isPlainObject(target)) return target;
99
+ const out = {};
100
+ for (const [key, value] of Object.entries(target)) {
101
+ if (key === "transitionEnd") {
102
+ notes.dropped.push({
103
+ at: `${at}.${key}`,
104
+ reason: "A stylesheet can't set values once a transition ends."
105
+ });
106
+ continue;
107
+ }
108
+ if (key === "transition") {
109
+ if (isPlainObject(value)) out.transition = cssTransitionOf({
110
+ transition: value,
111
+ at: `${at}.transition`,
112
+ notes
113
+ });
114
+ continue;
115
+ }
116
+ if (Array.isArray(value) && value.length > 1 && !holdsThenMoves(value)) notes.approximated.push({
117
+ at: `${at}.${key}`,
118
+ reason: "A CSS transition plays a list of keyframes as its last value."
119
+ });
120
+ out[key] = value;
121
+ }
122
+ return out;
123
+ }
124
+ /** Whether a keyframe list holds its first value and then moves to its last, which CSS plays as a
125
+ * delay. */
126
+ function holdsThenMoves(value) {
127
+ if (value.length < 3) return false;
128
+ const first = JSON.stringify(value[0]);
129
+ return value.slice(1, -1).every((step) => JSON.stringify(step) === first);
130
+ }
131
+ /**
132
+ * A JS transition as CSS: the timing every value takes (`default` where it sets one, as
133
+ * `motion/react` reads it), and each value's own under `properties`.
134
+ */
135
+ function cssTransitionOf({ transition, at, notes }) {
136
+ const out = cssTimingOf({
137
+ ...isPlainObject(transition.default) ? {
138
+ timing: transition.default,
139
+ at: `${at}.default`
140
+ } : {
141
+ timing: transition,
142
+ at
143
+ },
144
+ notes
145
+ });
146
+ const properties = {};
147
+ for (const [key, value] of Object.entries(transition)) {
148
+ if (key === "default" || JS_TIMING_KEYS.has(key)) continue;
149
+ if (JS_ORCHESTRATION_KEYS.has(key) || JS_REPEAT_KEYS.includes(key)) continue;
150
+ if (!isPlainObject(value) || isRef(value)) {
151
+ notes.dropped.push({
152
+ at: `${at}.${key}`,
153
+ reason: `\`${key}\` is no timing CSS reads.`
154
+ });
155
+ continue;
156
+ }
157
+ properties[key] = cssTimingOf({
158
+ timing: value,
159
+ at: `${at}.${key}`,
160
+ notes
161
+ });
162
+ }
163
+ if (Object.keys(properties).length > 0) out.properties = properties;
164
+ return out;
165
+ }
166
+ /** One JS timing as CSS. A timing that sets how a value moves keeps doing so, so a tween that leaves
167
+ * its duration to `motion/react`'s default writes that default out. */
168
+ function cssTimingOf({ timing, at, notes }) {
169
+ const out = {};
170
+ for (const key of JS_REPEAT_KEYS) {
171
+ if (timing[key] === void 0 || timing[key] === 0) continue;
172
+ notes.refused.push({
173
+ at: `${at}.${key}`,
174
+ reason: "A CSS transition plays once, so it has no repeat."
175
+ });
176
+ }
177
+ if (timing.type === "inertia" || timing.type === "decay") notes.refused.push({
178
+ at: `${at}.type`,
179
+ reason: `CSS has no \`${timing.type}\` animation.`
180
+ });
181
+ for (const key of Object.keys(timing)) {
182
+ if (!JS_ORCHESTRATION_KEYS.has(key)) continue;
183
+ notes.dropped.push({
184
+ at: `${at}.${key}`,
185
+ reason: `CSS has no \`${key}\`: each element's transition plays on its own.`
186
+ });
187
+ }
188
+ if (timing.delay !== void 0) out.delay = cssTimeOf({
189
+ value: timing.delay,
190
+ at: `${at}.delay`,
191
+ notes
192
+ });
193
+ if (Array.isArray(timing.times)) out.times = timing.times;
194
+ if (timing.type === "spring") {
195
+ const spring = {};
196
+ for (const key of [
197
+ "stiffness",
198
+ "damping",
199
+ "mass",
200
+ "bounce"
201
+ ]) if (timing[key] !== void 0) spring[key] = timing[key];
202
+ for (const key of ["duration", "visualDuration"]) {
203
+ if (timing[key] === void 0) continue;
204
+ spring[key] = cssTimeOf({
205
+ value: timing[key],
206
+ at: `${at}.${key}`,
207
+ notes
208
+ });
209
+ }
210
+ out.easing = { spring };
211
+ return out;
212
+ }
213
+ if (timing.duration !== void 0) out.duration = cssTimeOf({
214
+ value: timing.duration,
215
+ at: `${at}.duration`,
216
+ notes
217
+ });
218
+ if (timing.ease !== void 0) {
219
+ const easing = cssEaseOf({
220
+ ease: timing.ease,
221
+ duration: out.duration
222
+ });
223
+ if (easing === void 0) notes.approximated.push({
224
+ at: `${at}.ease`,
225
+ reason: "CSS has one easing per transition, so it plays `motion/react`'s default instead."
226
+ });
227
+ else out.easing = easing;
228
+ }
229
+ if (Object.keys(timing).some((key) => JS_TIMING_KEYS.has(key) && key !== "delay" && timing[key] !== void 0) && out.duration === void 0 && out.easing === void 0 && out.times === void 0) out.duration = "300ms";
230
+ return out;
231
+ }
232
+ /** A time as CSS: a number is seconds, as `motion/react` reads it. A token or a CSS time stays. */
233
+ function cssTimeOf({ value, at, notes }) {
234
+ if (typeof value === "number") return `${Math.round(value * 1e3)}ms`;
235
+ if (typeof value === "string" || isRef(value)) return value;
236
+ notes.refused.push({
237
+ at,
238
+ reason: "This is no time."
239
+ });
240
+ }
241
+ /** A JS `ease` as a CSS easing, or `undefined` for a list of them, one per keyframe. */
242
+ function cssEaseOf({ ease, duration }) {
243
+ if (isRef(ease)) return ease;
244
+ return cssEasing({
245
+ ease,
246
+ duration: (typeof duration === "string" ? millisecondsOf(duration) : void 0) ?? 300
247
+ });
248
+ }
249
+ function millisecondsOf(time) {
250
+ const match = CSS_TIME.exec(time.trim());
251
+ if (!match) return void 0;
252
+ return match[2] === "ms" ? Number(match[1]) : Number(match[1]) * 1e3;
253
+ }
254
+ function jsOf(body, notes) {
255
+ const out = { runtime: "js" };
256
+ if (body.keyframes !== void 0) {
257
+ if (STATES.some((state) => body[state] !== void 0) || body.variants !== void 0) {
258
+ notes.refused.push({
259
+ at: "keyframes",
260
+ reason: "A JS preset plays keyframes as a state, so a preset with both has no single JS form."
261
+ });
262
+ return out;
263
+ }
264
+ Object.assign(out, jsKeyframesOf(body, notes));
265
+ }
266
+ for (const state of STATES) {
267
+ if (body[state] === void 0) continue;
268
+ out[state] = jsStateOf({
269
+ target: body[state],
270
+ at: state,
271
+ notes
272
+ });
273
+ }
274
+ if (isPlainObject(body.variants)) out.variants = Object.fromEntries(Object.entries(body.variants).map(([name, target]) => [name, jsStateOf({
275
+ target,
276
+ at: `variants.${name}`,
277
+ notes
278
+ })]));
279
+ if (isPlainObject(body.transition)) out.transition = jsTransitionOf({
280
+ transition: body.transition,
281
+ at: "transition",
282
+ notes
283
+ });
284
+ if (body.reducedMotion !== void 0) out.reducedMotion = body.reducedMotion;
285
+ return out;
286
+ }
287
+ function jsStateOf({ target, at, notes }) {
288
+ if (!isPlainObject(target)) return target;
289
+ if (!isPlainObject(target.transition)) return target;
290
+ return {
291
+ ...target,
292
+ transition: jsTransitionOf({
293
+ transition: target.transition,
294
+ at: `${at}.transition`,
295
+ notes
296
+ })
297
+ };
298
+ }
299
+ function jsTransitionOf({ transition, at, notes }) {
300
+ const { properties, ...timing } = transition;
301
+ const out = jsTimingOf({
302
+ timing,
303
+ at,
304
+ notes
305
+ });
306
+ if (isPlainObject(properties)) for (const [key, value] of Object.entries(properties)) {
307
+ if (!isPlainObject(value)) continue;
308
+ out[key] = jsTimingOf({
309
+ timing: value,
310
+ at: `${at}.properties.${key}`,
311
+ notes
312
+ });
313
+ }
314
+ return out;
315
+ }
316
+ function jsTimingOf({ timing, at, notes }) {
317
+ const out = {};
318
+ if (timing.delay !== void 0) out.delay = secondsOf({
319
+ value: timing.delay,
320
+ at: `${at}.delay`,
321
+ notes
322
+ });
323
+ if (Array.isArray(timing.times)) out.times = timing.times;
324
+ const easing = timing.easing;
325
+ if (isPlainObject(easing) && isPlainObject(easing.spring)) {
326
+ out.type = "spring";
327
+ for (const [key, value] of Object.entries(easing.spring)) out[key] = key === "duration" || key === "visualDuration" ? secondsOf({
328
+ value,
329
+ at: `${at}.easing.spring.${key}`,
330
+ notes
331
+ }) : value;
332
+ if (timing.duration !== void 0) notes.approximated.push({
333
+ at: `${at}.duration`,
334
+ reason: "`motion/react` plays a spring for as long as the spring takes, so a duration beside it isn't kept."
335
+ });
336
+ return out;
337
+ }
338
+ if (timing.duration !== void 0) out.duration = secondsOf({
339
+ value: timing.duration,
340
+ at: `${at}.duration`,
341
+ notes
342
+ });
343
+ if (easing !== void 0) out.ease = motionEaseOf({
344
+ easing,
345
+ at: `${at}.easing`,
346
+ notes
347
+ });
348
+ return out;
349
+ }
350
+ /** A CSS time as `motion/react` reads it, in seconds. A token stays, since its value is read to
351
+ * seconds when the preset is built. */
352
+ function secondsOf({ value, at, notes }) {
353
+ if (isRef(value)) return value;
354
+ if (typeof value === "string") {
355
+ const ms = millisecondsOf(value);
356
+ if (ms !== void 0) return ms / 1e3;
357
+ }
358
+ notes.refused.push({
359
+ at,
360
+ reason: `\`motion/react\` reads a time as seconds, and \`${String(value)}\` is no plain time.`
361
+ });
362
+ }
363
+ /** A CSS easing as `motion/react`'s `ease`. A token stays, and is read when the preset is built. */
364
+ function motionEaseOf({ easing, at, notes }) {
365
+ if (isRef(easing)) return easing;
366
+ if (typeof easing === "string") {
367
+ const text = easing.trim();
368
+ if (MOTION_EASING_NAMES.has(text)) return text;
369
+ const keyword = motionOfCssKeyword(text);
370
+ if (keyword !== void 0) return typeof keyword === "string" ? keyword : [...keyword];
371
+ const curve = bezierPoints(text);
372
+ if (curve) return [...curve];
373
+ }
374
+ notes.refused.push({
375
+ at,
376
+ reason: `\`motion/react\` has no easing for \`${String(easing)}\`.`
377
+ });
378
+ }
379
+ /** CSS `@keyframes` as a JS preset's `animate`: one list per property, at the keyframes' offsets. */
380
+ function jsKeyframesOf(body, notes) {
381
+ const keyframes = isPlainObject(body.keyframes) ? body.keyframes : {};
382
+ const frames = Object.entries(keyframes).map(([offset, values]) => ({
383
+ at: offsetOf(offset),
384
+ values
385
+ })).filter((frame) => frame.at !== void 0 && isPlainObject(frame.values)).sort((a, b) => a.at - b.at);
386
+ if (frames.length !== Object.keys(keyframes).length || frames.length < 2) {
387
+ notes.refused.push({
388
+ at: "keyframes",
389
+ reason: "A JS preset needs keyframes at `from`, `to` or percentages, at least two of them."
390
+ });
391
+ return {};
392
+ }
393
+ const properties = new Set(frames.flatMap((frame) => Object.keys(frame.values)));
394
+ const animate = {};
395
+ for (const property of properties) {
396
+ const values = frames.map((frame) => frame.values[property]);
397
+ if (values.some((value) => value === void 0)) {
398
+ notes.refused.push({
399
+ at: `keyframes.${property}`,
400
+ reason: `\`${property}\` isn't set at every keyframe, which \`motion/react\`'s keyframe list needs.`
401
+ });
402
+ continue;
403
+ }
404
+ animate[camelCase(property)] = values;
405
+ }
406
+ const transition = { times: frames.map((frame) => frame.at) };
407
+ if (body.duration !== void 0) transition.duration = secondsOf({
408
+ value: body.duration,
409
+ at: "duration",
410
+ notes
411
+ });
412
+ if (body.delay !== void 0) transition.delay = secondsOf({
413
+ value: body.delay,
414
+ at: "delay",
415
+ notes
416
+ });
417
+ if (body.timingFunction !== void 0) transition.ease = motionEaseOf({
418
+ easing: body.timingFunction,
419
+ at: "timingFunction",
420
+ notes
421
+ });
422
+ const count = body.iterationCount;
423
+ if (count === "infinite") notes.refused.push({
424
+ at: "iterationCount",
425
+ reason: "`motion/react` repeats forever with `Infinity`, which a stored preset can't hold."
426
+ });
427
+ else if (count !== void 0) {
428
+ const times = Number(count);
429
+ if (!Number.isInteger(times) || times < 1) notes.refused.push({
430
+ at: "iterationCount",
431
+ reason: "`motion/react` repeats a whole number of times."
432
+ });
433
+ else if (times > 1) transition.repeat = times - 1;
434
+ }
435
+ if (body.direction === "alternate") transition.repeatType = "reverse";
436
+ else if (body.direction !== void 0 && body.direction !== "normal") notes.refused.push({
437
+ at: "direction",
438
+ reason: `\`motion/react\` plays keyframes forwards, or back and forth, so it has no \`${String(body.direction)}\`.`
439
+ });
440
+ if (body.fillMode === "none" || body.fillMode === "backwards") notes.approximated.push({
441
+ at: "fillMode",
442
+ reason: "`motion/react` holds the last keyframe once it ends."
443
+ });
444
+ return {
445
+ animate,
446
+ transition
447
+ };
448
+ }
449
+ /** A keyframe's offset from 0 to 1, from `from`, `to` or a percentage. */
450
+ function offsetOf(offset) {
451
+ const text = offset.trim();
452
+ if (text === "from") return 0;
453
+ if (text === "to") return 1;
454
+ const percent = /^(\d+(?:\.\d+)?)%$/.exec(text);
455
+ if (!percent) return void 0;
456
+ const value = Number(percent[1]) / 100;
457
+ return value >= 0 && value <= 1 ? value : void 0;
458
+ }
459
+ const camelCase = (property) => property.startsWith("--") ? property : property.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
460
+ /** What converting the preset at `path` would do, without doing it. */
461
+ function planMotionConversion({ config, path, to }) {
462
+ const body = config.resolve("motion", path)?.toJSON();
463
+ if (!body) reject("NOT_FOUND", `There is no motion preset "${path}".`);
464
+ const link = config.borrowedFrom("motion", path);
465
+ const conversion = convertMotion({
466
+ body,
467
+ to
468
+ });
469
+ return {
470
+ path,
471
+ to,
472
+ ...link === void 0 ? {} : { link },
473
+ dropped: conversion.dropped,
474
+ approximated: conversion.approximated,
475
+ refused: conversion.refused,
476
+ ...conversion.refused.length > 0 ? { blocked: refusal({
477
+ path,
478
+ to,
479
+ refused: conversion.refused
480
+ }) } : {}
481
+ };
482
+ }
483
+ /**
484
+ * The preset at `path` rewritten for `to`. A preset of this config's own is replaced by its
485
+ * conversion. One borrowed from a linked system takes `runtime: to` on the link, which converts the
486
+ * source's preset as it reads, so it keeps following what the source changes.
487
+ */
488
+ function convertMotionPreset({ config, path, to }) {
489
+ const plan = planMotionConversion({
490
+ config,
491
+ path,
492
+ to
493
+ });
494
+ if (plan.blocked !== void 0) reject("UNSUPPORTED", plan.blocked);
495
+ const body = config.resolve("motion", path)?.toJSON();
496
+ if (body.runtime === to) return config;
497
+ if (plan.link !== void 0) return config.apply({
498
+ kind: "motion",
499
+ operation: "update",
500
+ path,
501
+ data: { runtime: to }
502
+ });
503
+ return config.apply({
504
+ kind: "motion",
505
+ operation: "replace",
506
+ path,
507
+ data: convertMotion({
508
+ body,
509
+ to
510
+ }).body
511
+ });
512
+ }
513
+ function refusal({ path, to, refused }) {
514
+ return `"${path}" can't be converted to ${to === "css" ? "CSS" : "JS"}. ${refused.map((note) => `\`${note.at}\`: ${note.reason}`).join(" ")}`;
515
+ }
516
+ //#endregion
517
+ export { convertMotion, convertMotionPreset, planMotionConversion };
@@ -1,4 +1,5 @@
1
1
  import { Config } from "../../framework/Config.js";
2
+ import { ReducedMotion } from "./reduced-motion.js";
2
3
 
3
4
  //#region src/entities/system/motion-presets.d.ts
4
5
  /**
@@ -23,10 +24,17 @@ interface MotionPresetToken {
23
24
  declare function jsMotionPresets(config: Config): Record<string, Record<string, unknown>>;
24
25
  /** Every JS preset a style rule writes in place, by its id, resolved as {@link jsMotionPresets} are. */
25
26
  declare function inlineMotionProps(config: Config): Record<string, Record<string, unknown>>;
27
+ /** The package JS motion plays through. A system that plays all of its motion as CSS imports none of it. */
28
+ declare const MOTION_PACKAGE = "motion";
26
29
  /** Whether anything in the system animates through `motion/react`: a JS preset, named or written in
27
30
  * place in a style rule. */
28
31
  declare function hasJsMotion(config: Config): boolean;
29
32
  /** The tokens JS presets name that a preset cannot carry as one value, for the build to report. */
30
33
  declare function motionPresetTokens(config: Config): MotionPresetToken[];
34
+ /**
35
+ * Each preset's own `reducedMotion`, the default for a layer that plays it and sets none, by path:
36
+ * the named presets of either runtime, and the JS presets a style rule writes in place by their id.
37
+ */
38
+ declare const presetReducedMotions: (config: Config) => Readonly<Record<string, ReducedMotion>>;
31
39
  //#endregion
32
- export { MotionPresetToken, hasJsMotion, inlineMotionProps, jsMotionPresets, motionPresetTokens };
40
+ export { MOTION_PACKAGE, MotionPresetToken, hasJsMotion, inlineMotionProps, jsMotionPresets, motionPresetTokens, presetReducedMotions };
@@ -1,7 +1,9 @@
1
1
  import { collectRefs, isRef, kindOf, pathOf } from "../../framework/utils/refs.js";
2
2
  import { composedValueFormOf } from "./composed-values.js";
3
3
  import { createSliceMemo } from "../../framework/memo.js";
4
+ import { MOTION_EASING_NAMES, bezierPoints, motionOfCssKeyword } from "./easings.js";
4
5
  import { inlineMotions } from "./motion-variants.js";
6
+ import { isReducedMotion } from "./reduced-motion.js";
5
7
  import { jsMotionPropFields } from "./Motion.js";
6
8
  import { tokenValueRow } from "./token-values.js";
7
9
  import { renderValue } from "./value-render.js";
@@ -21,34 +23,6 @@ function motionValue(css) {
21
23
  if (time) return time[2] === "ms" ? Number(time[1]) / 1e3 : Number(time[1]);
22
24
  return BARE_NUMBER.test(text) ? Number(text) : text;
23
25
  }
24
- /** The easings `motion/react` names for itself, which a preset may write as they are. */
25
- const MOTION_EASINGS = new Set([
26
- "linear",
27
- "easeIn",
28
- "easeOut",
29
- "easeInOut",
30
- "circIn",
31
- "circOut",
32
- "circInOut",
33
- "backIn",
34
- "backOut",
35
- "backInOut",
36
- "anticipate"
37
- ]);
38
- /** CSS's easing keywords, as `motion/react` spells them. `ease` has no name there, so it is its curve. */
39
- const CSS_EASINGS = {
40
- linear: "linear",
41
- "ease-in": "easeIn",
42
- "ease-out": "easeOut",
43
- "ease-in-out": "easeInOut",
44
- ease: [
45
- .25,
46
- .1,
47
- .25,
48
- 1
49
- ]
50
- };
51
- const CUBIC_BEZIER = /^cubic-bezier\(\s*([^,\s]+)\s*,\s*([^,\s]+)\s*,\s*([^,\s]+)\s*,\s*([^,\s)]+)\s*\)$/;
52
26
  /**
53
27
  * An `ease` as `motion/react` reads it: one of its own names, a CSS keyword as its name, a
54
28
  * `cubic-bezier(a, b, c, d)` as `[a, b, c, d]`, a curve already written as four numbers, or a list of
@@ -57,15 +31,11 @@ const CUBIC_BEZIER = /^cubic-bezier\(\s*([^,\s]+)\s*,\s*([^,\s]+)\s*,\s*([^,\s]+
57
31
  function motionEasing(value) {
58
32
  if (typeof value === "string") {
59
33
  const text = value.trim();
60
- if (MOTION_EASINGS.has(text)) return text;
61
- if (Object.hasOwn(CSS_EASINGS, text)) {
62
- const easing = CSS_EASINGS[text];
63
- return Array.isArray(easing) ? [...easing] : easing;
64
- }
65
- const curve = CUBIC_BEZIER.exec(text);
66
- if (!curve) return void 0;
67
- const points = curve.slice(1).map(Number);
68
- return points.every(Number.isFinite) ? points : void 0;
34
+ if (MOTION_EASING_NAMES.has(text)) return text;
35
+ const keyword = motionOfCssKeyword(text);
36
+ if (keyword !== void 0) return typeof keyword === "string" ? keyword : [...keyword];
37
+ const curve = bezierPoints(text);
38
+ return curve ? [...curve] : void 0;
69
39
  }
70
40
  if (Array.isArray(value)) {
71
41
  if (value.length === 4 && value.every((n) => typeof n === "number")) return value;
@@ -181,6 +151,7 @@ function resolveBody(config, preset, body, tokens) {
181
151
  });
182
152
  if (resolved) props[field] = resolved.value;
183
153
  }
154
+ if (isReducedMotion(body.reducedMotion)) props.reducedMotion = body.reducedMotion;
184
155
  return props;
185
156
  }
186
157
  /**
@@ -225,6 +196,8 @@ function jsMotionPresets(config) {
225
196
  function inlineMotionProps(config) {
226
197
  return resolveJsMotionPresets(config).inline;
227
198
  }
199
+ /** The package JS motion plays through. A system that plays all of its motion as CSS imports none of it. */
200
+ const MOTION_PACKAGE = "motion";
228
201
  /** Whether anything in the system animates through `motion/react`: a JS preset, named or written in
229
202
  * place in a style rule. */
230
203
  function hasJsMotion(config) {
@@ -235,5 +208,21 @@ function hasJsMotion(config) {
235
208
  function motionPresetTokens(config) {
236
209
  return resolveJsMotionPresets(config).tokens;
237
210
  }
211
+ /**
212
+ * Each preset's own `reducedMotion`, the default for a layer that plays it and sets none, by path:
213
+ * the named presets of either runtime, and the JS presets a style rule writes in place by their id.
214
+ */
215
+ const presetReducedMotions = createSliceMemo({
216
+ kinds: ["motion", "component"],
217
+ compute: (config) => {
218
+ const out = {};
219
+ for (const preset of config.list("motion")) {
220
+ const { reducedMotion } = preset.toJSON();
221
+ if (isReducedMotion(reducedMotion)) out[preset.path] = reducedMotion;
222
+ }
223
+ for (const entry of inlineMotions(config).valid) if (isReducedMotion(entry.body.reducedMotion)) out[entry.id] = entry.body.reducedMotion;
224
+ return out;
225
+ }
226
+ });
238
227
  //#endregion
239
- export { hasJsMotion, inlineMotionProps, jsMotionPresets, motionPresetTokens };
228
+ export { MOTION_PACKAGE, hasJsMotion, inlineMotionProps, jsMotionPresets, motionPresetTokens, presetReducedMotions, resolvePresetValue };