@enact-ui/animate 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/api-schema.json +206 -0
  2. package/dist/components/CountUp.d.ts +84 -0
  3. package/dist/components/CountUp.d.ts.map +1 -0
  4. package/dist/components/CountUp.js +68 -0
  5. package/dist/components/CountUp.js.map +1 -0
  6. package/dist/components/MotionDiv.d.ts +159 -0
  7. package/dist/components/MotionDiv.d.ts.map +1 -0
  8. package/dist/components/MotionDiv.js +162 -0
  9. package/dist/components/MotionDiv.js.map +1 -0
  10. package/dist/components/StaggerContainer.d.ts +136 -0
  11. package/dist/components/StaggerContainer.d.ts.map +1 -0
  12. package/dist/components/StaggerContainer.js +166 -0
  13. package/dist/components/StaggerContainer.js.map +1 -0
  14. package/dist/hooks/use-component-animation.d.ts +156 -0
  15. package/dist/hooks/use-component-animation.d.ts.map +1 -0
  16. package/dist/hooks/use-component-animation.js +231 -0
  17. package/dist/hooks/use-component-animation.js.map +1 -0
  18. package/dist/hooks/use-count-up.d.ts +111 -0
  19. package/dist/hooks/use-count-up.d.ts.map +1 -0
  20. package/dist/hooks/use-count-up.js +246 -0
  21. package/dist/hooks/use-count-up.js.map +1 -0
  22. package/dist/hooks/use-draw-path.d.ts +96 -0
  23. package/dist/hooks/use-draw-path.d.ts.map +1 -0
  24. package/dist/hooks/use-draw-path.js +227 -0
  25. package/dist/hooks/use-draw-path.js.map +1 -0
  26. package/dist/hooks/use-motion-preset.d.ts.map +1 -1
  27. package/dist/hooks/use-motion-preset.js +17 -16
  28. package/dist/hooks/use-motion-preset.js.map +1 -1
  29. package/dist/hooks/use-stagger.d.ts +174 -0
  30. package/dist/hooks/use-stagger.d.ts.map +1 -0
  31. package/dist/hooks/use-stagger.js +256 -0
  32. package/dist/hooks/use-stagger.js.map +1 -0
  33. package/dist/index.d.ts +17 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +2442 -26
  36. package/dist/index.js.map +1 -1
  37. package/dist/index.mjs +2335 -25
  38. package/dist/index.mjs.map +1 -1
  39. package/dist/presets/component-presets.d.ts +246 -0
  40. package/dist/presets/component-presets.d.ts.map +1 -0
  41. package/dist/presets/component-presets.js +472 -0
  42. package/dist/presets/component-presets.js.map +1 -0
  43. package/dist/presets/micro-interactions.d.ts +451 -0
  44. package/dist/presets/micro-interactions.d.ts.map +1 -0
  45. package/dist/presets/micro-interactions.js +856 -0
  46. package/dist/presets/micro-interactions.js.map +1 -0
  47. package/dist/presets/motion-presets.d.ts.map +1 -1
  48. package/dist/presets/motion-presets.js +0 -1
  49. package/dist/presets/motion-presets.js.map +1 -1
  50. package/dist/presets/motion-styles.d.ts +186 -0
  51. package/dist/presets/motion-styles.d.ts.map +1 -0
  52. package/dist/presets/motion-styles.js +204 -0
  53. package/dist/presets/motion-styles.js.map +1 -0
  54. package/dist/presets/stagger-presets.d.ts +378 -0
  55. package/dist/presets/stagger-presets.d.ts.map +1 -0
  56. package/dist/presets/stagger-presets.js +582 -0
  57. package/dist/presets/stagger-presets.js.map +1 -0
  58. package/dist/showcase/motion-presets.demo.d.ts +25 -0
  59. package/dist/showcase/motion-presets.demo.d.ts.map +1 -0
  60. package/dist/showcase/motion-presets.demo.js +96 -0
  61. package/dist/showcase/motion-presets.demo.js.map +1 -0
  62. package/dist/showcase/motion-presets.story.d.ts +37 -0
  63. package/dist/showcase/motion-presets.story.d.ts.map +1 -0
  64. package/dist/showcase/motion-presets.story.js +151 -0
  65. package/dist/showcase/motion-presets.story.js.map +1 -0
  66. package/dist/utils/easing.d.ts +294 -0
  67. package/dist/utils/easing.d.ts.map +1 -0
  68. package/dist/utils/easing.js +265 -0
  69. package/dist/utils/easing.js.map +1 -0
  70. package/dist/utils/reduced-motion.d.ts +322 -0
  71. package/dist/utils/reduced-motion.d.ts.map +1 -0
  72. package/dist/utils/reduced-motion.js +362 -0
  73. package/dist/utils/reduced-motion.js.map +1 -0
  74. package/dist/utils/select-preset.d.ts +186 -0
  75. package/dist/utils/select-preset.d.ts.map +1 -0
  76. package/dist/utils/select-preset.js +320 -0
  77. package/dist/utils/select-preset.js.map +1 -0
  78. package/dist/utils/spring-configs.d.ts +187 -0
  79. package/dist/utils/spring-configs.d.ts.map +1 -0
  80. package/dist/utils/spring-configs.js +169 -0
  81. package/dist/utils/spring-configs.js.map +1 -0
  82. package/package.json +4 -3
@@ -0,0 +1,856 @@
1
+ // Copyright (c) 2026 Amsterdam Data Labs
2
+ import { getMotionStyle, msToSeconds } from "./motion-styles";
3
+ /**
4
+ * Checks if the user prefers reduced motion.
5
+ * Returns true if reduced motion is preferred, false otherwise.
6
+ *
7
+ * @returns Whether reduced motion is preferred
8
+ */
9
+ function prefersReducedMotion() {
10
+ if (typeof window === "undefined")
11
+ return false;
12
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
13
+ }
14
+ /**
15
+ * Returns empty props for reduced motion scenarios.
16
+ * Animations are disabled while maintaining component functionality.
17
+ */
18
+ function getReducedMotionProps() {
19
+ return {};
20
+ }
21
+ // =============================================================================
22
+ // BUTTON PRESS PRESET
23
+ // =============================================================================
24
+ /**
25
+ * Button Press Micro-Interaction
26
+ *
27
+ * Creates a subtle press effect where the element scales down slightly when clicked
28
+ * and springs back to its original size on release. Provides tactile feedback for
29
+ * interactive elements.
30
+ *
31
+ * @ai-hint Use buttonPress for clickable elements like buttons, cards, or list items
32
+ * that need tactile feedback. Best for primary actions and interactive controls.
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * import { motion } from "motion/react";
37
+ * import { buttonPress } from "@enact-ui/animate";
38
+ *
39
+ * function Button({ children }) {
40
+ * const props = buttonPress.getProps("standard");
41
+ * return <motion.button {...props}>{children}</motion.button>;
42
+ * }
43
+ * ```
44
+ */
45
+ export const buttonPress = {
46
+ name: "buttonPress",
47
+ description: "Scale down slightly on press, spring back on release",
48
+ useCase: ["buttons", "clickable cards", "list items", "interactive elements", "call-to-action"],
49
+ getProps: (style) => {
50
+ if (prefersReducedMotion())
51
+ return getReducedMotionProps();
52
+ const config = getMotionStyle(style);
53
+ const scaleAmount = style === "subtle" ? 0.98 : style === "playful" ? 0.92 : 0.95;
54
+ return {
55
+ whileTap: {
56
+ scale: scaleAmount,
57
+ },
58
+ whileHover: {
59
+ scale: style === "subtle" ? 1.01 : 1.02,
60
+ },
61
+ transition: {
62
+ type: "spring",
63
+ stiffness: config.spring.stiffness,
64
+ damping: config.spring.damping,
65
+ mass: config.spring.mass,
66
+ },
67
+ };
68
+ },
69
+ };
70
+ // =============================================================================
71
+ // CARD HOVER PRESET
72
+ // =============================================================================
73
+ /**
74
+ * Card Hover Micro-Interaction
75
+ *
76
+ * Creates a subtle lift effect with shadow enhancement when hovering over card-like
77
+ * elements. Provides visual depth and indicates interactivity.
78
+ *
79
+ * @ai-hint Use cardHover for cards, tiles, or panels that are clickable or expandable.
80
+ * Creates a "lifting" effect that suggests the element can be interacted with.
81
+ *
82
+ * @example
83
+ * ```tsx
84
+ * import { motion } from "motion/react";
85
+ * import { cardHover } from "@enact-ui/animate";
86
+ *
87
+ * function Card({ children }) {
88
+ * const props = cardHover.getProps("standard");
89
+ * return <motion.article {...props}>{children}</motion.article>;
90
+ * }
91
+ * ```
92
+ */
93
+ export const cardHover = {
94
+ name: "cardHover",
95
+ description: "Subtle lift with shadow enhancement on hover",
96
+ useCase: ["cards", "tiles", "panels", "content blocks", "media items", "product listings"],
97
+ getProps: (style) => {
98
+ if (prefersReducedMotion())
99
+ return getReducedMotionProps();
100
+ const config = getMotionStyle(style);
101
+ const liftAmount = style === "subtle" ? -2 : style === "playful" ? -8 : -4;
102
+ const shadowIntensity = style === "subtle" ? 0.08 : style === "playful" ? 0.2 : 0.12;
103
+ return {
104
+ whileHover: {
105
+ y: liftAmount,
106
+ boxShadow: `0 ${Math.abs(liftAmount) * 2}px ${Math.abs(liftAmount) * 4}px rgba(0, 0, 0, ${shadowIntensity})`,
107
+ },
108
+ transition: {
109
+ type: "spring",
110
+ stiffness: config.spring.stiffness,
111
+ damping: config.spring.damping,
112
+ mass: config.spring.mass,
113
+ },
114
+ };
115
+ },
116
+ };
117
+ // =============================================================================
118
+ // ICON SPIN PRESET
119
+ // =============================================================================
120
+ /**
121
+ * Icon Spin Micro-Interaction
122
+ *
123
+ * Performs a full 360-degree rotation, ideal for refresh icons, loading indicators,
124
+ * or interactive feedback on icon clicks.
125
+ *
126
+ * @ai-hint Use iconSpin for refresh buttons, sync icons, or loading states.
127
+ * Good for indicating an action is in progress or for playful icon interactions.
128
+ *
129
+ * @example
130
+ * ```tsx
131
+ * import { motion } from "motion/react";
132
+ * import { iconSpin } from "@enact-ui/animate";
133
+ *
134
+ * function RefreshButton() {
135
+ * const props = iconSpin.getProps("standard");
136
+ * return (
137
+ * <motion.button {...props}>
138
+ * <RefreshIcon />
139
+ * </motion.button>
140
+ * );
141
+ * }
142
+ * ```
143
+ */
144
+ export const iconSpin = {
145
+ name: "iconSpin",
146
+ description: "360 degree rotation animation",
147
+ useCase: ["refresh buttons", "sync icons", "loading indicators", "settings icons", "interactive icons"],
148
+ getProps: (style) => {
149
+ if (prefersReducedMotion())
150
+ return getReducedMotionProps();
151
+ const config = getMotionStyle(style);
152
+ return {
153
+ whileTap: {
154
+ rotate: 360,
155
+ },
156
+ whileHover: {
157
+ rotate: style === "playful" ? 15 : 5,
158
+ },
159
+ transition: {
160
+ type: "spring",
161
+ stiffness: config.spring.stiffness * 0.8,
162
+ damping: config.spring.damping,
163
+ mass: config.spring.mass,
164
+ },
165
+ };
166
+ },
167
+ };
168
+ // =============================================================================
169
+ // PULSE PRESET
170
+ // =============================================================================
171
+ /**
172
+ * Pulse Micro-Interaction
173
+ *
174
+ * Creates a breathing/pulsing scale animation. Can be used for attention-grabbing
175
+ * elements, loading states, or to indicate something is active/live.
176
+ *
177
+ * @ai-hint Use pulse for notifications, live indicators, or elements needing attention.
178
+ * The breathing effect draws the eye without being too distracting.
179
+ *
180
+ * @example
181
+ * ```tsx
182
+ * import { motion } from "motion/react";
183
+ * import { pulse } from "@enact-ui/animate";
184
+ *
185
+ * function LiveIndicator() {
186
+ * const props = pulse.getProps("standard");
187
+ * return <motion.span {...props} className="live-dot" />;
188
+ * }
189
+ * ```
190
+ */
191
+ export const pulse = {
192
+ name: "pulse",
193
+ description: "Scale pulse animation with breathing effect",
194
+ useCase: ["notifications", "live indicators", "attention grabbers", "status badges", "loading states"],
195
+ getProps: (style) => {
196
+ if (prefersReducedMotion())
197
+ return getReducedMotionProps();
198
+ const config = getMotionStyle(style);
199
+ const scaleRange = style === "subtle" ? 1.03 : style === "playful" ? 1.12 : 1.06;
200
+ return {
201
+ initial: {
202
+ scale: 1,
203
+ },
204
+ animate: {
205
+ scale: [1, scaleRange, 1],
206
+ },
207
+ transition: {
208
+ duration: msToSeconds(config.duration.slow),
209
+ repeat: Infinity,
210
+ ease: config.easing.interactive,
211
+ },
212
+ };
213
+ },
214
+ };
215
+ // =============================================================================
216
+ // SHAKE PRESET
217
+ // =============================================================================
218
+ /**
219
+ * Shake Micro-Interaction
220
+ *
221
+ * Horizontal shake animation commonly used for error states, invalid input,
222
+ * or to indicate that an action cannot be completed.
223
+ *
224
+ * @ai-hint Use shake for error states, invalid form inputs, or denied actions.
225
+ * The horizontal movement mimics the "no" head shake gesture.
226
+ *
227
+ * @example
228
+ * ```tsx
229
+ * import { motion } from "motion/react";
230
+ * import { shake } from "@enact-ui/animate";
231
+ *
232
+ * function FormInput({ error }) {
233
+ * const props = error ? shake.getProps("standard") : {};
234
+ * return <motion.input {...props} />;
235
+ * }
236
+ * ```
237
+ */
238
+ export const shake = {
239
+ name: "shake",
240
+ description: "Horizontal shake animation for error states",
241
+ useCase: ["error states", "invalid inputs", "denied actions", "form validation", "authentication failures"],
242
+ getProps: (style) => {
243
+ if (prefersReducedMotion())
244
+ return getReducedMotionProps();
245
+ const config = getMotionStyle(style);
246
+ const shakeDistance = style === "subtle" ? 4 : style === "playful" ? 12 : 8;
247
+ return {
248
+ initial: {
249
+ x: 0,
250
+ },
251
+ animate: {
252
+ x: [0, -shakeDistance, shakeDistance, -shakeDistance, shakeDistance, 0],
253
+ },
254
+ transition: {
255
+ duration: msToSeconds(config.duration.fast),
256
+ ease: config.easing.interactive,
257
+ },
258
+ };
259
+ },
260
+ };
261
+ // =============================================================================
262
+ // WIGGLE PRESET
263
+ // =============================================================================
264
+ /**
265
+ * Wiggle Micro-Interaction
266
+ *
267
+ * Playful rotation wiggle animation. Great for drawing attention to elements
268
+ * in a fun, non-intrusive way. Often used in gamified interfaces or
269
+ * celebratory moments.
270
+ *
271
+ * @ai-hint Use wiggle for playful interfaces, gamification, or to draw attention
272
+ * to elements in a fun way. Works well for icons, badges, or decorative elements.
273
+ *
274
+ * @example
275
+ * ```tsx
276
+ * import { motion } from "motion/react";
277
+ * import { wiggle } from "@enact-ui/animate";
278
+ *
279
+ * function AchievementBadge() {
280
+ * const props = wiggle.getProps("playful");
281
+ * return <motion.div {...props}><BadgeIcon /></motion.div>;
282
+ * }
283
+ * ```
284
+ */
285
+ export const wiggle = {
286
+ name: "wiggle",
287
+ description: "Playful rotation wiggle animation",
288
+ useCase: ["playful interfaces", "gamification", "attention indicators", "celebration elements", "decorative icons"],
289
+ getProps: (style) => {
290
+ if (prefersReducedMotion())
291
+ return getReducedMotionProps();
292
+ const config = getMotionStyle(style);
293
+ const wiggleAngle = style === "subtle" ? 3 : style === "playful" ? 12 : 6;
294
+ return {
295
+ whileHover: {
296
+ rotate: [0, -wiggleAngle, wiggleAngle, -wiggleAngle, wiggleAngle, 0],
297
+ },
298
+ transition: {
299
+ duration: msToSeconds(config.duration.normal),
300
+ ease: config.easing.interactive,
301
+ },
302
+ };
303
+ },
304
+ };
305
+ // =============================================================================
306
+ // HEARTBEAT PRESET
307
+ // =============================================================================
308
+ /**
309
+ * Heartbeat Micro-Interaction
310
+ *
311
+ * Double-pulse heartbeat pattern that creates a realistic heartbeat effect.
312
+ * Perfect for health apps, like buttons, or emphasizing something "alive".
313
+ *
314
+ * @ai-hint Use heartbeat for health/fitness apps, like/love buttons, or to indicate
315
+ * something is active/alive. The double-pulse mimics a real heartbeat rhythm.
316
+ *
317
+ * @example
318
+ * ```tsx
319
+ * import { motion } from "motion/react";
320
+ * import { heartbeat } from "@enact-ui/animate";
321
+ *
322
+ * function LikeButton({ isLiked }) {
323
+ * const props = isLiked ? heartbeat.getProps("playful") : {};
324
+ * return <motion.button {...props}><HeartIcon /></motion.button>;
325
+ * }
326
+ * ```
327
+ */
328
+ export const heartbeat = {
329
+ name: "heartbeat",
330
+ description: "Double-pulse heartbeat pattern animation",
331
+ useCase: ["like buttons", "health apps", "love reactions", "favorite indicators", "activity monitors"],
332
+ getProps: (style) => {
333
+ if (prefersReducedMotion())
334
+ return getReducedMotionProps();
335
+ const config = getMotionStyle(style);
336
+ const pulseScale = style === "subtle" ? 1.1 : style === "playful" ? 1.3 : 1.2;
337
+ return {
338
+ initial: {
339
+ scale: 1,
340
+ },
341
+ animate: {
342
+ scale: [1, pulseScale, 1, pulseScale * 0.95, 1],
343
+ },
344
+ transition: {
345
+ duration: msToSeconds(config.duration.slow * 1.5),
346
+ repeat: Infinity,
347
+ repeatDelay: msToSeconds(config.duration.normal),
348
+ ease: config.easing.interactive,
349
+ },
350
+ };
351
+ },
352
+ };
353
+ // =============================================================================
354
+ // RUBBER BAND PRESET
355
+ // =============================================================================
356
+ /**
357
+ * Rubber Band Micro-Interaction
358
+ *
359
+ * Elastic stretch and snap back animation inspired by the classic rubber band effect.
360
+ * Creates a bouncy, playful feel that's perfect for attention-grabbing elements.
361
+ *
362
+ * @ai-hint Use rubberBand for emphasis animations, attention grabbers, or playful
363
+ * interfaces. The elastic effect creates a memorable, tactile impression.
364
+ *
365
+ * @example
366
+ * ```tsx
367
+ * import { motion } from "motion/react";
368
+ * import { rubberBand } from "@enact-ui/animate";
369
+ *
370
+ * function BouncyButton({ children }) {
371
+ * const props = rubberBand.getProps("playful");
372
+ * return <motion.button {...props}>{children}</motion.button>;
373
+ * }
374
+ * ```
375
+ */
376
+ export const rubberBand = {
377
+ name: "rubberBand",
378
+ description: "Elastic stretch and snap back animation",
379
+ useCase: ["emphasis animations", "attention grabbers", "playful buttons", "celebration effects", "interactive feedback"],
380
+ getProps: (style) => {
381
+ if (prefersReducedMotion())
382
+ return getReducedMotionProps();
383
+ const config = getMotionStyle(style);
384
+ const stretchX = style === "subtle" ? 1.05 : style === "playful" ? 1.25 : 1.15;
385
+ const stretchY = style === "subtle" ? 0.95 : style === "playful" ? 0.75 : 0.85;
386
+ return {
387
+ whileTap: {
388
+ scaleX: [1, stretchX, 0.95, 1.05, 1],
389
+ scaleY: [1, stretchY, 1.05, 0.95, 1],
390
+ },
391
+ whileHover: {
392
+ scaleX: 1.02,
393
+ scaleY: 0.98,
394
+ },
395
+ transition: {
396
+ type: "spring",
397
+ stiffness: config.spring.stiffness * 0.6,
398
+ damping: config.spring.damping * 0.5,
399
+ mass: config.spring.mass,
400
+ },
401
+ };
402
+ },
403
+ };
404
+ // =============================================================================
405
+ // SCALE PRESET (Micro-Interaction)
406
+ // =============================================================================
407
+ /**
408
+ * Scale Micro-Interaction
409
+ *
410
+ * Creates a scale animation effect for selection feedback. The element scales up
411
+ * briefly when activated, providing satisfying tactile feedback for toggles,
412
+ * checkboxes, and selection states.
413
+ *
414
+ * @ai-hint Use scale for checkboxes, toggles, radio buttons, or any selection
415
+ * feedback. The scale effect provides clear visual confirmation of state change.
416
+ *
417
+ * @example
418
+ * ```tsx
419
+ * import { motion } from "motion/react";
420
+ * import { scale } from "@enact-ui/animate";
421
+ *
422
+ * function Checkbox({ isSelected }) {
423
+ * const props = isSelected ? scale.getProps("standard") : {};
424
+ * return <motion.div {...props}>{isSelected && <Check />}</motion.div>;
425
+ * }
426
+ * ```
427
+ */
428
+ export const scale = {
429
+ name: "scale",
430
+ description: "Scale up animation for selection feedback",
431
+ useCase: ["checkboxes", "toggles", "radio buttons", "selection states", "activation feedback"],
432
+ getProps: (style) => {
433
+ if (prefersReducedMotion())
434
+ return getReducedMotionProps();
435
+ const config = getMotionStyle(style);
436
+ const scaleAmount = style === "subtle" ? 1.05 : style === "playful" ? 1.2 : 1.1;
437
+ return {
438
+ initial: {
439
+ scale: 0.8,
440
+ opacity: 0,
441
+ },
442
+ animate: {
443
+ scale: [0.8, scaleAmount, 1],
444
+ opacity: 1,
445
+ },
446
+ transition: {
447
+ type: "spring",
448
+ stiffness: config.spring.stiffness,
449
+ damping: config.spring.damping,
450
+ mass: config.spring.mass,
451
+ },
452
+ };
453
+ },
454
+ };
455
+ // =============================================================================
456
+ // SHIMMER PRESET
457
+ // =============================================================================
458
+ /**
459
+ * Shimmer Micro-Interaction
460
+ *
461
+ * Creates a gradient shimmer effect that slides across the element.
462
+ * Perfect for loading skeletons, progress indicators, or placeholder content.
463
+ *
464
+ * @ai-hint Use shimmer for skeleton loading states, progress bars, or placeholder
465
+ * content. Creates a polished "loading" appearance that indicates content is coming.
466
+ *
467
+ * @example
468
+ * ```tsx
469
+ * import { motion } from "motion/react";
470
+ * import { shimmer } from "@enact-ui/animate";
471
+ *
472
+ * function SkeletonCard() {
473
+ * const props = shimmer.getProps("standard");
474
+ * return (
475
+ * <motion.div
476
+ * {...props}
477
+ * className="h-20 bg-gray-200 rounded overflow-hidden relative"
478
+ * style={{
479
+ * background: "linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%)",
480
+ * backgroundSize: "200% 100%",
481
+ * }}
482
+ * />
483
+ * );
484
+ * }
485
+ * ```
486
+ */
487
+ export const shimmer = {
488
+ name: "shimmer",
489
+ description: "Gradient shimmer effect that slides across the element",
490
+ useCase: ["skeleton loading", "placeholder content", "progress indicators", "loading states", "content placeholders"],
491
+ getProps: (style) => {
492
+ if (prefersReducedMotion())
493
+ return getReducedMotionProps();
494
+ const config = getMotionStyle(style);
495
+ const duration = style === "subtle" ? 2 : style === "playful" ? 1.2 : 1.5;
496
+ return {
497
+ initial: {
498
+ backgroundPosition: "200% 0",
499
+ },
500
+ animate: {
501
+ backgroundPosition: ["-200% 0", "200% 0"],
502
+ },
503
+ transition: {
504
+ duration,
505
+ repeat: Infinity,
506
+ ease: config.easing.exit,
507
+ repeatDelay: 0.5,
508
+ },
509
+ };
510
+ },
511
+ };
512
+ // =============================================================================
513
+ // GLOW PULSE PRESET
514
+ // =============================================================================
515
+ /**
516
+ * Glow Pulse Micro-Interaction
517
+ *
518
+ * Creates a glowing pulse effect using box-shadow. Perfect for AI-generated
519
+ * content indicators, special items, or elements that need extra emphasis.
520
+ *
521
+ * @ai-hint Use glowPulse for AI content indicators, featured items, or elements
522
+ * needing premium emphasis. The glow effect creates a "magical" or "special" feel.
523
+ *
524
+ * @example
525
+ * ```tsx
526
+ * import { motion } from "motion/react";
527
+ * import { glowPulse } from "@enact-ui/animate";
528
+ *
529
+ * function AIGeneratedBadge() {
530
+ * const props = glowPulse.getProps("standard");
531
+ * return <motion.span {...props} className="ai-badge">AI</motion.span>;
532
+ * }
533
+ * ```
534
+ */
535
+ export const glowPulse = {
536
+ name: "glowPulse",
537
+ description: "Glowing pulse effect for emphasis and AI indicators",
538
+ useCase: ["AI content indicators", "featured items", "premium elements", "special badges", "magical effects"],
539
+ getProps: (style) => {
540
+ if (prefersReducedMotion())
541
+ return getReducedMotionProps();
542
+ const config = getMotionStyle(style);
543
+ const glowIntensity = style === "subtle" ? 8 : style === "playful" ? 20 : 12;
544
+ const glowColor = "rgba(99, 102, 241, 0.6)"; // Indigo glow
545
+ return {
546
+ initial: {
547
+ boxShadow: `0 0 0 rgba(99, 102, 241, 0)`,
548
+ },
549
+ animate: {
550
+ boxShadow: [`0 0 0 rgba(99, 102, 241, 0)`, `0 0 ${glowIntensity}px ${glowColor}`, `0 0 0 rgba(99, 102, 241, 0)`],
551
+ },
552
+ transition: {
553
+ duration: msToSeconds(config.duration.slow * 1.5),
554
+ repeat: Infinity,
555
+ ease: config.easing.interactive,
556
+ },
557
+ };
558
+ },
559
+ };
560
+ // =============================================================================
561
+ // PROGRESS FILL PRESET
562
+ // =============================================================================
563
+ /**
564
+ * Progress Fill Micro-Interaction
565
+ *
566
+ * Animates width from 0% to the target value. Perfect for progress bars,
567
+ * completion indicators, and skill bars.
568
+ *
569
+ * Note: This preset returns a function that takes the target percentage
570
+ * and returns animation props. Use with scaleX transform for best performance.
571
+ *
572
+ * @ai-hint Use progressFill for progress bars, skill meters, or completion
573
+ * indicators. Animates smoothly from 0 to the target percentage.
574
+ *
575
+ * @example
576
+ * ```tsx
577
+ * import { motion } from "motion/react";
578
+ * import { progressFill } from "@enact-ui/animate";
579
+ *
580
+ * function ProgressBar({ value }: { value: number }) {
581
+ * const props = progressFill.getProps("standard");
582
+ * return (
583
+ * <div className="h-2 bg-gray-200 rounded overflow-hidden">
584
+ * <motion.div
585
+ * {...props}
586
+ * className="h-full bg-blue-500 origin-left"
587
+ * style={{ width: `${value}%` }}
588
+ * />
589
+ * </div>
590
+ * );
591
+ * }
592
+ * ```
593
+ */
594
+ export const progressFill = {
595
+ name: "progressFill",
596
+ description: "Animated fill from 0% to target width for progress bars",
597
+ useCase: ["progress bars", "completion indicators", "skill bars", "loading progress", "data visualization"],
598
+ getProps: (style) => {
599
+ if (prefersReducedMotion())
600
+ return getReducedMotionProps();
601
+ const config = getMotionStyle(style);
602
+ const duration = style === "subtle" ? 0.6 : style === "playful" ? 1.2 : 0.8;
603
+ return {
604
+ initial: {
605
+ scaleX: 0,
606
+ },
607
+ animate: {
608
+ scaleX: 1,
609
+ },
610
+ transition: {
611
+ duration,
612
+ ease: config.easing.exit,
613
+ },
614
+ };
615
+ },
616
+ };
617
+ // =============================================================================
618
+ // PRESET COLLECTION
619
+ // =============================================================================
620
+ /**
621
+ * All available micro-interaction presets indexed by name.
622
+ *
623
+ * @ai-hint Use this collection to iterate over all presets or to access
624
+ * presets by name dynamically. Each preset adapts to the motion style.
625
+ *
626
+ * @example
627
+ * ```tsx
628
+ * import { microInteractions } from "@enact-ui/animate";
629
+ *
630
+ * // Get a specific preset by name
631
+ * const pressProps = microInteractions.buttonPress.getProps("standard");
632
+ *
633
+ * // List all available presets
634
+ * Object.keys(microInteractions).forEach(name => {
635
+ * console.log(microInteractions[name].description);
636
+ * });
637
+ * ```
638
+ */
639
+ // =============================================================================
640
+ // CHECKMARK DRAW PRESET
641
+ // =============================================================================
642
+ /**
643
+ * Checkmark Draw Micro-Interaction
644
+ *
645
+ * Animated checkmark draw effect, commonly used for success states,
646
+ * completed tasks, or confirmation feedback.
647
+ *
648
+ * @ai-hint Use checkmarkDraw for success confirmations, completed checkboxes,
649
+ * or any "done" state indication. The drawing animation provides satisfying feedback.
650
+ *
651
+ * @example
652
+ * ```tsx
653
+ * import { motion } from "motion/react";
654
+ * import { checkmarkDraw } from "@enact-ui/animate";
655
+ *
656
+ * function SuccessIcon() {
657
+ * const props = checkmarkDraw.getProps("standard");
658
+ * return <motion.svg {...props}><path d="M5 13l4 4L19 7" /></motion.svg>;
659
+ * }
660
+ * ```
661
+ */
662
+ export const checkmarkDraw = {
663
+ name: "checkmarkDraw",
664
+ description: "Animated checkmark draw effect for success states",
665
+ useCase: ["success confirmations", "completed tasks", "checkboxes", "form submissions", "achievement unlocks"],
666
+ getProps: (style) => {
667
+ if (prefersReducedMotion())
668
+ return getReducedMotionProps();
669
+ const config = getMotionStyle(style);
670
+ const duration = style === "subtle" ? config.duration.fast : style === "playful" ? config.duration.slow : config.duration.normal;
671
+ return {
672
+ initial: {
673
+ pathLength: 0,
674
+ opacity: 0,
675
+ },
676
+ animate: {
677
+ pathLength: 1,
678
+ opacity: 1,
679
+ },
680
+ transition: {
681
+ duration: msToSeconds(duration),
682
+ ease: config.easing.interactive,
683
+ },
684
+ };
685
+ },
686
+ };
687
+ // =============================================================================
688
+ // SUCCESS FLASH PRESET
689
+ // =============================================================================
690
+ /**
691
+ * Success Flash Micro-Interaction
692
+ *
693
+ * Brief highlight flash animation for success feedback. Creates a quick
694
+ * visual confirmation without being distracting.
695
+ *
696
+ * @ai-hint Use successFlash for brief success highlights, toast notifications,
697
+ * or inline success messages. The flash draws attention without lingering.
698
+ *
699
+ * @example
700
+ * ```tsx
701
+ * import { motion } from "motion/react";
702
+ * import { successFlash } from "@enact-ui/animate";
703
+ *
704
+ * function SuccessMessage() {
705
+ * const props = successFlash.getProps("standard");
706
+ * return <motion.div {...props}>Saved!</motion.div>;
707
+ * }
708
+ * ```
709
+ */
710
+ export const successFlash = {
711
+ name: "successFlash",
712
+ description: "Brief success highlight flash animation",
713
+ useCase: ["success toasts", "inline confirmations", "save indicators", "completion feedback", "achievement notifications"],
714
+ getProps: (style) => {
715
+ if (prefersReducedMotion())
716
+ return getReducedMotionProps();
717
+ const config = getMotionStyle(style);
718
+ const scale = style === "subtle" ? 1.02 : style === "playful" ? 1.08 : 1.05;
719
+ return {
720
+ initial: {
721
+ scale: 1,
722
+ backgroundColor: "transparent",
723
+ },
724
+ animate: {
725
+ scale: [1, scale, 1],
726
+ backgroundColor: ["transparent", "var(--color-surface-success-subtle)", "transparent"],
727
+ },
728
+ transition: {
729
+ duration: msToSeconds(config.duration.fast),
730
+ ease: config.easing.interactive,
731
+ },
732
+ };
733
+ },
734
+ };
735
+ // =============================================================================
736
+ // BADGE POP PRESET
737
+ // =============================================================================
738
+ /**
739
+ * Badge Pop Micro-Interaction
740
+ *
741
+ * Pop animation for badge count changes. Provides satisfying feedback
742
+ * when numbers update, drawing attention to the change.
743
+ *
744
+ * @ai-hint Use badgePop for notification badges, count indicators, or any
745
+ * numeric value that changes. The pop effect highlights the update.
746
+ *
747
+ * @example
748
+ * ```tsx
749
+ * import { motion } from "motion/react";
750
+ * import { badgePop } from "@enact-ui/animate";
751
+ *
752
+ * function NotificationBadge({ count }) {
753
+ * const props = badgePop.getProps("standard");
754
+ * return <motion.span {...props} key={count}>{count}</motion.span>;
755
+ * }
756
+ * ```
757
+ */
758
+ export const badgePop = {
759
+ name: "badgePop",
760
+ description: "Pop animation for badge count changes",
761
+ useCase: ["notification badges", "count indicators", "numeric updates", "inventory changes", "score updates"],
762
+ getProps: (style) => {
763
+ if (prefersReducedMotion())
764
+ return getReducedMotionProps();
765
+ const config = getMotionStyle(style);
766
+ const scale = style === "subtle" ? 1.15 : style === "playful" ? 1.4 : 1.25;
767
+ return {
768
+ initial: {
769
+ scale: 0,
770
+ opacity: 0,
771
+ },
772
+ animate: {
773
+ scale: [0, scale, 1],
774
+ opacity: [0, 1, 1],
775
+ },
776
+ transition: {
777
+ duration: msToSeconds(config.duration.fast),
778
+ ease: config.easing.interactive,
779
+ },
780
+ };
781
+ },
782
+ };
783
+ // =============================================================================
784
+ // PRESET COLLECTION
785
+ // =============================================================================
786
+ export const microInteractions = {
787
+ buttonPress,
788
+ cardHover,
789
+ iconSpin,
790
+ pulse,
791
+ scale,
792
+ shake,
793
+ wiggle,
794
+ heartbeat,
795
+ rubberBand,
796
+ shimmer,
797
+ glowPulse,
798
+ progressFill,
799
+ checkmarkDraw,
800
+ successFlash,
801
+ badgePop,
802
+ };
803
+ /**
804
+ * Gets a micro-interaction preset by name.
805
+ *
806
+ * @param name - The name of the preset to retrieve
807
+ * @returns The micro-interaction preset, or undefined if not found
808
+ *
809
+ * @example
810
+ * ```ts
811
+ * const preset = getMicroInteraction("buttonPress");
812
+ * if (preset) {
813
+ * const props = preset.getProps("standard");
814
+ * }
815
+ * ```
816
+ */
817
+ export function getMicroInteraction(name) {
818
+ return microInteractions[name];
819
+ }
820
+ /**
821
+ * Gets all micro-interaction preset names.
822
+ *
823
+ * @returns Array of all preset names
824
+ *
825
+ * @example
826
+ * ```ts
827
+ * const names = getMicroInteractionNames();
828
+ * // ["buttonPress", "cardHover", "iconSpin", ...]
829
+ * ```
830
+ */
831
+ export function getMicroInteractionNames() {
832
+ return Object.keys(microInteractions);
833
+ }
834
+ /**
835
+ * Finds micro-interaction presets suitable for a given use case.
836
+ *
837
+ * @param useCase - The use case to search for (case-insensitive)
838
+ * @returns Array of presets that match the use case
839
+ *
840
+ * @ai-hint Use this function to find appropriate presets based on your component's
841
+ * purpose. It searches the useCase arrays of all presets for matches.
842
+ *
843
+ * @example
844
+ * ```ts
845
+ * const errorPresets = findPresetsForUseCase("error");
846
+ * // Returns [shake] preset since it includes "error states" in its use cases
847
+ *
848
+ * const buttonPresets = findPresetsForUseCase("button");
849
+ * // Returns [buttonPress, rubberBand] presets
850
+ * ```
851
+ */
852
+ export function findPresetsForUseCase(useCase) {
853
+ const searchTerm = useCase.toLowerCase();
854
+ return Object.values(microInteractions).filter((preset) => preset.useCase.some((uc) => uc.toLowerCase().includes(searchTerm)));
855
+ }
856
+ //# sourceMappingURL=micro-interactions.js.map