UNPKG

5.84 kBJavaScriptView Raw
1function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
3import * as React from 'react';
4import { Animated, Easing, Platform, StyleSheet, View } from 'react-native';
5import { withTheme } from '../core/theming';
6const DURATION = 2400;
7/**
8 * Activity indicator is used to present progress of some activity in the app.
9 * It can be used as a drop-in for the ActivityIndicator shipped with React Native.
10 *
11 * <div class="screenshots">
12 * <img src="screenshots/activity-indicator.gif" style="width: 100px;" />
13 * </div>
14 *
15 * ## Usage
16 * ```js
17 * import * as React from 'react';
18 * import { ActivityIndicator, Colors } from 'react-native-paper';
19 *
20 * const MyComponent = () => (
21 * <ActivityIndicator animating={true} color={Colors.red800} />
22 * );
23 *
24 * export default MyComponent;
25 * ```
26 */
27
28const ActivityIndicator = _ref => {
29 let {
30 animating = true,
31 color: indicatorColor,
32 hidesWhenStopped = true,
33 size: indicatorSize = 'small',
34 style,
35 theme,
36 ...rest
37 } = _ref;
38 const {
39 current: timer
40 } = React.useRef(new Animated.Value(0));
41 const {
42 current: fade
43 } = React.useRef(new Animated.Value(!animating && hidesWhenStopped ? 0 : 1));
44 const rotation = React.useRef(undefined);
45 const {
46 animation: {
47 scale
48 }
49 } = theme;
50 const startRotation = React.useCallback(() => {
51 // Show indicator
52 Animated.timing(fade, {
53 duration: 200 * scale,
54 toValue: 1,
55 isInteraction: false,
56 useNativeDriver: true
57 }).start(); // Circular animation in loop
58
59 if (rotation.current) {
60 timer.setValue(0); // $FlowFixMe
61
62 Animated.loop(rotation.current).start();
63 }
64 }, [scale, fade, timer]);
65
66 const stopRotation = () => {
67 if (rotation.current) {
68 rotation.current.stop();
69 }
70 };
71
72 React.useEffect(() => {
73 if (rotation.current === undefined) {
74 // Circular animation in loop
75 rotation.current = Animated.timing(timer, {
76 duration: DURATION,
77 easing: Easing.linear,
78 // Animated.loop does not work if useNativeDriver is true on web
79 useNativeDriver: Platform.OS !== 'web',
80 toValue: 1,
81 isInteraction: false
82 });
83 }
84
85 if (animating) {
86 startRotation();
87 } else if (hidesWhenStopped) {
88 // Hide indicator first and then stop rotation
89 Animated.timing(fade, {
90 duration: 200 * scale,
91 toValue: 0,
92 useNativeDriver: true,
93 isInteraction: false
94 }).start(stopRotation);
95 } else {
96 stopRotation();
97 }
98 }, [animating, fade, hidesWhenStopped, startRotation, scale, timer]);
99 const color = indicatorColor || theme.colors.primary;
100 const size = typeof indicatorSize === 'string' ? indicatorSize === 'small' ? 24 : 48 : indicatorSize ? indicatorSize : 24;
101 const frames = 60 * DURATION / 1000;
102 const easing = Easing.bezier(0.4, 0.0, 0.7, 1.0);
103 const containerStyle = {
104 width: size,
105 height: size / 2,
106 overflow: 'hidden'
107 };
108 return /*#__PURE__*/React.createElement(View, _extends({
109 style: [styles.container, style]
110 }, rest, {
111 accessible: true,
112 accessibilityRole: "progressbar",
113 accessibilityState: {
114 busy: animating
115 }
116 }), /*#__PURE__*/React.createElement(Animated.View, {
117 style: [{
118 width: size,
119 height: size,
120 opacity: fade
121 }],
122 collapsable: false
123 }, [0, 1].map(index => {
124 // Thanks to https://github.com/n4kz/react-native-indicators for the great work
125 const inputRange = Array.from(new Array(frames), (_, frameIndex) => frameIndex / (frames - 1));
126 const outputRange = Array.from(new Array(frames), (_, frameIndex) => {
127 let progress = 2 * frameIndex / (frames - 1);
128 const rotation = index ? +(360 - 15) : -(180 - 15);
129
130 if (progress > 1.0) {
131 progress = 2.0 - progress;
132 }
133
134 const direction = index ? -1 : +1;
135 return `${direction * (180 - 30) * easing(progress) + rotation}deg`;
136 });
137 const layerStyle = {
138 width: size,
139 height: size,
140 transform: [{
141 rotate: timer.interpolate({
142 inputRange: [0, 1],
143 outputRange: [`${0 + 30 + 15}deg`, `${2 * 360 + 30 + 15}deg`]
144 })
145 }]
146 };
147 const viewportStyle = {
148 width: size,
149 height: size,
150 transform: [{
151 translateY: index ? -size / 2 : 0
152 }, {
153 rotate: timer.interpolate({
154 inputRange,
155 outputRange
156 })
157 }]
158 };
159 const offsetStyle = index ? {
160 top: size / 2
161 } : null;
162 const lineStyle = {
163 width: size,
164 height: size,
165 borderColor: color,
166 borderWidth: size / 10,
167 borderRadius: size / 2
168 };
169 return /*#__PURE__*/React.createElement(Animated.View, {
170 key: index,
171 style: [styles.layer]
172 }, /*#__PURE__*/React.createElement(Animated.View, {
173 style: layerStyle
174 }, /*#__PURE__*/React.createElement(Animated.View, {
175 style: [containerStyle, offsetStyle],
176 collapsable: false
177 }, /*#__PURE__*/React.createElement(Animated.View, {
178 style: viewportStyle
179 }, /*#__PURE__*/React.createElement(Animated.View, {
180 style: containerStyle,
181 collapsable: false
182 }, /*#__PURE__*/React.createElement(Animated.View, {
183 style: lineStyle
184 }))))));
185 })));
186};
187
188const styles = StyleSheet.create({
189 container: {
190 justifyContent: 'center',
191 alignItems: 'center'
192 },
193 layer: { ...StyleSheet.absoluteFillObject,
194 justifyContent: 'center',
195 alignItems: 'center'
196 }
197});
198export default withTheme(ActivityIndicator);
199//# sourceMappingURL=ActivityIndicator.js.map
\No newline at end of file