react-native-ui-lib
Version:
<p align="center"> <img src="https://user-images.githubusercontent.com/1780255/105469025-56759000-5ca0-11eb-993d-3568c1fd54f4.png" height="250px" style="display:block"/> </p> <p align="center">UI Toolset & Components Library for React Native</p> <p a
254 lines (214 loc) • 6.43 kB
JavaScript
import _pt from "prop-types";
import { isUndefined } from 'lodash';
import React, { PureComponent } from 'react';
import { StyleSheet, AccessibilityInfo } from 'react-native';
import { Typography, Spacings } from "../../style";
import { asBaseComponent } from "../../commons/new";
import View from "../view";
import Text from "../text";
import Button from "../button";
var ActionType;
(function (ActionType) {
ActionType["MINUS"] = "minus";
ActionType["PLUS"] = "plus";
})(ActionType || (ActionType = {}));
const minusOutline = require("./assets/minusOutline.png");
const minusOutlineSmall = require("./assets/minusOutlineSmall.png");
const plusOutline = require("./assets/plusOutline.png");
const plusOutlineSmall = require("./assets/plusOutlineSmall.png");
const DEFAULT_STEP = 1;
/**
* @description: A stepper component
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/StepperScreen.js
*/
class Stepper extends PureComponent {
static propTypes = {
/**
* Initial value of the Stepper.
*/
value: _pt.number,
/**
* Minimum value.
*/
minValue: _pt.number,
/**
* Maximum value.
*/
maxValue: _pt.number,
/**
* The step to increase and decrease by (default is 1)
*/
step: _pt.number,
/**
* On value change callback function
*/
onValueChange: _pt.func,
/**
* disables interaction with the stepper
*/
disabled: _pt.bool,
/**
* Renders a small sized Stepper
*/
small: _pt.bool,
/**
* Component accessibility label
*/
accessibilityLabel: _pt.string,
/**
* Test id for component
*/
testID: _pt.string
};
constructor(props) {
super(props);
const {
value,
minValue = 0,
maxValue = 1,
testID
} = props;
let initialValue = 0;
if (minValue) {
initialValue = minValue;
}
if (value !== undefined) {
initialValue = value;
}
this.state = {
currentValue: initialValue
};
if (initialValue < minValue) {
console.warn(`Stepper: ${testID}'s minimum value: ${minValue} is greater than current value: ${initialValue}`);
}
if (initialValue > maxValue) {
console.warn(`Stepper: ${testID}'s maximum value: ${maxValue} is less than current value: ${initialValue}`);
}
if (minValue > maxValue) {
console.warn(`Stepper: ${testID}'s minimum value: ${minValue} is greater than the maximum value: ${maxValue}`);
}
}
static getDerivedStateFromProps(nextProps, prevState) {
if (!isUndefined(nextProps.value) && prevState.currentValue !== nextProps.value) {
return {
currentValue: nextProps.value
};
}
return null;
}
getAccessibilityProps() {
const {
currentValue
} = this.state;
const {
accessibilityLabel
} = this.props;
const labelSuffix = `value = ${currentValue}`;
return {
accessibilityLabel: accessibilityLabel ? `${accessibilityLabel}, ${labelSuffix}` : `Stepper, ${labelSuffix}`,
accessible: true,
accessibilityRole: 'adjustable',
accessibilityActions: [{
name: 'decrement'
}, {
name: 'increment'
}],
onAccessibilityAction: this.onAccessibilityAction
};
}
onAccessibilityAction = event => {
const {
currentValue
} = this.state;
const {
step = DEFAULT_STEP
} = this.props;
const eventMsgContext = event.nativeEvent.actionName === 'decrement' ? 'Minimum' : 'Maximum';
const stepperLimitMsg = `${eventMsgContext} stepper value, ${currentValue}, reached`;
switch (event.nativeEvent.actionName) {
case 'decrement':
this.accessibilityActionHandler(ActionType.MINUS, currentValue - step, stepperLimitMsg);
break;
case 'increment':
this.accessibilityActionHandler(ActionType.PLUS, currentValue + step, stepperLimitMsg);
break;
default:
break;
}
};
accessibilityActionHandler(actionType, newStepperValue, actionLimitMsg) {
if (this.allowStepChange(actionType)) {
this.handleStepChange(actionType);
AccessibilityInfo.announceForAccessibility?.(newStepperValue.toString());
} else {
AccessibilityInfo.announceForAccessibility?.(actionLimitMsg);
}
}
allowStepChange(actionType) {
const {
minValue,
maxValue
} = this.props;
const {
currentValue
} = this.state;
if (actionType === ActionType.PLUS) {
return maxValue === undefined || currentValue < maxValue;
}
if (actionType === ActionType.MINUS) {
return minValue === undefined || currentValue > minValue;
}
}
handleStepChange(actionType) {
const {
testID,
step = DEFAULT_STEP
} = this.props;
const {
currentValue
} = this.state;
let newCurrent = currentValue;
if (actionType === ActionType.MINUS) {
newCurrent = currentValue - step;
} else {
newCurrent = currentValue + step;
}
this.setState({
currentValue: newCurrent
});
this.props.onValueChange?.(newCurrent, testID);
}
renderButton(actionType) {
const {
disabled,
small,
testID
} = this.props;
const allowStepChange = this.allowStepChange(actionType);
const minusButton = small ? minusOutlineSmall : minusOutline;
const plusButton = small ? plusOutlineSmall : plusOutline;
return <Button link throttleTime={0} iconSource={actionType === ActionType.MINUS ? minusButton : plusButton} disabled={disabled || !allowStepChange} onPress={() => this.handleStepChange(actionType)} testID={actionType === ActionType.MINUS ? `${testID}.minusStep` : `${testID}.plusStep`} useCustomTheme />;
}
render() {
const {
testID,
disabled
} = this.props;
const {
currentValue
} = this.state;
return <View row centerV {...this.getAccessibilityProps()}>
{this.renderButton(ActionType.MINUS)}
<Text grey50={disabled} style={[Typography.text70M, styles.text]} testID={`${testID}.currentValue`}>
{currentValue}
</Text>
{this.renderButton(ActionType.PLUS)}
</View>;
}
}
const styles = StyleSheet.create({
text: {
marginHorizontal: Spacings.s5
}
});
export default asBaseComponent(Stepper);