diff --git a/src/components/colorSwatch/index.js b/src/components/colorSwatch/index.js new file mode 100644 index 0000000000..78fbfd950d --- /dev/null +++ b/src/components/colorSwatch/index.js @@ -0,0 +1,235 @@ +import React, {PureComponent} from 'react'; +import {StyleSheet, Animated, Easing} from 'react-native'; +import Assets from '../../assets'; +import {Colors} from '../../style'; +import {asBaseComponent, Constants} from '../../commons/new'; +import View from '../view'; +import TouchableOpacity from '../touchableOpacity'; +import Image from '../image'; +const DEFAULT_SIZE = Constants.isTablet ? 44 : 36; +export const SWATCH_MARGIN = 12; +export const SWATCH_SIZE = DEFAULT_SIZE; +const DEFAULT_COLOR = Colors.grey30; + +/** + * @description: A color swatch component + * @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/ColorPickerScreen.tsx + * @gif: https://github.com/wix/react-native-ui-lib/blob/master/demo/showcase/ColorPalette/ColorPalette.gif?raw=true + */ +class ColorSwatch extends PureComponent { + static displayName = 'ColorSwatch'; + state = { + isSelected: new Animated.Value(0), + animatedOpacity: new Animated.Value(0.3), + animatedScale: new Animated.Value(0.5) + }; + styles = createStyles({ + ...this.props, + color: this.color + }); + layout = { + x: 0, + y: 0 + }; + componentDidMount() { + this.animateCheckmark(this.props.selected); + this.animateSwatch(1); + } + componentDidUpdate(prevProps) { + if (prevProps.selected !== this.props.selected) { + this.animateCheckmark(this.props.selected); + } + } + get color() { + const {color, modifiers} = this.props; + return color || modifiers?.color; + } + animateSwatch(newValue) { + const {animatedOpacity, animatedScale} = this.state; + Animated.parallel([ + Animated.timing(animatedOpacity, { + duration: 250, + toValue: newValue, + useNativeDriver: true + }), + Animated.spring(animatedScale, { + toValue: newValue, + // easing: Easing.bezier(0, 0, 0.58, 1), // => easeOut + bounciness: 18, + speed: 12, + delay: 170, + useNativeDriver: true + }) + ]).start(); + } + animateCheckmark(newValue = false) { + const {isSelected} = this.state; + Animated.timing(isSelected, { + duration: 150, + easing: Easing.bezier(0.165, 0.84, 0.44, 1.0), + // => easeOutQuart + toValue: Number(newValue), + delay: 50, + useNativeDriver: true + }).start(); + } + onPress = () => { + const {value, index} = this.props; + const tintColor = this.getTintColor(value); + const result = value || this.color || ''; + const hexString = Colors.getHexString(result); + this.props.onPress?.(result, { + tintColor, + index, + hexString + }); + }; + getTintColor(color) { + if (color) { + if (Colors.isTransparent(color)) { + return Colors.$iconDefault; + } + return Colors.isDark(color) ? Colors.white : Colors.grey10; + } + } + getAccessibilityInfo() { + const color = this.color || DEFAULT_COLOR; + const defaultText = !this.color ? 'default' : ''; + return { + accessible: true, + accessibilityLabel: `${defaultText} color ${Colors.getColorName(color)}`, + accessibilityState: { + selected: this.props.selected + } + }; + } + getLayout() { + return this.layout; + } + onLayout = event => { + this.layout = event.nativeEvent.layout; + }; + renderContent() { + const {style, onPress, unavailable, size = DEFAULT_SIZE, ...others} = this.props; + const {isSelected} = this.state; + const Container = onPress ? TouchableOpacity : View; + const tintColor = this.getTintColor(this.color); + return ( + + {Colors.isTransparent(this.color) && ( + + )} + {unavailable ? ( + + ) : ( + + )} + + ); + } + renderSwatch = () => { + const {animated} = this.props; + const {animatedOpacity, animatedScale} = this.state; + if (animated) { + return ( + + {this.renderContent()} + + ); + } + return this.renderContent(); + }; + render() { + return this.renderSwatch(); + } +} +export default asBaseComponent(ColorSwatch); +function createStyles({color = DEFAULT_COLOR}) { + return StyleSheet.create({ + container: { + backgroundColor: color, + borderWidth: Colors.isTransparent(color) ? undefined : 1, + borderColor: Colors.rgba(Colors.$outlineDisabledHeavy, 0.8), + margin: SWATCH_MARGIN, + overflow: 'hidden' + }, + transparentImage: { + ...StyleSheet.absoluteFillObject, + width: DEFAULT_SIZE, + height: DEFAULT_SIZE, + borderWidth: 1, + // borderRadius: BorderRadiuses.br100, + borderColor: Colors.rgba(Colors.$outlineDisabledHeavy, 0.2) + }, + unavailable: { + height: '100%', + width: 3, + transform: [ + { + rotate: '45deg' + } + ], + opacity: 0.7 + } + }); +}