@umituz/react-native-design-system 2.6.48 → 2.6.50
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/package.json +1 -1
- package/src/atoms/skeleton/AtomicSkeleton.tsx +16 -62
- package/src/index.ts +0 -32
- package/src/molecules/bottom-sheet/components/BottomSheetModal.tsx +1 -12
- package/src/molecules/index.ts +0 -6
- package/src/molecules/animation/core/AnimationCore.ts +0 -29
- package/src/molecules/animation/domain/entities/Animation.ts +0 -81
- package/src/molecules/animation/domain/entities/Fireworks.ts +0 -44
- package/src/molecules/animation/domain/entities/Theme.ts +0 -76
- package/src/molecules/animation/index.ts +0 -146
- package/src/molecules/animation/infrastructure/services/AnimationConfigService.ts +0 -35
- package/src/molecules/animation/infrastructure/services/SpringAnimationConfigService.ts +0 -67
- package/src/molecules/animation/infrastructure/services/TimingAnimationConfigService.ts +0 -57
- package/src/molecules/animation/infrastructure/services/__tests__/SpringAnimationConfigService.test.ts +0 -114
- package/src/molecules/animation/infrastructure/services/__tests__/TimingAnimationConfigService.test.ts +0 -105
- package/src/molecules/animation/presentation/components/Fireworks.tsx +0 -127
- package/src/molecules/animation/presentation/components/__tests__/Fireworks.test.tsx +0 -185
- package/src/molecules/animation/presentation/hooks/__tests__/useAnimation.integration.test.ts +0 -210
- package/src/molecules/animation/presentation/hooks/__tests__/useFireworks.test.ts +0 -242
- package/src/molecules/animation/presentation/hooks/__tests__/useGesture.test.ts +0 -108
- package/src/molecules/animation/presentation/hooks/__tests__/useSpringAnimation.test.ts +0 -127
- package/src/molecules/animation/presentation/hooks/__tests__/useTimingAnimation.test.ts +0 -172
- package/src/molecules/animation/presentation/hooks/__tests__/useTransformAnimation.test.ts +0 -133
- package/src/molecules/animation/presentation/hooks/useAnimation.ts +0 -77
- package/src/molecules/animation/presentation/hooks/useFireworks.ts +0 -144
- package/src/molecules/animation/presentation/hooks/useGesture.ts +0 -57
- package/src/molecules/animation/presentation/hooks/useGestureCreators.ts +0 -163
- package/src/molecules/animation/presentation/hooks/useGestureState.ts +0 -53
- package/src/molecules/animation/presentation/hooks/useIconAnimations.ts +0 -120
- package/src/molecules/animation/presentation/hooks/useModalAnimations.ts +0 -124
- package/src/molecules/animation/presentation/hooks/useReanimatedReady.ts +0 -60
- package/src/molecules/animation/presentation/hooks/useSpringAnimation.ts +0 -69
- package/src/molecules/animation/presentation/hooks/useTimingAnimation.ts +0 -111
- package/src/molecules/animation/presentation/hooks/useTransformAnimation.ts +0 -57
- package/src/molecules/animation/presentation/providers/AnimationThemeProvider.tsx +0 -60
- package/src/molecules/animation/presentation/providers/__tests__/AnimationThemeProvider.test.tsx +0 -165
- package/src/molecules/celebration/domain/entities/CelebrationConfig.ts +0 -17
- package/src/molecules/celebration/domain/entities/FireworksConfig.ts +0 -32
- package/src/molecules/celebration/index.ts +0 -93
- package/src/molecules/celebration/infrastructure/services/FireworksConfigService.ts +0 -49
- package/src/molecules/celebration/presentation/components/CelebrationFireworksOverlay.tsx +0 -33
- package/src/molecules/celebration/presentation/components/CelebrationModal.tsx +0 -81
- package/src/molecules/celebration/presentation/components/CelebrationModalContent.tsx +0 -88
- package/src/molecules/celebration/presentation/hooks/useCelebrationModalAnimation.ts +0 -49
- package/src/molecules/celebration/presentation/hooks/useCelebrationState.ts +0 -45
- package/src/molecules/celebration/presentation/styles/CelebrationModalStyles.ts +0 -65
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useTimingAnimation Hook Tests
|
|
3
|
-
*
|
|
4
|
-
* Unit tests for timing-based animations (fade, slide).
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { renderHook, act } from '@testing-library/react';
|
|
8
|
-
import { useTimingAnimation } from '../useTimingAnimation';
|
|
9
|
-
import { ANIMATION_CONSTANTS } from '../../../domain/entities/Animation';
|
|
10
|
-
import { withTiming } from 'react-native-reanimated';
|
|
11
|
-
|
|
12
|
-
// Mock react-native-reanimated
|
|
13
|
-
jest.mock('react-native-reanimated', () => ({
|
|
14
|
-
useSharedValue: jest.fn((initialValue) => ({
|
|
15
|
-
value: initialValue,
|
|
16
|
-
})),
|
|
17
|
-
useAnimatedStyle: jest.fn((styleFactory) => styleFactory()),
|
|
18
|
-
withTiming: jest.fn((toValue, config) => ({ toValue, config })),
|
|
19
|
-
Easing: {
|
|
20
|
-
ease: jest.fn(),
|
|
21
|
-
out: jest.fn((easing) => easing),
|
|
22
|
-
bezier: jest.fn(() => jest.fn()),
|
|
23
|
-
},
|
|
24
|
-
}));
|
|
25
|
-
|
|
26
|
-
// Mock AnimationConfigService
|
|
27
|
-
jest.mock('../../../infrastructure/services/TimingAnimationConfigService', () => ({
|
|
28
|
-
TimingAnimationConfigService: {
|
|
29
|
-
getTimingConfig: jest.fn(() => ({
|
|
30
|
-
duration: ANIMATION_CONSTANTS.DURATION.NORMAL,
|
|
31
|
-
})),
|
|
32
|
-
},
|
|
33
|
-
}));
|
|
34
|
-
|
|
35
|
-
describe('useTimingAnimation', () => {
|
|
36
|
-
beforeEach(() => {
|
|
37
|
-
jest.clearAllMocks();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('should initialize with default values', () => {
|
|
41
|
-
const { result } = renderHook(() => useTimingAnimation());
|
|
42
|
-
|
|
43
|
-
expect(result.current.opacity.value).toBe(1);
|
|
44
|
-
expect(result.current.translateY.value).toBe(0);
|
|
45
|
-
expect(result.current.translateX.value).toBe(0);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('should provide animation functions', () => {
|
|
49
|
-
const { result } = renderHook(() => useTimingAnimation());
|
|
50
|
-
|
|
51
|
-
expect(typeof result.current.fadeIn).toBe('function');
|
|
52
|
-
expect(typeof result.current.fadeOut).toBe('function');
|
|
53
|
-
expect(typeof result.current.slideInUp).toBe('function');
|
|
54
|
-
expect(typeof result.current.slideInDown).toBe('function');
|
|
55
|
-
expect(typeof result.current.slideInLeft).toBe('function');
|
|
56
|
-
expect(typeof result.current.slideInRight).toBe('function');
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
describe('fadeIn', () => {
|
|
60
|
-
it('should animate opacity to 1 with default config', () => {
|
|
61
|
-
const { result } = renderHook(() => useTimingAnimation());
|
|
62
|
-
|
|
63
|
-
act(() => {
|
|
64
|
-
result.current.fadeIn();
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
expect(withTiming).toHaveBeenCalledWith(1, {
|
|
68
|
-
duration: ANIMATION_CONSTANTS.DURATION.NORMAL,
|
|
69
|
-
easing: expect.any(Function),
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('should use custom config when provided', () => {
|
|
74
|
-
const { result } = renderHook(() => useTimingAnimation());
|
|
75
|
-
const customConfig = { duration: 500 };
|
|
76
|
-
|
|
77
|
-
act(() => {
|
|
78
|
-
result.current.fadeIn(customConfig);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
expect(withTiming).toHaveBeenCalledWith(1, customConfig);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
describe('fadeOut', () => {
|
|
86
|
-
it('should animate opacity to 0 with default config', () => {
|
|
87
|
-
const { result } = renderHook(() => useTimingAnimation());
|
|
88
|
-
|
|
89
|
-
act(() => {
|
|
90
|
-
result.current.fadeOut();
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
expect(withTiming).toHaveBeenCalledWith(0, {
|
|
94
|
-
duration: ANIMATION_CONSTANTS.DURATION.NORMAL,
|
|
95
|
-
easing: expect.any(Function),
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
describe('slideInUp', () => {
|
|
101
|
-
it('should slide from bottom to top with default distance', () => {
|
|
102
|
-
const { result } = renderHook(() => useTimingAnimation());
|
|
103
|
-
|
|
104
|
-
act(() => {
|
|
105
|
-
result.current.slideInUp();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
expect(result.current.translateY.value).toBe(0);
|
|
109
|
-
expect(withTiming).toHaveBeenCalledWith(0, {
|
|
110
|
-
duration: ANIMATION_CONSTANTS.DURATION.NORMAL,
|
|
111
|
-
easing: expect.any(Function),
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('should use custom distance', () => {
|
|
116
|
-
const { result } = renderHook(() => useTimingAnimation());
|
|
117
|
-
const customDistance = 150;
|
|
118
|
-
|
|
119
|
-
act(() => {
|
|
120
|
-
result.current.slideInUp(customDistance);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
expect(result.current.translateY.value).toBe(0);
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
describe('slideInDown', () => {
|
|
128
|
-
it('should slide from top to bottom', () => {
|
|
129
|
-
const { result } = renderHook(() => useTimingAnimation());
|
|
130
|
-
|
|
131
|
-
act(() => {
|
|
132
|
-
result.current.slideInDown();
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
expect(result.current.translateY.value).toBe(0);
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
describe('slideInLeft', () => {
|
|
140
|
-
it('should slide from left to right', () => {
|
|
141
|
-
const { result } = renderHook(() => useTimingAnimation());
|
|
142
|
-
|
|
143
|
-
act(() => {
|
|
144
|
-
result.current.slideInLeft();
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
expect(result.current.translateX.value).toBe(0);
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
describe('slideInRight', () => {
|
|
152
|
-
it('should slide from right to left', () => {
|
|
153
|
-
const { result } = renderHook(() => useTimingAnimation());
|
|
154
|
-
|
|
155
|
-
act(() => {
|
|
156
|
-
result.current.slideInRight();
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
expect(result.current.translateX.value).toBe(0);
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
describe('shared values', () => {
|
|
164
|
-
it('should expose shared values for custom animations', () => {
|
|
165
|
-
const { result } = renderHook(() => useTimingAnimation());
|
|
166
|
-
|
|
167
|
-
expect(result.current.opacity).toBeDefined();
|
|
168
|
-
expect(result.current.translateY).toBeDefined();
|
|
169
|
-
expect(result.current.translateX).toBeDefined();
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
});
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useTransformAnimation Hook Tests
|
|
3
|
-
*
|
|
4
|
-
* Unit tests for transform-based animations (spin, pulse, shake).
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { renderHook, act } from '@testing-library/react';
|
|
8
|
-
import { useTransformAnimation } from '../useTransformAnimation';
|
|
9
|
-
import { withTiming, withSequence, withRepeat, Easing } from 'react-native-reanimated';
|
|
10
|
-
|
|
11
|
-
// Mock react-native-reanimated
|
|
12
|
-
jest.mock('react-native-reanimated', () => ({
|
|
13
|
-
useSharedValue: jest.fn((initialValue) => ({
|
|
14
|
-
value: initialValue,
|
|
15
|
-
})),
|
|
16
|
-
useAnimatedStyle: jest.fn((styleFactory) => styleFactory()),
|
|
17
|
-
withTiming: jest.fn((toValue, config) => ({ toValue, config })),
|
|
18
|
-
withSequence: jest.fn((...animations) => animations),
|
|
19
|
-
withRepeat: jest.fn((animation, count, reverse) => ({ animation, count, reverse })),
|
|
20
|
-
Easing: {
|
|
21
|
-
linear: jest.fn(),
|
|
22
|
-
},
|
|
23
|
-
}));
|
|
24
|
-
|
|
25
|
-
describe('useTransformAnimation', () => {
|
|
26
|
-
beforeEach(() => {
|
|
27
|
-
jest.clearAllMocks();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should initialize with default values', () => {
|
|
31
|
-
const { result } = renderHook(() => useTransformAnimation());
|
|
32
|
-
|
|
33
|
-
expect(result.current.translateX.value).toBe(0);
|
|
34
|
-
expect(result.current.scale.value).toBe(1);
|
|
35
|
-
expect(result.current.rotate.value).toBe(0);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should provide animation functions', () => {
|
|
39
|
-
const { result } = renderHook(() => useTransformAnimation());
|
|
40
|
-
|
|
41
|
-
expect(typeof result.current.shake).toBe('function');
|
|
42
|
-
expect(typeof result.current.pulse).toBe('function');
|
|
43
|
-
expect(typeof result.current.spin).toBe('function');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
describe('shake', () => {
|
|
47
|
-
it('should create shake animation sequence', () => {
|
|
48
|
-
const { result } = renderHook(() => useTransformAnimation());
|
|
49
|
-
|
|
50
|
-
act(() => {
|
|
51
|
-
result.current.shake();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
expect(withSequence).toHaveBeenCalledWith(
|
|
55
|
-
withTiming(-10, { duration: 50 }),
|
|
56
|
-
withRepeat(withTiming(10, { duration: 50 }), 4, true),
|
|
57
|
-
withTiming(0, { duration: 50 })
|
|
58
|
-
);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
describe('pulse', () => {
|
|
63
|
-
it('should create pulse animation with default repeat count', () => {
|
|
64
|
-
const { result } = renderHook(() => useTransformAnimation());
|
|
65
|
-
|
|
66
|
-
act(() => {
|
|
67
|
-
result.current.pulse();
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
expect(withRepeat).toHaveBeenCalledWith(
|
|
71
|
-
withSequence(withTiming(1.1, { duration: 500 }), withTiming(1, { duration: 500 })),
|
|
72
|
-
-1,
|
|
73
|
-
false
|
|
74
|
-
);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('should use custom repeat count', () => {
|
|
78
|
-
const { result } = renderHook(() => useTransformAnimation());
|
|
79
|
-
const customRepeatCount = 3;
|
|
80
|
-
|
|
81
|
-
act(() => {
|
|
82
|
-
result.current.pulse(customRepeatCount);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
expect(withRepeat).toHaveBeenCalledWith(
|
|
86
|
-
expect.any(Object),
|
|
87
|
-
customRepeatCount,
|
|
88
|
-
false
|
|
89
|
-
);
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
describe('spin', () => {
|
|
94
|
-
it('should create spin animation with default repeat count', () => {
|
|
95
|
-
const { result } = renderHook(() => useTransformAnimation());
|
|
96
|
-
|
|
97
|
-
act(() => {
|
|
98
|
-
result.current.spin();
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
expect(withRepeat).toHaveBeenCalledWith(
|
|
102
|
-
withTiming(360, { duration: 1000, easing: Easing.linear }),
|
|
103
|
-
-1,
|
|
104
|
-
false
|
|
105
|
-
);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('should use custom repeat count', () => {
|
|
109
|
-
const { result } = renderHook(() => useTransformAnimation());
|
|
110
|
-
const customRepeatCount = 2;
|
|
111
|
-
|
|
112
|
-
act(() => {
|
|
113
|
-
result.current.spin(customRepeatCount);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
expect(withRepeat).toHaveBeenCalledWith(
|
|
117
|
-
expect.any(Object),
|
|
118
|
-
customRepeatCount,
|
|
119
|
-
false
|
|
120
|
-
);
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
describe('shared values', () => {
|
|
125
|
-
it('should expose shared values for custom animations', () => {
|
|
126
|
-
const { result } = renderHook(() => useTransformAnimation());
|
|
127
|
-
|
|
128
|
-
expect(result.current.translateX).toBeDefined();
|
|
129
|
-
expect(result.current.scale).toBeDefined();
|
|
130
|
-
expect(result.current.rotate).toBeDefined();
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
});
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useAnimation Hook
|
|
3
|
-
*
|
|
4
|
-
* Orchestrator hook that combines all animation types.
|
|
5
|
-
* Single Responsibility: Compose and expose animation hooks.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { useAnimatedStyle } from 'react-native-reanimated';
|
|
9
|
-
import { useTimingAnimation } from './useTimingAnimation';
|
|
10
|
-
import { useSpringAnimation } from './useSpringAnimation';
|
|
11
|
-
import { useTransformAnimation } from './useTransformAnimation';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Hook for declarative animations
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* const { fadeIn, fadeOut, animatedStyle } = useAnimation();
|
|
18
|
-
*
|
|
19
|
-
* // Trigger animations
|
|
20
|
-
* fadeIn();
|
|
21
|
-
* fadeOut();
|
|
22
|
-
*
|
|
23
|
-
* // Apply to component
|
|
24
|
-
* <Animated.View style={animatedStyle}>...</Animated.View>
|
|
25
|
-
*/
|
|
26
|
-
export const useAnimation = () => {
|
|
27
|
-
const timing = useTimingAnimation();
|
|
28
|
-
const spring = useSpringAnimation();
|
|
29
|
-
const transform = useTransformAnimation();
|
|
30
|
-
|
|
31
|
-
// Combine all shared values for animated style
|
|
32
|
-
const animatedStyle = useAnimatedStyle(() => {
|
|
33
|
-
// Use transform values if they're non-zero, otherwise use timing/spring values
|
|
34
|
-
const translateXValue = transform.translateX.value !== 0
|
|
35
|
-
? transform.translateX.value
|
|
36
|
-
: timing.translateX.value;
|
|
37
|
-
const scaleValue = transform.scale.value !== 1
|
|
38
|
-
? transform.scale.value
|
|
39
|
-
: spring.scale.value;
|
|
40
|
-
|
|
41
|
-
return {
|
|
42
|
-
opacity: timing.opacity.value,
|
|
43
|
-
transform: [
|
|
44
|
-
{ translateY: timing.translateY.value },
|
|
45
|
-
{ translateX: translateXValue },
|
|
46
|
-
{ scale: scaleValue },
|
|
47
|
-
{ rotate: `${transform.rotate.value}deg` },
|
|
48
|
-
] as any,
|
|
49
|
-
};
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
// Timing animations
|
|
54
|
-
fadeIn: timing.fadeIn,
|
|
55
|
-
fadeOut: timing.fadeOut,
|
|
56
|
-
slideInUp: timing.slideInUp,
|
|
57
|
-
slideInDown: timing.slideInDown,
|
|
58
|
-
slideInLeft: timing.slideInLeft,
|
|
59
|
-
slideInRight: timing.slideInRight,
|
|
60
|
-
// Spring animations
|
|
61
|
-
scaleIn: spring.scaleIn,
|
|
62
|
-
scaleOut: spring.scaleOut,
|
|
63
|
-
bounce: spring.bounce,
|
|
64
|
-
// Transform animations
|
|
65
|
-
shake: transform.shake,
|
|
66
|
-
pulse: transform.pulse,
|
|
67
|
-
spin: transform.spin,
|
|
68
|
-
// Shared values (for custom animations)
|
|
69
|
-
opacity: timing.opacity,
|
|
70
|
-
translateY: timing.translateY,
|
|
71
|
-
translateX: timing.translateX,
|
|
72
|
-
scale: spring.scale,
|
|
73
|
-
rotate: transform.rotate,
|
|
74
|
-
// Animated style
|
|
75
|
-
animatedStyle,
|
|
76
|
-
};
|
|
77
|
-
};
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useFireworks Hook
|
|
3
|
-
*
|
|
4
|
-
* Hook for managing fireworks particle system.
|
|
5
|
-
* Single Responsibility: Manage fireworks state and lifecycle.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
9
|
-
import type { ParticleConfig, FireworksConfig } from '../../domain/entities/Fireworks';
|
|
10
|
-
import { FIREWORKS_CONSTANTS } from '../../domain/entities/Fireworks';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Hook for fireworks animation
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* const { particles, trigger, isActive } = useFireworks();
|
|
17
|
-
*
|
|
18
|
-
* // Trigger fireworks
|
|
19
|
-
* trigger();
|
|
20
|
-
*
|
|
21
|
-
* // Render particles
|
|
22
|
-
* {particles.map((particle, index) => (
|
|
23
|
-
* <Particle key={index} {...particle} />
|
|
24
|
-
* ))}
|
|
25
|
-
*/
|
|
26
|
-
export const useFireworks = (config: FireworksConfig) => {
|
|
27
|
-
const [particles, setParticles] = useState<ParticleConfig[]>([]);
|
|
28
|
-
const [isActive, setIsActive] = useState(false);
|
|
29
|
-
const animationFrameRef = useRef<number | null>(null);
|
|
30
|
-
const particlesRef = useRef<ParticleConfig[]>([]);
|
|
31
|
-
|
|
32
|
-
const {
|
|
33
|
-
particleCount = FIREWORKS_CONSTANTS.DEFAULT_PARTICLE_COUNT,
|
|
34
|
-
colors,
|
|
35
|
-
duration = FIREWORKS_CONSTANTS.DEFAULT_DURATION,
|
|
36
|
-
particleSize = FIREWORKS_CONSTANTS.DEFAULT_PARTICLE_SIZE,
|
|
37
|
-
spread = FIREWORKS_CONSTANTS.DEFAULT_SPREAD,
|
|
38
|
-
} = config;
|
|
39
|
-
|
|
40
|
-
if (!colors || colors.length === 0) {
|
|
41
|
-
// Return no-op when colors not provided
|
|
42
|
-
return { particles: [], trigger: () => { }, isActive: false };
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const createParticle = useCallback((
|
|
46
|
-
x: number,
|
|
47
|
-
y: number,
|
|
48
|
-
color: string
|
|
49
|
-
): ParticleConfig => {
|
|
50
|
-
const angle = Math.random() * Math.PI * 2;
|
|
51
|
-
const velocity = Math.random() * spread + 50;
|
|
52
|
-
|
|
53
|
-
return {
|
|
54
|
-
x,
|
|
55
|
-
y,
|
|
56
|
-
color,
|
|
57
|
-
size: particleSize + Math.random() * 2,
|
|
58
|
-
velocityX: Math.cos(angle) * velocity,
|
|
59
|
-
velocityY: Math.sin(angle) * velocity,
|
|
60
|
-
life: 1.0,
|
|
61
|
-
decay: FIREWORKS_CONSTANTS.DECAY_RATE + Math.random() * 0.01,
|
|
62
|
-
};
|
|
63
|
-
}, [particleSize, spread]);
|
|
64
|
-
|
|
65
|
-
const isActiveRef = useRef(false);
|
|
66
|
-
|
|
67
|
-
const updateParticles = useCallback(() => {
|
|
68
|
-
const currentParticles = [...particlesRef.current];
|
|
69
|
-
const updated: ParticleConfig[] = [];
|
|
70
|
-
|
|
71
|
-
for (const particle of currentParticles) {
|
|
72
|
-
const newX = particle.x + particle.velocityX * 0.1;
|
|
73
|
-
const newY = particle.y + particle.velocityY * 0.1 + FIREWORKS_CONSTANTS.GRAVITY;
|
|
74
|
-
const newVelocityY = particle.velocityY + FIREWORKS_CONSTANTS.GRAVITY;
|
|
75
|
-
const newLife = particle.life - particle.decay;
|
|
76
|
-
|
|
77
|
-
if (newLife > 0) {
|
|
78
|
-
updated.push({
|
|
79
|
-
...particle,
|
|
80
|
-
x: newX,
|
|
81
|
-
y: newY,
|
|
82
|
-
velocityY: newVelocityY,
|
|
83
|
-
life: newLife,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
particlesRef.current = updated;
|
|
89
|
-
setParticles(updated);
|
|
90
|
-
|
|
91
|
-
if (updated.length > 0 && isActiveRef.current) {
|
|
92
|
-
animationFrameRef.current = requestAnimationFrame(updateParticles);
|
|
93
|
-
} else {
|
|
94
|
-
setIsActive(false);
|
|
95
|
-
isActiveRef.current = false;
|
|
96
|
-
}
|
|
97
|
-
}, [setIsActive]); // Removed isActive dependency
|
|
98
|
-
|
|
99
|
-
const trigger = useCallback((x?: number, y?: number) => {
|
|
100
|
-
const centerX = x ?? 0;
|
|
101
|
-
const centerY = y ?? 0;
|
|
102
|
-
|
|
103
|
-
const newParticles: ParticleConfig[] = [];
|
|
104
|
-
for (let i = 0; i < particleCount; i++) {
|
|
105
|
-
const colorIndex = Math.floor(Math.random() * colors.length);
|
|
106
|
-
const color = colors[colorIndex] ?? colors[0] ?? '#FFFFFF';
|
|
107
|
-
newParticles.push(createParticle(centerX, centerY, color));
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Create new array reference to avoid worklet issues
|
|
111
|
-
const particlesArray = [...newParticles];
|
|
112
|
-
particlesRef.current = particlesArray;
|
|
113
|
-
setParticles(particlesArray);
|
|
114
|
-
setIsActive(true);
|
|
115
|
-
isActiveRef.current = true;
|
|
116
|
-
|
|
117
|
-
if (animationFrameRef.current) {
|
|
118
|
-
cancelAnimationFrame(animationFrameRef.current);
|
|
119
|
-
}
|
|
120
|
-
animationFrameRef.current = requestAnimationFrame(updateParticles);
|
|
121
|
-
|
|
122
|
-
// Auto-stop after duration
|
|
123
|
-
setTimeout(() => {
|
|
124
|
-
setIsActive(false);
|
|
125
|
-
if (animationFrameRef.current) {
|
|
126
|
-
cancelAnimationFrame(animationFrameRef.current);
|
|
127
|
-
}
|
|
128
|
-
}, duration);
|
|
129
|
-
}, [particleCount, colors, duration, createParticle, updateParticles]);
|
|
130
|
-
|
|
131
|
-
useEffect(() => {
|
|
132
|
-
return () => {
|
|
133
|
-
if (animationFrameRef.current) {
|
|
134
|
-
cancelAnimationFrame(animationFrameRef.current);
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
}, []);
|
|
138
|
-
|
|
139
|
-
return {
|
|
140
|
-
particles,
|
|
141
|
-
trigger,
|
|
142
|
-
isActive,
|
|
143
|
-
};
|
|
144
|
-
};
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useGesture Hook
|
|
3
|
-
*
|
|
4
|
-
* Orchestrator hook that combines gesture creators and state.
|
|
5
|
-
* Single Responsibility: Compose and expose gesture functionality.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { GestureDetector } from 'react-native-gesture-handler';
|
|
9
|
-
import { useGestureState } from './useGestureState';
|
|
10
|
-
import { useGestureCreators } from './useGestureCreators';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Hook for gesture handling
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* const { createPanGesture, animatedStyle, GestureDetector } = useGesture();
|
|
18
|
-
*
|
|
19
|
-
* const panGesture = createPanGesture({
|
|
20
|
-
* onEnd: (x, y) => console.log('Dragged to:', x, y),
|
|
21
|
-
* });
|
|
22
|
-
*
|
|
23
|
-
* return (
|
|
24
|
-
* <GestureDetector gesture={panGesture}>
|
|
25
|
-
* <Animated.View style={animatedStyle}>...</Animated.View>
|
|
26
|
-
* </GestureDetector>
|
|
27
|
-
* );
|
|
28
|
-
*/
|
|
29
|
-
export const useGesture = () => {
|
|
30
|
-
const state = useGestureState();
|
|
31
|
-
const creators = useGestureCreators(state);
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
// Gesture creators
|
|
35
|
-
createTapGesture: creators.createTapGesture,
|
|
36
|
-
createPanGesture: creators.createPanGesture,
|
|
37
|
-
createPinchGesture: creators.createPinchGesture,
|
|
38
|
-
createLongPressGesture: creators.createLongPressGesture,
|
|
39
|
-
// Shared values (for custom gestures)
|
|
40
|
-
translateX: state.translateX,
|
|
41
|
-
translateY: state.translateY,
|
|
42
|
-
scale: state.scale,
|
|
43
|
-
// Utilities
|
|
44
|
-
reset: state.reset,
|
|
45
|
-
// Animated style
|
|
46
|
-
animatedStyle: state.animatedStyle,
|
|
47
|
-
// Re-export GestureDetector for convenience
|
|
48
|
-
GestureDetector: GestureDetector as typeof GestureDetector,
|
|
49
|
-
};
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
// Re-export types for convenience
|
|
53
|
-
export type {
|
|
54
|
-
TapGestureOptions,
|
|
55
|
-
PanGestureOptions,
|
|
56
|
-
PinchGestureOptions,
|
|
57
|
-
} from './useGestureCreators';
|