Skip to content

Commit 0175897

Browse files
committedJun 16, 2019
feat: Port v2 features from vue color picker
1 parent 8b469b7 commit 0175897

8 files changed

+2038
-2991
lines changed
 

‎.babelrc

+5-8
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
{
22
"presets": [
33
[
4-
"babel-preset-env",
4+
"@babel/preset-env",
55
{
6-
"modules": false
6+
"loose": true,
7+
"modules": false,
78
}
89
],
9-
"babel-preset-react"
10+
"@babel/preset-react"
1011
],
1112
"plugins": [
12-
"transform-class-properties",
13-
"external-helpers"
14-
],
15-
"ignore": [
16-
"node_modules/**"
13+
"@babel/plugin-proposal-class-properties"
1714
]
1815
}

‎.browserslistrc

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
> 0.75%
2+
last 2 major versions
3+
not ie <= 11
4+
not dead
5+
not op_mini all

‎package.json

+17-26
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"description": "Radial Color Picker - React",
55
"main": "dist/react-color-picker.cjs.js",
66
"module": "dist/react-color-picker.esm.js",
7-
"browser": "dist/react-color-picker.umd.min.js",
87
"unpkg": "dist/react-color-picker.umd.min.js",
98
"files": [
109
"dist"
@@ -14,36 +13,28 @@
1413
"start": "rollup -c -w"
1514
},
1615
"dependencies": {
17-
"@radial-color-picker/color-wheel": "1.0.0",
18-
"@radial-color-picker/rotator": "1.0.1"
16+
"@radial-color-picker/color-wheel": "2.0.0",
17+
"@radial-color-picker/rotator": "2.0.1"
1918
},
2019
"devDependencies": {
21-
"autoprefixer": "9.1.0",
22-
"babel-cli": "6.26.0",
23-
"babel-core": "6.26.3",
24-
"babel-plugin-external-helpers": "6.22.0",
25-
"babel-plugin-transform-class-properties": "6.24.1",
26-
"babel-preset-env": "1.7.0",
27-
"babel-preset-react": "6.24.1",
28-
"node-sass": "4.9.2",
29-
"postcss": "7.0.2",
30-
"react": "^16.3.2",
31-
"react-dom": "^16.3.2",
32-
"rollup": "0.63.5",
33-
"rollup-plugin-babel": "3.0.7",
34-
"rollup-plugin-butternut": "0.1.0",
35-
"rollup-plugin-commonjs": "9.1.4",
36-
"rollup-plugin-node-resolve": "3.3.0",
37-
"rollup-plugin-postcss": "1.6.2"
20+
"@babel/core": "7.4.5",
21+
"@babel/plugin-proposal-class-properties": "7.4.4",
22+
"@babel/preset-env": "7.4.5",
23+
"@babel/preset-react": "7.0.0",
24+
"autoprefixer": "9.6.0",
25+
"postcss": "7.0.17",
26+
"react": "16.8.6",
27+
"react-dom": "16.8.6",
28+
"rollup": "1.15.6",
29+
"rollup-plugin-babel": "4.3.2",
30+
"rollup-plugin-commonjs": "10.0.0",
31+
"rollup-plugin-node-resolve": "5.0.3",
32+
"rollup-plugin-postcss": "2.0.3",
33+
"rollup-plugin-terser": "5.0.0"
3834
},
3935
"peerDependencies": {
40-
"react": "^16.3.2"
36+
"react": "^16.7.0"
4137
},
42-
"browserslist": [
43-
">0.75%",
44-
"not ie 11",
45-
"not op_mini all"
46-
],
4738
"homepage": "https://github.com/radial-color-picker/react-color-picker#readme",
4839
"repository": "git@github.com:radial-color-picker/react-color-picker.git",
4940
"author": "Rosen Kanev <rkunev@gmail.com>",

‎rollup.config.js

+42-14
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,70 @@
11
import autoprefixer from 'autoprefixer';
22
import babel from 'rollup-plugin-babel';
3-
import butternut from 'rollup-plugin-butternut';
4-
import commonjs from 'rollup-plugin-commonjs';
3+
import { terser } from 'rollup-plugin-terser';
54
import postcss from 'rollup-plugin-postcss';
65
import resolve from 'rollup-plugin-node-resolve';
76

8-
import pkg from './package.json';
7+
const file = 'dist/react-color-picker';
98

109
export default [
1110
{
1211
input: 'src/index.js',
1312
output: [
14-
{ file: pkg.main, format: 'cjs' },
15-
{ file: pkg.module, format: 'es' }
13+
{ file: `${file}.cjs.js`, format: 'cjs' },
14+
{ file: `${file}.esm.js`, format: 'esm' }
1615
],
1716
external: ['react'],
1817
plugins: [
19-
postcss({ extract: false }),
20-
babel({ exclude: ['node_modules/**'] }),
2118
resolve(),
22-
commonjs(),
19+
postcss({
20+
extract: false,
21+
inject: false,
22+
}),
23+
babel(),
2324
]
2425
},
2526
{
2627
input: 'src/index.js',
2728
output: {
28-
file: pkg.browser,
29+
file: `${file}.umd.js`,
2930
format: 'umd',
3031
name: 'ReactColorPicker',
31-
globals: { react: 'React' },
32+
globals: { react: 'React' }
3233
},
3334
external: ['react'],
3435
plugins: [
35-
postcss({ extract: true, plugins: [autoprefixer], minimize: true }),
36-
babel({ exclude: ['node_modules/**'] }),
3736
resolve(),
38-
commonjs(),
39-
butternut(),
37+
postcss({
38+
extract: `${file}.css`,
39+
inject: false,
40+
minimize: false,
41+
sourceMap: false,
42+
plugins: [autoprefixer],
43+
}),
44+
babel(),
4045
]
4146
},
47+
{
48+
input: 'src/index.js',
49+
output: {
50+
file: `${file}.umd.min.js`,
51+
format: 'umd',
52+
name: 'ReactColorPicker',
53+
globals: { react: 'React' },
54+
sourcemap: true,
55+
},
56+
external: ['react'],
57+
plugins: [
58+
resolve(),
59+
postcss({
60+
extract: `${file}.min.css`,
61+
inject: false,
62+
minimize: true,
63+
sourceMap: true,
64+
plugins: [autoprefixer],
65+
}),
66+
babel(),
67+
terser(),
68+
],
69+
},
4270
];

‎src/index.js

+62-55
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
import React from 'react';
22
import fillColorWheel from '@radial-color-picker/color-wheel';
33
import Rotator from '@radial-color-picker/rotator';
4-
import './style.scss';
4+
import './style.css';
55

66
const noop = () => {};
77

88
export default class ColorPicker extends React.Component {
99
paletteRef = React.createRef();
1010
rotatorRef = React.createRef();
11+
elRef = React.createRef();
1112

1213
rotator = null;
1314

1415
state = {
15-
isKnobIn: true,
16-
isPaletteIn: true,
16+
isKnobIn: !this.props.initiallyCollapsed,
17+
isPaletteIn: !this.props.initiallyCollapsed,
1718
isPressed: false,
1819
isRippling: false,
19-
isDisabled: false,
2020
isDragging: false,
2121
};
2222

@@ -27,16 +27,32 @@ export default class ColorPicker extends React.Component {
2727
alpha: 1,
2828
step: 2,
2929
mouseScroll: false,
30+
variant: 'collapsible', // collapsible | persistent
31+
disabled: false,
32+
initiallyCollapsed: false,
33+
onInput: noop,
3034
onChange: noop,
31-
onSelect: noop,
3235
};
3336

3437
componentDidMount() {
3538
if (this.props.mouseScroll) {
3639
this.rotatorRef.current.addEventListener('wheel', this.onScroll);
3740
}
3841

39-
fillColorWheel(this.paletteRef.current, this.paletteRef.current.offsetWidth || 280);
42+
if (this.props.initiallyCollapsed && this.props.variant === 'persistent') {
43+
console.warn(`Incorrect config: using variant="persistent" and initiallyCollapsed={true} at the same time is not supported.`);
44+
}
45+
46+
const isConicGradientSupported = getComputedStyle(this.paletteRef.current)
47+
.backgroundImage
48+
.includes('conic');
49+
50+
if (!isConicGradientSupported) {
51+
fillColorWheel(
52+
this.paletteRef.current.firstElementChild,
53+
this.elRef.current.offsetWidth || 280
54+
);
55+
}
4056

4157
this.rotator = new Rotator(this.rotatorRef.current, {
4258
angle: this.props.hue,
@@ -66,7 +82,8 @@ export default class ColorPicker extends React.Component {
6682
}
6783

6884
onScroll = ev => {
69-
if (this.state.isDisabled) return;
85+
if (this.state.isPressed || !this.state.isKnobIn)
86+
return;
7087

7188
ev.preventDefault();
7289

@@ -86,7 +103,8 @@ export default class ColorPicker extends React.Component {
86103
};
87104

88105
onKeyDown = ev => {
89-
if (this.state.isDisabled) return;
106+
if (this.props.disabled || this.state.isPressed || !this.state.isKnobIn)
107+
return;
90108

91109
const isIncrementing = ev.key === 'ArrowUp' || ev.key === 'ArrowRight';
92110
const isDecrementing = ev.key === 'ArrowDown' || ev.key === 'ArrowLeft';
@@ -108,103 +126,92 @@ export default class ColorPicker extends React.Component {
108126
};
109127

110128
updateColor = hue => {
111-
const { saturation, luminosity, alpha } = this.props;
112-
113-
this.props.onChange({ hue, saturation, luminosity, alpha });
129+
this.props.onInput(hue);
114130
};
115131

116132
rotateToMouse = ev => {
117-
if (this.isDisabled || ev.target !== this.rotatorRef.current) return;
133+
if (this.state.isPressed || !this.state.isKnobIn || ev.target !== this.rotatorRef.current)
134+
return;
118135

119136
this.rotator.setAngleFromEvent(ev);
120137
};
121138

122139
selectColor = () => {
123140
this.setState({ isPressed: true });
124141

125-
if (!this.state.isDisabled) {
126-
const { hue, saturation, luminosity, alpha } = this.props;
127-
128-
this.props.onSelect({ hue, saturation, luminosity, alpha });
129-
142+
if (this.state.isPaletteIn && this.state.isKnobIn) {
143+
this.props.onChange(this.props.hue);
130144
this.setState({ isRippling: true });
131145
} else {
132146
this.setState({ isPaletteIn: true });
133147
}
134148
};
135149

136150
togglePicker = () => {
151+
if (this.props.variant !== 'persistent') {
152+
if (this.state.isKnobIn) {
153+
this.setState({ isKnobIn: false });
154+
} else {
155+
this.setState({
156+
isKnobIn: true,
157+
isPaletteIn: true,
158+
});
159+
}
160+
}
161+
137162
this.setState({
138-
isKnobIn: this.state.isDisabled,
163+
isRippling: false,
139164
isPressed: false,
140165
});
141166
};
142167

143168
hidePalette = () => {
144-
if (!this.state.isDisabled) {
169+
if (!this.state.isKnobIn) {
145170
this.setState({ isPaletteIn: false });
146-
} else {
147-
this.setState({ isDisabled: false });
148-
}
149-
};
150-
151-
stopRipple = () => {
152-
this.setState({ isRippling: false });
153-
};
154-
155-
toggleKnob = ev => {
156-
// 'transitionend' fires for every transitioned property
157-
if (ev.propertyName === 'transform') {
158-
if (this.state.isDisabled) {
159-
this.setState({ isKnobIn: true });
160-
} else {
161-
this.setState({ isDisabled: true });
162-
}
163171
}
164172
};
165173

166174
render() {
167-
const { isRippling, isPressed, isPaletteIn, isDragging, isKnobIn, isDisabled } = this.state;
168-
169-
const { hue, saturation, luminosity, alpha } = this.props;
175+
const { disabled, hue, saturation, luminosity, alpha } = this.props;
176+
const { isDragging, isPressed, isPaletteIn, isKnobIn, isRippling } = this.state;
170177

171-
const paletteClassName = `palette ${isPaletteIn ? 'is-in' : 'is-out'}`;
172-
const colorSelClassName = `selector ${isPressed ? 'is-pressed' : ''}`;
173-
const colorShadowClassName = `ripple ${isRippling ? 'is-rippling' : ''}`;
174-
const rotatorClassName = `rotator ${isDisabled ? 'disabled' : ''} ${
175-
isDragging ? 'dragging' : ''
176-
}`;
177-
const knobClassName = `knob ${isKnobIn ? 'is-in' : 'is-out'}`;
178178
const color = `hsla(${hue}, ${saturation}%, ${luminosity}%, ${alpha})`;
179179

180180
return (
181181
<div
182-
className="color-picker"
183-
tabIndex="0"
182+
ref={this.elRef}
183+
className={`rcp ${isDragging ? 'dragging' : ''} ${disabled ? 'disabled' : ''}`.trim()}
184+
tabIndex={disabled ? -1 : 0}
184185
onKeyUp={this.onKeyUp}
185186
onKeyDown={this.onKeyDown}
186187
>
187-
<div className={paletteClassName} onTransitionEnd={this.toggleKnob}>
188-
<canvas ref={this.paletteRef} />
188+
<div
189+
ref={this.paletteRef}
190+
className={`rcp__palette ${isPaletteIn ? 'in' : 'out'}`}
191+
>
192+
<canvas />
189193
</div>
190194

191195
<div
192196
ref={this.rotatorRef}
193-
className={rotatorClassName}
197+
className="rcp__rotator"
198+
style={{ pointerEvents: disabled || isPressed || !isKnobIn ? 'none' : null }}
194199
onDoubleClick={this.rotateToMouse}
195200
>
196-
<div onTransitionEnd={this.hidePalette} className={knobClassName} />
201+
<div
202+
className={`rcp__knob ${isKnobIn ? 'in' : 'out'}`}
203+
onTransitionEnd={this.hidePalette}
204+
/>
197205
</div>
198206

199207
<div
200-
className={colorShadowClassName}
208+
className={`rcp__ripple ${isRippling ? 'rippling' : ''}`.trim()}
201209
style={{ borderColor: color }}
202-
onAnimationEnd={this.stopRipple}
203210
/>
204211

205212
<button
206213
type="button"
207-
className={colorSelClassName}
214+
className={`rcp__well ${isPressed ? 'pressed' : ''}`.trim()}
208215
style={{ backgroundColor: color }}
209216
onClick={this.selectColor}
210217
onAnimationEnd={this.togglePicker}

‎src/style.css

+186
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
.rcp, .rcp div, .rcp button {
2+
-webkit-touch-callout: none;
3+
-webkit-tap-highlight-color: transparent;
4+
user-select: none;
5+
box-sizing: border-box;
6+
}
7+
8+
.rcp {
9+
display: block;
10+
overflow: hidden;
11+
width: 280px;
12+
height: 280px;
13+
position: relative;
14+
transform: scale(1.001);
15+
transition: transform 0.15s cubic-bezier(0.68, 0, 0.47, 2);
16+
}
17+
18+
.rcp:focus {
19+
outline: 0;
20+
}
21+
22+
.rcp:hover .rcp__knob {
23+
box-shadow: 0 0 20px rgba(0, 0, 0, 0.19), 0 0 10px rgba(0, 0, 0, 0.24);
24+
}
25+
26+
.rcp.dragging {
27+
transform: scale(1.04);
28+
}
29+
30+
.rcp.disabled {
31+
cursor: not-allowed;
32+
transform: scale(0.96);
33+
}
34+
35+
.rcp.dragging .rcp__rotator {
36+
z-index: 1;
37+
}
38+
39+
.rcp__palette {
40+
position: absolute;
41+
top: 0;
42+
left: 0;
43+
right: 0;
44+
bottom: 0;
45+
width: 100%;
46+
height: 100%;
47+
background-size: 100% 100%;
48+
background-image: conic-gradient(red, yellow, lime, aqua, blue, magenta, red);
49+
border-radius: 50%;
50+
overflow: hidden;
51+
will-change: transform, opacity;
52+
transition: transform .5s cubic-bezier(0.35, 0, 0.25, 1),
53+
opacity .5s cubic-bezier(0.35, 0, 0.25, 1);
54+
}
55+
56+
.rcp__palette::before {
57+
box-sizing: border-box;
58+
content: '';
59+
display: block;
60+
position: absolute;
61+
width: 76%;
62+
height: 76%;
63+
top: 12%;
64+
left: 12%;
65+
background-color: #fff;
66+
border-radius: 50%;
67+
}
68+
69+
.rcp__palette.in {
70+
transform: scale(1);
71+
opacity: 1;
72+
}
73+
74+
.rcp__palette.out {
75+
transform: scale(0);
76+
opacity: 0;
77+
}
78+
79+
.disabled .rcp__palette {
80+
background-image: radial-gradient(#808080, #fff) !important;
81+
}
82+
83+
.rcp__rotator {
84+
width: 100%;
85+
height: 100%;
86+
position: absolute;
87+
}
88+
89+
.rcp__knob {
90+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.12), 0 0 5px rgba(0, 0, 0, 0.16);
91+
border-radius: 50%;
92+
position: absolute;
93+
width: 7%;
94+
height: 7%;
95+
top: 2.5%;
96+
left: 46.5%;
97+
background-color: #fff;
98+
transition: transform .4s cubic-bezier(0.35, 0, 0.25, 1);
99+
outline: 0;
100+
border-style: none;
101+
}
102+
103+
.rcp__knob.in {
104+
transform: scale(1);
105+
}
106+
107+
.rcp__knob.out {
108+
transform: scale(0);
109+
}
110+
111+
.disabled .rcp__knob {
112+
box-shadow: none !important;
113+
pointer-events: none;
114+
}
115+
116+
.rcp__well {
117+
position: absolute;
118+
width: 25%;
119+
height: 25%;
120+
top: 37.5%;
121+
left: 37.5%;
122+
padding: 0;
123+
margin: 0;
124+
border-radius: 50%;
125+
background-color: #ff0000;
126+
outline: 0;
127+
cursor: pointer;
128+
overflow: visible;
129+
border: 6px solid #fff;
130+
box-shadow: 0 0 0 1px #b2b2b2;
131+
}
132+
133+
.rcp__well::-moz-focus-inner {
134+
border: 0;
135+
}
136+
137+
.rcp__well:hover {
138+
box-shadow: 0 0 1px 1px #333;
139+
}
140+
141+
.rcp__well:focus {
142+
box-shadow: 0 0 1px 2px #b2b2b2;
143+
}
144+
145+
.rcp__well.pressed {
146+
animation: rcp-beat .4s cubic-bezier(0.35, 0, 0.25, 1) forwards;
147+
}
148+
149+
.disabled .rcp__well {
150+
background-color: #bfbfbf !important;
151+
pointer-events: none;
152+
}
153+
154+
.rcp__ripple {
155+
width: 20%;
156+
height: 20%;
157+
border-radius: 50%;
158+
border: #ff0000 solid 8px;
159+
opacity: 0;
160+
position: absolute;
161+
top: 40%;
162+
left: 40%;
163+
z-index: -1;
164+
}
165+
166+
.rcp__ripple.rippling {
167+
z-index: 0;
168+
animation: rcp-ripple .5s cubic-bezier(0.35, 0, 0.25, 1) forwards;
169+
}
170+
171+
@keyframes rcp-ripple {
172+
0% { transform: scale(1); opacity: .3; }
173+
50% { opacity: .1; }
174+
100% {
175+
opacity: 0;
176+
border-width: 0;
177+
transform: scale(3.8);
178+
}
179+
}
180+
181+
@keyframes rcp-beat {
182+
0% { transform: scale(1); }
183+
25% { transform: scale(0.8); }
184+
50% { transform: scale(1); }
185+
100% { transform: scale(1); }
186+
}

‎src/style.scss

-191
This file was deleted.

‎yarn.lock

+1,721-2,697
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.