Basic dropdown
This commit is contained in:
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
|
|||||||
import autobind from 'autobind-decorator'
|
import autobind from 'autobind-decorator'
|
||||||
import { regExpPattern } from 'regexp-pattern'
|
import { regExpPattern } from 'regexp-pattern'
|
||||||
import { api } from 'src/API'
|
import { api } from 'src/API'
|
||||||
import { Row, Column, BoundInput, BoundButton, BoundCheckbox, BoundEmailIcon } from 'ui'
|
import { Row, Column, BoundInput, BoundButton, BoundCheckbox, BoundEmailIcon, DropdownList } from 'ui'
|
||||||
import { FormBinder } from 'react-form-binder'
|
import { FormBinder } from 'react-form-binder'
|
||||||
import { sizeInfo } from 'ui/style'
|
import { sizeInfo } from 'ui/style'
|
||||||
|
|
||||||
@@ -144,6 +144,13 @@ export class UserForm extends React.Component {
|
|||||||
</Row.Item>
|
</Row.Item>
|
||||||
</Row>
|
</Row>
|
||||||
</Column.Item>
|
</Column.Item>
|
||||||
|
<Column.Item>
|
||||||
|
<DropdownList render={(item) => (
|
||||||
|
<DropdownList.Item style={{ paddingLeft: 8 }}>
|
||||||
|
<DropdownList.Text>Team Name</DropdownList.Text>
|
||||||
|
</DropdownList.Item>
|
||||||
|
)} />
|
||||||
|
</Column.Item>
|
||||||
<Column.Item height={sizeInfo.formColumnSpacing} />
|
<Column.Item height={sizeInfo.formColumnSpacing} />
|
||||||
<Column.Item minHeight={sizeInfo.buttonHeight}>
|
<Column.Item minHeight={sizeInfo.buttonHeight}>
|
||||||
<Row>
|
<Row>
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export class Users extends Component {
|
|||||||
|
|
||||||
api.listUsers().then((list) => {
|
api.listUsers().then((list) => {
|
||||||
list.items.sort((userA, userB) => (userA.lastName.localeCompare(userB.lastName)))
|
list.items.sort((userA, userB) => (userA.lastName.localeCompare(userB.lastName)))
|
||||||
this.setState({ users: list.items })
|
this.setState({ users: list.items, selectedUser: list.items[0] }) // TODO: <- Remove
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
messageModal: {
|
messageModal: {
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ export class Box extends Component {
|
|||||||
borderRight: PropTypes.object,
|
borderRight: PropTypes.object,
|
||||||
borderLeft: PropTypes.object,
|
borderLeft: PropTypes.object,
|
||||||
border: PropTypes.object,
|
border: PropTypes.object,
|
||||||
radius: PropTypes.number,
|
radius: PropTypes.oneOfType([PropTypes.number, PropTypes.object]),
|
||||||
|
radiusTopLeft: PropTypes.number,
|
||||||
|
radiusTopRight: PropTypes.number,
|
||||||
|
radiusBottomLeft: PropTypes.number,
|
||||||
|
radiusBottomRight: PropTypes.number,
|
||||||
background: PropTypes.string,
|
background: PropTypes.string,
|
||||||
style: PropTypes.object,
|
style: PropTypes.object,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
@@ -20,8 +24,9 @@ export class Box extends Component {
|
|||||||
let {
|
let {
|
||||||
children, background,
|
children, background,
|
||||||
borderTop, borderBottom, borderLeft, borderRight,
|
borderTop, borderBottom, borderLeft, borderRight,
|
||||||
border, radius, style } = this.props
|
border, style, radius,
|
||||||
const flatten = (border) => {
|
radiusTopLeft, radiusTopRight, radiusBottomLeft, radiusBottomRight } = this.props
|
||||||
|
const stringify = (border) => {
|
||||||
if (!border) {
|
if (!border) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -34,19 +39,48 @@ export class Box extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (border) {
|
if (border) {
|
||||||
borderTop = borderBottom = borderLeft = borderRight = flatten(border)
|
borderTop = borderBottom = borderLeft = borderRight = stringify(border)
|
||||||
} else {
|
} else {
|
||||||
borderTop = flatten(borderTop)
|
borderTop = stringify(borderTop)
|
||||||
borderBottom = flatten(borderBottom)
|
borderBottom = stringify(borderBottom)
|
||||||
borderLeft = flatten(borderLeft)
|
borderLeft = stringify(borderLeft)
|
||||||
borderRight = flatten(borderRight)
|
borderRight = stringify(borderRight)
|
||||||
|
}
|
||||||
|
|
||||||
|
let borderTopLeftRadius, borderTopRightRadius, borderBottomLeftRadius, borderBottomRightRadius
|
||||||
|
|
||||||
|
if (radius) {
|
||||||
|
if (typeof radius === 'number') {
|
||||||
|
borderTopLeftRadius = borderTopRightRadius = borderBottomLeftRadius = borderBottomRightRadius = radius
|
||||||
|
} else {
|
||||||
|
borderTopLeftRadius = radius.topLeft
|
||||||
|
borderTopRightRadius = radius.topRight
|
||||||
|
borderBottomLeftRadius = radius.bottomLeft
|
||||||
|
borderBottomRightRadius = radius.bottomRight
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
borderTopLeftRadius = radiusTopLeft
|
||||||
|
borderTopRightRadius = radiusTopRight
|
||||||
|
borderBottomLeftRadius = radiusBottomLeft
|
||||||
|
borderBottomRightRadius = radiusBottomRight
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={[
|
<div style={[
|
||||||
{ height: '100%', width: '100%', boxSizing: 'border-box', borderTop, borderBottom, borderLeft, borderRight },
|
{
|
||||||
|
height: '100%',
|
||||||
|
width: '100%',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
borderTop,
|
||||||
|
borderBottom,
|
||||||
|
borderLeft,
|
||||||
|
borderRight,
|
||||||
|
borderTopLeftRadius,
|
||||||
|
borderTopRightRadius,
|
||||||
|
borderBottomLeftRadius,
|
||||||
|
borderBottomRightRadius,
|
||||||
|
},
|
||||||
background && { backgroundColor: background },
|
background && { backgroundColor: background },
|
||||||
radius && { borderRadius: radius },
|
|
||||||
style,
|
style,
|
||||||
]}>
|
]}>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
156
website/src/ui/DropdownList.js
Normal file
156
website/src/ui/DropdownList.js
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
import Radium from 'radium'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import { Box, Icon } from '.'
|
||||||
|
import { sizeInfo, colorInfo, fontInfo } from './style'
|
||||||
|
import downArrowImage from './images/down-arrow.svg'
|
||||||
|
import upArrowImage from './images/up-arrow.svg'
|
||||||
|
import autobind from 'autobind-decorator'
|
||||||
|
|
||||||
|
@Radium
|
||||||
|
export class DropdownList extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
data: PropTypes.array,
|
||||||
|
render: PropTypes.func.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {
|
||||||
|
open: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleClick(e) {
|
||||||
|
this.setState({ open: !this.state.open })
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { data, render } = this.props
|
||||||
|
const { open } = this.state
|
||||||
|
const dropDownIconSize = 20
|
||||||
|
const dropdownIconMargin = 8
|
||||||
|
const dropdownBackground = 'white'
|
||||||
|
const selectedItem = data ? data[0] : <span />
|
||||||
|
const border = { width: sizeInfo.listBorderWidth, color: colorInfo.listBorder }
|
||||||
|
|
||||||
|
let dropdown = null
|
||||||
|
|
||||||
|
if (open) {
|
||||||
|
dropdown = (
|
||||||
|
<Box
|
||||||
|
borderLeft={border}
|
||||||
|
borderRight={border}
|
||||||
|
borderBottom={border}
|
||||||
|
radius={{ bottomLeft: sizeInfo.formBoxRadius, bottomRight: sizeInfo.formBoxRadius }}
|
||||||
|
background={dropdownBackground}
|
||||||
|
style={{
|
||||||
|
height: 200,
|
||||||
|
zIndex: 100,
|
||||||
|
position: 'absolute',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Box
|
||||||
|
borderTop={border}
|
||||||
|
borderLeft={border}
|
||||||
|
borderRight={border}
|
||||||
|
borderBottom={open ? { width: sizeInfo.listBorderWidth, color: 'transparent' } : { width: sizeInfo.listBorderWidth, color: colorInfo.listBorder }}
|
||||||
|
radius={open ? { topLeft: sizeInfo.formBoxRadius, topRight: sizeInfo.formBoxRadius } : sizeInfo.formBoxRadius}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
fontSize: fontInfo.size.large,
|
||||||
|
fontFamily: fontInfo.family,
|
||||||
|
}}>
|
||||||
|
<div style={{ flexGrow: 1 }}>
|
||||||
|
{render(selectedItem)}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<img src={open ? upArrowImage : downArrowImage}
|
||||||
|
style={{
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
width: dropDownIconSize,
|
||||||
|
height: dropDownIconSize,
|
||||||
|
margin: dropdownIconMargin,
|
||||||
|
}}
|
||||||
|
onClick={this.handleClick} />
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
{dropdown}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DropdownList.Item = Radium(class ListItem extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
active: PropTypes.bool,
|
||||||
|
onClick: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { children, active, onClick } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
background: active ? colorInfo.listBackgroundActive : 'transparent',
|
||||||
|
':hover': {
|
||||||
|
background: colorInfo.listBackgroundHover
|
||||||
|
},
|
||||||
|
}} onClick={onClick}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
DropdownList.Icon = Radium(class ListIcon extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
name: PropTypes.string,
|
||||||
|
size: PropTypes.number,
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { size, name } = this.props
|
||||||
|
let source = Icon.svgs[name] || Icon.svgs['placeholder']
|
||||||
|
|
||||||
|
return (
|
||||||
|
<img src={source} style={{
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
margin: sizeInfo.listIconMargin,
|
||||||
|
}} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
DropdownList.Text = Radium(class ListText extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { children } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span style={{
|
||||||
|
color: fontInfo.color.normal,
|
||||||
|
align: 'left',
|
||||||
|
textAlign: 'left',
|
||||||
|
cursor: 'default',
|
||||||
|
paddingLeft: 8,
|
||||||
|
}}>{children}</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
14
website/src/ui/images/down-arrow.svg
Normal file
14
website/src/ui/images/down-arrow.svg
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="76px" height="76px" viewBox="0 0 76 76" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 49.1 (51147) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>down-arrow</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs></defs>
|
||||||
|
<g id="Login" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="Home" transform="translate(-121.000000, -304.000000)" fill="#9C9C9C">
|
||||||
|
<g id="down-arrow" transform="translate(121.000000, 304.000000)">
|
||||||
|
<path d="M6.29010254,25 L67.709898,25 C68.2621828,25 68.709898,25.4477153 68.709898,26 C68.709898,26.2697399 68.6009273,26.5280401 68.4077205,26.7162707 L37.6978228,56.6352081 C37.3095159,57.0135138 36.6904847,57.0135138 36.3021777,56.6352081 L5.59228001,26.7162707 C5.19669462,26.330874 5.18843509,25.6977629 5.57383183,25.3021775 C5.7620624,25.1089707 6.02036264,25 6.29010254,25 Z"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
14
website/src/ui/images/up-arrow.svg
Normal file
14
website/src/ui/images/up-arrow.svg
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="76px" height="76px" viewBox="0 0 76 76" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 49.1 (51147) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>up-arrow</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs></defs>
|
||||||
|
<g id="Login" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="Home" transform="translate(-220.000000, -304.000000)" fill="#9C9C9C">
|
||||||
|
<g id="up-arrow" transform="translate(220.000000, 304.000000)">
|
||||||
|
<path d="M6.29010254,25 L67.709898,25 C68.2621828,25 68.709898,25.4477153 68.709898,26 C68.709898,26.2697399 68.6009273,26.5280401 68.4077205,26.7162707 L37.6978228,56.6352081 C37.3095159,57.0135138 36.6904847,57.0135138 36.3021777,56.6352081 L5.59228001,26.7162707 C5.19669462,26.330874 5.18843509,25.6977629 5.57383183,25.3021775 C5.7620624,25.1089707 6.02036264,25 6.29010254,25 Z" transform="translate(37.000000, 40.959469) scale(1, -1) translate(-37.000000, -40.959469) "></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -11,6 +11,7 @@ export { Text } from './Text'
|
|||||||
export { Link } from './Link'
|
export { Link } from './Link'
|
||||||
export { Icon } from './Icon'
|
export { Icon } from './Icon'
|
||||||
export { List } from './List'
|
export { List } from './List'
|
||||||
|
export { DropdownList } from './DropdownList'
|
||||||
export { Modal } from './Modal'
|
export { Modal } from './Modal'
|
||||||
export { Dimmer } from './Dimmer'
|
export { Dimmer } from './Dimmer'
|
||||||
export { Loader } from './Loader'
|
export { Loader } from './Loader'
|
||||||
|
|||||||
Reference in New Issue
Block a user