├── .babelrc ├── .gitignore ├── .npmignore ├── .prettierrc ├── LICENSE ├── README.md ├── docs ├── bundle.js └── index.html ├── examples ├── CustomExample.js ├── SingleExample.js ├── SwatchesExample.js ├── index.js └── styles.css ├── index.html ├── package.json ├── src ├── components │ ├── ColorPicker.js │ ├── Draggable.js │ ├── Map.js │ └── Slider.js └── index.js ├── webpack.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/env", "@babel/react"], 3 | "plugins": ["@babel/plugin-proposal-export-default-from"] 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | npm-debug.log 3 | /dist 4 | /lib 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | examples/ 2 | src/ 3 | docs/ 4 | .babelrc 5 | Makefile 6 | webpack.config.js 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "trailingComma": "es5" 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 Lionel T. 5 | Copyright (c) 2014 Nick Williams 6 | Copyright (c) 2014 George Czabania 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ColoReact 2 | 3 | A tiny **Color Picker** for **React** [ [Demo](https://elrumordelaluz.github.io/coloreact/) ] 4 | 5 | 6 | ### Install 7 | 8 | ``` 9 | $ npm i --save coloreact 10 | ``` 11 | 12 | ### How to use 13 | 14 | ```js 15 | 16 | import ColorPicker from 'coloreact'; 17 | 18 | // ... 19 | return ( 20 | 25 | ); 26 | // ... 27 | 28 | ``` 29 | 30 | ### Custom Parts 31 | 32 | It is possible to create your own _ColorPicker_ using `Map` and `Slider`s. 33 | 34 | 35 | ```js 36 | import { Map, Slider } from 'coloreact'; 37 | 38 | // ... 39 | return ( 40 |
41 | 48 | 54 |
55 | // ... 56 | 57 | 58 | ``` 59 | 60 | ### Examples 61 | 62 | [Live examples](https://elrumordelaluz.github.io/coloreact/) | 63 | [Code](https://github.com/elrumordelaluz/coloreact/tree/master/examples) 64 | 65 | ``` 66 | $ npm start 67 | # goto http://localhost:3040/ 68 | ``` 69 | 70 | ### License and Attribution 71 | 72 | The project start as a derivate of the awesome [react-simple-colorpicker](https://github.com/WickyNilliams/react-simple-colorpicker) which is based on [react-colorpicker](https://github.com/stayradiated/react-colorpicker). Licensed MIT. 73 | 74 | ### Docs 75 | 76 | coming soon 77 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ColoReact ~ Demo 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/CustomExample.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Slider, Map } from '../src' 3 | import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' 4 | import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism' 5 | import tinycolor from 'tinycolor2' 6 | 7 | class CustomExample extends Component { 8 | constructor(props) { 9 | super(props) 10 | this.state = { 11 | h: 155, 12 | s: 50, 13 | v: 50, 14 | mapActive: false, 15 | sliderActive: false, 16 | } 17 | this._sv = this._sv.bind(this) 18 | this._h = this._h.bind(this) 19 | this.resetActive = this.resetActive.bind(this) 20 | } 21 | 22 | _sv(s, v) { 23 | this.setState({ s, v, mapActive: true }) 24 | } 25 | 26 | _h(h) { 27 | this.setState({ h, sliderActive: true }) 28 | } 29 | 30 | resetActive() { 31 | this.setState({ 32 | mapActive: false, 33 | sliderActive: false, 34 | }) 35 | } 36 | 37 | getHue() { 38 | const color = tinycolor(`hsv(${this.state.h}, 100, 100)`) 39 | // return u.toRgbString([this.state.h, 100, 100]); 40 | return color.toRgbString() 41 | } 42 | 43 | render() { 44 | const color = tinycolor( 45 | `hsv(${this.state.h}, ${this.state.s}, ${this.state.v})` 46 | ) 47 | const rgb = color.toRgbString() 48 | return ( 49 |
50 |

Custom ColorPicker

51 |
59 | 86 | 114 |
115 | 116 | {`import { Map, Slider } from 'coloreact'; 117 | 118 | 127 | 128 | `} 136 | 137 | 141 | Source 142 | {' '} 143 | |{' '} 144 | 148 | Edit 149 | 150 |
151 | ) 152 | } 153 | } 154 | 155 | export default CustomExample 156 | -------------------------------------------------------------------------------- /examples/SingleExample.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ColorPicker from '../src' 3 | import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' 4 | import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism' 5 | 6 | const SingleExample = () => { 7 | return ( 8 |
9 |

Single ColorPicker

10 | console.log('single-example color:', color.hex)} 19 | /> 20 | 21 | {`import ColorPicker from 'coloreact'; 22 | console.log(color.hex)} />`} 23 | 24 | 28 | Source 29 | {' '} 30 | |{' '} 31 | 35 | Edit 36 | 37 |
38 | ) 39 | } 40 | 41 | export default SingleExample 42 | -------------------------------------------------------------------------------- /examples/SwatchesExample.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import ColorPicker from '../src' 3 | import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' 4 | import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism' 5 | 6 | class SwatchesExample extends Component { 7 | constructor(props) { 8 | super(props) 9 | this.state = { 10 | swatches: [ 11 | 'rgb(157, 41, 177)', 12 | '#673AB7', 13 | 'rgba(182, 73, 98, 1)', 14 | '#00BCD4', 15 | 'LightSeaGreen', 16 | 'rgb(76, 175, 80)', 17 | 'rgba(8, 136, 124, .7)', 18 | '#CDDC39', 19 | ], 20 | selected: 5, 21 | } 22 | this.selectSwatch = this.selectSwatch.bind(this) 23 | this.handleChange = this.handleChange.bind(this) 24 | } 25 | 26 | selectSwatch(selected) { 27 | this.setState({ 28 | selected, 29 | }) 30 | } 31 | 32 | handleChange(color) { 33 | const swatches = [...this.state.swatches] 34 | swatches[this.state.selected] = color.rgbString 35 | this.setState({ 36 | swatches, 37 | }) 38 | } 39 | 40 | render() { 41 | const actualColor = this.state.swatches[this.state.selected] 42 | return ( 43 |
44 |

ColorPicker with Swatches

45 | 55 | 56 | {actualColor} 57 | 58 | 69 | 70 | {`import ColorPicker from 'coloreact'; 71 | `} 75 | 76 | 80 | Source 81 | {' '} 82 | |{' '} 83 | 87 | Edit 88 | 89 |
90 | ) 91 | } 92 | } 93 | 94 | export default SwatchesExample 95 | -------------------------------------------------------------------------------- /examples/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import Styles from './styles.css'; 4 | import ColorPicker, { Slider, Map } from '../src'; 5 | 6 | import SingleExample from './SingleExample'; 7 | import SwatchesExample from './SwatchesExample'; 8 | import CustomExample from './CustomExample'; 9 | 10 | class App extends Component { 11 | constructor (props) { 12 | super(props); 13 | } 14 | 15 | render () { 16 | return ( 17 |
18 |
19 |

20 | coloreact 21 |

22 |

A tiny Color Picker for React

23 |
24 | 25 | 26 | 27 |
28 | ); 29 | } 30 | } 31 | 32 | ReactDOM.render(, document.querySelector('.container')); 33 | -------------------------------------------------------------------------------- /examples/styles.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=Ubuntu+Mono); 2 | 3 | body { 4 | font-family: 'Ubuntu Mono', monospace; 5 | background-color: #f2f2f2; 6 | } 7 | 8 | pre, 9 | code { 10 | font-family: 'Ubuntu Mono', monospace; 11 | } 12 | 13 | pre { 14 | padding: .75rem 1rem !important; 15 | } 16 | 17 | pre code { 18 | line-height: 1.5; 19 | } 20 | 21 | .examples { 22 | margin-left: 10vw; 23 | margin-right: 10vw; 24 | } 25 | 26 | .examples-header { 27 | margin-bottom: 2.5em; 28 | } 29 | 30 | .example { 31 | padding: 1em 2em; 32 | border-radius: .3em; 33 | background-color: rgba(255,255,255,.5); 34 | margin-bottom: 2.5em; 35 | } 36 | 37 | .example h3 { 38 | text-align: center;; 39 | } 40 | 41 | .example__codeLink { 42 | display: inline-block; 43 | text-decoration: none; 44 | color: inherit; 45 | } 46 | 47 | .example__codeLink:hover { 48 | text-decoration: underline; 49 | } 50 | 51 | .main-title a { 52 | text-decoration: none; 53 | color: inherit; 54 | } 55 | 56 | .swatches { 57 | list-style: none; 58 | padding: 0; 59 | margin: 0; 60 | text-align: center; 61 | } 62 | .swatches li { 63 | cursor: pointer; 64 | display: inline-block; 65 | height: 2em; 66 | margin: 1px; 67 | border-width: 3px; 68 | border-style: solid; 69 | border-color: transparent; 70 | width: 2em; 71 | transition: box-shadow .3s; 72 | } 73 | 74 | .swatches li:hover { 75 | box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); 76 | } 77 | 78 | .swatches li.selected { 79 | box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); 80 | } 81 | 82 | .swatch-value { 83 | opacity: .5; 84 | display: block; 85 | text-align: center; 86 | padding: 1em; 87 | } 88 | 89 | 90 | @keyframes moveLeftRight { 91 | to { 92 | transform: translateX(50%); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ColoReact ~ Demo 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "coloreact", 3 | "version": "0.3.2", 4 | "description": "A tiny Color Picker for React", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "prepublish": "npm run lib", 8 | "start": "webpack-dev-server --mode development --open", 9 | "lib": "NODE_ENV=production babel -d lib/ src/", 10 | "build": "webpack --mode production" 11 | }, 12 | "keywords": [ 13 | "color-picker", 14 | "react", 15 | "color" 16 | ], 17 | "author": "Lionel T (elrumordelaluz.com)", 18 | "license": "MIT", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/elrumordelaluz/coloreact" 22 | }, 23 | "devDependencies": { 24 | "@babel/cli": "7.6.2", 25 | "@babel/core": "7.6.2", 26 | "@babel/plugin-proposal-export-default-from": "7.5.2", 27 | "@babel/preset-env": "7.6.2", 28 | "@babel/preset-react": "7.0.0", 29 | "babel-loader": "8.0.6", 30 | "css-loader": "3.2.0", 31 | "mini-css-extract-plugin": "0.8.0", 32 | "prop-types": "15.7.2", 33 | "react": "16.10.1", 34 | "react-dom": "16.10.1", 35 | "react-syntax-highlighter": "11.0.2", 36 | "style-loader": "1.0.0", 37 | "webpack": "4.41.0", 38 | "webpack-cli": "3.3.9", 39 | "webpack-dev-server": "3.8.1" 40 | }, 41 | "dependencies": { 42 | "hoist-non-react-statics": "3.3.0", 43 | "lodash.throttle": "4.1.1", 44 | "tinycolor2": "^1.3.0" 45 | }, 46 | "peerDependencies": { 47 | "react": ">= 15.6.1" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/components/ColorPicker.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import Slider from './Slider' 4 | import Map from './Map' 5 | import throttle from 'lodash.throttle' 6 | import tinycolor from 'tinycolor2' 7 | 8 | class ColorPicker extends Component { 9 | constructor(props) { 10 | super(props) 11 | const c = tinycolor(this.props.color).toHsv() 12 | this.state = { 13 | color: this.toPercentage(c), 14 | } 15 | 16 | this.throttle = throttle(function(fn, data) { 17 | fn(data) 18 | }, 100) 19 | 20 | this.handleSaturationValueChange = this.handleSaturationValueChange.bind( 21 | this 22 | ) 23 | this.handleHueChange = this.handleHueChange.bind(this) 24 | this.handleAlphaChange = this.handleAlphaChange.bind(this) 25 | this.showLastValue = this.showLastValue.bind(this) 26 | } 27 | 28 | componentWillReceiveProps(nextProps) { 29 | if (!tinycolor.equals(nextProps.color, this.state.color)) { 30 | const c = tinycolor(nextProps.color).toHsv() 31 | this.setState({ 32 | color: this.toPercentage(c), 33 | }) 34 | } 35 | } 36 | 37 | toPercentage(hsv) { 38 | const { h, s, v, a } = hsv 39 | return { 40 | h, 41 | s: s * 100, 42 | v: v * 100, 43 | a, 44 | } 45 | } 46 | 47 | handleHueChange(h) { 48 | const { s, v, a } = this.state.color 49 | this.update({ h, s, v, a }) 50 | } 51 | 52 | handleSaturationValueChange(s, v) { 53 | const { h, a } = this.state.color 54 | this.update({ h, s, v, a }) 55 | } 56 | 57 | handleAlphaChange(a) { 58 | const { h, s, v } = this.state.color 59 | this.update({ h, s, v, a }) 60 | } 61 | 62 | getAlpha() { 63 | return this.state.color.a === undefined ? 1 : this.state.color.a 64 | } 65 | 66 | getBackgroundHue() { 67 | return tinycolor({ 68 | h: this.state.color.h, 69 | s: 100, 70 | v: 100, 71 | }).toRgbString() 72 | } 73 | 74 | getBackgroundGradient() { 75 | const { h, s, v } = this.state.color 76 | const opaque = tinycolor({ 77 | h, 78 | s, 79 | v, 80 | a: 1, 81 | }).toRgbString() 82 | return `linear-gradient(to right, rgba(0,0,0,0) 0%, ${opaque} 100%)` 83 | } 84 | 85 | update(color) { 86 | this.setState({ color }, () => { 87 | this.throttle(this.props.onChange, this.output()) 88 | }) 89 | } 90 | 91 | output() { 92 | const c = tinycolor(this.state.color) 93 | return { 94 | hsl: c.toHsl(), 95 | hex: c.toHex(), 96 | hexString: c.toHexString(), 97 | rgb: c.toRgb(), 98 | rgbString: c.toRgbString(), 99 | } 100 | } 101 | 102 | showLastValue() { 103 | this.props.onComplete && this.props.onComplete(this.output()) 104 | } 105 | 106 | render() { 107 | const { h, s, v, a } = this.state.color 108 | return ( 109 |
113 | 142 | 143 | {this.props.opacity && ( 144 | 43 | 44 |
45 | 46 | { this.props.rect && ( 47 |
48 | )} 49 |
50 | ); 51 | } 52 | } 53 | 54 | Map.propTypes = { 55 | x: PropTypes.number.isRequired, 56 | y: PropTypes.number.isRequired, 57 | backgroundColor: PropTypes.string, 58 | className: PropTypes.string, 59 | }; 60 | 61 | Map.defaultProps = { 62 | x: 0, 63 | y: 0, 64 | backgroundColor: 'transparent', 65 | className: 'Map', 66 | }; 67 | 68 | Map.defaultStyles = { 69 | // Map 70 | map: { 71 | position: 'absolute', 72 | top: 0, 73 | bottom: 0, 74 | right: 0, 75 | left: 0, 76 | overflow: 'hidden', 77 | userSelect: 'none', 78 | }, 79 | mapActive: { 80 | cursor: 'none', 81 | }, 82 | 83 | // Pointer 84 | pointer: { 85 | position: 'absolute', 86 | width: 10, 87 | height: 10, 88 | marginLeft: -5, 89 | marginBottom: -5, 90 | borderRadius: '100%', 91 | border: '1px solid', 92 | willChange: 'auto', 93 | }, 94 | 95 | // Background 96 | bg: { 97 | top: 0, 98 | left: 0, 99 | position: 'absolute', 100 | height: '100%', 101 | width: '100%', 102 | }, 103 | bgOverlay: { 104 | display: 'block', 105 | position: 'absolute', 106 | top: 0, 107 | left: 0, 108 | bottom: 0, 109 | right: 0, 110 | background: `linear-gradient(to bottom, rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%), 111 | linear-gradient(to right, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%)`, 112 | }, 113 | }; 114 | 115 | export default draggable()(Map); 116 | -------------------------------------------------------------------------------- /src/components/Slider.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import draggable from './Draggable'; 4 | 5 | class Slider extends Component { 6 | getPointerStyles() { 7 | const { pointer } = Slider.defaultStyles; 8 | const attr = this.props.vertical ? 'bottom' : 'left'; 9 | return Object.assign({}, 10 | pointer, 11 | this.props.pointerStyle && this.props.pointerStyle, 12 | { 13 | [attr]: this.props.getPercentageValue(this.props.value) 14 | }, 15 | ); 16 | } 17 | 18 | getSliderStyles () { 19 | const { slider, verticalSlider, horizontalSlider } = Slider.defaultStyles; 20 | return Object.assign({}, 21 | slider, 22 | this.props.vertical && verticalSlider, 23 | !this.props.vertical && horizontalSlider, 24 | this.props.style && this.props.style, 25 | ); 26 | } 27 | 28 | getTrackStyles () { 29 | const { track, horizontalTrack, verticalTrack, opacityTrack, hueTrack } = Slider.defaultStyles; 30 | const background = this.props.background; 31 | return Object.assign({}, 32 | track, 33 | this.props.vertical && verticalTrack, 34 | !this.props.vertical && horizontalTrack, 35 | this.props.trackStyle && this.props.trackStyle, 36 | this.props.background && { background: this.props.background }, 37 | ); 38 | } 39 | 40 | render () { 41 | const { opacitySlider, opacitySlider__track }= Slider.defaultStyles; 42 | return ( 43 |
48 | 49 |
50 | 51 | { this.props.rect && ( 52 |
53 | )} 54 |
55 | ); 56 | } 57 | } 58 | 59 | Slider.propTypes = { 60 | value: PropTypes.number.isRequired, 61 | background: PropTypes.string, 62 | }; 63 | 64 | Slider.defaultProps = { 65 | value: 0, 66 | background: '', 67 | }; 68 | 69 | Slider.defaultStyles = { 70 | // Slider 71 | slider: { 72 | position: 'absolute', 73 | userSelect: 'none', 74 | }, 75 | horizontalSlider: { 76 | height: 8, 77 | left: 0, 78 | right: 0, 79 | height: 10, 80 | cursor: 'ew-resize', 81 | }, 82 | verticalSlider: { 83 | top: 0, 84 | bottom: 0, 85 | width: 10, 86 | cursor: 'ns-resize', 87 | }, 88 | 89 | // Track 90 | track: { 91 | background: '#efefef', 92 | position: 'absolute', 93 | }, 94 | horizontalTrack: { 95 | height: '100%', 96 | left: 0, 97 | right: 0, 98 | }, 99 | verticalTrack: { 100 | bottom: 0, 101 | top: 0, 102 | width: '100%', 103 | }, 104 | 105 | // Pointer 106 | pointer: { 107 | position: 'absolute', 108 | bottom: '50%', 109 | left: '50%', 110 | width: 16, 111 | height: 16, 112 | marginLeft: -8, 113 | marginBottom: -8, 114 | background: '#efefef', 115 | willChange: 'auto', 116 | }, 117 | }; 118 | 119 | export default draggable({ single: true })(Slider); 120 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import ColorPicker from './components/ColorPicker'; 2 | export Slider from './components/Slider'; 3 | export Map from './components/Map'; 4 | 5 | export default ColorPicker; 6 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 2 | const path = require('path') 3 | 4 | module.exports = { 5 | entry: { 6 | app: ['./examples/index.js'], 7 | }, 8 | output: { 9 | path: path.join(__dirname, './docs'), 10 | publicPath: '/docs', 11 | filename: 'bundle.js', 12 | library: 'coloreact', 13 | libraryTarget: 'umd', 14 | }, 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.js$/, 19 | exclude: /node_modules/, 20 | loader: 'babel-loader', 21 | }, 22 | { 23 | test: /\.css$/, 24 | use: ['style-loader', 'css-loader'], 25 | }, 26 | ], 27 | }, 28 | resolve: { 29 | modules: [path.join(__dirname, 'src'), 'node_modules'], 30 | }, 31 | plugins: [ 32 | new MiniCssExtractPlugin({ 33 | filename: '[name].css', 34 | }), 35 | ], 36 | optimization: { 37 | minimize: true, 38 | }, 39 | devServer: { 40 | historyApiFallback: true, 41 | contentBase: './', 42 | }, 43 | } 44 | --------------------------------------------------------------------------------