@telus-uds/components-base 3.3.0 → 3.5.0
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/CHANGELOG.md +28 -2
- package/lib/cjs/ActivityIndicator/Dots.js +165 -0
- package/lib/cjs/ActivityIndicator/Dots.native.js +221 -0
- package/lib/cjs/ActivityIndicator/Spinner.js +57 -50
- package/lib/cjs/ActivityIndicator/Spinner.native.js +90 -108
- package/lib/cjs/ActivityIndicator/index.js +12 -1
- package/lib/cjs/ActivityIndicator/shared.js +53 -6
- package/lib/cjs/Button/ButtonBase.js +1 -1
- package/lib/cjs/Button/ButtonLink.js +1 -0
- package/lib/cjs/Carousel/Carousel.js +18 -7
- package/lib/cjs/ExpandCollapse/ExpandCollapse.js +3 -1
- package/lib/cjs/ExpandCollapseMini/ExpandCollapseMini.js +10 -1
- package/lib/cjs/FileUpload/FileUpload.js +31 -2
- package/lib/cjs/Link/Link.js +8 -1
- package/lib/cjs/Link/LinkBase.js +2 -0
- package/lib/cjs/MultiSelectFilter/MultiSelectFilter.js +3 -2
- package/lib/cjs/utils/containUniqueFields.js +5 -5
- package/lib/cjs/utils/useUniqueId.js +2 -6
- package/lib/esm/ActivityIndicator/Dots.js +158 -0
- package/lib/esm/ActivityIndicator/Dots.native.js +212 -0
- package/lib/esm/ActivityIndicator/Spinner.js +58 -51
- package/lib/esm/ActivityIndicator/Spinner.native.js +90 -110
- package/lib/esm/ActivityIndicator/index.js +12 -1
- package/lib/esm/ActivityIndicator/shared.js +52 -5
- package/lib/esm/Button/ButtonBase.js +2 -2
- package/lib/esm/Button/ButtonLink.js +2 -1
- package/lib/esm/Carousel/Carousel.js +18 -7
- package/lib/esm/ExpandCollapse/ExpandCollapse.js +4 -2
- package/lib/esm/ExpandCollapseMini/ExpandCollapseMini.js +11 -2
- package/lib/esm/FileUpload/FileUpload.js +31 -2
- package/lib/esm/Link/Link.js +8 -1
- package/lib/esm/Link/LinkBase.js +2 -0
- package/lib/esm/MultiSelectFilter/MultiSelectFilter.js +3 -2
- package/lib/esm/utils/containUniqueFields.js +5 -5
- package/lib/esm/utils/useUniqueId.js +3 -7
- package/lib/package.json +4 -3
- package/package.json +4 -3
- package/src/ActivityIndicator/Dots.jsx +200 -0
- package/src/ActivityIndicator/Dots.native.jsx +213 -0
- package/src/ActivityIndicator/Spinner.jsx +95 -59
- package/src/ActivityIndicator/Spinner.native.jsx +125 -132
- package/src/ActivityIndicator/index.jsx +17 -2
- package/src/ActivityIndicator/shared.js +52 -5
- package/src/Button/ButtonBase.jsx +4 -2
- package/src/Button/ButtonLink.jsx +3 -1
- package/src/Carousel/Carousel.jsx +28 -7
- package/src/ExpandCollapse/ExpandCollapse.jsx +9 -4
- package/src/ExpandCollapseMini/ExpandCollapseMini.jsx +15 -3
- package/src/FileUpload/FileUpload.jsx +32 -2
- package/src/Link/Link.jsx +8 -1
- package/src/Link/LinkBase.jsx +2 -0
- package/src/MultiSelectFilter/MultiSelectFilter.jsx +2 -2
- package/src/utils/containUniqueFields.js +5 -5
- package/src/utils/useUniqueId.js +3 -8
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
let id = 0;
|
|
1
|
+
import { useId } from 'react';
|
|
3
2
|
function useUniqueId() {
|
|
4
3
|
let prefix = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
return `${prefix}-${id}`;
|
|
8
|
-
});
|
|
9
|
-
return uniqueId;
|
|
4
|
+
const id = useId();
|
|
5
|
+
return `${prefix ? `${prefix}-` : ''}${id}`;
|
|
10
6
|
}
|
|
11
7
|
export default useUniqueId;
|
package/lib/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"@gorhom/portal": "^1.0.14",
|
|
13
13
|
"@react-native-picker/picker": "^2.9.0",
|
|
14
14
|
"@telus-uds/system-constants": "^3.0.0",
|
|
15
|
-
"@telus-uds/system-theme-tokens": "^4.
|
|
15
|
+
"@telus-uds/system-theme-tokens": "^4.3.0",
|
|
16
16
|
"airbnb-prop-types": "^2.16.0",
|
|
17
17
|
"css-mediaquery": "^0.1.2",
|
|
18
18
|
"expo-document-picker": "^13.0.1",
|
|
@@ -59,7 +59,8 @@
|
|
|
59
59
|
"react": "^18.2.0",
|
|
60
60
|
"react-dom": "^18.2.0",
|
|
61
61
|
"react-native": "^0.74.5",
|
|
62
|
-
"react-native-web": "^0.19.10"
|
|
62
|
+
"react-native-web": "^0.19.10",
|
|
63
|
+
"react-native-svg": "15.7.1"
|
|
63
64
|
},
|
|
64
65
|
"react-native": "src/index.js",
|
|
65
66
|
"repository": {
|
|
@@ -83,6 +84,6 @@
|
|
|
83
84
|
"standard-engine": {
|
|
84
85
|
"skip": true
|
|
85
86
|
},
|
|
86
|
-
"version": "3.
|
|
87
|
+
"version": "3.5.0",
|
|
87
88
|
"types": "types/index.d.ts"
|
|
88
89
|
}
|
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"@gorhom/portal": "^1.0.14",
|
|
13
13
|
"@react-native-picker/picker": "^2.9.0",
|
|
14
14
|
"@telus-uds/system-constants": "^3.0.0",
|
|
15
|
-
"@telus-uds/system-theme-tokens": "^4.
|
|
15
|
+
"@telus-uds/system-theme-tokens": "^4.3.0",
|
|
16
16
|
"airbnb-prop-types": "^2.16.0",
|
|
17
17
|
"css-mediaquery": "^0.1.2",
|
|
18
18
|
"expo-document-picker": "^13.0.1",
|
|
@@ -59,7 +59,8 @@
|
|
|
59
59
|
"react": "^18.2.0",
|
|
60
60
|
"react-dom": "^18.2.0",
|
|
61
61
|
"react-native": "^0.74.5",
|
|
62
|
-
"react-native-web": "^0.19.10"
|
|
62
|
+
"react-native-web": "^0.19.10",
|
|
63
|
+
"react-native-svg": "15.7.1"
|
|
63
64
|
},
|
|
64
65
|
"react-native": "src/index.js",
|
|
65
66
|
"repository": {
|
|
@@ -83,6 +84,6 @@
|
|
|
83
84
|
"standard-engine": {
|
|
84
85
|
"skip": true
|
|
85
86
|
},
|
|
86
|
-
"version": "3.
|
|
87
|
+
"version": "3.5.0",
|
|
87
88
|
"types": "types/index.d.ts"
|
|
88
89
|
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {
|
|
3
|
+
DOTS_TOTAL_ANIMATION_DURATION,
|
|
4
|
+
OVERSHOOT_FACTOR,
|
|
5
|
+
UNDERSHOOT_FACTOR,
|
|
6
|
+
BOUNCY_CURVE,
|
|
7
|
+
DOTS_SPACING_X,
|
|
8
|
+
DOTS_BASE_Y_FACTOR,
|
|
9
|
+
DOTS_JUMP_HEIGHT_FACTOR,
|
|
10
|
+
DOTS_PADDING_FACTOR,
|
|
11
|
+
DOTS_FADEOUT_KEYTIMES,
|
|
12
|
+
DOTS_FADEOUT_VALUES,
|
|
13
|
+
DOT1_COLOR_KEYTIMES,
|
|
14
|
+
DOT2_COLOR_KEYTIMES,
|
|
15
|
+
DOT3_COLOR_KEYTIMES,
|
|
16
|
+
DOT1_ANIMATION_KEYTIMES,
|
|
17
|
+
DOT2_ANIMATION_KEYTIMES,
|
|
18
|
+
DOT3_ANIMATION_KEYTIMES,
|
|
19
|
+
propTypes
|
|
20
|
+
} from './shared'
|
|
21
|
+
import { useA11yInfo } from '../A11yInfoProvider'
|
|
22
|
+
|
|
23
|
+
const CY_PROPS = {
|
|
24
|
+
attributeName: 'cy',
|
|
25
|
+
calcMode: 'spline',
|
|
26
|
+
dur: `${DOTS_TOTAL_ANIMATION_DURATION}ms`,
|
|
27
|
+
repeatCount: 'indefinite'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const FILL_PROPS = {
|
|
31
|
+
attributeName: 'fill',
|
|
32
|
+
calcMode: 'linear',
|
|
33
|
+
dur: `${DOTS_TOTAL_ANIMATION_DURATION}ms`,
|
|
34
|
+
repeatCount: 'indefinite'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const OPACITY_PROPS = {
|
|
38
|
+
attributeName: 'opacity',
|
|
39
|
+
calcMode: 'linear',
|
|
40
|
+
dur: `${DOTS_TOTAL_ANIMATION_DURATION}ms`,
|
|
41
|
+
repeatCount: 'indefinite'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const Dots = React.forwardRef(
|
|
45
|
+
({ size, color, indicatorBackgroundColor, label, isStatic = false }, ref) => {
|
|
46
|
+
const { reduceMotionEnabled } = useA11yInfo()
|
|
47
|
+
const reduceMotion = reduceMotionEnabled || isStatic
|
|
48
|
+
|
|
49
|
+
const spacingX = size + DOTS_SPACING_X
|
|
50
|
+
const baseY = size * DOTS_BASE_Y_FACTOR
|
|
51
|
+
const jumpHeight = size * DOTS_JUMP_HEIGHT_FACTOR
|
|
52
|
+
const padding = size * DOTS_PADDING_FACTOR
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<svg
|
|
56
|
+
ref={ref}
|
|
57
|
+
aria-valuetext={label}
|
|
58
|
+
role="progressbar"
|
|
59
|
+
aria-busy="true"
|
|
60
|
+
width={size * 3 + spacingX * 2 + padding * 2}
|
|
61
|
+
height={size * 6}
|
|
62
|
+
viewBox={`0 0 ${size * 3 + spacingX * 2 + padding * 2} ${size * 6}`}
|
|
63
|
+
>
|
|
64
|
+
<g>
|
|
65
|
+
{/* ===================== DOT 1 ===================== */}
|
|
66
|
+
<circle cx={padding + size / 2} cy={baseY} r={size / 2} fill={color}>
|
|
67
|
+
{!reduceMotion && (
|
|
68
|
+
<>
|
|
69
|
+
<animate
|
|
70
|
+
{...CY_PROPS}
|
|
71
|
+
keyTimes={DOT1_ANIMATION_KEYTIMES}
|
|
72
|
+
keySplines={`${BOUNCY_CURVE};${BOUNCY_CURVE};${BOUNCY_CURVE}`}
|
|
73
|
+
values={`
|
|
74
|
+
${baseY};
|
|
75
|
+
${baseY - jumpHeight * OVERSHOOT_FACTOR};
|
|
76
|
+
${baseY + jumpHeight * UNDERSHOOT_FACTOR};
|
|
77
|
+
${baseY}
|
|
78
|
+
`}
|
|
79
|
+
/>
|
|
80
|
+
{/* color (fill) */}
|
|
81
|
+
<animate
|
|
82
|
+
{...FILL_PROPS}
|
|
83
|
+
keyTimes={DOT1_COLOR_KEYTIMES}
|
|
84
|
+
values={`
|
|
85
|
+
${color};
|
|
86
|
+
${color};
|
|
87
|
+
${indicatorBackgroundColor};
|
|
88
|
+
${indicatorBackgroundColor}
|
|
89
|
+
`}
|
|
90
|
+
/>
|
|
91
|
+
{/* fade-out */}
|
|
92
|
+
<animate
|
|
93
|
+
{...OPACITY_PROPS}
|
|
94
|
+
keyTimes={DOTS_FADEOUT_KEYTIMES}
|
|
95
|
+
values={DOTS_FADEOUT_VALUES}
|
|
96
|
+
/>
|
|
97
|
+
</>
|
|
98
|
+
)}
|
|
99
|
+
</circle>
|
|
100
|
+
|
|
101
|
+
{/* ===================== DOT 2 ===================== */}
|
|
102
|
+
<circle
|
|
103
|
+
cx={padding + size + spacingX + size / 2}
|
|
104
|
+
cy={baseY}
|
|
105
|
+
r={size / 2}
|
|
106
|
+
fill={indicatorBackgroundColor}
|
|
107
|
+
>
|
|
108
|
+
{reduceMotion ? null : (
|
|
109
|
+
<>
|
|
110
|
+
<animate
|
|
111
|
+
{...CY_PROPS}
|
|
112
|
+
keyTimes={DOT2_ANIMATION_KEYTIMES}
|
|
113
|
+
keySplines={`
|
|
114
|
+
${BOUNCY_CURVE};
|
|
115
|
+
${BOUNCY_CURVE};
|
|
116
|
+
${BOUNCY_CURVE};
|
|
117
|
+
${BOUNCY_CURVE}
|
|
118
|
+
`}
|
|
119
|
+
values={`
|
|
120
|
+
${baseY};
|
|
121
|
+
${baseY};
|
|
122
|
+
${baseY - jumpHeight * OVERSHOOT_FACTOR};
|
|
123
|
+
${baseY + jumpHeight * UNDERSHOOT_FACTOR};
|
|
124
|
+
${baseY}
|
|
125
|
+
`}
|
|
126
|
+
/>
|
|
127
|
+
{/* color (fill) */}
|
|
128
|
+
<animate
|
|
129
|
+
{...FILL_PROPS}
|
|
130
|
+
keyTimes={DOT2_COLOR_KEYTIMES}
|
|
131
|
+
values={`
|
|
132
|
+
${indicatorBackgroundColor};
|
|
133
|
+
${indicatorBackgroundColor};
|
|
134
|
+
${color};
|
|
135
|
+
${color};
|
|
136
|
+
${indicatorBackgroundColor};
|
|
137
|
+
${indicatorBackgroundColor}
|
|
138
|
+
`}
|
|
139
|
+
/>
|
|
140
|
+
{/* fade-out */}
|
|
141
|
+
<animate
|
|
142
|
+
{...OPACITY_PROPS}
|
|
143
|
+
keyTimes={DOTS_FADEOUT_KEYTIMES}
|
|
144
|
+
values={DOTS_FADEOUT_VALUES}
|
|
145
|
+
/>
|
|
146
|
+
</>
|
|
147
|
+
)}
|
|
148
|
+
</circle>
|
|
149
|
+
|
|
150
|
+
{/* ===================== DOT 3 ===================== */}
|
|
151
|
+
<circle
|
|
152
|
+
cx={padding + (size + spacingX) * 2 + size / 2}
|
|
153
|
+
cy={baseY}
|
|
154
|
+
r={size / 2}
|
|
155
|
+
fill={indicatorBackgroundColor}
|
|
156
|
+
>
|
|
157
|
+
{reduceMotion ? null : (
|
|
158
|
+
<>
|
|
159
|
+
<animate
|
|
160
|
+
{...CY_PROPS}
|
|
161
|
+
keyTimes={DOT3_ANIMATION_KEYTIMES}
|
|
162
|
+
keySplines={`
|
|
163
|
+
${BOUNCY_CURVE};
|
|
164
|
+
${BOUNCY_CURVE};
|
|
165
|
+
${BOUNCY_CURVE};
|
|
166
|
+
${BOUNCY_CURVE}
|
|
167
|
+
`}
|
|
168
|
+
values={`
|
|
169
|
+
${baseY};
|
|
170
|
+
${baseY};
|
|
171
|
+
${baseY - jumpHeight * OVERSHOOT_FACTOR};
|
|
172
|
+
${baseY + jumpHeight * UNDERSHOOT_FACTOR};
|
|
173
|
+
${baseY}
|
|
174
|
+
`}
|
|
175
|
+
/>
|
|
176
|
+
{/* color (fill) */}
|
|
177
|
+
<animate
|
|
178
|
+
{...FILL_PROPS}
|
|
179
|
+
keyTimes={DOT3_COLOR_KEYTIMES}
|
|
180
|
+
values={`
|
|
181
|
+
${indicatorBackgroundColor};
|
|
182
|
+
${indicatorBackgroundColor};
|
|
183
|
+
${color};
|
|
184
|
+
${color};
|
|
185
|
+
${color}
|
|
186
|
+
`}
|
|
187
|
+
/>
|
|
188
|
+
</>
|
|
189
|
+
)}
|
|
190
|
+
</circle>
|
|
191
|
+
</g>
|
|
192
|
+
</svg>
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
Dots.displayName = 'Dots'
|
|
198
|
+
Dots.propTypes = propTypes
|
|
199
|
+
|
|
200
|
+
export default Dots
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { View, StyleSheet, Animated, Easing } from 'react-native'
|
|
3
|
+
import Svg, { Circle } from 'react-native-svg'
|
|
4
|
+
import {
|
|
5
|
+
DOTS_TOTAL_ANIMATION_DURATION,
|
|
6
|
+
OVERSHOOT_FACTOR,
|
|
7
|
+
UNDERSHOOT_FACTOR,
|
|
8
|
+
DOTS_SPACING_X,
|
|
9
|
+
DOTS_BASE_Y_FACTOR,
|
|
10
|
+
DOTS_JUMP_HEIGHT_FACTOR,
|
|
11
|
+
DOTS_PADDING_FACTOR,
|
|
12
|
+
DOT_FADEOUT_INPUT_RANGE,
|
|
13
|
+
DOT_FADEOUT_OUTPUT_RANGE,
|
|
14
|
+
DOT1_ANIMATION_INPUT_RANGE,
|
|
15
|
+
DOT2_ANIMATION_INPUT_RANGE,
|
|
16
|
+
DOT3_ANIMATION_INPUT_RANGE,
|
|
17
|
+
propTypes
|
|
18
|
+
} from './shared'
|
|
19
|
+
import { useA11yInfo } from '../A11yInfoProvider'
|
|
20
|
+
|
|
21
|
+
const AnimatedCircle = Animated.createAnimatedComponent(Circle)
|
|
22
|
+
|
|
23
|
+
const Dots = React.forwardRef(
|
|
24
|
+
({ size, color, indicatorBackgroundColor, label, isStatic = false }, ref) => {
|
|
25
|
+
const { reduceMotionEnabled } = useA11yInfo()
|
|
26
|
+
const reduceMotion = reduceMotionEnabled || isStatic
|
|
27
|
+
|
|
28
|
+
const spacingX = size + DOTS_SPACING_X
|
|
29
|
+
const baseY = size * DOTS_BASE_Y_FACTOR
|
|
30
|
+
const jumpHeight = size * DOTS_JUMP_HEIGHT_FACTOR
|
|
31
|
+
const padding = size * DOTS_PADDING_FACTOR
|
|
32
|
+
const width = (size + 1) * 6
|
|
33
|
+
const height = size * 6
|
|
34
|
+
|
|
35
|
+
const animationValue = React.useRef(new Animated.Value(0)).current
|
|
36
|
+
|
|
37
|
+
React.useEffect(() => {
|
|
38
|
+
if (reduceMotion) {
|
|
39
|
+
animationValue.stopAnimation()
|
|
40
|
+
animationValue.setValue(0)
|
|
41
|
+
return () => {}
|
|
42
|
+
}
|
|
43
|
+
const loopAnimation = Animated.loop(
|
|
44
|
+
Animated.timing(animationValue, {
|
|
45
|
+
toValue: 1,
|
|
46
|
+
duration: DOTS_TOTAL_ANIMATION_DURATION,
|
|
47
|
+
easing: Easing.linear,
|
|
48
|
+
useNativeDriver: false
|
|
49
|
+
})
|
|
50
|
+
)
|
|
51
|
+
loopAnimation.start()
|
|
52
|
+
return () => loopAnimation.stop()
|
|
53
|
+
}, [reduceMotion, animationValue])
|
|
54
|
+
|
|
55
|
+
const makeStepInterpolation = (steps) => {
|
|
56
|
+
const inputRange = []
|
|
57
|
+
const outputRange = []
|
|
58
|
+
steps.forEach(({ time, value }) => {
|
|
59
|
+
inputRange.push(time)
|
|
60
|
+
outputRange.push(value)
|
|
61
|
+
})
|
|
62
|
+
return { inputRange, outputRange }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ---- DOT 1 ----
|
|
66
|
+
const dot1Cy = animationValue.interpolate({
|
|
67
|
+
inputRange: DOT1_ANIMATION_INPUT_RANGE,
|
|
68
|
+
outputRange: [
|
|
69
|
+
baseY,
|
|
70
|
+
baseY - jumpHeight * OVERSHOOT_FACTOR,
|
|
71
|
+
baseY + jumpHeight * UNDERSHOOT_FACTOR,
|
|
72
|
+
baseY
|
|
73
|
+
],
|
|
74
|
+
extrapolate: 'clamp'
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
const { inputRange: dot1FillInputRange, outputRange: dot1FillOutputRange } =
|
|
78
|
+
makeStepInterpolation([
|
|
79
|
+
{ time: 0, value: color },
|
|
80
|
+
{ time: 0.3, value: color },
|
|
81
|
+
{ time: 0.4, value: indicatorBackgroundColor },
|
|
82
|
+
{ time: 1, value: indicatorBackgroundColor }
|
|
83
|
+
])
|
|
84
|
+
const dot1Fill = animationValue.interpolate({
|
|
85
|
+
inputRange: dot1FillInputRange,
|
|
86
|
+
outputRange: dot1FillOutputRange,
|
|
87
|
+
extrapolate: 'clamp'
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
// Fade-out Dot 1
|
|
91
|
+
const dot1Opacity = animationValue.interpolate({
|
|
92
|
+
inputRange: DOT_FADEOUT_INPUT_RANGE,
|
|
93
|
+
outputRange: DOT_FADEOUT_OUTPUT_RANGE,
|
|
94
|
+
extrapolate: 'clamp'
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
// ---- DOT 2 ----
|
|
98
|
+
const dot2Cy = animationValue.interpolate({
|
|
99
|
+
inputRange: DOT2_ANIMATION_INPUT_RANGE,
|
|
100
|
+
outputRange: [
|
|
101
|
+
baseY,
|
|
102
|
+
baseY,
|
|
103
|
+
baseY - jumpHeight * OVERSHOOT_FACTOR,
|
|
104
|
+
baseY + jumpHeight * UNDERSHOOT_FACTOR,
|
|
105
|
+
baseY
|
|
106
|
+
],
|
|
107
|
+
extrapolate: 'clamp'
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
const { inputRange: dot2FillInputRange, outputRange: dot2FillOutputRange } =
|
|
111
|
+
makeStepInterpolation([
|
|
112
|
+
{ time: 0, value: indicatorBackgroundColor },
|
|
113
|
+
{ time: 0.3, value: indicatorBackgroundColor },
|
|
114
|
+
{ time: 0.4, value: color },
|
|
115
|
+
{ time: 0.6, value: color },
|
|
116
|
+
{ time: 0.7, value: indicatorBackgroundColor },
|
|
117
|
+
{ time: 1, value: indicatorBackgroundColor }
|
|
118
|
+
])
|
|
119
|
+
const dot2Fill = animationValue.interpolate({
|
|
120
|
+
inputRange: dot2FillInputRange,
|
|
121
|
+
outputRange: dot2FillOutputRange,
|
|
122
|
+
extrapolate: 'clamp'
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
// Fade-out Dot 2
|
|
126
|
+
const dot2Opacity = animationValue.interpolate({
|
|
127
|
+
inputRange: DOT_FADEOUT_INPUT_RANGE,
|
|
128
|
+
outputRange: DOT_FADEOUT_OUTPUT_RANGE,
|
|
129
|
+
extrapolate: 'clamp'
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
// ---- DOT 3 ----
|
|
133
|
+
const dot3Cy = animationValue.interpolate({
|
|
134
|
+
inputRange: DOT3_ANIMATION_INPUT_RANGE,
|
|
135
|
+
outputRange: [
|
|
136
|
+
baseY,
|
|
137
|
+
baseY,
|
|
138
|
+
baseY - jumpHeight * OVERSHOOT_FACTOR,
|
|
139
|
+
baseY + jumpHeight * UNDERSHOOT_FACTOR,
|
|
140
|
+
baseY
|
|
141
|
+
],
|
|
142
|
+
extrapolate: 'clamp'
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
const { inputRange: dot3FillInputRange, outputRange: dot3FillOutputRange } =
|
|
146
|
+
makeStepInterpolation([
|
|
147
|
+
{ time: 0, value: indicatorBackgroundColor },
|
|
148
|
+
{ time: 0.5, value: indicatorBackgroundColor },
|
|
149
|
+
{ time: 0.6, value: color },
|
|
150
|
+
{ time: 0.8, value: color },
|
|
151
|
+
{ time: 1, value: color }
|
|
152
|
+
])
|
|
153
|
+
const dot3Fill = animationValue.interpolate({
|
|
154
|
+
inputRange: dot3FillInputRange,
|
|
155
|
+
outputRange: dot3FillOutputRange,
|
|
156
|
+
extrapolate: 'clamp'
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
const dot3Opacity = new Animated.Value(1)
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<View
|
|
163
|
+
ref={ref}
|
|
164
|
+
style={styles.container}
|
|
165
|
+
accessible
|
|
166
|
+
accessibilityLabel={label}
|
|
167
|
+
accessibilityRole="progressbar"
|
|
168
|
+
accessibilityState={{ busy: true }}
|
|
169
|
+
>
|
|
170
|
+
<Svg width={width} height={height} viewBox={`0 0 ${width} ${height}`}>
|
|
171
|
+
{/* DOT 1 */}
|
|
172
|
+
<AnimatedCircle
|
|
173
|
+
cx={size}
|
|
174
|
+
cy={reduceMotion ? baseY : dot1Cy}
|
|
175
|
+
r={padding}
|
|
176
|
+
fill={reduceMotion ? color : dot1Fill}
|
|
177
|
+
opacity={reduceMotion ? 1 : dot1Opacity}
|
|
178
|
+
/>
|
|
179
|
+
|
|
180
|
+
{/* DOT 2 */}
|
|
181
|
+
<AnimatedCircle
|
|
182
|
+
cx={2 * size + spacingX}
|
|
183
|
+
cy={reduceMotion ? baseY : dot2Cy}
|
|
184
|
+
r={padding}
|
|
185
|
+
fill={reduceMotion ? indicatorBackgroundColor : dot2Fill}
|
|
186
|
+
opacity={reduceMotion ? 1 : dot2Opacity}
|
|
187
|
+
/>
|
|
188
|
+
|
|
189
|
+
{/* DOT 3 */}
|
|
190
|
+
<AnimatedCircle
|
|
191
|
+
cx={3 * size + 2 * spacingX}
|
|
192
|
+
cy={reduceMotion ? baseY : dot3Cy}
|
|
193
|
+
r={padding}
|
|
194
|
+
fill={reduceMotion ? indicatorBackgroundColor : dot3Fill}
|
|
195
|
+
opacity={reduceMotion ? 1 : dot3Opacity}
|
|
196
|
+
/>
|
|
197
|
+
</Svg>
|
|
198
|
+
</View>
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
Dots.displayName = 'Dots'
|
|
204
|
+
Dots.propTypes = propTypes
|
|
205
|
+
|
|
206
|
+
const styles = StyleSheet.create({
|
|
207
|
+
container: {
|
|
208
|
+
alignItems: 'center',
|
|
209
|
+
justifyContent: 'center'
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
export default Dots
|
|
@@ -1,83 +1,119 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
DURATION,
|
|
4
|
+
SVG_CIRCUMFERENCE,
|
|
5
|
+
SVG_SIZE,
|
|
6
|
+
ROTATION_TRANSFORM,
|
|
7
|
+
SVG_CENTER,
|
|
8
|
+
SPINNER_RADIUS,
|
|
9
|
+
SPINNER_ROTATION_DEGREES,
|
|
10
|
+
SPINNER_DASHARRAY_MIN,
|
|
11
|
+
SPINNER_DASHARRAY_MAX,
|
|
12
|
+
SPINNER_KEYTIMES,
|
|
13
|
+
SPINNER_DASHARRAY_HALF,
|
|
14
|
+
SPINNER_DASHOFFSET_FACTOR,
|
|
15
|
+
propTypes
|
|
16
|
+
} from './shared'
|
|
3
17
|
import { useA11yInfo } from '../A11yInfoProvider'
|
|
4
18
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
const MIN_SVG_LENGTH = (MIN_STROKE_ANGLE / 360) * SVG_CIRCUMFERENCE
|
|
8
|
-
const MAX_SVG_LENGTH = (1 - MIN_EMPTY_ANGLE / 360) * SVG_CIRCUMFERENCE
|
|
9
|
-
|
|
10
|
-
const animationProps = {
|
|
11
|
-
begin: '0s',
|
|
12
|
-
dur: `${DURATION}ms`,
|
|
13
|
-
fill: 'freeze',
|
|
19
|
+
const animateProps = {
|
|
20
|
+
keyTimes: SPINNER_KEYTIMES,
|
|
14
21
|
repeatCount: 'indefinite'
|
|
15
22
|
}
|
|
16
23
|
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
)}
|
|
24
|
+
const Spinner = React.forwardRef(
|
|
25
|
+
({ size, color, indicatorBackgroundColor, thickness, label, isStatic = false }, ref) => {
|
|
26
|
+
const { reduceMotionEnabled } = useA11yInfo()
|
|
27
|
+
const reduceMotion = reduceMotionEnabled || isStatic
|
|
28
|
+
|
|
29
|
+
const strokeWidth = (thickness * SVG_SIZE) / size
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<svg
|
|
33
|
+
ref={ref}
|
|
34
|
+
width={`${size}px`}
|
|
35
|
+
height={`${size}px`}
|
|
36
|
+
viewBox={`0 0 ${SVG_SIZE} ${SVG_SIZE}`}
|
|
37
|
+
aria-valuetext={label}
|
|
38
|
+
role="progressbar"
|
|
39
|
+
aria-busy="true"
|
|
40
|
+
>
|
|
41
|
+
{/* Base static circle with background color */}
|
|
42
|
+
<circle
|
|
43
|
+
fill="none"
|
|
44
|
+
stroke={indicatorBackgroundColor}
|
|
45
|
+
strokeWidth={strokeWidth}
|
|
46
|
+
cx={SVG_CENTER}
|
|
47
|
+
cy={SVG_CENTER}
|
|
48
|
+
r={SPINNER_RADIUS}
|
|
49
|
+
/>
|
|
50
|
+
|
|
51
|
+
{/* Animated circle */}
|
|
46
52
|
<circle
|
|
47
53
|
fill="none"
|
|
48
54
|
stroke={color}
|
|
49
|
-
strokeWidth={
|
|
55
|
+
strokeWidth={strokeWidth}
|
|
50
56
|
strokeLinecap="round"
|
|
51
|
-
cx=
|
|
52
|
-
cy=
|
|
53
|
-
r=
|
|
54
|
-
strokeDasharray={
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
cx={SVG_CENTER}
|
|
58
|
+
cy={SVG_CENTER}
|
|
59
|
+
r={SPINNER_RADIUS}
|
|
60
|
+
strokeDasharray={`${SPINNER_DASHARRAY_MIN * SVG_CIRCUMFERENCE}, ${
|
|
61
|
+
SPINNER_DASHARRAY_MAX * SVG_CIRCUMFERENCE
|
|
62
|
+
}`}
|
|
63
|
+
strokeDashoffset="0"
|
|
64
|
+
// Circle rotated -90° so that 0% is at the top (12 o'clock).
|
|
65
|
+
transform={ROTATION_TRANSFORM}
|
|
57
66
|
>
|
|
58
67
|
{reduceMotion ? null : (
|
|
59
68
|
<>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
69
|
+
{/*
|
|
70
|
+
Rotation animated from 0° to 183°,
|
|
71
|
+
with additive="sum" is added to the base rotation of -90°.
|
|
72
|
+
*/}
|
|
73
|
+
<animateTransform
|
|
74
|
+
attributeName="transform"
|
|
75
|
+
type="rotate"
|
|
76
|
+
from="0 24 24"
|
|
77
|
+
to={`${SPINNER_ROTATION_DEGREES} ${SVG_CENTER} ${SVG_CENTER}`}
|
|
78
|
+
dur={`${DURATION}ms`}
|
|
79
|
+
repeatCount="indefinite"
|
|
80
|
+
additive="sum"
|
|
65
81
|
/>
|
|
66
82
|
<animate
|
|
67
83
|
attributeName="stroke-dasharray"
|
|
68
|
-
|
|
69
|
-
{
|
|
70
|
-
|
|
84
|
+
dur={`${DURATION}ms`}
|
|
85
|
+
values={`
|
|
86
|
+
${SPINNER_DASHARRAY_MIN * SVG_CIRCUMFERENCE},${
|
|
87
|
+
SPINNER_DASHARRAY_MAX * SVG_CIRCUMFERENCE
|
|
88
|
+
};
|
|
89
|
+
${SPINNER_DASHARRAY_HALF * SVG_CIRCUMFERENCE},${
|
|
90
|
+
SPINNER_DASHARRAY_HALF * SVG_CIRCUMFERENCE
|
|
91
|
+
};
|
|
92
|
+
${SPINNER_DASHARRAY_MIN * SVG_CIRCUMFERENCE},${
|
|
93
|
+
SPINNER_DASHARRAY_MAX * SVG_CIRCUMFERENCE
|
|
94
|
+
}
|
|
95
|
+
`}
|
|
96
|
+
{...animateProps}
|
|
97
|
+
/>
|
|
98
|
+
<animate
|
|
99
|
+
attributeName="stroke-dashoffset"
|
|
100
|
+
dur={`${DURATION}ms`}
|
|
101
|
+
values={`
|
|
102
|
+
0;
|
|
103
|
+
0;
|
|
104
|
+
-${SPINNER_DASHOFFSET_FACTOR * SVG_CIRCUMFERENCE}
|
|
105
|
+
`}
|
|
106
|
+
{...animateProps}
|
|
71
107
|
/>
|
|
72
108
|
</>
|
|
73
109
|
)}
|
|
74
110
|
</circle>
|
|
75
|
-
</
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
Spinner.displayName = 'Spinner'
|
|
111
|
+
</svg>
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
)
|
|
80
115
|
|
|
116
|
+
Spinner.displayName = 'Spinner'
|
|
81
117
|
Spinner.propTypes = propTypes
|
|
82
118
|
|
|
83
119
|
export default Spinner
|