Skip to content
This repository was archived by the owner on Feb 23, 2021. It is now read-only.

Commit 2ae5b54

Browse files
authored
Merge pull request #377 from lightninglabs/spinner-components
Implement small spinner
2 parents 10f8c69 + 909edd7 commit 2ae5b54

File tree

6 files changed

+258
-1
lines changed

6 files changed

+258
-1
lines changed

package-lock.json

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
"react-art": "^16.2.0",
3838
"react-dom": "^16.2.0",
3939
"react-native-web": "^0.6.0",
40-
"react-scripts": "^1.1.1"
40+
"react-scripts": "^1.1.1",
41+
"svgs": "^3.2.1"
4142
},
4243
"devDependencies": {
4344
"@storybook/addon-actions": "^3.3.15",

src/component/spinner.js

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
import React from 'react';
2+
import {
3+
ActivityIndicator,
4+
StyleSheet,
5+
View,
6+
ViewPropTypes,
7+
} from 'react-native';
8+
import PropTypes from 'prop-types';
9+
import { color, font } from './style';
10+
import Icon from './icon';
11+
import Text from './text';
12+
import Svg, { Path, Circle, Defs, Stop, LinearGradient } from './svg';
13+
14+
//
15+
// Small Spinner
16+
//
17+
18+
const smallStyles = StyleSheet.create({
19+
spinner: {
20+
transform: [{ scale: 1.0 }],
21+
},
22+
});
23+
24+
export const SmallSpinner = ({ ...props }) => (
25+
<ActivityIndicator
26+
size="small"
27+
color={color.purple}
28+
style={smallStyles.spinner}
29+
{...props}
30+
/>
31+
);
32+
33+
//
34+
// Load Network Spinner
35+
//
36+
37+
const sizeM = 80;
38+
const progressWidthM = 3;
39+
40+
const loadNetworkStyles = StyleSheet.create({
41+
spinner: {
42+
margin: 20,
43+
},
44+
bolt: {
45+
height: 126 / 4.5,
46+
width: 64 / 4.5,
47+
},
48+
copy: {
49+
fontSize: font.sizeXS,
50+
marginTop: 5,
51+
color: color.white,
52+
textAlign: 'center',
53+
},
54+
});
55+
56+
export const LoadNetworkSpinner = ({ percentage, msg }) => (
57+
<View style={loadNetworkStyles.spinner}>
58+
<ResizeableSpinner
59+
percentage={percentage}
60+
size={sizeM}
61+
progressWidth={progressWidthM}
62+
gradient="loadNetworkGrad"
63+
icon="lightning-bolt"
64+
iconStyles={loadNetworkStyles.bolt}
65+
/>
66+
<Text style={loadNetworkStyles.copy}>{msg}</Text>
67+
</View>
68+
);
69+
70+
LoadNetworkSpinner.propTypes = {
71+
percentage: PropTypes.number.isRequired,
72+
msg: PropTypes.string.isRequired,
73+
};
74+
75+
//
76+
// Resizeable Spinner
77+
//
78+
79+
const resizeableStyles = StyleSheet.create({
80+
iconWrapper: {
81+
position: 'absolute',
82+
top: 0,
83+
left: 0,
84+
bottom: 0,
85+
right: 0,
86+
justifyContent: 'center',
87+
alignItems: 'center',
88+
},
89+
});
90+
91+
const ResizeableSpinner = ({
92+
percentage,
93+
size,
94+
gradient,
95+
progressWidth,
96+
icon,
97+
iconStyles,
98+
}) => (
99+
<View style={{ width: size, height: size }}>
100+
<Svg width={size} height={size}>
101+
<Gradients />
102+
<SpinnerProgress
103+
width={size}
104+
percentage={percentage}
105+
color={`url(#${gradient})`}
106+
/>
107+
{
108+
<SpinnerFill
109+
spinnerWidth={size}
110+
progressWidth={progressWidth}
111+
color={color.blackDark}
112+
/>
113+
}
114+
</Svg>
115+
<View style={resizeableStyles.iconWrapper}>
116+
<Icon image={icon} style={iconStyles} />
117+
</View>
118+
</View>
119+
);
120+
121+
ResizeableSpinner.propTypes = {
122+
percentage: PropTypes.number.isRequired,
123+
size: PropTypes.number.isRequired,
124+
progressWidth: PropTypes.number.isRequired,
125+
gradient: PropTypes.string.isRequired,
126+
icon: PropTypes.string.isRequired,
127+
iconStyles: ViewPropTypes.style,
128+
};
129+
130+
//
131+
// Loading Network Gradient
132+
//
133+
134+
const Gradients = () => (
135+
<Defs>
136+
<LinearGradient id="loadNetworkGrad" x1="0" y1="0" x2="1" y2="1">
137+
<Stop offset="0%" stopColor={color.loadNetworkLightPurple} />
138+
<Stop offset="50%" stopColor={color.loadNetworkMedPurple} />
139+
<Stop offset="70%" stopColor={color.loadNetworkMedDarkPurple} />
140+
<Stop offset="100%" stopColor={color.purple} />
141+
</LinearGradient>
142+
<LinearGradient id="openChannelsGrad" x1="0" y1="0" x2="1" y2="1">
143+
<Stop offset="0%" stopColor={color.openChansLightPurple} />
144+
<Stop offset="50%" stopColor={color.openChansDarkPurple} />
145+
</LinearGradient>
146+
</Defs>
147+
);
148+
149+
//
150+
// Spinner Progress Path
151+
//
152+
153+
const SpinnerProgress = ({ width, percentage, color }) => (
154+
<Path
155+
d={`M${width / 2} ${width / 2} L${width / 2} 0 ${generateArc(
156+
percentage,
157+
width / 2
158+
)} Z`}
159+
fill={color}
160+
/>
161+
);
162+
163+
SpinnerProgress.propTypes = {
164+
width: PropTypes.number.isRequired,
165+
percentage: PropTypes.number.isRequired,
166+
color: PropTypes.string.isRequired,
167+
};
168+
169+
//
170+
// Spinner Fill
171+
//
172+
173+
const SpinnerFill = ({ spinnerWidth, progressWidth, color }) => (
174+
<Circle
175+
cx={spinnerWidth / 2}
176+
cy={spinnerWidth / 2}
177+
r={spinnerWidth / 2 - progressWidth}
178+
fill={color}
179+
/>
180+
);
181+
182+
SpinnerFill.propTypes = {
183+
spinnerWidth: PropTypes.number.isRequired,
184+
progressWidth: PropTypes.number.isRequired,
185+
color: PropTypes.string.isRequired,
186+
};
187+
188+
const generateArc = (percentage, radius) => {
189+
if (percentage === 0) {
190+
percentage = 1;
191+
} else if (percentage === 100) {
192+
percentage = 99.999;
193+
}
194+
const a = percentage * 2 * Math.PI / 100; // angle (in radian) depends on percentage
195+
const r = radius; // radius of the circle
196+
var rx = r,
197+
ry = r,
198+
xAxisRotation = 0,
199+
largeArcFlag = 1,
200+
sweepFlag = 1,
201+
x = r + r * Math.sin(a),
202+
y = r - r * Math.cos(a);
203+
if (percentage <= 50) {
204+
largeArcFlag = 0;
205+
} else {
206+
largeArcFlag = 1;
207+
}
208+
209+
return `A${rx} ${ry} ${xAxisRotation} ${largeArcFlag} ${sweepFlag} ${x} ${y}`;
210+
};

src/component/style.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ export const color = {
1111
greyLight: '#D7D4D4',
1212
purple: '#57038D',
1313
lightPurple: '#A540CD',
14+
loadNetworkLightPurple: '#A95BDC',
15+
loadNetworkMedPurple: '#651399',
16+
loadNetworkMedDarkPurple: '#610F96',
17+
openChansLightPurple: '#A540CD',
18+
openChansDarkPurple: '#6B249C',
1419
orange: '#F66B1C',
1520
blackDark: '#252F4A',
1621
blackText: '#4A4A4A',

src/component/svg.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import Svg from 'svgs';
2+
export default Svg;
3+
export * from 'svgs';

stories/component/spinner.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react';
2+
import { storiesOf } from '@storybook/react';
3+
import MainContent from '../../src/component/main-content';
4+
import { LoadNetworkSpinner, SmallSpinner } from '../../src/component/spinner';
5+
import { color } from '../../src/component/style';
6+
import Background from '../../src/component/background';
7+
8+
storiesOf('Spinner', module)
9+
.addDecorator(story => (
10+
<MainContent style={{ justifyContent: 'center' }}>{story()}</MainContent>
11+
))
12+
.add('SmallSpinner', () => <SmallSpinner />);
13+
14+
storiesOf('Spinner', module)
15+
.addDecorator(story => (
16+
<Background color={color.blackDark}>{story()}</Background>
17+
))
18+
.add('LoadNetworkSpinner', () => (
19+
<MainContent style={{ alignItems: 'flex-start', flexDirection: 'row' }}>
20+
<LoadNetworkSpinner percentage={30} msg="Loading network..." />
21+
<LoadNetworkSpinner percentage={50} msg="Almost done..." />
22+
<LoadNetworkSpinner percentage={100} msg="Just a few seconds..." />
23+
</MainContent>
24+
));

0 commit comments

Comments
 (0)