├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── example ├── .expo-shared │ └── assets.json ├── .gitignore ├── .watchmanconfig ├── App.tsx ├── app.json ├── assets │ ├── icon.png │ └── splash.png ├── babel.config.js ├── metro.config.js ├── package-lock.json ├── package.json └── tsconfig.json ├── images └── color-picker.png ├── package-lock.json ├── package.json └── src ├── color-option.js ├── icon.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | example -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 holmansv 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Native Color Palette. 2 | 3 | A react native module for simple hex color selection 4 | 5 | ![alt text](./images/color-picker.png "Logo Title Text 1") 6 | 7 | * [x] Controlled and Uncontrolled implementations 8 | * [x] Simple to use 9 | 10 | 11 | ## Getting started 12 | Install the color picker 13 | ``` 14 | npm install react-native-color-palette --save 15 | ``` 16 | 17 | ```javascript 18 | import ColorPalette from 'react-native-color-palette' 19 | 20 | const UncontrolledColorPicker = () => ( 21 | alert(`Color selected: ${color}`)} 23 | defaultColor={'#C0392B'} 24 | colors={['#C0392B', '#E74C3C', '#9B59B6', '#8E44AD', '#2980B9']} 25 | title={"Uncontrolled Color Palette:"} 26 | icon={ 27 | ︎ 28 | // Icon can just be text or ASCII 29 | } 30 | /> 31 | ) 32 | 33 | const ControlledColorPicker = () => { 34 | let selectedColor = '#C0392B'; 35 | return ( 36 | selectedColor = color} 38 | value={selectedColor} 39 | colors={['#C0392B', '#E74C3C', '#9B59B6', '#8E44AD', '#2980B9']} 40 | title={"Controlled Color Palette:"} 41 | icon={ 42 | 43 | // React-Native-Vector-Icons Example 44 | } 45 | />) 46 | } 47 | ``` 48 | Due to its Flexbox design, Color Palette will use the space you provide! 49 | 50 | ## API 51 | ### Props 52 | 53 | Color Palette accepts properties below. 54 | 55 | | Property | Type | Note | 56 | | --------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------ | 57 | | `colors` | `Array` | Array of hex color strings for rendering. ex) ['#C0392B', '#E74C3C', '#9B59B6', '#8E44AD', '#2980B9'] | 58 | | `defaultColor` | `String` | Defines initial selected color in uncontrolled component. | 59 | | `value` | `String` | Defines selected color in controlled component. | 60 | | `paletteStyles` | `ViewStyle` | Styles passed to color palette container | 61 | | `onChange` | `Function` | Callback with color (HEX string) as argument called when user confirms color selection. | 62 | | `title` | `String` | Text to display at the top of the palette. | 63 | | `titleStyles` | `TextStyle` | Inherits the default react-native Text Styles | 64 | | `icon` | `Text` or `Icon` | Selector Text or Icon to be displayed in place of checkmark. | 65 | | `scaleToWindow` | `Bool` | This will automatically scale the palette to fit 6 per line and will scale up/down for iPads/ iPhones depending on window size | 66 | -------------------------------------------------------------------------------- /example/.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "f9155ac790fd02fadcdeca367b02581c04a353aa6d5aa84409a59f6804c87acd": true, 3 | "89ed26367cdb9b771858e026f2eb95bfdb90e5ae943e716575327ec325f39c44": true 4 | } -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | npm-debug.* 4 | *.jks 5 | *.p8 6 | *.p12 7 | *.key 8 | *.mobileprovision 9 | *.orig.* 10 | web-build/ 11 | web-report/ 12 | -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /example/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { StyleSheet, Text, View } from 'react-native'; 3 | import ColorPalette from 'react-native-color-palette'; 4 | 5 | export default function App() { 6 | const [selectedColor, setSelectedColor] = useState('#C0392B'); 7 | return ( 8 | 9 | 14 | 15 | {selectedColor} 16 | 17 | 18 | ); 19 | } 20 | 21 | const styles = StyleSheet.create({ 22 | container: { 23 | flex: 1, 24 | backgroundColor: '#fff', 25 | alignItems: 'center', 26 | justifyContent: 'center', 27 | }, 28 | }); 29 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "Example", 4 | "slug": "example", 5 | "privacy": "public", 6 | "sdkVersion": "34.0.0", 7 | "platforms": [ 8 | "ios", 9 | "android", 10 | "web" 11 | ], 12 | "version": "1.0.0", 13 | "orientation": "portrait", 14 | "icon": "./assets/icon.png", 15 | "splash": { 16 | "image": "./assets/splash.png", 17 | "resizeMode": "contain", 18 | "backgroundColor": "#ffffff" 19 | }, 20 | "updates": { 21 | "fallbackToCacheTimeout": 0 22 | }, 23 | "assetBundlePatterns": [ 24 | "**/*" 25 | ], 26 | "ios": { 27 | "supportsTablet": true 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /example/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holmansv/react-native-color-palette/462ffd0995e40adb0da1ada0b9fa0327ab4cf614/example/assets/icon.png -------------------------------------------------------------------------------- /example/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holmansv/react-native-color-palette/462ffd0995e40adb0da1ada0b9fa0327ab4cf614/example/assets/splash.png -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const blacklist = require('metro-config/src/defaults/blacklist'); 3 | const pak = require('../package.json'); 4 | const escape = require('escape-string-regexp'); 5 | 6 | const dependencies = Object.keys(pak.dependencies); 7 | 8 | module.exports = { 9 | projectRoot: __dirname, 10 | watchFolders: [path.resolve(__dirname, '..')], 11 | 12 | resolver: { 13 | blacklistRE: blacklist([ 14 | new RegExp( 15 | `^${escape(path.resolve(__dirname, '..', 'node_modules'))}\\/.*$` 16 | ), 17 | new RegExp( 18 | `^${escape( 19 | path.resolve(__dirname, '..', 'docs', 'node_modules') 20 | )}\\/.*$` 21 | ), 22 | ]), 23 | 24 | providesModuleNodeModules: [ 25 | 'react-native', 26 | 'react', 27 | 'prop-types', 28 | '@babel/runtime', 29 | ...dependencies, 30 | ], 31 | }, 32 | }; -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject" 9 | }, 10 | "dependencies": { 11 | "expo": "^34.0.1", 12 | "react": "16.8.3", 13 | "react-dom": "^16.8.6", 14 | "react-native": "https://github.com/expo/react-native/archive/sdk-34.0.0.tar.gz", 15 | "react-native-color-palette": "file:..", 16 | "react-native-web": "^0.11.4" 17 | }, 18 | "devDependencies": { 19 | "@types/react": "^16.8.23", 20 | "@types/react-native": "^0.57.65", 21 | "babel-preset-expo": "^6.0.0", 22 | "typescript": "^3.4.5" 23 | }, 24 | "private": true 25 | } 26 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "jsx": "react-native", 5 | "lib": ["dom", "esnext"], 6 | "moduleResolution": "node", 7 | "noEmit": true, 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /images/color-picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holmansv/react-native-color-palette/462ffd0995e40adb0da1ada0b9fa0327ab4cf614/images/color-picker.png -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-color-palette", 3 | "version": "2.2.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/runtime": { 8 | "version": "7.6.2", 9 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.2.tgz", 10 | "integrity": "sha512-EXxN64agfUqqIGeEjI5dL5z0Sw0ZwWo1mLTi4mQowCZ42O59b7DRpZAnTC6OqdF28wMBMFKNb/4uFGrVaigSpg==", 11 | "dev": true, 12 | "requires": { 13 | "regenerator-runtime": "^0.13.2" 14 | } 15 | }, 16 | "js-tokens": { 17 | "version": "4.0.0", 18 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 19 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 20 | "dev": true 21 | }, 22 | "loose-envify": { 23 | "version": "1.4.0", 24 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 25 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 26 | "dev": true, 27 | "requires": { 28 | "js-tokens": "^3.0.0 || ^4.0.0" 29 | } 30 | }, 31 | "object-assign": { 32 | "version": "4.1.1", 33 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 34 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 35 | "dev": true 36 | }, 37 | "prop-types": { 38 | "version": "15.7.2", 39 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", 40 | "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", 41 | "dev": true, 42 | "requires": { 43 | "loose-envify": "^1.4.0", 44 | "object-assign": "^4.1.1", 45 | "react-is": "^16.8.1" 46 | } 47 | }, 48 | "react-is": { 49 | "version": "16.9.0", 50 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", 51 | "integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==", 52 | "dev": true 53 | }, 54 | "regenerator-runtime": { 55 | "version": "0.13.3", 56 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", 57 | "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", 58 | "dev": true 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-color-palette", 3 | "version": "2.2.0", 4 | "description": "A react native module for simple hex color selection", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/holmansv/react-native-color-palette.git" 12 | }, 13 | "keywords": [ 14 | "react", 15 | "react-native", 16 | "colors", 17 | "color-picker", 18 | "color-palette", 19 | "palette" 20 | ], 21 | "author": "Holman Strategic Ventures", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/holmansv/react-native-color-palette/issues" 25 | }, 26 | "homepage": "https://github.com/holmansv/react-native-color-palette#readme", 27 | "dependencies": {}, 28 | "devDependencies": { 29 | "@babel/runtime": "^7.2.0", 30 | "prop-types": "15.7.2" 31 | }, 32 | "peerDependencies": { 33 | "react": "*", 34 | "react-native": "*" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/color-option.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { TouchableOpacity, Dimensions, StyleSheet } from 'react-native' 4 | 5 | import Icon from './icon'; 6 | 7 | const { width } = Dimensions.get('window'); 8 | 9 | const ColorOption = (props) => { 10 | const { icon, color, isSelected, scaleToWindow, onColorChange } = props; 11 | let scaledWidth = width * .025; 12 | return ( 13 | onColorChange(color)} 15 | style={[ 16 | styles.colorOption, 17 | { backgroundColor: color }, 18 | scaleToWindow && { 19 | width: width * .07, 20 | height: width * .07, 21 | marginHorizontal: scaledWidth, 22 | marginVertical: scaledWidth, 23 | borderRadius: scaledWidth * 2 24 | } 25 | ]} 26 | > 27 | {isSelected && } 28 | 29 | ); 30 | } 31 | 32 | const styles = StyleSheet.create({ 33 | colorOption: { 34 | borderWidth: 1, 35 | borderColor: 'rgba(0,0,0,0.2)', 36 | alignItems: 'center', 37 | justifyContent: 'center', 38 | width: 30, 39 | height: 30, 40 | marginHorizontal: 10, 41 | marginVertical: 10, 42 | borderRadius: 15, 43 | elevation: 5, 44 | shadowOffset: { width: 2, height: 2 }, 45 | shadowColor: 'black', 46 | shadowOpacity: .25, 47 | } 48 | }); 49 | 50 | ColorOption.propTypes = { 51 | icon: PropTypes.node, 52 | color: PropTypes.string.isRequired, 53 | isSelected: PropTypes.bool.isRequired, 54 | scaleToWindow: PropTypes.bool.isRequired, 55 | onColorChange: PropTypes.func.isRequired, 56 | } 57 | 58 | export default ColorOption; -------------------------------------------------------------------------------- /src/icon.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Text } from 'react-native' 4 | 5 | const getContrastColor = hex => parseInt(hex.substring(1), 16) > 0xffffff / 2 ? '#000000' : '#FFFFFF'; 6 | 7 | const Icon = (props) => { 8 | const { icon, color } = props; 9 | if (icon) return icon; 10 | return ✔︎; 11 | } 12 | 13 | Icon.defaultProps = { 14 | icon: undefined 15 | } 16 | 17 | Icon.propTypes = { 18 | icon: PropTypes.node, 19 | color: PropTypes.string.isRequired, 20 | } 21 | 22 | export default Icon -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React, { useState, useEffect, Fragment, useCallback } from 'react'; 3 | import { View, Text, StyleSheet } from 'react-native'; 4 | 5 | import ColorOption from './color-option'; 6 | 7 | const ColorPalette = (props) => { 8 | const { 9 | colors, 10 | defaultColor, 11 | icon, 12 | onChange, 13 | paletteStyles, 14 | scaleToWindow, 15 | title, 16 | titleStyles, 17 | value, 18 | } = props; 19 | const [color, setColor] = useState(value || defaultColor); 20 | 21 | useEffect(() => { 22 | value && setColor(value); 23 | }, [value]); 24 | 25 | const onColorChange = useCallback((color) => { 26 | setColor(color); 27 | onChange(color); 28 | }, [onChange]); 29 | 30 | return ( 31 | 32 | {title} 33 | 34 | {colors.map((c) => ( 35 | 43 | ))} 44 | 45 | 46 | ); 47 | } 48 | 49 | const styles = StyleSheet.create({ 50 | titleStyles: { 51 | color: 'black', 52 | }, 53 | colorContainer: { 54 | flexDirection: 'row', 55 | flexWrap: 'wrap', 56 | alignItems: 'center', 57 | justifyContent: 'center' 58 | } 59 | }); 60 | 61 | ColorPalette.defaultProps = { 62 | colors: [ 63 | '#C0392B', '#E74C3C', '#9B59B6', '#8E44AD', '#2980B9', '#3498DB', '#1ABC9C', 64 | '#16A085', '#27AE60', '#2ECC71', '#F1C40F', '#F39C12', '#E67E22', '#D35400', 65 | '#FFFFFF', '#BDC3C7', '#95A5A6', '#7F8C8D', '#34495E', '#2C3E50', '#000000', 66 | ], 67 | defaultColor: null, 68 | onChange: () => { }, 69 | paletteStyles: {}, 70 | scaleToWindow: false, 71 | title: "Color Palette:", 72 | titleStyles: {}, 73 | value: null, 74 | }; 75 | 76 | ColorPalette.propTypes = { 77 | colors: PropTypes.arrayOf(PropTypes.string), 78 | title: PropTypes.string, 79 | onChange: PropTypes.func, 80 | defaultColor: PropTypes.string, 81 | value: PropTypes.string, 82 | paletteStyles: PropTypes.shape({}) 83 | }; 84 | 85 | export default ColorPalette; 86 | --------------------------------------------------------------------------------