Skip to content

Commit

Permalink
feat: implement ColorPicker component
Browse files Browse the repository at this point in the history
  • Loading branch information
yvmunayev committed Aug 13, 2020
1 parent a1313a9 commit fcfd4a6
Show file tree
Hide file tree
Showing 33 changed files with 1,042 additions and 33 deletions.
28 changes: 28 additions & 0 deletions src/components/ColorPicker/alpha.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';
import { StyledAlphaSlider } from './styled';
import { recomposeColor } from '../../styles/helpers/color';
import { getAlpha } from './helpers';

export default function Alpha(props) {
const { rgbaColor, onChange } = props;
const a = getAlpha(rgbaColor);

const handleChange = event => {
const alpha = parseInt(event.target.value, 10);
rgbaColor.values[3] = alpha / 100;
onChange(recomposeColor(rgbaColor));
};

return <StyledAlphaSlider value={a} min={0} max={100} onChange={handleChange} />;
}

Alpha.propTypes = {
rgbaColor: PropTypes.object,
onChange: PropTypes.func,
};

Alpha.defaultProps = {
rgbaColor: '',
onChange: () => {},
};
37 changes: 37 additions & 0 deletions src/components/ColorPicker/colors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import PropTypes from 'prop-types';
import { StyledDefaulColor, StyledColorsContainer } from './styled';
import { colorToRgba, isValidColor } from '../../styles/helpers/color';

const Colors = React.memo(props => {
const { colors, onChange } = props;

const handleChange = color => {
const rgbaColor = colorToRgba(color);
if (isValidColor(rgbaColor)) {
onChange(rgbaColor);
}
};
const ListColors = () =>
colors.map(color => (
<StyledDefaulColor key={color} $color={color} onClick={() => handleChange(color)} />
));

return (
<StyledColorsContainer>
<ListColors />
</StyledColorsContainer>
);
});

Colors.propTypes = {
colors: PropTypes.array,
onChange: PropTypes.func,
};

Colors.defaultProps = {
colors: [],
onChange: () => {},
};

export default Colors;
3 changes: 3 additions & 0 deletions src/components/ColorPicker/helpers/getAlpha.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function getAlpha(rgbaColor) {
return Math.round(rgbaColor.values[3] * 100);
}
3 changes: 3 additions & 0 deletions src/components/ColorPicker/helpers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as getAlpha } from './getAlpha';
export { default as normalazeColor } from './normalazeColor';
export { default as isAchromatic } from './isAchromatic';
7 changes: 7 additions & 0 deletions src/components/ColorPicker/helpers/isAchromatic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function isAchromatic(color) {
const [r, g, b] = color.values;

const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
return max === min;
}
6 changes: 6 additions & 0 deletions src/components/ColorPicker/helpers/normalazeColor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { colorToRgba } from '../../../styles/helpers/color';

export default function normalazeColor(color) {
const rgbaColor = colorToRgba(color);
return rgbaColor !== '' ? rgbaColor : 'rgba(0, 0, 0, 1)';
}
58 changes: 58 additions & 0 deletions src/components/ColorPicker/hexColor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import Input from '../Input';
import { StyledHexColorIcon } from './styled';
import { hexToRgba } from '../../styles/helpers/color';

export default function HexColor(props) {
const { hexColor, onChange } = props;
const [color, setColor] = useState();
const [isFocused, setIsFocused] = useState(false);

useEffect(() => {
if (!isFocused) {
setColor(hexColor);
}
}, [hexColor, isFocused]);

const handleChange = event => {
const value = event.target.value;
setColor(value);
const hex = `#${value}`;
const rgbaColor = hexToRgba(hex);
if (rgbaColor !== '') {
onChange(rgbaColor);
}
};

const handleBlur = event => {
setIsFocused(false);
const value = event.target.value;
const hex = `#${value}`;
const rgbaColor = hexToRgba(hex);
if (rgbaColor !== '') {
onChange(rgbaColor);
}
};

return (
<Input
value={color}
bottomHelpText="HEX"
onChange={handleChange}
onFocus={() => setIsFocused(true)}
onBlur={handleBlur}
icon={<StyledHexColorIcon>#</StyledHexColorIcon>}
/>
);
}

HexColor.propTypes = {
hexColor: PropTypes.string,
onChange: PropTypes.func,
};

HexColor.defaultProps = {
hexColor: '',
onChange: () => {},
};
35 changes: 35 additions & 0 deletions src/components/ColorPicker/hue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react';
import PropTypes from 'prop-types';
import { StyledHueSlider } from './styled';
import { hsvToRgb, rgbToRgba } from '../../styles/helpers/color';

export default function Hue(props) {
const { hsvColor, hue, setHue, alpha, onChange } = props;

const handleChange = event => {
const value = parseInt(event.target.value, 10);
setHue(value);
hsvColor.values[0] = value;
const rgbColor = hsvToRgb(hsvColor);
const rgbaColor = rgbToRgba(rgbColor, alpha);
onChange(rgbaColor);
};

return <StyledHueSlider value={hue} min={0} max={359} onChange={handleChange} />;
}

Hue.propTypes = {
hsvColor: PropTypes.object,
alpha: PropTypes.number,
hue: PropTypes.number,
setHue: PropTypes.func,
onChange: PropTypes.func,
};

Hue.defaultProps = {
hsvColor: undefined,
alpha: undefined,
hue: undefined,
setHue: () => {},
onChange: () => {},
};
9 changes: 9 additions & 0 deletions src/components/ColorPicker/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { BaseProps } from '../types';

export interface ColorPickerProps extends BaseProps {
id?: string;
color?: string;
onChange?: (value: string) => void;
}

export default function(props: ColorPickerProps): JSX.Element | null;
105 changes: 105 additions & 0 deletions src/components/ColorPicker/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import RenderIf from '../RenderIf';
import Saturation from './saturation';
import RgbaColor from './rgbaColor';
import HexColor from './hexColor';
import Alpha from './alpha';
import Hue from './hue';
import Colors from './colors';
import {
StyledContainer,
StyledPreview,
StyledFlexContainer,
StyledLabel,
StyledSaturationContainer,
StyledSlidersContainer,
StyledHexColorContainer,
StyledRgbaColorContainer,
} from './styled';
import { rgbaToHex, rgbToHsv, decomposeColor } from '../../styles/helpers/color';
import { normalazeColor, isAchromatic } from './helpers';

export default function ColorPicker(props) {
const { id, color: colorProp, colors, label, labelColors, onChange, className, style } = props;
const [hue, setHue] = useState(0);

const color = normalazeColor(colorProp);
const rgbaColor = decomposeColor(color);
const hexColor = rgbaToHex(rgbaColor);
const hsvColor = decomposeColor(rgbToHsv(color));
const alpha = rgbaColor.values[3];

useEffect(() => {
if (!isAchromatic(rgbaColor)) {
setHue(hsvColor.values[0]);
}
}, [hsvColor.values, rgbaColor]);

const hasColors = Array.isArray(colors) && colors.length > 0;

return (
<StyledContainer className={className} style={style} id={id}>
<StyledLabel>{label}</StyledLabel>
<StyledSaturationContainer>
<Saturation rgbaColor={rgbaColor} hsvColor={hsvColor} onChange={onChange} />
</StyledSaturationContainer>
<StyledFlexContainer>
<StyledSlidersContainer>
<Hue
hsvColor={hsvColor}
alpha={alpha}
hue={hue}
setHue={setHue}
onChange={onChange}
/>
<Alpha rgbaColor={rgbaColor} onChange={onChange} />
</StyledSlidersContainer>
<StyledPreview $color={color} />
</StyledFlexContainer>

<StyledFlexContainer>
<StyledHexColorContainer>
<HexColor hexColor={hexColor} onChange={onChange} />
</StyledHexColorContainer>
<StyledRgbaColorContainer>
<RgbaColor rgbaColor={rgbaColor} onChange={onChange} />
</StyledRgbaColorContainer>
</StyledFlexContainer>
<RenderIf isTrue={hasColors}>
<StyledLabel>{labelColors}</StyledLabel>
<Colors colors={colors} onChange={onChange} />
</RenderIf>
</StyledContainer>
);
}

ColorPicker.propTypes = {
/** The id of the outer element. */
id: PropTypes.string,
/** Specifies the color of ColorPicker. */
color: PropTypes.string,
/** Specifies the default colors to choice. */
colors: PropTypes.array,
/** Text label for the input. */
label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
/** Text label for the default colors. */
labelColors: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
/** The action triggered when the value changes. */
onChange: PropTypes.func,
/** A CSS class for the outer element, in addition to the component's base classes. */
className: PropTypes.string,
/** An object with custom style applied to the outer element. */
style: PropTypes.object,
};

ColorPicker.defaultProps = {
id: undefined,
color: '',
colors: [],
label: 'Color Picker',
labelColors: 'Defualt',
onChange: () => {},
className: undefined,
style: undefined,
};
97 changes: 97 additions & 0 deletions src/components/ColorPicker/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
##### ColorPicker info

```js
import React, { useState } from 'react';
import styled from 'styled-components'
import { ColorPicker, Card } from 'react-rainbow-components';

const Container = styled.div`
display: flex;
justify-content: center;
align-items: center;
padding: 50px;
`;

const StyledCard = styled(Card)`
width: 26rem;
height: 27rem;
`;

const ColorPickerExample = () => {
const [color, setColor] = useState('#ff0');
const handleChange = value => {
// console.log(value);
setColor(value);
}
return <ColorPicker color={color} onChange={handleChange}/>;
}

<Container>
<StyledCard>
<ColorPickerExample />
</StyledCard>
</Container>
```

##### ColorPicker with default colors

```js
import React, {useState, useCallback} from 'react';
import styled from 'styled-components'
import { ColorPicker, Card } from 'react-rainbow-components';

const colors = [
'#e3aaec',
'#c3dbf7',
'#9fd6ff',
'#9de7da',
'#9ef0bf',
'#fef199',
'#fdd499',
'#d174e0',
'#86baf3',
'#5ebbff',
'#42d8be',
'#3be282',
'#ffe654',
'#ffb758',
'#bd35bd',
'#5779c1',
'#4A90E2',
'#06aea9',
'#3dba4c',
'#f5bc24',
'#f99222',
'#570e8c',
'#021970',
'#0b2399',
'#0d7477',
'#0a6b50',
'#b67e12',
'#b75d0c',
];

const Container = styled.div`
display: flex;
justify-content: center;
align-items: center;
padding: 50px;
`;

const StyledCard = styled(Card)`
width: 26rem;
height: 40rem;
`;

const ColorPickerExample = () => {
const [color, setColor] = useState('#ff0');

return <ColorPicker color={color} onChange={setColor} colors={colors} />;
}

<Container>
<StyledCard>
<ColorPickerExample />
</StyledCard>
</Container>
```
Loading

0 comments on commit fcfd4a6

Please sign in to comment.