zepio/app/components/select.js

210 lines
5.4 KiB
JavaScript
Raw Normal View History

2018-12-19 13:41:00 -08:00
// @flow
2019-02-04 20:41:45 -08:00
2018-12-19 13:41:00 -08:00
import React, { PureComponent } from 'react';
import styled from 'styled-components';
import { TextComponent } from './text';
import ChevronUp from '../assets/images/chevron-up.svg';
import ChevronDown from '../assets/images/chevron-down.svg';
2019-01-24 06:56:49 -08:00
import theme from '../theme';
/* eslint-disable max-len */
type SelectWrapperProps = PropsWithTheme<{
bgColor: ?string,
isOpen: boolean,
placement: string,
}>;
2018-12-19 13:41:00 -08:00
const SelectWrapper = styled.div`
align-items: center;
2018-12-19 13:41:00 -08:00
display: flex;
flex-direction: row;
border-radius: ${(props: SelectWrapperProps) => props.theme.boxBorderRadius};
2018-12-19 13:41:00 -08:00
border: none;
background-color: ${(props: SelectWrapperProps) => props.bgColor || props.theme.colors.inputBackground};
color: ${(props: SelectWrapperProps) => props.theme.colors.text};
2018-12-19 13:41:00 -08:00
width: 100%;
outline: none;
font-family: ${(props: SelectWrapperProps) => props.theme.fontFamily};
2018-12-19 13:41:00 -08:00
cursor: pointer;
position: relative;
${(props: SelectWrapperProps) => (props.isOpen
? `border-${String(props.placement)}-left-radius: 0; border-${String(
props.placement,
)}-right-radius: 0;`
: '')}
2018-12-19 13:41:00 -08:00
`;
2019-01-24 06:56:49 -08:00
/* eslint-enable max-len */
2018-12-19 13:41:00 -08:00
const ValueWrapper = styled.div`
width: 95%;
padding: 13px;
opacity: ${(props: PropsWithTheme<{ hasValue: boolean }>) => (props.hasValue ? '1' : '0.2')};
text-transform: ${(props: PropsWithTheme<{ capitalize: boolean }>) => (props.capitalize ? 'capitalize' : 'none')};
2018-12-20 10:55:27 -08:00
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
2018-12-19 13:41:00 -08:00
`;
const SelectMenuButtonWrapper = styled.div`
2018-12-19 13:41:00 -08:00
border: none;
background-color: transparent;
width: 50px;
height: 100%;
padding: 13px;
border-left: ${props => `1px solid ${props.theme.colors.background}`};
2018-12-19 13:41:00 -08:00
`;
2019-02-04 20:41:45 -08:00
2018-12-19 13:41:00 -08:00
/* eslint-disable max-len */
const SelectMenuButton = styled.button`
padding: 3px 7px;
2018-12-19 13:41:00 -08:00
outline: none;
background-color: transparent;
border: 1px solid
${(props: PropsWithTheme<{ isOpen: boolean }>) => (props.isOpen ? props.theme.colors.primary : '#29292D')};
2018-12-19 13:41:00 -08:00
border-radius: 100%;
`;
const Icon = styled.img`
width: 10px;
height: 10px;
`;
const OptionsWrapper = styled.div`
display: flex;
flex-direction: column;
position: absolute;
width: 100%;
${(props: PropsWithTheme<{ placement: string, optionsAmount: number }>) => `${String(props.placement)}: ${`-${String((props.optionsAmount || 0) * 40)}px`}`};
2019-01-10 05:07:02 -08:00
overflow-y: auto;
2018-12-19 13:41:00 -08:00
`;
const Option = styled.button`
border: none;
background: none;
2019-01-24 19:37:53 -08:00
height: 40px;
background-color: #5d5d5d;
2018-12-19 13:41:00 -08:00
cursor: pointer;
z-index: 99;
text-transform: ${(props: PropsWithTheme<{ capitalize: boolean }>) => (props.capitalize ? 'capitalize' : 'none')};
2019-01-24 19:37:53 -08:00
padding: 5px 10px;
border-bottom: 1px solid #4e4b4b;
2018-12-19 13:41:00 -08:00
&:hover {
background-color: ${props => props.theme.colors.background};
2018-12-19 13:41:00 -08:00
}
2019-01-24 19:37:53 -08:00
&:last-child {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: 1px solid transparent;
}
2018-12-19 13:41:00 -08:00
`;
type Props = {
value: string,
options: { value: string, label: string }[],
placeholder?: string,
onChange: string => void,
2018-12-20 09:16:46 -08:00
placement?: 'top' | 'bottom',
2019-01-24 06:56:49 -08:00
bgColor?: string,
capitalize?: boolean,
2018-12-19 13:41:00 -08:00
};
2019-02-04 20:41:45 -08:00
2018-12-19 13:41:00 -08:00
type State = {
isOpen: boolean,
};
export class SelectComponent extends PureComponent<Props, State> {
state = {
isOpen: false,
};
static defaultProps = {
placeholder: '',
2018-12-20 09:16:46 -08:00
placement: 'bottom',
2019-01-24 06:56:49 -08:00
bgColor: theme.colors.inputBackground,
capitalize: true,
2018-12-19 13:41:00 -08:00
};
onSelect = (value: string) => {
const { onChange } = this.props;
this.setState(() => ({ isOpen: false }), () => onChange(value));
};
handleClickOutside = (event: Object) => {
const { isOpen } = this.state;
2019-02-04 20:41:45 -08:00
if (isOpen && event.target.id !== 'select-options-wrapper') {
this.setState(() => ({ isOpen: false }));
}
2018-12-19 13:41:00 -08:00
};
getSelectedLabel = (value: string) => {
const { options } = this.props;
const option = options.find(opt => opt.value === value);
if (option) return option.label;
};
2018-12-20 09:16:46 -08:00
getSelectIcon = () => {
const { placement } = this.props;
const { isOpen } = this.state;
2019-01-24 06:56:49 -08:00
if (placement === 'top') {
2019-01-24 19:37:53 -08:00
return !isOpen ? ChevronUp : ChevronDown;
2018-12-20 09:16:46 -08:00
}
2019-01-24 19:37:53 -08:00
return !isOpen ? ChevronDown : ChevronUp;
2018-12-20 09:16:46 -08:00
};
2018-12-19 13:41:00 -08:00
render() {
2019-02-04 20:41:45 -08:00
const { isOpen } = this.state;
2018-12-20 09:16:46 -08:00
const {
value, options, placeholder, placement, bgColor, capitalize,
2018-12-20 09:16:46 -08:00
} = this.props;
2018-12-19 13:41:00 -08:00
return (
<SelectWrapper
data-testid='Select'
2019-01-23 09:04:11 -08:00
id='select-component'
isOpen={isOpen}
placement={placement}
onClick={() => this.setState(() => ({ isOpen: !isOpen }))}
2019-01-24 06:56:49 -08:00
bgColor={bgColor}
>
<ValueWrapper hasValue={Boolean(value)} capitalize={capitalize}>
{this.getSelectedLabel(value) || placeholder}
2018-12-19 13:41:00 -08:00
</ValueWrapper>
<SelectMenuButtonWrapper>
2019-01-24 06:56:49 -08:00
<SelectMenuButton isOpen={isOpen}>
2018-12-20 09:16:46 -08:00
<Icon src={this.getSelectIcon()} />
2018-12-19 13:41:00 -08:00
</SelectMenuButton>
</SelectMenuButtonWrapper>
{isOpen && (
<OptionsWrapper
id='select-options-wrapper'
optionsAmount={options.length}
2018-12-20 09:16:46 -08:00
placement={placement}
2018-12-19 13:41:00 -08:00
>
{options.map(({ label, value: optionValue }) => (
2018-12-20 09:16:46 -08:00
<Option
2019-01-23 09:04:11 -08:00
id={optionValue}
2018-12-20 09:16:46 -08:00
key={label + optionValue}
onClick={() => this.onSelect(optionValue)}
2019-01-24 06:56:49 -08:00
bgColor={bgColor}
capitalize={capitalize}
2018-12-20 09:16:46 -08:00
>
2018-12-19 13:41:00 -08:00
<TextComponent value={label} />
</Option>
))}
</OptionsWrapper>
)}
</SelectWrapper>
);
}
}