Basic UI elements in place
This commit is contained in:
63
website/src/ui/BoundButton.js
Normal file
63
website/src/ui/BoundButton.js
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
39
website/src/ui/BoundCheckbox.js
Normal file
39
website/src/ui/BoundCheckbox.js
Normal 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} />
|
||||
)
|
||||
}
|
||||
}
|
||||
42
website/src/ui/BoundEmailIcon.js
Normal file
42
website/src/ui/BoundEmailIcon.js
Normal 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> </Text>
|
||||
<Icon name='mail' color='green' size='big' />
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div width={this.props.width}>
|
||||
<Text> </Text>
|
||||
<Button icon='mail outline' color='red' labelPosition='left'
|
||||
content='Resend Email' onClick={this.props.onClick} disabled={this.state.disabled} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
52
website/src/ui/BoundInput.js
Normal file
52
website/src/ui/BoundInput.js
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
25
website/src/ui/HeaderButton.js
Normal file
25
website/src/ui/HeaderButton.js
Normal 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)
|
||||
15
website/src/ui/HeaderButton.style.js
Normal file
15
website/src/ui/HeaderButton.style.js
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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} />
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }} />
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user