Basic UI elements in place

This commit is contained in:
John Lyon-Smith
2018-02-27 12:16:27 -08:00
parent 5faa4600f5
commit 73b5cf6caa
49 changed files with 525 additions and 937 deletions

View File

@@ -0,0 +1,63 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Button } from 'ui'
import { reactAutoBind } from 'auto-bind2'
export default class BoundButton extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.string,
width: PropTypes.number,
size: PropTypes.string,
content: PropTypes.string,
binder: PropTypes.object.isRequired,
submit: PropTypes.bool,
color: PropTypes.string,
onClick: PropTypes.func
}
constructor(props) {
super(props)
reactAutoBind(this)
let { name, binder } = this.props
binder.addListener(name, this.updateValue)
this.state = binder.getFieldState(name)
}
updateValue(name) {
this.setState(this.props.binder.getFieldState(name))
}
componentWillUnmount() {
this.props.binder.removeListener(this.props.name, this.updateValue)
}
componentWillReceiveProps(nextProps) {
if (nextProps.binder !== this.props.binder) {
this.props.binder.removeListener(this.props.name, this.updateValue)
nextProps.binder.addListener(nextProps.name, this.updateValue)
this.setState(nextProps.binder.getFieldState(nextProps.name))
}
}
render() {
if (this.state.visible) {
return (
<div width={this.props.width} disabled={this.state.disabled}>
<label>{this.props.label}</label>
<Button
color={this.props.color}
submit={this.props.submit}
onClick={this.props.onClick}
size={this.props.size} name={this.props.name}>
{this.props.content}
</Button>
</div>
)
} else {
return null
}
}
}

View File

@@ -0,0 +1,39 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Checkbox } from 'ui'
import { reactAutoBind } from 'auto-bind2'
export default class BoundCheckbox extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.string,
binder: PropTypes.object.isRequired,
}
constructor(props) {
super(props)
reactAutoBind(this)
this.state = props.binder.getFieldState(props.name)
}
handleChange(e, data) {
const { binder, name } = this.props
const state = binder.getFieldState(name)
if (!state.readOnly && !state.disabled) {
this.setState(binder.updateFieldValue(name, data.checked))
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.binder !== this.props.binder) {
this.setState(nextProps.binder.getFieldState(nextProps.name))
}
}
render() {
return (
<Checkbox value={!!this.state.value} name={this.props.name} label={this.props.label} />
)
}
}

View File

@@ -0,0 +1,42 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Text, Button, Icon } from 'ui'
export default class BoundEmailIcon extends React.Component {
static propTypes = {
name: PropTypes.string,
binder: PropTypes.object,
width: PropTypes.number,
onClick: PropTypes.func
}
constructor(props) {
super(props)
this.state = props.binder.getField('emailValidated')
}
componentWillReceiveProps(nextProps) {
if (nextProps.binder !== this.props.binder) {
this.setState(nextProps.binder.getField(nextProps.name))
}
}
render() {
if (this.state.value) {
return (
<div width={this.props.width}>
<Text>&nbsp;</Text>
<Icon name='mail' color='green' size='big' />
</div>
)
} else {
return (
<div width={this.props.width}>
<Text>&nbsp;</Text>
<Button icon='mail outline' color='red' labelPosition='left'
content='Resend Email' onClick={this.props.onClick} disabled={this.state.disabled} />
</div>
)
}
}
}

View File

@@ -0,0 +1,52 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Input, Text } from 'ui'
import { reactAutoBind } from 'auto-bind2'
export default class BoundInput extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired,
message: PropTypes.string,
label: PropTypes.string,
binder: PropTypes.object.isRequired,
password: PropTypes.bool,
placeholder: PropTypes.string
}
constructor(props) {
super(props)
reactAutoBind(this)
this.state = props.binder.getFieldState(props.name)
}
handleChange(e, data) {
const { binder, name } = this.props
const state = binder.getFieldState(name)
if (!state.readOnly && !state.disabled) {
this.setState(binder.updateFieldValue(name, data.value))
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.binder !== this.props.binder) {
this.setState(nextProps.binder.getFieldState(nextProps.name))
}
}
render() {
return (
<div style={{ width: '100%' }} disabled={this.state.disabled}>
<Text>{this.props.label}</Text>
<br />
<Input value={this.state.value}
password={this.props.password}
name={this.props.name}
onChange={this.handleChange}
placeholder={this.props.placeholder} />
<br />
<Text size='small' tone='alert'>{this.props.message}</Text>
</div>
)
}
}

View File

@@ -18,6 +18,7 @@ class Box extends Component {
return (
<div style={[
{ height: '100%', width: '100%' },
color && { backgroundColor: color },
border ? { border } : { borderTop, borderBottom, borderLeft, borderRight }]}>
{children}

View File

@@ -31,9 +31,9 @@ class Checkbox extends Component {
</div>
<span style={{
verticalAlign: 'top',
fontSize: fontInfo.size['medium'],
fontSize: fontInfo.size.medium,
fontFamily: fontInfo.family,
color: fontInfo.color['normal'],
color: fontInfo.color.normal,
marginLeft: 10
}}>{this.props.label}</span>
</div>

View File

@@ -12,7 +12,7 @@ class Column extends Component {
const { children, minHeight } = this.props
return (
<div style={{ display: 'flex', minHeight, flexDirection: 'column' }}>{children}</div>
<div style={{ width: '100%', display: 'flex', minHeight, flexDirection: 'column' }}>{children}</div>
)
}
}
@@ -21,15 +21,21 @@ Column.Item = Radium(class StackLayoutItem extends Component {
static propTypes = {
children: PropTypes.node,
minHeight: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
height: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
paddingTop: PropTypes.number,
paddingBottom: PropTypes.number,
padding: PropTypes.number,
grow: PropTypes.bool
}
render() {
const { children, grow, minHeight } = this.props
const { children, grow, height, minHeight, padding, paddingTop, paddingBottom } = this.props
const flexGrow = grow ? 1 : null
return (
<div style={{ minHeight, flexGrow }}>{children}</div>
<div style={{ height, minHeight, flexGrow, paddingTap: paddingTop || padding, paddingBottom: paddingBottom || padding }}>
{children}
</div>
)
}
})

View File

@@ -0,0 +1,25 @@
import Radium from 'radium'
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import style from './HeaderButton.style'
import { Icon } from 'ui'
class HeaderButton extends Component {
static propTypes = {
icon: PropTypes.string,
onClick: PropTypes.func,
size: PropTypes.number
}
render() {
const { onClick, icon, size } = this.props
return (
<button type='button' style={[{ height: size, width: size }, style.base]} onClick={onClick}>
<Icon name={icon} size={size} />
</button>
)
}
}
export default Radium(HeaderButton)

View File

@@ -0,0 +1,15 @@
import { colorInfo } from './style'
export default {
base: {
background: colorInfo.headerButtonBackground,
verticalAlign: 'middle',
borderWidth: 0,
padding: '0 0 0 0',
outline: 'none',
':hover': {
background: colorInfo.headerButtonBackgroundHover,
}
}
}

View File

@@ -1,28 +1,32 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { sizeInfo } from './style'
// See https://www.flaticon.com/packs/web-button-compilation for more icons
// See https://www.flaticon.com/packs/free-basic-ui-elements
export default class Icon extends Component {
static propTypes = {
name: PropTypes.string.isRequired,
size: PropTypes.number,
margin: PropTypes.number
}
static defaultProps = {
size: 50,
margin: 5,
name: 'shapes'
size: 50
}
static svgs = {
logout: require('icons/logout.svg'),
shapes: require('icons/shapes.svg')
profile: require('icons/profile.svg'),
placeholder: require('icons/placeholder.svg'),
}
render() {
return <img style={{ width: this.props.size, height: this.props.size, margin: this.props.margin }}
src={Icon.svgs[this.props.name]} />
let { size, name } = this.props
let source = Icon.svgs[name] || Icon.svgs['placeholder']
const margin = sizeInfo.iconMargin
size -= margin * 2
return <img style={{ width: size, height: size, margin }} src={source} />
}
}

View File

@@ -1,18 +1,24 @@
import Radium from 'radium'
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { sizeInfo } from './style'
class Image extends Component {
static propTypes = {
source: PropTypes.string,
width: PropTypes.number,
height: PropTypes.number,
margin: PropTypes.number
}
render() {
let { source, width, height } = this.props
const margin = sizeInfo.imageMargin
width = width ? (width - margin * 2) : null
height = height ? (height - margin * 2) : null
return (
<img src={this.props.source} style={{ width: this.props.width, height: this.props.height, margin: this.props.margin }} />
<img src={source} style={{ width, height, margin }} />
)
}
}

View File

@@ -12,7 +12,7 @@ class Row extends Component {
const { children, minWidth } = this.props
return (
<div style={{ display: 'flex', minWidth, flexDirection: 'row' }}>{children}</div>
<div style={{ height: '100%', display: 'flex', minWidth, flexDirection: 'row' }}>{children}</div>
)
}
}

View File

@@ -1,5 +1,6 @@
export { default as Box } from './Box'
export { default as Button } from './Button'
export { default as HeaderButton } from './HeaderButton'
export { default as Checkbox } from './Checkbox'
export { default as Input } from './Input'
export { default as Image } from './Image'
@@ -13,3 +14,7 @@ export { default as Dimmer } from './Dimmer'
export { default as Loader } from './Loader'
export { default as Row } from './Row'
export { default as Column } from './Column'
export { default as BoundButton } from './BoundButton'
export { default as BoundCheckbox } from './BoundCheckbox'
export { default as BoundInput } from './BoundInput'
export { default as BoundEmailIcon } from './BoundEmailIcon'

View File

@@ -3,7 +3,16 @@ export const colorInfo = {
alertText: '#FF0000',
grayText: '#B0B0B0',
buttonBackground: '#3498DB',
buttonBackgroundHover: '#3CB0FD'
buttonBackgroundHover: '#3CB0FD',
headerButtonBackground: '#FAFAFA',
headerButtonBackgroundHover: '#DADADA',
}
export const sizeInfo = {
headerHeight: 60,
imageMargin: 5, // The margin around images
iconMargin: 10, // The margin around icons
headerBorderWidth: 1
}
export const fontInfo = {