├── .travis.yml ├── src ├── index.js ├── utils.js ├── HsvColorPicker.js ├── SaturationValuePicker.js └── HuePicker.js ├── .gitignore ├── test ├── index.test.js └── __snapshots__ │ └── index.test.js.snap ├── babel.config.js ├── .eslintrc.js ├── setup-tests.js ├── package.json └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import HsvColorPicker from './HsvColorPicker'; 2 | 3 | export default HsvColorPicker; 4 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | const normalizeValue = (value) => { 2 | if (value < 0) return 0; 3 | if (value > 1) return 1; 4 | return value; 5 | }; 6 | 7 | export default normalizeValue; 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # npm 2 | npm-debug.* 3 | yarn-error.log 4 | node_modules 5 | 6 | # mac 7 | .DS_Store 8 | 9 | # windows 10 | Thumbs.db 11 | 12 | # webstorm 13 | .idea/ 14 | 15 | # jest 16 | coverage/ 17 | 18 | # expo 19 | .expo/* 20 | -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { mount } from 'enzyme'; 3 | import HsvColorPicker from '../src'; 4 | 5 | describe('', () => { 6 | test('renders correctly', () => { 7 | const wrapper = mount(); 8 | expect(wrapper).toMatchSnapshot(); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = (api) => { 2 | const isBabelJest = api.caller(caller => caller && caller.name === 'babel-jest'); 3 | if (isBabelJest) { 4 | return { 5 | presets: [ 6 | 'module:metro-react-native-babel-preset', 7 | ], 8 | }; 9 | } 10 | return { 11 | presets: ['babel-preset-expo'], 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | parser: 'babel-eslint', 5 | extends: 'airbnb', 6 | plugins: [ 7 | 'react', 8 | 'react-native' 9 | ], 10 | env: { 11 | 'jest': true, 12 | 'react-native/react-native': true 13 | }, 14 | rules: { 15 | 'react/prop-types': 0, 16 | // allow js file extension 17 | 'react/jsx-filename-extension': [ 18 | 'error', 19 | { 20 | extensions: ['.js', '.jsx'] 21 | } 22 | ], 23 | // for post defining style object in react-native 24 | 'no-use-before-define': ['error', { variables: false }], 25 | // react-native rules 26 | 'react-native/no-unused-styles': 2, 27 | 'react-native/split-platform-components': 2, 28 | 'react-native/no-inline-styles': 2, 29 | 'react-native/no-raw-text': 2, 30 | // allow devDependencies 31 | 'import/no-extraneous-dependencies': ['error', { devDependencies: true }] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /setup-tests.js: -------------------------------------------------------------------------------- 1 | // setup-tests.js 2 | 3 | import 'react-native'; 4 | import 'jest-enzyme'; 5 | import Adapter from 'enzyme-adapter-react-16'; 6 | import Enzyme from 'enzyme'; 7 | 8 | /** 9 | * Set up DOM in node.js environment for Enzyme to mount to 10 | */ 11 | const { JSDOM } = require('jsdom'); 12 | 13 | const jsdom = new JSDOM(''); 14 | const { window } = jsdom; 15 | 16 | function copyProps(src, target) { 17 | const props = Object.getOwnPropertyNames(src) 18 | .filter(prop => typeof target[prop] === 'undefined') 19 | .map(prop => Object.getOwnPropertyDescriptor(src, prop)); 20 | Object.defineProperties(target, props); 21 | } 22 | 23 | global.window = window; 24 | global.document = window.document; 25 | global.navigator = { 26 | userAgent: 'node.js', 27 | }; 28 | copyProps(window, global); 29 | 30 | /** 31 | * Set up Enzyme to mount to DOM, simulate events, 32 | * and inspect the DOM in tests. 33 | */ 34 | Enzyme.configure({ adapter: new Adapter() }); 35 | 36 | // Ignore React Web errors when using React Native 37 | console.error = message => message; 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-hsv-color-picker", 3 | "version": "1.0.1", 4 | "description": "a react native HSV(hue, saturation, value) color picker", 5 | "author": "Yuan Fu", 6 | "bugs": { 7 | "url": "https://github.com/yuanfux/react-native-hsv-color-picker/issues" 8 | }, 9 | "homepage": "https://github.com/yuanfux/react-native-hsv-color-picker", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/yuanfux/react-native-hsv-color-picker.git" 13 | }, 14 | "keywords": [ 15 | "react native", 16 | "react-native", 17 | "color", 18 | "picker", 19 | "color picker", 20 | "hsv color picker" 21 | ], 22 | "main": "src/index.js", 23 | "files": [ 24 | "src/*" 25 | ], 26 | "dependencies": { 27 | "react-native-linear-gradient": "git+https://github.com/wordpress-mobile/react-native-linear-gradient.git#52bf43077171cff8714ce3e0155f3ebb7f55bc37", 28 | "tinycolor2": "^1.4.1" 29 | }, 30 | "devDependencies": { 31 | "@babel/core": "^7.2.2", 32 | "@babel/runtime": "^7.3.1", 33 | "babel-eslint": "^10.0.1", 34 | "babel-jest": "^24.0.0", 35 | "babel-preset-expo": "~8.0.0", 36 | "enzyme": "^3.10.0", 37 | "enzyme-adapter-react-16": "^1.14.0", 38 | "enzyme-to-json": "^3.3.5", 39 | "escape-regex-string": "^1.0.6", 40 | "eslint": "^5.13.0", 41 | "eslint-config-airbnb": "^17.1.0", 42 | "eslint-plugin-import": "^2.16.0", 43 | "eslint-plugin-jsx-a11y": "^6.2.1", 44 | "eslint-plugin-react": "^7.12.4", 45 | "eslint-plugin-react-native": "^3.6.0", 46 | "expo": "~36.0.0", 47 | "expo-keep-awake": "^8.0.0", 48 | "jest": "^24.0.0", 49 | "jest-enzyme": "^7.0.2", 50 | "jest-expo": "^36.0.1", 51 | "jsdom": "^15.1.1", 52 | "react": "~16.9.0", 53 | "react-dom": "~16.9.0", 54 | "react-native": "https://github.com/expo/react-native/archive/sdk-36.0.1.tar.gz" 55 | }, 56 | "scripts": { 57 | "start": "npx expo-cli start --config \"./demo/app.json\" -c", 58 | "eject": "npx expo-cli eject --config \"./demo/app.json\"", 59 | "test:lint": "npx eslint \"src/*.js\"", 60 | "test": "npm run test:lint && npx jest" 61 | }, 62 | "jest": { 63 | "preset": "jest-expo", 64 | "snapshotSerializers": [ 65 | "enzyme-to-json/serializer" 66 | ], 67 | "setupFilesAfterEnv": [ 68 | "/setup-tests.js" 69 | ], 70 | "modulePathIgnorePatterns": [ 71 | "/demo/" 72 | ], 73 | "collectCoverage": true 74 | }, 75 | "license": "MIT" 76 | } 77 | -------------------------------------------------------------------------------- /src/HsvColorPicker.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { 3 | View, 4 | StyleSheet, 5 | Dimensions, 6 | } from 'react-native'; 7 | import HuePicker from './HuePicker'; 8 | import SaturationValuePicker from './SaturationValuePicker'; 9 | 10 | export default class HsvColorPicker extends Component { 11 | constructor(props) { 12 | super(props); 13 | this.satValPicker = React.createRef(); 14 | } 15 | 16 | getCurrentColor() { 17 | return this.satValPicker.current.getCurrentColor(); 18 | } 19 | 20 | render() { 21 | const maxWidth = Dimensions.get('window').width - 32; 22 | const { 23 | containerStyle = {}, 24 | huePickerContainerStyle = {}, 25 | huePickerBorderRadius = 0, 26 | huePickerHue = 0, 27 | huePickerBarWidth = maxWidth, 28 | huePickerBarHeight = 12, 29 | huePickerSliderSize = 24, 30 | onHuePickerDragStart, 31 | onHuePickerDragMove, 32 | onHuePickerDragEnd, 33 | onHuePickerDragTerminate, 34 | onHuePickerPress, 35 | satValPickerContainerStyle = {}, 36 | satValPickerBorderRadius = 0, 37 | satValPickerSize = { width: maxWidth, height: 200 }, 38 | satValPickerSliderSize = 24, 39 | satValPickerHue = 0, 40 | satValPickerSaturation = 1, 41 | satValPickerValue = 1, 42 | onSatValPickerDragStart, 43 | onSatValPickerDragMove, 44 | onSatValPickerDragEnd, 45 | onSatValPickerDragTerminate, 46 | onSatValPickerPress, 47 | } = this.props; 48 | return ( 49 | 50 | 65 | 78 | 79 | ); 80 | } 81 | } 82 | 83 | const styles = StyleSheet.create({ 84 | container: { 85 | justifyContent: 'center', 86 | alignItems: 'center', 87 | }, 88 | }); 89 | -------------------------------------------------------------------------------- /src/SaturationValuePicker.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { 3 | View, 4 | PanResponder, 5 | StyleSheet, 6 | } from 'react-native'; 7 | import LinearGradient from 'react-native-linear-gradient'; 8 | import tinycolor from 'tinycolor2'; 9 | import normalizeValue from './utils'; 10 | 11 | export default class SaturationValuePicker extends Component { 12 | constructor(props) { 13 | super(props); 14 | 15 | this.panResponder = PanResponder.create({ 16 | onStartShouldSetPanResponder: () => true, 17 | onStartShouldSetPanResponderCapture: () => true, 18 | onMoveShouldSetPanResponder: () => true, 19 | onMoveShouldSetPanResponderCapture: () => true, 20 | onPanResponderGrant: (evt, gestureState) => { 21 | const { onPress } = this.props; 22 | const { saturation, value } = this.computeSatValPress(evt); 23 | this.dragStartValue = { 24 | saturation, 25 | value, 26 | }; 27 | 28 | if (onPress) { 29 | onPress({ 30 | ...this.computeSatValPress(evt), 31 | nativeEvent: evt.nativeEvent, 32 | }); 33 | } 34 | 35 | this.fireDragEvent('onDragStart', gestureState); 36 | }, 37 | onPanResponderMove: (evt, gestureState) => { 38 | this.fireDragEvent('onDragMove', gestureState); 39 | }, 40 | onPanResponderTerminationRequest: () => true, 41 | onPanResponderRelease: (evt, gestureState) => { 42 | this.fireDragEvent('onDragEnd', gestureState); 43 | }, 44 | onPanResponderTerminate: (evt, gestureState) => { 45 | this.fireDragEvent('onDragTerminate', gestureState); 46 | }, 47 | onShouldBlockNativeResponder: () => true, 48 | }); 49 | } 50 | 51 | getCurrentColor() { 52 | const { hue = 0, saturation = 1, value = 1 } = this.props; 53 | return tinycolor(`hsv ${hue} ${saturation} ${value}`).toHexString(); 54 | } 55 | 56 | computeSatValDrag(gestureState) { 57 | const { dx, dy } = gestureState; 58 | const { size } = this.props; 59 | const { saturation, value } = this.dragStartValue; 60 | const diffx = dx / size.width; 61 | const diffy = dy / size.height; 62 | return { 63 | saturation: normalizeValue(saturation + diffx), 64 | value: normalizeValue(value - diffy), 65 | }; 66 | } 67 | 68 | computeSatValPress(event) { 69 | const { nativeEvent } = event; 70 | const { locationX, locationY } = nativeEvent; 71 | const { size } = this.props; 72 | return { 73 | saturation: normalizeValue(locationX / size.width), 74 | value: 1 - normalizeValue(locationY / size.height), 75 | }; 76 | } 77 | 78 | fireDragEvent(eventName, gestureState) { 79 | const { [eventName]: event } = this.props; 80 | if (event) { 81 | event({ 82 | ...this.computeSatValDrag(gestureState), 83 | gestureState, 84 | }); 85 | } 86 | } 87 | 88 | render() { 89 | const { 90 | size, 91 | sliderSize = 24, 92 | hue = 0, 93 | value = 1, 94 | saturation = 1, 95 | containerStyle = {}, 96 | borderRadius = 0, 97 | } = this.props; 98 | 99 | return ( 100 | 111 | 122 | 128 | 134 | 135 | 136 | 153 | 154 | ); 155 | } 156 | } 157 | 158 | const styles = StyleSheet.create({ 159 | container: { 160 | justifyContent: 'center', 161 | alignItems: 'center', 162 | }, 163 | gradientContainer: { 164 | overflow: 'hidden', 165 | }, 166 | slider: { 167 | top: 0, 168 | left: 0, 169 | position: 'absolute', 170 | borderColor: '#fff', 171 | }, 172 | }); 173 | -------------------------------------------------------------------------------- /src/HuePicker.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { 3 | Animated, 4 | View, 5 | PanResponder, 6 | StyleSheet, 7 | } from 'react-native'; 8 | import LinearGradient from 'react-native-linear-gradient'; 9 | import tinycolor from 'tinycolor2'; 10 | import normalizeValue from './utils'; 11 | 12 | export default class HuePicker extends Component { 13 | constructor(props) { 14 | super(props); 15 | this.hueColors = [ 16 | '#ff0000', 17 | '#ffff00', 18 | '#00ff00', 19 | '#00ffff', 20 | '#0000ff', 21 | '#ff00ff', 22 | '#ff0000', 23 | ]; 24 | this.sliderX = new Animated.Value(props.barHeight * props.hue / 360); 25 | this.panResponder = PanResponder.create({ 26 | onStartShouldSetPanResponder: () => true, 27 | onStartShouldSetPanResponderCapture: () => true, 28 | onMoveShouldSetPanResponder: () => true, 29 | onMoveShouldSetPanResponderCapture: () => true, 30 | onPanResponderGrant: (evt, gestureState) => { 31 | const { onPress } = this.props; 32 | this.dragStartValue = this.computeHueValuePress(evt); 33 | 34 | if (onPress) { 35 | onPress({ 36 | hue: this.computeHueValuePress(evt), 37 | nativeEvent: evt.nativeEvent, 38 | }); 39 | } 40 | 41 | this.fireDragEvent('onDragStart', gestureState); 42 | }, 43 | onPanResponderMove: (evt, gestureState) => { 44 | this.fireDragEvent('onDragMove', gestureState); 45 | }, 46 | onPanResponderTerminationRequest: () => true, 47 | onPanResponderRelease: (evt, gestureState) => { 48 | this.fireDragEvent('onDragEnd', gestureState); 49 | }, 50 | onPanResponderTerminate: (evt, gestureState) => { 51 | this.fireDragEvent('onDragTerminate', gestureState); 52 | }, 53 | onShouldBlockNativeResponder: () => true, 54 | }); 55 | } 56 | 57 | componentDidUpdate(prevProps) { 58 | const { hue = 0, barWidth = 200, sliderSize = 24 } = this.props; 59 | const borderWidth = sliderSize / 10; 60 | if ( 61 | prevProps.hue !== hue 62 | || prevProps.barWidth !== barWidth 63 | ) { 64 | this.sliderX.setValue((barWidth - sliderSize + borderWidth) * hue / 360); 65 | } 66 | } 67 | 68 | getContainerStyle() { 69 | const { sliderSize = 24, barHeight = 12, containerStyle = {} } = this.props; 70 | const paddingLeft = sliderSize / 2; 71 | const paddingTop = sliderSize - barHeight > 0 ? (sliderSize - barHeight) / 2 : 0; 72 | return [ 73 | styles.container, 74 | containerStyle, 75 | { 76 | paddingTop, 77 | paddingBottom: paddingTop, 78 | paddingLeft, 79 | paddingRight: paddingLeft, 80 | }, 81 | ]; 82 | } 83 | 84 | getCurrentColor() { 85 | const { hue = 0 } = this.props; 86 | return tinycolor(`hue ${hue} 1.0 0.5`).toHexString(); 87 | } 88 | 89 | computeHueValueDrag(gestureState) { 90 | const { dx } = gestureState; 91 | const { barWidth = 200 } = this.props; 92 | const { dragStartValue } = this; 93 | const diff = dx / barWidth; 94 | const updatedHue = normalizeValue(dragStartValue / 360 + diff) * 360; 95 | return updatedHue; 96 | } 97 | 98 | computeHueValuePress(event) { 99 | const { nativeEvent } = event; 100 | const { locationX } = nativeEvent; 101 | const { barWidth = 200 } = this.props; 102 | const updatedHue = normalizeValue(locationX / barWidth) * 360; 103 | return updatedHue; 104 | } 105 | 106 | fireDragEvent(eventName, gestureState) { 107 | const { [eventName]: event } = this.props; 108 | if (event) { 109 | event({ 110 | hue: this.computeHueValueDrag(gestureState), 111 | gestureState, 112 | }); 113 | } 114 | } 115 | 116 | firePressEvent(event) { 117 | const { onPress } = this.props; 118 | if (onPress) { 119 | onPress({ 120 | hue: this.computeHueValuePress(event), 121 | nativeEvent: event.nativeEvent, 122 | }); 123 | } 124 | } 125 | 126 | render() { 127 | const { hueColors } = this; 128 | const { 129 | sliderSize = 24, 130 | barWidth = 200, 131 | barHeight = 12, 132 | borderRadius = 0, 133 | } = this.props; 134 | const borderWidth = sliderSize / 10; 135 | return ( 136 | 143 | 151 | 155 | 156 | 171 | 172 | ); 173 | } 174 | } 175 | 176 | const styles = StyleSheet.create({ 177 | container: { 178 | justifyContent: 'center', 179 | alignItems: 'center', 180 | }, 181 | slider: { 182 | position: 'absolute', 183 | backgroundColor: '#fff', 184 | shadowColor: '#000', 185 | shadowOffset: { 186 | width: 0, 187 | height: 7, 188 | }, 189 | shadowOpacity: 0.43, 190 | shadowRadius: 10, 191 | elevation: 5, 192 | }, 193 | }); 194 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # react-native-hsv-color-picker 3 | > a react native HSV(hue, saturation, value) color picker 4 | 5 | ![npm](https://img.shields.io/npm/v/react-native-hsv-color-picker.svg?style=flat-square) ![](https://img.shields.io/travis/yuanfux/react-native-hsv-color-picker/master.svg?style=flat-square) ![GitHub issues](https://img.shields.io/github/issues/yuanfux/react-native-hsv-color-picker.svg?style=flat-square) ![NPM](https://img.shields.io/npm/l/react-native-hsv-color-picker.svg?style=flat-square) ![](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square) ![](https://img.shields.io/maintenance/yes/2020.svg?style=flat-square) 6 | 7 |

8 | 9 |

10 | 11 | ## Preview 12 | [View Live Demo](https://snack.expo.io/@fuyuanx/react-native-hsv-color-picker) 13 | 14 | `react-native-hsv-color-picker` is a React Native component for building an [HSV](https://en.wikipedia.org/wiki/HSL_and_HSV) (hue, saturation, value) color picker. 15 | 16 | Highlighted Features: 17 | 1. **Real Rendering** - no image involved / all colors are truly rendered 18 | 2. **Performance** - empowered by native gradient lib 19 | 4. **Fully Controlled** - no inner state involved 20 | 3. **Fully Supported** - support both React Native & Expo projects 21 | 22 | ## Install 23 | ```bash 24 | $ npm install react-native-hsv-color-picker --save 25 | ``` 26 | 27 | ### Use with Expo Project 28 | > You are all set. 29 | 30 | ### Use with React Native Project 31 | > `react-native-hsv-color-picker` is powered by the lib [`expo-linear-gradient`](https://github.com/react-native-community/react-native-linear-gradient). Besides above command, you have to follow this [Instruction](https://github.com/expo/expo/tree/master/packages/expo-linear-gradient#installation-in-bare-react-native-projects) to add relevant dependencies to your project. 32 | 33 | ## Usage 34 | > a minimally-configured HSV color picker 35 | ```js 36 | import React from 'react'; 37 | import { StyleSheet, View } from 'react-native'; 38 | import HsvColorPicker from 'react-native-hsv-color-picker'; 39 | 40 | export default class Example extends React.Component { 41 | constructor(props) { 42 | super(props); 43 | this.state = { 44 | hue: 0, 45 | sat: 0, 46 | val: 1, 47 | }; 48 | this.onSatValPickerChange = this.onSatValPickerChange.bind(this); 49 | this.onHuePickerChange = this.onHuePickerChange.bind(this); 50 | } 51 | 52 | onSatValPickerChange({ saturation, value }) { 53 | this.setState({ 54 | sat: saturation, 55 | val: value, 56 | }); 57 | } 58 | 59 | onHuePickerChange({ hue }) { 60 | this.setState({ 61 | hue, 62 | }); 63 | } 64 | 65 | render() { 66 | const { hue, sat, val } = this.state; 67 | return ( 68 | 69 | 79 | 80 | ); 81 | } 82 | } 83 | 84 | const styles = StyleSheet.create({ 85 | container: { 86 | flex: 1, 87 | backgroundColor: '#fff', 88 | alignItems: 'center', 89 | justifyContent: 'center', 90 | }, 91 | }); 92 | ``` 93 | 94 | 95 | ## Props 96 | #### Basic Props 97 | | Prop | Type | Default | Description | 98 | |--|--|--| -- | 99 | | `containerStyle` | ViewPropTypes.style | `{}` | style for the outmost container | 100 | | `huePickerContainerStyle` | ViewPropTypes.style | `{}` | style for the hue picker container | 101 | | `huePickerBorderRadius` | number | `0` | border radius for the hue picker | 102 | | `huePickerHue` | number | `0` | hue value(`h` in `hsv`, ranged in `[0, 360]`) for the hue picker | 103 | | `huePickerBarWidth` | number | `12` | bar width for the hue picker | 104 | | `huePickerBarHeight` | number | `200` | bar height for the hue picker | 105 | | `huePickerSliderSize` | number | `24` | slider diameter for the hue picker | 106 | | `satValPickerContainerStyle` | ViewPropTypes.style | `{}` | style for the saturation & value picker container | 107 | | `satValPickerBorderRadius` | number | `0` | border radius for the saturation & value picker | 108 | | `satValPickerSize` | number | `200` | width / height for the saturation & value picker | 109 | | `satValPickerSliderSize` | number | `24` | slider diameter for the saturation & value picker | 110 | | `satValPickerHue` | number | `0` | hue value(`h` in `hsv`, ranged in `[0, 360]`) for the saturation & value picker | 111 | | `satValPickerSaturation` | number | `1` | saturation value(`s` in `hsv`, ranged in `[0, 1]`) for the saturation & value picker | 112 | | `satValPickerValue` | number | `1` | value(`v` in `hsv`, ranged in `[0, 1]`) for the saturation & value picker | 113 | 114 | #### Callback Props 115 | | Prop | Callback Params | Description | 116 | |--|--| -- | 117 | | `onHuePickerDragStart` | {
    hue: number,
    gestureState: [gestureState](https://facebook.github.io/react-native/docs/panresponder)
} | called when hue picker starts to drag | 118 | | `onHuePickerDragMove` | {
    hue: number,
    gestureState: [gestureState](https://facebook.github.io/react-native/docs/panresponder)
} | called when hue picker is dragging | 119 | | `onHuePickerDragEnd` | {
    hue: number,
    gestureState: [gestureState](https://facebook.github.io/react-native/docs/panresponder)
} | called when hue picker stops dragging | 120 | | `onHuePickerDragTerminate` | {
    hue: number,
    gestureState: [gestureState](https://facebook.github.io/react-native/docs/panresponder)
} | called when another component has become the responder | 121 | | `onHuePickerPress` | {
    hue: number,
    nativeEvent: [nativeEvent](https://facebook.github.io/react-native/docs/panresponder)
} | called when hue picker is pressed | 122 | | `onSatValPickerDragStart` | {
    saturation: number,
    value: number,
    gestureState: [gestureState](https://facebook.github.io/react-native/docs/panresponder)
} | called when saturation & value picker starts to drag | 123 | | `onSatValPickerDragMove` | {
    saturation: number,
    value: number,
    gestureState: [gestureState](https://facebook.github.io/react-native/docs/panresponder)
} | called when saturation & value picker is dragging | 124 | | `onSatValPickerDragEnd` | {
    saturation: number,
    value: number,
    gestureState: [gestureState](https://facebook.github.io/react-native/docs/panresponder)
} | called when saturation & value picker stops dragging | 125 | | `onSatValPickerDragTerminate` | {
    saturation: number,
    value: number,
    gestureState: [gestureState](https://facebook.github.io/react-native/docs/panresponder)
} | called when another component has become the responder | 126 | | `onSatValPickerPress` | {
    saturation: number,
    value: number,
    nativeEvent: [nativeEvent](https://facebook.github.io/react-native/docs/panresponder)
} | called when saturation & value picker is pressed | 127 | 128 | ## Methods 129 | #### Instance Methods 130 | > Use [`ref`](https://facebook.github.io/react/docs/refs-and-the-dom.html) to call instance methods 131 | 132 | | Method | Params | Return Type| Description | 133 | |--|:--:|:--:| -- | 134 | | `getCurrentColor` | - | `string` | get current picked color in hex format | 135 | 136 | 137 | 138 | ## Dev 139 | > The `demo` folder contains a standalone Expo project, which can be used for dev purpose. 140 | 141 | > react-native - 0.61
142 | > react - 16.9 143 | 144 | 1. Start Expo 145 | ```bash 146 | $ npm install 147 | 148 | $ npm start 149 | ``` 150 | 151 | 2. Run on `simulator` 152 | - type the following command in the `Terminal` after the project is booted up 153 | - `a` for `android` simulator 154 | - `i` for `iOS` simulator 155 | 156 | 3. Run on `device` 157 | - require the installation of corresponding [`iOS client`](https://itunes.apple.com/app/apple-store/id982107779) or [`android client`](https://play.google.com/store/apps/details?id=host.exp.exponent&referrer=www) on the device 158 | - scan the QR code from `Terminal` using the device 159 | 160 | 4. More on [`Expo Guide`](https://docs.expo.io/versions/v36.0.0/) 161 | 162 | ## Related 163 | - scaffolded by [**react-native-component-cli**](https://github.com/yuanfux/react-native-component-cli) 164 | 165 | ## License 166 | MIT 167 | -------------------------------------------------------------------------------- /test/__snapshots__/index.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` renders correctly 1`] = ` 4 | 30 | 42 | 54 | 68 | 83 | 98 | 101 | 135 | 169 | ) 170 | accessible={true} 171 | colors={ 172 | Array [ 173 | 4294967295, 174 | 4294901760, 175 | ] 176 | } 177 | endPoint={ 178 | Array [ 179 | 1, 180 | 0.5, 181 | ] 182 | } 183 | focusable={true} 184 | onClick={[Function]} 185 | onResponderGrant={[Function]} 186 | onResponderMove={[Function]} 187 | onResponderRelease={[Function]} 188 | onResponderTerminate={[Function]} 189 | onResponderTerminationRequest={[Function]} 190 | onStartShouldSetResponder={[Function]} 191 | startPoint={ 192 | Array [ 193 | 0, 194 | 0.5, 195 | ] 196 | } 197 | style={ 198 | Object { 199 | "borderRadius": 0, 200 | } 201 | } 202 | > 203 | 236 | 269 | 277 | 285 | ) 286 | colors={ 287 | Array [ 288 | 0, 289 | 4278190080, 290 | ] 291 | } 292 | > 293 | 306 | 319 | 327 | 335 | 336 | 337 | 338 | )> 339 | 340 | 341 | 342 | 343 | )> 344 | 345 | 346 | 347 | 386 | 425 | 426 | 427 | 428 | 429 | 442 | 459 | 476 | 479 | 506 | 533 | ) 534 | accessible={true} 535 | colors={ 536 | Array [ 537 | 4294901760, 538 | 4294967040, 539 | 4278255360, 540 | 4278255615, 541 | 4278190335, 542 | 4294902015, 543 | 4294901760, 544 | ] 545 | } 546 | focusable={true} 547 | onClick={[Function]} 548 | onResponderGrant={[Function]} 549 | onResponderMove={[Function]} 550 | onResponderRelease={[Function]} 551 | onResponderTerminate={[Function]} 552 | onResponderTerminationRequest={[Function]} 553 | onStartShouldSetResponder={[Function]} 554 | style={ 555 | Object { 556 | "borderRadius": 0, 557 | } 558 | } 559 | > 560 | 592 | 624 | 632 | 640 | 641 | 642 | 643 | )> 644 | 645 | 646 | 647 | 682 | 713 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | `; 753 | --------------------------------------------------------------------------------