feat(joyent-ui-toolkit): Add dropdown component
This commit is contained in:
parent
edc5fcba87
commit
3fc72f667e
@ -1,40 +1,42 @@
|
||||
import { css } from 'styled-components';
|
||||
import remcalc from 'remcalc';
|
||||
|
||||
const bounds = {
|
||||
export const breakpoints = {
|
||||
small: {
|
||||
upper: remcalc(768)
|
||||
upper: 768
|
||||
},
|
||||
medium: {
|
||||
upper: remcalc(1024),
|
||||
lower: remcalc(769)
|
||||
upper: 1024,
|
||||
lower: 769
|
||||
},
|
||||
large: {
|
||||
upper: remcalc(1200),
|
||||
lower: remcalc(1025)
|
||||
upper: 1200,
|
||||
lower: 1025
|
||||
},
|
||||
xlarge: {
|
||||
lower: remcalc(1201)
|
||||
lower: 1201
|
||||
}
|
||||
};
|
||||
|
||||
const screens = {
|
||||
// >= 768px
|
||||
smallOnly: `only screen and (max-width: ${bounds.small.upper})`,
|
||||
small: `only screen and (min-width: ${bounds.small.upper}})`,
|
||||
smallOnly: `only screen and (max-width: ${remcalc(breakpoints.small.upper)})`,
|
||||
small: `only screen and (min-width: ${remcalc(breakpoints.small.upper)})`,
|
||||
// >= 1024px
|
||||
mediumOnly: `only screen and (min-width: ${bounds.medium.lower})
|
||||
and (max-width: ${bounds.medium.upper})`,
|
||||
mediumDown: `only screen and (max-width: ${bounds.medium.upper})`,
|
||||
medium: `only screen and (min-width: ${bounds.medium.lower})`,
|
||||
mediumOnly: `only screen and (min-width: ${remcalc(breakpoints.medium.lower)})
|
||||
and (max-width: ${remcalc(breakpoints.medium.upper)})`,
|
||||
mediumDown: `only screen and (max-width: ${remcalc(
|
||||
breakpoints.medium.upper
|
||||
)})`,
|
||||
medium: `only screen and (min-width: ${remcalc(breakpoints.medium.lower)})`,
|
||||
// >= 1200px
|
||||
largeOnly: `only screen and (min-width: ${bounds.large.lower})
|
||||
and (max-width: ${bounds.large.upper})`,
|
||||
largeDown: `only screen and (max-width: ${bounds.large.upper})`,
|
||||
large: `only screen and (min-width: ${bounds.large.upper})`,
|
||||
xlarge: `only screen and (min-width: ${bounds.xlarge.lower})
|
||||
and (max-width: ${bounds.xlarge.upper})`,
|
||||
xlargeUp: `only screen and (min-width: ${bounds.xlarge.lower})`
|
||||
largeOnly: `only screen and (min-width: ${remcalc(breakpoints.large.lower)})
|
||||
and (max-width: ${remcalc(breakpoints.large.upper)})`,
|
||||
largeDown: `only screen and (max-width: ${remcalc(breakpoints.large.upper)})`,
|
||||
large: `only screen and (min-width: ${remcalc(breakpoints.large.upper)})`,
|
||||
xlarge: `only screen and (min-width: ${remcalc(breakpoints.xlarge.lower)})
|
||||
and (max-width: ${remcalc(breakpoints.xlarge.upper)})`,
|
||||
xlargeUp: `only screen and (min-width: ${remcalc(breakpoints.xlarge.lower)})`
|
||||
};
|
||||
|
||||
const breakpoint = label => (...args) => css`
|
||||
@ -64,5 +66,6 @@ export default {
|
||||
largeDown,
|
||||
large,
|
||||
xlarge,
|
||||
xlargeUp
|
||||
xlargeUp,
|
||||
breakpoints
|
||||
};
|
||||
|
131
packages/ui-toolkit/src/dropdown/index.js
Normal file
131
packages/ui-toolkit/src/dropdown/index.js
Normal file
@ -0,0 +1,131 @@
|
||||
import React, { Component } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
import { Select } from '../form';
|
||||
import { Tooltip, TooltipButton as DropdownItem } from '../tooltip';
|
||||
import Baseline from '../baseline';
|
||||
import { small, smallOnly } from '../breakpoints';
|
||||
import { ArrowIcon } from '../icons';
|
||||
import remcalc from 'remcalc';
|
||||
|
||||
const StyledSelectList = styled(Tooltip)`
|
||||
${smallOnly`
|
||||
display: none;
|
||||
`};
|
||||
width: 100%;
|
||||
ul {
|
||||
position: relative;
|
||||
display: block;
|
||||
left: auto;
|
||||
}
|
||||
ul:after, ul:before {
|
||||
left: 97%;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledSelect = styled(Select)`
|
||||
${small`
|
||||
option {
|
||||
display: none;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const StyledArrowIcon = styled(ArrowIcon)`
|
||||
${smallOnly`
|
||||
display: none;
|
||||
`};
|
||||
position: absolute;
|
||||
left: 97%;
|
||||
top: 50%;
|
||||
margin-left: -${remcalc(4.5)};
|
||||
`;
|
||||
|
||||
const Container = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
/**
|
||||
* @example ./usage.md
|
||||
*/
|
||||
class Dropdown extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.toggleDropdown = this.toggleDropdown.bind(this);
|
||||
this.dropdownOnChange = this.dropdownOnChange.bind(this);
|
||||
this.dropdownOnBlur = this.dropdownOnBlur.bind(this);
|
||||
|
||||
this.state = {
|
||||
isDroppedDown: false
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
window.addEventListener('click', this.dropdownOnBlur);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
window.addEventListener('click', this.dropdownOnBlur);
|
||||
}
|
||||
toggleDropdown() {
|
||||
this.setState(prevState => ({ isDroppedDown: !prevState.isDroppedDown }));
|
||||
}
|
||||
dropdownOnBlur(ev) {
|
||||
if (
|
||||
!ReactDOM.findDOMNode(this).contains(ev.target) &&
|
||||
this.state.isDroppedDown
|
||||
) {
|
||||
this.toggleDropdown();
|
||||
}
|
||||
}
|
||||
dropdownOnChange(ev) {
|
||||
this.setState({ isDroppedDown: false });
|
||||
if (!this.props.onChange) {
|
||||
return;
|
||||
}
|
||||
this.props.onChange(ev.target.value);
|
||||
}
|
||||
render() {
|
||||
const { data, placeholder, className, id, ...rest } = this.props;
|
||||
return (
|
||||
<Container className={className} id={id}>
|
||||
<StyledSelect
|
||||
defaultValue={placeholder}
|
||||
onChange={this.dropdownOnChange}
|
||||
onClick={this.toggleDropdown}
|
||||
{...rest}
|
||||
>
|
||||
<option disabled value={placeholder}>{placeholder}</option>
|
||||
{data.map((val, index) =>
|
||||
<option value={val} key={index}>{val}</option>
|
||||
)}
|
||||
</StyledSelect>
|
||||
<StyledArrowIcon onClick={this.toggleDropdown} />
|
||||
{this.state.isDroppedDown &&
|
||||
<StyledSelectList>
|
||||
{data.map((val, index) =>
|
||||
<DropdownItem
|
||||
key={index}
|
||||
value={val}
|
||||
onClick={this.dropdownOnChange}
|
||||
>
|
||||
{val}
|
||||
</DropdownItem>
|
||||
)}
|
||||
</StyledSelectList>}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Dropdown.propTypes = {
|
||||
data: PropTypes.arrayOf(PropTypes.string),
|
||||
placeholder: PropTypes.string,
|
||||
onChange: PropTypes.func
|
||||
};
|
||||
|
||||
Dropdown.defaultProps = {
|
||||
placeholder: 'Choose'
|
||||
};
|
||||
|
||||
export default Baseline(Dropdown);
|
10
packages/ui-toolkit/src/dropdown/usage.md
Normal file
10
packages/ui-toolkit/src/dropdown/usage.md
Normal file
@ -0,0 +1,10 @@
|
||||
```
|
||||
const Dropdown = require('./index').default;
|
||||
const Label = require('../form/label').default;
|
||||
|
||||
<div style={{ position: 'relative', height: '175px' }}>
|
||||
<Label>Service</Label>
|
||||
<Dropdown placeholder="Choose" data={["Wordpress", "Nginx", "Percona"]}>
|
||||
</Dropdown>
|
||||
</div>
|
||||
```
|
7
packages/ui-toolkit/src/icons/arrow.js
Normal file
7
packages/ui-toolkit/src/icons/arrow.js
Normal file
@ -0,0 +1,7 @@
|
||||
import Baseline from '../baseline';
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import React from 'react';
|
||||
|
||||
import ArrowIcon from './svg/icon_arrow.svg';
|
||||
|
||||
export default Baseline(ArrowIcon);
|
@ -1,3 +1,4 @@
|
||||
export { default as CloseIcon } from './close';
|
||||
export { default as PlusIcon } from './plus';
|
||||
export { default as MinusIcon } from './minus';
|
||||
export { default as ArrowIcon } from './arrow';
|
||||
|
@ -18,6 +18,7 @@ export { default as CloseButton } from './close-button';
|
||||
export { default as IconButton } from './icon-button';
|
||||
|
||||
export { Tooltip, TooltipButton, TooltipDivider } from './tooltip';
|
||||
export { Dropdown } from './dropdown';
|
||||
|
||||
export {
|
||||
borderRadius,
|
||||
|
@ -88,11 +88,19 @@ class Tooltip extends Component {
|
||||
top = 'auto',
|
||||
left = 'auto',
|
||||
bottom = 'auto',
|
||||
right = 'auto'
|
||||
right = 'auto',
|
||||
className,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<StyledContainer top={top} left={left} bottom={bottom} right={right}>
|
||||
<StyledContainer
|
||||
className={className}
|
||||
top={top}
|
||||
left={left}
|
||||
bottom={bottom}
|
||||
right={right}
|
||||
{...rest}
|
||||
>
|
||||
<StyledList>
|
||||
{children}
|
||||
</StyledList>
|
||||
|
@ -59,6 +59,7 @@ module.exports = {
|
||||
'src/form/input.js',
|
||||
'src/form/number-input.js',
|
||||
'src/form/checkbox.js',
|
||||
'src/dropdown/index.js',
|
||||
'src/form/radio.js',
|
||||
'src/form/select.js',
|
||||
'src/form/toggle.js',
|
||||
|
Loading…
Reference in New Issue
Block a user