@nice2dev/icons 1.0.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 (132) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/LICENSE +21 -0
  3. package/README.md +581 -0
  4. package/dist/cjs/NiceIconPicker.js +387 -0
  5. package/dist/cjs/NiceIconPicker.js.map +1 -0
  6. package/dist/cjs/NiceVectorEditor.js +675 -0
  7. package/dist/cjs/NiceVectorEditor.js.map +1 -0
  8. package/dist/cjs/animationControls.js +690 -0
  9. package/dist/cjs/animationControls.js.map +1 -0
  10. package/dist/cjs/createIcon.js +142 -0
  11. package/dist/cjs/createIcon.js.map +1 -0
  12. package/dist/cjs/figmaExport.js +288 -0
  13. package/dist/cjs/figmaExport.js.map +1 -0
  14. package/dist/cjs/icons/actions.js +257 -0
  15. package/dist/cjs/icons/actions.js.map +1 -0
  16. package/dist/cjs/icons/brand.js +128 -0
  17. package/dist/cjs/icons/brand.js.map +1 -0
  18. package/dist/cjs/icons/business.js +228 -0
  19. package/dist/cjs/icons/business.js.map +1 -0
  20. package/dist/cjs/icons/devtech.js +374 -0
  21. package/dist/cjs/icons/devtech.js.map +1 -0
  22. package/dist/cjs/icons/ecommerce.js +171 -0
  23. package/dist/cjs/icons/ecommerce.js.map +1 -0
  24. package/dist/cjs/icons/files.js +286 -0
  25. package/dist/cjs/icons/files.js.map +1 -0
  26. package/dist/cjs/icons/fintech.js +151 -0
  27. package/dist/cjs/icons/fintech.js.map +1 -0
  28. package/dist/cjs/icons/fun.js +124 -0
  29. package/dist/cjs/icons/fun.js.map +1 -0
  30. package/dist/cjs/icons/health.js +132 -0
  31. package/dist/cjs/icons/health.js.map +1 -0
  32. package/dist/cjs/icons/media.js +219 -0
  33. package/dist/cjs/icons/media.js.map +1 -0
  34. package/dist/cjs/icons/navigation.js +147 -0
  35. package/dist/cjs/icons/navigation.js.map +1 -0
  36. package/dist/cjs/icons/saas.js +141 -0
  37. package/dist/cjs/icons/saas.js.map +1 -0
  38. package/dist/cjs/icons/shapes.js +205 -0
  39. package/dist/cjs/icons/shapes.js.map +1 -0
  40. package/dist/cjs/icons/smarthome.js +141 -0
  41. package/dist/cjs/icons/smarthome.js.map +1 -0
  42. package/dist/cjs/icons/social.js +159 -0
  43. package/dist/cjs/icons/social.js.map +1 -0
  44. package/dist/cjs/icons/ui.js +262 -0
  45. package/dist/cjs/icons/ui.js.map +1 -0
  46. package/dist/cjs/icons/weather.js +187 -0
  47. package/dist/cjs/icons/weather.js.map +1 -0
  48. package/dist/cjs/index.js +362 -0
  49. package/dist/cjs/index.js.map +1 -0
  50. package/dist/cjs/nicetodev-icons.css +1 -0
  51. package/dist/cjs/smilAnimations.js +182 -0
  52. package/dist/cjs/smilAnimations.js.map +1 -0
  53. package/dist/cjs/types.js +47 -0
  54. package/dist/cjs/types.js.map +1 -0
  55. package/dist/esm/NiceIconPicker.js +385 -0
  56. package/dist/esm/NiceIconPicker.js.map +1 -0
  57. package/dist/esm/NiceVectorEditor.js +673 -0
  58. package/dist/esm/NiceVectorEditor.js.map +1 -0
  59. package/dist/esm/animationControls.js +680 -0
  60. package/dist/esm/animationControls.js.map +1 -0
  61. package/dist/esm/createIcon.js +140 -0
  62. package/dist/esm/createIcon.js.map +1 -0
  63. package/dist/esm/figmaExport.js +279 -0
  64. package/dist/esm/figmaExport.js.map +1 -0
  65. package/dist/esm/icons/actions.js +231 -0
  66. package/dist/esm/icons/actions.js.map +1 -0
  67. package/dist/esm/icons/brand.js +124 -0
  68. package/dist/esm/icons/brand.js.map +1 -0
  69. package/dist/esm/icons/business.js +209 -0
  70. package/dist/esm/icons/business.js.map +1 -0
  71. package/dist/esm/icons/devtech.js +342 -0
  72. package/dist/esm/icons/devtech.js.map +1 -0
  73. package/dist/esm/icons/ecommerce.js +154 -0
  74. package/dist/esm/icons/ecommerce.js.map +1 -0
  75. package/dist/esm/icons/files.js +254 -0
  76. package/dist/esm/icons/files.js.map +1 -0
  77. package/dist/esm/icons/fintech.js +136 -0
  78. package/dist/esm/icons/fintech.js.map +1 -0
  79. package/dist/esm/icons/fun.js +110 -0
  80. package/dist/esm/icons/fun.js.map +1 -0
  81. package/dist/esm/icons/health.js +118 -0
  82. package/dist/esm/icons/health.js.map +1 -0
  83. package/dist/esm/icons/media.js +195 -0
  84. package/dist/esm/icons/media.js.map +1 -0
  85. package/dist/esm/icons/navigation.js +132 -0
  86. package/dist/esm/icons/navigation.js.map +1 -0
  87. package/dist/esm/icons/saas.js +127 -0
  88. package/dist/esm/icons/saas.js.map +1 -0
  89. package/dist/esm/icons/shapes.js +182 -0
  90. package/dist/esm/icons/shapes.js.map +1 -0
  91. package/dist/esm/icons/smarthome.js +127 -0
  92. package/dist/esm/icons/smarthome.js.map +1 -0
  93. package/dist/esm/icons/social.js +150 -0
  94. package/dist/esm/icons/social.js.map +1 -0
  95. package/dist/esm/icons/ui.js +233 -0
  96. package/dist/esm/icons/ui.js.map +1 -0
  97. package/dist/esm/icons/weather.js +166 -0
  98. package/dist/esm/icons/weather.js.map +1 -0
  99. package/dist/esm/index.js +25 -0
  100. package/dist/esm/index.js.map +1 -0
  101. package/dist/esm/nicetodev-icons.css +1 -0
  102. package/dist/esm/smilAnimations.js +176 -0
  103. package/dist/esm/smilAnimations.js.map +1 -0
  104. package/dist/esm/types.js +44 -0
  105. package/dist/esm/types.js.map +1 -0
  106. package/dist/types/NiceIconPicker.d.ts +149 -0
  107. package/dist/types/NiceVectorEditor.d.ts +75 -0
  108. package/dist/types/animationControls.d.ts +307 -0
  109. package/dist/types/createIcon.d.ts +76 -0
  110. package/dist/types/figmaExport.d.ts +243 -0
  111. package/dist/types/icons/actions.d.ts +25 -0
  112. package/dist/types/icons/brand.d.ts +3 -0
  113. package/dist/types/icons/business.d.ts +25 -0
  114. package/dist/types/icons/devtech.d.ts +39 -0
  115. package/dist/types/icons/ecommerce.d.ts +16 -0
  116. package/dist/types/icons/files.d.ts +31 -0
  117. package/dist/types/icons/fintech.d.ts +14 -0
  118. package/dist/types/icons/fun.d.ts +13 -0
  119. package/dist/types/icons/health.d.ts +14 -0
  120. package/dist/types/icons/index.d.ts +17 -0
  121. package/dist/types/icons/media.d.ts +23 -0
  122. package/dist/types/icons/navigation.d.ts +14 -0
  123. package/dist/types/icons/saas.d.ts +13 -0
  124. package/dist/types/icons/shapes.d.ts +22 -0
  125. package/dist/types/icons/smarthome.d.ts +13 -0
  126. package/dist/types/icons/social.d.ts +18 -0
  127. package/dist/types/icons/ui.d.ts +28 -0
  128. package/dist/types/icons/weather.d.ts +20 -0
  129. package/dist/types/index.d.ts +69 -0
  130. package/dist/types/smilAnimations.d.ts +102 -0
  131. package/dist/types/types.d.ts +179 -0
  132. package/package.json +86 -0
@@ -0,0 +1,680 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import React, { forwardRef, useState, useRef, useEffect, useImperativeHandle, useMemo, useCallback } from 'react';
3
+
4
+ /* ────────────────────────────────────────────────────────────────────────────
5
+ 2. HELPER FUNCTIONS
6
+ ───────────────────────────────────────────────────────────────────────────── */
7
+ /**
8
+ * Build CSS class for animation
9
+ */
10
+ function getAnimationClass(animation) {
11
+ return `ntd-anim-${animation}`;
12
+ }
13
+ /**
14
+ * Build inline animation style
15
+ */
16
+ function getAnimationStyle(animation, duration = 1000, easing = "ease-in-out", iterations = 1, delay = 0) {
17
+ const iterationValue = iterations < 0 ? "infinite" : iterations;
18
+ return {
19
+ animationName: `ntd-${animation}`,
20
+ animationDuration: `${duration}ms`,
21
+ animationTimingFunction: easing,
22
+ animationIterationCount: iterationValue,
23
+ animationDelay: `${delay}ms`,
24
+ animationFillMode: "forwards",
25
+ };
26
+ }
27
+ /**
28
+ * Check if config is a sequence
29
+ */
30
+ function isSequenceConfig(config) {
31
+ return typeof config === "object" && "steps" in config;
32
+ }
33
+ /* ────────────────────────────────────────────────────────────────────────────
34
+ 3. ANIMATION SEQUENCE HOOK
35
+ ───────────────────────────────────────────────────────────────────────────── */
36
+ /**
37
+ * Hook to manage animation sequences
38
+ */
39
+ function useAnimationSequence(config) {
40
+ const [currentStepIndex, setCurrentStepIndex] = useState(0);
41
+ const [loopIteration, setLoopIteration] = useState(0);
42
+ const [state, setState] = useState("idle");
43
+ const [direction, setDirection] = useState("forward");
44
+ const timeoutRef = useRef(null);
45
+ const startTimeRef = useRef(0);
46
+ const elapsedRef = useRef(0);
47
+ const steps = useMemo(() => {
48
+ if (config.direction === "reverse") {
49
+ return [...config.steps].reverse();
50
+ }
51
+ return config.steps;
52
+ }, [config.steps, config.direction]);
53
+ const currentStep = steps[currentStepIndex];
54
+ const clearTimer = useCallback(() => {
55
+ if (timeoutRef.current) {
56
+ clearTimeout(timeoutRef.current);
57
+ timeoutRef.current = null;
58
+ }
59
+ }, []);
60
+ const play = useCallback(() => {
61
+ if (state === "finished") {
62
+ // Reset if finished
63
+ setCurrentStepIndex(0);
64
+ setLoopIteration(0);
65
+ elapsedRef.current = 0;
66
+ }
67
+ setState("playing");
68
+ startTimeRef.current = Date.now();
69
+ }, [state]);
70
+ const pause = useCallback(() => {
71
+ if (state === "playing") {
72
+ clearTimer();
73
+ elapsedRef.current += Date.now() - startTimeRef.current;
74
+ setState("paused");
75
+ }
76
+ }, [state, clearTimer]);
77
+ const reset = useCallback(() => {
78
+ clearTimer();
79
+ setCurrentStepIndex(0);
80
+ setLoopIteration(0);
81
+ setDirection("forward");
82
+ elapsedRef.current = 0;
83
+ setState("idle");
84
+ }, [clearTimer]);
85
+ const goToStep = useCallback((index) => {
86
+ if (index >= 0 && index < steps.length) {
87
+ clearTimer();
88
+ setCurrentStepIndex(index);
89
+ elapsedRef.current = 0;
90
+ if (state === "playing") {
91
+ startTimeRef.current = Date.now();
92
+ }
93
+ }
94
+ }, [steps.length, clearTimer, state]);
95
+ const getProgress = useCallback(() => {
96
+ if (state === "idle")
97
+ return 0;
98
+ if (state === "finished")
99
+ return 1;
100
+ const stepProgress = currentStepIndex / steps.length;
101
+ return stepProgress;
102
+ }, [state, currentStepIndex, steps.length]);
103
+ // Handle step progression
104
+ useEffect(() => {
105
+ if (state !== "playing" || !currentStep)
106
+ return;
107
+ const stepDuration = (currentStep.duration || 1000) * (currentStep.iterations || 1);
108
+ const remainingTime = stepDuration - elapsedRef.current;
109
+ timeoutRef.current = setTimeout(() => {
110
+ elapsedRef.current = 0;
111
+ // Determine next step
112
+ let nextStepIndex;
113
+ if (config.direction === "alternate") {
114
+ if (direction === "forward") {
115
+ if (currentStepIndex >= steps.length - 1) {
116
+ setDirection("backward");
117
+ nextStepIndex = currentStepIndex - 1;
118
+ }
119
+ else {
120
+ nextStepIndex = currentStepIndex + 1;
121
+ }
122
+ }
123
+ else {
124
+ if (currentStepIndex <= 0) {
125
+ setDirection("forward");
126
+ nextStepIndex = currentStepIndex + 1;
127
+ }
128
+ else {
129
+ nextStepIndex = currentStepIndex - 1;
130
+ }
131
+ }
132
+ }
133
+ else {
134
+ nextStepIndex = currentStepIndex + 1;
135
+ }
136
+ // Check if sequence complete
137
+ if (nextStepIndex >= steps.length && config.direction !== "alternate") {
138
+ if (config.loop) {
139
+ const newIteraton = loopIteration + 1;
140
+ if (config.loopCount && newIteraton >= config.loopCount) {
141
+ setState("finished");
142
+ }
143
+ else {
144
+ setLoopIteration(newIteraton);
145
+ setCurrentStepIndex(0);
146
+ // Apply loop delay
147
+ if (config.loopDelay) {
148
+ setState("paused");
149
+ timeoutRef.current = setTimeout(() => {
150
+ setState("playing");
151
+ startTimeRef.current = Date.now();
152
+ }, config.loopDelay);
153
+ }
154
+ else {
155
+ startTimeRef.current = Date.now();
156
+ }
157
+ }
158
+ }
159
+ else {
160
+ setState("finished");
161
+ }
162
+ }
163
+ else if (nextStepIndex >= 0 && nextStepIndex < steps.length) {
164
+ setCurrentStepIndex(nextStepIndex);
165
+ startTimeRef.current = Date.now();
166
+ }
167
+ }, remainingTime + (currentStep.delay || 0));
168
+ return clearTimer;
169
+ }, [
170
+ state,
171
+ currentStep,
172
+ currentStepIndex,
173
+ steps.length,
174
+ direction,
175
+ config.loop,
176
+ config.loopCount,
177
+ config.loopDelay,
178
+ config.direction,
179
+ loopIteration,
180
+ clearTimer,
181
+ ]);
182
+ return {
183
+ currentStep,
184
+ currentStepIndex,
185
+ state,
186
+ loopIteration,
187
+ play,
188
+ pause,
189
+ reset,
190
+ goToStep,
191
+ getProgress,
192
+ };
193
+ }
194
+ /* ────────────────────────────────────────────────────────────────────────────
195
+ 4. ANIMATION CONTROLLER COMPONENT
196
+ ───────────────────────────────────────────────────────────────────────────── */
197
+ /**
198
+ * Animation Controller component
199
+ *
200
+ * Wraps an icon and provides play/pause/reset controls.
201
+ *
202
+ * @example
203
+ * ```tsx
204
+ * const ref = useRef<AnimationControllerHandle>(null);
205
+ *
206
+ * <AnimationController
207
+ * ref={ref}
208
+ * animation="spin"
209
+ * autoPlay={false}
210
+ * >
211
+ * <NiceIcon name="loading" />
212
+ * </AnimationController>
213
+ *
214
+ * <button onClick={() => ref.current?.play()}>Play</button>
215
+ * <button onClick={() => ref.current?.pause()}>Pause</button>
216
+ * <button onClick={() => ref.current?.reset()}>Reset</button>
217
+ * ```
218
+ *
219
+ * @example Sequence
220
+ * ```tsx
221
+ * <AnimationController
222
+ * animation={{
223
+ * steps: [
224
+ * { animation: 'bounce', duration: 500 },
225
+ * { animation: 'spin', duration: 1000 },
226
+ * { animation: 'pulse', duration: 800 },
227
+ * ],
228
+ * loop: true,
229
+ * loopCount: 3
230
+ * }}
231
+ * autoPlay
232
+ * >
233
+ * <NiceIcon name="star" />
234
+ * </AnimationController>
235
+ * ```
236
+ */
237
+ const AnimationController = forwardRef(function AnimationController(props, ref) {
238
+ const { children, animation, autoPlay = false, playState: externalPlayState, onStart, onEnd, onStepChange, onPause, onResume, className, style, } = props;
239
+ // Determine if sequence or single animation
240
+ const isSequence = isSequenceConfig(animation);
241
+ // For single animations
242
+ const [singleState, setSingleState] = useState(autoPlay ? "playing" : "idle");
243
+ const singleIterationRef = useRef(0);
244
+ // For sequences
245
+ const sequenceConfig = isSequence
246
+ ? animation
247
+ : { steps: [{ animation, duration: 1000 }] };
248
+ const sequence = useAnimationSequence(sequenceConfig);
249
+ // Unified state
250
+ const state = isSequence ? sequence.state : singleState;
251
+ const currentStepIndex = isSequence ? sequence.currentStepIndex : 0;
252
+ // Handle external playState control
253
+ useEffect(() => {
254
+ if (externalPlayState !== undefined) {
255
+ if (isSequence) {
256
+ if (externalPlayState === "playing")
257
+ sequence.play();
258
+ else if (externalPlayState === "paused")
259
+ sequence.pause();
260
+ else if (externalPlayState === "idle")
261
+ sequence.reset();
262
+ }
263
+ else {
264
+ setSingleState(externalPlayState);
265
+ }
266
+ }
267
+ }, [externalPlayState, isSequence, sequence]);
268
+ // Auto-play
269
+ useEffect(() => {
270
+ if (autoPlay) {
271
+ if (isSequence) {
272
+ sequence.play();
273
+ }
274
+ else {
275
+ setSingleState("playing");
276
+ }
277
+ onStart === null || onStart === void 0 ? void 0 : onStart();
278
+ }
279
+ // eslint-disable-next-line react-hooks/exhaustive-deps
280
+ }, []);
281
+ // Step change callback
282
+ const prevStepRef = useRef(currentStepIndex);
283
+ useEffect(() => {
284
+ if (prevStepRef.current !== currentStepIndex && isSequence) {
285
+ const step = sequenceConfig.steps[currentStepIndex];
286
+ onStepChange === null || onStepChange === void 0 ? void 0 : onStepChange(currentStepIndex, step);
287
+ }
288
+ prevStepRef.current = currentStepIndex;
289
+ }, [currentStepIndex, isSequence, sequenceConfig.steps, onStepChange]);
290
+ // State change callbacks
291
+ const prevStateRef = useRef(state);
292
+ useEffect(() => {
293
+ if (prevStateRef.current !== state) {
294
+ if (state === "playing" && prevStateRef.current === "paused") {
295
+ onResume === null || onResume === void 0 ? void 0 : onResume();
296
+ }
297
+ else if (state === "paused" && prevStateRef.current === "playing") {
298
+ onPause === null || onPause === void 0 ? void 0 : onPause();
299
+ }
300
+ else if (state === "finished") {
301
+ onEnd === null || onEnd === void 0 ? void 0 : onEnd();
302
+ }
303
+ else if (state === "playing" && prevStateRef.current === "idle") {
304
+ onStart === null || onStart === void 0 ? void 0 : onStart();
305
+ }
306
+ }
307
+ prevStateRef.current = state;
308
+ }, [state, onStart, onEnd, onPause, onResume]);
309
+ // Imperative handle
310
+ useImperativeHandle(ref, () => ({
311
+ play: () => {
312
+ if (isSequence) {
313
+ sequence.play();
314
+ }
315
+ else {
316
+ setSingleState("playing");
317
+ singleIterationRef.current++;
318
+ }
319
+ onStart === null || onStart === void 0 ? void 0 : onStart();
320
+ },
321
+ pause: () => {
322
+ if (isSequence) {
323
+ sequence.pause();
324
+ }
325
+ else {
326
+ setSingleState("paused");
327
+ }
328
+ onPause === null || onPause === void 0 ? void 0 : onPause();
329
+ },
330
+ reset: () => {
331
+ if (isSequence) {
332
+ sequence.reset();
333
+ }
334
+ else {
335
+ setSingleState("idle");
336
+ singleIterationRef.current++;
337
+ }
338
+ },
339
+ goToStep: (index) => {
340
+ if (isSequence) {
341
+ sequence.goToStep(index);
342
+ }
343
+ },
344
+ getState: () => state,
345
+ getCurrentStep: () => currentStepIndex,
346
+ getProgress: () => {
347
+ if (isSequence) {
348
+ return sequence.getProgress();
349
+ }
350
+ return state === "playing" ? 0.5 : state === "finished" ? 1 : 0;
351
+ },
352
+ }));
353
+ // Build animation styles
354
+ const animationStyles = useMemo(() => {
355
+ if (state === "idle") {
356
+ return {};
357
+ }
358
+ if (state === "paused") {
359
+ return { animationPlayState: "paused" };
360
+ }
361
+ if (isSequence && sequence.currentStep) {
362
+ const step = sequence.currentStep;
363
+ return getAnimationStyle(step.animation, step.duration, step.easing, step.iterations, step.delay);
364
+ }
365
+ if (!isSequence) {
366
+ return getAnimationStyle(animation, 1000, "ease-in-out", -1);
367
+ }
368
+ return {};
369
+ }, [state, isSequence, sequence.currentStep, animation]);
370
+ // Animation class
371
+ const animationClass = state === "playing" || state === "paused"
372
+ ? isSequence && sequence.currentStep
373
+ ? getAnimationClass(sequence.currentStep.animation)
374
+ : getAnimationClass(animation)
375
+ : "";
376
+ // Clone child with animation props
377
+ const animatedChild = React.cloneElement(children, {
378
+ className: `${children.props.className || ""} ${animationClass}`.trim(),
379
+ style: {
380
+ ...children.props.style,
381
+ ...animationStyles,
382
+ },
383
+ key: isSequence
384
+ ? `seq-${currentStepIndex}-${sequence.loopIteration}`
385
+ : `single-${singleIterationRef.current}`,
386
+ });
387
+ return (jsx("span", { className: `ntd-animation-controller ntd-animation-${state} ${className || ""}`.trim(), style: style, children: animatedChild }));
388
+ });
389
+ /* ────────────────────────────────────────────────────────────────────────────
390
+ 5. INTERACTIVE ICON COMPONENT
391
+ ───────────────────────────────────────────────────────────────────────────── */
392
+ /**
393
+ * Interactive icon wrapper with hover/click/focus triggers
394
+ *
395
+ * @example
396
+ * ```tsx
397
+ * <InteractiveIcon
398
+ * config={{
399
+ * trigger: 'hover',
400
+ * enterAnimation: 'bounce',
401
+ * leaveAnimation: 'fadeOut',
402
+ * }}
403
+ * >
404
+ * <NiceIcon name="heart" />
405
+ * </InteractiveIcon>
406
+ * ```
407
+ *
408
+ * @example Click
409
+ * ```tsx
410
+ * <InteractiveIcon
411
+ * config={{
412
+ * trigger: 'click',
413
+ * enterAnimation: 'pulse',
414
+ * once: true,
415
+ * }}
416
+ * >
417
+ * <NiceIcon name="notification" />
418
+ * </InteractiveIcon>
419
+ * ```
420
+ */
421
+ const InteractiveIcon = forwardRef(function InteractiveIcon(props, ref) {
422
+ const { children, config, disabled = false, onInteractionStart, onInteractionEnd, className, style, } = props;
423
+ const [isActive, setIsActive] = useState(false);
424
+ const [isLeaving, setIsLeaving] = useState(false);
425
+ const [hasPlayed, setHasPlayed] = useState(false);
426
+ const wrapperRef = useRef(null);
427
+ const timeoutRef = useRef(null);
428
+ // Merge refs
429
+ const setRefs = useCallback((node) => {
430
+ wrapperRef.current =
431
+ node;
432
+ if (typeof ref === "function") {
433
+ ref(node);
434
+ }
435
+ else if (ref) {
436
+ ref.current = node;
437
+ }
438
+ }, [ref]);
439
+ // Clear any pending timeouts
440
+ const clearTimer = useCallback(() => {
441
+ if (timeoutRef.current) {
442
+ clearTimeout(timeoutRef.current);
443
+ timeoutRef.current = null;
444
+ }
445
+ }, []);
446
+ useEffect(() => clearTimer, [clearTimer]);
447
+ // Handle activation
448
+ const activate = useCallback(() => {
449
+ if (disabled)
450
+ return;
451
+ if (config.once && hasPlayed)
452
+ return;
453
+ clearTimer();
454
+ setIsLeaving(false);
455
+ if (config.delay) {
456
+ timeoutRef.current = setTimeout(() => {
457
+ setIsActive(true);
458
+ setHasPlayed(true);
459
+ onInteractionStart === null || onInteractionStart === void 0 ? void 0 : onInteractionStart();
460
+ }, config.delay);
461
+ }
462
+ else {
463
+ setIsActive(true);
464
+ setHasPlayed(true);
465
+ onInteractionStart === null || onInteractionStart === void 0 ? void 0 : onInteractionStart();
466
+ }
467
+ }, [disabled, config.once, config.delay, hasPlayed, clearTimer, onInteractionStart]);
468
+ // Handle deactivation
469
+ const deactivate = useCallback(() => {
470
+ if (!isActive)
471
+ return;
472
+ clearTimer();
473
+ if (config.leaveAnimation) {
474
+ setIsLeaving(true);
475
+ setIsActive(false);
476
+ timeoutRef.current = setTimeout(() => {
477
+ setIsLeaving(false);
478
+ onInteractionEnd === null || onInteractionEnd === void 0 ? void 0 : onInteractionEnd();
479
+ }, config.leaveDuration || 300);
480
+ }
481
+ else {
482
+ setIsActive(false);
483
+ onInteractionEnd === null || onInteractionEnd === void 0 ? void 0 : onInteractionEnd();
484
+ }
485
+ }, [isActive, config.leaveAnimation, config.leaveDuration, clearTimer, onInteractionEnd]);
486
+ // Visibility observer
487
+ useEffect(() => {
488
+ var _a;
489
+ if (config.trigger !== "visible" || !wrapperRef.current)
490
+ return;
491
+ const threshold = (_a = config.visibilityThreshold) !== null && _a !== void 0 ? _a : 0.5;
492
+ const observer = new IntersectionObserver(([entry]) => {
493
+ if (entry.isIntersecting) {
494
+ activate();
495
+ }
496
+ else if (!config.once) {
497
+ deactivate();
498
+ }
499
+ }, { threshold });
500
+ observer.observe(wrapperRef.current);
501
+ return () => observer.disconnect();
502
+ }, [config.trigger, config.visibilityThreshold, config.once, activate, deactivate]);
503
+ // Event handlers
504
+ const handleMouseEnter = config.trigger === "hover" ? activate : undefined;
505
+ const handleMouseLeave = config.trigger === "hover" ? deactivate : undefined;
506
+ const handleClick = config.trigger === "click" ? activate : undefined;
507
+ const handleFocus = config.trigger === "focus" ? activate : undefined;
508
+ const handleBlur = config.trigger === "focus" ? deactivate : undefined;
509
+ // Build animation props
510
+ const currentAnimation = isActive
511
+ ? config.enterAnimation
512
+ : isLeaving
513
+ ? config.leaveAnimation
514
+ : undefined;
515
+ const animationDuration = isActive
516
+ ? config.enterDuration
517
+ : config.leaveDuration;
518
+ const animationStyles = currentAnimation
519
+ ? getAnimationStyle(currentAnimation, animationDuration || 300)
520
+ : {};
521
+ const animationClass = currentAnimation
522
+ ? getAnimationClass(currentAnimation)
523
+ : "";
524
+ // Clone with animation
525
+ const animatedChild = React.cloneElement(children, {
526
+ className: `${children.props.className || ""} ${animationClass}`.trim(),
527
+ style: {
528
+ ...children.props.style,
529
+ ...animationStyles,
530
+ },
531
+ });
532
+ return (jsx("span", { ref: setRefs, className: `ntd-interactive-icon ${isActive ? "ntd-active" : ""} ${isLeaving ? "ntd-leaving" : ""} ${disabled ? "ntd-disabled" : ""} ${className || ""}`.trim(), style: {
533
+ display: "inline-flex",
534
+ cursor: disabled ? "default" : config.trigger === "click" ? "pointer" : undefined,
535
+ ...style,
536
+ }, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onClick: handleClick, onFocus: handleFocus, onBlur: handleBlur, tabIndex: config.trigger === "focus" ? 0 : undefined, role: config.trigger === "click" ? "button" : undefined, children: animatedChild }));
537
+ });
538
+ /* ────────────────────────────────────────────────────────────────────────────
539
+ 6. UTILITY FUNCTIONS
540
+ ───────────────────────────────────────────────────────────────────────────── */
541
+ /**
542
+ * Create an animation sequence configuration
543
+ *
544
+ * @example
545
+ * ```tsx
546
+ * const seq = createAnimationSequence([
547
+ * { animation: 'shake', duration: 300 },
548
+ * { animation: 'pulse', duration: 500 },
549
+ * { animation: 'bounce', duration: 400 },
550
+ * ], { loop: true, loopCount: 2 });
551
+ * ```
552
+ */
553
+ function createAnimationSequence(steps, options) {
554
+ var _a, _b;
555
+ return {
556
+ steps,
557
+ loop: (_a = options === null || options === void 0 ? void 0 : options.loop) !== null && _a !== void 0 ? _a : false,
558
+ loopCount: options === null || options === void 0 ? void 0 : options.loopCount,
559
+ loopDelay: options === null || options === void 0 ? void 0 : options.loopDelay,
560
+ direction: (_b = options === null || options === void 0 ? void 0 : options.direction) !== null && _b !== void 0 ? _b : "normal",
561
+ };
562
+ }
563
+ /**
564
+ * Create an interactive config for hover effect
565
+ */
566
+ function createHoverAnimation(enterAnimation, leaveAnimation, options) {
567
+ var _a, _b;
568
+ return {
569
+ trigger: "hover",
570
+ enterAnimation,
571
+ leaveAnimation,
572
+ enterDuration: (_a = options === null || options === void 0 ? void 0 : options.enterDuration) !== null && _a !== void 0 ? _a : 300,
573
+ leaveDuration: (_b = options === null || options === void 0 ? void 0 : options.leaveDuration) !== null && _b !== void 0 ? _b : 200,
574
+ delay: options === null || options === void 0 ? void 0 : options.delay,
575
+ };
576
+ }
577
+ /**
578
+ * Create an interactive config for click effect
579
+ */
580
+ function createClickAnimation(animation, options) {
581
+ var _a, _b;
582
+ return {
583
+ trigger: "click",
584
+ enterAnimation: animation,
585
+ enterDuration: (_a = options === null || options === void 0 ? void 0 : options.duration) !== null && _a !== void 0 ? _a : 500,
586
+ once: (_b = options === null || options === void 0 ? void 0 : options.once) !== null && _b !== void 0 ? _b : false,
587
+ delay: options === null || options === void 0 ? void 0 : options.delay,
588
+ };
589
+ }
590
+ /**
591
+ * Create an interactive config for visibility trigger
592
+ */
593
+ function createVisibilityAnimation(animation, options) {
594
+ var _a, _b, _c;
595
+ return {
596
+ trigger: "visible",
597
+ enterAnimation: animation,
598
+ enterDuration: (_a = options === null || options === void 0 ? void 0 : options.duration) !== null && _a !== void 0 ? _a : 600,
599
+ once: (_b = options === null || options === void 0 ? void 0 : options.once) !== null && _b !== void 0 ? _b : true,
600
+ visibilityThreshold: (_c = options === null || options === void 0 ? void 0 : options.threshold) !== null && _c !== void 0 ? _c : 0.5,
601
+ };
602
+ }
603
+ /* ────────────────────────────────────────────────────────────────────────────
604
+ 7. PRESET SEQUENCES
605
+ ───────────────────────────────────────────────────────────────────────────── */
606
+ /**
607
+ * Preset animation sequences for common use cases
608
+ */
609
+ const PRESET_SEQUENCES = {
610
+ /** Attention grabber: bounce → shake → pulse */
611
+ attention: createAnimationSequence([
612
+ { animation: "bounce", duration: 400 },
613
+ { animation: "shake", duration: 300 },
614
+ { animation: "pulse", duration: 500 },
615
+ ]),
616
+ /** Success animation: pop → bounce */
617
+ success: createAnimationSequence([
618
+ { animation: "pop", duration: 200 },
619
+ { animation: "bounce", duration: 500 },
620
+ ]),
621
+ /** Error animation: shake → pulse */
622
+ error: createAnimationSequence([
623
+ { animation: "shake", duration: 500, iterations: 2 },
624
+ { animation: "pulse", duration: 300 },
625
+ ]),
626
+ /** Loading sequence: spin → pulse (looping) */
627
+ loading: createAnimationSequence([
628
+ { animation: "spin", duration: 1000 },
629
+ { animation: "pulse", duration: 500 },
630
+ ], { loop: true }),
631
+ /** Celebration: bounce → tada → heartbeat */
632
+ celebrate: createAnimationSequence([
633
+ { animation: "bounce", duration: 500 },
634
+ { animation: "tada", duration: 800 },
635
+ { animation: "heartbeat", duration: 600 },
636
+ ]),
637
+ /** Notification: swing → bounce */
638
+ notification: createAnimationSequence([
639
+ { animation: "swing", duration: 600 },
640
+ { animation: "bounce", duration: 400 },
641
+ ]),
642
+ /** Gentle: pulse → breathe */
643
+ gentle: createAnimationSequence([
644
+ { animation: "pulse", duration: 800 },
645
+ { animation: "breathe", duration: 1500 },
646
+ ], { loop: true }),
647
+ /** Reveal: fade-in → pop */
648
+ reveal: createAnimationSequence([
649
+ { animation: "fade-in", duration: 300 },
650
+ { animation: "pop", duration: 200 },
651
+ ]),
652
+ };
653
+ /**
654
+ * Preset interactive configs
655
+ */
656
+ const PRESET_INTERACTIONS = {
657
+ /** Bounce on hover */
658
+ hoverBounce: createHoverAnimation("bounce"),
659
+ /** Pulse on hover */
660
+ hoverPulse: createHoverAnimation("pulse"),
661
+ /** Spin on hover */
662
+ hoverSpin: createHoverAnimation("spin"),
663
+ /** Shake on hover */
664
+ hoverShake: createHoverAnimation("shake"),
665
+ /** Pop on click */
666
+ clickPop: createClickAnimation("pop", { once: true }),
667
+ /** Bounce on click */
668
+ clickBounce: createClickAnimation("bounce"),
669
+ /** Tada on click */
670
+ clickTada: createClickAnimation("tada"),
671
+ /** FadeIn on visible */
672
+ visibleFadeIn: createVisibilityAnimation("fade-in", { once: true }),
673
+ /** Bounce on visible */
674
+ visibleBounce: createVisibilityAnimation("bounce", { once: true }),
675
+ /** Scale in on visible */
676
+ visibleScaleIn: createVisibilityAnimation("scale-in", { once: true }),
677
+ };
678
+
679
+ export { AnimationController, InteractiveIcon, PRESET_INTERACTIONS, PRESET_SEQUENCES, createAnimationSequence, createClickAnimation, createHoverAnimation, createVisibilityAnimation, useAnimationSequence };
680
+ //# sourceMappingURL=animationControls.js.map