>(HudContext);
63 | return context;
64 | };
65 |
66 | export type HudProviderProps = React.PropsWithChildren & {
67 | IconComponent: React.ComponentType
;
68 | containerStyles?: ViewStyle;
69 | useNativeDriver?: boolean;
70 | } & ViewProps &
71 | HudOptions;
72 |
73 | export const HudProvider =
({
74 | IconComponent,
75 | backgroundColor = 'rgba(0,0,0,0.8)',
76 | borderRadius = 5,
77 | children,
78 | color = 'white',
79 | containerStyles,
80 | fadeDuration = 700,
81 | height = 80,
82 | rotate = false,
83 | rotateDuration = 800,
84 | style,
85 | useNativeDriver = true,
86 | width = 80,
87 | ...viewProps
88 | }: HudProviderProps
) => {
89 | const fadeAnimationRef = useRef(new Animated.Value(0));
90 | const rotationAnimationRef = useRef(new Animated.Value(0));
91 | const [iconProps, setIconProps] = useState
();
92 |
93 | const defaultOptions = useMemo>(
94 | () => ({
95 | backgroundColor,
96 | borderRadius,
97 | color,
98 | fadeDuration,
99 | height,
100 | rotate,
101 | rotateDuration,
102 | width,
103 | }),
104 | [
105 | backgroundColor,
106 | borderRadius,
107 | color,
108 | fadeDuration,
109 | height,
110 | rotate,
111 | rotateDuration,
112 | width,
113 | ]
114 | );
115 |
116 | const [options, setOptions] = useState(defaultOptions);
117 |
118 | const fadeIn = useCallback(() => {
119 | Animated.timing(fadeAnimationRef.current, {
120 | duration: options.fadeDuration,
121 | toValue: 1,
122 | useNativeDriver,
123 | }).start();
124 | }, [options.fadeDuration, useNativeDriver]);
125 |
126 | const fadeOut = useCallback(() => {
127 | Animated.timing(fadeAnimationRef.current, {
128 | duration: options.fadeDuration,
129 | toValue: 0,
130 | useNativeDriver,
131 | }).start(({ finished }) => {
132 | if (finished) {
133 | rotationAnimationRef.current.stopAnimation();
134 | setIconProps(undefined);
135 | }
136 | });
137 | }, [options.fadeDuration, useNativeDriver]);
138 |
139 | const startRotating = useCallback(() => {
140 | rotationAnimationRef.current.setValue(0);
141 |
142 | Animated.loop(
143 | Animated.timing(rotationAnimationRef.current, {
144 | duration: options.rotateDuration,
145 | easing: Easing.linear,
146 | toValue: 1,
147 | useNativeDriver,
148 | })
149 | ).start();
150 | }, [options.rotateDuration, useNativeDriver]);
151 |
152 | const show = useCallback['show']>(
153 | (nextIconProps, incomingOptions) => {
154 | fadeIn();
155 |
156 | const filteredIncomingOptions = incomingOptions
157 | ? Object.fromEntries(
158 | Object.entries(incomingOptions).filter(
159 | ([, value]) => value !== undefined
160 | )
161 | )
162 | : undefined;
163 |
164 | const nextOptions = { ...defaultOptions, ...filteredIncomingOptions };
165 | setOptions(nextOptions);
166 |
167 | if (nextOptions.rotate) {
168 | startRotating();
169 | }
170 |
171 | setIconProps({
172 | color: defaultOptions.color,
173 | size: Math.min(nextOptions.width, nextOptions.height) * 0.5,
174 | ...nextIconProps,
175 | });
176 | },
177 | [defaultOptions, fadeIn, startRotating]
178 | );
179 |
180 | const hide = useCallback(() => {
181 | fadeOut();
182 | }, [fadeOut]);
183 |
184 | const iconContainerStyles = useMemo(
185 | () => [
186 | styles.iconContainer,
187 | {
188 | backgroundColor: options.backgroundColor,
189 | borderRadius: options.borderRadius,
190 | height: options.height,
191 | opacity: fadeAnimationRef.current,
192 | width: options.width,
193 | },
194 | containerStyles,
195 | ],
196 | [
197 | containerStyles,
198 | options.backgroundColor,
199 | options.borderRadius,
200 | options.height,
201 | options.width,
202 | ]
203 | );
204 |
205 | const iconWrapperStyles = useMemo(
206 | () =>
207 | options?.rotate
208 | ? {
209 | transform: [
210 | {
211 | rotate: rotationAnimationRef.current.interpolate({
212 | inputRange: [0, 1],
213 | outputRange: ['0deg', '360deg'],
214 | }),
215 | },
216 | ],
217 | }
218 | : {},
219 | [options?.rotate]
220 | );
221 |
222 | return (
223 |
224 |
229 | {children}
230 | {iconProps ? (
231 |
236 |
240 |
244 |
245 |
246 |
247 |
248 | ) : null}
249 |
250 |
251 | );
252 | };
253 |
--------------------------------------------------------------------------------
/src/__snapshots__/HudProvider.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`HudProvider renders correctly 1`] = `
4 |
12 |
15 |
16 | `;
17 |
18 | exports[`HudProvider renders correctly when toggling hud 1`] = `
19 |
27 |
30 |
65 |
100 |
101 |
117 |
132 |
137 |
138 |
139 |
140 | `;
141 |
142 | exports[`HudProvider renders correctly when toggling hud 2`] = `
143 |
151 |
154 |
189 |
224 |
225 |
226 | `;
227 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export { HudProvider, useHudContext, HudProviderProps } from './HudProvider';
2 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig",
3 | "exclude": ["dist", "**/*.test.*"]
4 | }
5 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6",
4 | "module": "es6",
5 | "declaration": true,
6 | "strict": true,
7 | "jsx": "react-native",
8 | "moduleResolution": "node",
9 | "forceConsistentCasingInFileNames": true,
10 | "allowSyntheticDefaultImports": true,
11 | "esModuleInterop": true,
12 | "noImplicitAny": true,
13 | "lib": ["es6"],
14 | "skipLibCheck": true,
15 | "resolveJsonModule": true,
16 | "outDir": "dist",
17 | "rootDir": "src",
18 | "types": ["react-native", "jest", "node", "@testing-library/jest-native"]
19 | },
20 | "include": ["src"],
21 | "exclude": ["dist"]
22 | }
23 |
--------------------------------------------------------------------------------