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
366 lines (331 loc) • 9.68 kB
JavaScript
import _pt from "prop-types";
import _ from 'lodash';
import React, { PureComponent } from 'react';
import { LayoutAnimation, StyleSheet, Keyboard, TextInput, PixelRatio, I18nManager } from 'react-native';
import { Constants, asBaseComponent } from "../../commons/new";
import Assets from "../../assets";
import { Colors, Typography } from "../../style";
import View from "../view";
import Text from "../text";
import TouchableOpacity from "../touchableOpacity";
import Dialog from "../dialog";
import Button from "../button";
import ColorSliderGroup from "../slider/ColorSliderGroup";
import PanningProvider from "../panningViews/panningProvider";
const KEYBOARD_HEIGHT = 216;
/**
* @description: A color picker dialog component
* @extends: Dialog
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/ColorPickerScreen.tsx
*/
class ColorPickerDialog extends PureComponent {
static propTypes = {
/**
* The initial color to pass the picker dialog
*/
initialColor: _pt.string,
/**
* onSubmit callback for the picker dialog color change
*/
onSubmit: _pt.func,
/**
* Props to pass the Dialog component // TODO: deprecate 'dialogProps' prop
*/
dialogProps: _pt.object,
/**
* Ok (v) button color
*/
/**
* Accessibility labels as an object of strings, ex. {addButton: 'add custom color using hex code', dismissButton: 'dismiss', doneButton: 'done', input: 'custom hex color code'}
*/
doneButtonColor: _pt.string,
accessibilityLabels: _pt.shape({
dismissButton: _pt.string,
doneButton: _pt.string,
input: _pt.string
})
};
static displayName = 'ColorPicker';
static defaultProps = {
initialColor: Colors.grey80
};
constructor(props) {
super(props);
const color = Colors.getHSL(props.initialColor);
const text = this.getColorValue(props.initialColor);
const {
valid
} = this.getValidColorString(text);
this.state = {
keyboardHeight: KEYBOARD_HEIGHT,
color,
text,
valid
};
}
textInput = React.createRef(); //@ts-ignore
componentDidMount() {
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this.keyboardDidShow);
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this.keyboardDidHide);
}
componentWillUnmount() {
this.keyboardDidShowListener.remove();
this.keyboardDidHideListener.remove();
}
keyboardDidShow = e => {
if (Constants.isIOS && this.state.keyboardHeight !== e.endCoordinates.height) {
this.setState({
keyboardHeight: e.endCoordinates.height
});
} // For down arrow button in Android keyboard
this.changeHeight(0);
};
keyboardDidHide = () => {
this.changeHeight(KEYBOARD_HEIGHT);
};
onFocus = () => {
this.changeHeight(0);
};
setFocus = () => {
this.textInput?.current?.focus();
};
changeHeight(height) {
if (Constants.isAndroid && this.state.keyboardHeight !== height) {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
this.setState({
keyboardHeight: height
});
}
}
getColorValue(color) {
if (!color) {
return;
}
return color.replace('#', '');
}
getHexColor(text) {
if (!Colors.isTransparent(text)) {
const trimmed = text.replace(/\s+/g, '');
const hex = `#${trimmed}`;
return hex;
}
return text;
}
getHexString(color) {
return _.toUpper(Colors.getHexString(color));
}
getTextColor(color) {
return Colors.isDark(color) ? Colors.white : Colors.grey10;
}
getValidColorString(text) {
if (text) {
const hex = this.getHexColor(text);
if (Colors.isValidHex(hex)) {
return {
hex,
valid: true
};
}
}
return {
undefined,
valid: false
};
}
applyColor = text => {
const {
hex,
valid
} = this.getValidColorString(text);
if (hex) {
this.setState({
color: Colors.getHSL(hex),
text,
valid
});
} else {
this.setState({
text,
valid
});
}
};
updateColor(color) {
const hex = this.getHexString(color);
const text = this.getColorValue(hex);
this.setState({
color,
text,
valid: true
});
}
resetValues() {
const {
initialColor
} = this.props;
const color = Colors.getHSL(initialColor);
const text = this.getColorValue(initialColor);
const {
valid
} = this.getValidColorString(text);
this.setState({
color,
text,
valid
});
}
onSliderValueChange = color => {
this.updateColor(color);
};
onChangeText = value => {
this.applyColor(value);
};
onDonePressed = () => {
const {
text
} = this.state;
const {
hex
} = this.getValidColorString(text);
if (hex) {
this.props.onSubmit?.(hex, this.getTextColor(hex));
this.onDismiss();
}
};
onDismiss = () => {
this.resetValues();
this.props.onDismiss?.();
};
renderHeader() {
const {
doneButtonColor,
accessibilityLabels
} = this.props;
const {
valid
} = this.state;
return <View row spread bg-white paddingH-20 style={styles.header}>
<Button link iconSource={Assets.icons.x} iconStyle={{
tintColor: Colors.grey10
}} onPress={this.onDismiss} accessibilityLabel={_.get(accessibilityLabels, 'dismissButton')} />
<Button color={doneButtonColor} disabled={!valid} link iconSource={Assets.icons.check} onPress={this.onDonePressed} accessibilityLabel={_.get(accessibilityLabels, 'doneButton')} />
</View>;
}
renderSliders() {
const {
keyboardHeight,
color
} = this.state;
const colorValue = color.a === 0 ? Colors.black : Colors.getHexString(color);
return <ColorSliderGroup initialColor={colorValue} containerStyle={[styles.sliderGroup, {
height: keyboardHeight
}]} sliderContainerStyle={styles.slider} showLabels labelsStyle={styles.label} onValueChange={this.onSliderValueChange} accessible={false} />;
}
renderPreview() {
const {
accessibilityLabels,
previewInputStyle
} = this.props;
const {
color,
text
} = this.state;
const hex = this.getHexString(color);
const textColor = this.getTextColor(hex);
const fontScale = PixelRatio.getFontScale();
const value = Colors.isTransparent(text) ? '000000' : text;
return <View style={[styles.preview, {
backgroundColor: hex
}]}>
<TouchableOpacity center onPress={this.setFocus} activeOpacity={1} accessible={false}>
<View style={styles.inputContainer}>
<Text text60 white marginL-13 marginR-5={Constants.isIOS} style={{
color: textColor,
transform: [{
scaleX: I18nManager.isRTL ? -1 : 1
}]
}} accessible={false}>
#
</Text>
<TextInput ref={this.textInput} value={value} maxLength={6} numberOfLines={1} onChangeText={this.onChangeText} style={[styles.input, {
color: textColor,
width: value ? (value.length + 1) * 16.5 * fontScale : undefined
}, Constants.isAndroid && {
padding: 0
}, previewInputStyle]} selectionColor={textColor} underlineColorAndroid="transparent" autoCorrect={false} autoComplete={'off'} autoCapitalize={'characters'} // keyboardType={'numbers-and-punctuation'} // doesn't work with `autoCapitalize`
returnKeyType={'done'} enablesReturnKeyAutomatically onFocus={this.onFocus} accessibilityLabel={accessibilityLabels?.input} />
</View>
<View style={[{
backgroundColor: textColor
}, styles.underline]} />
</TouchableOpacity>
</View>;
}
renderDialog() {
const {
visible,
dialogProps,
testID
} = this.props;
return <Dialog visible={visible} //TODO: pass all Dialog props instead
width="100%" bottom centerH onDismiss={this.onDismiss} containerStyle={styles.dialog} panDirection={PanningProvider.Directions.DOWN} testID={`${testID}.dialog`} supportedOrientations={['portrait', 'landscape', 'landscape-left', 'landscape-right']} // iOS only
{...dialogProps}>
{this.renderHeader()}
{this.renderPreview()}
{this.renderSliders()}
</Dialog>;
}
render() {
return this.renderDialog();
}
}
export default asBaseComponent(ColorPickerDialog);
const BORDER_RADIUS = 12;
const styles = StyleSheet.create({
dialog: {
backgroundColor: Colors.white,
borderTopLeftRadius: BORDER_RADIUS,
borderTopRightRadius: BORDER_RADIUS
},
preview: {
height: 200,
alignItems: 'center',
justifyContent: 'center'
},
header: {
height: 56,
borderTopLeftRadius: BORDER_RADIUS,
borderTopRightRadius: BORDER_RADIUS
},
inputContainer: {
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
marginBottom: Constants.isAndroid ? 5 : 8,
transform: [{
scaleX: I18nManager.isRTL ? -1 : 1
}]
},
input: { ...Typography.text60,
letterSpacing: 3,
transform: [{
scaleX: I18nManager.isRTL ? -1 : 1
}]
},
underline: {
height: 1.5,
width: Constants.isAndroid ? 119 : 134,
marginRight: Constants.isAndroid ? 13 : 8
},
sliderGroup: {
paddingTop: 12,
marginHorizontal: 20
},
slider: {
marginBottom: 15,
height: 26
},
label: {
marginBottom: 3
}
});