You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
189 lines
4.4 KiB
189 lines
4.4 KiB
import React from 'react'
|
|
import PropTypes from 'prop-types'
|
|
import { Box, Flex } from 'rebass'
|
|
import styled, { withTheme } from 'styled-components'
|
|
import FaAngleDown from 'react-icons/lib/fa/angle-down'
|
|
import FaAngleUp from 'react-icons/lib/fa/angle-up'
|
|
import Check from 'components/Icon/Check'
|
|
import Text from 'components/UI/Text'
|
|
|
|
/**
|
|
* Container
|
|
*/
|
|
const DropdownContainer = styled(Flex)({})
|
|
DropdownContainer.defaultProps = {
|
|
flexDirection: 'column',
|
|
flexWrap: 'none',
|
|
position: 'relative'
|
|
}
|
|
|
|
/**
|
|
* Button
|
|
*/
|
|
const DropdownButton = styled(Box)({
|
|
appearance: 'none',
|
|
display: 'inline-block',
|
|
textAlign: 'center',
|
|
lineHeight: 'inherit',
|
|
textDecoration: 'none',
|
|
border: 'none',
|
|
outline: 'none',
|
|
background: 'transparent',
|
|
color: 'inherit',
|
|
cursor: 'pointer'
|
|
})
|
|
DropdownButton.defaultProps = {
|
|
as: 'button',
|
|
m: 0,
|
|
p: 0,
|
|
textAlign: 'left',
|
|
justify: 'left'
|
|
}
|
|
|
|
/**
|
|
* Menu
|
|
*/
|
|
const MenuContainer = styled(Box)({
|
|
position: 'relative'
|
|
})
|
|
|
|
const Menu = styled(Box)({
|
|
cursor: 'pointer',
|
|
display: 'inline-block',
|
|
position: 'absolute',
|
|
'z-index': '999',
|
|
'min-width': '70px',
|
|
'list-style-type': 'none',
|
|
'border-radius': '3px',
|
|
'box-shadow': '0 3px 4px 0 rgba(30, 30, 30, 0.5)'
|
|
})
|
|
Menu.defaultProps = {
|
|
as: 'ul',
|
|
m: 0,
|
|
mt: 1,
|
|
p: 0,
|
|
bg: 'secondaryColor'
|
|
}
|
|
|
|
/**
|
|
* MenuItem
|
|
*/
|
|
const MenuItem = styled(Box)`
|
|
cursor: pointer;
|
|
&:hover {
|
|
background-color: ${props => props.theme.colors.primaryColor};
|
|
}
|
|
`
|
|
MenuItem.defaultProps = {
|
|
as: 'li',
|
|
px: 2,
|
|
py: 2
|
|
}
|
|
|
|
/**
|
|
* @render react
|
|
* @name Dropdown
|
|
* @example
|
|
* <Dropdown items={[
|
|
* {name: 'Item 1', key: 'key1'},
|
|
* {name: 'Item 2', key: 'key2'}
|
|
* ]} activeKey="key1" />
|
|
*/
|
|
class Dropdown extends React.Component {
|
|
state = {
|
|
isOpen: false
|
|
}
|
|
onChange = this.onChange.bind(this)
|
|
toggleMenu = this.toggleMenu.bind(this)
|
|
setWrapperRef = this.setWrapperRef.bind(this)
|
|
handleClickOutside = this.handleClickOutside.bind(this)
|
|
|
|
static propTypes = {
|
|
activeKey: PropTypes.string.isRequired,
|
|
items: PropTypes.array.isRequired,
|
|
onChange: PropTypes.func
|
|
}
|
|
|
|
componentDidMount() {
|
|
document.addEventListener('mousedown', this.handleClickOutside)
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
document.removeEventListener('mousedown', this.handleClickOutside)
|
|
}
|
|
|
|
onChange(key) {
|
|
const { onChange, activeKey } = this.props
|
|
if (key !== activeKey) {
|
|
if (onChange) {
|
|
onChange(key)
|
|
}
|
|
}
|
|
this.setState({ isOpen: false })
|
|
}
|
|
|
|
setWrapperRef(node) {
|
|
this.wrapperRef = node
|
|
}
|
|
|
|
handleClickOutside(event) {
|
|
if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
|
|
this.setState({ isOpen: false })
|
|
}
|
|
}
|
|
|
|
toggleMenu() {
|
|
const { isOpen } = this.state
|
|
this.setState({ isOpen: !isOpen })
|
|
}
|
|
|
|
render() {
|
|
const { isOpen } = this.state
|
|
let { activeKey, items, justify, theme, ...rest } = this.props
|
|
// coerce array of strings into array of objects.
|
|
items = items.map(item => {
|
|
if (typeof item === 'string') {
|
|
return {
|
|
name: item,
|
|
key: item
|
|
}
|
|
}
|
|
return item
|
|
})
|
|
const selectedItem = items.find(c => c.key === activeKey)
|
|
return (
|
|
<div style={{ display: 'inline-block' }}>
|
|
<DropdownContainer ref={this.setWrapperRef} {...rest}>
|
|
<DropdownButton type="button" onClick={this.toggleMenu}>
|
|
<Text textAlign="left">
|
|
{selectedItem ? selectedItem.name : activeKey}{' '}
|
|
{isOpen ? <FaAngleUp /> : <FaAngleDown />}
|
|
</Text>
|
|
</DropdownButton>
|
|
{isOpen && (
|
|
<MenuContainer>
|
|
<Menu css={justify === 'right' ? { right: 0 } : null}>
|
|
{items.map(item => {
|
|
return (
|
|
<MenuItem key={item.key} onClick={() => this.onChange(item.key)}>
|
|
<Flex alignItems="center">
|
|
<Text width="20px" textAlign="center">
|
|
{activeKey === item.key && (
|
|
<Check height="0.95em" color={theme.colors.superGreen} />
|
|
)}
|
|
</Text>
|
|
<Text>{item.name}</Text>
|
|
</Flex>
|
|
</MenuItem>
|
|
)
|
|
})}
|
|
</Menu>
|
|
</MenuContainer>
|
|
)}
|
|
</DropdownContainer>
|
|
</div>
|
|
)
|
|
}
|
|
}
|
|
|
|
export default withTheme(Dropdown)
|
|
|