react-native-glitter 1.0.3 → 1.0.5

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/README.md CHANGED
@@ -167,6 +167,9 @@ function ControlledGlitter() {
167
167
  | `iterations` | `number` | `-1` | Number of animation cycles (-1 for infinite) |
168
168
  | `onAnimationStart` | `() => void` | - | Callback when animation starts |
169
169
  | `onAnimationComplete` | `() => void` | - | Callback when all iterations complete |
170
+ | `testID` | `string` | - | Test ID for e2e testing |
171
+ | `accessibilityLabel` | `string` | - | Accessibility label for screen readers |
172
+ | `accessible` | `boolean` | `true` | Whether the component is accessible |
170
173
 
171
174
  ## Examples
172
175
 
@@ -261,6 +264,44 @@ function ControlledGlitter() {
261
264
  </Glitter>
262
265
  ```
263
266
 
267
+ ## Ref API
268
+
269
+ You can control the animation programmatically using a ref:
270
+
271
+ ```tsx
272
+ import { useRef } from 'react';
273
+ import { Glitter, type GlitterRef } from 'react-native-glitter';
274
+
275
+ function MyComponent() {
276
+ const glitterRef = useRef<GlitterRef>(null);
277
+
278
+ const handleStart = () => glitterRef.current?.start();
279
+ const handleStop = () => glitterRef.current?.stop();
280
+ const handleRestart = () => glitterRef.current?.restart();
281
+ const checkStatus = () => console.log(glitterRef.current?.isAnimating());
282
+
283
+ return (
284
+ <>
285
+ <Glitter ref={glitterRef} active={false}>
286
+ <View style={styles.box} />
287
+ </Glitter>
288
+ <Button title="Start" onPress={handleStart} />
289
+ <Button title="Stop" onPress={handleStop} />
290
+ <Button title="Restart" onPress={handleRestart} />
291
+ </>
292
+ );
293
+ }
294
+ ```
295
+
296
+ ### Ref Methods
297
+
298
+ | Method | Return | Description |
299
+ |--------|--------|-------------|
300
+ | `start()` | `void` | Start the shimmer animation |
301
+ | `stop()` | `void` | Stop the shimmer animation |
302
+ | `restart()` | `void` | Restart the animation from the beginning |
303
+ | `isAnimating()` | `boolean` | Check if animation is currently running |
304
+
264
305
  ## TypeScript
265
306
 
266
307
  This library is written in TypeScript and includes type definitions:
@@ -269,6 +310,7 @@ This library is written in TypeScript and includes type definitions:
269
310
  import {
270
311
  Glitter,
271
312
  type GlitterProps,
313
+ type GlitterRef,
272
314
  type GlitterMode,
273
315
  type GlitterPosition,
274
316
  type GlitterDirection,
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useRef, useState, useCallback, useMemo, memo, } from 'react';
2
+ import { useEffect, useRef, useState, useCallback, useMemo, memo, useImperativeHandle, forwardRef, } from 'react';
3
3
  import { View, Animated, StyleSheet, Easing, } from 'react-native';
4
4
  function generateGlitterOpacities(count, peak = 1) {
5
5
  const opacities = [];
@@ -52,7 +52,7 @@ const HEIGHT_MULTIPLIER = 1.5;
52
52
  const NORMAL_FADE_RATIO = (HEIGHT_MULTIPLIER - 1) / HEIGHT_MULTIPLIER / 2;
53
53
  const ANIMATED_SEGMENTS = generateVerticalSegments(0.25);
54
54
  const NORMAL_SEGMENTS = generateVerticalSegments(NORMAL_FADE_RATIO);
55
- function GlitterComponent({ children, duration = 1500, delay = 400, color = 'rgba(255, 255, 255, 0.8)', angle = 20, shimmerWidth = 60, active = true, style, easing, mode = 'normal', position = 'center', direction = 'left-to-right', iterations = -1, onAnimationStart, onAnimationComplete, }) {
55
+ function GlitterComponent({ children, duration = 1500, delay = 400, color = 'rgba(255, 255, 255, 0.8)', angle = 20, shimmerWidth = 60, active = true, style, easing, mode = 'normal', position = 'center', direction = 'left-to-right', iterations = -1, onAnimationStart, onAnimationComplete, testID, accessibilityLabel, accessible = true, }, ref) {
56
56
  const animatedValue = useRef(new Animated.Value(0)).current;
57
57
  const [containerWidth, setContainerWidth] = useState(0);
58
58
  const [containerHeight, setContainerHeight] = useState(0);
@@ -68,6 +68,23 @@ function GlitterComponent({ children, duration = 1500, delay = 400, color = 'rgb
68
68
  currentIterationRef.current?.stop();
69
69
  currentIterationRef.current = null;
70
70
  }, []);
71
+ const restartAnimation = useCallback(() => {
72
+ stopAnimation();
73
+ animatedValue.setValue(0);
74
+ // Trigger re-render to start animation
75
+ setContainerWidth((prev) => prev);
76
+ }, [stopAnimation, animatedValue]);
77
+ useImperativeHandle(ref, () => ({
78
+ start: () => {
79
+ if (!isAnimatingRef.current && containerWidth > 0) {
80
+ // Force start by triggering the effect
81
+ setContainerWidth((prev) => prev);
82
+ }
83
+ },
84
+ stop: stopAnimation,
85
+ restart: restartAnimation,
86
+ isAnimating: () => isAnimatingRef.current,
87
+ }), [stopAnimation, restartAnimation, containerWidth]);
71
88
  const startAnimation = useCallback(() => {
72
89
  if (!active || containerWidth === 0)
73
90
  return;
@@ -198,7 +215,7 @@ function GlitterComponent({ children, duration = 1500, delay = 400, color = 'rgb
198
215
  : [],
199
216
  },
200
217
  ], [layerWidth, lineHeight, isAnimated, transformOriginOffset, scaleY]);
201
- return (_jsxs(View, { style: [styles.container, style], onLayout: onLayout, children: [children, active && containerWidth > 0 && containerHeight > 0 && (_jsx(Animated.View, { style: shimmerContainerStyle, pointerEvents: "none", children: _jsx(View, { style: rotationWrapperStyle, children: shimmerLayers.map((layer, layerIndex) => (_jsx(Animated.View, { style: getShimmerLineStyle(layer.position), children: segments.map((segment, vIndex) => (_jsx(View, { style: [
218
+ return (_jsxs(View, { style: [styles.container, style], onLayout: onLayout, testID: testID, accessibilityLabel: accessibilityLabel, accessible: accessible, children: [children, active && containerWidth > 0 && containerHeight > 0 && (_jsx(Animated.View, { style: shimmerContainerStyle, pointerEvents: "none", children: _jsx(View, { style: rotationWrapperStyle, children: shimmerLayers.map((layer, layerIndex) => (_jsx(Animated.View, { style: getShimmerLineStyle(layer.position), children: segments.map((segment, vIndex) => (_jsx(View, { style: [
202
219
  styles.segment,
203
220
  {
204
221
  height: lineHeight * segment.heightRatio,
@@ -231,5 +248,6 @@ const styles = StyleSheet.create({
231
248
  width: '100%',
232
249
  },
233
250
  });
234
- export const Glitter = memo(GlitterComponent);
251
+ const ForwardedGlitter = forwardRef(GlitterComponent);
252
+ export const Glitter = memo(ForwardedGlitter);
235
253
  export default Glitter;
@@ -1,4 +1,4 @@
1
- import { type ReactNode, type ReactElement } from 'react';
1
+ import { type ReactNode } from 'react';
2
2
  import { type StyleProp, type ViewStyle } from 'react-native';
3
3
  export type GlitterMode = 'normal' | 'expand' | 'shrink';
4
4
  export type GlitterPosition = 'top' | 'center' | 'bottom';
@@ -19,7 +19,23 @@ export interface GlitterProps {
19
19
  iterations?: number;
20
20
  onAnimationStart?: () => void;
21
21
  onAnimationComplete?: () => void;
22
+ /** Test ID for e2e testing */
23
+ testID?: string;
24
+ /** Accessibility label for screen readers */
25
+ accessibilityLabel?: string;
26
+ /** Whether the component is accessible (default: true) */
27
+ accessible?: boolean;
22
28
  }
23
- declare function GlitterComponent({ children, duration, delay, color, angle, shimmerWidth, active, style, easing, mode, position, direction, iterations, onAnimationStart, onAnimationComplete, }: GlitterProps): ReactElement;
24
- export declare const Glitter: import("react").MemoExoticComponent<typeof GlitterComponent>;
29
+ /** Ref methods exposed by Glitter component */
30
+ export interface GlitterRef {
31
+ /** Start the shimmer animation */
32
+ start: () => void;
33
+ /** Stop the shimmer animation */
34
+ stop: () => void;
35
+ /** Restart the shimmer animation from the beginning */
36
+ restart: () => void;
37
+ /** Check if animation is currently running */
38
+ isAnimating: () => boolean;
39
+ }
40
+ export declare const Glitter: import("react").NamedExoticComponent<GlitterProps & import("react").RefAttributes<GlitterRef>>;
25
41
  export default Glitter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-glitter",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "A beautiful shimmer/glitter effect component for React Native. Add a sparkling diagonal shine animation to any component!",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
package/src/index.tsx CHANGED
@@ -5,8 +5,11 @@ import {
5
5
  useCallback,
6
6
  useMemo,
7
7
  memo,
8
+ useImperativeHandle,
9
+ forwardRef,
8
10
  type ReactNode,
9
11
  type ReactElement,
12
+ type ForwardedRef,
10
13
  } from 'react';
11
14
  import {
12
15
  View,
@@ -40,6 +43,24 @@ export interface GlitterProps {
40
43
  iterations?: number;
41
44
  onAnimationStart?: () => void;
42
45
  onAnimationComplete?: () => void;
46
+ /** Test ID for e2e testing */
47
+ testID?: string;
48
+ /** Accessibility label for screen readers */
49
+ accessibilityLabel?: string;
50
+ /** Whether the component is accessible (default: true) */
51
+ accessible?: boolean;
52
+ }
53
+
54
+ /** Ref methods exposed by Glitter component */
55
+ export interface GlitterRef {
56
+ /** Start the shimmer animation */
57
+ start: () => void;
58
+ /** Stop the shimmer animation */
59
+ stop: () => void;
60
+ /** Restart the shimmer animation from the beginning */
61
+ restart: () => void;
62
+ /** Check if animation is currently running */
63
+ isAnimating: () => boolean;
43
64
  }
44
65
 
45
66
  function generateGlitterOpacities(count: number, peak: number = 1): number[] {
@@ -108,23 +129,29 @@ const NORMAL_FADE_RATIO = (HEIGHT_MULTIPLIER - 1) / HEIGHT_MULTIPLIER / 2;
108
129
  const ANIMATED_SEGMENTS = generateVerticalSegments(0.25);
109
130
  const NORMAL_SEGMENTS = generateVerticalSegments(NORMAL_FADE_RATIO);
110
131
 
111
- function GlitterComponent({
112
- children,
113
- duration = 1500,
114
- delay = 400,
115
- color = 'rgba(255, 255, 255, 0.8)',
116
- angle = 20,
117
- shimmerWidth = 60,
118
- active = true,
119
- style,
120
- easing,
121
- mode = 'normal',
122
- position = 'center',
123
- direction = 'left-to-right',
124
- iterations = -1,
125
- onAnimationStart,
126
- onAnimationComplete,
127
- }: GlitterProps): ReactElement {
132
+ function GlitterComponent(
133
+ {
134
+ children,
135
+ duration = 1500,
136
+ delay = 400,
137
+ color = 'rgba(255, 255, 255, 0.8)',
138
+ angle = 20,
139
+ shimmerWidth = 60,
140
+ active = true,
141
+ style,
142
+ easing,
143
+ mode = 'normal',
144
+ position = 'center',
145
+ direction = 'left-to-right',
146
+ iterations = -1,
147
+ onAnimationStart,
148
+ onAnimationComplete,
149
+ testID,
150
+ accessibilityLabel,
151
+ accessible = true,
152
+ }: GlitterProps,
153
+ ref: ForwardedRef<GlitterRef>
154
+ ): ReactElement {
128
155
  const animatedValue = useRef(new Animated.Value(0)).current;
129
156
  const [containerWidth, setContainerWidth] = useState(0);
130
157
  const [containerHeight, setContainerHeight] = useState(0);
@@ -145,6 +172,29 @@ function GlitterComponent({
145
172
  currentIterationRef.current = null;
146
173
  }, []);
147
174
 
175
+ const restartAnimation = useCallback(() => {
176
+ stopAnimation();
177
+ animatedValue.setValue(0);
178
+ // Trigger re-render to start animation
179
+ setContainerWidth((prev) => prev);
180
+ }, [stopAnimation, animatedValue]);
181
+
182
+ useImperativeHandle(
183
+ ref,
184
+ () => ({
185
+ start: () => {
186
+ if (!isAnimatingRef.current && containerWidth > 0) {
187
+ // Force start by triggering the effect
188
+ setContainerWidth((prev) => prev);
189
+ }
190
+ },
191
+ stop: stopAnimation,
192
+ restart: restartAnimation,
193
+ isAnimating: () => isAnimatingRef.current,
194
+ }),
195
+ [stopAnimation, restartAnimation, containerWidth]
196
+ );
197
+
148
198
  const startAnimation = useCallback(() => {
149
199
  if (!active || containerWidth === 0) return;
150
200
 
@@ -332,7 +382,13 @@ function GlitterComponent({
332
382
  );
333
383
 
334
384
  return (
335
- <View style={[styles.container, style]} onLayout={onLayout}>
385
+ <View
386
+ style={[styles.container, style]}
387
+ onLayout={onLayout}
388
+ testID={testID}
389
+ accessibilityLabel={accessibilityLabel}
390
+ accessible={accessible}
391
+ >
336
392
  {children}
337
393
  {active && containerWidth > 0 && containerHeight > 0 && (
338
394
  <Animated.View style={shimmerContainerStyle} pointerEvents="none">
@@ -389,6 +445,7 @@ const styles = StyleSheet.create({
389
445
  },
390
446
  });
391
447
 
392
- export const Glitter = memo(GlitterComponent);
448
+ const ForwardedGlitter = forwardRef(GlitterComponent);
449
+ export const Glitter = memo(ForwardedGlitter);
393
450
 
394
451
  export default Glitter;