@idealyst/animate 1.2.29

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 ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@idealyst/animate",
3
+ "version": "1.2.29",
4
+ "description": "Cross-platform animation hooks for React and React Native",
5
+ "readme": "README.md",
6
+ "main": "src/index.ts",
7
+ "module": "src/index.ts",
8
+ "types": "src/index.ts",
9
+ "react-native": "src/index.native.ts",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/IdealystIO/idealyst-framework.git",
13
+ "directory": "packages/animate"
14
+ },
15
+ "author": "Idealyst <contact@idealyst.io>",
16
+ "license": "MIT",
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "react-native": "./src/index.native.ts",
23
+ "import": "./src/index.ts",
24
+ "require": "./src/index.ts",
25
+ "types": "./src/index.ts"
26
+ }
27
+ },
28
+ "scripts": {
29
+ "prepublishOnly": "echo 'Publishing TypeScript source directly'",
30
+ "publish:npm": "npm publish"
31
+ },
32
+ "peerDependencies": {
33
+ "@idealyst/theme": "^1.2.29",
34
+ "react": ">=16.8.0",
35
+ "react-native": ">=0.60.0",
36
+ "react-native-reanimated": ">=3.0.0",
37
+ "react-native-svg": ">=13.0.0"
38
+ },
39
+ "peerDependenciesMeta": {
40
+ "react-native": {
41
+ "optional": true
42
+ },
43
+ "react-native-reanimated": {
44
+ "optional": true
45
+ },
46
+ "react-native-svg": {
47
+ "optional": true
48
+ }
49
+ },
50
+ "devDependencies": {
51
+ "@idealyst/theme": "^1.2.29",
52
+ "@types/react": "^19.1.0",
53
+ "react": "^19.1.0",
54
+ "react-native": "^0.80.1",
55
+ "react-native-reanimated": "^4.1.3",
56
+ "react-native-svg": "^15.14.0",
57
+ "typescript": "^5.0.0"
58
+ },
59
+ "files": [
60
+ "src",
61
+ "README.md"
62
+ ],
63
+ "keywords": [
64
+ "react",
65
+ "react-native",
66
+ "animation",
67
+ "hooks",
68
+ "cross-platform",
69
+ "reanimated",
70
+ "css-transitions"
71
+ ]
72
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * @idealyst/animate - Native exports
3
+ *
4
+ * Cross-platform animation hooks for React and React Native.
5
+ * This module provides a unified API for creating smooth animations
6
+ * that work on both web and native platforms.
7
+ *
8
+ * On native, animations use Reanimated for UI thread performance.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * import Animated from 'react-native-reanimated';
13
+ * import { useAnimatedStyle, usePresence, useGradientBorder } from '@idealyst/animate';
14
+ *
15
+ * // State-driven animations
16
+ * const style = useAnimatedStyle({
17
+ * opacity: isVisible ? 1 : 0,
18
+ * }, { duration: 'normal', easing: 'easeOut' });
19
+ *
20
+ * return <Animated.View style={style}>Content</Animated.View>;
21
+ *
22
+ * // Mount/unmount animations
23
+ * const { isPresent, style } = usePresence(isOpen, {
24
+ * enter: { opacity: 1 },
25
+ * exit: { opacity: 0 },
26
+ * });
27
+ *
28
+ * // Gradient borders
29
+ * const { containerStyle, contentStyle, GradientBorder } = useGradientBorder({
30
+ * colors: ['#3b82f6', '#8b5cf6'],
31
+ * animation: 'spin',
32
+ * });
33
+ * ```
34
+ */
35
+
36
+ // Type exports
37
+ export type {
38
+ AnimatableStyle,
39
+ AnimatableProperties,
40
+ TransformProperty,
41
+ AnimationOptions,
42
+ PlatformOverrides,
43
+ UseAnimatedStyleOptions,
44
+ AnimatedValue,
45
+ InterpolationConfig,
46
+ SequenceStep,
47
+ UseSequenceResult,
48
+ KeyframePercentage,
49
+ KeyframeDefinition,
50
+ UseKeyframesOptions,
51
+ UseKeyframesResult,
52
+ UsePresenceOptions,
53
+ UsePresenceResult,
54
+ GradientAnimation,
55
+ UseGradientBorderOptions,
56
+ UseGradientBorderResult,
57
+ } from './types';
58
+
59
+ // Hook exports (native implementations)
60
+ export { useAnimatedStyle } from './useAnimatedStyle.native';
61
+ export { useAnimatedValue } from './useAnimatedValue.native';
62
+ export { usePresence } from './usePresence.native';
63
+ export { useGradientBorder } from './useGradientBorder.native';
64
+
65
+ // Re-export animation tokens for convenience
66
+ export { durations, easings, presets } from '@idealyst/theme/animation';
package/src/index.ts ADDED
@@ -0,0 +1,64 @@
1
+ /**
2
+ * @idealyst/animate - Web exports
3
+ *
4
+ * Cross-platform animation hooks for React and React Native.
5
+ * This module provides a unified API for creating smooth animations
6
+ * that work on both web and native platforms.
7
+ *
8
+ * On web, animations use CSS transitions and requestAnimationFrame
9
+ * for optimal performance.
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * import { useAnimatedStyle, usePresence, useGradientBorder } from '@idealyst/animate';
14
+ *
15
+ * // State-driven animations
16
+ * const style = useAnimatedStyle({
17
+ * opacity: isVisible ? 1 : 0,
18
+ * }, { duration: 'normal', easing: 'easeOut' });
19
+ *
20
+ * // Mount/unmount animations
21
+ * const { isPresent, style } = usePresence(isOpen, {
22
+ * enter: { opacity: 1 },
23
+ * exit: { opacity: 0 },
24
+ * });
25
+ *
26
+ * // Gradient borders
27
+ * const { containerStyle, contentStyle } = useGradientBorder({
28
+ * colors: ['#3b82f6', '#8b5cf6'],
29
+ * animation: 'spin',
30
+ * });
31
+ * ```
32
+ */
33
+
34
+ // Type exports
35
+ export type {
36
+ AnimatableStyle,
37
+ AnimatableProperties,
38
+ TransformProperty,
39
+ AnimationOptions,
40
+ PlatformOverrides,
41
+ UseAnimatedStyleOptions,
42
+ AnimatedValue,
43
+ InterpolationConfig,
44
+ SequenceStep,
45
+ UseSequenceResult,
46
+ KeyframePercentage,
47
+ KeyframeDefinition,
48
+ UseKeyframesOptions,
49
+ UseKeyframesResult,
50
+ UsePresenceOptions,
51
+ UsePresenceResult,
52
+ GradientAnimation,
53
+ UseGradientBorderOptions,
54
+ UseGradientBorderResult,
55
+ } from './types';
56
+
57
+ // Hook exports
58
+ export { useAnimatedStyle } from './useAnimatedStyle';
59
+ export { useAnimatedValue } from './useAnimatedValue';
60
+ export { usePresence } from './usePresence';
61
+ export { useGradientBorder, createGradientBorderStyle } from './useGradientBorder';
62
+
63
+ // Re-export animation tokens for convenience
64
+ export { durations, easings, presets } from '@idealyst/theme/animation';
package/src/types.ts ADDED
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Type definitions for @idealyst/animate
3
+ */
4
+
5
+ import type { ViewStyle, TextStyle, ImageStyle } from 'react-native';
6
+ import type { Duration, EasingKey, SpringType } from '@idealyst/theme/animation';
7
+
8
+ // Style types
9
+ export type AnimatableStyle = ViewStyle | TextStyle | ImageStyle;
10
+ export type StyleValue = string | number | undefined;
11
+
12
+ // Transform types (React Native style)
13
+ export type TransformProperty =
14
+ | { perspective: number }
15
+ | { rotate: string }
16
+ | { rotateX: string }
17
+ | { rotateY: string }
18
+ | { rotateZ: string }
19
+ | { scale: number }
20
+ | { scaleX: number }
21
+ | { scaleY: number }
22
+ | { translateX: number }
23
+ | { translateY: number }
24
+ | { skewX: string }
25
+ | { skewY: string };
26
+
27
+ // Animatable properties (subset that can be animated smoothly)
28
+ export interface AnimatableProperties {
29
+ // Opacity
30
+ opacity?: number;
31
+
32
+ // Background
33
+ backgroundColor?: string;
34
+
35
+ // Border
36
+ borderColor?: string;
37
+ borderWidth?: number;
38
+ borderRadius?: number;
39
+ borderTopLeftRadius?: number;
40
+ borderTopRightRadius?: number;
41
+ borderBottomLeftRadius?: number;
42
+ borderBottomRightRadius?: number;
43
+
44
+ // Dimensions (use sparingly - triggers layout)
45
+ width?: number | string;
46
+ height?: number | string;
47
+ maxHeight?: number | string;
48
+ maxWidth?: number | string;
49
+ minHeight?: number | string;
50
+ minWidth?: number | string;
51
+
52
+ // Positioning (use sparingly - triggers layout)
53
+ top?: number | string;
54
+ right?: number | string;
55
+ bottom?: number | string;
56
+ left?: number | string;
57
+
58
+ // Transform (preferred for performance)
59
+ transform?: TransformProperty[];
60
+ }
61
+
62
+ // Animation configuration
63
+ export interface AnimationOptions {
64
+ /** Duration in ms or token key */
65
+ duration?: Duration;
66
+ /** Easing function key */
67
+ easing?: EasingKey;
68
+ /** Delay before animation starts (ms) */
69
+ delay?: number;
70
+ }
71
+
72
+ // Platform-specific overrides
73
+ export interface PlatformOverrides {
74
+ /** Web-specific options */
75
+ web?: AnimationOptions & {
76
+ /** Custom CSS transition string */
77
+ transition?: string;
78
+ };
79
+ /** Native-specific options */
80
+ native?: AnimationOptions & {
81
+ /** Use spring animation instead of timing */
82
+ useSpring?: boolean;
83
+ /** Spring type if useSpring is true */
84
+ springType?: SpringType;
85
+ };
86
+ }
87
+
88
+ // useAnimatedStyle options
89
+ export interface UseAnimatedStyleOptions extends AnimationOptions, PlatformOverrides {}
90
+
91
+ // useAnimatedValue return type
92
+ export interface AnimatedValue {
93
+ /** Current value (for reading) */
94
+ readonly value: number;
95
+ /** Set value with animation */
96
+ set: (target: number, options?: AnimationOptions) => void;
97
+ /** Set value immediately without animation */
98
+ setImmediate: (target: number) => void;
99
+ /** Interpolate value to another range */
100
+ interpolate: <T extends string | number>(config: InterpolationConfig<T>) => T;
101
+ }
102
+
103
+ export interface InterpolationConfig<T> {
104
+ inputRange: number[];
105
+ outputRange: T[];
106
+ extrapolate?: 'extend' | 'clamp' | 'identity';
107
+ extrapolateLeft?: 'extend' | 'clamp' | 'identity';
108
+ extrapolateRight?: 'extend' | 'clamp' | 'identity';
109
+ }
110
+
111
+ // useSequence types
112
+ export interface SequenceStep {
113
+ /** Style properties for this step */
114
+ style?: AnimatableProperties;
115
+ /** Duration for this step */
116
+ duration?: Duration;
117
+ /** Easing for this step */
118
+ easing?: EasingKey;
119
+ /** Delay before this step */
120
+ delay?: number;
121
+ }
122
+
123
+ export interface UseSequenceResult {
124
+ /** Current animated style */
125
+ style: AnimatableStyle;
126
+ /** Start/restart the sequence */
127
+ play: () => void;
128
+ /** Reset to initial state */
129
+ reset: () => void;
130
+ /** Pause animation (web only) */
131
+ pause: () => void;
132
+ /** Whether animation is currently playing */
133
+ isPlaying: boolean;
134
+ }
135
+
136
+ // useKeyframes types
137
+ export type KeyframePercentage = `${number}%` | 'from' | 'to';
138
+ export type KeyframeDefinition = Partial<Record<KeyframePercentage, AnimatableProperties>>;
139
+
140
+ export interface UseKeyframesOptions extends AnimationOptions {
141
+ /** Number of iterations (default: 1) */
142
+ iterations?: number | 'infinite';
143
+ /** Animation direction */
144
+ direction?: 'normal' | 'reverse' | 'alternate' | 'alternate-reverse';
145
+ /** Fill mode */
146
+ fillMode?: 'none' | 'forwards' | 'backwards' | 'both';
147
+ /** Auto-play on mount */
148
+ autoPlay?: boolean;
149
+ }
150
+
151
+ export interface UseKeyframesResult {
152
+ /** Animated style to apply */
153
+ style: AnimatableStyle;
154
+ /** Start animation */
155
+ play: () => void;
156
+ /** Pause animation */
157
+ pause: () => void;
158
+ /** Reset to initial state */
159
+ reset: () => void;
160
+ /** Whether animation is playing */
161
+ isPlaying: boolean;
162
+ }
163
+
164
+ // usePresence types
165
+ export interface UsePresenceOptions extends AnimationOptions {
166
+ /** Style when entering/visible */
167
+ enter: AnimatableProperties;
168
+ /** Style when exiting/hidden */
169
+ exit: AnimatableProperties;
170
+ /** Initial style (defaults to exit) */
171
+ initial?: AnimatableProperties;
172
+ }
173
+
174
+ export interface UsePresenceResult {
175
+ /** Whether element should be rendered */
176
+ isPresent: boolean;
177
+ /** Animated style to apply */
178
+ style: AnimatableStyle;
179
+ /** Trigger exit animation manually */
180
+ exit: () => void;
181
+ }
182
+
183
+ // useGradientBorder types
184
+ export type GradientAnimation = 'spin' | 'pulse' | 'wave';
185
+
186
+ export interface UseGradientBorderOptions {
187
+ /** Gradient colors */
188
+ colors: string[];
189
+ /** Border width in pixels */
190
+ borderWidth?: number;
191
+ /** Border radius in pixels */
192
+ borderRadius?: number;
193
+ /** Animation duration in ms or token */
194
+ duration?: Duration;
195
+ /** Animation type */
196
+ animation?: GradientAnimation;
197
+ /** Whether animation is active */
198
+ active?: boolean;
199
+ }
200
+
201
+ export interface UseGradientBorderResult {
202
+ /** Style for the outer container */
203
+ containerStyle: AnimatableStyle;
204
+ /** Style for the inner content wrapper */
205
+ contentStyle: AnimatableStyle;
206
+ /** Whether gradient CSS has been injected (web only) */
207
+ isReady: boolean;
208
+ }
@@ -0,0 +1,168 @@
1
+ /**
2
+ * useAnimatedStyle - Native implementation
3
+ *
4
+ * Creates an animated style using Reanimated.
5
+ * Uses either timing or spring animations based on configuration.
6
+ */
7
+
8
+ import { useEffect, useMemo } from 'react';
9
+ import {
10
+ useSharedValue,
11
+ useAnimatedStyle as useReanimatedStyle,
12
+ withTiming,
13
+ withSpring,
14
+ withDelay,
15
+ Easing,
16
+ } from 'react-native-reanimated';
17
+ import { timingConfig, springConfig, isSpringEasing } from '@idealyst/theme/animation';
18
+ import type { AnimatableProperties, UseAnimatedStyleOptions, AnimatableStyle } from './types';
19
+
20
+ /**
21
+ * Hook that returns an animated style object.
22
+ * When the style properties change, they animate smoothly using Reanimated.
23
+ *
24
+ * @param style - The target style properties
25
+ * @param options - Animation configuration
26
+ * @returns Animated style object to spread onto an Animated component
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * import Animated from 'react-native-reanimated';
31
+ *
32
+ * const style = useAnimatedStyle({
33
+ * opacity: isVisible ? 1 : 0,
34
+ * transform: [{ translateY: isVisible ? 0 : 20 }],
35
+ * }, {
36
+ * duration: 'normal',
37
+ * easing: 'easeOut',
38
+ * });
39
+ *
40
+ * return <Animated.View style={style}>Content</Animated.View>;
41
+ * ```
42
+ */
43
+ export function useAnimatedStyle(
44
+ style: AnimatableProperties,
45
+ options: UseAnimatedStyleOptions = {}
46
+ ): AnimatableStyle {
47
+ const { duration = 'normal', easing = 'easeOut', delay = 0, native } = options;
48
+
49
+ // Use native-specific options if provided
50
+ const finalDuration = native?.duration ?? duration;
51
+ const finalEasing = native?.easing ?? easing;
52
+ const finalDelay = native?.delay ?? delay;
53
+ const useSpring = native?.useSpring ?? isSpringEasing(finalEasing);
54
+ const springType = native?.springType ?? (isSpringEasing(finalEasing) ? finalEasing : 'spring');
55
+
56
+ // Create shared values for each animatable property
57
+ const opacity = useSharedValue(style.opacity ?? 1);
58
+ const backgroundColor = useSharedValue(style.backgroundColor ?? 'transparent');
59
+ const borderColor = useSharedValue(style.borderColor ?? 'transparent');
60
+ const borderWidth = useSharedValue(style.borderWidth ?? 0);
61
+ const borderRadius = useSharedValue(style.borderRadius ?? 0);
62
+
63
+ // Transform values
64
+ const translateX = useSharedValue(0);
65
+ const translateY = useSharedValue(0);
66
+ const scale = useSharedValue(1);
67
+ const scaleX = useSharedValue(1);
68
+ const scaleY = useSharedValue(1);
69
+ const rotate = useSharedValue('0deg');
70
+
71
+ // Extract transform values from style
72
+ const transforms = useMemo(() => {
73
+ const result = {
74
+ translateX: 0,
75
+ translateY: 0,
76
+ scale: 1,
77
+ scaleX: 1,
78
+ scaleY: 1,
79
+ rotate: '0deg',
80
+ };
81
+
82
+ if (style.transform) {
83
+ style.transform.forEach((t) => {
84
+ const [key, value] = Object.entries(t)[0];
85
+ if (key in result) {
86
+ (result as any)[key] = value;
87
+ }
88
+ });
89
+ }
90
+
91
+ return result;
92
+ }, [style.transform]);
93
+
94
+ // Animate function
95
+ const animate = (sharedValue: any, targetValue: any, isString = false) => {
96
+ 'worklet';
97
+ if (useSpring && !isString) {
98
+ const config = springConfig(springType as any);
99
+ return finalDelay > 0
100
+ ? withDelay(finalDelay, withSpring(targetValue, config))
101
+ : withSpring(targetValue, config);
102
+ } else {
103
+ const config = timingConfig(finalDuration, finalEasing);
104
+ const timingOptions = {
105
+ duration: config.duration,
106
+ easing: Easing.bezier(config.easing[0], config.easing[1], config.easing[2], config.easing[3]),
107
+ };
108
+ return finalDelay > 0
109
+ ? withDelay(finalDelay, withTiming(targetValue, timingOptions))
110
+ : withTiming(targetValue, timingOptions);
111
+ }
112
+ };
113
+
114
+ // Update shared values when style changes
115
+ useEffect(() => {
116
+ if (style.opacity !== undefined) {
117
+ opacity.value = animate(opacity, style.opacity);
118
+ }
119
+ if (style.backgroundColor !== undefined) {
120
+ backgroundColor.value = animate(backgroundColor, style.backgroundColor, true);
121
+ }
122
+ if (style.borderColor !== undefined) {
123
+ borderColor.value = animate(borderColor, style.borderColor, true);
124
+ }
125
+ if (style.borderWidth !== undefined) {
126
+ borderWidth.value = animate(borderWidth, style.borderWidth);
127
+ }
128
+ if (style.borderRadius !== undefined) {
129
+ borderRadius.value = animate(borderRadius, style.borderRadius);
130
+ }
131
+
132
+ // Update transform values
133
+ translateX.value = animate(translateX, transforms.translateX);
134
+ translateY.value = animate(translateY, transforms.translateY);
135
+ scale.value = animate(scale, transforms.scale);
136
+ scaleX.value = animate(scaleX, transforms.scaleX);
137
+ scaleY.value = animate(scaleY, transforms.scaleY);
138
+ rotate.value = animate(rotate, transforms.rotate, true);
139
+ }, [
140
+ style.opacity,
141
+ style.backgroundColor,
142
+ style.borderColor,
143
+ style.borderWidth,
144
+ style.borderRadius,
145
+ transforms,
146
+ ]);
147
+
148
+ // Create animated style
149
+ const animatedStyle = useReanimatedStyle(() => {
150
+ return {
151
+ opacity: opacity.value,
152
+ backgroundColor: backgroundColor.value,
153
+ borderColor: borderColor.value,
154
+ borderWidth: borderWidth.value,
155
+ borderRadius: borderRadius.value,
156
+ transform: [
157
+ { translateX: translateX.value },
158
+ { translateY: translateY.value },
159
+ { scale: scale.value },
160
+ { scaleX: scaleX.value },
161
+ { scaleY: scaleY.value },
162
+ { rotate: rotate.value },
163
+ ],
164
+ };
165
+ });
166
+
167
+ return animatedStyle as AnimatableStyle;
168
+ }