├── .babelrc
├── .env
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── jsconfig.json
├── package.json
├── public
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
├── rollup.config.js
├── src
├── App.js
├── index.js
└── lib
│ ├── assets
│ └── images
│ │ └── alpha-background.svg
│ ├── components
│ ├── ColorPicker
│ │ ├── Area
│ │ │ ├── Alpha
│ │ │ │ └── index.jsx
│ │ │ ├── GradientPoints
│ │ │ │ ├── GradientPoint
│ │ │ │ │ └── index.jsx
│ │ │ │ └── index.jsx
│ │ │ ├── Hue
│ │ │ │ └── index.jsx
│ │ │ ├── Picking
│ │ │ │ └── index.jsx
│ │ │ ├── Preview
│ │ │ │ └── index.jsx
│ │ │ └── index.jsx
│ │ ├── Gradient
│ │ │ ├── GradientControls
│ │ │ │ └── index.jsx
│ │ │ └── index.jsx
│ │ ├── Preview
│ │ │ ├── Hex
│ │ │ │ └── index.jsx
│ │ │ ├── RGB
│ │ │ │ ├── index.jsx
│ │ │ │ └── item
│ │ │ │ │ └── index.jsx
│ │ │ └── index.jsx
│ │ ├── Solid
│ │ │ └── index.jsx
│ │ └── index.jsx
│ └── UI
│ │ ├── Input
│ │ ├── index.jsx
│ │ └── index.scss
│ │ └── index.jsx
│ ├── helpers
│ ├── calculateDegree.js
│ ├── changePicker.js
│ ├── generateStyles.js
│ ├── getAlpha.js
│ ├── getHue.js
│ ├── getRgbByHue.js
│ ├── getRightValue.js
│ ├── hexToRgb.js
│ ├── hsvToRgb.js
│ ├── index.js
│ ├── rgbToHex.js
│ ├── rgbToHsv.js
│ ├── setRgba.js
│ └── updateGradientActivePercent.js
│ ├── hooks
│ ├── index.js
│ ├── mouseEvents.js
│ └── useMount.js
│ ├── index.js
│ └── index.scss
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env", { "modules": false }],
4 | ["@babel/preset-react"]
5 | ],
6 | "plugins": [
7 | [
8 | "@babel/plugin-proposal-object-rest-spread"
9 | ],
10 | [
11 | "@babel/plugin-syntax-optional-catch-binding"
12 | ],
13 | [
14 | "@babel/plugin-proposal-optional-catch-binding"
15 | ],
16 | [
17 | "@babel/plugin-proposal-nullish-coalescing-operator"
18 | ]
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | SKIP_PREFLIGHT_CHECK=true
2 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | *.test.tsx
2 | Test/
3 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es6": true
5 | },
6 | "extends": [
7 | "plugin:react/recommended",
8 | "airbnb"
9 | ],
10 | "globals": {
11 | "Atomics": "readonly",
12 | "SharedArrayBuffer": "readonly"
13 | },
14 | "parserOptions": {
15 | "ecmaFeatures": {
16 | "jsx": true,
17 | "modules": true
18 | },
19 | "ecmaVersion": 2020,
20 | "sourceType": "module"
21 | },
22 | "plugins": [
23 | "react-hooks",
24 | "react",
25 | "babel"
26 | ],
27 | "rules": {
28 | // eslint
29 | "indent": ["error", 4, { "SwitchCase": 1 }],
30 | "semi": [2, "always"],
31 | "camelcase": 0,
32 | "max-len": [2, 200, 4],
33 | "arrow-parens": [2, "as-needed"],
34 | "consistent-return": 0,
35 | "import/extensions": 0,
36 | "import/no-unresolved": 0,
37 | "import/prefer-default-export": 0,
38 | "import/no-cycle": 0,
39 | "no-bitwise": 0,
40 | "no-plusplus": 0,
41 | "no-shadow": 0,
42 | "no-param-reassign": 0,
43 | "no-restricted-syntax": 0,
44 | "no-case-declarations": 0,
45 | "no-mixed-operators": 0,
46 | "no-prototype-builtins": 0,
47 | "no-restricted-properties": 0,
48 | "no-return-assign": 0,
49 | "no-nested-ternary": 0,
50 | "no-unused-expressions": 0,
51 | // babel plugin
52 | "babel/no-unused-expressions": 0,
53 | // react plugin
54 | "react/jsx-indent": ["error", 4],
55 | "react/jsx-indent-props": ["error", 4],
56 | "react/button-has-type": 0,
57 | "react/prop-types": 0,
58 | "react/jsx-props-no-spreading": 0,
59 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx", ".tsx"] }],
60 | "react/jsx-first-prop-new-line": [2, "multiline"],
61 | "react-hooks/rules-of-hooks": 2,
62 | "react-hooks/exhaustive-deps": 2,
63 | // jsx-a11y
64 | "jsx-a11y/no-static-element-interactions": 0,
65 | "jsx-a11y/no-noninteractive-element-interactions": 0,
66 | "jsx-a11y/click-events-have-key-events": 0,
67 | "jsx-a11y/label-has-associated-control": 0,
68 | //eslint-import-plugin
69 | "import/order": [
70 | "error",
71 | {
72 | "groups": ["builtin", "external", "internal"],
73 | "pathGroups": [
74 | {
75 | "pattern": "react",
76 | "group": "external",
77 | "position": "before"
78 | }
79 | ],
80 | "pathGroupsExcludedImportTypes": ["react"],
81 | "newlines-between": "always"
82 | }
83 | ]
84 | },
85 |
86 | "settings": {
87 | "import/resolver": {
88 | "node": {
89 | "paths": ["src"]
90 | }
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 | /dist
14 |
15 | # misc
16 | .idea
17 | .DS_Store
18 | .env.local
19 | .env.development.local
20 | .env.test.local
21 | .env.production.local
22 | package-lock.json
23 |
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .idea
2 | build
3 | src
4 | public
5 | .babelrc
6 | .env
7 | .eslintignore
8 | .eslintrc
9 | jsconfig.json
10 | rollup.config.js
11 | yarn.lock
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Arthur Hayrapetyan
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 Color Gradient Picker
2 |
3 | ## Table of Contents
4 |
5 | * [Installation](#installation)
6 | * [Examples](#examples)
7 | * [Demos](#demo)
8 |
9 | ## Installation
10 |
11 | To install, you can use [npm](https://npmjs.org/) or [yarn](https://yarnpkg.com):
12 |
13 |
14 | $ npm install react-color-gradient-picker
15 | $ yarn add react-color-gradient-picker
16 |
17 | ## Examples
18 |
19 | Here is a simple examples of react-color-gradient-picker being used in an app:
20 |
21 | ### Color Picker
22 | ```jsx
23 | import React, { useState } from 'react';
24 | import ReactDOM from 'react-dom';
25 | import { ColorPicker } from 'react-color-gradient-picker';
26 | import 'react-color-gradient-picker/dist/index.css';
27 |
28 | const color = {
29 | red: 255,
30 | green: 0,
31 | blue: 0,
32 | alpha: 1,
33 | };
34 |
35 | function App() {
36 | const [colorAttrs, setColorAttrs] = useState(color);
37 |
38 | const onChange = (colorAttrs) => {
39 | setColorAttrs(colorAttrs);
40 | };
41 |
42 | return (
43 |
49 |
50 | );
51 | }
52 |
53 | ReactDOM.render(, document.getElementById('app-id'));
54 | ```
55 |
56 | ### Gradient Color Picker
57 | ```jsx
58 | import React, { useState } from 'react';
59 | import ReactDOM from 'react-dom';
60 | import { ColorPicker } from 'react-color-gradient-picker';
61 | import 'react-color-gradient-picker/dist/index.css';
62 |
63 | const gradient = {
64 | points: [
65 | {
66 | left: 0,
67 | red: 0,
68 | green: 0,
69 | blue: 0,
70 | alpha: 1,
71 | },
72 | {
73 | left: 100,
74 | red: 255,
75 | green: 0,
76 | blue: 0,
77 | alpha: 1,
78 | },
79 | ],
80 | degree: 0,
81 | type: 'linear',
82 | };
83 |
84 | function App() {
85 | const [gradientAttrs, setGradientAttrs] = useState(gradient);
86 |
87 | const onChange = (gradientAttrs) => {
88 | setGradientAttrs(gradientAttrs);
89 | };
90 |
91 | return (
92 |
99 |
100 | );
101 | }
102 |
103 | ReactDOM.render(, document.getElementById('app-id'));
104 | ```
105 | ## Demo
106 |
107 | * [Solid and gradient pickers live demo](https://arthay.github.io/react-color-gradient-picker/)
108 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./src",
4 | "paths": {
5 | "~/*": ["*"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-color-gradient-picker",
3 | "description": "Color picker for react",
4 | "version": "0.1.2",
5 | "main": "./dist/index-cjs.js",
6 | "module": "./dist/index-es.js",
7 | "browser": "./dist/index-cjs.js",
8 | "style": "./dist/index.css",
9 | "author": "Arthur Hayrapetyan",
10 | "license": "MIT",
11 | "homepage": "https://arthay.github.io/react-color-gradient-picker/",
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/arthay/react-color-gradient-picker.git"
15 | },
16 | "bugs": {
17 | "url": "https://github.com/arthay/react-color-gradient-picker/issues"
18 | },
19 | "peerDependencies": {
20 | "react": "^16.13.1",
21 | "react-dom": "^16.13.1"
22 | },
23 | "devDependencies": {
24 | "@babel/cli": "^7.8.4",
25 | "@babel/core": "^7.9.6",
26 | "@babel/plugin-proposal-class-properties": "^7.8.3",
27 | "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3",
28 | "@babel/plugin-proposal-object-rest-spread": "^7.9.6",
29 | "@babel/plugin-proposal-optional-catch-binding": "^7.8.3",
30 | "@babel/plugin-syntax-dynamic-import": "^7.8.3",
31 | "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
32 | "@babel/polyfill": "^7.8.7",
33 | "@babel/preset-env": "^7.9.6",
34 | "@babel/preset-react": "^7.9.4",
35 | "@rollup/plugin-buble": "^0.21.3",
36 | "@rollup/plugin-commonjs": "^12.0.0",
37 | "@rollup/plugin-json": "^4.0.3",
38 | "@rollup/plugin-multi-entry": "^3.0.1",
39 | "@rollup/plugin-node-resolve": "^8.0.0",
40 | "@testing-library/jest-dom": "^4.2.4",
41 | "@testing-library/react": "^9.3.2",
42 | "@testing-library/user-event": "^7.1.2",
43 | "babel-loader": "^8.1.0",
44 | "babel-plugin-webpack-alias": "^2.1.2",
45 | "eslint": "^7.0.0",
46 | "eslint-config-airbnb": "^18.1.0",
47 | "eslint-plugin-babel": "^5.3.0",
48 | "eslint-plugin-import": "^2.20.2",
49 | "eslint-plugin-json": "^2.1.1",
50 | "eslint-plugin-jsx-a11y": "^6.2.3",
51 | "eslint-plugin-react": "^7.20.0",
52 | "eslint-plugin-react-hooks": "^4.0.2",
53 | "gh-pages": "^2.2.0",
54 | "node-sass": "^4.14.1",
55 | "react": "^16.13.1",
56 | "react-dom": "^16.13.1",
57 | "react-scripts": "3.4.1",
58 | "rollup": "^2.10.5",
59 | "rollup-plugin-copy-assets": "^2.0.1",
60 | "rollup-plugin-includepaths": "^0.2.3",
61 | "rollup-plugin-scss": "^2.5.0",
62 | "rollup-plugin-terser": "^5.3.0"
63 | },
64 | "scripts": {
65 | "start": "react-scripts start",
66 | "build": "rimraf dist && rollup -c",
67 | "build:demo": "react-scripts build",
68 | "lint": "eslint 'src/**/*.{js,ts,tsx}' --quiet --fix"
69 | },
70 | "tags": [
71 | "react",
72 | "color-picker",
73 | "gradient"
74 | ],
75 | "keywords": [
76 | "react",
77 | "react-hook",
78 | "react-functional-component",
79 | "color-picker",
80 | "picker",
81 | "gradient",
82 | "rgb",
83 | "hsv"
84 | ],
85 | "browserslist": {
86 | "production": [
87 | ">0.2%",
88 | "not dead",
89 | "not op_mini all"
90 | ],
91 | "development": [
92 | "last 1 chrome version",
93 | "last 1 firefox version",
94 | "last 1 safari version"
95 | ]
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arthay/react-color-gradient-picker/2b6a37105935dcbb17a8f3640434353fb955daac/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | Color Picker React
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arthay/react-color-gradient-picker/2b6a37105935dcbb17a8f3640434353fb955daac/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arthay/react-color-gradient-picker/2b6a37105935dcbb17a8f3640434353fb955daac/public/logo512.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import nodeResolve from '@rollup/plugin-node-resolve';
2 | import babel from '@rollup/plugin-buble';
3 | import json from '@rollup/plugin-json';
4 | import commonjs from '@rollup/plugin-commonjs';
5 | import scss from 'rollup-plugin-scss';
6 | import copy from 'rollup-plugin-copy-assets';
7 | import includePaths from 'rollup-plugin-includepaths';
8 |
9 | import pkg from './package.json';
10 |
11 | const extensions = ['.js', '.jsx', '.ts', '.tsx'];
12 | const config = {
13 | input: 'src/lib/index.js',
14 | external: Object.keys(Object.assign(pkg.peerDependencies, pkg.dependencies)),
15 | output: [{
16 | format: 'cjs',
17 | name: 'react-color-gradient-picker', // umd and iife for var name = (function(){})()
18 | entryFileNames: 'index-[format].js',
19 | dir: 'dist',
20 | sourcemap: true,
21 | globals: { // umd and iife for for import externals
22 | 'react-dom': 'reactDOM',
23 | },
24 | }, {
25 | format: 'es',
26 | dir: 'dist',
27 | entryFileNames: 'index-[format].js',
28 | sourcemap: true,
29 | name: 'react-color-gradient-picker',
30 | }],
31 | plugins: [
32 | scss({
33 | output: 'dist/index.css',
34 | }),
35 | nodeResolve({
36 | mainFields: ['module', 'main', 'jsnext:main', 'browser'],
37 | extensions,
38 | }),
39 | commonjs(),
40 | babel({
41 | exclude: './node_modules/**',
42 | extensions,
43 | objectAssign: 'Object.assign',
44 | }),
45 | json(),
46 | copy({
47 | assets: [
48 | // You can include directories
49 | 'src/lib/assets',
50 | ],
51 | }),
52 | includePaths({
53 | paths: ['src'],
54 | extensions: ['.js', '.jsx'],
55 |
56 | }),
57 | ],
58 | };
59 |
60 | export default config;
61 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { ColorPicker } from './lib';
4 |
5 | function App() {
6 | const onChange = (attrs, name) => {
7 | console.log(attrs, name);
8 | };
9 |
10 | return (
11 |
12 |
13 |
Solid
14 |
onChange(color, 'start')}
16 | onChange={color => onChange(color, 'change')}
17 | onEndChange={color => onChange(color, 'end')}
18 | />
19 |
20 |
21 |
Gradient
22 |
onChange(color, 'start')}
24 | onChange={color => onChange(color, 'change')}
25 | onEndChange={color => onChange(color, 'end')}
26 | isGradient
27 | />
28 |
29 |
30 | );
31 | }
32 |
33 | export default App;
34 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import ReactDOM from 'react-dom';
4 |
5 | import App from './App';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root'),
12 | );
13 |
--------------------------------------------------------------------------------
/src/lib/assets/images/alpha-background.svg:
--------------------------------------------------------------------------------
1 |
2 |
10 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Area/Alpha/index.jsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | useCallback, useEffect, useRef, useState,
3 | } from 'react';
4 |
5 | import { useMouseEvents } from 'lib/hooks';
6 | import { getAlpha } from 'lib/helpers';
7 |
8 | function Alpha({
9 | red, green, blue, alpha, updateRgb,
10 | }) {
11 | const alphaMaskRef = useRef();
12 | const [width, setWidth] = useState(0);
13 |
14 | useEffect(() => {
15 | if (alphaMaskRef.current) {
16 | setWidth(alphaMaskRef.current.clientWidth);
17 | }
18 | }, [setWidth]);
19 |
20 | const mouseDownHandler = useCallback(event => {
21 | const elementX = event.currentTarget.getBoundingClientRect().x;
22 | const startX = event.pageX;
23 | const positionX = startX - elementX;
24 |
25 | updateRgb({ alpha: getAlpha(positionX, width) }, 'onStartChange');
26 | return {
27 | startX,
28 | positionX,
29 |
30 | };
31 | }, [width, updateRgb]);
32 |
33 | const changeObjectPositions = useCallback((event, { startX, positionX }) => {
34 | const moveX = event.pageX - startX;
35 | positionX += moveX;
36 |
37 | const alpha = getAlpha(positionX, width);
38 |
39 | return {
40 | positions: {
41 | positionX,
42 | startX: event.pageX,
43 | },
44 | alpha,
45 | };
46 | }, [width]);
47 |
48 | const mouseMoveHandler = useCallback((event, { startX, positionX }) => {
49 | const { positions, alpha } = changeObjectPositions(event, { startX, positionX });
50 |
51 | updateRgb({ alpha }, 'onChange');
52 |
53 | return positions;
54 | }, [changeObjectPositions, updateRgb]);
55 |
56 | const mouseUpHandler = useCallback((event, { startX, positionX }) => {
57 | const { positions, alpha } = changeObjectPositions(event, { startX, positionX });
58 |
59 | updateRgb({ alpha }, 'onEndChange');
60 |
61 | return positions;
62 | }, [changeObjectPositions, updateRgb]);
63 |
64 | const mouseEvents = useMouseEvents(mouseDownHandler, mouseMoveHandler, mouseUpHandler);
65 |
66 | const onMouseDown = event => {
67 | mouseEvents(event);
68 | };
69 |
70 | const style = {
71 | background: `linear-gradient(to right, rgba(0, 0, 0, 0), rgb(${red}, ${green}, ${blue}))`,
72 | };
73 |
74 | const offsetLeft = ((alpha * width) | 0) - 6;
75 |
76 | const pointerStyle = {
77 | left: `${offsetLeft}px`,
78 | };
79 |
80 | return (
81 |
92 | );
93 | }
94 |
95 | export default Alpha;
96 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Area/GradientPoints/GradientPoint/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from 'react';
2 |
3 | import { updateGradientActivePercent } from 'lib/helpers';
4 | import { useMouseEvents } from 'lib/hooks';
5 |
6 | function GradientPoint({
7 | point,
8 | activePointIndex,
9 | index,
10 | width,
11 | positions,
12 | changeActivePointIndex,
13 | updateGradientLeft,
14 | removePoint,
15 | }) {
16 | const activeClassName = activePointIndex === index ? ' active' : '';
17 |
18 | const pointStyle = {
19 | left: `${(point.left * (width / 100)) - 6}px`,
20 | };
21 |
22 | const mouseDownHandler = useCallback(event => {
23 | changeActivePointIndex(index);
24 |
25 | const startX = event.pageX;
26 | const startY = event.pageY;
27 | const offsetX = startX - positions.x;
28 |
29 | updateGradientLeft(point.left, index, 'onStartChange');
30 |
31 | return {
32 | startX,
33 | startY,
34 | offsetX,
35 |
36 | };
37 | }, [point.left, index, positions, changeActivePointIndex, updateGradientLeft]);
38 |
39 | const changeObjectPositions = useCallback((event, { startX, offsetX }) => {
40 | const moveX = event.pageX - startX;
41 | offsetX += moveX;
42 | // update point percent
43 | const left = updateGradientActivePercent(offsetX, width);
44 |
45 | return {
46 | positions: {
47 | offsetX,
48 | startX: event.pageX,
49 | },
50 | left,
51 | };
52 | }, [width]);
53 |
54 | const mouseMoveHandler = useCallback((event, { startX, offsetX }) => {
55 | const { positions, left } = changeObjectPositions(event, { startX, offsetX });
56 |
57 | updateGradientLeft(left, index, 'onChange');
58 |
59 | return positions;
60 | }, [index, changeObjectPositions, updateGradientLeft]);
61 |
62 | const mouseUpHandler = useCallback((event, { startX, offsetX }) => {
63 | const { positions, left } = changeObjectPositions(event, { startX, offsetX });
64 |
65 | updateGradientLeft(left, index, 'onEndChange');
66 |
67 | return positions;
68 | }, [index, changeObjectPositions, updateGradientLeft]);
69 |
70 | const mouseEvents = useMouseEvents(mouseDownHandler, mouseMoveHandler, mouseUpHandler);
71 |
72 | const onMouseDown = event => {
73 | changeActivePointIndex(index);
74 | mouseEvents(event);
75 | };
76 |
77 | const pointerClickHandler = event => {
78 | event.stopPropagation();
79 | };
80 |
81 | return (
82 | removePoint(index)}
88 | >
89 |
90 |
91 | );
92 | }
93 |
94 | export default GradientPoint;
95 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Area/GradientPoints/index.jsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | useEffect, useState, useRef, useCallback,
3 | } from 'react';
4 |
5 | import { generateGradientStyle, updateGradientActivePercent } from 'lib/helpers';
6 |
7 | import GradientPoint from './GradientPoint';
8 |
9 | function GradientPoints({
10 | points, activePointIndex, changeActivePointIndex, updateGradientLeft, addPoint, removePoint,
11 | }) {
12 | const [pointsStyle, setpointsStyle] = useState({});
13 | const [width, setWidth] = useState(0);
14 | const [positions, setPositions] = useState({});
15 |
16 | const pointsContainerRef = useRef();
17 |
18 |
19 | useEffect(() => {
20 | if (pointsContainerRef.current) {
21 | setWidth(pointsContainerRef.current.clientWidth);
22 |
23 | const pointerPos = pointsContainerRef.current.getBoundingClientRect();
24 | setPositions({ x: pointerPos.x, y: pointerPos.y });
25 | }
26 | }, []);
27 | useEffect(() => {
28 | const style = generateGradientStyle(points, 'linear', 90);
29 |
30 | setpointsStyle({ background: style });
31 | }, [points]);
32 |
33 |
34 | const pointsContainerClick = useCallback(event => {
35 | const left = updateGradientActivePercent(event.pageX - positions.x, width);
36 |
37 | addPoint(left);
38 | }, [addPoint, positions.x, width]);
39 |
40 | const pointsContainer = () => (
41 |
45 | {
46 | points && points.map((point, index) => (
47 |
58 | ))
59 | }
60 |
61 | );
62 |
63 | return (
64 |
69 | {pointsContainer()}
70 |
71 |
72 |
73 | );
74 | }
75 |
76 | export default GradientPoints;
77 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Area/Hue/index.jsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | useCallback, useEffect, useRef, useState,
3 | } from 'react';
4 |
5 | import { useMouseEvents } from 'lib/hooks';
6 | import { getHue } from 'lib/helpers';
7 |
8 | function Hue({
9 | hue, saturation, value, updateRgb,
10 | }) {
11 | const hueRef = useRef();
12 | const [width, setWidth] = useState(0);
13 |
14 | useEffect(() => {
15 | if (hueRef.current) {
16 | setWidth(hueRef.current.clientWidth);
17 | }
18 | }, [setWidth]);
19 |
20 | const mouseDownHandler = useCallback(event => {
21 | const elementX = event.currentTarget.getBoundingClientRect().x;
22 | const startX = event.pageX;
23 | const positionX = startX - elementX;
24 |
25 | const color = getHue(positionX, width, saturation, value);
26 |
27 | updateRgb(color, 'onStartChange');
28 |
29 | return {
30 | startX,
31 | positionX,
32 | };
33 | }, [width, saturation, value, updateRgb]);
34 |
35 | const changeObjectPositions = useCallback((event, { startX, positionX }) => {
36 | const moveX = event.pageX - startX;
37 | positionX += moveX;
38 |
39 | // update value and saturation
40 | const offsetX = positionX > width ? width : positionX <= 0 ? 0 : positionX;
41 | const color = getHue(offsetX, width, saturation, value);
42 |
43 | return {
44 | positions: {
45 | positionX,
46 | startX: event.pageX,
47 | },
48 | color,
49 | };
50 | }, [width, saturation, value]);
51 |
52 | const mouseMoveHandler = useCallback((event, { startX, positionX }) => {
53 | const { positions, color } = changeObjectPositions(event, { startX, positionX });
54 |
55 | updateRgb(color, 'onChange');
56 |
57 | return positions;
58 | }, [changeObjectPositions, updateRgb]);
59 |
60 | const mouseUpHandler = useCallback((event, { startX, positionX }) => {
61 | const { positions, color } = changeObjectPositions(event, { startX, positionX });
62 |
63 | updateRgb(color, 'onEndChange');
64 |
65 | return positions;
66 | }, [changeObjectPositions, updateRgb]);
67 |
68 | const mouseEvents = useMouseEvents(mouseDownHandler, mouseMoveHandler, mouseUpHandler);
69 |
70 | const onMouseDown = event => {
71 | mouseEvents(event);
72 | };
73 |
74 | const offsetLeft = ((hue * width / 360) | 0) - 6;
75 |
76 | const pointerStyle = {
77 | left: `${offsetLeft}px`,
78 | };
79 |
80 | return (
81 |
92 | );
93 | }
94 |
95 | export default Hue;
96 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Area/Picking/index.jsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | useEffect, useCallback, useRef, useState,
3 | } from 'react';
4 |
5 | import { changePicker, getRgbByHue } from 'lib/helpers';
6 | import { useMouseEvents } from 'lib/hooks';
7 |
8 | function Picking({
9 | red,
10 | green,
11 | blue,
12 | hue,
13 | saturation,
14 | value,
15 | updateRgb,
16 | }) {
17 | const pickingAreaRef = useRef();
18 | const [width, setWidth] = useState(0);
19 | const [height, setHeight] = useState(0);
20 |
21 | useEffect(() => {
22 | if (pickingAreaRef.current) {
23 | setWidth(pickingAreaRef.current.clientWidth);
24 | setHeight(pickingAreaRef.current.clientHeight);
25 | }
26 | }, [pickingAreaRef, setWidth, setHeight]);
27 |
28 | useEffect(() => {
29 | const { red, green, blue } = getRgbByHue(hue);
30 |
31 | pickingAreaRef.current.style.backgroundColor = `rgb(${red}, ${green}, ${blue})`;
32 | }, [hue]);
33 |
34 | // generate offsetLeft by saturation
35 | const offsetLeft = ((saturation * width / 100) | 0) - 6;
36 |
37 | // generate offsetTop by value
38 | const offsetTop = (height - (value * height / 100) | 0) - 6;
39 |
40 | const getPointerStyle = {
41 | backgroundColor: `rgb(${red}, ${green}, ${blue})`,
42 | left: `${offsetLeft}px`,
43 | top: `${offsetTop}px`,
44 | };
45 |
46 | const mouseDownHandler = useCallback(event => {
47 | const elementX = event.currentTarget.getBoundingClientRect().x;
48 | const elementY = event.currentTarget.getBoundingClientRect().y;
49 | const startX = event.pageX;
50 | const startY = event.pageY;
51 | const positionX = startX - elementX;
52 | const positionY = startY - elementY;
53 |
54 | const color = changePicker(positionX, positionY, height, width, hue);
55 |
56 | updateRgb(color, 'onStartChange');
57 | return {
58 | startX,
59 | startY,
60 | positionX,
61 | positionY,
62 |
63 | };
64 | }, [height, width, hue, updateRgb]);
65 |
66 | const changeObjectPositions = useCallback((event, {
67 | startX, startY, positionX, positionY,
68 | }) => {
69 | const moveX = event.pageX - startX;
70 | const moveY = event.pageY - startY;
71 | positionX += moveX;
72 | positionY += moveY;
73 |
74 | const color = changePicker(positionX, positionY, height, width, hue);
75 |
76 | return {
77 | positions: {
78 | positionX,
79 | positionY,
80 | startX: event.pageX,
81 | startY: event.pageY,
82 | },
83 | color,
84 | };
85 | }, [height, hue, width]);
86 |
87 | const mouseMoveHandler = useCallback((event, {
88 | startX, startY, positionX, positionY,
89 | }) => {
90 | const { positions, color } = changeObjectPositions(event, {
91 | startX, startY, positionX, positionY,
92 | });
93 |
94 | updateRgb(color, 'onChange');
95 |
96 | return positions;
97 | }, [updateRgb, changeObjectPositions]);
98 |
99 | const mouseUpHandler = useCallback((event, {
100 | startX, startY, positionX, positionY,
101 | }) => {
102 | const { positions, color } = changeObjectPositions(event, {
103 | startX, startY, positionX, positionY,
104 | });
105 |
106 | updateRgb(color, 'onEndChange');
107 |
108 | return positions;
109 | }, [updateRgb, changeObjectPositions]);
110 |
111 | const mouseEvents = useMouseEvents(mouseDownHandler, mouseMoveHandler, mouseUpHandler);
112 |
113 | const onMouseDown = event => {
114 | mouseEvents(event);
115 | };
116 |
117 | return (
118 |
129 | );
130 | }
131 |
132 | export default Picking;
133 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Area/Preview/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 |
3 | import { generateSolidStyle, generateGradientStyle } from 'lib/helpers';
4 |
5 | function Preview({
6 | red,
7 | green,
8 | blue,
9 | alpha,
10 | isGradient,
11 | points,
12 | gradientType,
13 | gradientDegree,
14 | }) {
15 | const [style, setStyle] = useState({});
16 |
17 | useEffect(() => {
18 | if (isGradient) {
19 | const style = generateGradientStyle(points, gradientType, gradientDegree);
20 |
21 | setStyle({ background: style });
22 |
23 | return;
24 | }
25 |
26 | const style = generateSolidStyle(red, green, blue, alpha);
27 |
28 | setStyle({ backgroundColor: style });
29 | }, [points, gradientDegree, gradientType, isGradient, red, green, blue, alpha]);
30 |
31 | return (
32 |
35 | );
36 | }
37 |
38 | export default Preview;
39 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Area/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Picking from './Picking';
4 | import Preview from './Preview';
5 | import Hue from './Hue';
6 | import Alpha from './Alpha';
7 | import GradientPoints from './GradientPoints';
8 |
9 | function Area({
10 | red,
11 | green,
12 | blue,
13 | alpha,
14 | hue,
15 | saturation,
16 | value,
17 | isGradient,
18 | type,
19 | degree,
20 | points,
21 | activePointIndex,
22 | updateRgb,
23 | changeActivePointIndex,
24 | updateGradientLeft,
25 | addPoint,
26 | removePoint,
27 | }) {
28 | return (
29 |
30 |
39 |
40 | {
41 | isGradient
42 | && (
43 |
53 | )
54 | }
55 |
56 |
84 |
85 | );
86 | }
87 |
88 | export default Area;
89 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Gradient/GradientControls/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useState } from 'react';
2 |
3 | import { calculateDegree } from 'lib/helpers';
4 | import { useMouseEvents } from 'lib/hooks';
5 |
6 | function GradientControls({ type, degree, changeGradientControl }) {
7 | const [disableClick, setDisableClick] = useState(false);
8 |
9 | const onClickGradientDegree = useCallback(() => {
10 | if (disableClick) {
11 | setDisableClick(false);
12 | return;
13 | }
14 |
15 | let gradientDegree = degree + 45;
16 |
17 | if (gradientDegree >= 360) {
18 | gradientDegree = 0;
19 | }
20 |
21 | changeGradientControl({ degree: parseInt(gradientDegree, 10) });
22 | }, [disableClick, degree, changeGradientControl]);
23 |
24 | const mouseDownHandler = useCallback(event => {
25 | const pointer = event.target;
26 | const pointerBox = pointer.getBoundingClientRect();
27 | const centerY = pointerBox.top + parseInt(8 - window.pageYOffset, 10);
28 | const centerX = pointerBox.left + parseInt(8 - window.pageXOffset, 10);
29 |
30 | return {
31 | centerY,
32 | centerX,
33 |
34 | };
35 | }, []);
36 |
37 | const mouseMoveHandler = useCallback((event, { centerX, centerY }) => {
38 | setDisableClick(true);
39 |
40 | const newDegree = calculateDegree(event.clientX, event.clientY, centerX, centerY);
41 |
42 | changeGradientControl({ degree: parseInt(newDegree, 10) });
43 |
44 | return { centerX, centerY };
45 | }, [changeGradientControl]);
46 |
47 | const mouseUpHandler = event => {
48 | const targetClasses = event.target.classList;
49 |
50 | if (targetClasses.contains('gradient-degrees') || targetClasses.contains('icon-rotate')) {
51 | return;
52 | }
53 |
54 | setDisableClick(false);
55 | };
56 |
57 | const mouseEvents = useMouseEvents(mouseDownHandler, mouseMoveHandler, mouseUpHandler);
58 |
59 | const onMouseDown = event => {
60 | mouseEvents(event);
61 | };
62 |
63 | const degreesStyle = {
64 | transform: `rotate(${degree}deg)`,
65 | };
66 |
67 | return (
68 |
69 |
70 |
changeGradientControl({ type: 'linear' })}
73 | />
74 |
changeGradientControl({ type: 'radial' })}
77 | />
78 |
79 | {
80 | type === 'linear'
81 | && (
82 |
83 |
92 |
93 |
94 | {degree}
95 | °
96 |
97 |
98 |
99 | )
100 | }
101 |
102 | );
103 | }
104 |
105 | export default GradientControls;
106 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Gradient/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useEffect, useState } from 'react';
2 |
3 | import { useMount } from 'lib/hooks';
4 | import { rgbToHsv, getRightValue, generateGradientStyle } from 'lib/helpers';
5 |
6 | import Area from '../Area';
7 | import Preview from '../Preview';
8 | import GradientControls from './GradientControls';
9 |
10 | function Gradient({
11 | points, type, degree, onChange, onStartChange, onEndChange,
12 | }) {
13 | const [activePointIndex, setActivePointIndex] = useState(0);
14 | const [gradientPoints, setGradientPoints] = useState(points);
15 | const [activePoint, setActivePoint] = useState(gradientPoints[0]);
16 | const [colorRed, setColorRed] = useState(activePoint.red);
17 | const [colorGreen, setColorGreen] = useState(activePoint.green);
18 | const [colorBlue, setColorBlue] = useState(activePoint.blue);
19 | const [colorAlpha, setColorAlpha] = useState(activePoint.alpha);
20 | const [colorHue, setColorHue] = useState(0);
21 | const [colorSaturation, setColorSaturation] = useState(100);
22 | const [colorValue, setColorValue] = useState(100);
23 | const [gradientType, setGradientType] = useState(type);
24 | const [gradientDegree, setGradientDegree] = useState(degree);
25 |
26 | const actions = {
27 | onChange,
28 | onStartChange,
29 | onEndChange,
30 | };
31 |
32 | useMount(() => {
33 | const { hue, saturation, value } = rgbToHsv({ red: colorRed, green: colorGreen, blue: colorBlue });
34 |
35 | setColorHue(hue);
36 | setColorSaturation(saturation);
37 | setColorValue(value);
38 | });
39 |
40 | const removePoint = useCallback((index = activePointIndex) => {
41 | if (gradientPoints.length <= 2) {
42 | return;
43 | }
44 |
45 | const localGradientPoints = gradientPoints.slice();
46 | localGradientPoints.splice(index, 1);
47 |
48 | setGradientPoints(localGradientPoints);
49 |
50 | if (index > 0) {
51 | setActivePointIndex(index - 1);
52 | }
53 |
54 | onChange && onChange({
55 | points: localGradientPoints,
56 | type: gradientType,
57 | degree: gradientDegree,
58 | style: generateGradientStyle(localGradientPoints, gradientType, gradientDegree),
59 | });
60 | }, [gradientPoints, activePointIndex, gradientType, gradientDegree, onChange]);
61 |
62 | const keyUpHandler = useCallback(event => {
63 | if ((event.keyCode === 46 || event.keyCode === 8)) {
64 | removePoint(activePointIndex);
65 | }
66 | }, [activePointIndex, removePoint]);
67 |
68 | useEffect(() => {
69 | document.addEventListener('keyup', keyUpHandler);
70 |
71 | return () => {
72 | document.removeEventListener('keyup', keyUpHandler);
73 | };
74 | });
75 |
76 | const changeGradientControl = useCallback(({ type, degree }, actionName = 'onChange') => {
77 | type = getRightValue(type, gradientType);
78 | degree = getRightValue(degree, gradientDegree);
79 |
80 | setGradientType(type);
81 | setGradientDegree(degree);
82 |
83 | const action = actions[actionName];
84 |
85 | action && action({
86 | points: gradientPoints,
87 | type,
88 | degree,
89 | style: generateGradientStyle(gradientPoints, type, degree),
90 | });
91 | }, [actions, gradientPoints, gradientDegree, gradientType]);
92 |
93 | const changeActivePointIndex = useCallback(index => {
94 | setActivePointIndex(index);
95 |
96 | const localGradientPoint = gradientPoints[index];
97 | const {
98 | red, green, blue, alpha,
99 | } = localGradientPoint;
100 | setActivePoint(localGradientPoint);
101 |
102 | setColorRed(red);
103 | setColorGreen(green);
104 | setColorBlue(blue);
105 | setColorAlpha(alpha);
106 |
107 | const { hue, saturation, value } = rgbToHsv({ red, green, blue });
108 |
109 | setColorHue(hue);
110 | setColorSaturation(saturation);
111 | setColorValue(value);
112 | }, [gradientPoints]);
113 |
114 | const updateColor = useCallback(({
115 | red, green, blue, alpha, hue, saturation, value,
116 | }, actionName = 'onChange') => {
117 | red = getRightValue(red, colorRed);
118 | green = getRightValue(green, colorGreen);
119 | blue = getRightValue(blue, colorBlue);
120 | alpha = getRightValue(alpha, colorAlpha);
121 | hue = getRightValue(hue, colorHue);
122 | saturation = getRightValue(saturation, colorSaturation);
123 | value = getRightValue(value, colorValue);
124 |
125 | const localGradientPoints = gradientPoints.slice();
126 |
127 | localGradientPoints[activePointIndex] = {
128 | ...localGradientPoints[activePointIndex],
129 | red,
130 | green,
131 | blue,
132 | alpha,
133 | };
134 |
135 | setColorRed(red);
136 | setColorGreen(green);
137 | setColorBlue(blue);
138 | setColorAlpha(alpha);
139 | setColorHue(hue);
140 | setColorSaturation(saturation);
141 | setColorValue(value);
142 | setGradientPoints(localGradientPoints);
143 |
144 | const action = actions[actionName];
145 |
146 | action && action({
147 | points: localGradientPoints,
148 | type: gradientType,
149 | degree: gradientDegree,
150 | style: generateGradientStyle(localGradientPoints, gradientType, gradientDegree),
151 | });
152 | }, [
153 | colorRed, colorGreen, colorBlue, colorAlpha,
154 | colorHue, colorSaturation, colorValue,
155 | activePointIndex, gradientPoints, actions, gradientType, gradientDegree,
156 | ]);
157 |
158 | const updateGradientLeft = useCallback((left, index, actionName = 'onChange') => {
159 | const localGradientPoints = gradientPoints.slice();
160 | localGradientPoints[index].left = left;
161 |
162 | setGradientPoints(localGradientPoints);
163 |
164 | const action = actions[actionName];
165 |
166 | action && action({
167 | points: localGradientPoints,
168 | type: gradientType,
169 | degree: gradientDegree,
170 | style: generateGradientStyle(localGradientPoints, gradientType, gradientDegree),
171 | });
172 | }, [actions, gradientPoints, gradientDegree, gradientType]);
173 |
174 | const addPoint = useCallback(left => {
175 | const localGradientPoints = gradientPoints.slice();
176 |
177 | localGradientPoints.push({
178 | ...localGradientPoints[activePointIndex],
179 | left,
180 | });
181 |
182 | setGradientPoints(localGradientPoints);
183 | setActivePointIndex(localGradientPoints.length - 1);
184 |
185 | onChange && onChange({
186 | points: localGradientPoints,
187 | type: gradientType,
188 | degree: gradientDegree,
189 | style: generateGradientStyle(localGradientPoints, gradientType, gradientDegree),
190 | });
191 | }, [onChange, gradientPoints, activePointIndex, gradientType, gradientDegree]);
192 |
193 | return (
194 | <>
195 |
200 |
220 |
227 | >
228 | );
229 | }
230 |
231 | export default Gradient;
232 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Preview/Hex/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useCallback } from 'react';
2 |
3 | import { rgbToHex, hexToRgb } from 'lib/helpers';
4 | import { Input } from 'lib/components/UI';
5 |
6 | function Hex({
7 | red, green, blue, updateRgb,
8 | }) {
9 | const [hexValue, setHexValue] = useState('');
10 | const [progress, setProgress] = useState(false);
11 |
12 | useEffect(() => {
13 | if (progress) {
14 | return;
15 | }
16 | const hex = rgbToHex(red, green, blue);
17 |
18 | setHexValue(hex);
19 | }, [red, green, blue, progress]);
20 |
21 | const changeHex = useCallback(event => {
22 | setHexValue(event.target.value);
23 | const color = hexToRgb(event.target.value);
24 |
25 | if (color) {
26 | updateRgb(color);
27 | }
28 | }, [setHexValue, updateRgb]);
29 |
30 | return (
31 |
setProgress(true)}
36 | onBlur={() => setProgress(false)}
37 | classes="hex"
38 | />
39 | );
40 | }
41 |
42 | export default Hex;
43 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Preview/RGB/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from 'react';
2 |
3 | import { rgbToHsv } from 'lib/helpers';
4 |
5 | import RGBItem from './item';
6 |
7 | function RGB({
8 | red, green, blue, alpha, updateRgb,
9 | }) {
10 | const changeValue = useCallback((field, value) => {
11 | if (field === 'alpha') {
12 | updateRgb({ alpha: value / 100 });
13 |
14 | return;
15 | }
16 |
17 | const color = rgbToHsv({
18 | red, green, blue, [field]: value,
19 | });
20 |
21 | updateRgb({ ...color, [field]: value });
22 | }, [red, green, blue, updateRgb]);
23 |
24 | return (
25 | <>
26 |
changeValue('red', value)}
31 | />
32 |
33 | changeValue('green', value)}
38 | />
39 |
40 | changeValue('blue', value)}
45 | />
46 |
47 | changeValue('alpha', value)}
52 | />
53 | >
54 | );
55 | }
56 |
57 | export default RGB;
58 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Preview/RGB/item/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useEffect, useState } from 'react';
2 |
3 | import { Input } from 'lib/components/UI';
4 |
5 | export default function RGBItem({
6 | value, type, label, onChange,
7 | }) {
8 | const [inputValue, setInputValue] = useState(value);
9 |
10 | useEffect(() => {
11 | if (value !== +inputValue && inputValue !== '') {
12 | setInputValue(value);
13 | }
14 | }, [inputValue, value]);
15 |
16 | const onChangeHandler = useCallback(event => {
17 | const value = +event.target.value;
18 |
19 | if (Number.isNaN(value) || value.length > 3 || value < 0 || value > 255) {
20 | return;
21 | }
22 | setInputValue(event.target.value);
23 |
24 | onChange(value);
25 | }, [onChange]);
26 |
27 | const onBlur = useCallback(() => {
28 | !inputValue && inputValue !== 0 && setInputValue(value);
29 | }, [inputValue, setInputValue, value]);
30 |
31 | return (
32 |
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Preview/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Hex from './Hex';
4 | import RGB from './RGB';
5 |
6 | function Preview({
7 | red, green, blue, alpha, updateRgb,
8 | }) {
9 | return (
10 |
11 |
12 |
18 |
19 |
26 |
27 |
28 | );
29 | }
30 |
31 | export default Preview;
32 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Solid/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useState } from 'react';
2 |
3 | import { useMount } from 'lib/hooks';
4 | import { rgbToHsv, getRightValue, generateSolidStyle } from 'lib/helpers';
5 |
6 | import Area from '../Area';
7 | import Preview from '../Preview';
8 |
9 | function Solid({
10 | red,
11 | green,
12 | blue,
13 | alpha,
14 | onChange,
15 | onStartChange,
16 | onEndChange,
17 | }) {
18 | const [colorRed, setColorRed] = useState(red);
19 | const [colorGreen, setColorGreen] = useState(green);
20 | const [colorBlue, setColorBlue] = useState(blue);
21 | const [colorAlpha, setColorAlpha] = useState(alpha);
22 | const [colorHue, setColorHue] = useState(0);
23 | const [colorSaturation, setColorSaturation] = useState(100);
24 | const [colorValue, setColorValue] = useState(100);
25 |
26 | const actions = {
27 | onChange,
28 | onStartChange,
29 | onEndChange,
30 | };
31 |
32 | useMount(() => {
33 | const { hue, saturation, value } = rgbToHsv({ red: colorRed, green: colorGreen, blue: colorBlue });
34 |
35 | setColorHue(hue);
36 | setColorSaturation(saturation);
37 | setColorValue(value);
38 | });
39 |
40 | const updateColor = useCallback(({
41 | red, green, blue, alpha, hue, saturation, value,
42 | }, actionName = 'onChange') => {
43 | red = getRightValue(red, colorRed);
44 | green = getRightValue(green, colorGreen);
45 | blue = getRightValue(blue, colorBlue);
46 | alpha = getRightValue(alpha, colorAlpha);
47 | hue = getRightValue(hue, colorHue);
48 | saturation = getRightValue(saturation, colorSaturation);
49 | value = getRightValue(value, colorValue);
50 |
51 | setColorRed(red);
52 | setColorGreen(green);
53 | setColorBlue(blue);
54 | setColorAlpha(alpha);
55 | setColorHue(hue);
56 | setColorSaturation(saturation);
57 | setColorValue(value);
58 |
59 | const action = actions[actionName];
60 |
61 | action && action({
62 | red,
63 | green,
64 | blue,
65 | alpha,
66 | hue,
67 | saturation,
68 | value,
69 | style: generateSolidStyle(red, green, blue, alpha),
70 | });
71 | }, [
72 | colorRed, colorGreen, colorBlue, colorAlpha,
73 | colorHue, colorSaturation, colorValue,
74 | actions,
75 | ]);
76 |
77 | return (
78 | <>
79 |
89 |
96 | >
97 | );
98 | }
99 |
100 | Solid.defaultProps = {
101 | red: 255,
102 | green: 0,
103 | blue: 0,
104 | alpha: 1,
105 | };
106 |
107 | export default Solid;
108 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Solid from './Solid';
4 | import Gradient from './Gradient';
5 |
6 | function ColorPicker({
7 | color,
8 | isGradient,
9 | gradient,
10 | onStartChange,
11 | onChange,
12 | onEndChange,
13 | }) {
14 | return (
15 |
16 | {
17 | isGradient
18 | ? (
19 |
28 | )
29 | : (
30 |
39 | )
40 | }
41 |
42 | );
43 | }
44 |
45 | ColorPicker.defaultProps = {
46 | isGradient: false,
47 | onChange: () => {},
48 | color: {
49 | red: 255,
50 | green: 0,
51 | blue: 0,
52 | alpha: 1,
53 | },
54 | gradient: {
55 | points: [
56 | {
57 | left: 0,
58 | red: 0,
59 | green: 0,
60 | blue: 0,
61 | alpha: 1,
62 | },
63 | {
64 | left: 100,
65 | red: 255,
66 | green: 0,
67 | blue: 0,
68 | alpha: 1,
69 | },
70 | ],
71 | degree: 0,
72 | type: 'linear',
73 | },
74 | };
75 |
76 | export default ColorPicker;
77 |
--------------------------------------------------------------------------------
/src/lib/components/UI/Input/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './index.scss';
3 |
4 | function Input({
5 | value, label, type = 'text', onChange, onFocus, onBlur, classes,
6 | }) {
7 | return (
8 |
9 |
10 |
17 |
18 |
19 | {label}
20 |
21 |
22 | );
23 | }
24 |
25 | export { Input };
26 |
--------------------------------------------------------------------------------
/src/lib/components/UI/Input/index.scss:
--------------------------------------------------------------------------------
1 | .input-field {
2 | display: flex;
3 | flex-shrink: 0;
4 | align-items: center;
5 | flex-direction: column;
6 |
7 | .label {
8 | font-size: 12px;
9 | line-height: 15px;
10 | font-weight: 600;
11 | margin-top: 6px;
12 | margin-bottom: 0;
13 | color: #1F2667;
14 | }
15 |
16 | .input-container {
17 | display: flex;
18 | align-items: center;
19 | position: relative;
20 | width: 100%;
21 | border-radius: 6px;
22 | color: #28314d;
23 |
24 | .input {
25 | width: 100%;
26 | outline: 0;
27 | color: #1F2667;
28 | border-radius: inherit;
29 | border: 1px solid #bbbfc5;
30 | height: 24px;
31 | font-size: 12px;
32 | font-weight: 600;
33 | padding: 0 6px;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/lib/components/UI/index.jsx:
--------------------------------------------------------------------------------
1 | export * from './Input';
2 |
--------------------------------------------------------------------------------
/src/lib/helpers/calculateDegree.js:
--------------------------------------------------------------------------------
1 | export default function calculateDegree(x, y, centerX, centerY) {
2 | const radians = Math.atan2(x - centerX, y - centerY);
3 | return (radians * (180 / Math.PI) * -1) + 180;
4 | }
5 |
--------------------------------------------------------------------------------
/src/lib/helpers/changePicker.js:
--------------------------------------------------------------------------------
1 | import hsvToRgb from './hsvToRgb';
2 |
3 | export default function changePicker(x, y, height, width, hue) {
4 | if (x > width) x = width;
5 | if (y > height) y = height;
6 | if (x < 0) x = 0;
7 | if (y < 0) y = 0;
8 | const value = 100 - (y * 100 / height) | 0;
9 | const saturation = x * 100 / width | 0;
10 | return {
11 | ...hsvToRgb(hue, saturation, value),
12 | saturation,
13 | value,
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/src/lib/helpers/generateStyles.js:
--------------------------------------------------------------------------------
1 | export function generateSolidStyle(red, green, blue, alpha) {
2 | return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
3 | }
4 |
5 | export function generateGradientStyle(points, type, degree) {
6 | let style = '';
7 | const sortedPoints = points.slice();
8 |
9 | sortedPoints.sort((a, b) => a.left - b.left);
10 |
11 | if (type === 'linear') {
12 | style = `linear-gradient(${degree}deg,`;
13 | } else {
14 | style = 'radial-gradient(';
15 | }
16 |
17 | sortedPoints.forEach((point, index) => {
18 | style += `rgba(${point.red}, ${point.green}, ${point.blue}, ${point.alpha}) ${point.left}%`;
19 |
20 | if (index !== sortedPoints.length - 1) {
21 | style += ',';
22 | }
23 | });
24 |
25 | style += ')';
26 | return style;
27 | }
28 |
--------------------------------------------------------------------------------
/src/lib/helpers/getAlpha.js:
--------------------------------------------------------------------------------
1 | export default function getAlpha(value, width) {
2 | value = Number((value / width).toFixed(2));
3 |
4 | return value > 1 ? 1 : value < 0 ? 0 : value;
5 | }
6 |
--------------------------------------------------------------------------------
/src/lib/helpers/getHue.js:
--------------------------------------------------------------------------------
1 | import { hsvToRgb } from './index';
2 |
3 | export default function getHue(offsetX, width, saturation, value) {
4 | let hue = ((360 * offsetX) / width) | 0;
5 |
6 | hue = hue < 0 ? 0 : hue > 360 ? 360 : hue;
7 |
8 | return {
9 | ...hsvToRgb(hue, saturation, value),
10 | hue,
11 | };
12 | }
13 |
--------------------------------------------------------------------------------
/src/lib/helpers/getRgbByHue.js:
--------------------------------------------------------------------------------
1 | export default function getRgbByHue(hue) {
2 | let C = 1;
3 | const H = hue / 60;
4 | let X = C * (1 - Math.abs(H % 2 - 1));
5 | const m = 0;
6 | const precision = 255;
7 | let r = 0;
8 | let g = 0;
9 | let b = 0;
10 |
11 | C = (C + m) * precision | 0;
12 | X = (X + m) * precision | 0;
13 |
14 | if (H >= 0 && H < 1) {
15 | r = C | 0;
16 | g = X | 0;
17 | b = m | 0;
18 | }
19 | if (H >= 1 && H < 2) {
20 | r = X | 0;
21 | g = C | 0;
22 | b = m | 0;
23 | }
24 | if (H >= 2 && H < 3) {
25 | r = m | 0;
26 | g = C | 0;
27 | b = X | 0;
28 | }
29 | if (H >= 3 && H < 4) {
30 | r = m | 0;
31 | g = X | 0;
32 | b = C | 0;
33 | }
34 | if (H >= 4 && H < 5) {
35 | r = X | 0;
36 | g = m | 0;
37 | b = C | 0;
38 | }
39 | if (H >= 5 && H <= 6) {
40 | r = C | 0;
41 | g = m | 0;
42 | b = X | 0;
43 | }
44 |
45 | return {
46 | red: r,
47 | green: g,
48 | blue: b,
49 | };
50 | }
51 |
--------------------------------------------------------------------------------
/src/lib/helpers/getRightValue.js:
--------------------------------------------------------------------------------
1 | export default function getRightValue(newValue, oldValue) {
2 | return (!newValue && newValue !== 0) ? oldValue : newValue;
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/helpers/hexToRgb.js:
--------------------------------------------------------------------------------
1 | import { rgbToHsv, setRgba } from './index';
2 |
3 | const hexRegexp = /(^#{0,1}[0-9A-F]{6}$)|(^#{0,1}[0-9A-F]{3}$)|(^#{0,1}[0-9A-F]{8}$)/i;
4 |
5 | const regexp = /([0-9A-F])([0-9A-F])([0-9A-F])/i;
6 |
7 | export default function hexToRgb(value) {
8 | const valid = hexRegexp.test(value);
9 |
10 | if (valid) {
11 | if (value[0] === '#') value = value.slice(1, value.length);
12 |
13 | if (value.length === 3) value = value.replace(regexp, '$1$1$2$2$3$3');
14 |
15 | const red = parseInt(value.substr(0, 2), 16);
16 | const green = parseInt(value.substr(2, 2), 16);
17 | const blue = parseInt(value.substr(4, 2), 16);
18 | const alpha = parseInt(value.substr(6, 2), 16) / 255;
19 |
20 | const color = setRgba(red, green, blue, alpha);
21 | const hsv = rgbToHsv({ ...color });
22 |
23 | return {
24 | ...color,
25 | ...hsv,
26 | };
27 | }
28 |
29 | return false;
30 | }
31 |
--------------------------------------------------------------------------------
/src/lib/helpers/hsvToRgb.js:
--------------------------------------------------------------------------------
1 | import setRGBA from './setRgba';
2 |
3 | export default function hsvToRgb(hue, saturation, value) {
4 | value /= 100;
5 | const sat = saturation / 100;
6 | let C = sat * value;
7 | const H = hue / 60;
8 | let X = C * (1 - Math.abs(H % 2 - 1));
9 | let m = value - C;
10 | const precision = 255;
11 |
12 | C = (C + m) * precision | 0;
13 | X = (X + m) * precision | 0;
14 | m = m * precision | 0;
15 |
16 | if (H >= 1 && H < 2) {
17 | return setRGBA(X, C, m);
18 | }
19 | if (H >= 2 && H < 3) {
20 | return setRGBA(m, C, X);
21 | }
22 | if (H >= 3 && H < 4) {
23 | return setRGBA(m, X, C);
24 | }
25 | if (H >= 4 && H < 5) {
26 | return setRGBA(X, m, C);
27 | }
28 | if (H >= 5 && H <= 6) {
29 | return setRGBA(C, m, X);
30 | }
31 |
32 | return setRGBA(C, X, m);
33 | }
34 |
--------------------------------------------------------------------------------
/src/lib/helpers/index.js:
--------------------------------------------------------------------------------
1 | export { default as rgbToHsv } from './rgbToHsv';
2 | export { default as getRgbByHue } from './getRgbByHue';
3 | export { default as changePicker } from './changePicker';
4 | export { default as hsvToRgb } from './hsvToRgb';
5 | export { default as getHue } from './getHue';
6 | export { default as getAlpha } from './getAlpha';
7 | export { default as rgbToHex } from './rgbToHex';
8 | export { default as hexToRgb } from './hexToRgb';
9 | export { default as setRgba } from './setRgba';
10 | export { default as updateGradientActivePercent } from './updateGradientActivePercent';
11 | export { default as calculateDegree } from './calculateDegree';
12 | export { default as getRightValue } from './getRightValue';
13 | export * from './generateStyles';
14 |
--------------------------------------------------------------------------------
/src/lib/helpers/rgbToHex.js:
--------------------------------------------------------------------------------
1 | export default function rgbToHex(red, green, blue) {
2 | let r16 = red.toString(16);
3 | let g16 = green.toString(16);
4 | let b16 = blue.toString(16);
5 |
6 | if (red < 16) r16 = `0${r16}`;
7 | if (green < 16) g16 = `0${g16}`;
8 | if (blue < 16) b16 = `0${b16}`;
9 |
10 | return r16 + g16 + b16;
11 | }
12 |
--------------------------------------------------------------------------------
/src/lib/helpers/rgbToHsv.js:
--------------------------------------------------------------------------------
1 | export default function rgbToHSv({ red, green, blue }) {
2 | let rr;
3 | let gg;
4 | let bb;
5 | let h;
6 | let s;
7 |
8 | const rabs = red / 255;
9 | const gabs = green / 255;
10 | const babs = blue / 255;
11 | const v = Math.max(rabs, gabs, babs);
12 | const diff = v - Math.min(rabs, gabs, babs);
13 | const diffc = c => (v - c) / 6 / diff + 1 / 2;
14 | if (diff === 0) {
15 | h = 0;
16 | s = 0;
17 | } else {
18 | s = diff / v;
19 | rr = diffc(rabs);
20 | gg = diffc(gabs);
21 | bb = diffc(babs);
22 |
23 | if (rabs === v) {
24 | h = bb - gg;
25 | } else if (gabs === v) {
26 | h = (1 / 3) + rr - bb;
27 | } else if (babs === v) {
28 | h = (2 / 3) + gg - rr;
29 | }
30 | if (h < 0) {
31 | h += 1;
32 | } else if (h > 1) {
33 | h -= 1;
34 | }
35 | }
36 |
37 | return {
38 | hue: Math.round(h * 360),
39 | saturation: Math.round(s * 100),
40 | value: Math.round(v * 100),
41 | };
42 | }
43 |
--------------------------------------------------------------------------------
/src/lib/helpers/setRgba.js:
--------------------------------------------------------------------------------
1 | function isValidRGBValue(value) {
2 | return (typeof (value) === 'number' && Number.isNaN(value) === false && value >= 0 && value <= 255);
3 | }
4 |
5 | export default function setRGBA(red, green, blue, alpha) {
6 | if (isValidRGBValue(red) && isValidRGBValue(green) && isValidRGBValue(blue)) {
7 | const color = {
8 | red: red | 0,
9 | green: green | 0,
10 | blue: blue | 0,
11 | };
12 |
13 | if (isValidRGBValue(alpha) === true) {
14 | color.alpha = alpha | 0;
15 | }
16 |
17 | // RGBToHSL(color.r, color.g, color.b);
18 |
19 | return color;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/lib/helpers/updateGradientActivePercent.js:
--------------------------------------------------------------------------------
1 | export default function updateGradientActivePercent(offsetX, width) {
2 | const leftPercent = (offsetX * 100) / width;
3 | return leftPercent < 0 ? 0 : leftPercent > 100 ? 100 : leftPercent;
4 | }
5 |
--------------------------------------------------------------------------------
/src/lib/hooks/index.js:
--------------------------------------------------------------------------------
1 | export { default as useMouseEvents } from './mouseEvents';
2 | export { default as useMount } from './useMount';
3 |
--------------------------------------------------------------------------------
/src/lib/hooks/mouseEvents.js:
--------------------------------------------------------------------------------
1 | export default function useMouseEvents(mouseDownHandler, mouseMoveHandler, mouseUpHandler) {
2 | return function mouseEventsHandler(event) {
3 | let positions = mouseDownHandler(event);
4 |
5 | function onMouseMove(event) {
6 | positions = mouseMoveHandler(event, positions) || positions;
7 | }
8 |
9 | window.addEventListener('mousemove', onMouseMove);
10 |
11 | window.addEventListener('mouseup', event => {
12 | window.removeEventListener('mousemove', onMouseMove);
13 |
14 | mouseUpHandler && mouseUpHandler(event, positions);
15 | }, { once: true });
16 | };
17 | }
18 |
--------------------------------------------------------------------------------
/src/lib/hooks/useMount.js:
--------------------------------------------------------------------------------
1 | /* eslint react-hooks/exhaustive-deps: 0 */
2 |
3 | import { useEffect } from 'react';
4 |
5 | const useMount = effect => useEffect(effect, []);
6 |
7 | export default useMount;
8 |
--------------------------------------------------------------------------------
/src/lib/index.js:
--------------------------------------------------------------------------------
1 | import './index.scss';
2 |
3 | export { default as ColorPicker } from './components/ColorPicker';
4 |
--------------------------------------------------------------------------------
/src/lib/index.scss:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 |
5 | .ui-color-picker {
6 | margin: 8px;
7 | background-color: #FFFFFF;
8 | display: flex;
9 | flex-direction: column;
10 | width: 280px;
11 | user-select: none;
12 |
13 | .gradient-controls {
14 | display: flex;
15 | flex-direction: row;
16 | align-items: center;
17 | width: 100%;
18 | margin-bottom: 8px;
19 | margin-top: 5px;
20 | height: 24px;
21 | padding: 0 16px;
22 |
23 | .gradient-type {
24 | flex: 1;
25 | display: flex;
26 |
27 | .gradient-type-item {
28 | height: 28px;
29 | width: 28px;
30 | border-radius: 50%;
31 | position: relative;
32 | cursor: pointer;
33 |
34 | &.active {
35 | &::after {
36 | content: '';
37 | display: block;
38 | position: absolute;
39 | top: -3px;
40 | bottom: -3px;
41 | left: -3px;
42 | right: -3px;
43 | border: 2px solid #1F2667;
44 | border-radius: 50%;
45 | }
46 | }
47 |
48 | &.liner-gradient {
49 | background: linear-gradient(270deg, #FFFFFF 0%, #1F2667 100%);
50 | }
51 |
52 | &.radial-gradient {
53 | margin-left: 8px;
54 | background: radial-gradient(circle, #FFFFFF 0%, #1F2667 100%);
55 | }
56 | }
57 |
58 | }
59 |
60 | .gradient-degrees-options {
61 | position: relative;
62 |
63 | .gradient-degrees {
64 | display: -ms-flexbox;
65 | display: flex;
66 | -webkit-box-pack: center;
67 | -ms-flex-pack: center;
68 | justify-content: center;
69 | -webkit-box-align: center;
70 | -ms-flex-align: center;
71 | align-items: center;
72 | position: relative;
73 | width: 28px;
74 | height: 28px;
75 | border: 3px solid #1F2667;
76 | border-radius: 18px;
77 | margin-right: 54px;
78 |
79 | .gradient-degree-center {
80 | position: relative;
81 | width: 6px;
82 | height: 22px;
83 | pointer-events: none;
84 |
85 | .gradient-degree-pointer {
86 | position: absolute;
87 | width: 6px;
88 | height: 6px;
89 | top: 2px;
90 | border-radius: 3px;
91 | background: #1F2667;
92 | }
93 | }
94 | }
95 |
96 | .gradient-degree-value {
97 | position: absolute;
98 | top: 0;
99 | right: 0;
100 | width: 48px;
101 | height: 28px;
102 | display: flex;
103 | align-items: center;
104 | justify-content: center;
105 | border: 1px solid #bbbfc5;
106 | border-radius: 6px;
107 |
108 | p {
109 | text-align: center;
110 | padding: 0 6px;
111 | }
112 | }
113 | }
114 | }
115 |
116 | .picker-area {
117 | display: flex;
118 | flex-direction: column;
119 | padding: 0 16px;
120 |
121 | &.gradient-tab {
122 | .picking-area {
123 | margin-bottom: 10px;
124 | }
125 | }
126 |
127 | .picking-area {
128 | width: 100%;
129 | height: 160px;
130 | margin-bottom: 16px;
131 | position: relative;
132 | border-radius: 8px;
133 |
134 | &:hover {
135 | cursor: default;
136 | }
137 |
138 | .picking-area-overlay1 {
139 | height: 100%;
140 | width: 100%;
141 | background: linear-gradient(to right, white 0%, rgba(255, 255, 255, 0) 100%);
142 | border-radius: 3px;
143 |
144 | .picking-area-overlay2 {
145 | height: 100%;
146 | width: 100%;
147 | background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, black 100%);
148 | border-radius: 8px;
149 | }
150 | }
151 | }
152 |
153 | .preview {
154 | display: flex;
155 | flex-direction: row;
156 | margin-bottom: 16px;
157 |
158 | .preview-box {
159 | box-sizing: border-box;
160 | height: 36px;
161 | width: 36px;
162 | border-radius: 8px;
163 | border: 1px solid #EBEDF5;
164 | }
165 |
166 | .color-hue-alpha {
167 | display: flex;
168 | flex-direction: column;
169 | flex: 1;
170 | margin-left: 6px;
171 |
172 | .hue {
173 | width: 100%;
174 | position: relative;
175 | border-radius: 10px;
176 | margin-bottom: 8px;
177 | padding: 0 7px;
178 | background-color: red;
179 |
180 | .hue-area {
181 | position: relative;
182 | height: 14px;
183 | background: -webkit-linear-gradient(left, #ff0000, #ff0080, #ff00ff, #8000ff, #0000ff, #0080ff, #00ffff, #00ff80, #00ff00, #80ff00, #ffff00, #ff8000, #ff0000);
184 | background: -o-linear-gradient(left, #ff0000, #ff8000, #ffff00, #80ff00, #00ff00, #00ff80, #00ffff, #0080ff, #0000ff, #8000ff, #ff00ff, #ff0080, #ff0000);
185 | background: -ms-linear-gradient(left, #ff0000, #ff8000, #ffff00, #80ff00, #00ff00, #00ff80, #00ffff, #0080ff, #0000ff, #8000ff, #ff00ff, #ff0080, #ff0000);
186 | background: -moz-linear-gradient(left, #ff0000, #ff8000, #ffff00, #80ff00, #00ff00, #00ff80, #00ffff, #0080ff, #0000ff, #8000ff, #ff00ff, #ff0080, #ff0000);
187 | background: linear-gradient(to right, #ff0000, #ff8000, #ffff00, #80ff00, #00ff00, #00ff80, #00ffff, #0080ff, #0000ff, #8000ff, #ff00ff, #ff0080, #ff0000);
188 | }
189 | }
190 |
191 | .alpha {
192 | position: relative;
193 | width: 100%;
194 | overflow: hidden;
195 | border-radius: 10px;
196 | height: 14px;
197 | cursor: pointer;
198 |
199 | .gradient {
200 | position: absolute;
201 | top: 0;
202 | left: 0;
203 | right: 0;
204 | bottom: 0;
205 | }
206 |
207 | .alpha-area {
208 | width: 100%;
209 | height: 100%;
210 | background: url("assets/images/alpha-background.svg");
211 | background-size: auto;
212 | background-position: 50% 50%;
213 | border-radius: 10px;
214 | padding: 0 7px;
215 |
216 | .alpha-mask {
217 | width: 100%;
218 | height: 100%;
219 | position: relative;
220 | }
221 | }
222 | }
223 | }
224 | }
225 |
226 | .gradient {
227 | width: 100%;
228 | height: 14px;
229 | position: relative;
230 | cursor: pointer;
231 | border-radius: 10px;
232 | margin-bottom: 8px;
233 | padding: 0 7px;
234 |
235 | .gradient-slider-container {
236 | height: 100%;
237 | width: 100%;
238 | position: relative;
239 | }
240 | }
241 |
242 | .picker-pointer {
243 | position: absolute;
244 | top: 1px;
245 | height: 12px;
246 | width: 12px;
247 | border: 1px solid #FFFFFF;
248 | background: transparent;
249 | border-radius: 50%;
250 | box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.3);
251 |
252 | .child-point {
253 | position: absolute;
254 | top: 50%;
255 | left: 50%;
256 | transform: translate(-50%, -50%);
257 | height: 3px;
258 | width: 3px;
259 | background: #FFFFFF;
260 | border-radius: 50%;
261 | opacity: 0;
262 | transition: opacity 0.33s;
263 |
264 | &.active {
265 | opacity: 1;
266 | }
267 | }
268 | }
269 | }
270 |
271 | .color-preview-area {
272 | margin-bottom: 8px;
273 | padding: 0 16px;
274 |
275 | .input-group {
276 | width: 100%;
277 | display: flex;
278 | flex-direction: row;
279 | justify-content: space-between;
280 |
281 | .uc-field-group:not(:last-child) {
282 | margin-right: 7px;
283 | }
284 | }
285 |
286 | .hex {
287 | width: 65px;
288 | }
289 |
290 | .rgb {
291 | width: 40px;
292 | }
293 | }
294 |
295 | .colors {
296 | padding: 3px 16px 0;
297 |
298 | .colors-label {
299 | display: flex;
300 | align-items: center;
301 | margin-bottom: 4px;
302 | cursor: pointer;
303 |
304 | .uc-icon {
305 | margin-right: 8px;
306 | transition: transform 0.3s;
307 | }
308 |
309 | .tp-text {
310 | text-transform: uppercase;
311 | }
312 |
313 | &.show {
314 | & + .colors-item-container {
315 | max-height: 80px;
316 | padding-bottom: 16px;
317 | }
318 |
319 | .uc-icon {
320 | transform: rotate(-90deg);
321 | }
322 | }
323 | }
324 |
325 | .template {
326 | display: flex;
327 | flex-direction: column;
328 | }
329 |
330 | .global {
331 | display: flex;
332 | flex-direction: column;
333 | align-items: flex-start;
334 | }
335 |
336 | .colors-item-container {
337 | display: flex;
338 | flex-wrap: wrap;
339 | width: 100%;
340 | transition: max-height 0.3s, padding-bottom 0.3s;
341 | max-height: 0;
342 | overflow: hidden;
343 |
344 | .colors-item {
345 | height: 24px;
346 | width: 24px;
347 | border-radius: 50%;
348 | background-color: #EBEDF5;
349 | cursor: pointer;
350 | position: relative;
351 | margin-top: 4px;
352 | flex-shrink: 0;
353 |
354 | &:not(.plus) {
355 | border: 1px solid #EBEDF5;
356 | }
357 |
358 | &.empty {
359 | display: flex;
360 | align-items: center;
361 | justify-content: center;
362 |
363 | .line {
364 | width: 2px;
365 | height: 16px;
366 | background-color: #8892B3;
367 | border-radius: 1px;
368 | transform: rotate(45deg);
369 | }
370 | }
371 |
372 | &.plus {
373 | margin-bottom: 4px;
374 |
375 | .uc-icon {
376 | position: absolute;
377 | top: 50%;
378 | left: 50%;
379 | transform: translate(-50%, -50%);
380 | opacity: 1;
381 | }
382 | }
383 |
384 | &:not(:first-child):not(:nth-child(9)) {
385 | margin-left: 8px;
386 | }
387 |
388 | .uc-icon {
389 | position: absolute;
390 | right: -8px;
391 | top: -3px;
392 | opacity: 0;
393 | transition: opacity 0.3s;
394 | }
395 |
396 | &:hover {
397 | .uc-icon {
398 | opacity: 1;
399 | }
400 | }
401 |
402 | &.active {
403 | &::after {
404 | content: '';
405 | display: block;
406 | position: absolute;
407 | top: -3px;
408 | bottom: -3px;
409 | left: -3px;
410 | right: -3px;
411 | border: 2px solid #8892B3;
412 | border-radius: 50%;
413 | }
414 | }
415 | }
416 | }
417 | }
418 | }
419 |
--------------------------------------------------------------------------------