mirror of
https://github.com/hanzo-labs/wallet-2.git
synced 2026-10-30 18:54:05 +00:00
251 lines
5.6 KiB
JavaScript
251 lines
5.6 KiB
JavaScript
import React from 'react'
|
|
|
|
import valueOrCall from '../../src/util/valueOrCall'
|
|
import classnames from 'classnames'
|
|
|
|
// Base control class
|
|
let controlId = 0
|
|
|
|
export default function control(ControlComponent) {
|
|
return class Control extends React.Component {
|
|
// Props should be
|
|
constructor(props) {
|
|
super(props)
|
|
|
|
// Unique ID for referencing the control
|
|
this.controlId = controlId++
|
|
|
|
if (props.emitter && this.props.data) {
|
|
props.emitter.unique('control:submit', () => {
|
|
return this._change(this.state.value, true)
|
|
})
|
|
|
|
// emitter.off to force any duplicate unmounted inputs out of scope
|
|
props.emitter.unique('control:value', (v) => {
|
|
if (v != null) {
|
|
this.props.data.set(this.props.name, v)
|
|
this.setState({
|
|
value: v
|
|
})
|
|
|
|
if (v != this.props.defaultValue) {
|
|
this._change(v)
|
|
}
|
|
}
|
|
return this.props.data.get(this.props.name)
|
|
})
|
|
}
|
|
|
|
let value
|
|
if (this.props.data) {
|
|
value = this.props.data.get(this.props.name)
|
|
}
|
|
|
|
if (value == null) {
|
|
value = props.value
|
|
}
|
|
|
|
if (value == null) {
|
|
value == props.defaultValue
|
|
}
|
|
|
|
this.state = {
|
|
value: value,
|
|
valid: false,
|
|
errorMessage: '',
|
|
appIsMounted: false
|
|
}
|
|
|
|
if (props.value != props.defaultValue) {
|
|
this._change(props.value)
|
|
}
|
|
|
|
this.inputRef = React.createRef()
|
|
}
|
|
|
|
componentDidMount() {
|
|
requestAnimationFrame(() => {
|
|
this.setState({ appIsMounted: true })
|
|
});
|
|
|
|
if (this.props.data) {
|
|
this._onSet = (k, v) => {
|
|
if (k==this.props.name && v != this.state.value) {
|
|
this.change(v)
|
|
}
|
|
}
|
|
this.props.data.on('set', this._onSet)
|
|
}
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
// These are unbound on the form level
|
|
// this.props.emitter.off('control:submit')
|
|
// this.props.emitter.off('control:value')
|
|
//
|
|
if (this.props.data) {
|
|
this.props.data.off('set', this._onSet)
|
|
}
|
|
}
|
|
|
|
getId() {
|
|
return 'control-' + this.controlId
|
|
}
|
|
|
|
getName() {
|
|
return valueOrCall(this.props.name || '').replace(/\\./g, '-')
|
|
}
|
|
|
|
getValue(eventOrValue) {
|
|
if (eventOrValue == null) {
|
|
return ''
|
|
}
|
|
|
|
if (!eventOrValue.target) {
|
|
return eventOrValue
|
|
}
|
|
|
|
let val = eventOrValue.target.value
|
|
|
|
return val || ''
|
|
}
|
|
|
|
getErrorMessage() {
|
|
return this.state.errorMessage || ''
|
|
}
|
|
|
|
error(value, e) {
|
|
this.setState({
|
|
errorMessage: e,
|
|
value: value,
|
|
valid: false
|
|
}, () => {
|
|
if (this.props.scrollToError && this.inputRef.current) {
|
|
this.inputRef.current.scrollIntoView()
|
|
this.inputRef.current.focus()
|
|
}
|
|
})
|
|
}
|
|
|
|
runMiddleware(value) {
|
|
// need to replace with actual middleware stack
|
|
let [p, ...middleware] = this.props.middleware || []
|
|
|
|
if (!p) {
|
|
return new Promise((resolve) => {
|
|
resolve(value)
|
|
})
|
|
}
|
|
|
|
let oldValue = this.state.value
|
|
let name = this.props.name
|
|
|
|
p = p.call(this.props, value, oldValue, name).then((v) => {
|
|
return v
|
|
}).catch((err) => {
|
|
throw err
|
|
})
|
|
|
|
for(let k in middleware) {
|
|
let m = middleware[k]
|
|
|
|
p = p.then((v) => {
|
|
return new Promise((resolve, reject)=> {
|
|
m.call(this.props, v, oldValue, name).then((v) => {
|
|
resolve(v)
|
|
}).catch((err) => {
|
|
reject(err)
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
return p
|
|
}
|
|
|
|
// Note, its atleast 2 characters (max savings unbounded) shorter to abuse ES6 automatic this binding syntax with => than adding explicit binds
|
|
change = (event) => {
|
|
this.setState({
|
|
errorMessage: '',
|
|
valid: false
|
|
})
|
|
this._change(this.getValue(event))
|
|
}
|
|
|
|
_change(value, rethrow) {
|
|
// trim the value we run the middleware on
|
|
let valueTrimmed = value
|
|
|
|
if (typeof valueTrimmed == 'string') {
|
|
valueTrimmed = value.trim()
|
|
}
|
|
|
|
return this.runMiddleware(valueTrimmed)
|
|
.then((newValue) => {
|
|
if (newValue == valueTrimmed) {
|
|
this.changed(value)
|
|
} else {
|
|
this.changed(newValue)
|
|
}
|
|
}).catch((err) => {
|
|
this.error(value, err.message)
|
|
if (rethrow) {
|
|
throw err
|
|
}
|
|
})
|
|
}
|
|
|
|
changed(value) {
|
|
// we also store the trimmed value
|
|
let valueTrimmed = value
|
|
|
|
if (typeof valueTrimmed == 'string') {
|
|
valueTrimmed = value.trim()
|
|
}
|
|
|
|
if (this.props.data) {
|
|
this.props.data.set(this.props.name, valueTrimmed)
|
|
}
|
|
|
|
this.setState({
|
|
value: value,
|
|
valid: true,
|
|
})
|
|
}
|
|
|
|
render() {
|
|
let {
|
|
value,
|
|
valid,
|
|
errorMessage,
|
|
} = this.state
|
|
|
|
let props = Object.assign({}, this.props, {
|
|
id: this.getId(),
|
|
name: this.getName(),
|
|
onChange: this.change,
|
|
})
|
|
|
|
delete props.value
|
|
delete props.className
|
|
|
|
return pug`
|
|
.control(
|
|
ref=this.inputRef
|
|
className=classnames({
|
|
valid: valid,
|
|
invalid: !!errorMessage,
|
|
}) + (this.props.className ? ' ' + this.props.className : '')
|
|
)
|
|
ControlComponent(
|
|
...props
|
|
value=value
|
|
valid=valid
|
|
errorMessage=errorMessage
|
|
)
|
|
`
|
|
}
|
|
}
|
|
}
|
|
|