├── .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 |      
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 |
--------------------------------------------------------------------------------