Removed Semantic UI React

This commit is contained in:
John Lyon-Smith
2018-02-25 17:51:52 -08:00
parent c60bfcedf8
commit 0571196a7f
68 changed files with 981 additions and 1343 deletions

View File

@@ -3,16 +3,18 @@ import { Login, Logout, ResetPassword, ForgotPassword, ConfirmEmail, ProtectedRo
import { Home } from './Home'
import { Profile } from './Profile'
import { Users } from './Users'
import { HolyGrail } from './ui'
import { HolyGrail, Image, Text, Icon } from './ui'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import logoImage from 'images/logo.png'
import { versionInfo } from './version'
export class App extends React.Component {
render() {
return (
<HolyGrail>
<HolyGrail.Header>
<img src={logoImage} style={{ width: '50px', height: '50px', margin: '5px' }} />
<Image source={logoImage} width={50} height={50} margin={5} />
<Icon name='logout' />
</HolyGrail.Header>
<HolyGrail.Body>
<Router basename='/'>
@@ -30,7 +32,7 @@ export class App extends React.Component {
</Router>
</HolyGrail.Body>
<HolyGrail.Footer>
<div style={{ margin: '10px' }}>v1.0.0. Copyright &copy; 2018, Deighton.</div>
<Text margin={10}>{versionInfo.fullVersion} &copy; 2018, Kingston Software Solutions.</Text>
</HolyGrail.Footer>
</HolyGrail>
)

View File

@@ -1,9 +1,7 @@
import React from 'react'
import { api } from '../helpers'
import PropTypes from 'prop-types'
import { Container } from 'semantic-ui-react'
import { MessageDialog, WaitDialog } from '../Dialog'
import './ConfirmEmail.scss'
export class ConfirmEmail extends React.Component {
static propTypes = {
@@ -56,7 +54,7 @@ export class ConfirmEmail extends React.Component {
render() {
return (
<Container className='email-confirm-container'>
<div>
<WaitDialog active={!!this.state.waitDialog}
message={this.state.waitDialog ? this.state.waitDialog.message : ''} />
@@ -64,7 +62,7 @@ export class ConfirmEmail extends React.Component {
title={this.state.messageDialog ? this.state.messageDialog.title : ''}
message={this.state.messageDialog ? this.state.messageDialog.message : ''}
onDismiss={this.handleMessageDialogDismiss} />
</Container>
</div>
)
}
}

View File

@@ -1,11 +0,0 @@
.ui.container.email-confirm-container {
display: flex;
height: 80vh;
flex-direction: column;
align-items: center;
justify-content: center;
}
.ui.container.email-confirm-container button {
margin-top: 1em;
}

View File

@@ -1,8 +1,7 @@
import React from 'react'
import PropTypes from 'prop-types'
import { regExpPattern } from 'regexp-pattern'
import { Container, Header, Form, Message } from 'semantic-ui-react'
import './ForgotPassword.scss'
import { Text, StackLayout } from '../ui'
import { MessageDialog, WaitDialog } from '../Dialog'
import { Validator, ValidatedInput, ValidatedButton } from '../Validated'
import { api } from '../helpers'
@@ -67,16 +66,26 @@ export class ForgotPassword extends React.Component {
render() {
return (
<Container className='forgot-password-container'>
<Header content='Forgotten Password' size='large' />
<Form size='large' onSubmit={this.handleSubmit}>
<ValidatedInput label='Email' name='email'
placeholder='example@xyz.com' validator={this.state.validator}
message='A valid email address' />
<Message info content='The email address of an existing user to send the password reset link to.' />
<ValidatedButton className='submit' name='submit' content='Submit'
primary submit validator={this.state.validator}>Submit</ValidatedButton>
</Form>
<div>
<form onSubmit={this.handleSubmit}>
<StackLayout>
<StackLayout.Item>
<Text size='large'>Forgotten Password</Text>
</StackLayout.Item>
<StackLayout.Item>
<ValidatedInput label='Email' name='email'
placeholder='example@xyz.com' validator={this.state.validator}
message='A valid email address' />
</StackLayout.Item>
<StackLayout.Item>
<Text>The email address of an existing user to send the password reset link to.</Text>
</StackLayout.Item>
<StackLayout.Item>
<ValidatedButton className='submit' name='submit' content='Submit'
primary submit validator={this.state.validator}>Submit</ValidatedButton>
</StackLayout.Item>
</StackLayout>
</form>
<WaitDialog active={!!this.state.waitDialog}
message={this.state.waitDialog ? this.state.waitDialog.message : ''} />
@@ -87,7 +96,7 @@ export class ForgotPassword extends React.Component {
title={this.state.messageDialog ? this.state.messageDialog.title : ''}
message={this.state.messageDialog ? this.state.messageDialog.message : ''}
onDismiss={this.handleMessageDialogDismiss} />
</Container>
</div>
)
}
}

View File

@@ -1,23 +0,0 @@
.ui.container.forgot-password-container {
height: 80vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.ui.container.forgot-password-container .header {
border-bottom: 1px solid #d4d4d5;
width: 40%;
padding-bottom: 0.5em;
margin-bottom: 1em;
}
.ui.container.forgot-password-container form {
width: 40%;
}
.ui.container.forgot-password-container .message {
margin: 2em 0;
font-size: 0.8em;
}

View File

@@ -1,13 +1,11 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Container, Header, Form, Message } from 'semantic-ui-react'
import './Login.scss'
import { regExpPattern } from 'regexp-pattern'
import { api } from '../helpers'
import { Validator, ValidatedInput, ValidatedCheckbox } from '../Validated'
import { Validator, ValidatedInput, ValidatedCheckbox, ValidatedButton } from '../Validated'
import { WaitDialog, MessageDialog } from '../Dialog'
import { Link } from 'react-router-dom'
import { Button } from '../ui'
import { Image, Link, Text, StackLayout } from '../ui'
import headerLogo from 'images/deighton.png'
export class Login extends React.Component {
static propTypes = {
@@ -90,30 +88,40 @@ export class Login extends React.Component {
render() {
return (
<Container id='login' className='password-reset-container'>
<Header size='large'>Login Portal</Header>
<Form onSubmit={this.handleSubmit}>
{/* Add in 'Username' field pass */}
<ValidatedInput label='Email' name='email'
placeholder='example@xyz.com' validator={this.state.validator}
message='Enter the email address associated with your account.' width={16} />
<ValidatedInput password label='Password' name='password'
validator={this.state.validator} message='Enter your password.' width={16} />
<Form.Group widths='equal' className='login-options'>
<Form.Field className='login-password'>
<div>
<form onSubmit={this.handleSubmit}>
<StackLayout>
<StackLayout.Item>
<Image source={headerLogo} width={279} height={73} />
</StackLayout.Item>
<StackLayout.Item>
<ValidatedInput label='Email' name='email'
placeholder='example@xyz.com' validator={this.state.validator}
message='Enter the email address associated with your account.' width={16} />
</StackLayout.Item>
<StackLayout.Item>
<ValidatedInput password label='Password' name='password'
validator={this.state.validator} message='Enter your password.' width={16} />
</StackLayout.Item>
<StackLayout.Item>
<Link to='/forgot-password'>Forgot your password?</Link>
</Form.Field>
<ValidatedCheckbox label='Remember Me'
name='rememberMe' onChange={this.handleChange} validator={this.state.validator}
message='Should we keep you logged in on this computer?' className='login-checkbox' />
</Form.Group>
{ /* <ValidatedButton className='submit' name='submit' content='Submit'
primary submit validator={this.state.validator} /> */ }
<Button>Login</Button>
<Message info>
Please contact <a href='mailto:support@jamoki.com'>support@jamoki.com</a> to request login credentials.
</Message>
</Form>
</StackLayout.Item>
<StackLayout.Item>
<ValidatedCheckbox label='Remember Me'
name='rememberMe' onChange={this.handleChange} validator={this.state.validator}
message='Should we keep you logged in on this computer?' className='login-checkbox' />
</StackLayout.Item>
<StackLayout.Item>
<ValidatedButton className='submit' name='submit' content='Login'
submit validator={this.state.validator} />
</StackLayout.Item>
<StackLayout.Item>
<Text>
Please contact <Link to='mailto:support@jamoki.com'>support@jamoki.com</Link> to request login credentials.
</Text>
</StackLayout.Item>
</StackLayout>
</form>
<WaitDialog active={this.state.waitDialog} message='Logging In' />
@@ -121,7 +129,7 @@ export class Login extends React.Component {
title={this.state.messageDialog ? this.state.messageDialog.title : ''}
message={this.state.messageDialog ? this.state.messageDialog.message : ''}
onDismiss={this.handleMessageDialogDismiss} />
</Container>
</div>
)
}
}

View File

@@ -1,3 +0,0 @@
#login .login-options { margin: 1.5em 0 3em 0; }
#login .login-password { text-align: left; }
#login .login-checkbox { text-align: right; }

View File

@@ -1,7 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Container, Header, Form, Message } from 'semantic-ui-react'
import './ResetPassword.scss'
import { Text, StackLayout } from '../ui'
import { Validator, ValidatedInput, ValidatedButton } from '../Validated'
import { MessageDialog, WaitDialog } from '../Dialog'
import { api } from '../helpers'
@@ -62,23 +61,35 @@ export class ResetPassword extends React.Component {
render() {
return (
<Container className='password-reset-container'>
<Header content='Reset Password' size='large' />
<Form size='large' onSubmit={this.handleSubmit}>
<ValidatedInput label='New Password' password name='newPassword'
message='A new password, cannot be blank or the same as your old password'
width={16} validator={this.state.validator} />
<ValidatedInput label='Re-entered New Password' password name='reenteredNewPassword'
message='The new password again, must match and cannot be blank'
width={16} validator={this.state.validator} />
<Message info>
Passwords can contain special characters and are discouraged from being simple or reused from other sites or applications.
<br /><br />
Passwords must be at least 6 characters long.
</Message>
<ValidatedButton className='submit' name='submit' content='Submit'
primary submit validator={this.state.validator} />
</Form>
<div>
<form onSubmit={this.handleSubmit}>
<StackLayout>
<StackLayout.Item>
<Text size='large'>Reset Password</Text>
</StackLayout.Item>
<StackLayout.Item>
<ValidatedInput label='New Password' password name='newPassword'
message='A new password, cannot be blank or the same as your old password'
width={16} validator={this.state.validator} />
</StackLayout.Item>
<StackLayout.Item>
<ValidatedInput label='Re-entered New Password' password name='reenteredNewPassword'
message='The new password again, must match and cannot be blank'
width={16} validator={this.state.validator} />
</StackLayout.Item>
<StackLayout.Item>
<Text>
Passwords can contain special characters and should be unique to this application.
<br /><br />
Passwords must be at least 6 characters long.
</Text>
</StackLayout.Item>
<StackLayout.Item>
<ValidatedButton className='submit' name='submit' content='Submit'
submit validator={this.state.validator} />
</StackLayout.Item>
</StackLayout>
</form>
<MessageDialog error open={!!this.state.messageDialog}
title={this.state.messageDialog ? this.state.messageDialog.title : ''}
@@ -87,7 +98,7 @@ export class ResetPassword extends React.Component {
<WaitDialog active={!!this.state.waitDialog}
message={this.state.waitDialog ? this.state.waitDialog.message : ''} />
</Container>
</div>
)
}
}

View File

@@ -1,23 +0,0 @@
.ui.container.password-reset-container {
height: 80vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.ui.container.password-reset-container .header {
border-bottom: 1px solid #d4d4d5;
width: 40%;
padding-bottom: 0.5em;
margin-bottom: 1em;
}
.ui.container.password-reset-container form {
width: 40%;
}
.ui.container.password-reset-container .message {
margin: 2em 0;
font-size: 0.8em;
}

View File

@@ -1,8 +1,8 @@
import React from 'react'
import PropTypes from 'prop-types'
import { autoBind } from 'auto-bind2'
import { Modal, Button, Icon, Header, Grid, Form } from 'semantic-ui-react'
import { ValidatedInput, ValidatedActionsButton, Validator } from '../Validated'
import { Modal, Button, Icon, StackLayout, Text } from '../ui'
import { ValidatedInput, ValidatedButton, Validator } from '../Validated'
import { regExpPattern } from 'regexp-pattern'
export class ChangeEmailDialog extends React.Component {
@@ -57,28 +57,26 @@ export class ChangeEmailDialog extends React.Component {
return (
<Modal dimmer='inverted' open={this.props.open} onClose={this.handleClose}
closeOnDimmerClick={false}>
<Header color='black' icon='edit' content='Change Email' />
<Modal.Content>
<Form className='user-form' id='emailForm' onSubmit={this.handleSubmit}>
<Grid>
<Grid.Column width={16}>
<Form.Group>
<ValidatedInput label='New Email' name='newEmail' width={16}
message='Your new email address, e.g. xyz@abc.com, cannot be blank'
validator={this.state.validator} />
</Form.Group>
</Grid.Column>
</Grid>
</Form>
</Modal.Content>
<Modal.Actions>
<ValidatedActionsButton primary submit form='emailForm' name='submit' validator={this.state.validator}>
<Icon name='checkmark' /> OK
</ValidatedActionsButton>
<Button color='red' onClick={this.handleClick}>
<Icon name='close' /> Cancel
</Button>
</Modal.Actions>
<form className='user-form' id='emailForm' onSubmit={this.handleSubmit}>
<StackLayout>
<StackLayout.Item>
<Text>Change Email</Text>
</StackLayout.Item>
<StackLayout.Item>
<ValidatedInput label='New Email' name='newEmail' width={16}
message='Your new email address, e.g. xyz@abc.com, cannot be blank'
validator={this.state.validator} />
</StackLayout.Item>
<StackLayout.Item>
<ValidatedButton primary submit form='emailForm' name='submit' validator={this.state.validator}>
<Icon name='checkmark' /> OK
</ValidatedButton>
<Button color='red' onClick={this.handleClick}>
<Icon name='close' /> Cancel
</Button>
</StackLayout.Item>
</StackLayout>
</form>
</Modal>
)
}

View File

@@ -1,7 +1,7 @@
import React from 'react'
import PropTypes from 'prop-types'
import { autoBind } from 'auto-bind2'
import { Modal, Button, Icon, Header, Grid, Form } from 'semantic-ui-react'
import { Modal, Button, Icon, StackLayout, RowLayout, Text } from '../ui'
import { ValidatedInput, ValidatedActionsButton, Validator } from '../Validated'
export class ChangePasswordDialog extends React.Component {
@@ -64,38 +64,41 @@ export class ChangePasswordDialog extends React.Component {
render() {
return (
<Modal dimmer='inverted' open={this.props.open} onClose={this.handleClose}
closeOnDimmerClick={false}>
<Header color='black' icon='edit' content='Change Password' />
<Modal.Content>
<Form className='user-form' id='passwordForm' onSubmit={this.handleSubmit}>
<Grid stackable>
<Grid.Column width={16}>
<Form.Group>
<ValidatedInput label='Current Password' password name='oldPassword'
message='Your existing password, cannot be blank'
width={8} validator={this.state.validator} />
</Form.Group>
<Form.Group>
<ValidatedInput label='New Password' password name='newPassword'
message='A new password, cannot be blank or the same as your old password'
width={8} validator={this.state.validator} />
<ValidatedInput label='Re-entered New Password' password name='reenteredNewPassword'
message='The new password again, must match and cannot be blank'
width={8} validator={this.state.validator} />
</Form.Group>
</Grid.Column>
</Grid>
</Form>
</Modal.Content>
<Modal.Actions>
<ValidatedActionsButton primary submit form='passwordForm' name='submit' validator={this.state.validator}>
<Icon name='checkmark' /> OK
</ValidatedActionsButton>
<Button color='red' onClick={this.handleClick}>
<Icon name='close' /> Cancel
</Button>
</Modal.Actions>
<Modal dimmer='inverted' open={this.props.open} onClose={this.handleClose} closeOnDimmerClick={false}>
<form id='passwordForm' onSubmit={this.handleSubmit}>
<StackLayout.Item color='black' icon='edit'>
<Text size='large'>Change Password</Text>
</StackLayout.Item>
<StackLayout.Item>
<StackLayout>
<StackLayout.Item>
<ValidatedInput label='Current Password' password name='oldPassword'
message='Your existing password, cannot be blank'
width={8} validator={this.state.validator} />
</StackLayout.Item>
<StackLayout.Item>
<ValidatedInput label='New Password' password name='newPassword'
message='A new password, cannot be blank or the same as your old password'
width={8} validator={this.state.validator} />
</StackLayout.Item>
<StackLayout.Item>
<ValidatedInput label='Re-entered New Password' password name='reenteredNewPassword'
message='The new password again, must match and cannot be blank'
width={8} validator={this.state.validator} />
</StackLayout.Item>
</StackLayout>
</StackLayout.Item>
<StackLayout.Item>
<RowLayout>
<ValidatedActionsButton primary submit form='passwordForm' name='submit' validator={this.state.validator}>
<Icon name='checkmark' /> OK
</ValidatedActionsButton>
<Button color='red' onClick={this.handleClick}>
<Icon name='close' /> Cancel
</Button>
</RowLayout>
</StackLayout.Item>
</form>
</Modal>
)
}

View File

@@ -1,11 +1,10 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Modal, Button, Icon, Header } from 'semantic-ui-react'
import { Modal, Button, Icon, StackLayout, Text } from '../ui'
export class MessageDialog extends React.Component {
static propTypes = {
open: PropTypes.bool,
error: PropTypes.bool,
title: PropTypes.string.isRequired,
message: PropTypes.string.isRequired,
onDismiss: PropTypes.func
@@ -13,20 +12,20 @@ export class MessageDialog extends React.Component {
render() {
return (
<Modal dimmer='inverted' open={this.props.open} onClose={this.props.onDismiss}
closeOnDimmerClick={false}>
<Header
color={this.props.error ? 'red' : 'blue'}
icon={this.props.error ? 'warning circle' : 'info circle'}
content={this.props.title} />
<Modal.Content>
<h3>{this.props.message}</h3>
</Modal.Content>
<Modal.Actions>
<Button onClick={this.props.onDismiss}>
<Icon name='checkmark' /> OK
</Button>
</Modal.Actions>
<Modal open={this.props.open} onClose={this.props.onDismiss} closeOnDimmerClick={false}>
<StackLayout>
<StackLayout.Item>
<Text>{this.props.title}</Text>
</StackLayout.Item>
<StackLayout.Item>
<Text>{this.props.message}</Text>
</StackLayout.Item>
<StackLayout.Item>
<Button onClick={this.props.onDismiss}>
<Icon name='checkmark' /> OK
</Button>
</StackLayout.Item>
</StackLayout>
</Modal>
)
}

View File

@@ -1,7 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Dimmer, Button, Progress } from 'semantic-ui-react'
import './ProgressDialog.scss'
import { Dimmer, Button, Progress } from '../ui'
export class ProgressDialog extends React.Component {
static propTypes = {

View File

@@ -1,11 +0,0 @@
.progress-dialog {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
}
#progress {
width: 60%;
}

View File

@@ -1,6 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Dimmer, Loader } from 'semantic-ui-react'
import { Dimmer, Loader } from '../ui'
export class WaitDialog extends React.Component {
static propTypes = {
@@ -10,9 +10,61 @@ export class WaitDialog extends React.Component {
render() {
return (
<Dimmer inverted page active={this.props.active}>
<div style={style.dimmer}>
<Loader inverted size='massive'>{this.props.message}...</Loader>
</Dimmer>
</div>
)
}
}
import React from 'react';
import PropTypes from 'prop-types';
import { keyframes, css } from 'emotion';
import { onlyUpdateForKeys } from 'recompose';
// This returns an animation
const pulse = keyframes`
0% {transform: scale(1);opacity: 1}
45% {transform: scale(0.1);opacity: 0.7}
80% {transform: scale(1);opacity: 1}
`;
class Loader extends React.Component {
style = i => css`{
background-color: ${this.props.color};
width: ${this.props.size}px;
height: ${this.props.size}px;
margin: ${this.props.margin};
border-radius: 100%;
display: inline-block;
animation: ${pulse} 0.75s ${i * 0.12}s infinite cubic-bezier(.2,.68,.18,1.08);
animation-fill-mode: both;
}`;
render() {
return this.props.loading ?
<div>
<div className={this.style(1)} />
<div className={this.style(2)} />
<div className={this.style(3)} />
</div> : null;
}
}
Loader.propTypes = {
loading: PropTypes.bool,
color: PropTypes.string,
size: PropTypes.number,
margin: PropTypes.string
};
Loader.defaultProps = {
loading: true,
color: '#000000',
size: 15,
margin: '2px'
};
const Component = onlyUpdateForKeys(['loading', 'color', 'size', 'margin'])(Loader);
Component.defaultProps = Loader.defaultProps;
export default Component;

View File

@@ -1,7 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import './YesNoMessageDialog.scss'
import { Modal, Button, Icon, Header } from 'semantic-ui-react'
import { Modal, Button, StackLayout, Text, Icon } from '../ui'
export class YesNoMessageDialog extends React.Component {
static propTypes = {
@@ -17,20 +16,24 @@ export class YesNoMessageDialog extends React.Component {
render() {
return (
<Modal dimmer='inverted' open={this.props.open} onClose={this.onDismiss(false)}
<Modal open={this.props.open} onClose={this.onDismiss(false)}
closeOnDimmerClick={false} className='yes-no-modal'>
<Header color='orange' size='large' icon='warning circle' content={this.props.title} />
<Modal.Content>
<p>{this.props.message}</p>
</Modal.Content>
<Modal.Actions>
<Button negative onClick={this.onDismiss(false)}>
<Icon name='remove' /> No
</Button>
<Button positive onClick={this.onDismiss(true)}>
<Icon name='checkmark' /> Yes
</Button>
</Modal.Actions>
<StackLayout>
<StackLayout.Item color='orange'>
<Text>{this.props.title}</Text>
</StackLayout.Item>
<StackLayout.Item>
<Text>{this.props.message}</Text>
</StackLayout.Item>
<StackLayout.Item>
<Button negative onClick={this.onDismiss(false)}>
<Icon name='remove' /> No
</Button>
<Button positive onClick={this.onDismiss(true)}>
<Icon name='checkmark' /> Yes
</Button>
</StackLayout.Item>
</StackLayout>
</Modal>
)
}

View File

@@ -1,6 +0,0 @@
.yes-no-modal p {
font-size: 1.125em;
line-height: 1.6em;
width: 90%;
margin: auto;
}

View File

@@ -1,106 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Button, Header, Icon } from 'semantic-ui-react'
import { autoBind } from 'auto-bind2'
import './FilePicker.scss'
export class FilePicker extends React.Component {
static propTypes = {
validExtensions: PropTypes.arrayOf(PropTypes.string).isRequired, // Make sure these are lowercase
onFileSelect: PropTypes.func,
content: PropTypes.string,
horizontal: PropTypes.bool
}
constructor(props) {
super(props)
autoBind(this, (name) => (name.startsWith('handle')))
this.state = {
icon: 'none',
value: ''
}
this.counter = 0
}
handleDragEnter(e) {
this.counter++
this.setState({ icon: 'pending' })
}
handleDragLeave(e) {
this.counter--
if (this.counter === 0) {
this.setState({ icon: 'none' })
}
}
handleDragOver(e) {
e.preventDefault()
e.stopPropagation()
}
handleDragDrop(e) {
e.preventDefault()
const files = e.dataTransfer.files
if (files.length > 0) {
const file = files[0]
if (file) {
const fileName = file.name.toLowerCase()
const isValidFile = this.props.validExtensions.some((ext) => (fileName.endsWith(ext)))
this.setState({ icon: isValidFile ? 'valid' : 'invalid' })
this.counter = 0
if (isValidFile && this.props.onFileSelect) {
this.props.onFileSelect(file)
window.setTimeout(function() {
this.setState({ icon: 'none' })
}.bind(this), 1000)
}
}
}
}
handleOnFileChange(e) {
let file = e.currentTarget.files[0]
this.setState({ value: '' })
if (this.props.onFileSelect) {
this.props.onFileSelect(file)
}
}
render() {
return (
<div draggable id={this.props.horizontal ? 'holderHorizontal' : 'holderVertical'}
onDragOver={this.handleDragOver}
onDragEnter={this.handleDragEnter} onDragLeave={this.handleDragLeave}
onDrop={this.handleDragDrop}>
<input id='fileInput' type='file' onChange={this.handleOnFileChange} value={this.state.value} />
<Button as='label' htmlFor='fileInput' size={'medium'} type='browse' name='selectFile'
id='selectFile' content={this.props.content} icon='upload' primary />
{ this.props.horizontal
? (<Icon id='fileIconHorizontal'
name={'file' + ((this.state.icon === 'none' || this.state.icon === 'pending') ? ' outline' : '')}
size='huge'
color={this.state.icon === 'valid' ? 'green' : this.state.icon === 'invalid' ? 'red' : 'grey'}
style={this.state.icon === 'pending' ? { transform: 'scale(1.5)' } : {}} />)
: (<Header as='h3' color='grey' icon>
OR
<Header.Subheader>
Drag-and-drop the file here
</Header.Subheader>
<Icon id='fileIconVertical' className=''
name={'file' + ((this.state.icon === 'none' || this.state.icon === 'pending') ? ' outline' : '')}
size='huge'
color={this.state.icon === 'valid' ? 'green' : this.state.icon === 'invalid' ? 'red' : 'grey'}
style={this.state.icon === 'pending' ? { transform: 'scale(1.5)' } : {}} />
</Header>)
}
</div>
)
}
}

View File

@@ -1,51 +0,0 @@
#holderVertical {
border: 3px dashed lightgray;
text-align: center;
border-radius: 0.5em;
display: flex;
flex-direction: column;
align-items: center;
padding: 0.25em 1.25em;
width: auto;
overflow: hidden;
}
#holderHorizontal {
border: 3px dashed lightgray;
text-align: center;
border-radius: 0.5em;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 0.25em 1.25em;
width: auto;
}
#helpText {
height: 2em;
margin-top: 2em;
margin-bottom: 2em;
color: lightgray;
}
#fileIconVertical {
margin-top: 0.5em;
margin-bottom: 0.5em;
transition: all 0.3s ease;
}
#fileIconHorizontal {
margin-left: 0.3em;
transition: all 0.3s ease;
}
#selectFile {
margin-top: 1.5em;
margin-bottom: 1.5em;
white-space: nowrap;
}
#fileInput {
display: none;
}

View File

@@ -1 +0,0 @@
export { FilePicker } from './FilePicker'

View File

@@ -1,5 +1,4 @@
import React from 'react'
import { Container } from 'semantic-ui-react'
import { ProfileForm } from './ProfileForm'
import { Constants, api } from '../helpers'
import { WaitDialog, MessageDialog, ChangePasswordDialog, ProgressDialog, ChangeEmailDialog } from '../Dialog'
@@ -147,7 +146,7 @@ export class Profile extends React.Component {
render() {
return (
<Container>
<div>
<ProfileForm
user={this.state.user}
onSaved={this.handleSaved}
@@ -171,7 +170,7 @@ export class Profile extends React.Component {
message={this.state.progressDialog ? this.state.progressDialog.message : ''}
percent={this.state.uploadPercent}
onCancel={this.handleUploadCancel} />
</Container>
</div>
)
}
}

View File

@@ -1,11 +1,8 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Grid, Form, Image } from 'semantic-ui-react'
import { StackLayout, Button } from '../ui'
import { regExpPattern } from 'regexp-pattern'
import './ProfileForm.scss'
import { Constants } from '../helpers'
import { FilePicker } from '../FilePicker'
import { Validator, ValidatedInput, ValidatedDropdown, ValidatedButton, ValidatedDatePicker } from '../Validated'
import { Validator, ValidatedInput, ValidatedButton } from '../Validated'
export class ProfileForm extends React.Component {
static propTypes = {
@@ -13,9 +10,7 @@ export class ProfileForm extends React.Component {
onSaved: PropTypes.func.isRequired,
onModifiedChanged: PropTypes.func,
onChangePassword: PropTypes.func,
onChangeEmail: PropTypes.func,
onSelectImage: PropTypes.func,
userImageUrl: PropTypes.string
onChangeEmail: PropTypes.func
}
static validations = {
@@ -99,72 +94,32 @@ export class ProfileForm extends React.Component {
render() {
return (
<Form className='profile-form' onSubmit={this.handleSubmit}>
<Grid stackable>
<Grid.Column width={3}>
<Image id='userImage' shape='circular' size='medium' src={this.props.userImageUrl} centered />
<FilePicker validExtensions={['.jpg', '.jpeg', '.png']} content='Select Image' onFileSelect={this.props.onSelectImage} />
</Grid.Column>
<Grid.Column width={13}>
<Form.Group>
<ValidatedInput label='First Name' name='firstName' width={8}
validator={this.state.validator} />
<ValidatedInput label='Last Name' name='lastName' width={8}
validator={this.state.validator} />
</Form.Group>
<Form.Group>
<ValidatedInput label='Email' name='email' width={8} message='Required. Must be a valid email address.'
validator={this.state.validator} />
<Form.Button fluid content={'Change Email'} label='&nbsp;'
width={4} onClick={this.props.onChangeEmail} />
<Form.Button fluid content={'Change Password'} label='&nbsp;'
width={4} onClick={this.props.onChangePassword} />
</Form.Group>
<Form.Group>
<ValidatedInput label='Zip' name='zip' width={4}
validator={this.state.validator} message='5 Character U.S. Zip Code. Optional.' />
<ValidatedDropdown label='State' name='state' width={6} message='Type or select a U.S. State or Province.'
placeholder='Select State' options={Constants.stateOptions}
validator={this.state.validator} searchable />
<ValidatedInput label='City' name='city' width={6}
validator={this.state.validator} message='U.S. City. Optional.' />
</Form.Group>
<Form.Group>
<ValidatedInput label='Address' name='address1' width={12}
validator={this.state.validator} message='Primary Street Address. Optional.' />
<ValidatedInput label='Apt. #' name='address2' width={4}
validator={this.state.validator} message='Apartment/Unit number. Optional.' />
</Form.Group>
<Form.Group>
<ValidatedInput label='Home Phone' name='homePhone' width={8}
validator={this.state.validator} message='A valid U.S. phone number. IE: (555)123-4567. Optional.' />
<ValidatedInput label='Cell Phone' name='cellPhone' width={8}
validator={this.state.validator} message='A valid U.S. phone number. IE: (555)123-4567. Optional.' />
</Form.Group>
<Form.Group>
<ValidatedDatePicker label='Date of Birth' name='dateOfBirth' width={5}
validator={this.state.validator} message='Select a date.' />
<ValidatedInput label='SSN' name='ssn' width={6}
validator={this.state.validator} message='U.S. Social Security Number. IE: 123-45-6789' />
<ValidatedDatePicker label='Hire Date' name='dateOfHire' width={5}
validator={this.state.validator} message='Select a date.' />
</Form.Group>
<Form.Group>
<Form.Field width={12} />
<ValidatedButton submit primary width={4} size='medium' content='Save' label='&nbsp;' name='save'
validator={this.state.validator} />
</Form.Group>
</Grid.Column>
</Grid>
</Form>
<form className='profile-form' onSubmit={this.handleSubmit}>
<StackLayout stackable>
<StackLayout.Item>
<ValidatedInput label='First Name' name='firstName' width={8}
validator={this.state.validator} />
</StackLayout.Item>
<StackLayout.Item>
<ValidatedInput label='Last Name' name='lastName' width={8}
validator={this.state.validator} />
</StackLayout.Item>
<StackLayout.Item>
<ValidatedInput label='Email' name='email' width={8} message='Required. Must be a valid email address.'
validator={this.state.validator} />
</StackLayout.Item>
<StackLayout.Item>
<Button fluid content={'Change Email'} label='&nbsp;'
width={4} onClick={this.props.onChangeEmail} />
<Button fluid content={'Change Password'} label='&nbsp;'
width={4} onClick={this.props.onChangePassword} />
</StackLayout.Item>
<StackLayout.Item>
<ValidatedButton submit primary width={4} size='medium' content='Save' label='&nbsp;' name='save'
validator={this.state.validator} />
</StackLayout.Item>
</StackLayout>
</form>
)
}
}

View File

@@ -1,12 +0,0 @@
.profile-form {
text-align: left;
margin: 3em auto 4em auto;
}
.profile-form > .fields {
margin-bottom: 1.5em !important;
}
#userImage {
margin-bottom: 2em;
}

View File

@@ -2,11 +2,10 @@ import React from 'react'
import PropTypes from 'prop-types'
import { autoBind } from 'auto-bind2'
import { regExpPattern } from 'regexp-pattern'
import { Grid, Form } from 'semantic-ui-react'
import './UserForm.scss'
import { ValidatedEmailIcon } from './ValidatedEmailIcon'
import { Constants, api } from '../helpers'
import { Validator, ValidatedInput, ValidatedButton, ValidatedDropdown, ValidatedDatePicker, ValidatedContainer } from '../Validated'
import { Validator, ValidatedInput, ValidatedButton, ValidatedCheckbox } from '../Validated'
import { StackLayout } from '../ui'
export class UserForm extends React.Component {
static propTypes = {
@@ -144,87 +143,43 @@ export class UserForm extends React.Component {
render() {
return (
<Form className='user-form' onSubmit={this.handleSubmit}>
<Grid stackable>
<Grid.Column width={16}>
<Form.Group>
<ValidatedDropdown label={'Deighton Access & Security Level'} width={6} selection
options={Constants.accessLevels} name='role' message='The user role and security level'
placeholder='' validator={this.state.validator} />
</Form.Group>
<Form.Group>
<ValidatedInput label='First Name' name='firstName'
width={8} validator={this.state.validator} />
<ValidatedInput label='Last Name' name='lastName'
width={8} validator={this.state.validator} />
</Form.Group>
<Form.Group>
<ValidatedInput label='Email' name='email' width={8} message='Must be a valid email address. Required.'
validator={this.state.validator} />
<ValidatedEmailIcon name='emailValidated' validator={this.state.validator} width={4}
onClick={this.handleResendEmail} />
<ValidatedButton width={4} size='medium' content='Change Email' label='&nbsp;' name='changeEmail'
validator={this.state.validator} onClick={this.handleChangeEmail} />
</Form.Group>
<form onSubmit={this.handleSubmit}>
<StackLayout>
<StackLayout.Item>
<ValidatedCheckbox label={'Deighton Access & Security Level'} width={6} selection
options={Constants.accessLevels} name='role' message='The user role and security level'
placeholder='' validator={this.state.validator} />
</StackLayout.Item>
<StackLayout.Item>
<ValidatedInput label='First Name' name='firstName'
width={8} validator={this.state.validator} />
</StackLayout.Item>
<StackLayout.Item>
<ValidatedInput label='Last Name' name='lastName'
width={8} validator={this.state.validator} />
</StackLayout.Item>
<StackLayout.Item>
<ValidatedInput label='Email' name='email' width={8} message='Must be a valid email address. Required.'
validator={this.state.validator} />
<ValidatedEmailIcon name='emailValidated' validator={this.state.validator} width={4}
onClick={this.handleResendEmail} />
</StackLayout.Item>
<StackLayout.Item>
<ValidatedButton width={4} size='medium' content='Change Email' label='&nbsp;' name='changeEmail'
validator={this.state.validator} onClick={this.handleChangeEmail} />
</StackLayout.Item>
<ValidatedContainer name='standard-fields' validator={this.state.validator}>
<Form.Group>
<ValidatedInput label='Zip' width={4} name='zip' message='5 Character U.S. Zip Code. Optional.'
position='bottom center' validator={this.state.validator} />
<ValidatedDropdown label='State' name='state' width={4}
placeholder='Select State' options={Constants.stateOptions} validator={this.state.validator} searchable />
<ValidatedInput label='City' width={8} type='text' name='city' message='U.S. City. Optional.'
validator={this.state.validator} />
</Form.Group>
<Form.Group>
<ValidatedInput label='Address' width={12} name='address1'
validator={this.state.validator} message='Primary Street Address. Optional.' />
<ValidatedInput label='Apt. #' width={4} name='address2'
validator={this.state.validator} message='Apartment/Unit number. Optional.' />
</Form.Group>
<Form.Group>
<ValidatedInput label='Home Phone' width={8} name='homePhone'
validator={this.state.validator} message='A valid U.S. phone number. IE: (555)123-4567. Optional.' />
<ValidatedInput label='Cell Phone' width={8} name='cellPhone'
validator={this.state.validator} message='A valid U.S. phone number. IE: (555)123-4567. Optional.' />
</Form.Group>
<Form.Group>
<ValidatedDatePicker label='Date of Birth' width={5} name='dateOfBirth'
validator={this.state.validator} />
<ValidatedInput label='SSN' width={6} name='ssn'
validator={this.state.validator} message='U.S. Social Security Number. IE: 123-45-6789' />
<ValidatedDatePicker label='Hire Date' width={5} name='dateOfHire'
validator={this.state.validator} />
</Form.Group>
</ValidatedContainer>
<ValidatedContainer name='broker-fields' validator={this.state.validator}>
<Form.Group>
<ValidatedInput label='# Households' width={5} name='numHouseholds'
message='Number of households in this brokers account' validator={this.state.validator} />
<ValidatedInput label='T-12' width={6} name='t12' message='This brokers T-12 info.'
validator={this.state.validator} />
<ValidatedInput label='AUM' width={5} name='aum'
message='This brokers AUM information.' validator={this.state.validator} />
</Form.Group>
</ValidatedContainer>
<Form.Group>
<ValidatedButton color='red' width={4} size='medium' content='Remove' label='&nbsp;' name='remove'
validator={this.state.validator} onClick={this.props.onRemove} />
<ValidatedButton width={4} size='medium' content='Reset' label='&nbsp;' name='reset'
validator={this.state.validator} onClick={this.handleReset} />
<Form.Field width={this.state.validator._id ? 8 : 12} />
<ValidatedButton primary submit width={4} size='medium'
content={this.state.validator._id ? 'Save' : 'Add'} label='&nbsp;' name='submit'
validator={this.state.validator} />
</Form.Group>
</Grid.Column>
</Grid>
</Form>
<StackLayout.Item>
<ValidatedButton color='red' width={4} size='medium' content='Remove' label='&nbsp;' name='remove'
validator={this.state.validator} onClick={this.props.onRemove} />
<ValidatedButton width={4} size='medium' content='Reset' label='&nbsp;' name='reset'
validator={this.state.validator} onClick={this.handleReset} />
<ValidatedButton primary submit width={4} size='medium'
content={this.state.validator._id ? 'Save' : 'Add'} label='&nbsp;' name='submit'
validator={this.state.validator} />
</StackLayout.Item>
</StackLayout>
</form>
)
}
}

View File

@@ -1,6 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Dropdown, List, Icon, Button, Image } from 'semantic-ui-react'
import { Dropdown, List, Icon, Button, Image } from '../ui'
import { Constants, api } from '../helpers'
import './UserList.scss'

View File

@@ -1,10 +1,10 @@
import React from 'react'
import { Container, Grid } from 'semantic-ui-react'
import { autoBind } from 'auto-bind2'
import { UserList } from './UserList'
import { UserForm } from './UserForm'
import { UserFormPlaceholder } from './UserFormPlaceholder'
import { api } from '../helpers'
import { RowLayout } from '../ui'
import { YesNoMessageDialog, MessageDialog, ChangeEmailDialog, WaitDialog } from '../Dialog'
export class Users extends React.Component {
@@ -237,19 +237,19 @@ export class Users extends React.Component {
render() {
return (
<Container>
<div>
<div>Users</div>
<Grid stackable>
<RowLayout>
{/* User List - Displayed on left hand side. */}
<Grid.Column width={5}>
<RowLayout.Item width={5}>
<UserList users={this.state.users} selectedUser={this.state.selectedUser}
selectionModified={this.state.modified} onUserListClick={this.handleUserListClick}
onAddNewUser={this.handleAddNewUser} />
</Grid.Column>
</RowLayout.Item>
{/* User Info - Displayed on right hand side. */}
<Grid.Column width={11}>
<RowLayout.Item>
{
this.state.selectedUser
? <UserForm user={this.state.selectedUser} onSave={this.handleSave}
@@ -257,8 +257,8 @@ export class Users extends React.Component {
onChangeEmail={this.handleChangeEmail} onResendEmail={this.handleResendEmail} />
: <UserFormPlaceholder />
}
</Grid.Column>
</Grid>
</RowLayout.Item>
</RowLayout>
<ChangeEmailDialog open={!!this.state.changeEmailDialog} onDismiss={this.handleChangeEmailDismiss} />
@@ -275,7 +275,7 @@ export class Users extends React.Component {
onDismiss={this.handleMessageDialogDismiss} />
<WaitDialog active={!!this.state.waitDialog} message={this.state.waitDialog ? this.state.waitDialog.message : ''} />
</Container>
</div>
)
}
}

View File

@@ -1,6 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Form, Icon, Popup, Button } from 'semantic-ui-react'
import { Label, Button, Icon } from '../ui'
import './ValidatedEmailIcon.scss'
// This is a validated component with a value that cannot change itself and is specialized
@@ -27,20 +27,19 @@ export class ValidatedEmailIcon extends React.Component {
render() {
if (this.state.value) {
return (
<Form.Field width={this.props.width}>
<label>&nbsp;</label>
<Popup content='Email Validated' position='bottom center' hoverable trigger={
<div width={this.props.width}>
<Label>&nbsp;
<Icon name='mail' color='green' size='big' className='mail-validated-icon' />
} />
</Form.Field>
</Label>
</div>
)
} else {
return (
<Form.Field width={this.props.width}>
<label>&nbsp;</label>
<div width={this.props.width}>
<Label>&nbsp;</Label>
<Button fluid icon='mail outline' color='red' labelPosition='left'
content='Resend Email' onClick={this.props.onClick} disabled={this.state.disabled} />
</Form.Field>
</div>
)
}
}

View File

@@ -1,6 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Button } from 'semantic-ui-react'
import { Button } from '../ui'
export class ValidatedActionsButton extends React.Component {
static propTypes = {

View File

@@ -1,6 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Form, Button } from 'semantic-ui-react'
import { Button } from '../ui'
export class ValidatedButton extends React.Component {
static propTypes = {
@@ -46,13 +46,15 @@ export class ValidatedButton extends React.Component {
render() {
if (this.state.visible) {
return (
<Form.Field width={this.props.width} disabled={this.state.disabled}>
<div width={this.props.width} disabled={this.state.disabled}>
<label>{this.props.label}</label>
<Button fluid color={this.props.color} primary={this.props.primary}
<Button color={this.props.color} primary={this.props.primary}
type={this.props.submit ? 'submit' : 'button'} onClick={this.props.onClick}
content={this.props.content} size={this.props.size} name={this.props.name}
floated={this.props.floated} />
</Form.Field>
size={this.props.size} name={this.props.name}
floated={this.props.floated}>
{this.props.content}
</Button>
</div>
)
} else {
return null

View File

@@ -1,7 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Form } from 'semantic-ui-react'
import { Checkbox } from '../ui'
import { Checkbox, Label } from '../ui'
// This is an example of a validated component with a value that can change itself, that cannot ever be invalid.
@@ -38,9 +37,10 @@ export class ValidatedCheckbox extends React.Component {
render() {
return (
<Form.Field width={this.props.width} disabled={this.state.disabled} className={this.props.className}>
<Checkbox value={!!this.state.value} name={this.props.name} label={this.props.label} />
</Form.Field>
<Label width={this.props.width} disabled={this.state.disabled} className={this.props.className}>
<Checkbox value={!!this.state.value} name={this.props.name} />
{this.props.label}
</Label>
)
}
}

View File

@@ -1,6 +1,5 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Container } from 'semantic-ui-react'
export class ValidatedContainer extends React.Component {
static propTypes = {
@@ -41,9 +40,9 @@ export class ValidatedContainer extends React.Component {
render() {
if (this.state.visible) {
return (
<Container>
<div>
{this.props.children}
</Container>
</div>
)
} else {
return null

View File

@@ -1,56 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Form, Popup } from 'semantic-ui-react'
import 'moment'
import { DatePickerInput } from 'rc-datepicker'
import 'rc-datepicker/lib/style.css'
export class ValidatedDatePicker extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired,
message: PropTypes.string.isRequired,
label: PropTypes.string,
width: PropTypes.number,
validator: PropTypes.object.isRequired
}
constructor(props) {
super(props)
this.state = props.validator.getField(props.name)
this.handleChange = this.handleChange.bind(this)
this.handleClear = this.handleClear.bind(this)
}
handleChange(e, data) {
const { validator, name } = this.props
const state = validator.getField(name)
if (!state.readOnly && !state.disabled) {
// NOTE: data is a little different for this control - no value property
this.setState(validator.updateValue(name, data))
}
}
handleClear(e) {
this.props.validator.updateValue(this.props.name, '')
this.setState(this.props.validator.getField(this.props.name))
}
componentWillReceiveProps(nextProps) {
if (nextProps.validator !== this.props.validator) {
this.setState(nextProps.validator.getField(nextProps.name))
}
}
render() {
return (
<Form.Field error={!this.state.valid} width={this.props.width} disabled={this.state.disabled}>
<label>{this.props.label}</label>
<Popup content={this.props.message} position='right center' hoverable trigger={
<DatePickerInput value={this.state.value} name={this.props.name} onChange={this.handleChange}
showOnInputClick onClear={this.handleClear} />} />
</Form.Field>
)
}
}

View File

@@ -1,6 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Form, Dropdown, Popup } from 'semantic-ui-react'
import { Dropdown, Text } from '../ui'
export class ValidatedDropdown extends React.Component {
static propTypes = {
@@ -35,13 +35,13 @@ export class ValidatedDropdown extends React.Component {
render() {
return (
<Form.Field error={!this.state.valid} width={this.props.width}>
<div error={!this.state.valid} width={this.props.width}>
<label>{this.props.label}</label>
<Popup content={this.props.message} position='right center' hoverable trigger={
<Dropdown selection fluid disabled={this.state.disabled} readOnly={this.state.readOnly}
placeholder={this.props.placeholder} options={this.props.options}
value={this.state.value} onChange={this.handleChange} search={this.props.searchable} />} />
</Form.Field>
<Dropdown selection disabled={this.state.disabled} readOnly={this.state.readOnly}
placeholder={this.props.placeholder} options={this.props.options}
value={this.state.value} onChange={this.handleChange} search={this.props.searchable} />
<Text>{this.props.message}</Text>
</div>
)
}
}

View File

@@ -1,7 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Form } from 'semantic-ui-react'
import { Input } from '../ui'
import { Input, Label, Text } from '../ui'
// This is an example of a validated component with a value that changes itself
@@ -13,10 +12,7 @@ export class ValidatedInput extends React.Component {
width: PropTypes.number,
validator: PropTypes.object.isRequired,
password: PropTypes.bool,
className: PropTypes.string,
placeholder: PropTypes.string,
icon: PropTypes.string,
iconPosition: PropTypes.string
placeholder: PropTypes.string
}
constructor(props) {
@@ -43,17 +39,17 @@ export class ValidatedInput extends React.Component {
render() {
return (
<Form.Field error={!this.state.valid} width={this.props.width} disabled={this.state.disabled} className={this.props.className}>
<label>{this.props.label}</label>
<Input value={this.state.value}
hidden={this.props.password}
name={this.props.name}
onChange={this.handleChange}
placeholder={this.props.placeholder}
className={this.props.className} icon={this.props.icon}
iconPosition={this.props.iconPosition} />
<label>{this.props.message}</label>
</Form.Field>
<div width={this.props.width} disabled={this.state.disabled}>
<Label>{this.props.label}<br />
<Input value={this.state.value}
password={this.props.password}
name={this.props.name}
onChange={this.handleChange}
placeholder={this.props.placeholder} />
</Label>
<br />
<Text size='small'>{this.props.message}</Text>
</div>
)
}
}

View File

@@ -3,6 +3,5 @@ export { ValidatedInput } from './ValidatedInput'
export { ValidatedButton } from './ValidatedButton'
export { ValidatedActionsButton } from './ValidatedActionsButton'
export { ValidatedDropdown } from './ValidatedDropdown'
export { ValidatedDatePicker } from './ValidatedDatePicker'
export { ValidatedCheckbox } from './ValidatedCheckbox'
export { ValidatedContainer } from './ValidatedContainer'

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 320.002 320.002" style="enable-background:new 0 0 320.002 320.002;" xml:space="preserve">
<g id="XMLID_6_">
<path id="XMLID_7_" d="M51.213,175.001h173.785c8.284,0,15-6.716,15-15c0-8.284-6.716-15-15-15H51.213l19.394-19.394
c5.858-5.858,5.858-15.355,0-21.213c-5.857-5.858-15.355-5.858-21.213,0L4.396,149.393c-0.351,0.351-0.683,0.719-0.997,1.103
c-0.137,0.167-0.256,0.344-0.385,0.515c-0.165,0.22-0.335,0.435-0.488,0.664c-0.14,0.209-0.261,0.426-0.389,0.64
c-0.123,0.206-0.252,0.407-0.365,0.619c-0.118,0.22-0.217,0.446-0.323,0.67c-0.104,0.219-0.213,0.435-0.306,0.659
c-0.09,0.219-0.164,0.442-0.243,0.664c-0.087,0.24-0.179,0.477-0.253,0.722c-0.067,0.222-0.116,0.447-0.172,0.672
c-0.063,0.249-0.133,0.497-0.183,0.751c-0.051,0.259-0.082,0.521-0.119,0.782c-0.032,0.223-0.075,0.443-0.097,0.669
c-0.048,0.484-0.073,0.971-0.074,1.457c0,0.007-0.001,0.015-0.001,0.022c0,0.007,0.001,0.015,0.001,0.022
c0.001,0.487,0.026,0.973,0.074,1.458c0.022,0.223,0.064,0.44,0.095,0.661c0.038,0.264,0.069,0.528,0.121,0.79
c0.05,0.252,0.119,0.496,0.182,0.743c0.057,0.227,0.107,0.456,0.175,0.681c0.073,0.241,0.164,0.474,0.248,0.71
c0.081,0.226,0.155,0.453,0.247,0.675c0.091,0.22,0.198,0.431,0.3,0.646c0.108,0.229,0.21,0.46,0.33,0.685
c0.11,0.205,0.235,0.4,0.354,0.599c0.131,0.221,0.256,0.444,0.4,0.659c0.146,0.219,0.309,0.424,0.466,0.635
c0.136,0.181,0.262,0.368,0.407,0.544c0.299,0.364,0.616,0.713,0.947,1.048c0.016,0.016,0.029,0.034,0.045,0.05l45,45.001
c2.93,2.929,6.768,4.394,10.607,4.394c3.838-0.001,7.678-1.465,10.606-4.393c5.858-5.858,5.858-15.355,0.001-21.213L51.213,175.001
z"/>
<path id="XMLID_8_" d="M305.002,25h-190c-8.284,0-15,6.716-15,15v60c0,8.284,6.716,15,15,15s15-6.716,15-15V55h160v210.001h-160
v-45.001c0-8.284-6.716-15-15-15s-15,6.716-15,15v60.001c0,8.284,6.716,15,15,15h190c8.284,0,15-6.716,15-15V40
C320.002,31.716,313.286,25,305.002,25z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 210 210" style="enable-background:new 0 0 210 210;" xml:space="preserve">
<g id="XMLID_12_">
<path id="XMLID_13_" d="M195,0h-20c-8.284,0-15,6.716-15,15v20c0,8.284,6.716,15,15,15h20c8.284,0,15-6.716,15-15V15
C210,6.716,203.284,0,195,0z"/>
<path id="XMLID_14_" d="M115,0H95c-8.284,0-15,6.716-15,15v20c0,8.284,6.716,15,15,15h20c8.284,0,15-6.716,15-15V15
C130,6.716,123.284,0,115,0z"/>
<path id="XMLID_15_" d="M35,0H15C6.716,0,0,6.716,0,15v20c0,8.284,6.716,15,15,15h20c8.284,0,15-6.716,15-15V15
C50,6.716,43.284,0,35,0z"/>
<path id="XMLID_16_" d="M195,160h-20c-8.284,0-15,6.716-15,15v20c0,8.284,6.716,15,15,15h20c8.284,0,15-6.716,15-15v-20
C210,166.716,203.284,160,195,160z"/>
<path id="XMLID_17_" d="M115,160H95c-8.284,0-15,6.716-15,15v20c0,8.284,6.716,15,15,15h20c8.284,0,15-6.716,15-15v-20
C130,166.716,123.284,160,115,160z"/>
<path id="XMLID_18_" d="M35,160H15c-8.284,0-15,6.716-15,15v20c0,8.284,6.716,15,15,15h20c8.284,0,15-6.716,15-15v-20
C50,166.716,43.284,160,35,160z"/>
<path id="XMLID_19_" d="M195,80h-20c-8.284,0-15,6.716-15,15v20c0,8.284,6.716,15,15,15h20c8.284,0,15-6.716,15-15V95
C210,86.716,203.284,80,195,80z"/>
<path id="XMLID_20_" d="M115,80H95c-8.284,0-15,6.716-15,15v20c0,8.284,6.716,15,15,15h20c8.284,0,15-6.716,15-15V95
C130,86.716,123.284,80,115,80z"/>
<path id="XMLID_21_" d="M35,80H15C6.716,80,0,86.716,0,95v20c0,8.284,6.716,15,15,15h20c8.284,0,15-6.716,15-15V95
C50,86.716,43.284,80,35,80z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -1,9 +1,8 @@
import React from 'react'
import ReactDOM from 'react-dom'
import { App } from './App'
import './index.scss'
ReactDOM.render(
<App />,
<App style={{ minHeight: '100vh', position: 'relative' }} />,
document.getElementById('root')
)

View File

@@ -1,6 +0,0 @@
@import '~semantic-ui-css/semantic.min.css';
body #root {
min-height: 100vh;
position: relative;
}

View File

@@ -1,16 +1,18 @@
import { colorInfo, fontInfo } from './style'
export default {
base: {
borderRadius: '10px',
fontFamily: 'Arial',
color: '#ffffff',
fontFamily: fontInfo.family,
color: '#FFFFFF',
fontSize: '20px',
background: '#3498db',
background: colorInfo.buttonBackgroundHover,
padding: '10px 20px 10px 20px',
textDecoration: 'none',
outline: 'none',
':hover': {
background: '#3cb0fd',
background: colorInfo.buttonBackground,
textDecoration: 'none'
}
}

View File

@@ -1,8 +1,10 @@
import { colorInfo } from './style'
export default {
checkbox: {
cursor: 'pointer',
position: 'relative',
backgroundColor: '#2196F3',
backgroundColor: colorInfo.buttonBackground,
top: 0,
left: 0,
height: 25,

View File

@@ -0,0 +1,19 @@
import Radium from 'radium'
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import style from './Input.style'
class Dropdown extends Component {
static propTypes = {
password: PropTypes.bool,
children: PropTypes.node
}
render() {
return (
<input type={this.props.password ? 'password' : 'text'} style={style.base}>{this.props.children}</input>
)
}
}
export default Radium(Dropdown)

View File

@@ -0,0 +1,2 @@
export default {
}

View File

@@ -0,0 +1,28 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
// See https://www.flaticon.com/packs/web-button-compilation for more icons
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'
}
static svgs = {
logout: require('icons/logout.svg'),
shapes: require('icons/shapes.svg')
}
render() {
return <img style={{ width: this.props.size, height: this.props.size, margin: this.props.margin }}
src={Icon.svgs[this.props.name]} />
}
}

View File

@@ -0,0 +1,20 @@
import Radium from 'radium'
import PropTypes from 'prop-types'
import React, { Component } from 'react'
class Image extends Component {
static propTypes = {
source: PropTypes.string,
width: PropTypes.number,
height: PropTypes.number,
margin: PropTypes.number
}
render() {
return (
<img src={this.props.source} style={{ width: this.props.width, height: this.props.height, margin: this.props.margin }} />
)
}
}
export default Radium(Image)

View File

@@ -5,13 +5,13 @@ import style from './Input.style'
class Input extends Component {
static propTypes = {
hidden: PropTypes.bool,
password: PropTypes.bool,
children: PropTypes.node
}
render() {
return (
<input type={this.props.hidden ? 'password' : 'text'} style={style.base}>{this.props.children}</input>
<input type={this.props.password ? 'password' : 'text'} style={style.base}>{this.props.children}</input>
)
}
}

33
website/src/ui/Label.js Normal file
View File

@@ -0,0 +1,33 @@
import Radium from 'radium'
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { fontInfo } from './style'
class Label extends Component {
static propTypes = {
size: PropTypes.string,
color: PropTypes.string,
margin: PropTypes.number,
children: PropTypes.node
}
static defaultProps = {
size: 'medium',
color: 'normal',
margin: 0
}
render() {
return (
<label style={{
display: 'inline-block',
fontSize: fontInfo.size[this.props.size],
color: fontInfo.color[this.props.color],
fontFamily: fontInfo.family,
margin: this.props.margin
}}>{this.props.children}</label>
)
}
}
export default Radium(Label)

30
website/src/ui/Link.js Normal file
View File

@@ -0,0 +1,30 @@
import Radium from 'radium'
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { fontInfo } from './style'
class Link extends Component {
static propTypes = {
to: PropTypes.string,
size: PropTypes.string,
margin: PropTypes.number,
children: PropTypes.node
}
static defaultProps = {
size: 'medium',
margin: 0
}
render() {
return (
<a href={this.props.to} style={{
fontSize: fontInfo.size[this.props.size],
fontFamily: fontInfo.family,
margin: this.props.margin
}}>{this.props.children}</a>
)
}
}
export default Radium(Link)

30
website/src/ui/List.js Normal file
View File

@@ -0,0 +1,30 @@
import Radium from 'radium'
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { fontInfo } from './style'
class List extends Component {
static propTypes = {
size: PropTypes.string,
margin: PropTypes.number,
children: PropTypes.node
}
static defaultProps = {
size: 'medium',
margin: 0
}
render() {
return (
<span style={{
display: 'inline-block',
fontSize: fontInfo.size[this.props.size],
fontFamily: fontInfo.family,
margin: this.props.margin
}}>{this.props.children}</span>
)
}
}
export default Radium(List)

51
website/src/ui/Loader.js Normal file
View File

@@ -0,0 +1,51 @@
import React from 'react';
import PropTypes from 'prop-types';
import { keyframes, css } from 'emotion';
import { onlyUpdateForKeys } from 'recompose';
// This returns an animation
const pulse = keyframes`
0% {transform: scale(1);opacity: 1}
45% {transform: scale(0.1);opacity: 0.7}
80% {transform: scale(1);opacity: 1}
`;
class Loader extends React.Component {
style = i => css`{
background-color: ${this.props.color};
width: ${this.props.size}px;
height: ${this.props.size}px;
margin: ${this.props.margin};
border-radius: 100%;
display: inline-block;
animation: ${pulse} 0.75s ${i * 0.12}s infinite cubic-bezier(.2,.68,.18,1.08);
animation-fill-mode: both;
}`;
render() {
return this.props.loading ?
<div>
<div className={this.style(1)} />
<div className={this.style(2)} />
<div className={this.style(3)} />
</div> : null;
}
}
Loader.propTypes = {
loading: PropTypes.bool,
color: PropTypes.string,
size: PropTypes.number,
margin: PropTypes.string
};
Loader.defaultProps = {
loading: true,
color: '#000000',
size: 15,
margin: '2px'
};
const Component = onlyUpdateForKeys(['loading', 'color', 'size', 'margin'])(Loader);
Component.defaultProps = Loader.defaultProps;
export default Component;

29
website/src/ui/Modal.js Normal file
View File

@@ -0,0 +1,29 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import style from './Modal.style'
import { reactAutoBind } from 'auto-bind2'
export class Modal extends Component {
static propTypes = {
children: PropTypes.node
}
constructor(props) {
super(props)
reactAutoBind(this)
}
preventPropagation(e) {
e.stopPropagation()
}
render() {
return (
<div style={style.dimmer} onClick={this.preventPropagation}>
<div style={style.modal}>
{this.props.children}
</div>
</div>
)
}
}

View File

@@ -0,0 +1,28 @@
export default {
dimmer: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
height: '100vh',
width: '100vw',
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: 100,
background: 'rgba(25, 25, 30, 0.75)'
},
modal: {
zIndex: 101,
background: '#FFFFFF',
margin: 0,
padding: '18px 24px',
width: '80%',
border: '1px solid $border-primary',
borderRadius: 4,
boxShadow: '0 0 25px #000000'
}
}

View File

@@ -0,0 +1,31 @@
import Radium from 'radium'
import React, { Component } from 'react'
import PropTypes from 'prop-types'
class RowLayout extends Component {
static propTypes = {
children: PropTypes.node,
width: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]).isRequired
}
render() {
return (
<div style={{ display: 'flex', minWidth: this.props.width, flexDirection: 'row' }}>{this.props.children}</div>
)
}
}
RowLayout.Item = Radium(class RowLayoutItem extends Component {
static propTypes = {
children: PropTypes.node,
width: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ])
}
render() {
return (
<div style={[ this.props.width ? { minWidth: this.props.width } : { flexGrow: 1 } ]}>{this.props.children}</div>
)
}
})
export default Radium(RowLayout)

View File

@@ -0,0 +1,31 @@
import Radium from 'radium'
import React, { Component } from 'react'
import PropTypes from 'prop-types'
class StackLayout extends Component {
static propTypes = {
children: PropTypes.node,
height: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]).isRequired
}
render() {
return (
<div style={{ display: 'flex', minHeight: this.props.height, flexDirection: 'column' }}>{this.props.children}</div>
)
}
}
StackLayout.Item = Radium(class StackLayoutItem extends Component {
static propTypes = {
children: PropTypes.node,
height: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ])
}
render() {
return (
<div style={[ this.props.height ? { minHeight: this.props.height } : { flexGrow: 1 } ]}>{this.props.children}</div>
)
}
})
export default Radium(StackLayout)

View File

@@ -0,0 +1,30 @@
import Radium from 'radium'
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { fontInfo } from './style'
class Text extends Component {
static propTypes = {
size: PropTypes.string,
margin: PropTypes.number,
children: PropTypes.node
}
static defaultProps = {
size: 'medium',
margin: 0
}
render() {
return (
<span style={{
display: 'inline-block',
fontSize: fontInfo.size[this.props.size],
fontFamily: fontInfo.family,
margin: this.props.margin
}}>{this.props.children}</span>
)
}
}
export default Radium(Text)

View File

@@ -1,4 +1,16 @@
export { default as Button } from './Button'
export { default as Checkbox } from './Checkbox'
export { default as Input } from './Input'
export { default as Image } from './Image'
export { default as Text } from './Text'
export { default as Link } from './Link'
export { default as Label } from './Label'
export { default as Icon } from './Icon'
export { default as List } from './List'
export { default as Dropdown } from './Dropdown'
export { default as Modal } from './Modal'
export { default as Dimmer } from './Dimmer'
export { default as Loader } from './Loader'
export { default as HolyGrail } from './HolyGrail'
export { default as RowLayout } from './RowLayout'
export { default as StackLayout } from './StackLayout'

19
website/src/ui/style.js Normal file
View File

@@ -0,0 +1,19 @@
export const colorInfo = {
text: '#000000',
alertText: '#FF0000',
buttonBackground: '#3498DB',
buttonBackgroundHover: '#3CB0FD'
}
export const fontInfo = {
family: 'Hind, sans-serif', // https://fonts.google.com/specimen/Hind?selection.family=Hind
size: {
small: '10pt',
medium: '12pt',
large: '14pt'
},
color: {
'normal': colorInfo.text,
'alert': colorInfo.alertText
}
}

View File

@@ -1,5 +1,5 @@
export class VersionInfo {
static version = '0.0.4'
static fullVersion = '0.0.4-20171018.0'
static startYear = '2017'
export const versionInfo = {
version: '0.1.0',
fullVersion: '0.1.0-20180225.0',
startYear: '2017'
}