[add] ProgressBar component

Ref #91
This commit is contained in:
Nicolas Gallagher
2016-09-01 16:49:39 -07:00
parent 977d8729f5
commit 0aef117733
5 changed files with 260 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
# ProgressBar
Display an activity progress bar.
## Props
[...View props](./View.md)
**color**: string = '#1976D2'
Color of the progress bar.
**indeterminate**: bool = true
Whether the progress bar will show indeterminate progress.
**progress**: number
The progress value (between 0 and 1).
(web) **trackColor**: string = 'transparent'
Color of the track bar.

View File

@@ -0,0 +1,96 @@
import { ProgressBar, StyleSheet, View } from 'react-native'
import React from 'react';
import { storiesOf, action } from '@kadira/storybook';
import TimerMixin from 'react-timer-mixin';
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @flow
*/
var ProgressBarExample = React.createClass({
mixins: [TimerMixin],
getInitialState() {
return {
progress: 0,
};
},
componentDidMount() {
this.updateProgress();
},
updateProgress() {
var progress = this.state.progress + 0.01;
this.setState({ progress });
this.requestAnimationFrame(() => this.updateProgress());
},
getProgress(offset) {
var progress = this.state.progress + offset;
return Math.sin(progress % Math.PI) % 1;
},
render() {
return (
<View style={styles.container}>
<ProgressBar style={styles.progressView} color="purple" progress={this.getProgress(0.2)} />
<ProgressBar style={styles.progressView} color="red" progress={this.getProgress(0.4)} />
<ProgressBar style={styles.progressView} color="orange" progress={this.getProgress(0.6)} />
<ProgressBar style={styles.progressView} color="yellow" progress={this.getProgress(0.8)} />
</View>
);
},
});
const examples = [{
title: 'progress',
render() {
return (
<ProgressBarExample />
);
},
}, {
title: 'indeterminate',
render() {
return (
<ProgressBar indeterminate style={styles.progressView} trackColor='#D1E3F6' />
);
}
}];
var styles = StyleSheet.create({
container: {
minWidth: 200,
marginTop: -20,
backgroundColor: 'transparent',
},
progressView: {
marginTop: 20,
minWidth: 200
}
});
examples.forEach((example) => {
storiesOf('component: ProgressBar', module)
.add(example.title, () => example.render())
})

View File

@@ -0,0 +1,20 @@
/* eslint-env mocha */
import assert from 'assert';
import React from 'react';
import { shallow } from 'enzyme';
import ProgressBar from '..';
suite('components/ProgressBar', () => {
suite('progress', () => {
test('value as percentage is set to "aria-valuenow"', () => {
const component = shallow(<ProgressBar progress={0.5} />);
assert(component.prop('aria-valuenow') === 50);
});
test('is ignored when "indeterminate" is "true"', () => {
const component = shallow(<ProgressBar indeterminate progress={0.5} />);
assert(component.prop('aria-valuenow') === null);
});
});
});

View File

@@ -0,0 +1,119 @@
import Animated from '../../apis/Animated';
import applyNativeMethods from '../../modules/applyNativeMethods';
import ColorPropType from '../../propTypes/ColorPropType';
import StyleSheet from '../../apis/StyleSheet';
import View from '../View';
import React, { Component, PropTypes } from 'react';
const indeterminateWidth = '25%';
const translateInterpolation = { inputRange: [ 0, 1 ], outputRange: [ '-100%', '400%' ] };
class ProgressBar extends Component {
static propTypes = {
...View.propTypes,
color: ColorPropType,
indeterminate: PropTypes.bool,
progress: PropTypes.number,
trackColor: ColorPropType
};
static defaultProps = {
color: '#1976D2',
indeterminate: false,
progress: 0,
trackColor: 'transparent'
};
constructor(props) {
super(props);
this.state = {
animationScale: new Animated.Value(0),
animationTranslate: new Animated.Value(0)
};
}
componentDidMount() {
this._manageAnimation();
}
componentDidUpdate() {
this._manageAnimation();
}
render() {
const {
color,
indeterminate,
progress,
trackColor,
style,
...other
} = this.props;
const { animationTranslate } = this.state;
const percentageProgress = indeterminate ? 50 : progress * 100;
return (
<View {...other}
accessibilityRole='progressbar'
aria-valuemax='100'
aria-valuemin='0'
aria-valuenow={indeterminate ? null : percentageProgress}
style={[
styles.track,
style,
{ backgroundColor: trackColor }
]}
>
<Animated.View style={[
styles.progress,
{ backgroundColor: color },
indeterminate ? {
transform: [
{ translateX: animationTranslate.interpolate(translateInterpolation) }
],
width: indeterminateWidth
} : {
width: `${percentageProgress}%`
}
]} />
</View>
);
}
_manageAnimation() {
const { animationTranslate } = this.state;
const cycleAnimation = (animation) => {
animation.setValue(0);
Animated.timing(animation, {
duration: 1000,
toValue: 1
}).start((event) => {
if (event.finished) {
cycleAnimation(animation);
}
});
};
if (this.props.indeterminate) {
cycleAnimation(animationTranslate);
} else {
animationTranslate.stopAnimation();
}
}
}
const styles = StyleSheet.create({
track: {
height: 5,
overflow: 'hidden',
userSelect: 'none'
},
progress: {
height: '100%'
}
});
module.exports = applyNativeMethods(ProgressBar);

View File

@@ -25,6 +25,7 @@ import Vibration from './apis/Vibration';
import ActivityIndicator from './components/ActivityIndicator';
import Image from './components/Image';
import ListView from './components/ListView';
import ProgressBar from './components/ProgressBar';
import ScrollView from './components/ScrollView';
import Switch from './components/Switch';
import Text from './components/Text';
@@ -75,6 +76,7 @@ const ReactNative = {
ActivityIndicator,
Image,
ListView,
ProgressBar,
ScrollView,
Switch,
Text,