This repository has been archived on 2022-08-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
outline/app/components/ColorPicker.js
Tom Moor 032d843f5b Fix isInternalUrl for subdomain support
Refactor / reduce plumbing
2018-11-18 18:43:11 -08:00

190 lines
4.3 KiB
JavaScript

// @flow
import * as React from 'react';
import { observable, computed, action } from 'mobx';
import { observer } from 'mobx-react';
import styled from 'styled-components';
import Flex from 'shared/components/Flex';
import { LabelText, Outline } from 'components/Input';
import { validateColorHex } from 'shared/utils/color';
const colors = [
'#4E5C6E',
'#19B7FF',
'#7F6BFF',
'#FC7419',
'#FC2D2D',
'#FFE100',
'#14CF9F',
'#EE84F0',
'#2F362F',
];
type Props = {
onSelect: (color: string) => void,
value?: string,
};
@observer
class ColorPicker extends React.Component<Props> {
@observable selectedColor: string = colors[0];
@observable customColorValue: string = '';
@observable customColorSelected: boolean;
componentWillMount() {
const { value } = this.props;
if (value && colors.includes(value)) {
this.selectedColor = value;
} else if (value) {
this.customColorSelected = true;
this.customColorValue = value.replace('#', '');
}
}
componentDidMount() {
this.fireCallback();
}
fireCallback = () => {
this.props.onSelect(
this.customColorSelected ? this.customColor : this.selectedColor
);
};
@computed
get customColor(): string {
return this.customColorValue &&
validateColorHex(`#${this.customColorValue}`)
? `#${this.customColorValue}`
: colors[0];
}
@action
setColor = (color: string) => {
this.selectedColor = color;
this.customColorSelected = false;
this.fireCallback();
};
@action
focusOnCustomColor = (event: SyntheticEvent<*>) => {
this.selectedColor = '';
this.customColorSelected = true;
this.fireCallback();
};
@action
setCustomColor = (event: SyntheticEvent<*>) => {
let target = event.target;
if (target instanceof HTMLInputElement) {
const color = target.value;
this.customColorValue = color.replace('#', '');
this.fireCallback();
}
};
render() {
return (
<Flex column>
<LabelText>Color</LabelText>
<StyledOutline justify="space-between">
<Flex>
{colors.map(color => (
<Swatch
key={color}
color={color}
active={
color === this.selectedColor && !this.customColorSelected
}
onClick={() => this.setColor(color)}
/>
))}
</Flex>
<Flex justify="flex-end">
<strong>Custom color:</strong>
<HexHash>#</HexHash>
<CustomColorInput
placeholder="FFFFFF"
onFocus={this.focusOnCustomColor}
onChange={this.setCustomColor}
value={this.customColorValue}
maxLength={6}
/>
<Swatch
color={this.customColor}
active={this.customColorSelected}
/>
</Flex>
</StyledOutline>
</Flex>
);
}
}
type SwatchProps = {
onClick?: () => void,
color?: string,
active?: boolean,
};
const Swatch = ({ onClick, ...props }: SwatchProps) => (
<SwatchOutset onClick={onClick} {...props}>
<SwatchInset {...props} />
</SwatchOutset>
);
const SwatchOutset = styled(Flex)`
width: 24px;
height: 24px;
margin-right: 5px;
border: 2px solid ${({ active, color }) => (active ? color : 'transparent')};
border-radius: 2px;
background: ${({ color }) => color};
${({ onClick }) => onClick && `cursor: pointer;`} &:last-child {
margin-right: 0;
}
`;
const SwatchInset = styled(Flex)`
width: 20px;
height: 20px;
border: 1px solid ${({ active, color }) => (active ? 'white' : 'transparent')};
border-radius: 2px;
background: ${({ color }) => color};
`;
const StyledOutline = styled(Outline)`
padding: 5px;
flex-wrap: wrap;
strong {
font-weight: 500;
}
`;
const HexHash = styled.div`
margin-left: 12px;
padding-bottom: 0;
font-weight: 500;
user-select: none;
`;
const CustomColorInput = styled.input`
border: 0;
flex: 1;
width: 65px;
margin-right: 12px;
padding-bottom: 0;
outline: none;
background: none;
font-family: ${props => props.theme.monospaceFontFamily};
font-weight: 500;
&::placeholder {
color: ${props => props.theme.slate};
font-family: ${props => props.theme.monospaceFontFamily};
font-weight: 500;
}
`;
export default ColorPicker;