@hua-labs/motion-core 2.3.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,559 +1,9 @@
1
1
  "use client";
2
- import { createContext, useState, useRef, useCallback, useEffect, useMemo, createElement, useContext } from 'react';
2
+ import { getPagePreset, getMotionPreset, mergeWithPreset, motionStateManager, MOTION_PRESETS, useMotionProfile, getEasing, calculateSpring } from './chunk-47QAGQLN.mjs';
3
+ export { MOTION_PRESETS, MotionEngine, MotionProfileProvider, PAGE_MOTIONS, TransitionEffects, applyEasing, calculateSpring, easeIn, easeInOut, easeInOutQuad, easeInQuad, easeOut, easeOutQuad, easingPresets, getAvailableEasings, getEasing, getMotionPreset, getPagePreset, getPresetEasing, hua, isEasingFunction, isValidEasing, linear, mergeProfileOverrides, mergeWithPreset, motionEngine, neutral, resolveProfile, safeApplyEasing, transitionEffects, useMotionProfile } from './chunk-47QAGQLN.mjs';
4
+ import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
3
5
  import { jsx } from 'react/jsx-runtime';
4
6
 
5
- // src/core/MotionEngine.ts
6
- var MotionEngine = class {
7
- constructor() {
8
- this.motions = /* @__PURE__ */ new Map();
9
- this.isRunning = false;
10
- this.animationFrameId = null;
11
- }
12
- /**
13
- * 모션 시작
14
- */
15
- motion(element, motionFrames, options) {
16
- return new Promise((resolve) => {
17
- const motionId = this.generateMotionId();
18
- this.enableGPUAcceleration(element);
19
- this.createLayer(element);
20
- const motion = {
21
- id: motionId,
22
- element,
23
- isRunning: true,
24
- isPaused: false,
25
- currentProgress: 0,
26
- startTime: Date.now() + (options.delay || 0),
27
- pauseTime: 0,
28
- options: {
29
- ...options,
30
- onComplete: () => {
31
- options.onComplete?.();
32
- this.motions.delete(motionId);
33
- }
34
- }
35
- };
36
- this.motions.set(motionId, motion);
37
- if (!this.isRunning) {
38
- this.startAnimationLoop();
39
- }
40
- options.onStart?.();
41
- resolve(motionId);
42
- });
43
- }
44
- /**
45
- * 모션 중지
46
- */
47
- stop(motionId) {
48
- const motion = this.motions.get(motionId);
49
- if (motion) {
50
- motion.isRunning = false;
51
- motion.options.onCancel?.();
52
- this.motions.delete(motionId);
53
- }
54
- }
55
- /**
56
- * 모션 일시정지
57
- */
58
- pause(motionId) {
59
- const motion = this.motions.get(motionId);
60
- if (motion && motion.isRunning && !motion.isPaused) {
61
- motion.isPaused = true;
62
- motion.pauseTime = Date.now();
63
- }
64
- }
65
- /**
66
- * 모션 재개
67
- */
68
- resume(motionId) {
69
- const motion = this.motions.get(motionId);
70
- if (motion && motion.isPaused) {
71
- motion.isPaused = false;
72
- if (motion.pauseTime > 0) {
73
- motion.startTime += Date.now() - motion.pauseTime;
74
- }
75
- }
76
- }
77
- /**
78
- * 모든 모션 중지
79
- */
80
- stopAll() {
81
- this.motions.forEach((motion) => {
82
- motion.isRunning = false;
83
- motion.options.onCancel?.();
84
- });
85
- this.motions.clear();
86
- this.stopAnimationLoop();
87
- }
88
- /**
89
- * 모션 상태 확인
90
- */
91
- getMotion(motionId) {
92
- return this.motions.get(motionId);
93
- }
94
- /**
95
- * 실행 중인 모션 수
96
- */
97
- getActiveMotionCount() {
98
- return this.motions.size;
99
- }
100
- /**
101
- * 애니메이션 루프 시작
102
- */
103
- startAnimationLoop() {
104
- if (this.isRunning) return;
105
- this.isRunning = true;
106
- this.animate();
107
- }
108
- /**
109
- * 애니메이션 루프 중지
110
- */
111
- stopAnimationLoop() {
112
- if (this.animationFrameId) {
113
- cancelAnimationFrame(this.animationFrameId);
114
- this.animationFrameId = null;
115
- }
116
- this.isRunning = false;
117
- }
118
- /**
119
- * 메인 애니메이션 루프
120
- */
121
- animate() {
122
- if (!this.isRunning || this.motions.size === 0) {
123
- this.stopAnimationLoop();
124
- return;
125
- }
126
- const currentTime = Date.now();
127
- const completedMotions = [];
128
- this.motions.forEach((motion) => {
129
- if (!motion.isRunning || motion.isPaused) return;
130
- const elapsed = currentTime - motion.startTime;
131
- if (elapsed < 0) return;
132
- const progress = Math.min(elapsed / motion.options.duration, 1);
133
- const easedProgress = motion.options.easing(progress);
134
- motion.currentProgress = easedProgress;
135
- this.applyMotionFrame(motion.element, easedProgress);
136
- motion.options.onUpdate?.(easedProgress);
137
- if (progress >= 1) {
138
- completedMotions.push(motion.id);
139
- motion.isRunning = false;
140
- motion.options.onComplete?.();
141
- }
142
- });
143
- completedMotions.forEach((id) => this.motions.delete(id));
144
- this.animationFrameId = requestAnimationFrame(() => this.animate());
145
- }
146
- /**
147
- * 모션 프레임 적용
148
- */
149
- applyMotionFrame(element, progress) {
150
- const transforms = [];
151
- if (element.style.transform) {
152
- transforms.push(element.style.transform);
153
- }
154
- element.style.transform = transforms.join(" ");
155
- }
156
- /**
157
- * GPU 가속 활성화
158
- */
159
- enableGPUAcceleration(element) {
160
- element.style.willChange = "transform, opacity";
161
- element.style.transform = "translateZ(0)";
162
- }
163
- /**
164
- * 레이어 분리
165
- */
166
- createLayer(element) {
167
- element.style.transform = "translateZ(0)";
168
- element.style.backfaceVisibility = "hidden";
169
- }
170
- /**
171
- * 고유 모션 ID 생성
172
- */
173
- generateMotionId() {
174
- return `motion_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
175
- }
176
- /**
177
- * 정리
178
- */
179
- destroy() {
180
- this.stopAll();
181
- this.stopAnimationLoop();
182
- }
183
- };
184
- var motionEngine = new MotionEngine();
185
-
186
- // src/core/TransitionEffects.ts
187
- var TransitionEffects = class _TransitionEffects {
188
- constructor() {
189
- this.activeTransitions = /* @__PURE__ */ new Map();
190
- }
191
- static getInstance() {
192
- if (!_TransitionEffects.instance) {
193
- _TransitionEffects.instance = new _TransitionEffects();
194
- }
195
- return _TransitionEffects.instance;
196
- }
197
- /**
198
- * 공통 전환 실행 헬퍼
199
- */
200
- async executeTransition(element, options, config) {
201
- const transitionId = this.generateTransitionId();
202
- config.setup();
203
- this.enableGPUAcceleration(element);
204
- let resolveTransition;
205
- const completed = new Promise((resolve) => {
206
- resolveTransition = resolve;
207
- });
208
- const motionId = await motionEngine.motion(
209
- element,
210
- [
211
- { progress: 0, properties: config.keyframes[0] },
212
- { progress: 1, properties: config.keyframes[1] }
213
- ],
214
- {
215
- duration: options.duration,
216
- easing: options.easing || this.getDefaultEasing(),
217
- delay: options.delay,
218
- onStart: options.onTransitionStart,
219
- onUpdate: config.onUpdate,
220
- onComplete: () => {
221
- config.onCleanup();
222
- options.onTransitionComplete?.();
223
- this.activeTransitions.delete(transitionId);
224
- resolveTransition();
225
- }
226
- }
227
- );
228
- this.activeTransitions.set(transitionId, motionId);
229
- return completed;
230
- }
231
- /**
232
- * 페이드 인/아웃 전환
233
- */
234
- async fade(element, options) {
235
- const initialOpacity = parseFloat(getComputedStyle(element).opacity) || 1;
236
- const targetOpacity = options.direction === "reverse" ? 0 : 1;
237
- return this.executeTransition(element, options, {
238
- setup: () => {
239
- if (options.direction === "reverse") {
240
- element.style.opacity = initialOpacity.toString();
241
- } else {
242
- element.style.opacity = "0";
243
- }
244
- },
245
- keyframes: [
246
- { opacity: options.direction === "reverse" ? initialOpacity : 0 },
247
- { opacity: targetOpacity }
248
- ],
249
- onUpdate: (progress) => {
250
- const currentOpacity = options.direction === "reverse" ? initialOpacity * (1 - progress) : targetOpacity * progress;
251
- element.style.opacity = currentOpacity.toString();
252
- },
253
- onCleanup: () => {
254
- }
255
- });
256
- }
257
- /**
258
- * 슬라이드 전환
259
- */
260
- async slide(element, options) {
261
- const distance = options.distance || 100;
262
- const initialTransform = getComputedStyle(element).transform;
263
- const isReverse = options.direction === "reverse";
264
- return this.executeTransition(element, options, {
265
- setup: () => {
266
- if (!isReverse) {
267
- element.style.transform = `translateX(${distance}px)`;
268
- }
269
- },
270
- keyframes: [
271
- { translateX: isReverse ? 0 : distance },
272
- { translateX: isReverse ? distance : 0 }
273
- ],
274
- onUpdate: (progress) => {
275
- const currentTranslateX = isReverse ? distance * progress : distance * (1 - progress);
276
- element.style.transform = `translateX(${currentTranslateX}px)`;
277
- },
278
- onCleanup: () => {
279
- element.style.transform = initialTransform;
280
- }
281
- });
282
- }
283
- /**
284
- * 스케일 전환
285
- */
286
- async scale(element, options) {
287
- const scaleValue = options.scale || 0.8;
288
- const initialTransform = getComputedStyle(element).transform;
289
- const isReverse = options.direction === "reverse";
290
- return this.executeTransition(element, options, {
291
- setup: () => {
292
- if (!isReverse) {
293
- element.style.transform = `scale(${scaleValue})`;
294
- }
295
- },
296
- keyframes: [
297
- { scale: isReverse ? 1 : scaleValue },
298
- { scale: isReverse ? scaleValue : 1 }
299
- ],
300
- onUpdate: (progress) => {
301
- const currentScale = isReverse ? 1 - (1 - scaleValue) * progress : scaleValue + (1 - scaleValue) * progress;
302
- element.style.transform = `scale(${currentScale})`;
303
- },
304
- onCleanup: () => {
305
- element.style.transform = initialTransform;
306
- }
307
- });
308
- }
309
- /**
310
- * 플립 전환 (3D 회전)
311
- */
312
- async flip(element, options) {
313
- const perspective = options.perspective || 1e3;
314
- const initialTransform = getComputedStyle(element).transform;
315
- const isReverse = options.direction === "reverse";
316
- return this.executeTransition(element, options, {
317
- setup: () => {
318
- element.style.perspective = `${perspective}px`;
319
- element.style.transformStyle = "preserve-3d";
320
- if (!isReverse) {
321
- element.style.transform = `rotateY(90deg)`;
322
- }
323
- },
324
- keyframes: [
325
- { rotateY: isReverse ? 0 : 90 },
326
- { rotateY: isReverse ? 90 : 0 }
327
- ],
328
- onUpdate: (progress) => {
329
- const currentRotateY = isReverse ? 90 * progress : 90 * (1 - progress);
330
- element.style.transform = `rotateY(${currentRotateY}deg)`;
331
- },
332
- onCleanup: () => {
333
- element.style.transform = initialTransform;
334
- element.style.perspective = "";
335
- element.style.transformStyle = "";
336
- }
337
- });
338
- }
339
- /**
340
- * 큐브 전환 (3D 큐브 회전)
341
- */
342
- async cube(element, options) {
343
- const perspective = options.perspective || 1200;
344
- const initialTransform = getComputedStyle(element).transform;
345
- const isReverse = options.direction === "reverse";
346
- return this.executeTransition(element, options, {
347
- setup: () => {
348
- element.style.perspective = `${perspective}px`;
349
- element.style.transformStyle = "preserve-3d";
350
- if (!isReverse) {
351
- element.style.transform = `rotateX(90deg) rotateY(45deg)`;
352
- }
353
- },
354
- keyframes: [
355
- { rotateX: isReverse ? 0 : 90, rotateY: isReverse ? 0 : 45 },
356
- { rotateX: isReverse ? 90 : 0, rotateY: isReverse ? 45 : 0 }
357
- ],
358
- onUpdate: (progress) => {
359
- const currentRotateX = isReverse ? 90 * progress : 90 * (1 - progress);
360
- const currentRotateY = isReverse ? 45 * progress : 45 * (1 - progress);
361
- element.style.transform = `rotateX(${currentRotateX}deg) rotateY(${currentRotateY}deg)`;
362
- },
363
- onCleanup: () => {
364
- element.style.transform = initialTransform;
365
- element.style.perspective = "";
366
- element.style.transformStyle = "";
367
- }
368
- });
369
- }
370
- /**
371
- * 모프 전환 (복합 변형)
372
- */
373
- async morph(element, options) {
374
- const initialTransform = getComputedStyle(element).transform;
375
- const isReverse = options.direction === "reverse";
376
- return this.executeTransition(element, options, {
377
- setup: () => {
378
- if (!isReverse) {
379
- element.style.transform = `scale(0.9) rotate(5deg)`;
380
- }
381
- },
382
- keyframes: [
383
- { scale: isReverse ? 1 : 0.9, rotate: isReverse ? 0 : 5 },
384
- { scale: isReverse ? 0.9 : 1, rotate: isReverse ? 5 : 0 }
385
- ],
386
- onUpdate: (progress) => {
387
- const currentScale = isReverse ? 1 - 0.1 * progress : 0.9 + 0.1 * progress;
388
- const currentRotate = isReverse ? 5 * progress : 5 * (1 - progress);
389
- element.style.transform = `scale(${currentScale}) rotate(${currentRotate}deg)`;
390
- },
391
- onCleanup: () => {
392
- element.style.transform = initialTransform;
393
- }
394
- });
395
- }
396
- /**
397
- * 전환 중지
398
- */
399
- stopTransition(transitionId) {
400
- const motionId = this.activeTransitions.get(transitionId);
401
- if (motionId) {
402
- motionEngine.stop(motionId);
403
- this.activeTransitions.delete(transitionId);
404
- }
405
- }
406
- /**
407
- * 모든 전환 중지
408
- */
409
- stopAllTransitions() {
410
- this.activeTransitions.forEach((motionId) => {
411
- motionEngine.stop(motionId);
412
- });
413
- this.activeTransitions.clear();
414
- }
415
- /**
416
- * 활성 전환 수 확인
417
- */
418
- getActiveTransitionCount() {
419
- return this.activeTransitions.size;
420
- }
421
- /**
422
- * GPU 가속 활성화
423
- */
424
- enableGPUAcceleration(element) {
425
- element.style.willChange = "transform, opacity";
426
- const currentTransform = element.style.transform;
427
- if (currentTransform && currentTransform !== "none" && currentTransform !== "") {
428
- if (!currentTransform.includes("translateZ")) {
429
- element.style.transform = `${currentTransform} translateZ(0)`;
430
- }
431
- } else {
432
- element.style.transform = "translateZ(0)";
433
- }
434
- }
435
- /**
436
- * 기본 이징 함수
437
- */
438
- getDefaultEasing() {
439
- return (t) => t * t * (3 - 2 * t);
440
- }
441
- /**
442
- * 고유 전환 ID 생성
443
- */
444
- generateTransitionId() {
445
- return `transition_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
446
- }
447
- /**
448
- * 정리
449
- */
450
- destroy() {
451
- this.stopAllTransitions();
452
- }
453
- };
454
- var transitionEffects = TransitionEffects.getInstance();
455
-
456
- // src/presets/index.ts
457
- var MOTION_PRESETS = {
458
- hero: {
459
- entrance: "fadeIn",
460
- delay: 200,
461
- duration: 800,
462
- hover: false,
463
- click: false
464
- },
465
- title: {
466
- entrance: "slideUp",
467
- delay: 400,
468
- duration: 700,
469
- hover: false,
470
- click: false
471
- },
472
- button: {
473
- entrance: "scaleIn",
474
- delay: 600,
475
- duration: 300,
476
- hover: true,
477
- click: true
478
- },
479
- card: {
480
- entrance: "slideUp",
481
- delay: 800,
482
- duration: 500,
483
- hover: true,
484
- click: false
485
- },
486
- text: {
487
- entrance: "fadeIn",
488
- delay: 200,
489
- duration: 600,
490
- hover: false,
491
- click: false
492
- },
493
- image: {
494
- entrance: "scaleIn",
495
- delay: 400,
496
- duration: 600,
497
- hover: true,
498
- click: false
499
- }
500
- };
501
- var PAGE_MOTIONS = {
502
- // 홈페이지
503
- home: {
504
- hero: { type: "hero" },
505
- title: { type: "title" },
506
- description: { type: "text" },
507
- cta: { type: "button" },
508
- feature1: { type: "card" },
509
- feature2: { type: "card" },
510
- feature3: { type: "card" }
511
- },
512
- // 대시보드
513
- dashboard: {
514
- header: { type: "hero" },
515
- sidebar: { type: "card", entrance: "slideLeft" },
516
- main: { type: "text", entrance: "fadeIn" },
517
- card1: { type: "card" },
518
- card2: { type: "card" },
519
- card3: { type: "card" },
520
- chart: { type: "image" }
521
- },
522
- // 제품 페이지
523
- product: {
524
- hero: { type: "hero" },
525
- title: { type: "title" },
526
- image: { type: "image" },
527
- description: { type: "text" },
528
- price: { type: "text" },
529
- buyButton: { type: "button" },
530
- features: { type: "card" }
531
- },
532
- // 블로그
533
- blog: {
534
- header: { type: "hero" },
535
- title: { type: "title" },
536
- content: { type: "text" },
537
- sidebar: { type: "card", entrance: "slideRight" },
538
- related1: { type: "card" },
539
- related2: { type: "card" },
540
- related3: { type: "card" }
541
- }
542
- };
543
- function mergeWithPreset(preset, custom = {}) {
544
- return {
545
- ...preset,
546
- ...custom
547
- };
548
- }
549
- function getPagePreset(pageType) {
550
- return PAGE_MOTIONS[pageType];
551
- }
552
- function getMotionPreset(type) {
553
- return MOTION_PRESETS[type] || MOTION_PRESETS.text;
554
- }
555
-
556
- // src/hooks/useSimplePageMotion.ts
557
7
  function useSimplePageMotion(pageType) {
558
8
  const config = getPagePreset(pageType);
559
9
  return useSimplePageMotions(config);
@@ -658,156 +108,6 @@ function useSimplePageMotions(config) {
658
108
  }, [motions]);
659
109
  return getPageMotionRefs();
660
110
  }
661
-
662
- // src/managers/MotionStateManager.ts
663
- var MotionStateManager = class {
664
- constructor() {
665
- this.states = /* @__PURE__ */ new Map();
666
- this.listeners = /* @__PURE__ */ new Map();
667
- }
668
- /**
669
- * 요소의 상태를 초기화
670
- */
671
- initializeElement(elementId, config) {
672
- const initialState = {
673
- internalVisibility: false,
674
- // 초기에 숨김 상태로 시작 (스크롤 리빌용)
675
- triggeredVisibility: false,
676
- // Intersection Observer가 아직 트리거되지 않음
677
- finalVisibility: false,
678
- // 초기에 숨김 상태로 시작
679
- opacity: 0,
680
- // 초기에 투명 상태로 시작
681
- translateY: 20,
682
- // 초기에 아래로 이동된 상태로 시작
683
- translateX: 0,
684
- scale: 0.95,
685
- // 초기에 약간 축소된 상태로 시작
686
- rotation: 0,
687
- isHovered: false,
688
- isClicked: false,
689
- isAnimating: false
690
- };
691
- this.states.set(elementId, initialState);
692
- this.computeFinalState(elementId);
693
- }
694
- /**
695
- * 내부 가시성 상태 업데이트 (초기화, 리셋 등)
696
- */
697
- setInternalVisibility(elementId, visible) {
698
- const state = this.states.get(elementId);
699
- if (!state) return;
700
- state.internalVisibility = visible;
701
- this.computeFinalState(elementId);
702
- this.notifyListeners(elementId, state);
703
- }
704
- /**
705
- * 외부 트리거 가시성 상태 업데이트 (Intersection Observer)
706
- */
707
- setTriggeredVisibility(elementId, visible) {
708
- const state = this.states.get(elementId);
709
- if (!state) return;
710
- state.triggeredVisibility = visible;
711
- this.computeFinalState(elementId);
712
- this.notifyListeners(elementId, state);
713
- }
714
- /**
715
- * 모션 값 업데이트
716
- */
717
- updateMotionValues(elementId, values) {
718
- const state = this.states.get(elementId);
719
- if (!state) return;
720
- Object.assign(state, values);
721
- this.notifyListeners(elementId, state);
722
- }
723
- /**
724
- * 최종 상태 계산
725
- */
726
- computeFinalState(elementId) {
727
- const state = this.states.get(elementId);
728
- if (!state) return;
729
- state.finalVisibility = state.internalVisibility || state.triggeredVisibility;
730
- state.isAnimating = state.finalVisibility && (state.opacity < 1 || state.translateY > 0);
731
- }
732
- /**
733
- * 현재 상태 조회
734
- */
735
- getState(elementId) {
736
- return this.states.get(elementId);
737
- }
738
- /**
739
- * 모든 상태 조회
740
- */
741
- getAllStates() {
742
- return new Map(this.states);
743
- }
744
- /**
745
- * 상태 변경 리스너 등록
746
- */
747
- subscribe(elementId, listener) {
748
- if (!this.listeners.has(elementId)) {
749
- this.listeners.set(elementId, /* @__PURE__ */ new Set());
750
- }
751
- this.listeners.get(elementId).add(listener);
752
- return () => {
753
- const listeners = this.listeners.get(elementId);
754
- if (listeners) {
755
- listeners.delete(listener);
756
- }
757
- };
758
- }
759
- /**
760
- * 리스너들에게 상태 변경 알림
761
- */
762
- notifyListeners(elementId, state) {
763
- const listeners = this.listeners.get(elementId);
764
- if (!listeners) return;
765
- listeners.forEach((listener) => {
766
- try {
767
- listener(state);
768
- } catch (error) {
769
- if (process.env.NODE_ENV === "development") {
770
- console.error(`MotionStateManager listener error for ${elementId}:`, error);
771
- }
772
- }
773
- });
774
- }
775
- /**
776
- * 모든 상태 초기화
777
- */
778
- reset() {
779
- this.states.clear();
780
- this.listeners.clear();
781
- }
782
- /**
783
- * 특정 요소 상태 초기화
784
- */
785
- resetElement(elementId) {
786
- this.states.delete(elementId);
787
- this.listeners.delete(elementId);
788
- }
789
- /**
790
- * 디버그용 상태 출력
791
- */
792
- debug() {
793
- if (process.env.NODE_ENV === "development") {
794
- console.log("MotionStateManager Debug:");
795
- this.states.forEach((state, elementId) => {
796
- console.log(` ${elementId}:`, {
797
- internalVisibility: state.internalVisibility,
798
- triggeredVisibility: state.triggeredVisibility,
799
- finalVisibility: state.finalVisibility,
800
- opacity: state.opacity,
801
- translateY: state.translateY,
802
- isAnimating: state.isAnimating
803
- });
804
- });
805
- }
806
- }
807
- };
808
- var motionStateManager = new MotionStateManager();
809
-
810
- // src/hooks/usePageMotions.ts
811
111
  function usePageMotions(config) {
812
112
  const [motions, setMotions] = useState(/* @__PURE__ */ new Map());
813
113
  const observersRef = useRef(/* @__PURE__ */ new Map());
@@ -1300,155 +600,6 @@ function useSmartMotion(options = {}) {
1300
600
  };
1301
601
  }
1302
602
 
1303
- // src/profiles/neutral.ts
1304
- var neutral = {
1305
- name: "neutral",
1306
- base: {
1307
- duration: 700,
1308
- easing: "ease-out",
1309
- threshold: 0.1,
1310
- triggerOnce: true
1311
- },
1312
- entrance: {
1313
- slide: {
1314
- distance: 32,
1315
- easing: "ease-out"
1316
- },
1317
- fade: {
1318
- initialOpacity: 0
1319
- },
1320
- scale: {
1321
- from: 0.95
1322
- },
1323
- bounce: {
1324
- intensity: 0.3,
1325
- easing: "cubic-bezier(0.34, 1.56, 0.64, 1)"
1326
- }
1327
- },
1328
- stagger: {
1329
- perItem: 100,
1330
- baseDelay: 0
1331
- },
1332
- interaction: {
1333
- hover: {
1334
- scale: 1.05,
1335
- y: -2,
1336
- duration: 200,
1337
- easing: "ease-out"
1338
- }
1339
- },
1340
- spring: {
1341
- mass: 1,
1342
- stiffness: 100,
1343
- damping: 10,
1344
- restDelta: 0.01,
1345
- restSpeed: 0.01
1346
- },
1347
- reducedMotion: "fade-only"
1348
- };
1349
-
1350
- // src/profiles/hua.ts
1351
- var hua = {
1352
- name: "hua",
1353
- base: {
1354
- duration: 640,
1355
- easing: "cubic-bezier(0.22, 0.68, 0.35, 1.10)",
1356
- threshold: 0.12,
1357
- triggerOnce: true
1358
- },
1359
- entrance: {
1360
- slide: {
1361
- distance: 28,
1362
- easing: "cubic-bezier(0.22, 0.68, 0.35, 1.14)"
1363
- },
1364
- fade: {
1365
- initialOpacity: 0
1366
- },
1367
- scale: {
1368
- from: 0.97
1369
- },
1370
- bounce: {
1371
- intensity: 0.2,
1372
- easing: "cubic-bezier(0.22, 0.68, 0.35, 1.12)"
1373
- }
1374
- },
1375
- stagger: {
1376
- perItem: 80,
1377
- baseDelay: 0
1378
- },
1379
- interaction: {
1380
- hover: {
1381
- scale: 1.008,
1382
- y: -1,
1383
- duration: 180,
1384
- easing: "cubic-bezier(0.22, 0.68, 0.35, 1.10)"
1385
- }
1386
- },
1387
- spring: {
1388
- mass: 1,
1389
- stiffness: 180,
1390
- damping: 18,
1391
- restDelta: 5e-3,
1392
- restSpeed: 5e-3
1393
- },
1394
- reducedMotion: "fade-only"
1395
- };
1396
-
1397
- // src/profiles/index.ts
1398
- var PROFILES = {
1399
- neutral,
1400
- hua
1401
- };
1402
- function resolveProfile(profile) {
1403
- if (typeof profile === "string") {
1404
- return PROFILES[profile] ?? neutral;
1405
- }
1406
- return profile;
1407
- }
1408
- function mergeProfileOverrides(base, overrides) {
1409
- return deepMerge(
1410
- base,
1411
- overrides
1412
- );
1413
- }
1414
- function deepMerge(target, source) {
1415
- const result = { ...target };
1416
- for (const key of Object.keys(source)) {
1417
- const sourceVal = source[key];
1418
- const targetVal = result[key];
1419
- if (sourceVal !== null && sourceVal !== void 0 && typeof sourceVal === "object" && !Array.isArray(sourceVal) && typeof targetVal === "object" && targetVal !== null && !Array.isArray(targetVal)) {
1420
- result[key] = deepMerge(
1421
- targetVal,
1422
- sourceVal
1423
- );
1424
- } else if (sourceVal !== void 0) {
1425
- result[key] = sourceVal;
1426
- }
1427
- }
1428
- return result;
1429
- }
1430
-
1431
- // src/profiles/MotionProfileContext.ts
1432
- var MotionProfileContext = createContext(neutral);
1433
- function MotionProfileProvider({
1434
- profile = "neutral",
1435
- overrides,
1436
- children
1437
- }) {
1438
- const resolved = useMemo(() => {
1439
- const base = resolveProfile(profile);
1440
- return overrides ? mergeProfileOverrides(base, overrides) : base;
1441
- }, [profile, overrides]);
1442
- return createElement(
1443
- MotionProfileContext.Provider,
1444
- { value: resolved },
1445
- children
1446
- );
1447
- }
1448
- function useMotionProfile() {
1449
- return useContext(MotionProfileContext);
1450
- }
1451
-
1452
603
  // src/utils/sharedIntersectionObserver.ts
1453
604
  var pool = /* @__PURE__ */ new Map();
1454
605
  function makeKey(threshold, rootMargin) {
@@ -1541,8 +692,8 @@ function getInitialStyle(type, distance) {
1541
692
  function getVisibleStyle() {
1542
693
  return { opacity: 1, transform: "none" };
1543
694
  }
1544
- function getEasingForType(type, easing2) {
1545
- if (easing2) return easing2;
695
+ function getEasingForType(type, easing) {
696
+ if (easing) return easing;
1546
697
  if (type === "bounceIn") return "cubic-bezier(0.34, 1.56, 0.64, 1)";
1547
698
  return "ease-out";
1548
699
  }
@@ -1608,8 +759,8 @@ function getMultiEffectVisibleStyle(effects) {
1608
759
  }
1609
760
  return style;
1610
761
  }
1611
- function getMultiEffectEasing(effects, easing2) {
1612
- if (easing2) return easing2;
762
+ function getMultiEffectEasing(effects, easing) {
763
+ if (easing) return easing;
1613
764
  if (effects.bounce) return "cubic-bezier(0.34, 1.56, 0.64, 1)";
1614
765
  return "ease-out";
1615
766
  }
@@ -1621,7 +772,7 @@ function useUnifiedMotion(options) {
1621
772
  duration = profile.base.duration,
1622
773
  autoStart = true,
1623
774
  delay = 0,
1624
- easing: easing2,
775
+ easing,
1625
776
  threshold = profile.base.threshold,
1626
777
  triggerOnce = profile.base.triggerOnce,
1627
778
  distance = profile.entrance.slide.distance,
@@ -1631,7 +782,7 @@ function useUnifiedMotion(options) {
1631
782
  onReset
1632
783
  } = options;
1633
784
  const resolvedType = type ?? "fadeIn";
1634
- const resolvedEasing = getEasingForType(resolvedType, easing2);
785
+ const resolvedEasing = getEasingForType(resolvedType, easing);
1635
786
  const ref = useRef(null);
1636
787
  const [isVisible, setIsVisible] = useState(false);
1637
788
  const [isAnimating, setIsAnimating] = useState(false);
@@ -1683,7 +834,7 @@ function useUnifiedMotion(options) {
1683
834
  const style = useMemo(() => {
1684
835
  if (effects) {
1685
836
  const base2 = isVisible ? getMultiEffectVisibleStyle(effects) : getMultiEffectInitialStyle(effects, distance);
1686
- const resolvedEasingMulti = getMultiEffectEasing(effects, easing2);
837
+ const resolvedEasingMulti = getMultiEffectEasing(effects, easing);
1687
838
  return {
1688
839
  ...base2,
1689
840
  transition: `all ${duration}ms ${resolvedEasingMulti}`,
@@ -1702,7 +853,7 @@ function useUnifiedMotion(options) {
1702
853
  "--motion-easing": resolvedEasing,
1703
854
  "--motion-progress": `${progress}`
1704
855
  };
1705
- }, [isVisible, type, effects, distance, duration, resolvedEasing, easing2, delay, progress, resolvedType]);
856
+ }, [isVisible, type, effects, distance, duration, resolvedEasing, easing, delay, progress, resolvedType]);
1706
857
  return {
1707
858
  ref,
1708
859
  isVisible,
@@ -1721,7 +872,7 @@ function useFadeIn(options = {}) {
1721
872
  duration = profile.base.duration,
1722
873
  threshold = profile.base.threshold,
1723
874
  triggerOnce = profile.base.triggerOnce,
1724
- easing: easing2 = profile.base.easing,
875
+ easing = profile.base.easing,
1725
876
  autoStart = true,
1726
877
  initialOpacity = profile.entrance.fade.initialOpacity,
1727
878
  targetOpacity = 1,
@@ -1809,12 +960,12 @@ function useFadeIn(options = {}) {
1809
960
  }, [stop]);
1810
961
  const style = useMemo(() => ({
1811
962
  opacity: isVisible ? targetOpacity : initialOpacity,
1812
- transition: `opacity ${duration}ms ${easing2}`,
963
+ transition: `opacity ${duration}ms ${easing}`,
1813
964
  "--motion-delay": `${delay}ms`,
1814
965
  "--motion-duration": `${duration}ms`,
1815
- "--motion-easing": easing2,
966
+ "--motion-easing": easing,
1816
967
  "--motion-progress": `${progress}`
1817
- }), [isVisible, targetOpacity, initialOpacity, duration, easing2, delay, progress]);
968
+ }), [isVisible, targetOpacity, initialOpacity, duration, easing, delay, progress]);
1818
969
  return {
1819
970
  ref,
1820
971
  isVisible,
@@ -1833,7 +984,7 @@ function useSlideUp(options = {}) {
1833
984
  duration = profile.base.duration,
1834
985
  threshold = profile.base.threshold,
1835
986
  triggerOnce = profile.base.triggerOnce,
1836
- easing: easing2 = profile.entrance.slide.easing,
987
+ easing = profile.entrance.slide.easing,
1837
988
  autoStart = true,
1838
989
  direction = "up",
1839
990
  distance = profile.entrance.slide.distance,
@@ -1935,14 +1086,14 @@ function useSlideUp(options = {}) {
1935
1086
  const style = useMemo(() => ({
1936
1087
  opacity: isVisible ? 1 : 0,
1937
1088
  transform: isVisible ? finalTransform : initialTransform,
1938
- transition: `opacity ${duration}ms ${easing2}, transform ${duration}ms ${easing2}`,
1089
+ transition: `opacity ${duration}ms ${easing}, transform ${duration}ms ${easing}`,
1939
1090
  "--motion-delay": `${delay}ms`,
1940
1091
  "--motion-duration": `${duration}ms`,
1941
- "--motion-easing": easing2,
1092
+ "--motion-easing": easing,
1942
1093
  "--motion-progress": `${progress}`,
1943
1094
  "--motion-direction": direction,
1944
1095
  "--motion-distance": `${distance}px`
1945
- }), [isVisible, initialTransform, finalTransform, duration, easing2, delay, progress, direction, distance]);
1096
+ }), [isVisible, initialTransform, finalTransform, duration, easing, delay, progress, direction, distance]);
1946
1097
  return {
1947
1098
  ref,
1948
1099
  isVisible,
@@ -1972,7 +1123,7 @@ function useScaleIn(options = {}) {
1972
1123
  duration = profile.base.duration,
1973
1124
  delay = 0,
1974
1125
  autoStart = true,
1975
- easing: easing2 = profile.base.easing,
1126
+ easing = profile.base.easing,
1976
1127
  threshold = profile.base.threshold,
1977
1128
  triggerOnce = profile.base.triggerOnce,
1978
1129
  onComplete,
@@ -2050,12 +1201,12 @@ function useScaleIn(options = {}) {
2050
1201
  const style = useMemo(() => ({
2051
1202
  transform: `scale(${scale})`,
2052
1203
  opacity,
2053
- transition: `all ${duration}ms ${easing2}`,
1204
+ transition: `all ${duration}ms ${easing}`,
2054
1205
  "--motion-delay": `${delay}ms`,
2055
1206
  "--motion-duration": `${duration}ms`,
2056
- "--motion-easing": easing2,
1207
+ "--motion-easing": easing,
2057
1208
  "--motion-progress": `${progress}`
2058
- }), [scale, opacity, duration, easing2, delay, progress]);
1209
+ }), [scale, opacity, duration, easing, delay, progress]);
2059
1210
  return {
2060
1211
  ref,
2061
1212
  isVisible,
@@ -2076,7 +1227,7 @@ function useBounceIn(options = {}) {
2076
1227
  intensity = profile.entrance.bounce.intensity,
2077
1228
  threshold = profile.base.threshold,
2078
1229
  triggerOnce = profile.base.triggerOnce,
2079
- easing: easing2 = profile.entrance.bounce.easing,
1230
+ easing = profile.entrance.bounce.easing,
2080
1231
  onComplete,
2081
1232
  onStart,
2082
1233
  onStop,
@@ -2161,12 +1312,12 @@ function useBounceIn(options = {}) {
2161
1312
  const style = useMemo(() => ({
2162
1313
  transform: `scale(${scale})`,
2163
1314
  opacity,
2164
- transition: `all ${duration}ms ${easing2}`,
1315
+ transition: `all ${duration}ms ${easing}`,
2165
1316
  "--motion-delay": `${delay}ms`,
2166
1317
  "--motion-duration": `${duration}ms`,
2167
- "--motion-easing": easing2,
1318
+ "--motion-easing": easing,
2168
1319
  "--motion-progress": `${progress}`
2169
- }), [scale, opacity, duration, easing2, delay, progress]);
1320
+ }), [scale, opacity, duration, easing, delay, progress]);
2170
1321
  return {
2171
1322
  ref,
2172
1323
  isVisible,
@@ -2178,201 +1329,6 @@ function useBounceIn(options = {}) {
2178
1329
  stop
2179
1330
  };
2180
1331
  }
2181
-
2182
- // src/utils/easing.ts
2183
- var linear = (t) => t;
2184
- var easeIn = (t) => t * t;
2185
- var easeOut = (t) => 1 - (1 - t) * (1 - t);
2186
- var easeInOut = (t) => t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
2187
- var easeInQuad = (t) => t * t;
2188
- var easeOutQuad = (t) => 1 - (1 - t) * (1 - t);
2189
- var easeInOutQuad = (t) => t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
2190
- var easeInCubic = (t) => t * t * t;
2191
- var easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
2192
- var easeInOutCubic = (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
2193
- var easeInQuart = (t) => t * t * t * t;
2194
- var easeOutQuart = (t) => 1 - Math.pow(1 - t, 4);
2195
- var easeInOutQuart = (t) => t < 0.5 ? 8 * t * t * t * t : 1 - Math.pow(-2 * t + 2, 4) / 2;
2196
- var easeInQuint = (t) => t * t * t * t * t;
2197
- var easeOutQuint = (t) => 1 - Math.pow(1 - t, 5);
2198
- var easeInOutQuint = (t) => t < 0.5 ? 16 * t * t * t * t * t : 1 - Math.pow(-2 * t + 2, 5) / 2;
2199
- var easeInSine = (t) => 1 - Math.cos(t * Math.PI / 2);
2200
- var easeOutSine = (t) => Math.sin(t * Math.PI / 2);
2201
- var easeInOutSine = (t) => -(Math.cos(Math.PI * t) - 1) / 2;
2202
- var easeInExpo = (t) => t === 0 ? 0 : Math.pow(2, 10 * t - 10);
2203
- var easeOutExpo = (t) => t === 1 ? 1 : 1 - Math.pow(2, -10 * t);
2204
- var easeInOutExpo = (t) => {
2205
- if (t === 0) return 0;
2206
- if (t === 1) return 1;
2207
- if (t < 0.5) return Math.pow(2, 20 * t - 10) / 2;
2208
- return (2 - Math.pow(2, -20 * t + 10)) / 2;
2209
- };
2210
- var easeInCirc = (t) => 1 - Math.sqrt(1 - Math.pow(t, 2));
2211
- var easeOutCirc = (t) => Math.sqrt(1 - Math.pow(t - 1, 2));
2212
- var easeInOutCirc = (t) => {
2213
- if (t < 0.5) return (1 - Math.sqrt(1 - Math.pow(2 * t, 2))) / 2;
2214
- return (Math.sqrt(1 - Math.pow(-2 * t + 2, 2)) + 1) / 2;
2215
- };
2216
- var easeInBounce = (t) => 1 - easeOutBounce(1 - t);
2217
- var easeOutBounce = (t) => {
2218
- const n1 = 7.5625;
2219
- const d1 = 2.75;
2220
- if (t < 1 / d1) {
2221
- return n1 * t * t;
2222
- } else if (t < 2 / d1) {
2223
- return n1 * (t -= 1.5 / d1) * t + 0.75;
2224
- } else if (t < 2.5 / d1) {
2225
- return n1 * (t -= 2.25 / d1) * t + 0.9375;
2226
- } else {
2227
- return n1 * (t -= 2.625 / d1) * t + 0.984375;
2228
- }
2229
- };
2230
- var easeInOutBounce = (t) => {
2231
- if (t < 0.5) return (1 - easeOutBounce(1 - 2 * t)) / 2;
2232
- return (1 + easeOutBounce(2 * t - 1)) / 2;
2233
- };
2234
- var easeInBack = (t) => {
2235
- const c1 = 1.70158;
2236
- const c3 = c1 + 1;
2237
- return c3 * t * t * t - c1 * t * t;
2238
- };
2239
- var easeOutBack = (t) => {
2240
- const c1 = 1.70158;
2241
- const c3 = c1 + 1;
2242
- return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);
2243
- };
2244
- var easeInOutBack = (t) => {
2245
- const c1 = 1.70158;
2246
- const c2 = c1 * 1.525;
2247
- if (t < 0.5) {
2248
- return Math.pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2) / 2;
2249
- } else {
2250
- return (Math.pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2;
2251
- }
2252
- };
2253
- var easeInElastic = (t) => {
2254
- const c4 = 2 * Math.PI / 3;
2255
- if (t === 0) return 0;
2256
- if (t === 1) return 1;
2257
- return -Math.pow(2, 10 * t - 10) * Math.sin((t * 10 - 0.75) * c4);
2258
- };
2259
- var easeOutElastic = (t) => {
2260
- const c4 = 2 * Math.PI / 3;
2261
- if (t === 0) return 0;
2262
- if (t === 1) return 1;
2263
- return Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1;
2264
- };
2265
- var easeInOutElastic = (t) => {
2266
- const c5 = 2 * Math.PI / 4.5;
2267
- if (t === 0) return 0;
2268
- if (t === 1) return 1;
2269
- if (t < 0.5) {
2270
- return -(Math.pow(2, 20 * t - 10) * Math.sin((20 * t - 11.125) * c5)) / 2;
2271
- } else {
2272
- return Math.pow(2, -20 * t + 10) * Math.sin((20 * t - 11.125) * c5) / 2 + 1;
2273
- }
2274
- };
2275
- var pulse = (t) => Math.sin(t * Math.PI) * 0.5 + 0.5;
2276
- var pulseSmooth = (t) => Math.sin(t * Math.PI * 2) * 0.3 + 0.7;
2277
- var skeletonWave = (t) => Math.sin((t - 0.5) * Math.PI * 2) * 0.5 + 0.5;
2278
- var blink = (t) => t < 0.5 ? 1 : 0;
2279
- var easing = {
2280
- linear,
2281
- easeIn,
2282
- easeOut,
2283
- easeInOut,
2284
- easeInQuad,
2285
- easeOutQuad,
2286
- easeInOutQuad,
2287
- easeInCubic,
2288
- easeOutCubic,
2289
- easeInOutCubic,
2290
- easeInQuart,
2291
- easeOutQuart,
2292
- easeInOutQuart,
2293
- easeInQuint,
2294
- easeOutQuint,
2295
- easeInOutQuint,
2296
- easeInSine,
2297
- easeOutSine,
2298
- easeInOutSine,
2299
- easeInExpo,
2300
- easeOutExpo,
2301
- easeInOutExpo,
2302
- easeInCirc,
2303
- easeOutCirc,
2304
- easeInOutCirc,
2305
- easeInBounce,
2306
- easeOutBounce,
2307
- easeInOutBounce,
2308
- easeInBack,
2309
- easeOutBack,
2310
- easeInOutBack,
2311
- easeInElastic,
2312
- easeOutElastic,
2313
- easeInOutElastic,
2314
- pulse,
2315
- pulseSmooth,
2316
- skeletonWave,
2317
- blink
2318
- };
2319
- function isValidEasing(easingName) {
2320
- return easingName in easing;
2321
- }
2322
- function getEasing(easingName) {
2323
- if (typeof easingName === "function") {
2324
- return easingName;
2325
- }
2326
- if (typeof easingName === "string") {
2327
- if (easingName in easing) {
2328
- return easing[easingName];
2329
- }
2330
- if (easingName === "bounce") {
2331
- return easing.easeOutBounce;
2332
- }
2333
- if (process.env.NODE_ENV === "development") {
2334
- console.warn(`[HUA Motion] Unknown easing "${easingName}", using default "easeOut".`);
2335
- }
2336
- }
2337
- return easeOut;
2338
- }
2339
- function applyEasing(t, easingName) {
2340
- const easingFn = getEasing(easingName);
2341
- return easingFn(t);
2342
- }
2343
- function safeApplyEasing(t, easingName) {
2344
- try {
2345
- const easingFn = getEasing(easingName);
2346
- return easingFn(t);
2347
- } catch (err) {
2348
- if (process.env.NODE_ENV === "development") {
2349
- console.error(`[HUA Motion] Failed to apply easing "${easingName}":`, err);
2350
- }
2351
- return easeOut(t);
2352
- }
2353
- }
2354
- function getAvailableEasings() {
2355
- return Object.keys(easing);
2356
- }
2357
- function isEasingFunction(value) {
2358
- return typeof value === "function";
2359
- }
2360
- var easingPresets = {
2361
- default: "easeOut",
2362
- smooth: "easeInOutCubic",
2363
- fast: "easeOutQuad",
2364
- slow: "easeInOutSine",
2365
- bouncy: "easeOutBounce",
2366
- elastic: "easeOutElastic",
2367
- fade: "easeInOut",
2368
- scale: "easeOutBack"
2369
- };
2370
- function getPresetEasing(preset) {
2371
- const easingName = easingPresets[preset];
2372
- return getEasing(easingName);
2373
- }
2374
-
2375
- // src/hooks/usePulse.ts
2376
1332
  function usePulse(options = {}) {
2377
1333
  const {
2378
1334
  duration = 3e3,
@@ -2476,20 +1432,6 @@ function usePulse(options = {}) {
2476
1432
  reset
2477
1433
  };
2478
1434
  }
2479
-
2480
- // src/utils/springPhysics.ts
2481
- function calculateSpring(currentValue, currentVelocity, targetValue, deltaTime, config) {
2482
- const { stiffness, damping, mass } = config;
2483
- const displacement = currentValue - targetValue;
2484
- const springForce = -stiffness * displacement;
2485
- const dampingForce = -damping * currentVelocity;
2486
- const acceleration = (springForce + dampingForce) / mass;
2487
- const newVelocity = currentVelocity + acceleration * deltaTime;
2488
- const newValue = currentValue + newVelocity * deltaTime;
2489
- return { value: newValue, velocity: newVelocity };
2490
- }
2491
-
2492
- // src/hooks/useSpringMotion.ts
2493
1435
  function useSpringMotion(options) {
2494
1436
  const profile = useMotionProfile();
2495
1437
  const {
@@ -2629,7 +1571,7 @@ function useGradient(options = {}) {
2629
1571
  duration = 6e3,
2630
1572
  direction = "diagonal",
2631
1573
  size = 300,
2632
- easing: easing2 = "ease-in-out",
1574
+ easing = "ease-in-out",
2633
1575
  autoStart = false
2634
1576
  } = options;
2635
1577
  const ref = useRef(null);
@@ -2646,10 +1588,10 @@ function useGradient(options = {}) {
2646
1588
  return {
2647
1589
  background,
2648
1590
  backgroundSize,
2649
- animation: isAnimating ? `gradientShift ${duration}ms ${easing2} infinite` : "none",
1591
+ animation: isAnimating ? `gradientShift ${duration}ms ${easing} infinite` : "none",
2650
1592
  backgroundPosition: isAnimating ? void 0 : `${motionProgress}% 50%`
2651
1593
  };
2652
- }, [colors, direction, size, duration, easing2, isAnimating, motionProgress]);
1594
+ }, [colors, direction, size, duration, easing, isAnimating, motionProgress]);
2653
1595
  const start = useCallback(() => {
2654
1596
  setIsAnimating(true);
2655
1597
  }, []);
@@ -2697,7 +1639,7 @@ function useHoverMotion(options = {}) {
2697
1639
  const profile = useMotionProfile();
2698
1640
  const {
2699
1641
  duration = profile.interaction.hover.duration,
2700
- easing: easing2 = profile.interaction.hover.easing,
1642
+ easing = profile.interaction.hover.easing,
2701
1643
  hoverScale = profile.interaction.hover.scale,
2702
1644
  hoverY = profile.interaction.hover.y,
2703
1645
  hoverOpacity = 1
@@ -2731,8 +1673,8 @@ function useHoverMotion(options = {}) {
2731
1673
  const style = useMemo(() => ({
2732
1674
  transform: isHovered ? `scale(${hoverScale}) translateY(${hoverY}px)` : "scale(1) translateY(0px)",
2733
1675
  opacity: isHovered ? hoverOpacity : 1,
2734
- transition: `transform ${duration}ms ${easing2}, opacity ${duration}ms ${easing2}`
2735
- }), [isHovered, hoverScale, hoverY, hoverOpacity, duration, easing2]);
1676
+ transition: `transform ${duration}ms ${easing}, opacity ${duration}ms ${easing}`
1677
+ }), [isHovered, hoverScale, hoverY, hoverOpacity, duration, easing]);
2736
1678
  const start = useCallback(() => {
2737
1679
  setIsHovered(true);
2738
1680
  setIsAnimating(true);
@@ -2990,7 +1932,7 @@ function useScrollReveal(options = {}) {
2990
1932
  triggerOnce = profile.base.triggerOnce,
2991
1933
  delay = 0,
2992
1934
  duration = profile.base.duration,
2993
- easing: easing2 = profile.base.easing,
1935
+ easing = profile.base.easing,
2994
1936
  motionType = "fadeIn",
2995
1937
  onComplete,
2996
1938
  onStart,
@@ -3028,7 +1970,7 @@ function useScrollReveal(options = {}) {
3028
1970
  const slideDistance = profile.entrance.slide.distance;
3029
1971
  const scaleFrom = profile.entrance.scale.from;
3030
1972
  const style = useMemo(() => {
3031
- const baseTransition = `all ${duration}ms ${easing2}`;
1973
+ const baseTransition = `all ${duration}ms ${easing}`;
3032
1974
  if (!isVisible) {
3033
1975
  switch (motionType) {
3034
1976
  case "fadeIn":
@@ -3078,7 +2020,7 @@ function useScrollReveal(options = {}) {
3078
2020
  transform: "none",
3079
2021
  transition: baseTransition
3080
2022
  };
3081
- }, [isVisible, motionType, duration, easing2, slideDistance, scaleFrom]);
2023
+ }, [isVisible, motionType, duration, easing, slideDistance, scaleFrom]);
3082
2024
  const start = useCallback(() => {
3083
2025
  setIsAnimating(true);
3084
2026
  onStart?.();
@@ -3300,14 +2242,7 @@ function useMotionState(options = {}) {
3300
2242
  const seek = useCallback((targetProgress) => {
3301
2243
  const clampedProgress = Math.max(0, Math.min(100, targetProgress));
3302
2244
  setProgress(clampedProgress);
3303
- let targetElapsed = 0;
3304
- if (currentDirection === "reverse") {
3305
- targetElapsed = (100 - clampedProgress) / 100 * duration;
3306
- } else if (currentDirection === "alternate") {
3307
- targetElapsed = clampedProgress / 100 * duration;
3308
- } else {
3309
- targetElapsed = clampedProgress / 100 * duration;
3310
- }
2245
+ const targetElapsed = currentDirection === "reverse" ? (100 - clampedProgress) / 100 * duration : clampedProgress / 100 * duration;
3311
2246
  setElapsed(targetElapsed);
3312
2247
  if (state === "playing" && startTimeRef.current) {
3313
2248
  const currentTime = performance.now();
@@ -3476,7 +2411,7 @@ function useRepeat(options = {}) {
3476
2411
  };
3477
2412
  }
3478
2413
  function useToggleMotion(options = {}) {
3479
- const { duration = 300, delay = 0, easing: easing2 = "ease-in-out" } = options;
2414
+ const { duration = 300, delay = 0, easing = "ease-in-out" } = options;
3480
2415
  const ref = useRef(null);
3481
2416
  const [isVisible, setIsVisible] = useState(false);
3482
2417
  const [isAnimating, setIsAnimating] = useState(false);
@@ -3506,8 +2441,8 @@ function useToggleMotion(options = {}) {
3506
2441
  const style = useMemo(() => ({
3507
2442
  opacity: isVisible ? 1 : 0,
3508
2443
  transform: isVisible ? "translateY(0) scale(1)" : "translateY(10px) scale(0.95)",
3509
- transition: `opacity ${duration}ms ${easing2} ${delay}ms, transform ${duration}ms ${easing2} ${delay}ms`
3510
- }), [isVisible, duration, easing2, delay]);
2444
+ transition: `opacity ${duration}ms ${easing} ${delay}ms, transform ${duration}ms ${easing} ${delay}ms`
2445
+ }), [isVisible, duration, easing, delay]);
3511
2446
  return {
3512
2447
  ref,
3513
2448
  isVisible,
@@ -3978,7 +2913,7 @@ function useGestureMotion(options) {
3978
2913
  const {
3979
2914
  gestureType,
3980
2915
  duration = 300,
3981
- easing: easing2 = "ease-out",
2916
+ easing = "ease-out",
3982
2917
  sensitivity = 1,
3983
2918
  enabled = true,
3984
2919
  onGestureStart,
@@ -4020,10 +2955,10 @@ function useGestureMotion(options) {
4020
2955
  }
4021
2956
  setMotionStyle({
4022
2957
  transform,
4023
- transition: isActive ? "none" : `all ${duration}ms ${easing2}`,
2958
+ transition: isActive ? "none" : `all ${duration}ms ${easing}`,
4024
2959
  cursor: gestureType === "drag" && isActive ? "grabbing" : "pointer"
4025
2960
  });
4026
- }, [gestureState, gestureType, enabled, duration, easing2, sensitivity]);
2961
+ }, [gestureState, gestureType, enabled, duration, easing, sensitivity]);
4027
2962
  const handleMouseDown = useCallback((e) => {
4028
2963
  if (!enabled || gestureType !== "drag") return;
4029
2964
  isDragging.current = true;
@@ -4122,7 +3057,7 @@ function useGestureMotion(options) {
4122
3057
  function useButtonEffect(options = {}) {
4123
3058
  const {
4124
3059
  duration = 200,
4125
- easing: easing2 = "ease-out",
3060
+ easing = "ease-out",
4126
3061
  type = "scale",
4127
3062
  scaleAmount = 0.95,
4128
3063
  rippleColor = "rgba(255, 255, 255, 0.6)",
@@ -4396,7 +3331,7 @@ function useButtonEffect(options = {}) {
4396
3331
  `,
4397
3332
  boxShadow,
4398
3333
  opacity: disabled ? disabledOpacity : 1,
4399
- transition: `all ${duration}ms ${easing2}`,
3334
+ transition: `all ${duration}ms ${easing}`,
4400
3335
  willChange: "transform, box-shadow, opacity",
4401
3336
  cursor: disabled ? "not-allowed" : "pointer",
4402
3337
  position: "relative",
@@ -4433,7 +3368,7 @@ function useButtonEffect(options = {}) {
4433
3368
  function useVisibilityToggle(options = {}) {
4434
3369
  const {
4435
3370
  duration = 300,
4436
- easing: easing2 = "ease-out",
3371
+ easing = "ease-out",
4437
3372
  showScale = 1,
4438
3373
  showOpacity = 1,
4439
3374
  showRotate = 0,
@@ -4521,7 +3456,7 @@ function useVisibilityToggle(options = {}) {
4521
3456
  translate(${isVisible ? showTranslateX : hideTranslateX}px, ${isVisible ? showTranslateY : hideTranslateY}px)
4522
3457
  `,
4523
3458
  opacity: isVisible ? showOpacity : hideOpacity,
4524
- transition: `all ${duration}ms ${easing2}`,
3459
+ transition: `all ${duration}ms ${easing}`,
4525
3460
  willChange: "transform, opacity"
4526
3461
  };
4527
3462
  return {
@@ -4544,7 +3479,7 @@ function useVisibilityToggle(options = {}) {
4544
3479
  function useScrollToggle(options = {}) {
4545
3480
  const {
4546
3481
  duration = 300,
4547
- easing: easing2 = "ease-out",
3482
+ easing = "ease-out",
4548
3483
  showScale = 1,
4549
3484
  showOpacity = 1,
4550
3485
  showRotate = 0,
@@ -4653,7 +3588,7 @@ function useScrollToggle(options = {}) {
4653
3588
  translate(${isVisible ? showTranslateX : hideTranslateX}px, ${isVisible ? showTranslateY : hideTranslateY}px)
4654
3589
  `,
4655
3590
  opacity: isVisible ? showOpacity : hideOpacity,
4656
- transition: `all ${duration}ms ${easing2}`,
3591
+ transition: `all ${duration}ms ${easing}`,
4657
3592
  willChange: "transform, opacity"
4658
3593
  };
4659
3594
  return {
@@ -4672,7 +3607,7 @@ function useScrollToggle(options = {}) {
4672
3607
  function useCardList(options = {}) {
4673
3608
  const {
4674
3609
  duration = 500,
4675
- easing: easing2 = "ease-out",
3610
+ easing = "ease-out",
4676
3611
  staggerDelay = 100,
4677
3612
  cardScale = 1,
4678
3613
  cardOpacity = 1,
@@ -4714,7 +3649,7 @@ function useCardList(options = {}) {
4714
3649
  translate(${isCardVisible ? cardTranslateX : initialTranslateX}px, ${isCardVisible ? cardTranslateY : initialTranslateY}px)
4715
3650
  `,
4716
3651
  opacity: isCardVisible ? cardOpacity : initialOpacity,
4717
- transition: `all ${duration}ms ${easing2} ${delay}ms`,
3652
+ transition: `all ${duration}ms ${easing} ${delay}ms`,
4718
3653
  willChange: "transform, opacity"
4719
3654
  };
4720
3655
  });
@@ -4793,7 +3728,7 @@ function useCardList(options = {}) {
4793
3728
  function useLoadingSpinner(options = {}) {
4794
3729
  const {
4795
3730
  duration = 1e3,
4796
- easing: easing2 = "linear",
3731
+ easing = "linear",
4797
3732
  type = "rotate",
4798
3733
  rotationSpeed = 1,
4799
3734
  pulseSpeed = 1,
@@ -4957,7 +3892,7 @@ function useLoadingSpinner(options = {}) {
4957
3892
  borderTop: `${thickness}px solid ${color}`,
4958
3893
  borderRadius: "50%",
4959
3894
  transform: `rotate(${rotationAngle}deg)`,
4960
- transition: `transform ${duration}ms ${easing2}`
3895
+ transition: `transform ${duration}ms ${easing}`
4961
3896
  };
4962
3897
  case "pulse":
4963
3898
  return {
@@ -4965,7 +3900,7 @@ function useLoadingSpinner(options = {}) {
4965
3900
  backgroundColor: color,
4966
3901
  borderRadius: "50%",
4967
3902
  transform: `scale(${pulseScale})`,
4968
- transition: `transform ${duration}ms ${easing2}`
3903
+ transition: `transform ${duration}ms ${easing}`
4969
3904
  };
4970
3905
  case "bounce":
4971
3906
  return {
@@ -4973,7 +3908,7 @@ function useLoadingSpinner(options = {}) {
4973
3908
  backgroundColor: color,
4974
3909
  borderRadius: "50%",
4975
3910
  transform: `translateY(${bounceOffset}px)`,
4976
- transition: `transform ${duration}ms ${easing2}`
3911
+ transition: `transform ${duration}ms ${easing}`
4977
3912
  };
4978
3913
  case "wave":
4979
3914
  return {
@@ -5028,7 +3963,7 @@ function useLoadingSpinner(options = {}) {
5028
3963
  function useNavigation(options = {}) {
5029
3964
  const {
5030
3965
  duration = 300,
5031
- easing: easing2 = "ease-out",
3966
+ easing = "ease-out",
5032
3967
  type = "slide",
5033
3968
  slideDirection = "left",
5034
3969
  staggerDelay = 50,
@@ -5184,14 +4119,14 @@ function useNavigation(options = {}) {
5184
4119
  translate(${translateX}px, ${translateY}px)
5185
4120
  `,
5186
4121
  opacity,
5187
- transition: `all ${duration}ms ${easing2} ${delay}ms`,
4122
+ transition: `all ${duration}ms ${easing} ${delay}ms`,
5188
4123
  willChange: "transform, opacity",
5189
4124
  cursor: "pointer"
5190
4125
  };
5191
4126
  });
5192
4127
  const getNavigationStyle = () => {
5193
4128
  const baseStyle = {
5194
- transition: `all ${duration}ms ${easing2}`,
4129
+ transition: `all ${duration}ms ${easing}`,
5195
4130
  willChange: "transform, opacity"
5196
4131
  };
5197
4132
  switch (type) {
@@ -5245,7 +4180,7 @@ function useSkeleton(options = {}) {
5245
4180
  const {
5246
4181
  delay = 0,
5247
4182
  duration = 1500,
5248
- easing: easing2 = "ease-in-out",
4183
+ easing = "ease-in-out",
5249
4184
  autoStart = true,
5250
4185
  backgroundColor = "#f0f0f0",
5251
4186
  highlightColor = "#e0e0e0",
@@ -5254,7 +4189,7 @@ function useSkeleton(options = {}) {
5254
4189
  width = "100%",
5255
4190
  borderRadius = 4,
5256
4191
  wave = true,
5257
- pulse: pulse2 = false,
4192
+ pulse = false,
5258
4193
  onComplete,
5259
4194
  onStart,
5260
4195
  onStop,
@@ -5337,13 +4272,13 @@ function useSkeleton(options = {}) {
5337
4272
  position: "relative",
5338
4273
  overflow: "hidden",
5339
4274
  opacity: isVisible ? 1 : 0,
5340
- transition: `opacity ${duration}ms ${easing2}`
4275
+ transition: `opacity ${duration}ms ${easing}`
5341
4276
  };
5342
4277
  if (wave && isAnimating) {
5343
4278
  baseStyle.background = `linear-gradient(90deg, ${backgroundColor} 25%, ${highlightColor} 50%, ${backgroundColor} 75%)`;
5344
4279
  baseStyle.backgroundSize = "200% 100%";
5345
4280
  baseStyle.animation = `skeleton-wave ${motionSpeed}ms infinite linear`;
5346
- } else if (pulse2 && isAnimating) {
4281
+ } else if (pulse && isAnimating) {
5347
4282
  baseStyle.animation = `skeleton-pulse ${motionSpeed}ms infinite ease-in-out`;
5348
4283
  }
5349
4284
  return baseStyle;
@@ -5668,52 +4603,2672 @@ function useElementProgress(options = {}) {
5668
4603
  }, [start, end, clamp]);
5669
4604
  return { ref, progress, isInView };
5670
4605
  }
5671
- function Motion({
5672
- as: Component = "div",
5673
- type,
5674
- effects,
5675
- scroll,
5676
- delay,
5677
- duration,
5678
- children,
5679
- className,
5680
- style: userStyle,
5681
- ...rest
5682
- }) {
5683
- const scrollOptions = useMemo(() => {
5684
- if (!scroll) return null;
5685
- const base = typeof scroll === "object" ? scroll : {};
5686
- return {
5687
- ...base,
5688
- ...delay != null && { delay },
5689
- ...duration != null && { duration },
5690
- ...type != null && { motionType: type }
5691
- };
5692
- }, [scroll, delay, duration, type]);
5693
- const scrollMotion = useScrollReveal(scrollOptions ?? { delay: 0 });
5694
- const unifiedMotion = useUnifiedMotion({
5695
- type: type ?? "fadeIn",
5696
- effects,
5697
- delay,
5698
- duration,
5699
- autoStart: true
5700
- });
5701
- const isScroll = scroll != null && scroll !== false;
5702
- const motion = isScroll ? scrollMotion : unifiedMotion;
5703
- const mergedStyle = useMemo(() => {
5704
- if (!userStyle) return motion.style;
5705
- return { ...motion.style, ...userStyle };
5706
- }, [motion.style, userStyle]);
5707
- return /* @__PURE__ */ jsx(
5708
- Component,
5709
- {
5710
- ref: motion.ref,
5711
- className,
5712
- style: mergedStyle,
5713
- ...rest,
5714
- children
5715
- }
4606
+ function useAutoFade(options = {}) {
4607
+ const {
4608
+ initialOpacity = 0,
4609
+ targetOpacity = 1,
4610
+ duration = 1e3,
4611
+ delay = 0,
4612
+ repeat = false,
4613
+ repeatDelay = 1e3,
4614
+ repeatCount = -1,
4615
+ ease = "ease-in-out",
4616
+ autoStart = true,
4617
+ onComplete,
4618
+ onRepeat,
4619
+ showOnMount = false
4620
+ } = options;
4621
+ const [opacity, setOpacity] = useState(showOnMount ? initialOpacity : 0);
4622
+ const [isAnimating, setIsAnimating] = useState(false);
4623
+ const [isVisible, setIsVisible] = useState(
4624
+ showOnMount ? initialOpacity > 0 : false
4625
+ );
4626
+ const [mounted, setMounted] = useState(false);
4627
+ const motionRef = useRef(null);
4628
+ const timeoutRef = useRef(null);
4629
+ const repeatCountRef = useRef(0);
4630
+ const isFadingInRef = useRef(true);
4631
+ useEffect(() => {
4632
+ setMounted(true);
4633
+ }, []);
4634
+ const getEasing2 = useCallback(
4635
+ (t) => {
4636
+ switch (ease) {
4637
+ case "linear":
4638
+ return t;
4639
+ case "ease-in":
4640
+ return t * t;
4641
+ case "ease-out":
4642
+ return 1 - (1 - t) * (1 - t);
4643
+ case "ease-in-out":
4644
+ return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
4645
+ default:
4646
+ return t;
4647
+ }
4648
+ },
4649
+ [ease]
4650
+ );
4651
+ const animate = useCallback(
4652
+ (from, to, onFinish) => {
4653
+ if (!mounted) return;
4654
+ setIsAnimating(true);
4655
+ const startTime = performance.now();
4656
+ const startOpacity = from;
4657
+ const updateOpacity = (currentTime) => {
4658
+ const elapsed = currentTime - startTime;
4659
+ const progress = Math.min(elapsed / duration, 1);
4660
+ const easedProgress = getEasing2(progress);
4661
+ const currentOpacity = startOpacity + (to - startOpacity) * easedProgress;
4662
+ setOpacity(currentOpacity);
4663
+ setIsVisible(currentOpacity > 0);
4664
+ if (progress < 1) {
4665
+ motionRef.current = requestAnimationFrame(updateOpacity);
4666
+ } else {
4667
+ setIsAnimating(false);
4668
+ onFinish?.();
4669
+ }
4670
+ };
4671
+ motionRef.current = requestAnimationFrame(updateOpacity);
4672
+ },
4673
+ [mounted, duration, getEasing2]
5716
4674
  );
4675
+ const fadeIn = useCallback(() => {
4676
+ if (!mounted || isAnimating) return;
4677
+ animate(initialOpacity, targetOpacity, () => {
4678
+ onComplete?.();
4679
+ if (repeat && (repeatCount === -1 || repeatCountRef.current < repeatCount)) {
4680
+ repeatCountRef.current++;
4681
+ onRepeat?.(repeatCountRef.current);
4682
+ timeoutRef.current = window.setTimeout(() => {
4683
+ fadeOut();
4684
+ }, repeatDelay);
4685
+ }
4686
+ });
4687
+ }, [
4688
+ mounted,
4689
+ isAnimating,
4690
+ animate,
4691
+ initialOpacity,
4692
+ targetOpacity,
4693
+ onComplete,
4694
+ repeat,
4695
+ repeatCount,
4696
+ repeatDelay,
4697
+ onRepeat
4698
+ ]);
4699
+ const fadeOut = useCallback(() => {
4700
+ if (!mounted || isAnimating) return;
4701
+ animate(targetOpacity, initialOpacity, () => {
4702
+ onComplete?.();
4703
+ if (repeat && (repeatCount === -1 || repeatCountRef.current < repeatCount)) {
4704
+ repeatCountRef.current++;
4705
+ onRepeat?.(repeatCountRef.current);
4706
+ timeoutRef.current = window.setTimeout(() => {
4707
+ fadeIn();
4708
+ }, repeatDelay);
4709
+ }
4710
+ });
4711
+ }, [
4712
+ mounted,
4713
+ isAnimating,
4714
+ animate,
4715
+ targetOpacity,
4716
+ initialOpacity,
4717
+ onComplete,
4718
+ repeat,
4719
+ repeatCount,
4720
+ repeatDelay,
4721
+ onRepeat
4722
+ ]);
4723
+ const start = useCallback(() => {
4724
+ if (!mounted || isAnimating) return;
4725
+ if (delay > 0) {
4726
+ timeoutRef.current = window.setTimeout(() => {
4727
+ fadeIn();
4728
+ }, delay);
4729
+ } else {
4730
+ fadeIn();
4731
+ }
4732
+ }, [mounted, isAnimating, delay, fadeIn]);
4733
+ const stop = useCallback(() => {
4734
+ if (motionRef.current !== null) {
4735
+ cancelAnimationFrame(motionRef.current);
4736
+ motionRef.current = null;
4737
+ }
4738
+ if (timeoutRef.current !== null) {
4739
+ clearTimeout(timeoutRef.current);
4740
+ timeoutRef.current = null;
4741
+ }
4742
+ setIsAnimating(false);
4743
+ }, []);
4744
+ const reset = useCallback(() => {
4745
+ stop();
4746
+ setOpacity(initialOpacity);
4747
+ setIsVisible(initialOpacity > 0);
4748
+ repeatCountRef.current = 0;
4749
+ isFadingInRef.current = true;
4750
+ }, [stop, initialOpacity]);
4751
+ const toggle = useCallback(() => {
4752
+ if (isFadingInRef.current) {
4753
+ fadeOut();
4754
+ isFadingInRef.current = false;
4755
+ } else {
4756
+ fadeIn();
4757
+ isFadingInRef.current = true;
4758
+ }
4759
+ }, [fadeIn, fadeOut]);
4760
+ useEffect(() => {
4761
+ if (mounted && autoStart) {
4762
+ start();
4763
+ }
4764
+ }, [mounted, autoStart, start]);
4765
+ useEffect(() => {
4766
+ return () => {
4767
+ if (motionRef.current !== null) {
4768
+ cancelAnimationFrame(motionRef.current);
4769
+ }
4770
+ if (timeoutRef.current !== null) {
4771
+ clearTimeout(timeoutRef.current);
4772
+ }
4773
+ };
4774
+ }, []);
4775
+ return {
4776
+ opacity,
4777
+ isAnimating,
4778
+ isVisible,
4779
+ mounted,
4780
+ start,
4781
+ stop,
4782
+ reset,
4783
+ fadeIn,
4784
+ fadeOut,
4785
+ toggle
4786
+ };
4787
+ }
4788
+ function useAutoPlay(options = {}) {
4789
+ const {
4790
+ interval = 3e3,
4791
+ delay = 0,
4792
+ repeat = "infinite",
4793
+ autoStart = true,
4794
+ pauseOnHover = false,
4795
+ pauseOnBlur = true,
4796
+ showOnMount = false
4797
+ } = options;
4798
+ const [isPlaying, setIsPlaying] = useState(showOnMount ? autoStart : false);
4799
+ const [currentStep, setCurrentStep] = useState(0);
4800
+ const [mounted, setMounted] = useState(false);
4801
+ const [isPaused, setIsPaused] = useState(false);
4802
+ const intervalRef = useRef(null);
4803
+ const timeoutRef = useRef(null);
4804
+ const repeatCountRef = useRef(0);
4805
+ useEffect(() => {
4806
+ setMounted(true);
4807
+ }, []);
4808
+ const next = useCallback(() => {
4809
+ setCurrentStep((prev) => {
4810
+ const nextStep = prev + 1;
4811
+ if (repeat !== "infinite") {
4812
+ repeatCountRef.current += 1;
4813
+ if (repeatCountRef.current >= repeat) {
4814
+ stop();
4815
+ return prev;
4816
+ }
4817
+ }
4818
+ return nextStep;
4819
+ });
4820
+ }, [repeat]);
4821
+ const startInterval = useCallback(() => {
4822
+ if (intervalRef.current) {
4823
+ clearInterval(intervalRef.current);
4824
+ }
4825
+ intervalRef.current = window.setInterval(() => {
4826
+ next();
4827
+ }, interval);
4828
+ }, [interval, next]);
4829
+ const pause = useCallback(() => {
4830
+ if (!isPlaying) return;
4831
+ setIsPaused(true);
4832
+ if (intervalRef.current) {
4833
+ clearInterval(intervalRef.current);
4834
+ intervalRef.current = null;
4835
+ }
4836
+ }, [isPlaying]);
4837
+ const resume = useCallback(() => {
4838
+ if (!isPlaying || !isPaused) return;
4839
+ setIsPaused(false);
4840
+ startInterval();
4841
+ }, [isPlaying, isPaused, startInterval]);
4842
+ const stop = useCallback(() => {
4843
+ setIsPlaying(false);
4844
+ setIsPaused(false);
4845
+ setCurrentStep(0);
4846
+ repeatCountRef.current = 0;
4847
+ if (intervalRef.current) {
4848
+ clearInterval(intervalRef.current);
4849
+ intervalRef.current = null;
4850
+ }
4851
+ if (timeoutRef.current) {
4852
+ clearTimeout(timeoutRef.current);
4853
+ timeoutRef.current = null;
4854
+ }
4855
+ }, []);
4856
+ const start = useCallback(() => {
4857
+ if (!mounted) return;
4858
+ setIsPlaying(true);
4859
+ setIsPaused(false);
4860
+ setCurrentStep(0);
4861
+ repeatCountRef.current = 0;
4862
+ if (delay > 0) {
4863
+ timeoutRef.current = window.setTimeout(() => {
4864
+ startInterval();
4865
+ }, delay);
4866
+ } else {
4867
+ startInterval();
4868
+ }
4869
+ }, [mounted, delay, startInterval]);
4870
+ const previous = useCallback(() => {
4871
+ setCurrentStep((prev) => Math.max(0, prev - 1));
4872
+ }, []);
4873
+ const goTo = useCallback((step) => {
4874
+ setCurrentStep(Math.max(0, step));
4875
+ }, []);
4876
+ useEffect(() => {
4877
+ if (mounted && autoStart) {
4878
+ start();
4879
+ }
4880
+ }, [mounted, autoStart, start]);
4881
+ useEffect(() => {
4882
+ if (!pauseOnHover) return;
4883
+ const handleMouseEnter = () => {
4884
+ if (isPlaying && !isPaused) {
4885
+ pause();
4886
+ }
4887
+ };
4888
+ const handleMouseLeave = () => {
4889
+ if (isPlaying && isPaused) {
4890
+ resume();
4891
+ }
4892
+ };
4893
+ document.addEventListener("mouseenter", handleMouseEnter);
4894
+ document.addEventListener("mouseleave", handleMouseLeave);
4895
+ return () => {
4896
+ document.removeEventListener("mouseenter", handleMouseEnter);
4897
+ document.removeEventListener("mouseleave", handleMouseLeave);
4898
+ };
4899
+ }, [pauseOnHover, isPlaying, isPaused, pause, resume]);
4900
+ useEffect(() => {
4901
+ if (!pauseOnBlur) return;
4902
+ const handleBlur = () => {
4903
+ if (isPlaying && !isPaused) {
4904
+ pause();
4905
+ }
4906
+ };
4907
+ const handleFocus = () => {
4908
+ if (isPlaying && isPaused) {
4909
+ resume();
4910
+ }
4911
+ };
4912
+ window.addEventListener("blur", handleBlur);
4913
+ window.addEventListener("focus", handleFocus);
4914
+ return () => {
4915
+ window.removeEventListener("blur", handleBlur);
4916
+ window.removeEventListener("focus", handleFocus);
4917
+ };
4918
+ }, [pauseOnBlur, isPlaying, isPaused, pause, resume]);
4919
+ useEffect(() => {
4920
+ return () => {
4921
+ if (intervalRef.current) {
4922
+ clearInterval(intervalRef.current);
4923
+ }
4924
+ if (timeoutRef.current) {
4925
+ clearTimeout(timeoutRef.current);
4926
+ }
4927
+ };
4928
+ }, []);
4929
+ return {
4930
+ isPlaying,
4931
+ isPaused,
4932
+ currentStep,
4933
+ mounted,
4934
+ start,
4935
+ stop,
4936
+ pause,
4937
+ resume,
4938
+ next,
4939
+ previous,
4940
+ goTo
4941
+ };
4942
+ }
4943
+ function useAutoScale(options = {}) {
4944
+ const {
4945
+ initialScale = 0,
4946
+ targetScale = 1,
4947
+ duration = 1e3,
4948
+ delay = 0,
4949
+ repeat = false,
4950
+ repeatDelay = 1e3,
4951
+ repeatCount = -1,
4952
+ ease = "ease-in-out",
4953
+ autoStart = true,
4954
+ onComplete,
4955
+ onRepeat,
4956
+ showOnMount = false,
4957
+ centerTransform: _centerTransform = true
4958
+ } = options;
4959
+ const [scale, setScale] = useState(showOnMount ? initialScale : 0);
4960
+ const [isAnimating, setIsAnimating] = useState(false);
4961
+ const [isVisible, setIsVisible] = useState(
4962
+ showOnMount ? initialScale > 0 : false
4963
+ );
4964
+ const [mounted, setMounted] = useState(false);
4965
+ const motionRef = useRef(null);
4966
+ const timeoutRef = useRef(null);
4967
+ const repeatCountRef = useRef(0);
4968
+ const isScalingInRef = useRef(true);
4969
+ useEffect(() => {
4970
+ setMounted(true);
4971
+ }, []);
4972
+ const getEasing2 = useCallback(
4973
+ (t) => {
4974
+ switch (ease) {
4975
+ case "linear":
4976
+ return t;
4977
+ case "ease-in":
4978
+ return t * t;
4979
+ case "ease-out":
4980
+ return 1 - (1 - t) * (1 - t);
4981
+ case "ease-in-out":
4982
+ return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
4983
+ case "bounce":
4984
+ if (t < 1 / 2.75) {
4985
+ return 7.5625 * t * t;
4986
+ } else if (t < 2 / 2.75) {
4987
+ return 7.5625 * (t -= 1.5 / 2.75) * t + 0.75;
4988
+ } else if (t < 2.5 / 2.75) {
4989
+ return 7.5625 * (t -= 2.25 / 2.75) * t + 0.9375;
4990
+ } else {
4991
+ return 7.5625 * (t -= 2.625 / 2.75) * t + 0.984375;
4992
+ }
4993
+ case "elastic":
4994
+ if (t === 0) return 0;
4995
+ if (t === 1) return 1;
4996
+ return Math.pow(2, -10 * t) * Math.sin((t - 0.075) * (2 * Math.PI) / 0.3) + 1;
4997
+ default:
4998
+ return t;
4999
+ }
5000
+ },
5001
+ [ease]
5002
+ );
5003
+ const animate = useCallback(
5004
+ (from, to, onFinish) => {
5005
+ if (!mounted) return;
5006
+ setIsAnimating(true);
5007
+ const startTime = performance.now();
5008
+ const startScale = from;
5009
+ const updateScale = (currentTime) => {
5010
+ const elapsed = currentTime - startTime;
5011
+ const progress = Math.min(elapsed / duration, 1);
5012
+ const easedProgress = getEasing2(progress);
5013
+ const currentScale = startScale + (to - startScale) * easedProgress;
5014
+ setScale(currentScale);
5015
+ setIsVisible(currentScale > 0);
5016
+ if (progress < 1) {
5017
+ motionRef.current = requestAnimationFrame(updateScale);
5018
+ } else {
5019
+ setIsAnimating(false);
5020
+ onFinish?.();
5021
+ }
5022
+ };
5023
+ motionRef.current = requestAnimationFrame(updateScale);
5024
+ },
5025
+ [mounted, duration, getEasing2]
5026
+ );
5027
+ const scaleIn = useCallback(() => {
5028
+ if (!mounted || isAnimating) return;
5029
+ animate(initialScale, targetScale, () => {
5030
+ onComplete?.();
5031
+ if (repeat && (repeatCount === -1 || repeatCountRef.current < repeatCount)) {
5032
+ repeatCountRef.current++;
5033
+ onRepeat?.(repeatCountRef.current);
5034
+ timeoutRef.current = window.setTimeout(() => {
5035
+ scaleOut();
5036
+ }, repeatDelay);
5037
+ }
5038
+ });
5039
+ }, [
5040
+ mounted,
5041
+ isAnimating,
5042
+ animate,
5043
+ initialScale,
5044
+ targetScale,
5045
+ onComplete,
5046
+ repeat,
5047
+ repeatCount,
5048
+ repeatDelay,
5049
+ onRepeat
5050
+ ]);
5051
+ const scaleOut = useCallback(() => {
5052
+ if (!mounted || isAnimating) return;
5053
+ animate(targetScale, initialScale, () => {
5054
+ onComplete?.();
5055
+ if (repeat && (repeatCount === -1 || repeatCountRef.current < repeatCount)) {
5056
+ repeatCountRef.current++;
5057
+ onRepeat?.(repeatCountRef.current);
5058
+ timeoutRef.current = window.setTimeout(() => {
5059
+ scaleIn();
5060
+ }, repeatDelay);
5061
+ }
5062
+ });
5063
+ }, [
5064
+ mounted,
5065
+ isAnimating,
5066
+ animate,
5067
+ targetScale,
5068
+ initialScale,
5069
+ onComplete,
5070
+ repeat,
5071
+ repeatCount,
5072
+ repeatDelay,
5073
+ onRepeat
5074
+ ]);
5075
+ const start = useCallback(() => {
5076
+ if (!mounted || isAnimating) return;
5077
+ if (delay > 0) {
5078
+ timeoutRef.current = window.setTimeout(() => {
5079
+ scaleIn();
5080
+ }, delay);
5081
+ } else {
5082
+ scaleIn();
5083
+ }
5084
+ }, [mounted, isAnimating, delay, scaleIn]);
5085
+ const stop = useCallback(() => {
5086
+ if (motionRef.current !== null) {
5087
+ cancelAnimationFrame(motionRef.current);
5088
+ motionRef.current = null;
5089
+ }
5090
+ if (timeoutRef.current !== null) {
5091
+ clearTimeout(timeoutRef.current);
5092
+ timeoutRef.current = null;
5093
+ }
5094
+ setIsAnimating(false);
5095
+ }, []);
5096
+ const reset = useCallback(() => {
5097
+ stop();
5098
+ setScale(initialScale);
5099
+ setIsVisible(initialScale > 0);
5100
+ repeatCountRef.current = 0;
5101
+ isScalingInRef.current = true;
5102
+ }, [stop, initialScale]);
5103
+ const toggle = useCallback(() => {
5104
+ if (isScalingInRef.current) {
5105
+ scaleOut();
5106
+ isScalingInRef.current = false;
5107
+ } else {
5108
+ scaleIn();
5109
+ isScalingInRef.current = true;
5110
+ }
5111
+ }, [scaleIn, scaleOut]);
5112
+ useEffect(() => {
5113
+ if (mounted && autoStart) {
5114
+ start();
5115
+ }
5116
+ }, [mounted, autoStart, start]);
5117
+ useEffect(() => {
5118
+ return () => {
5119
+ if (motionRef.current !== null) {
5120
+ cancelAnimationFrame(motionRef.current);
5121
+ }
5122
+ if (timeoutRef.current !== null) {
5123
+ clearTimeout(timeoutRef.current);
5124
+ }
5125
+ };
5126
+ }, []);
5127
+ return {
5128
+ scale,
5129
+ isAnimating,
5130
+ isVisible,
5131
+ mounted,
5132
+ start,
5133
+ stop,
5134
+ reset,
5135
+ scaleIn,
5136
+ scaleOut,
5137
+ toggle
5138
+ };
5139
+ }
5140
+ function useAutoSlide(options = {}) {
5141
+ const {
5142
+ direction = "left",
5143
+ distance = 100,
5144
+ initialPosition,
5145
+ targetPosition,
5146
+ duration = 1e3,
5147
+ delay = 0,
5148
+ repeat = false,
5149
+ repeatDelay = 1e3,
5150
+ repeatCount = -1,
5151
+ ease = "ease-in-out",
5152
+ autoStart = true,
5153
+ onComplete,
5154
+ onRepeat,
5155
+ showOnMount = false
5156
+ } = options;
5157
+ const getDefaultPositions = useCallback(() => {
5158
+ const defaultInitial = { x: 0, y: 0 };
5159
+ const defaultTarget = { x: 0, y: 0 };
5160
+ switch (direction) {
5161
+ case "left":
5162
+ defaultInitial.x = distance;
5163
+ defaultTarget.x = 0;
5164
+ break;
5165
+ case "right":
5166
+ defaultInitial.x = -distance;
5167
+ defaultTarget.x = 0;
5168
+ break;
5169
+ case "up":
5170
+ defaultInitial.y = distance;
5171
+ defaultTarget.y = 0;
5172
+ break;
5173
+ case "down":
5174
+ defaultInitial.y = -distance;
5175
+ defaultTarget.y = 0;
5176
+ break;
5177
+ case "left-up":
5178
+ defaultInitial.x = distance;
5179
+ defaultInitial.y = distance;
5180
+ defaultTarget.x = 0;
5181
+ defaultTarget.y = 0;
5182
+ break;
5183
+ case "left-down":
5184
+ defaultInitial.x = distance;
5185
+ defaultInitial.y = -distance;
5186
+ defaultTarget.x = 0;
5187
+ defaultTarget.y = 0;
5188
+ break;
5189
+ case "right-up":
5190
+ defaultInitial.x = -distance;
5191
+ defaultInitial.y = distance;
5192
+ defaultTarget.x = 0;
5193
+ defaultTarget.y = 0;
5194
+ break;
5195
+ case "right-down":
5196
+ defaultInitial.x = -distance;
5197
+ defaultInitial.y = -distance;
5198
+ defaultTarget.x = 0;
5199
+ defaultTarget.y = 0;
5200
+ break;
5201
+ }
5202
+ return {
5203
+ initial: initialPosition || defaultInitial,
5204
+ target: targetPosition || defaultTarget
5205
+ };
5206
+ }, [direction, distance, initialPosition, targetPosition]);
5207
+ const positions = getDefaultPositions();
5208
+ const [position, setPosition] = useState(
5209
+ showOnMount ? positions.initial : positions.target
5210
+ );
5211
+ const [isAnimating, setIsAnimating] = useState(false);
5212
+ const [isVisible, setIsVisible] = useState(showOnMount ? true : false);
5213
+ const [mounted, setMounted] = useState(false);
5214
+ const motionRef = useRef(null);
5215
+ const timeoutRef = useRef(null);
5216
+ const repeatCountRef = useRef(0);
5217
+ const isSlidingInRef = useRef(true);
5218
+ useEffect(() => {
5219
+ setMounted(true);
5220
+ }, []);
5221
+ const getEasing2 = useCallback(
5222
+ (t) => {
5223
+ switch (ease) {
5224
+ case "linear":
5225
+ return t;
5226
+ case "ease-in":
5227
+ return t * t;
5228
+ case "ease-out":
5229
+ return 1 - (1 - t) * (1 - t);
5230
+ case "ease-in-out":
5231
+ return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
5232
+ default:
5233
+ return t;
5234
+ }
5235
+ },
5236
+ [ease]
5237
+ );
5238
+ const animate = useCallback(
5239
+ (from, to, onFinish) => {
5240
+ if (!mounted) return;
5241
+ setIsAnimating(true);
5242
+ const startTime = performance.now();
5243
+ const startPosition = from;
5244
+ const updatePosition = (currentTime) => {
5245
+ const elapsed = currentTime - startTime;
5246
+ const progress = Math.min(elapsed / duration, 1);
5247
+ const easedProgress = getEasing2(progress);
5248
+ const currentX = startPosition.x + (to.x - startPosition.x) * easedProgress;
5249
+ const currentY = startPosition.y + (to.y - startPosition.y) * easedProgress;
5250
+ setPosition({ x: currentX, y: currentY });
5251
+ setIsVisible(true);
5252
+ if (progress < 1) {
5253
+ motionRef.current = requestAnimationFrame(updatePosition);
5254
+ } else {
5255
+ setIsAnimating(false);
5256
+ onFinish?.();
5257
+ }
5258
+ };
5259
+ motionRef.current = requestAnimationFrame(updatePosition);
5260
+ },
5261
+ [mounted, duration, getEasing2]
5262
+ );
5263
+ const slideIn = useCallback(() => {
5264
+ if (!mounted || isAnimating) return;
5265
+ animate(positions.initial, positions.target, () => {
5266
+ onComplete?.();
5267
+ if (repeat && (repeatCount === -1 || repeatCountRef.current < repeatCount)) {
5268
+ repeatCountRef.current++;
5269
+ onRepeat?.(repeatCountRef.current);
5270
+ timeoutRef.current = window.setTimeout(() => {
5271
+ slideOut();
5272
+ }, repeatDelay);
5273
+ }
5274
+ });
5275
+ }, [
5276
+ mounted,
5277
+ isAnimating,
5278
+ animate,
5279
+ positions.initial,
5280
+ positions.target,
5281
+ onComplete,
5282
+ repeat,
5283
+ repeatCount,
5284
+ repeatDelay,
5285
+ onRepeat
5286
+ ]);
5287
+ const slideOut = useCallback(() => {
5288
+ if (!mounted || isAnimating) return;
5289
+ animate(positions.target, positions.initial, () => {
5290
+ onComplete?.();
5291
+ if (repeat && (repeatCount === -1 || repeatCountRef.current < repeatCount)) {
5292
+ repeatCountRef.current++;
5293
+ onRepeat?.(repeatCountRef.current);
5294
+ timeoutRef.current = window.setTimeout(() => {
5295
+ slideIn();
5296
+ }, repeatDelay);
5297
+ }
5298
+ });
5299
+ }, [
5300
+ mounted,
5301
+ isAnimating,
5302
+ animate,
5303
+ positions.target,
5304
+ positions.initial,
5305
+ onComplete,
5306
+ repeat,
5307
+ repeatCount,
5308
+ repeatDelay,
5309
+ onRepeat
5310
+ ]);
5311
+ const start = useCallback(() => {
5312
+ if (!mounted || isAnimating) return;
5313
+ if (delay > 0) {
5314
+ timeoutRef.current = window.setTimeout(() => {
5315
+ slideIn();
5316
+ }, delay);
5317
+ } else {
5318
+ slideIn();
5319
+ }
5320
+ }, [mounted, isAnimating, delay, slideIn]);
5321
+ const stop = useCallback(() => {
5322
+ if (motionRef.current !== null) {
5323
+ cancelAnimationFrame(motionRef.current);
5324
+ motionRef.current = null;
5325
+ }
5326
+ if (timeoutRef.current !== null) {
5327
+ clearTimeout(timeoutRef.current);
5328
+ timeoutRef.current = null;
5329
+ }
5330
+ setIsAnimating(false);
5331
+ }, []);
5332
+ const reset = useCallback(() => {
5333
+ stop();
5334
+ setPosition(positions.initial);
5335
+ setIsVisible(showOnMount ? true : false);
5336
+ repeatCountRef.current = 0;
5337
+ isSlidingInRef.current = true;
5338
+ }, [stop, positions.initial, showOnMount]);
5339
+ const toggle = useCallback(() => {
5340
+ if (isSlidingInRef.current) {
5341
+ slideOut();
5342
+ isSlidingInRef.current = false;
5343
+ } else {
5344
+ slideIn();
5345
+ isSlidingInRef.current = true;
5346
+ }
5347
+ }, [slideIn, slideOut]);
5348
+ useEffect(() => {
5349
+ if (mounted && autoStart) {
5350
+ start();
5351
+ }
5352
+ }, [mounted, autoStart, start]);
5353
+ useEffect(() => {
5354
+ return () => {
5355
+ if (motionRef.current !== null) {
5356
+ cancelAnimationFrame(motionRef.current);
5357
+ }
5358
+ if (timeoutRef.current !== null) {
5359
+ clearTimeout(timeoutRef.current);
5360
+ }
5361
+ };
5362
+ }, []);
5363
+ return {
5364
+ position,
5365
+ isAnimating,
5366
+ isVisible,
5367
+ mounted,
5368
+ start,
5369
+ stop,
5370
+ reset,
5371
+ slideIn,
5372
+ slideOut,
5373
+ toggle
5374
+ };
5375
+ }
5376
+ function useMotionOrchestra(options = {}) {
5377
+ const {
5378
+ mode = "sequential",
5379
+ staggerDelay = 100,
5380
+ autoStart = false,
5381
+ loop = false,
5382
+ onComplete
5383
+ } = options;
5384
+ const [orchestraState, setOrchestraState] = useState({
5385
+ isPlaying: false,
5386
+ currentStep: 0,
5387
+ completedSteps: /* @__PURE__ */ new Set()
5388
+ });
5389
+ const motionsRef = useRef([]);
5390
+ const timeoutsRef = useRef([]);
5391
+ const addMotion = useCallback((step) => {
5392
+ motionsRef.current.push(step);
5393
+ }, []);
5394
+ const removeMotion = useCallback((id) => {
5395
+ motionsRef.current = motionsRef.current.filter((step) => step.id !== id);
5396
+ }, []);
5397
+ const clearTimeouts = useCallback(() => {
5398
+ timeoutsRef.current.forEach((timeout) => clearTimeout(timeout));
5399
+ timeoutsRef.current = [];
5400
+ }, []);
5401
+ const playSequential = useCallback(() => {
5402
+ if (motionsRef.current.length === 0) return;
5403
+ const playStep = (index) => {
5404
+ if (index >= motionsRef.current.length) {
5405
+ setOrchestraState((prev) => ({
5406
+ ...prev,
5407
+ isPlaying: false,
5408
+ currentStep: 0
5409
+ }));
5410
+ onComplete?.();
5411
+ if (loop) {
5412
+ setTimeout(() => {
5413
+ setOrchestraState((prev) => ({
5414
+ ...prev,
5415
+ isPlaying: true,
5416
+ completedSteps: /* @__PURE__ */ new Set()
5417
+ }));
5418
+ playSequential();
5419
+ }, 1e3);
5420
+ }
5421
+ return;
5422
+ }
5423
+ const step = motionsRef.current[index];
5424
+ setOrchestraState((prev) => ({
5425
+ ...prev,
5426
+ currentStep: index,
5427
+ completedSteps: /* @__PURE__ */ new Set([...prev.completedSteps, step.id])
5428
+ }));
5429
+ step.motion();
5430
+ if (step.onComplete) {
5431
+ step.onComplete();
5432
+ }
5433
+ const timeout = setTimeout(() => {
5434
+ playStep(index + 1);
5435
+ }, step.delay || 0);
5436
+ timeoutsRef.current.push(timeout);
5437
+ };
5438
+ playStep(0);
5439
+ }, [loop, onComplete]);
5440
+ const playParallel = useCallback(() => {
5441
+ if (motionsRef.current.length === 0) return;
5442
+ const completedSteps = /* @__PURE__ */ new Set();
5443
+ motionsRef.current.forEach((step) => {
5444
+ const timeout = setTimeout(() => {
5445
+ step.motion();
5446
+ completedSteps.add(step.id);
5447
+ if (step.onComplete) {
5448
+ step.onComplete();
5449
+ }
5450
+ if (completedSteps.size === motionsRef.current.length) {
5451
+ setOrchestraState((prev) => ({
5452
+ ...prev,
5453
+ isPlaying: false,
5454
+ currentStep: 0
5455
+ }));
5456
+ onComplete?.();
5457
+ if (loop) {
5458
+ setTimeout(() => {
5459
+ setOrchestraState((prev) => ({ ...prev, isPlaying: true }));
5460
+ playParallel();
5461
+ }, 1e3);
5462
+ }
5463
+ }
5464
+ }, step.delay || 0);
5465
+ timeoutsRef.current.push(timeout);
5466
+ });
5467
+ setOrchestraState((prev) => ({
5468
+ ...prev,
5469
+ completedSteps: new Set(completedSteps)
5470
+ }));
5471
+ }, [loop, onComplete]);
5472
+ const playStagger = useCallback(() => {
5473
+ if (motionsRef.current.length === 0) return;
5474
+ const completedSteps = /* @__PURE__ */ new Set();
5475
+ motionsRef.current.forEach((step, index) => {
5476
+ const timeout = setTimeout(
5477
+ () => {
5478
+ step.motion();
5479
+ completedSteps.add(step.id);
5480
+ if (step.onComplete) {
5481
+ step.onComplete();
5482
+ }
5483
+ setOrchestraState((prev) => ({
5484
+ ...prev,
5485
+ currentStep: index,
5486
+ completedSteps: /* @__PURE__ */ new Set([...prev.completedSteps, step.id])
5487
+ }));
5488
+ if (completedSteps.size === motionsRef.current.length) {
5489
+ setOrchestraState((prev) => ({
5490
+ ...prev,
5491
+ isPlaying: false,
5492
+ currentStep: 0
5493
+ }));
5494
+ onComplete?.();
5495
+ if (loop) {
5496
+ setTimeout(() => {
5497
+ setOrchestraState((prev) => ({
5498
+ ...prev,
5499
+ isPlaying: true,
5500
+ completedSteps: /* @__PURE__ */ new Set()
5501
+ }));
5502
+ playStagger();
5503
+ }, 1e3);
5504
+ }
5505
+ }
5506
+ },
5507
+ (step.delay || 0) + index * staggerDelay
5508
+ );
5509
+ timeoutsRef.current.push(timeout);
5510
+ });
5511
+ }, [staggerDelay, loop, onComplete]);
5512
+ const play = useCallback(() => {
5513
+ clearTimeouts();
5514
+ setOrchestraState((prev) => ({
5515
+ ...prev,
5516
+ isPlaying: true,
5517
+ currentStep: 0,
5518
+ completedSteps: /* @__PURE__ */ new Set()
5519
+ }));
5520
+ switch (mode) {
5521
+ case "sequential":
5522
+ playSequential();
5523
+ break;
5524
+ case "parallel":
5525
+ playParallel();
5526
+ break;
5527
+ case "stagger":
5528
+ playStagger();
5529
+ break;
5530
+ }
5531
+ }, [mode, clearTimeouts, playSequential, playParallel, playStagger]);
5532
+ const stop = useCallback(() => {
5533
+ clearTimeouts();
5534
+ setOrchestraState((prev) => ({
5535
+ ...prev,
5536
+ isPlaying: false,
5537
+ currentStep: 0
5538
+ }));
5539
+ }, [clearTimeouts]);
5540
+ const pause = useCallback(() => {
5541
+ setOrchestraState((prev) => ({ ...prev, isPlaying: false }));
5542
+ }, []);
5543
+ const resume = useCallback(() => {
5544
+ if (orchestraState.currentStep < motionsRef.current.length) {
5545
+ setOrchestraState((prev) => ({ ...prev, isPlaying: true }));
5546
+ switch (mode) {
5547
+ case "sequential":
5548
+ playSequential();
5549
+ break;
5550
+ case "parallel":
5551
+ playParallel();
5552
+ break;
5553
+ case "stagger":
5554
+ playStagger();
5555
+ break;
5556
+ }
5557
+ }
5558
+ }, [
5559
+ mode,
5560
+ orchestraState.currentStep,
5561
+ playSequential,
5562
+ playParallel,
5563
+ playStagger
5564
+ ]);
5565
+ useEffect(() => {
5566
+ if (autoStart && motionsRef.current.length > 0) {
5567
+ play();
5568
+ }
5569
+ }, [autoStart, play]);
5570
+ useEffect(() => {
5571
+ return () => {
5572
+ clearTimeouts();
5573
+ };
5574
+ }, [clearTimeouts]);
5575
+ return {
5576
+ addMotion,
5577
+ removeMotion,
5578
+ play,
5579
+ stop,
5580
+ pause,
5581
+ resume,
5582
+ isPlaying: orchestraState.isPlaying,
5583
+ currentStep: orchestraState.currentStep,
5584
+ completedSteps: orchestraState.completedSteps,
5585
+ totalSteps: motionsRef.current.length
5586
+ };
5587
+ }
5588
+ function useOrchestration(options = {}) {
5589
+ const {
5590
+ autoStart = false,
5591
+ loop = false,
5592
+ loopCount = -1,
5593
+ loopDelay = 1e3,
5594
+ timeline = [],
5595
+ duration: totalDuration,
5596
+ speed = 1,
5597
+ reverse = false,
5598
+ onStart,
5599
+ onComplete,
5600
+ onLoop,
5601
+ onError: _onError,
5602
+ onProgress,
5603
+ onStepStart,
5604
+ onStepComplete
5605
+ } = options;
5606
+ const [state, setState] = useState({
5607
+ isPlaying: false,
5608
+ isPaused: false,
5609
+ currentTime: 0,
5610
+ progress: 0,
5611
+ currentStep: null,
5612
+ loopCount: 0,
5613
+ error: null
5614
+ });
5615
+ const [steps, setSteps] = useState(timeline);
5616
+ const [currentSpeed, setCurrentSpeed] = useState(speed);
5617
+ const [isReversed, setIsReversed] = useState(reverse);
5618
+ const motionRef = useRef(null);
5619
+ const startTimeRef = useRef(0);
5620
+ const pauseTimeRef = useRef(0);
5621
+ const stepStartTimesRef = useRef(/* @__PURE__ */ new Map());
5622
+ const stepDurationsRef = useRef(/* @__PURE__ */ new Map());
5623
+ const getEasing2 = useCallback(
5624
+ (t, ease = "linear") => {
5625
+ switch (ease) {
5626
+ case "linear":
5627
+ return t;
5628
+ case "ease-in":
5629
+ return t * t;
5630
+ case "ease-out":
5631
+ return 1 - (1 - t) * (1 - t);
5632
+ case "ease-in-out":
5633
+ return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
5634
+ case "bounce":
5635
+ if (t < 1 / 2.75) {
5636
+ return 7.5625 * t * t;
5637
+ } else if (t < 2 / 2.75) {
5638
+ return 7.5625 * (t -= 1.5 / 2.75) * t + 0.75;
5639
+ } else if (t < 2.5 / 2.75) {
5640
+ return 7.5625 * (t -= 2.25 / 2.75) * t + 0.9375;
5641
+ } else {
5642
+ return 7.5625 * (t -= 2.625 / 2.75) * t + 0.984375;
5643
+ }
5644
+ case "elastic":
5645
+ if (t === 0) return 0;
5646
+ if (t === 1) return 1;
5647
+ return Math.pow(2, -10 * t) * Math.sin((t - 0.075) * (2 * Math.PI) / 0.3) + 1;
5648
+ default:
5649
+ return t;
5650
+ }
5651
+ },
5652
+ []
5653
+ );
5654
+ const getTotalDuration = useCallback(() => {
5655
+ if (totalDuration) return totalDuration;
5656
+ let maxEndTime = 0;
5657
+ steps.forEach((step) => {
5658
+ const stepEndTime = (step.delay || 0) + step.duration;
5659
+ maxEndTime = Math.max(maxEndTime, stepEndTime);
5660
+ });
5661
+ return maxEndTime;
5662
+ }, [steps, totalDuration]);
5663
+ const calculateStepTimes = useCallback(() => {
5664
+ const stepTimes = /* @__PURE__ */ new Map();
5665
+ const stepDurations = /* @__PURE__ */ new Map();
5666
+ let currentTime = 0;
5667
+ steps.forEach((step) => {
5668
+ stepTimes.set(step.id, currentTime + (step.delay || 0));
5669
+ stepDurations.set(step.id, step.duration);
5670
+ currentTime += (step.delay || 0) + step.duration;
5671
+ });
5672
+ stepStartTimesRef.current = stepTimes;
5673
+ stepDurationsRef.current = stepDurations;
5674
+ }, [steps]);
5675
+ const getCurrentStep = useCallback(
5676
+ (time) => {
5677
+ for (const step of steps) {
5678
+ const startTime = stepStartTimesRef.current.get(step.id) || 0;
5679
+ const endTime = startTime + (stepDurationsRef.current.get(step.id) || 0);
5680
+ if (time >= startTime && time <= endTime) {
5681
+ return step.id;
5682
+ }
5683
+ }
5684
+ return null;
5685
+ },
5686
+ [steps]
5687
+ );
5688
+ const getStepProgress = useCallback(
5689
+ (stepId) => {
5690
+ const startTime = stepStartTimesRef.current.get(stepId) || 0;
5691
+ const duration = stepDurationsRef.current.get(stepId) || 0;
5692
+ const currentTime = state.currentTime;
5693
+ if (currentTime < startTime) return 0;
5694
+ if (currentTime > startTime + duration) return 1;
5695
+ const stepProgress = (currentTime - startTime) / duration;
5696
+ return Math.max(0, Math.min(1, stepProgress));
5697
+ },
5698
+ [state.currentTime]
5699
+ );
5700
+ const getStepTime = useCallback(
5701
+ (stepId) => {
5702
+ const startTime = stepStartTimesRef.current.get(stepId) || 0;
5703
+ const duration = stepDurationsRef.current.get(stepId) || 0;
5704
+ const currentTime = state.currentTime;
5705
+ if (currentTime < startTime) return 0;
5706
+ if (currentTime > startTime + duration) return duration;
5707
+ return currentTime - startTime;
5708
+ },
5709
+ [state.currentTime]
5710
+ );
5711
+ const updateMotion = useCallback(
5712
+ (currentTime) => {
5713
+ const total = getTotalDuration();
5714
+ const adjustedTime = isReversed ? total - currentTime : currentTime;
5715
+ const progress = Math.min(adjustedTime / total, 1);
5716
+ const currentStep = getCurrentStep(adjustedTime);
5717
+ setState((prev) => ({
5718
+ ...prev,
5719
+ currentTime: adjustedTime,
5720
+ progress,
5721
+ currentStep
5722
+ }));
5723
+ onProgress?.(progress);
5724
+ if (currentStep) {
5725
+ const step = steps.find((s) => s.id === currentStep);
5726
+ if (step) {
5727
+ const stepProgress = getStepProgress(currentStep);
5728
+ const easedProgress = getEasing2(stepProgress, step.ease);
5729
+ step.onUpdate?.(easedProgress);
5730
+ }
5731
+ }
5732
+ if (progress >= 1) {
5733
+ if (loop && (loopCount === -1 || state.loopCount < loopCount)) {
5734
+ setState((prev) => ({
5735
+ ...prev,
5736
+ loopCount: prev.loopCount + 1
5737
+ }));
5738
+ onLoop?.(state.loopCount + 1);
5739
+ setTimeout(() => {
5740
+ reset();
5741
+ play();
5742
+ }, loopDelay);
5743
+ } else {
5744
+ setState((prev) => ({
5745
+ ...prev,
5746
+ isPlaying: false,
5747
+ currentTime: isReversed ? 0 : total,
5748
+ progress: 1
5749
+ }));
5750
+ onComplete?.();
5751
+ }
5752
+ } else {
5753
+ motionRef.current = requestAnimationFrame(() => {
5754
+ const elapsed = (performance.now() - startTimeRef.current) * currentSpeed / 1e3;
5755
+ updateMotion(elapsed);
5756
+ });
5757
+ }
5758
+ },
5759
+ [
5760
+ getTotalDuration,
5761
+ isReversed,
5762
+ getCurrentStep,
5763
+ onProgress,
5764
+ steps,
5765
+ getStepProgress,
5766
+ getEasing2,
5767
+ loop,
5768
+ loopCount,
5769
+ state.loopCount,
5770
+ loopDelay,
5771
+ onLoop,
5772
+ onComplete,
5773
+ currentSpeed
5774
+ ]
5775
+ );
5776
+ const play = useCallback(() => {
5777
+ if (state.isPlaying) return;
5778
+ setState((prev) => ({
5779
+ ...prev,
5780
+ isPlaying: true,
5781
+ isPaused: false,
5782
+ error: null
5783
+ }));
5784
+ onStart?.();
5785
+ const startTime = performance.now() - state.currentTime * 1e3 / currentSpeed;
5786
+ startTimeRef.current = startTime;
5787
+ motionRef.current = requestAnimationFrame(() => {
5788
+ const elapsed = (performance.now() - startTimeRef.current) * currentSpeed / 1e3;
5789
+ updateMotion(elapsed);
5790
+ });
5791
+ }, [state.isPlaying, state.currentTime, currentSpeed, onStart, updateMotion]);
5792
+ const pause = useCallback(() => {
5793
+ if (!state.isPlaying || state.isPaused) return;
5794
+ setState((prev) => ({
5795
+ ...prev,
5796
+ isPaused: true
5797
+ }));
5798
+ if (motionRef.current) {
5799
+ cancelAnimationFrame(motionRef.current);
5800
+ motionRef.current = null;
5801
+ }
5802
+ pauseTimeRef.current = state.currentTime;
5803
+ }, [state.isPlaying, state.isPaused, state.currentTime]);
5804
+ const stop = useCallback(() => {
5805
+ setState((prev) => ({
5806
+ ...prev,
5807
+ isPlaying: false,
5808
+ isPaused: false,
5809
+ currentTime: 0,
5810
+ progress: 0,
5811
+ currentStep: null
5812
+ }));
5813
+ if (motionRef.current) {
5814
+ cancelAnimationFrame(motionRef.current);
5815
+ motionRef.current = null;
5816
+ }
5817
+ }, []);
5818
+ const reset = useCallback(() => {
5819
+ stop();
5820
+ setState((prev) => ({
5821
+ ...prev,
5822
+ currentTime: 0,
5823
+ progress: 0,
5824
+ currentStep: null,
5825
+ loopCount: 0
5826
+ }));
5827
+ }, [stop]);
5828
+ const seek = useCallback(
5829
+ (time) => {
5830
+ const total = getTotalDuration();
5831
+ const clampedTime = Math.max(0, Math.min(time, total));
5832
+ setState((prev) => ({
5833
+ ...prev,
5834
+ currentTime: clampedTime,
5835
+ progress: clampedTime / total,
5836
+ currentStep: getCurrentStep(clampedTime)
5837
+ }));
5838
+ },
5839
+ [getTotalDuration, getCurrentStep]
5840
+ );
5841
+ const setSpeed = useCallback((speed2) => {
5842
+ setCurrentSpeed(Math.max(0.1, speed2));
5843
+ }, []);
5844
+ const reverseDirection = useCallback(() => {
5845
+ setIsReversed((prev) => !prev);
5846
+ }, []);
5847
+ const addStep = useCallback((step) => {
5848
+ setSteps((prev) => [...prev, step]);
5849
+ }, []);
5850
+ const removeStep = useCallback((stepId) => {
5851
+ setSteps((prev) => prev.filter((step) => step.id !== stepId));
5852
+ }, []);
5853
+ const updateStep = useCallback(
5854
+ (stepId, updates) => {
5855
+ setSteps(
5856
+ (prev) => prev.map(
5857
+ (step) => step.id === stepId ? { ...step, ...updates } : step
5858
+ )
5859
+ );
5860
+ },
5861
+ []
5862
+ );
5863
+ const reorderSteps = useCallback((stepIds) => {
5864
+ setSteps((prev) => {
5865
+ const stepMap = new Map(prev.map((step) => [step.id, step]));
5866
+ return stepIds.map((id) => stepMap.get(id)).filter(Boolean);
5867
+ });
5868
+ }, []);
5869
+ useEffect(() => {
5870
+ const currentStep = state.currentStep;
5871
+ if (currentStep) {
5872
+ onStepStart?.(currentStep);
5873
+ const step = steps.find((s) => s.id === currentStep);
5874
+ step?.onStart?.();
5875
+ }
5876
+ }, [state.currentStep, steps, onStepStart]);
5877
+ useEffect(() => {
5878
+ const currentStep = state.currentStep;
5879
+ if (currentStep) {
5880
+ const stepProgress = getStepProgress(currentStep);
5881
+ if (stepProgress >= 1) {
5882
+ const step = steps.find((s) => s.id === currentStep);
5883
+ step?.onComplete?.();
5884
+ onStepComplete?.(currentStep);
5885
+ }
5886
+ }
5887
+ }, [
5888
+ state.currentTime,
5889
+ state.currentStep,
5890
+ steps,
5891
+ getStepProgress,
5892
+ onStepComplete
5893
+ ]);
5894
+ useEffect(() => {
5895
+ calculateStepTimes();
5896
+ }, [calculateStepTimes]);
5897
+ useEffect(() => {
5898
+ if (autoStart && steps.length > 0) {
5899
+ play();
5900
+ }
5901
+ }, [autoStart, steps.length, play]);
5902
+ useEffect(() => {
5903
+ return () => {
5904
+ if (motionRef.current) {
5905
+ cancelAnimationFrame(motionRef.current);
5906
+ }
5907
+ };
5908
+ }, []);
5909
+ return {
5910
+ // 상태
5911
+ isPlaying: state.isPlaying,
5912
+ isPaused: state.isPaused,
5913
+ currentTime: state.currentTime,
5914
+ progress: state.progress,
5915
+ currentStep: state.currentStep,
5916
+ loopCount: state.loopCount,
5917
+ error: state.error,
5918
+ // 제어
5919
+ play,
5920
+ pause,
5921
+ stop,
5922
+ reset,
5923
+ seek,
5924
+ setSpeed,
5925
+ reverse: reverseDirection,
5926
+ // 타임라인 관리
5927
+ addStep,
5928
+ removeStep,
5929
+ updateStep,
5930
+ reorderSteps,
5931
+ // 유틸리티
5932
+ getStepProgress,
5933
+ getStepTime,
5934
+ getTotalDuration
5935
+ };
5936
+ }
5937
+ function useSequence(sequence, options = {}) {
5938
+ const { autoStart = true, loop = false } = options;
5939
+ const [currentIndex, setCurrentIndex] = useState(0);
5940
+ const [isPlaying, setIsPlaying] = useState(false);
5941
+ const motionsRef = useRef([]);
5942
+ const timeoutsRef = useRef([]);
5943
+ const motions = sequence.map((item) => item.hook());
5944
+ const start = useCallback(() => {
5945
+ if (isPlaying) return;
5946
+ setIsPlaying(true);
5947
+ setCurrentIndex(0);
5948
+ motionsRef.current = motions;
5949
+ if (motionsRef.current[0]) {
5950
+ motionsRef.current[0].start();
5951
+ }
5952
+ sequence.forEach((item, index) => {
5953
+ if (index === 0) return;
5954
+ const timeout = window.setTimeout(() => {
5955
+ if (motionsRef.current[index]) {
5956
+ motionsRef.current[index].start();
5957
+ setCurrentIndex(index);
5958
+ }
5959
+ }, item.delay || 0);
5960
+ timeoutsRef.current.push(timeout);
5961
+ });
5962
+ const totalDuration = sequence.reduce((total, item) => {
5963
+ return total + (item.delay || 0);
5964
+ }, 0);
5965
+ const finalTimeout = window.setTimeout(() => {
5966
+ setIsPlaying(false);
5967
+ if (loop) {
5968
+ start();
5969
+ }
5970
+ }, totalDuration + 1e3);
5971
+ timeoutsRef.current.push(finalTimeout);
5972
+ }, [sequence, isPlaying, loop, motions]);
5973
+ const stop = useCallback(() => {
5974
+ setIsPlaying(false);
5975
+ setCurrentIndex(0);
5976
+ timeoutsRef.current.forEach((timeout) => window.clearTimeout(timeout));
5977
+ timeoutsRef.current = [];
5978
+ motionsRef.current.forEach((motion) => {
5979
+ if (motion && motion.stop) {
5980
+ motion.stop();
5981
+ }
5982
+ });
5983
+ }, []);
5984
+ const reset = useCallback(() => {
5985
+ stop();
5986
+ setCurrentIndex(0);
5987
+ motionsRef.current.forEach((motion) => {
5988
+ if (motion && motion.reset) {
5989
+ motion.reset();
5990
+ }
5991
+ });
5992
+ }, [stop]);
5993
+ const pause = useCallback(() => {
5994
+ setIsPlaying(false);
5995
+ if (motionsRef.current[currentIndex] && motionsRef.current[currentIndex].pause) {
5996
+ motionsRef.current[currentIndex].pause();
5997
+ }
5998
+ }, [currentIndex]);
5999
+ const resume = useCallback(() => {
6000
+ setIsPlaying(true);
6001
+ if (motionsRef.current[currentIndex] && motionsRef.current[currentIndex].resume) {
6002
+ motionsRef.current[currentIndex].resume();
6003
+ }
6004
+ }, [currentIndex]);
6005
+ useEffect(() => {
6006
+ if (autoStart && !isPlaying) {
6007
+ start();
6008
+ }
6009
+ }, [autoStart, isPlaying, start]);
6010
+ return {
6011
+ start,
6012
+ stop,
6013
+ pause,
6014
+ resume,
6015
+ reset,
6016
+ isPlaying,
6017
+ currentIndex,
6018
+ totalMotions: sequence.length,
6019
+ ref: motions[0]?.ref || (() => {
6020
+ })
6021
+ // 첫 번째 모션의 ref 반환
6022
+ };
6023
+ }
6024
+ function useLayoutMotion(config) {
6025
+ const {
6026
+ from,
6027
+ to,
6028
+ duration = 500,
6029
+ easing = "ease-in-out",
6030
+ autoStart = false,
6031
+ onComplete
6032
+ } = config;
6033
+ const ref = useRef(null);
6034
+ const [state, setState] = useState({
6035
+ isAnimating: false,
6036
+ progress: 0,
6037
+ currentStyle: {}
6038
+ });
6039
+ const motionFrameRef = useRef(null);
6040
+ const startTimeRef = useRef(0);
6041
+ const parseValue = useCallback(
6042
+ (value) => {
6043
+ if (typeof value === "number") return value;
6044
+ if (typeof value === "string") {
6045
+ const match = value.match(/^(\d+(?:\.\d+)?)(px|%|em|rem|vh|vw)?$/);
6046
+ return match ? parseFloat(match[1]) : 0;
6047
+ }
6048
+ return 0;
6049
+ },
6050
+ []
6051
+ );
6052
+ const interpolate = useCallback(
6053
+ (from2, to2, progress) => {
6054
+ return from2 + (to2 - from2) * progress;
6055
+ },
6056
+ []
6057
+ );
6058
+ const applyEasing2 = useCallback(
6059
+ (t) => {
6060
+ switch (easing) {
6061
+ case "ease-in":
6062
+ return t * t;
6063
+ case "ease-out":
6064
+ return 1 - (1 - t) * (1 - t);
6065
+ case "ease-in-out":
6066
+ return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
6067
+ default:
6068
+ return t;
6069
+ }
6070
+ },
6071
+ [easing]
6072
+ );
6073
+ const calculateStyle = useCallback(
6074
+ (progress) => {
6075
+ const easedProgress = applyEasing2(progress);
6076
+ const style = {};
6077
+ if (from.width !== void 0 && to.width !== void 0) {
6078
+ const fromWidth = parseValue(from.width);
6079
+ const toWidth = parseValue(to.width);
6080
+ style.width = `${interpolate(fromWidth, toWidth, easedProgress)}px`;
6081
+ }
6082
+ if (from.height !== void 0 && to.height !== void 0) {
6083
+ const fromHeight = parseValue(from.height);
6084
+ const toHeight = parseValue(to.height);
6085
+ style.height = `${interpolate(fromHeight, toHeight, easedProgress)}px`;
6086
+ }
6087
+ if (from.flexDirection !== to.flexDirection) {
6088
+ style.flexDirection = progress < 0.5 ? from.flexDirection : to.flexDirection;
6089
+ }
6090
+ if (from.justifyContent !== to.justifyContent) {
6091
+ style.justifyContent = progress < 0.5 ? from.justifyContent : to.justifyContent;
6092
+ }
6093
+ if (from.alignItems !== to.alignItems) {
6094
+ style.alignItems = progress < 0.5 ? from.alignItems : to.alignItems;
6095
+ }
6096
+ if (from.gap !== void 0 && to.gap !== void 0) {
6097
+ const fromGap = parseValue(from.gap);
6098
+ const toGap = parseValue(to.gap);
6099
+ style.gap = `${interpolate(fromGap, toGap, easedProgress)}px`;
6100
+ }
6101
+ if (from.gridTemplateColumns !== to.gridTemplateColumns) {
6102
+ style.gridTemplateColumns = progress < 0.5 ? from.gridTemplateColumns : to.gridTemplateColumns;
6103
+ }
6104
+ if (from.gridTemplateRows !== to.gridTemplateRows) {
6105
+ style.gridTemplateRows = progress < 0.5 ? from.gridTemplateRows : to.gridTemplateRows;
6106
+ }
6107
+ if (from.gridGap !== void 0 && to.gridGap !== void 0) {
6108
+ const fromGridGap = parseValue(from.gridGap);
6109
+ const toGridGap = parseValue(to.gridGap);
6110
+ style.gridGap = `${interpolate(fromGridGap, toGridGap, easedProgress)}px`;
6111
+ }
6112
+ return style;
6113
+ },
6114
+ [from, to, applyEasing2, parseValue, interpolate]
6115
+ );
6116
+ const updateMotion = useCallback(() => {
6117
+ if (!state.isAnimating) return;
6118
+ const elapsed = Date.now() - startTimeRef.current;
6119
+ const progress = Math.min(elapsed / duration, 1);
6120
+ setState((prev) => ({
6121
+ ...prev,
6122
+ progress,
6123
+ currentStyle: calculateStyle(progress)
6124
+ }));
6125
+ if (progress < 1) {
6126
+ motionFrameRef.current = requestAnimationFrame(updateMotion);
6127
+ } else {
6128
+ setState((prev) => ({ ...prev, isAnimating: false }));
6129
+ onComplete?.();
6130
+ }
6131
+ }, [state.isAnimating, duration, calculateStyle, onComplete]);
6132
+ const start = useCallback(() => {
6133
+ setState((prev) => ({ ...prev, isAnimating: true, progress: 0 }));
6134
+ startTimeRef.current = Date.now();
6135
+ updateMotion();
6136
+ }, [updateMotion]);
6137
+ const stop = useCallback(() => {
6138
+ setState((prev) => ({ ...prev, isAnimating: false }));
6139
+ if (motionFrameRef.current) {
6140
+ cancelAnimationFrame(motionFrameRef.current);
6141
+ }
6142
+ }, []);
6143
+ const reset = useCallback(() => {
6144
+ setState({
6145
+ isAnimating: false,
6146
+ progress: 0,
6147
+ currentStyle: calculateStyle(0)
6148
+ });
6149
+ if (motionFrameRef.current) {
6150
+ cancelAnimationFrame(motionFrameRef.current);
6151
+ }
6152
+ }, [calculateStyle]);
6153
+ useEffect(() => {
6154
+ if (autoStart) {
6155
+ start();
6156
+ }
6157
+ return () => {
6158
+ if (motionFrameRef.current) {
6159
+ cancelAnimationFrame(motionFrameRef.current);
6160
+ }
6161
+ };
6162
+ }, [autoStart, start]);
6163
+ return {
6164
+ ref,
6165
+ state,
6166
+ start,
6167
+ stop,
6168
+ reset
6169
+ };
6170
+ }
6171
+ function createLayoutTransition(from, to, options = {}) {
6172
+ return {
6173
+ from,
6174
+ to,
6175
+ duration: options.duration || 500,
6176
+ easing: options.easing || "ease-in-out",
6177
+ autoStart: options.autoStart || false,
6178
+ onComplete: options.onComplete
6179
+ };
6180
+ }
6181
+ function useKeyboardToggle(options = {}) {
6182
+ const {
6183
+ initialState = false,
6184
+ keys = [" "],
6185
+ keyCode,
6186
+ keyCombo,
6187
+ toggleOnKeyDown = true,
6188
+ toggleOnKeyUp = false,
6189
+ toggleOnKeyPress = false,
6190
+ autoReset = false,
6191
+ resetDelay = 3e3,
6192
+ preventDefault = false,
6193
+ stopPropagation = false,
6194
+ requireFocus = false,
6195
+ showOnMount = false
6196
+ } = options;
6197
+ const [isActive, setIsActive] = useState(showOnMount ? initialState : false);
6198
+ const [mounted, setMounted] = useState(false);
6199
+ const resetTimeoutRef = useRef(null);
6200
+ const elementRef = useRef(null);
6201
+ const pressedKeysRef = useRef(/* @__PURE__ */ new Set());
6202
+ useEffect(() => {
6203
+ setMounted(true);
6204
+ }, []);
6205
+ const startResetTimer = useCallback(() => {
6206
+ if (!autoReset || resetDelay <= 0) return;
6207
+ if (resetTimeoutRef.current !== null) {
6208
+ clearTimeout(resetTimeoutRef.current);
6209
+ }
6210
+ resetTimeoutRef.current = window.setTimeout(() => {
6211
+ setIsActive(false);
6212
+ resetTimeoutRef.current = null;
6213
+ }, resetDelay);
6214
+ }, [autoReset, resetDelay]);
6215
+ const toggle = useCallback(() => {
6216
+ if (!mounted) return;
6217
+ setIsActive((prev) => {
6218
+ const newState = !prev;
6219
+ if (newState && autoReset) {
6220
+ startResetTimer();
6221
+ } else if (!newState && resetTimeoutRef.current !== null) {
6222
+ clearTimeout(resetTimeoutRef.current);
6223
+ resetTimeoutRef.current = null;
6224
+ }
6225
+ return newState;
6226
+ });
6227
+ }, [mounted, autoReset, startResetTimer]);
6228
+ const activate = useCallback(() => {
6229
+ if (!mounted || isActive) return;
6230
+ setIsActive(true);
6231
+ if (autoReset) {
6232
+ startResetTimer();
6233
+ }
6234
+ }, [mounted, isActive, autoReset, startResetTimer]);
6235
+ const deactivate = useCallback(() => {
6236
+ if (!mounted || !isActive) return;
6237
+ setIsActive(false);
6238
+ if (resetTimeoutRef.current !== null) {
6239
+ clearTimeout(resetTimeoutRef.current);
6240
+ resetTimeoutRef.current = null;
6241
+ }
6242
+ }, [mounted, isActive]);
6243
+ const reset = useCallback(() => {
6244
+ setIsActive(initialState);
6245
+ if (resetTimeoutRef.current !== null) {
6246
+ clearTimeout(resetTimeoutRef.current);
6247
+ resetTimeoutRef.current = null;
6248
+ }
6249
+ }, [initialState]);
6250
+ const isKeyMatch = useCallback(
6251
+ (event) => {
6252
+ if (keyCode !== void 0 && event.keyCode === keyCode) {
6253
+ return true;
6254
+ }
6255
+ if (keys.length > 0 && keys.includes(event.key)) {
6256
+ return true;
6257
+ }
6258
+ if (keyCombo && keyCombo.length > 0) {
6259
+ const pressedKeys = Array.from(pressedKeysRef.current);
6260
+ const comboMatch = keyCombo.every((key) => pressedKeys.includes(key));
6261
+ return comboMatch;
6262
+ }
6263
+ return false;
6264
+ },
6265
+ [keys, keyCode, keyCombo]
6266
+ );
6267
+ const isFocused = useCallback(() => {
6268
+ if (!requireFocus) return true;
6269
+ return document.activeElement === elementRef.current;
6270
+ }, [requireFocus]);
6271
+ const handleKeyDown = useCallback(
6272
+ (event) => {
6273
+ if (!toggleOnKeyDown || !isFocused()) return;
6274
+ pressedKeysRef.current.add(event.key);
6275
+ if (isKeyMatch(event)) {
6276
+ if (preventDefault) event.preventDefault();
6277
+ if (stopPropagation) event.stopPropagation();
6278
+ toggle();
6279
+ }
6280
+ },
6281
+ [
6282
+ toggleOnKeyDown,
6283
+ isFocused,
6284
+ isKeyMatch,
6285
+ preventDefault,
6286
+ stopPropagation,
6287
+ toggle
6288
+ ]
6289
+ );
6290
+ const handleKeyUp = useCallback(
6291
+ (event) => {
6292
+ if (!toggleOnKeyUp || !isFocused()) return;
6293
+ pressedKeysRef.current.delete(event.key);
6294
+ if (isKeyMatch(event)) {
6295
+ if (preventDefault) event.preventDefault();
6296
+ if (stopPropagation) event.stopPropagation();
6297
+ toggle();
6298
+ }
6299
+ },
6300
+ [
6301
+ toggleOnKeyUp,
6302
+ isFocused,
6303
+ isKeyMatch,
6304
+ preventDefault,
6305
+ stopPropagation,
6306
+ toggle
6307
+ ]
6308
+ );
6309
+ const handleKeyPress = useCallback(
6310
+ (event) => {
6311
+ if (!toggleOnKeyPress || !isFocused()) return;
6312
+ if (isKeyMatch(event)) {
6313
+ if (preventDefault) event.preventDefault();
6314
+ if (stopPropagation) event.stopPropagation();
6315
+ toggle();
6316
+ }
6317
+ },
6318
+ [
6319
+ toggleOnKeyPress,
6320
+ isFocused,
6321
+ isKeyMatch,
6322
+ preventDefault,
6323
+ stopPropagation,
6324
+ toggle
6325
+ ]
6326
+ );
6327
+ useEffect(() => {
6328
+ if (requireFocus || !mounted) return;
6329
+ const handleGlobalKeyDown = (event) => {
6330
+ pressedKeysRef.current.add(event.key);
6331
+ if (isKeyMatch(event)) {
6332
+ if (preventDefault) event.preventDefault();
6333
+ if (stopPropagation) event.stopPropagation();
6334
+ toggle();
6335
+ }
6336
+ };
6337
+ const handleGlobalKeyUp = (event) => {
6338
+ pressedKeysRef.current.delete(event.key);
6339
+ if (isKeyMatch(event)) {
6340
+ if (preventDefault) event.preventDefault();
6341
+ if (stopPropagation) event.stopPropagation();
6342
+ toggle();
6343
+ }
6344
+ };
6345
+ document.addEventListener("keydown", handleGlobalKeyDown);
6346
+ document.addEventListener("keyup", handleGlobalKeyUp);
6347
+ return () => {
6348
+ document.removeEventListener("keydown", handleGlobalKeyDown);
6349
+ document.removeEventListener("keyup", handleGlobalKeyUp);
6350
+ };
6351
+ }, [
6352
+ requireFocus,
6353
+ mounted,
6354
+ isKeyMatch,
6355
+ preventDefault,
6356
+ stopPropagation,
6357
+ toggle
6358
+ ]);
6359
+ useEffect(() => {
6360
+ return () => {
6361
+ if (resetTimeoutRef.current !== null) {
6362
+ clearTimeout(resetTimeoutRef.current);
6363
+ }
6364
+ };
6365
+ }, []);
6366
+ const keyboardHandlers = {
6367
+ ...toggleOnKeyDown && { onKeyDown: handleKeyDown },
6368
+ ...toggleOnKeyUp && { onKeyUp: handleKeyUp },
6369
+ ...toggleOnKeyPress && { onKeyPress: handleKeyPress }
6370
+ };
6371
+ return {
6372
+ isActive,
6373
+ mounted,
6374
+ toggle,
6375
+ activate,
6376
+ deactivate,
6377
+ reset,
6378
+ keyboardHandlers,
6379
+ ref: elementRef
6380
+ };
6381
+ }
6382
+ function useScrollDirection(options = {}) {
6383
+ const { threshold = 10, idleDelay = 150, showOnMount = false } = options;
6384
+ const [direction, setDirection] = useState(
6385
+ showOnMount ? "idle" : "idle"
6386
+ );
6387
+ const [mounted, setMounted] = useState(false);
6388
+ const [lastScrollY, setLastScrollY] = useState(0);
6389
+ const [idleTimeout, setIdleTimeout] = useState(null);
6390
+ useEffect(() => {
6391
+ setMounted(true);
6392
+ }, []);
6393
+ useEffect(() => {
6394
+ if (!mounted) return;
6395
+ const handleScroll = () => {
6396
+ if (typeof window !== "undefined") {
6397
+ const currentScrollY = window.pageYOffset;
6398
+ const scrollDifference = Math.abs(currentScrollY - lastScrollY);
6399
+ if (idleTimeout !== null) {
6400
+ clearTimeout(idleTimeout);
6401
+ }
6402
+ if (scrollDifference > threshold) {
6403
+ const newDirection = currentScrollY > lastScrollY ? "down" : "up";
6404
+ setDirection(newDirection);
6405
+ setLastScrollY(currentScrollY);
6406
+ const timeout = setTimeout(() => {
6407
+ setDirection("idle");
6408
+ }, idleDelay);
6409
+ setIdleTimeout(timeout);
6410
+ }
6411
+ }
6412
+ };
6413
+ if (typeof window !== "undefined") {
6414
+ setLastScrollY(window.pageYOffset);
6415
+ }
6416
+ window.addEventListener("scroll", handleScroll, { passive: true });
6417
+ return () => {
6418
+ window.removeEventListener("scroll", handleScroll);
6419
+ if (idleTimeout !== null) {
6420
+ clearTimeout(idleTimeout);
6421
+ }
6422
+ };
6423
+ }, [threshold, idleDelay, mounted, lastScrollY, idleTimeout]);
6424
+ return {
6425
+ direction,
6426
+ mounted
6427
+ };
6428
+ }
6429
+ function useStickyToggle(options = {}) {
6430
+ const {
6431
+ offset = 0,
6432
+ behavior: _behavior = "smooth",
6433
+ showOnMount = false
6434
+ } = options;
6435
+ const [isSticky, setIsSticky] = useState(showOnMount);
6436
+ const [mounted, setMounted] = useState(false);
6437
+ useEffect(() => {
6438
+ setMounted(true);
6439
+ }, []);
6440
+ useEffect(() => {
6441
+ if (!mounted) return;
6442
+ const toggleSticky = () => {
6443
+ if (typeof window !== "undefined") {
6444
+ if (window.pageYOffset > offset) {
6445
+ setIsSticky(true);
6446
+ } else {
6447
+ setIsSticky(false);
6448
+ }
6449
+ }
6450
+ };
6451
+ toggleSticky();
6452
+ window.addEventListener("scroll", toggleSticky, { passive: true });
6453
+ window.addEventListener("resize", toggleSticky, { passive: true });
6454
+ return () => {
6455
+ window.removeEventListener("scroll", toggleSticky);
6456
+ window.removeEventListener("resize", toggleSticky);
6457
+ };
6458
+ }, [offset, mounted]);
6459
+ return {
6460
+ isSticky,
6461
+ mounted
6462
+ };
6463
+ }
6464
+ function useInteractive(config = {}) {
6465
+ const {
6466
+ hoverScale = 1.05,
6467
+ clickScale = 0.95,
6468
+ duration: _duration = 200
6469
+ } = config;
6470
+ const ref = useRef(null);
6471
+ const [scale, setScale] = useState(1);
6472
+ const [isHovered, setIsHovered] = useState(false);
6473
+ const [isClicked, setIsClicked] = useState(false);
6474
+ const handleMouseEnter = useCallback(() => {
6475
+ setIsHovered(true);
6476
+ setScale(hoverScale);
6477
+ }, [hoverScale]);
6478
+ const handleMouseLeave = useCallback(() => {
6479
+ setIsHovered(false);
6480
+ setScale(1);
6481
+ }, []);
6482
+ const handleMouseDown = useCallback(() => {
6483
+ setIsClicked(true);
6484
+ setScale(clickScale);
6485
+ }, [clickScale]);
6486
+ const handleMouseUp = useCallback(() => {
6487
+ setIsClicked(false);
6488
+ setScale(isHovered ? hoverScale : 1);
6489
+ }, [isHovered, hoverScale]);
6490
+ const setRef = (element) => {
6491
+ if (ref.current !== element) {
6492
+ ref.current = element;
6493
+ }
6494
+ };
6495
+ return {
6496
+ ref: setRef,
6497
+ scale,
6498
+ isHovered,
6499
+ isClicked,
6500
+ handleMouseEnter,
6501
+ handleMouseLeave,
6502
+ handleMouseDown,
6503
+ handleMouseUp
6504
+ };
6505
+ }
6506
+ function usePerformanceMonitor(config = {}) {
6507
+ const { threshold = 30, onPerformanceIssue } = config;
6508
+ const ref = useRef(null);
6509
+ const [fps, setFps] = useState(60);
6510
+ const [isLowPerformance, setIsLowPerformance] = useState(false);
6511
+ const [frameCount, setFrameCount] = useState(0);
6512
+ const lastTime = useRef(performance.now());
6513
+ const frameCountRef = useRef(0);
6514
+ const measurePerformance = () => {
6515
+ const now = performance.now();
6516
+ frameCountRef.current++;
6517
+ if (now - lastTime.current >= 1e3) {
6518
+ const currentFps = Math.round(
6519
+ frameCountRef.current * 1e3 / (now - lastTime.current)
6520
+ );
6521
+ setFps(currentFps);
6522
+ setFrameCount(frameCountRef.current);
6523
+ const lowPerformance = currentFps < threshold;
6524
+ setIsLowPerformance(lowPerformance);
6525
+ if (lowPerformance && onPerformanceIssue) {
6526
+ onPerformanceIssue(currentFps);
6527
+ }
6528
+ frameCountRef.current = 0;
6529
+ lastTime.current = now;
6530
+ }
6531
+ requestAnimationFrame(measurePerformance);
6532
+ };
6533
+ useEffect(() => {
6534
+ const motionId = requestAnimationFrame(measurePerformance);
6535
+ return () => cancelAnimationFrame(motionId);
6536
+ }, []);
6537
+ const setRef = (element) => {
6538
+ if (ref.current !== element) {
6539
+ ref.current = element;
6540
+ }
6541
+ };
6542
+ return {
6543
+ ref: setRef,
6544
+ fps,
6545
+ isLowPerformance,
6546
+ frameCount
6547
+ };
6548
+ }
6549
+ function useLanguageAwareMotion(options) {
6550
+ const {
6551
+ motionType,
6552
+ duration = 700,
6553
+ delay = 0,
6554
+ threshold = 0.1,
6555
+ pauseOnLanguageChange = true,
6556
+ restartOnLanguageChange = false,
6557
+ currentLanguage: externalLanguage
6558
+ } = options;
6559
+ const elementRef = useRef(null);
6560
+ const [isVisible, setIsVisible] = useState(false);
6561
+ const [isPaused, setIsPaused] = useState(false);
6562
+ const [internalLanguage, setInternalLanguage] = useState("");
6563
+ const isVisibleRef = useRef(false);
6564
+ const isPausedRef = useRef(false);
6565
+ isVisibleRef.current = isVisible;
6566
+ isPausedRef.current = isPaused;
6567
+ useEffect(() => {
6568
+ if (externalLanguage && internalLanguage !== externalLanguage) {
6569
+ setInternalLanguage(externalLanguage);
6570
+ if (pauseOnLanguageChange && isVisible) {
6571
+ setIsPaused(true);
6572
+ setTimeout(() => {
6573
+ setIsPaused(false);
6574
+ }, 200);
6575
+ }
6576
+ if (restartOnLanguageChange && isVisible) {
6577
+ setIsVisible(false);
6578
+ setTimeout(() => {
6579
+ setIsVisible(true);
6580
+ }, 100);
6581
+ }
6582
+ }
6583
+ }, [
6584
+ externalLanguage,
6585
+ internalLanguage,
6586
+ isVisible,
6587
+ pauseOnLanguageChange,
6588
+ restartOnLanguageChange
6589
+ ]);
6590
+ useEffect(() => {
6591
+ if (!elementRef.current) return;
6592
+ const observer = new IntersectionObserver(
6593
+ (entries) => {
6594
+ entries.forEach((entry) => {
6595
+ if (entry.isIntersecting && !isVisibleRef.current && !isPausedRef.current) {
6596
+ setTimeout(() => {
6597
+ setIsVisible(true);
6598
+ }, delay);
6599
+ }
6600
+ });
6601
+ },
6602
+ { threshold }
6603
+ );
6604
+ observer.observe(elementRef.current);
6605
+ return () => {
6606
+ observer.disconnect();
6607
+ };
6608
+ }, [delay, threshold]);
6609
+ const getMotionStyle = useCallback(() => {
6610
+ const baseTransition = `all ${duration}ms ease-out`;
6611
+ if (isPaused) {
6612
+ return {
6613
+ opacity: 1,
6614
+ transform: "none",
6615
+ transition: baseTransition
6616
+ };
6617
+ }
6618
+ if (!isVisible) {
6619
+ switch (motionType) {
6620
+ case "fadeIn":
6621
+ return {
6622
+ opacity: 0,
6623
+ transition: baseTransition
6624
+ };
6625
+ case "slideUp":
6626
+ return {
6627
+ opacity: 0,
6628
+ transform: "translateY(32px)",
6629
+ transition: baseTransition
6630
+ };
6631
+ case "slideLeft":
6632
+ return {
6633
+ opacity: 0,
6634
+ transform: "translateX(-32px)",
6635
+ transition: baseTransition
6636
+ };
6637
+ case "slideRight":
6638
+ return {
6639
+ opacity: 0,
6640
+ transform: "translateX(32px)",
6641
+ transition: baseTransition
6642
+ };
6643
+ case "scaleIn":
6644
+ return {
6645
+ opacity: 0,
6646
+ transform: "scale(0.95)",
6647
+ transition: baseTransition
6648
+ };
6649
+ case "bounceIn":
6650
+ return {
6651
+ opacity: 0,
6652
+ transform: "scale(0.75)",
6653
+ transition: baseTransition
6654
+ };
6655
+ default:
6656
+ return {
6657
+ opacity: 0,
6658
+ transition: baseTransition
6659
+ };
6660
+ }
6661
+ }
6662
+ return {
6663
+ opacity: 1,
6664
+ transform: "none",
6665
+ transition: baseTransition
6666
+ };
6667
+ }, [isVisible, isPaused, motionType, duration]);
6668
+ const pauseMotion = useCallback(() => {
6669
+ setIsPaused(true);
6670
+ }, []);
6671
+ const resumeMotion = useCallback(() => {
6672
+ setIsPaused(false);
6673
+ }, []);
6674
+ const restartMotion = useCallback(() => {
6675
+ setIsVisible(false);
6676
+ setTimeout(() => {
6677
+ setIsVisible(true);
6678
+ }, 100);
6679
+ }, []);
6680
+ const motionStyle = useMemo(() => getMotionStyle(), [getMotionStyle]);
6681
+ return {
6682
+ ref: elementRef,
6683
+ isVisible,
6684
+ isPaused,
6685
+ style: motionStyle,
6686
+ pauseMotion,
6687
+ resumeMotion,
6688
+ restartMotion,
6689
+ currentLanguage: internalLanguage
6690
+ };
6691
+ }
6692
+ function useGameLoop(options = {}) {
6693
+ const {
6694
+ fps = 60,
6695
+ autoStart = false,
6696
+ maxFPS = 120,
6697
+ minFPS = 30,
6698
+ showOnMount = false
6699
+ } = options;
6700
+ const [isRunning, setIsRunning] = useState(showOnMount ? autoStart : false);
6701
+ const [isPaused, setIsPaused] = useState(false);
6702
+ const [currentFPS, setCurrentFPS] = useState(0);
6703
+ const [deltaTime, setDeltaTime] = useState(0);
6704
+ const [elapsedTime, setElapsedTime] = useState(0);
6705
+ const [frameCount, setFrameCount] = useState(0);
6706
+ const [mounted, setMounted] = useState(false);
6707
+ const motionRef = useRef(null);
6708
+ const lastTimeRef = useRef(null);
6709
+ const frameTimeRef = useRef(1e3 / fps);
6710
+ const fpsUpdateTimeRef = useRef(0);
6711
+ const fpsFrameCountRef = useRef(0);
6712
+ const updateCallbacksRef = useRef([]);
6713
+ const renderCallbacksRef = useRef([]);
6714
+ useEffect(() => {
6715
+ setMounted(true);
6716
+ }, []);
6717
+ const gameLoop = useCallback(
6718
+ (currentTime) => {
6719
+ if (!isRunning || isPaused) return;
6720
+ if (lastTimeRef.current === null) {
6721
+ lastTimeRef.current = currentTime;
6722
+ motionRef.current = requestAnimationFrame(gameLoop);
6723
+ return;
6724
+ }
6725
+ const delta = currentTime - lastTimeRef.current;
6726
+ const targetDelta = frameTimeRef.current;
6727
+ if (delta >= targetDelta) {
6728
+ updateCallbacksRef.current.forEach((callback) => {
6729
+ try {
6730
+ callback(delta, elapsedTime);
6731
+ } catch (error) {
6732
+ if (process.env.NODE_ENV === "development") {
6733
+ console.error("Game loop update error:", error);
6734
+ }
6735
+ }
6736
+ });
6737
+ renderCallbacksRef.current.forEach((callback) => {
6738
+ try {
6739
+ callback(delta, elapsedTime);
6740
+ } catch (error) {
6741
+ if (process.env.NODE_ENV === "development") {
6742
+ console.error("Game loop render error:", error);
6743
+ }
6744
+ }
6745
+ });
6746
+ setDeltaTime(delta);
6747
+ setElapsedTime((prev) => prev + delta);
6748
+ setFrameCount((prev) => prev + 1);
6749
+ lastTimeRef.current = currentTime;
6750
+ fpsFrameCountRef.current++;
6751
+ if (currentTime - fpsUpdateTimeRef.current >= 1e3) {
6752
+ const newFPS = Math.round(
6753
+ fpsFrameCountRef.current * 1e3 / (currentTime - fpsUpdateTimeRef.current)
6754
+ );
6755
+ setCurrentFPS(newFPS);
6756
+ fpsFrameCountRef.current = 0;
6757
+ fpsUpdateTimeRef.current = currentTime;
6758
+ if (process.env.NODE_ENV === "development" && typeof window !== "undefined" && window.location.hostname === "localhost") {
6759
+ if (newFPS < minFPS) {
6760
+ console.warn(`Low FPS detected: ${newFPS} (min: ${minFPS})`);
6761
+ }
6762
+ }
6763
+ }
6764
+ }
6765
+ motionRef.current = requestAnimationFrame(gameLoop);
6766
+ },
6767
+ [isRunning, isPaused, elapsedTime, minFPS]
6768
+ );
6769
+ const start = useCallback(() => {
6770
+ if (!mounted) return;
6771
+ setIsRunning(true);
6772
+ setIsPaused(false);
6773
+ setElapsedTime(0);
6774
+ setFrameCount(0);
6775
+ setDeltaTime(0);
6776
+ setCurrentFPS(0);
6777
+ lastTimeRef.current = null;
6778
+ fpsUpdateTimeRef.current = 0;
6779
+ fpsFrameCountRef.current = 0;
6780
+ if (!motionRef.current) {
6781
+ motionRef.current = requestAnimationFrame(gameLoop);
6782
+ }
6783
+ }, [mounted, gameLoop]);
6784
+ const stop = useCallback(() => {
6785
+ setIsRunning(false);
6786
+ setIsPaused(false);
6787
+ if (motionRef.current) {
6788
+ cancelAnimationFrame(motionRef.current);
6789
+ motionRef.current = null;
6790
+ }
6791
+ }, []);
6792
+ const pause = useCallback(() => {
6793
+ if (!isRunning) return;
6794
+ setIsPaused(true);
6795
+ }, [isRunning]);
6796
+ const resume = useCallback(() => {
6797
+ if (!isRunning) return;
6798
+ setIsPaused(false);
6799
+ if (!motionRef.current) {
6800
+ motionRef.current = requestAnimationFrame(gameLoop);
6801
+ }
6802
+ }, [isRunning, gameLoop]);
6803
+ const reset = useCallback(() => {
6804
+ setElapsedTime(0);
6805
+ setFrameCount(0);
6806
+ setDeltaTime(0);
6807
+ setCurrentFPS(0);
6808
+ lastTimeRef.current = null;
6809
+ fpsUpdateTimeRef.current = 0;
6810
+ fpsFrameCountRef.current = 0;
6811
+ }, []);
6812
+ const onUpdate = useCallback(
6813
+ (callback) => {
6814
+ updateCallbacksRef.current.push(callback);
6815
+ return () => {
6816
+ const index = updateCallbacksRef.current.indexOf(callback);
6817
+ if (index > -1) {
6818
+ updateCallbacksRef.current.splice(index, 1);
6819
+ }
6820
+ };
6821
+ },
6822
+ []
6823
+ );
6824
+ const onRender = useCallback(
6825
+ (callback) => {
6826
+ renderCallbacksRef.current.push(callback);
6827
+ return () => {
6828
+ const index = renderCallbacksRef.current.indexOf(callback);
6829
+ if (index > -1) {
6830
+ renderCallbacksRef.current.splice(index, 1);
6831
+ }
6832
+ };
6833
+ },
6834
+ []
6835
+ );
6836
+ useEffect(() => {
6837
+ frameTimeRef.current = 1e3 / Math.min(fps, maxFPS);
6838
+ }, [fps, maxFPS]);
6839
+ useEffect(() => {
6840
+ if (mounted && autoStart && !isRunning) {
6841
+ start();
6842
+ }
6843
+ }, [mounted, autoStart, isRunning, start]);
6844
+ useEffect(() => {
6845
+ return () => {
6846
+ if (motionRef.current) {
6847
+ cancelAnimationFrame(motionRef.current);
6848
+ }
6849
+ };
6850
+ }, []);
6851
+ return {
6852
+ isRunning,
6853
+ fps: currentFPS,
6854
+ deltaTime,
6855
+ elapsedTime,
6856
+ frameCount,
6857
+ mounted,
6858
+ start,
6859
+ stop,
6860
+ pause,
6861
+ resume,
6862
+ reset,
6863
+ onUpdate,
6864
+ onRender
6865
+ };
6866
+ }
6867
+ function useMotion(configOrFrom = {}, to, options) {
6868
+ let config;
6869
+ let fromValues;
6870
+ let toValues;
6871
+ if (to && options) {
6872
+ fromValues = configOrFrom;
6873
+ toValues = to;
6874
+ config = {
6875
+ duration: options.duration || 1e3,
6876
+ delay: options.delay || 0,
6877
+ autoStart: options.autoStart || false,
6878
+ easing: options.ease || "ease-out"
6879
+ };
6880
+ } else {
6881
+ config = configOrFrom;
6882
+ const { type = "fade" } = config;
6883
+ switch (type) {
6884
+ case "fade":
6885
+ fromValues = { opacity: 0 };
6886
+ toValues = { opacity: 1 };
6887
+ break;
6888
+ case "slide":
6889
+ fromValues = { opacity: 0, translateX: 100 };
6890
+ toValues = { opacity: 1, translateX: 0 };
6891
+ break;
6892
+ case "scale":
6893
+ fromValues = { opacity: 0, scale: 0 };
6894
+ toValues = { opacity: 1, scale: 1 };
6895
+ break;
6896
+ case "rotate":
6897
+ fromValues = { opacity: 0, rotate: 180 };
6898
+ toValues = { opacity: 1, rotate: 0 };
6899
+ break;
6900
+ default:
6901
+ fromValues = { opacity: 0 };
6902
+ toValues = { opacity: 1 };
6903
+ }
6904
+ }
6905
+ const {
6906
+ duration = 1e3,
6907
+ delay = 0,
6908
+ autoStart = true,
6909
+ easing = "ease-out"
6910
+ } = config;
6911
+ const ref = useRef(null);
6912
+ const fromValuesRef = useRef(fromValues);
6913
+ const toValuesRef = useRef(toValues);
6914
+ const initialBgColor = useRef(fromValues.backgroundColor);
6915
+ const [state, setState] = useState({
6916
+ transform: "",
6917
+ opacity: fromValues.opacity ?? 1,
6918
+ backgroundColor: fromValues.backgroundColor,
6919
+ isAnimating: false
6920
+ });
6921
+ const easingFunction = useCallback(
6922
+ (t) => {
6923
+ switch (easing) {
6924
+ case "ease-in":
6925
+ return t * t;
6926
+ case "ease-out":
6927
+ return 1 - (1 - t) * (1 - t);
6928
+ case "ease-in-out":
6929
+ return t < 0.5 ? 2 * t * t : 1 - 2 * (1 - t) * (1 - t);
6930
+ default:
6931
+ return t;
6932
+ }
6933
+ },
6934
+ [easing]
6935
+ );
6936
+ const interpolate = useCallback(
6937
+ (from, to2, progress) => {
6938
+ return from + (to2 - from) * progress;
6939
+ },
6940
+ []
6941
+ );
6942
+ const updateMotion = useCallback(
6943
+ (progress) => {
6944
+ const easedProgress = easingFunction(progress);
6945
+ const from = fromValuesRef.current;
6946
+ const to2 = toValuesRef.current;
6947
+ const newState = {
6948
+ transform: "",
6949
+ opacity: 1,
6950
+ backgroundColor: initialBgColor.current,
6951
+ isAnimating: true
6952
+ };
6953
+ if (from.opacity !== void 0 && to2.opacity !== void 0) {
6954
+ newState.opacity = interpolate(from.opacity, to2.opacity, easedProgress);
6955
+ }
6956
+ const transforms = [];
6957
+ if (from.translateX !== void 0 && to2.translateX !== void 0) {
6958
+ const translateX = interpolate(
6959
+ from.translateX,
6960
+ to2.translateX,
6961
+ easedProgress
6962
+ );
6963
+ transforms.push(`translateX(${translateX}px)`);
6964
+ }
6965
+ if (from.translateY !== void 0 && to2.translateY !== void 0) {
6966
+ const translateY = interpolate(
6967
+ from.translateY,
6968
+ to2.translateY,
6969
+ easedProgress
6970
+ );
6971
+ transforms.push(`translateY(${translateY}px)`);
6972
+ }
6973
+ if (from.scale !== void 0 && to2.scale !== void 0) {
6974
+ const scaleVal = interpolate(from.scale, to2.scale, easedProgress);
6975
+ transforms.push(`scale(${scaleVal})`);
6976
+ }
6977
+ if (from.rotate !== void 0 && to2.rotate !== void 0) {
6978
+ const rotate = interpolate(from.rotate, to2.rotate, easedProgress);
6979
+ transforms.push(`rotate(${rotate}deg)`);
6980
+ }
6981
+ if (transforms.length > 0) {
6982
+ newState.transform = transforms.join(" ");
6983
+ }
6984
+ if (from.backgroundColor && to2.backgroundColor) {
6985
+ newState.backgroundColor = easedProgress > 0.5 ? to2.backgroundColor : from.backgroundColor;
6986
+ }
6987
+ setState(newState);
6988
+ },
6989
+ [easingFunction, interpolate]
6990
+ );
6991
+ const start = useCallback(() => {
6992
+ setState((prev) => ({ ...prev, isAnimating: true }));
6993
+ const startTime = Date.now();
6994
+ const animate = () => {
6995
+ const elapsed = Date.now() - startTime;
6996
+ const progress = Math.min(elapsed / duration, 1);
6997
+ updateMotion(progress);
6998
+ if (progress < 1) {
6999
+ requestAnimationFrame(animate);
7000
+ } else {
7001
+ setState((prev) => ({ ...prev, isAnimating: false }));
7002
+ }
7003
+ };
7004
+ setTimeout(() => {
7005
+ requestAnimationFrame(animate);
7006
+ }, delay);
7007
+ }, [duration, delay, updateMotion]);
7008
+ const reset = useCallback(() => {
7009
+ const from = fromValuesRef.current;
7010
+ setState({
7011
+ transform: "",
7012
+ opacity: from.opacity ?? 1,
7013
+ backgroundColor: from.backgroundColor,
7014
+ isAnimating: false
7015
+ });
7016
+ }, []);
7017
+ const stop = useCallback(() => {
7018
+ setState((prev) => ({ ...prev, isAnimating: false }));
7019
+ }, []);
7020
+ useEffect(() => {
7021
+ if (autoStart) {
7022
+ start();
7023
+ }
7024
+ }, []);
7025
+ const setRef = (element) => {
7026
+ if (ref.current !== element) {
7027
+ ref.current = element;
7028
+ }
7029
+ };
7030
+ const style = {
7031
+ opacity: state.opacity,
7032
+ ...state.transform && { transform: state.transform },
7033
+ ...state.backgroundColor && { backgroundColor: state.backgroundColor }
7034
+ };
7035
+ return {
7036
+ ref: setRef,
7037
+ style,
7038
+ transform: state.transform,
7039
+ opacity: state.opacity,
7040
+ backgroundColor: state.backgroundColor,
7041
+ isAnimating: state.isAnimating,
7042
+ start,
7043
+ stop,
7044
+ reset
7045
+ };
7046
+ }
7047
+ function useViewportToggle(options = {}) {
7048
+ const {
7049
+ threshold = 0.1,
7050
+ rootMargin = "0px",
7051
+ trigger = "both",
7052
+ once = false,
7053
+ showOnMount = false
7054
+ } = options;
7055
+ const [isVisible, setIsVisible] = useState(showOnMount);
7056
+ const [mounted, setMounted] = useState(false);
7057
+ const elementRef = useRef(null);
7058
+ const hasTriggeredRef = useRef(false);
7059
+ const isVisibleRef = useRef(showOnMount);
7060
+ isVisibleRef.current = isVisible;
7061
+ useEffect(() => {
7062
+ setMounted(true);
7063
+ }, []);
7064
+ useEffect(() => {
7065
+ if (!mounted || !elementRef.current) return;
7066
+ const observer = new IntersectionObserver(
7067
+ (entries) => {
7068
+ entries.forEach((entry) => {
7069
+ const isIntersecting = entry.isIntersecting;
7070
+ if (once && hasTriggeredRef.current) return;
7071
+ let shouldBeVisible = isVisibleRef.current;
7072
+ if (trigger === "enter" && isIntersecting) {
7073
+ shouldBeVisible = true;
7074
+ if (once) hasTriggeredRef.current = true;
7075
+ } else if (trigger === "exit" && !isIntersecting) {
7076
+ shouldBeVisible = true;
7077
+ if (once) hasTriggeredRef.current = true;
7078
+ } else if (trigger === "both") {
7079
+ shouldBeVisible = isIntersecting;
7080
+ if (once && isIntersecting) hasTriggeredRef.current = true;
7081
+ }
7082
+ setIsVisible(shouldBeVisible);
7083
+ });
7084
+ },
7085
+ {
7086
+ threshold,
7087
+ rootMargin
7088
+ }
7089
+ );
7090
+ observer.observe(elementRef.current);
7091
+ return () => {
7092
+ observer.disconnect();
7093
+ };
7094
+ }, [threshold, rootMargin, trigger, once, mounted]);
7095
+ return {
7096
+ ref: elementRef,
7097
+ isVisible,
7098
+ mounted
7099
+ };
7100
+ }
7101
+ function useScrollPositionToggle(options = {}) {
7102
+ const { threshold = 400, showOnMount = false, smooth = true } = options;
7103
+ const [isVisible, setIsVisible] = useState(showOnMount);
7104
+ const [mounted, setMounted] = useState(false);
7105
+ useEffect(() => {
7106
+ setMounted(true);
7107
+ }, []);
7108
+ useEffect(() => {
7109
+ if (!mounted) return;
7110
+ const toggleVisibility = () => {
7111
+ if (typeof window !== "undefined") {
7112
+ if (window.pageYOffset > threshold) {
7113
+ setIsVisible(true);
7114
+ } else {
7115
+ setIsVisible(false);
7116
+ }
7117
+ }
7118
+ };
7119
+ toggleVisibility();
7120
+ window.addEventListener("scroll", toggleVisibility, { passive: true });
7121
+ window.addEventListener("resize", toggleVisibility, { passive: true });
7122
+ return () => {
7123
+ window.removeEventListener("scroll", toggleVisibility);
7124
+ window.removeEventListener("resize", toggleVisibility);
7125
+ };
7126
+ }, [threshold, mounted]);
7127
+ const scrollToTop = () => {
7128
+ if (typeof window !== "undefined") {
7129
+ if (smooth) {
7130
+ window.scrollTo({
7131
+ top: 0,
7132
+ behavior: "smooth"
7133
+ });
7134
+ } else {
7135
+ window.scrollTo(0, 0);
7136
+ }
7137
+ }
7138
+ };
7139
+ return {
7140
+ isVisible,
7141
+ scrollToTop,
7142
+ mounted
7143
+ };
7144
+ }
7145
+ function Motion({
7146
+ as: Component = "div",
7147
+ type,
7148
+ effects,
7149
+ scroll,
7150
+ delay,
7151
+ duration,
7152
+ children,
7153
+ className,
7154
+ style: userStyle,
7155
+ ...rest
7156
+ }) {
7157
+ const scrollOptions = useMemo(() => {
7158
+ if (!scroll) return null;
7159
+ const base = typeof scroll === "object" ? scroll : {};
7160
+ return {
7161
+ ...base,
7162
+ ...delay != null && { delay },
7163
+ ...duration != null && { duration },
7164
+ ...type != null && { motionType: type }
7165
+ };
7166
+ }, [scroll, delay, duration, type]);
7167
+ const scrollMotion = useScrollReveal(scrollOptions ?? { delay: 0 });
7168
+ const unifiedMotion = useUnifiedMotion({
7169
+ type: type ?? "fadeIn",
7170
+ effects,
7171
+ delay,
7172
+ duration,
7173
+ autoStart: true
7174
+ });
7175
+ const isScroll = scroll != null && scroll !== false;
7176
+ const motion = isScroll ? scrollMotion : unifiedMotion;
7177
+ const mergedStyle = useMemo(() => {
7178
+ if (!userStyle) return motion.style;
7179
+ return { ...motion.style, ...userStyle };
7180
+ }, [motion.style, userStyle]);
7181
+ return /* @__PURE__ */ jsx(
7182
+ Component,
7183
+ {
7184
+ ref: motion.ref,
7185
+ className,
7186
+ style: mergedStyle,
7187
+ ...rest,
7188
+ children
7189
+ }
7190
+ );
7191
+ }
7192
+ function useCountUp(options) {
7193
+ const {
7194
+ end,
7195
+ suffix = "",
7196
+ duration = 1500,
7197
+ delay = 0,
7198
+ active = true
7199
+ } = options;
7200
+ const [value, setValue] = useState(0);
7201
+ const startedRef = useRef(false);
7202
+ useEffect(() => {
7203
+ if (!active || startedRef.current) return;
7204
+ const timer = setTimeout(() => {
7205
+ startedRef.current = true;
7206
+ const startTime = performance.now();
7207
+ const animate = (now) => {
7208
+ const elapsed = now - startTime;
7209
+ const progress = Math.min(elapsed / duration, 1);
7210
+ const eased = 1 - Math.pow(1 - progress, 3);
7211
+ setValue(Math.round(eased * end));
7212
+ if (progress < 1) requestAnimationFrame(animate);
7213
+ };
7214
+ requestAnimationFrame(animate);
7215
+ }, delay);
7216
+ return () => clearTimeout(timer);
7217
+ }, [active, end, duration, delay]);
7218
+ return { value, display: `${value}${suffix}` };
7219
+ }
7220
+ var SPRING_EASING = "cubic-bezier(0.34, 1.56, 0.64, 1)";
7221
+ function useClipReveal(options = {}) {
7222
+ const {
7223
+ delay = 0,
7224
+ duration = 900,
7225
+ easing = SPRING_EASING,
7226
+ active = true
7227
+ } = options;
7228
+ const [triggered, setTriggered] = useState(false);
7229
+ useEffect(() => {
7230
+ if (active && !triggered) {
7231
+ setTriggered(true);
7232
+ }
7233
+ }, [active, triggered]);
7234
+ const containerStyle = useMemo(() => ({
7235
+ overflow: "hidden",
7236
+ display: "inline-block"
7237
+ }), []);
7238
+ const textStyle = useMemo(() => ({
7239
+ display: "block",
7240
+ transform: triggered ? "translateY(0)" : "translateY(110%)",
7241
+ transition: `transform ${duration}ms ${easing} ${delay}ms`
7242
+ }), [triggered, duration, easing, delay]);
7243
+ return { containerStyle, textStyle, isVisible: triggered };
7244
+ }
7245
+ var SMOOTH_EASING = "cubic-bezier(0.16, 1, 0.3, 1)";
7246
+ function useBlurIn(options = {}) {
7247
+ const {
7248
+ delay = 0,
7249
+ duration = 1200,
7250
+ blurAmount = 12,
7251
+ scale = 0.95,
7252
+ easing = SMOOTH_EASING,
7253
+ active = true
7254
+ } = options;
7255
+ const [triggered, setTriggered] = useState(false);
7256
+ useEffect(() => {
7257
+ if (active && !triggered) {
7258
+ setTriggered(true);
7259
+ }
7260
+ }, [active, triggered]);
7261
+ const style = useMemo(() => ({
7262
+ opacity: triggered ? 1 : 0,
7263
+ filter: triggered ? "blur(0px)" : `blur(${blurAmount}px)`,
7264
+ transform: triggered ? "scale(1)" : `scale(${scale})`,
7265
+ transition: [
7266
+ `opacity ${duration}ms ${easing} ${delay}ms`,
7267
+ `filter ${duration}ms ${easing} ${delay}ms`,
7268
+ `transform ${duration}ms ${easing} ${delay}ms`
7269
+ ].join(", ")
7270
+ }), [triggered, duration, blurAmount, scale, easing, delay]);
7271
+ return { style, isVisible: triggered };
5717
7272
  }
5718
7273
  function getInitialTransform(motionType, slideDistance, scaleFrom) {
5719
7274
  switch (motionType) {
@@ -5741,7 +7296,7 @@ function useStagger(options) {
5741
7296
  duration = profile.base.duration,
5742
7297
  motionType = "fadeIn",
5743
7298
  threshold = profile.base.threshold,
5744
- easing: easing2 = profile.base.easing
7299
+ easing = profile.base.easing
5745
7300
  } = options;
5746
7301
  const containerRef = useRef(null);
5747
7302
  const [isVisible, setIsVisible] = useState(false);
@@ -5767,16 +7322,16 @@ function useStagger(options) {
5767
7322
  return {
5768
7323
  opacity: 0,
5769
7324
  transform: initialTransform,
5770
- transition: `opacity ${duration}ms ${easing2} ${itemDelay}ms, transform ${duration}ms ${easing2} ${itemDelay}ms`
7325
+ transition: `opacity ${duration}ms ${easing} ${itemDelay}ms, transform ${duration}ms ${easing} ${itemDelay}ms`
5771
7326
  };
5772
7327
  }
5773
7328
  return {
5774
7329
  opacity: 1,
5775
7330
  transform: "none",
5776
- transition: `opacity ${duration}ms ${easing2} ${itemDelay}ms, transform ${duration}ms ${easing2} ${itemDelay}ms`
7331
+ transition: `opacity ${duration}ms ${easing} ${itemDelay}ms, transform ${duration}ms ${easing} ${itemDelay}ms`
5777
7332
  };
5778
7333
  });
5779
- }, [count, isVisible, staggerDelay, baseDelay, duration, motionType, easing2, initialTransform]);
7334
+ }, [count, isVisible, staggerDelay, baseDelay, duration, motionType, easing, initialTransform]);
5780
7335
  return {
5781
7336
  containerRef,
5782
7337
  styles,
@@ -5784,6 +7339,6 @@ function useStagger(options) {
5784
7339
  };
5785
7340
  }
5786
7341
 
5787
- export { MOTION_PRESETS, Motion, MotionEngine, MotionProfileProvider, PAGE_MOTIONS, TransitionEffects, applyEasing, calculateSpring, easeIn, easeInOut, easeInOutQuad, easeInQuad, easeOut, easeOutQuad, easingPresets, getAvailableEasings, getEasing, getMotionPreset, getPagePreset, getPresetEasing, hua, isEasingFunction, isValidEasing, linear, mergeProfileOverrides, mergeWithPreset, motionEngine, neutral, observeElement, resolveProfile, safeApplyEasing, transitionEffects, useBounceIn, useButtonEffect, useCardList, useClickToggle, useCustomCursor, useElementProgress, useFadeIn, useFocusToggle, useGesture, useGestureMotion, useGradient, useHoverMotion, useInView, useLoadingSpinner, useMagneticCursor, useMotionProfile, useMotionState, useMouse, useNavigation, usePageMotions, usePulse, useReducedMotion, useRepeat, useScaleIn, useScrollProgress, useScrollReveal, useScrollToggle, useSimplePageMotion, useSkeleton, useSlideDown, useSlideLeft, useSlideRight, useSlideUp, useSmartMotion, useSmoothScroll, useSpringMotion, useStagger, useToggleMotion, useTypewriter, useUnifiedMotion, useVisibilityToggle, useWindowSize };
7342
+ export { Motion, createLayoutTransition, observeElement, useAutoFade, useAutoPlay, useAutoScale, useAutoSlide, useBlurIn, useBounceIn, useButtonEffect, useCardList, useClickToggle, useClipReveal, useCountUp, useCustomCursor, useElementProgress, useFadeIn, useFocusToggle, useGameLoop, useGesture, useGestureMotion, useGradient, useHoverMotion, useInView, useInteractive, useKeyboardToggle, useLanguageAwareMotion, useLayoutMotion, useLoadingSpinner, useMagneticCursor, useMotion, useMotionOrchestra, useMotionState, useMouse, useNavigation, useOrchestration, usePageMotions, usePerformanceMonitor, usePulse, useReducedMotion, useRepeat, useScaleIn, useScrollDirection, useScrollPositionToggle, useScrollProgress, useScrollReveal, useScrollToggle, useSequence, useSimplePageMotion, useSkeleton, useSlideDown, useSlideLeft, useSlideRight, useSlideUp, useSmartMotion, useSmoothScroll, useSpringMotion, useStagger, useStickyToggle, useToggleMotion, useTypewriter, useUnifiedMotion, useViewportToggle, useVisibilityToggle, useWindowSize };
5788
7343
  //# sourceMappingURL=index.mjs.map
5789
7344
  //# sourceMappingURL=index.mjs.map