Skip to content

Commit

Permalink
feat: replace react-color with react-colorful
Browse files Browse the repository at this point in the history
  • Loading branch information
atanasster committed Oct 20, 2020
1 parent d51409d commit 64d8da5
Show file tree
Hide file tree
Showing 6 changed files with 285 additions and 53 deletions.
6 changes: 6 additions & 0 deletions core/core/src/controls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,14 @@ export interface ComponentControlBoolean extends ComponentControlBase<boolean> {
type: ControlTypes.BOOLEAN;
}

export type ColorPickerKind = 'hex' | 'rgb' | 'rgba' | 'hsl' | 'hsla';

export interface ComponentControlColor extends ComponentControlBase<string> {
type: ControlTypes.COLOR;
/**
* format to save the color as a string
*/
kind?: ColorPickerKind;
}

export interface ComponentControlDate extends ComponentControlBase<Date> {
Expand Down
7 changes: 6 additions & 1 deletion examples/stories/src/tutorial/reference/controls.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,12 @@ color: {
},
```

all properties of [ControlType](/tutorial/reference/controls-api/#controltype)
all properties of [ControlType](/tutorial/reference/controls-api/#controltype) in addition to:

| Name | Type | Description |
| -------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `kind` | 'hex' \| 'rgb' \| 'rgba' \| 'hsl' \| 'hsla' | in what format to save the color as a string (default to `hex`) |


<Playground>
<Story name="color-control" controls={{ color: {
Expand Down
7 changes: 4 additions & 3 deletions ui/editors/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,17 @@
"global": "^4.3.2",
"polished": "^3.4.4",
"react": "^16.13.1",
"react-color": "^2.17.0",
"react-colorful": "^4.1.4",
"react-dom": "^16.13.1",
"theme-ui": "^0.4.0-rc.1"
"theme-ui": "^0.4.0-rc.1",
"tinycolor2": "^1.4.1"
},
"devDependencies": {
"@component-controls/jest-snapshots": "^1.33.1",
"@component-controls/ts-markdown-docs": "^1.21.0",
"@types/jest": "^26.0.10",
"@types/react": "^16.9.34",
"@types/react-color": "^3.0.1",
"@types/tinycolor2": "^1.4.2",
"cross-env": "^5.2.1",
"eslint": "^6.5.1",
"jest": "^26.4.2"
Expand Down
46 changes: 44 additions & 2 deletions ui/editors/src/ColorEditor/ColorEditor.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,54 @@ export const overview = () => {
};

export const rgb = () => {
const [state, setState] = React.useState('rgb(192,0,0)');
const [state, setState] = React.useState('rgb(192, 0, 0)');
return (
<ControlsStateProvider
onChange={(name, newVal) => setState(newVal)}
controls={{
prop: { type: ControlTypes.COLOR, value: state },
prop: { type: ControlTypes.COLOR, value: state, kind: 'rgb' },
}}
>
<ColorEditor name="prop" />
</ControlsStateProvider>
);
};

export const rgba = () => {
const [state, setState] = React.useState('rgba(0, 192, 0, 0.5)');
return (
<ControlsStateProvider
onChange={(name, newVal) => setState(newVal)}
controls={{
prop: { type: ControlTypes.COLOR, value: state, kind: 'rgba' },
}}
>
<ColorEditor name="prop" />
</ControlsStateProvider>
);
};

export const hsl = () => {
const [state, setState] = React.useState('hsl(213, 50%, 16%)');
return (
<ControlsStateProvider
onChange={(name, newVal) => setState(newVal)}
controls={{
prop: { type: ControlTypes.COLOR, value: state, kind: 'hsl' },
}}
>
<ColorEditor name="prop" />
</ControlsStateProvider>
);
};

export const hsla = () => {
const [state, setState] = React.useState('hsla(213, 50%, 16%, 0.5)');
return (
<ControlsStateProvider
onChange={(name, newVal) => setState(newVal)}
controls={{
prop: { type: ControlTypes.COLOR, value: state, kind: 'hsla' },
}}
>
<ColorEditor name="prop" />
Expand Down
267 changes: 220 additions & 47 deletions ui/editors/src/ColorEditor/ColorEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,71 +1,244 @@
import React from 'react';
import { SketchPicker, ColorResult } from 'react-color';
import { Button, Box } from 'theme-ui';
/** @jsx jsx */
import React, { ReactNode } from 'react';
import {
HexColorPicker,
HslaColorPicker,
HslColorPicker,
RgbColorPicker,
RgbaColorPicker,
} from 'react-colorful';
import { jsx, Button, Box, SxStyleProp } from 'theme-ui';
import tinycolor from 'tinycolor2';
import { Popover } from '@component-controls/components';
import { ComponentControlColor, ControlTypes } from '@component-controls/core';
import { useControl } from '@component-controls/store';
import { PropertyEditor } from '../types';
import { addPropertyEditor } from '../prop-factory';

const sxProps: SxStyleProp = {
position: 'relative',
display: 'flex',
flexDirection: 'column',
width: 200,
height: 200,
userSelect: 'none',
cursor: 'default',
'.react-colorful__saturation': {
position: 'relative',
flexGrow: 1,
borderBottom: '12px solid #000',
borderRadius: '8px 8px 0 0',
backgroundImage:
'linear-gradient(to top, #000, rgba(0, 0, 0, 0)), linear-gradient(to right, #fff, rgba(255, 255, 255, 0));',
},
'.react-colorful__alpha>div': {
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
},
'.react-colorful__alpha:after, .react-colorful__saturation-pointer>div, .react-colorful__hue-pointer>div, .react-colorful__alpha-pointer>div': {
content: '""',
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
pointerEvents: 'none',
borderRadius: 'inherit',
},

/* Improve elements rendering on light backgrounds */
'.react-colorful__alpha:after, .react-colorful__saturation': {
boxShadow: 'inset 0 0 0 1px rgba(0, 0, 0, 0.05)',
},

'.react-colorful__hue, .react-colorful__alpha': {
position: 'relative',
height: 24,
},

'.react-colorful__hue': {
background:
'linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%)',
},

/* Round bottom corners of the last element: `Hue` for `ColorPicker` or `Alpha` for `AlphaColorPicker` */
'.react-colorful__lastControl': {
borderRadius: '0 0 8px 8px',
},

'div[role=slider]': {
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
borderRadius: 'inherit',
outline: 'none',
/* Don't trigger the default scrolling behavior when the event is originating from this element */
touchAction: 'none',
},

'div[role=slider]:focus .react-colorful__saturation-pointer, div[role=slider]:focus .react-colorful__hue-pointer, div[role=slider]:focus .react-colorful__alpha-pointer': {
transform: 'translate(-50%, -50%) scale(1.1)',
},

'.react-colorful__saturation-pointer, .react-colorful__hue-pointer, .react-colorful__alpha-pointer': {
position: 'absolute',
zIndex: 1,
boxSizing: 'border-box',
width: 28,
height: 28,
transform: 'translate(-50%, -50%)',
backgroundColor: '#fff',
border: '2px solid #fff',
borderRadius: '50%',
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.2)',
},

/* Chessboard-like pattern for alpha related elements */
'.react-colorful__alpha': {
backgroundColor: '#fff',
backgroundImage: `url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill-opacity=".05"><rect x="8" width="8" height="8"/><rect y="8" width="8" height="8"/></svg>')`,
},

/* Display the saturation pointer over the hue one */
'.react-colorful__saturation-pointer': {
zIndex: 3,
},

/* Display the hue pointer over the alpha one */
'.react-colorful__hue-pointer': {
zIndex: 2,
},
};
/**
* Color control editor.
*/

export const ColorEditor: PropertyEditor = ({ name }) => {
const [control, onChange] = useControl<ComponentControlColor>(name);
const [displayColorPicker, setDisplayColorPicker] = React.useState(false);

const handleChange = (color: ColorResult) => {
onChange(
`rgba(${color.rgb.r},${color.rgb.g},${color.rgb.b},${color.rgb.a})`,
);
const [isOpen, setIsOpen] = React.useState(false);
const { kind } = control;
const handleChange = (color: string) => {
onChange(color);
};
const color = tinycolor(control.value);
let colorPicker: ReactNode;
switch (kind) {
case 'hex':
default:
colorPicker = (
<HexColorPicker
sx={sxProps}
color={control.value}
onChange={handleChange}
/>
);

break;
case 'rgb': {
colorPicker = (
<RgbColorPicker
sx={sxProps}
color={color.toRgb()}
onChange={({ r, g, b }) =>
handleChange(
`rgb(${r.toFixed(0)}, ${g.toFixed(0)}, ${b.toFixed(0)})`,
)
}
/>
);

break;
}
case 'rgba': {
colorPicker = (
<RgbaColorPicker
sx={sxProps}
color={color.toRgb()}
onChange={({ r, g, b, a }) =>
handleChange(
`rgba(${r.toFixed(0)}, ${g.toFixed(0)}, ${b.toFixed(
0,
)}, ${a.toFixed(2)})`,
)
}
/>
);
break;
}
case 'hsl': {
const hsl = color.toHsl();
colorPicker = (
<HslColorPicker
sx={sxProps}
color={{ h: hsl.h, s: hsl.s * 100, l: hsl.l * 100 }}
onChange={({ h, s, l }) =>
handleChange(
`hsl(${h.toFixed(0)}, ${s.toFixed(0)}%, ${l.toFixed(0)}%)`,
)
}
/>
);
break;
}
case 'hsla': {
const hsl = color.toHsl();
colorPicker = (
<HslaColorPicker
sx={sxProps}
color={{ h: hsl.h, s: hsl.s * 100, l: hsl.l * 100, a: hsl.a }}
onChange={({ h, s, l, a }) =>
handleChange(
`hsla(${h.toFixed(0)}, ${s.toFixed(0)}%, ${l.toFixed(
0,
)}%, ${a.toFixed(2)})`,
)
}
/>
);
break;
}
}

return (
<Button
name={name}
onClick={() => setDisplayColorPicker(!displayColorPicker)}
css={{
paddingLeft: '10px',
minHeight: '36px',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
position: 'relative',
<Popover
trigger="click"
placement="bottom"
tooltipShown={isOpen}
onVisibilityChange={(isVisible: boolean) => {
setIsOpen(isVisible);
}}
tooltip={() => <Box sx={{ p: 2 }}>{colorPicker}</Box>}
>
<Box
<Button
css={{
left: 6,
width: 16,
height: 16,
marginRight: 10,
backgroundColor: control.value,
borderRadius: '1rem',
paddingLeft: '10px',
minHeight: '36px',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
position: 'relative',
}}
/>
{typeof control.value === 'string' && control.value.toUpperCase()}
{displayColorPicker ? (
aria-label="edit the selected color"
>
<Box
css={{
top: '32px',
position: 'absolute',
zIndex: 3,
left: 6,
width: 16,
height: 16,
marginRight: 10,
backgroundColor: control.value,
borderRadius: '1rem',
}}
>
<Box
css={{
position: 'fixed',
top: '0px',
right: '0px',
bottom: '0px',
left: '0px',
}}
onClick={() => setDisplayColorPicker(false)}
/>
<SketchPicker color={control.value} onChange={handleChange} />
</Box>
) : null}
</Button>
/>
{typeof control.value === 'string' && control.value}
<Box />
</Button>
</Popover>
);
};

Expand Down
Loading

0 comments on commit 64d8da5

Please sign in to comment.