@idealyst/animate 1.2.37 → 1.2.39

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idealyst/animate",
3
- "version": "1.2.37",
3
+ "version": "1.2.39",
4
4
  "description": "Cross-platform animation hooks for React and React Native",
5
5
  "readme": "README.md",
6
6
  "main": "src/index.ts",
@@ -30,7 +30,7 @@
30
30
  "publish:npm": "npm publish"
31
31
  },
32
32
  "peerDependencies": {
33
- "@idealyst/theme": "^1.2.37",
33
+ "@idealyst/theme": "^1.2.39",
34
34
  "react": ">=16.8.0",
35
35
  "react-native": ">=0.60.0",
36
36
  "react-native-reanimated": ">=3.0.0",
@@ -48,7 +48,7 @@
48
48
  }
49
49
  },
50
50
  "devDependencies": {
51
- "@idealyst/theme": "^1.2.37",
51
+ "@idealyst/theme": "^1.2.39",
52
52
  "@types/react": "^19.1.0",
53
53
  "react": "^19.1.0",
54
54
  "react-native": "^0.80.1",
@@ -0,0 +1,417 @@
1
+ /**
2
+ * Animate Package Examples
3
+ *
4
+ * Demonstrates the @idealyst/animate hooks:
5
+ * - useAnimatedStyle: State-driven style animations
6
+ * - useAnimatedValue: Numeric value animations with interpolation
7
+ * - usePresence: Mount/unmount animations
8
+ * - useGradientBorder: Animated gradient borders
9
+ */
10
+
11
+ import React, { useState } from 'react';
12
+ import { Screen, View, Text, Button, Card, Slider, Divider, Switch } from '@idealyst/components';
13
+ import { useAnimatedStyle, useAnimatedValue, usePresence, useGradientBorder } from '../index';
14
+
15
+ // =============================================================================
16
+ // useAnimatedStyle Demo
17
+ // =============================================================================
18
+
19
+ const AnimatedStyleDemo: React.FC = () => {
20
+ const [isExpanded, setIsExpanded] = useState(false);
21
+ const [isHighlighted, setIsHighlighted] = useState(false);
22
+
23
+ const boxStyle = useAnimatedStyle(
24
+ {
25
+ opacity: isExpanded ? 1 : 0.6,
26
+ backgroundColor: isHighlighted ? '#6366f1' : '#e5e7eb',
27
+ borderRadius: isExpanded ? 16 : 8,
28
+ },
29
+ { duration: 'normal', easing: 'easeOut' }
30
+ );
31
+
32
+ return (
33
+ <Card padding="md" gap="md">
34
+ <Text typography="h4" weight="semibold">useAnimatedStyle</Text>
35
+ <Text color="secondary">
36
+ Animate styles based on state changes. Supports opacity, colors, border radius, and more.
37
+ </Text>
38
+
39
+ <View
40
+ style={{
41
+ height: 120,
42
+ justifyContent: 'center',
43
+ alignItems: 'center',
44
+ ...boxStyle,
45
+ }}
46
+ >
47
+ <Text weight="semibold" style={{ color: isHighlighted ? '#fff' : '#374151' }}>
48
+ {isExpanded ? 'Expanded!' : 'Normal'}
49
+ </Text>
50
+ </View>
51
+
52
+ <View direction="row" gap="sm">
53
+ <Button
54
+ type={isExpanded ? 'contained' : 'outlined'}
55
+ onPress={() => setIsExpanded(!isExpanded)}
56
+ size="sm"
57
+ >
58
+ {isExpanded ? 'Collapse' : 'Expand'}
59
+ </Button>
60
+ <Button
61
+ type={isHighlighted ? 'contained' : 'outlined'}
62
+ intent={isHighlighted ? 'primary' : undefined}
63
+ onPress={() => setIsHighlighted(!isHighlighted)}
64
+ size="sm"
65
+ >
66
+ {isHighlighted ? 'Remove Color' : 'Add Color'}
67
+ </Button>
68
+ </View>
69
+
70
+ <View background="secondary" padding="sm" radius="sm">
71
+ <Text typography="caption" style={{ fontFamily: 'monospace' }}>
72
+ {`const style = useAnimatedStyle({
73
+ opacity: isExpanded ? 1 : 0.6,
74
+ backgroundColor: isHighlighted ? '#6366f1' : '#e5e7eb',
75
+ borderRadius: isExpanded ? 16 : 8,
76
+ }, { duration: 'normal', easing: 'easeOut' });`}
77
+ </Text>
78
+ </View>
79
+ </Card>
80
+ );
81
+ };
82
+
83
+ // =============================================================================
84
+ // useAnimatedValue Demo
85
+ // =============================================================================
86
+
87
+ const AnimatedValueDemo: React.FC = () => {
88
+ const progress = useAnimatedValue(0);
89
+ const [targetValue, setTargetValue] = useState(0);
90
+
91
+ const handleSetProgress = (value: number) => {
92
+ setTargetValue(value);
93
+ progress.set(value, { duration: 'normal', easing: 'easeInOut' });
94
+ };
95
+
96
+ const color = progress.interpolate({
97
+ inputRange: [0, 0.5, 1],
98
+ outputRange: ['#ef4444', '#eab308', '#22c55e'],
99
+ });
100
+
101
+ return (
102
+ <Card padding="md" gap="md">
103
+ <Text typography="h4" weight="semibold">useAnimatedValue</Text>
104
+ <Text color="secondary">
105
+ Animate numeric values with interpolation support. Great for progress bars and counters.
106
+ </Text>
107
+
108
+ <View gap="sm">
109
+ <View
110
+ style={{
111
+ height: 24,
112
+ backgroundColor: '#e5e7eb',
113
+ borderRadius: 12,
114
+ overflow: 'hidden',
115
+ }}
116
+ >
117
+ <View
118
+ style={{
119
+ width: `${progress.value * 100}%`,
120
+ height: '100%',
121
+ borderRadius: 12,
122
+ backgroundColor: color,
123
+ transition: 'width 300ms ease-out, background-color 300ms ease-out',
124
+ }}
125
+ />
126
+ </View>
127
+ <Text typography="caption" color="secondary" style={{ textAlign: 'center' }}>
128
+ {Math.round(progress.value * 100)}%
129
+ </Text>
130
+ </View>
131
+
132
+ <View gap="xs">
133
+ <Text typography="caption" weight="semibold">Set Progress:</Text>
134
+ <Slider
135
+ value={targetValue}
136
+ onValueChange={handleSetProgress}
137
+ minimumValue={0}
138
+ maximumValue={1}
139
+ step={0.1}
140
+ />
141
+ </View>
142
+
143
+ <View direction="row" gap="sm" style={{ flexWrap: 'wrap' }}>
144
+ <Button type="outlined" size="sm" onPress={() => handleSetProgress(0)}>0%</Button>
145
+ <Button type="outlined" size="sm" onPress={() => handleSetProgress(0.25)}>25%</Button>
146
+ <Button type="outlined" size="sm" onPress={() => handleSetProgress(0.5)}>50%</Button>
147
+ <Button type="outlined" size="sm" onPress={() => handleSetProgress(0.75)}>75%</Button>
148
+ <Button type="outlined" size="sm" onPress={() => handleSetProgress(1)}>100%</Button>
149
+ </View>
150
+
151
+ <View background="secondary" padding="sm" radius="sm">
152
+ <Text typography="caption" style={{ fontFamily: 'monospace' }}>
153
+ {`const progress = useAnimatedValue(0);
154
+ progress.set(0.75, { duration: 'normal' });
155
+ progress.interpolate({
156
+ inputRange: [0, 0.5, 1],
157
+ outputRange: ['red', 'yellow', 'green'],
158
+ });`}
159
+ </Text>
160
+ </View>
161
+ </Card>
162
+ );
163
+ };
164
+
165
+ // =============================================================================
166
+ // usePresence Demo
167
+ // =============================================================================
168
+
169
+ const PresenceDemo: React.FC = () => {
170
+ const [showModal, setShowModal] = useState(false);
171
+ const [showNotification, setShowNotification] = useState(false);
172
+
173
+ const { isPresent: isModalPresent, style: modalStyle } = usePresence(showModal, {
174
+ enter: { opacity: 1 },
175
+ exit: { opacity: 0 },
176
+ duration: 'normal',
177
+ easing: 'easeOut',
178
+ });
179
+
180
+ const { isPresent: isNotificationPresent, style: notificationStyle } = usePresence(showNotification, {
181
+ enter: { opacity: 1 },
182
+ exit: { opacity: 0 },
183
+ duration: 'fast',
184
+ easing: 'easeInOut',
185
+ });
186
+
187
+ return (
188
+ <Card padding="md" gap="md">
189
+ <Text typography="h4" weight="semibold">usePresence</Text>
190
+ <Text color="secondary">
191
+ Animate elements when they mount and unmount. The element stays in the DOM until the exit animation completes.
192
+ </Text>
193
+
194
+ <View direction="row" gap="sm">
195
+ <Button
196
+ type={showModal ? 'contained' : 'outlined'}
197
+ onPress={() => setShowModal(!showModal)}
198
+ size="sm"
199
+ >
200
+ {showModal ? 'Hide Modal' : 'Show Modal'}
201
+ </Button>
202
+ <Button
203
+ type={showNotification ? 'contained' : 'outlined'}
204
+ onPress={() => setShowNotification(!showNotification)}
205
+ size="sm"
206
+ >
207
+ {showNotification ? 'Hide Toast' : 'Show Toast'}
208
+ </Button>
209
+ </View>
210
+
211
+ <View style={{ minHeight: 160, position: 'relative' }}>
212
+ {isModalPresent && (
213
+ <View
214
+ style={{
215
+ position: 'absolute',
216
+ top: 0,
217
+ left: 0,
218
+ right: 0,
219
+ backgroundColor: '#fff',
220
+ padding: 20,
221
+ borderRadius: 12,
222
+ borderWidth: 1,
223
+ borderColor: '#e5e7eb',
224
+ shadowColor: '#000',
225
+ shadowOffset: { width: 0, height: 4 },
226
+ shadowOpacity: 0.1,
227
+ shadowRadius: 12,
228
+ elevation: 4,
229
+ ...modalStyle,
230
+ }}
231
+ >
232
+ <Text weight="semibold">Modal Content</Text>
233
+ <Text color="secondary" typography="caption">
234
+ This modal animates in with fade, and animates out smoothly.
235
+ </Text>
236
+ </View>
237
+ )}
238
+
239
+ {isNotificationPresent && (
240
+ <View
241
+ style={{
242
+ position: 'absolute',
243
+ top: 0,
244
+ right: 0,
245
+ backgroundColor: '#22c55e',
246
+ padding: 12,
247
+ borderRadius: 8,
248
+ ...notificationStyle,
249
+ }}
250
+ >
251
+ <Text style={{ color: '#fff' }} weight="semibold">Success!</Text>
252
+ </View>
253
+ )}
254
+ </View>
255
+
256
+ <View background="secondary" padding="sm" radius="sm">
257
+ <Text typography="caption" style={{ fontFamily: 'monospace' }}>
258
+ {`const { isPresent, style } = usePresence(isOpen, {
259
+ enter: { opacity: 1 },
260
+ exit: { opacity: 0 },
261
+ duration: 'normal',
262
+ });
263
+
264
+ {isPresent && <View style={{...baseStyle, ...style}}>...</View>}`}
265
+ </Text>
266
+ </View>
267
+ </Card>
268
+ );
269
+ };
270
+
271
+ // =============================================================================
272
+ // useGradientBorder Demo
273
+ // =============================================================================
274
+
275
+ const GradientBorderDemo: React.FC = () => {
276
+ const [animation, setAnimation] = useState<'spin' | 'pulse' | 'wave'>('spin');
277
+ const [isActive, setIsActive] = useState(true);
278
+
279
+ const spinGradient = useGradientBorder({
280
+ colors: ['#6366f1', '#8b5cf6', '#ec4899', '#6366f1'],
281
+ borderWidth: 3,
282
+ borderRadius: 16,
283
+ animation: 'spin',
284
+ duration: 'slow',
285
+ active: isActive && animation === 'spin',
286
+ });
287
+
288
+ const pulseGradient = useGradientBorder({
289
+ colors: ['#22c55e', '#10b981', '#14b8a6', '#22c55e'],
290
+ borderWidth: 3,
291
+ borderRadius: 16,
292
+ animation: 'pulse',
293
+ duration: 'normal',
294
+ active: isActive && animation === 'pulse',
295
+ });
296
+
297
+ const waveGradient = useGradientBorder({
298
+ colors: ['#f59e0b', '#ef4444', '#ec4899', '#f59e0b'],
299
+ borderWidth: 3,
300
+ borderRadius: 16,
301
+ animation: 'wave',
302
+ duration: 'normal',
303
+ active: isActive && animation === 'wave',
304
+ });
305
+
306
+ const currentGradient =
307
+ animation === 'spin' ? spinGradient : animation === 'pulse' ? pulseGradient : waveGradient;
308
+
309
+ return (
310
+ <Card padding="md" gap="md">
311
+ <Text typography="h4" weight="semibold">useGradientBorder</Text>
312
+ <Text color="secondary">
313
+ Create animated gradient borders with spin, pulse, or wave effects.
314
+ </Text>
315
+
316
+ <View style={{ alignItems: 'center' }}>
317
+ <View style={{ padding: 3, ...currentGradient.containerStyle }}>
318
+ <View
319
+ style={{
320
+ padding: 24,
321
+ backgroundColor: '#fff',
322
+ alignItems: 'center',
323
+ justifyContent: 'center',
324
+ ...currentGradient.contentStyle,
325
+ }}
326
+ >
327
+ <Text weight="semibold">Gradient Border</Text>
328
+ <Text typography="caption" color="secondary">Animation: {animation}</Text>
329
+ </View>
330
+ </View>
331
+ </View>
332
+
333
+ <View direction="row" gap="sm" style={{ justifyContent: 'center' }}>
334
+ <Button
335
+ type={animation === 'spin' ? 'contained' : 'outlined'}
336
+ onPress={() => setAnimation('spin')}
337
+ size="sm"
338
+ >
339
+ Spin
340
+ </Button>
341
+ <Button
342
+ type={animation === 'pulse' ? 'contained' : 'outlined'}
343
+ onPress={() => setAnimation('pulse')}
344
+ size="sm"
345
+ >
346
+ Pulse
347
+ </Button>
348
+ <Button
349
+ type={animation === 'wave' ? 'contained' : 'outlined'}
350
+ onPress={() => setAnimation('wave')}
351
+ size="sm"
352
+ >
353
+ Wave
354
+ </Button>
355
+ </View>
356
+
357
+ <View direction="row" gap="sm" style={{ alignItems: 'center', justifyContent: 'center' }}>
358
+ <Text typography="caption">Animation Active:</Text>
359
+ <Switch value={isActive} onValueChange={setIsActive} />
360
+ </View>
361
+
362
+ <View background="secondary" padding="sm" radius="sm">
363
+ <Text typography="caption" style={{ fontFamily: 'monospace' }}>
364
+ {`const { containerStyle, contentStyle } = useGradientBorder({
365
+ colors: ['#6366f1', '#8b5cf6', '#ec4899'],
366
+ borderWidth: 3,
367
+ animation: 'spin',
368
+ active: true,
369
+ });`}
370
+ </Text>
371
+ </View>
372
+ </Card>
373
+ );
374
+ };
375
+
376
+ // =============================================================================
377
+ // Main Screen
378
+ // =============================================================================
379
+
380
+ export const AnimateExamples: React.FC = () => {
381
+ return (
382
+ <Screen background="primary" padding="lg" scrollable>
383
+ <View gap="lg">
384
+ <Text typography="h2" weight="bold" align="center">
385
+ Animate Examples
386
+ </Text>
387
+ <Text color="secondary" align="center">
388
+ Cross-platform animation hooks for React and React Native
389
+ </Text>
390
+
391
+ <Divider />
392
+
393
+ <AnimatedStyleDemo />
394
+ <AnimatedValueDemo />
395
+ <PresenceDemo />
396
+ <GradientBorderDemo />
397
+
398
+ <Card type="elevated" padding="md" gap="sm">
399
+ <Text weight="semibold">About @idealyst/animate</Text>
400
+ <Text color="secondary">
401
+ Uses CSS transitions on web and React Native Reanimated on native for optimal performance.
402
+ All animations run on the UI thread when possible.
403
+ </Text>
404
+ <View gap="xs">
405
+ <Text typography="caption" weight="semibold">Available Hooks:</Text>
406
+ <Text typography="caption" color="secondary">• useAnimatedStyle - State-driven style animations</Text>
407
+ <Text typography="caption" color="secondary">• useAnimatedValue - Numeric value animations</Text>
408
+ <Text typography="caption" color="secondary">• usePresence - Mount/unmount animations</Text>
409
+ <Text typography="caption" color="secondary">• useGradientBorder - Animated gradient borders</Text>
410
+ </View>
411
+ </Card>
412
+ </View>
413
+ </Screen>
414
+ );
415
+ };
416
+
417
+ export default AnimateExamples;
@@ -0,0 +1 @@
1
+ export { AnimateExamples } from './AnimateExamples';