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 { css } from 'styled-components';
|
||||||
import remcalc from 'remcalc';
|
import remcalc from 'remcalc';
|
||||||
|
|
||||||
const bounds = {
|
export const breakpoints = {
|
||||||
small: {
|
small: {
|
||||||
upper: remcalc(768)
|
upper: 768
|
||||||
},
|
},
|
||||||
medium: {
|
medium: {
|
||||||
upper: remcalc(1024),
|
upper: 1024,
|
||||||
lower: remcalc(769)
|
lower: 769
|
||||||
},
|
},
|
||||||
large: {
|
large: {
|
||||||
upper: remcalc(1200),
|
upper: 1200,
|
||||||
lower: remcalc(1025)
|
lower: 1025
|
||||||
},
|
},
|
||||||
xlarge: {
|
xlarge: {
|
||||||
lower: remcalc(1201)
|
lower: 1201
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const screens = {
|
const screens = {
|
||||||
// >= 768px
|
// >= 768px
|
||||||
smallOnly: `only screen and (max-width: ${bounds.small.upper})`,
|
smallOnly: `only screen and (max-width: ${remcalc(breakpoints.small.upper)})`,
|
||||||
small: `only screen and (min-width: ${bounds.small.upper}})`,
|
small: `only screen and (min-width: ${remcalc(breakpoints.small.upper)})`,
|
||||||
// >= 1024px
|
// >= 1024px
|
||||||
mediumOnly: `only screen and (min-width: ${bounds.medium.lower})
|
mediumOnly: `only screen and (min-width: ${remcalc(breakpoints.medium.lower)})
|
||||||
and (max-width: ${bounds.medium.upper})`,
|
and (max-width: ${remcalc(breakpoints.medium.upper)})`,
|
||||||
mediumDown: `only screen and (max-width: ${bounds.medium.upper})`,
|
mediumDown: `only screen and (max-width: ${remcalc(
|
||||||
medium: `only screen and (min-width: ${bounds.medium.lower})`,
|
breakpoints.medium.upper
|
||||||
|
)})`,
|
||||||
|
medium: `only screen and (min-width: ${remcalc(breakpoints.medium.lower)})`,
|
||||||
// >= 1200px
|
// >= 1200px
|
||||||
largeOnly: `only screen and (min-width: ${bounds.large.lower})
|
largeOnly: `only screen and (min-width: ${remcalc(breakpoints.large.lower)})
|
||||||
and (max-width: ${bounds.large.upper})`,
|
and (max-width: ${remcalc(breakpoints.large.upper)})`,
|
||||||
largeDown: `only screen and (max-width: ${bounds.large.upper})`,
|
largeDown: `only screen and (max-width: ${remcalc(breakpoints.large.upper)})`,
|
||||||
large: `only screen and (min-width: ${bounds.large.upper})`,
|
large: `only screen and (min-width: ${remcalc(breakpoints.large.upper)})`,
|
||||||
xlarge: `only screen and (min-width: ${bounds.xlarge.lower})
|
xlarge: `only screen and (min-width: ${remcalc(breakpoints.xlarge.lower)})
|
||||||
and (max-width: ${bounds.xlarge.upper})`,
|
and (max-width: ${remcalc(breakpoints.xlarge.upper)})`,
|
||||||
xlargeUp: `only screen and (min-width: ${bounds.xlarge.lower})`
|
xlargeUp: `only screen and (min-width: ${remcalc(breakpoints.xlarge.lower)})`
|
||||||
};
|
};
|
||||||
|
|
||||||
const breakpoint = label => (...args) => css`
|
const breakpoint = label => (...args) => css`
|
||||||
@ -64,5 +66,6 @@ export default {
|
|||||||
largeDown,
|
largeDown,
|
||||||
large,
|
large,
|
||||||
xlarge,
|
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 CloseIcon } from './close';
|
||||||
export { default as PlusIcon } from './plus';
|
export { default as PlusIcon } from './plus';
|
||||||
export { default as MinusIcon } from './minus';
|
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 { default as IconButton } from './icon-button';
|
||||||
|
|
||||||
export { Tooltip, TooltipButton, TooltipDivider } from './tooltip';
|
export { Tooltip, TooltipButton, TooltipDivider } from './tooltip';
|
||||||
|
export { Dropdown } from './dropdown';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
borderRadius,
|
borderRadius,
|
||||||
|
@ -88,11 +88,19 @@ class Tooltip extends Component {
|
|||||||
top = 'auto',
|
top = 'auto',
|
||||||
left = 'auto',
|
left = 'auto',
|
||||||
bottom = 'auto',
|
bottom = 'auto',
|
||||||
right = 'auto'
|
right = 'auto',
|
||||||
|
className,
|
||||||
|
...rest
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer top={top} left={left} bottom={bottom} right={right}>
|
<StyledContainer
|
||||||
|
className={className}
|
||||||
|
top={top}
|
||||||
|
left={left}
|
||||||
|
bottom={bottom}
|
||||||
|
right={right}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
<StyledList>
|
<StyledList>
|
||||||
{children}
|
{children}
|
||||||
</StyledList>
|
</StyledList>
|
||||||
|
@ -59,6 +59,7 @@ module.exports = {
|
|||||||
'src/form/input.js',
|
'src/form/input.js',
|
||||||
'src/form/number-input.js',
|
'src/form/number-input.js',
|
||||||
'src/form/checkbox.js',
|
'src/form/checkbox.js',
|
||||||
|
'src/dropdown/index.js',
|
||||||
'src/form/radio.js',
|
'src/form/radio.js',
|
||||||
'src/form/select.js',
|
'src/form/select.js',
|
||||||
'src/form/toggle.js',
|
'src/form/toggle.js',
|
||||||
|
Loading…
Reference in New Issue
Block a user