Created custom Loader component

This commit is contained in:
John Lyon-Smith
2018-03-02 08:55:17 -08:00
parent 5e790ba65c
commit 30fcdf9d77
13 changed files with 113 additions and 31 deletions

View File

@@ -1,5 +1,5 @@
{ {
"expoServerPort": 19000, "expoServerPort": 19000,
"packagerPort": 19001, "packagerPort": 19001,
"packagerPid": 46572 "packagerPid": 694
} }

View File

@@ -127,6 +127,11 @@
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
"dev": true "dev": true
}, },
"animejs": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/animejs/-/animejs-2.2.0.tgz",
"integrity": "sha1-Ne79/FNbgZScnLBvCz5gwC5v3IA="
},
"ansi-align": { "ansi-align": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-1.1.0.tgz", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-1.1.0.tgz",

View File

@@ -3,6 +3,7 @@
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"animejs": "^2.2.0",
"auto-bind2": "^1.0.2", "auto-bind2": "^1.0.2",
"eventemitter3": "^2.0.3", "eventemitter3": "^2.0.3",
"prop-types": "^15.5.10", "prop-types": "^15.5.10",

View File

@@ -150,7 +150,7 @@ export class Login extends React.Component {
</Row.Item> </Row.Item>
<Row.Item grow /> <Row.Item grow />
<WaitModal active={waitModal} message='Logging In' /> <WaitModal active={waitModal} message='Logging in...' />
<MessageModal error open={!!messageModal} <MessageModal error open={!!messageModal}
icon={messageModal ? messageModal.icon : ''} icon={messageModal ? messageModal.icon : ''}

View File

@@ -4,14 +4,17 @@ import { Dimmer, Loader, Text } from 'ui'
export class WaitModal extends React.Component { export class WaitModal extends React.Component {
static propTypes = { static propTypes = {
message: PropTypes.string.isRequired active: PropTypes.bool.isRequired,
message: PropTypes.string,
} }
render() { render() {
const { active, message } = this.props
return ( return (
<Dimmer> <Dimmer active={active}>
<Loader /> <Loader />
{this.props.message && <Text size='Huge'>{this.props.message}...</Text>} {message && <Text size='huge' color='inverse'>{message}</Text>}
</Dimmer> </Dimmer>
) )
} }

View File

@@ -1,8 +1,15 @@
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import { App } from './App' import { App } from './App'
import { colorInfo } from 'ui/style'
ReactDOM.render( ReactDOM.render(
<App style={{ minHeight: '100vh', position: 'relative' }} />, <App style={{ minHeight: '100vh', position: 'relative' }} />,
document.getElementById('root') document.getElementById('root')
) )
// HACK: See https://github.com/facebook/react/issues/5619
let css = document.createElement('style')
document.body.appendChild(css)
css.innerHTML = `::-webkit-input-placeholder { color: ${colorInfo.textPlaceholder} }`

50
website/src/ui/Anime.js Normal file
View File

@@ -0,0 +1,50 @@
import React from 'react'
import anime from 'animejs'
import PropTypes from 'prop-types'
export default class Anime extends React.Component {
static propTypes = {
as: PropTypes.string,
children: PropTypes.node,
}
constructor(props) {
super(props)
this.targets = []
}
componentDidMount() {
let animeProps = Object.assign({}, this.props, {
targets: this.targets
})
delete animeProps.children
delete animeProps.as
this.anime = anime(animeProps)
}
shouldComponentUpdate(nextProps) {
this.anime = anime(Object.assign({}, { targets: this.targets }, nextProps))
return true
}
addTarget = (newTarget) => {
this.targets = [...this.targets, newTarget]
}
render() {
let children = []
if (this.props.children) {
if (Array.isArray(this.props.children)) {
children = this.props.children
} else {
children = [this.props.children]
}
}
return React.createElement(
(this.props.as || 'div'), {}, children.map((child, i) => (React.cloneElement(child, { key: i, ref: this.addTarget })))
)
}
}

View File

@@ -6,8 +6,8 @@ import { reactAutoBind } from 'auto-bind2'
export class Dimmer extends Component { export class Dimmer extends Component {
static propTypes = { static propTypes = {
active: PropTypes.bool.isRequired,
children: PropTypes.node, children: PropTypes.node,
active: PropTypes.bool
} }
constructor(props) { constructor(props) {

View File

@@ -8,11 +8,8 @@ export default {
color: '#000000', color: '#000000',
borderStyle: 'solid', borderStyle: 'solid',
borderRadius: '5px', borderRadius: '5px',
// boxShadow: '0px 0px 0px rgba(66,66,66,.75)'
// textShadow: 'undefined 0px 0px 5px px rgba(66,66,66,.75)'
':focus': { ':focus': {
outline: 'none' outline: 'none'
} },
} }
} }

View File

@@ -1,6 +1,8 @@
import React from 'react' import React from 'react'
import Radium from 'radium' import Radium from 'radium'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { colorInfo } from 'ui/style'
import anime from 'animejs'
class Loader extends React.Component { class Loader extends React.Component {
static propTypes = { static propTypes = {
@@ -15,26 +17,38 @@ class Loader extends React.Component {
margin: '2px' margin: '2px'
} }
style(i) {
return {
backgroundColor: `${this.props.color}`,
width: `${this.props.size}px`,
height: `${this.props.size}px`,
margin: `${this.props.margin}`,
borderRadius: '100%',
display: 'inline-block',
// 0% {transform: scale(1); opacity: 1} 45% {transform: scale(0.1); opacity: 0.7} 80% {transform: scale(1); opacity: 1} 0.75s ${i * 0.12}s infinite cubic-bezier(.2,.68,.18,1.08)
animation: ``,
animationFillMode: 'both'
}
}
render() { render() {
const size = 20
const spacing = 5
const addAnimation = (elem, i) => {
anime({
targets: elem,
scale: [
{ value: 0.2, duration: 800, easing: 'easeOutSine', delay: 200 * i },
{ value: 1.0, duration: 800, easing: 'easeOutSine' },
{ value: 1.0, duration: 200 * (2 - i) }
],
loop: true
})
}
return ( return (
<div> <div style={{ height: size }}>
<div className={this.style(1)} /> {
<div className={this.style(2)} /> [0, 1, 2].map((i) => (
<div className={this.style(3)} /> <span key={i} ref={(elem) => addAnimation(elem, i)}
style={{
display: 'inline-block',
marginLeft: i > 0 ? spacing : 0,
width: size,
height: size,
background: colorInfo.textInverse,
borderSize: 0,
borderRadius: '100%'
}} />
))
}
</div> </div>
) )
} }

View File

@@ -5,10 +5,10 @@ import { fontInfo } from './style'
class Text extends Component { class Text extends Component {
static propTypes = { static propTypes = {
size: PropTypes.string, size: PropTypes.oneOf(['small', 'medium', 'large', 'huge']),
color: PropTypes.oneOf(['normal', 'inverse', 'alert', 'dimmed']),
margin: PropTypes.number, margin: PropTypes.number,
children: PropTypes.node, children: PropTypes.node,
color: PropTypes.oneOf(['normal', 'alert', 'dimmed']),
width: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]), width: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
align: PropTypes.string, align: PropTypes.string,
} }

View File

@@ -1,3 +1,4 @@
export { default as Anime } from './Anime'
export { default as Box } from './Box' export { default as Box } from './Box'
export { default as Button } from './Button' export { default as Button } from './Button'
export { default as HeaderButton } from './HeaderButton' export { default as HeaderButton } from './HeaderButton'

View File

@@ -1,5 +1,7 @@
export const colorInfo = { export const colorInfo = {
text: '#000000', text: '#000000',
textInverse: '#FFFFFF',
textPlaceholder: '#EEEEEE',
alertText: '#FF0000', alertText: '#FF0000',
grayText: '#B0B0B0', grayText: '#B0B0B0',
buttonBackground: '#3498DB', buttonBackground: '#3498DB',
@@ -28,10 +30,12 @@ export const fontInfo = {
size: { size: {
small: '10pt', small: '10pt',
medium: '12pt', medium: '12pt',
large: '14pt' large: '14pt',
huge: '26pt',
}, },
color: { color: {
'normal': colorInfo.text, 'normal': colorInfo.text,
'inverse': colorInfo.textInverse,
'alert': colorInfo.alertText, 'alert': colorInfo.alertText,
'dimmed': colorInfo.grayText, 'dimmed': colorInfo.grayText,
} }