Repository URL to install this package:
|
Version:
3.7.1 ▾
|
import { action, observable, computed } from 'xmobx/mobx'
import { CommonState } from '../CommonState'
import { DEFAULT_VALIDATOR, DEFAULT_SERIALIZER, toType } from '../deps'
import {
AnyObj,
ValidatorFunction,
SerializerFunction,
Type,
InputStateStore,
} from './typings'
// @note @circular
import { FormState } from '../forms/FormState'
let identifierIndex = 0
export class InputState<InputProps = AnyObj> extends CommonState {
@observable
identifier: string = (identifierIndex += 1) + '-input'
/**
* @description this is a type that Plugins could extend
* @todo this should have a generic
*/
@observable
type: Type = 'text'
/** @description these are the props for the ObserverInput/plugin */
@observable.shallow
attributes: Partial<InputProps> = {}
@observable.ref
validator: ValidatorFunction = DEFAULT_VALIDATOR
@observable.ref
serializer: SerializerFunction = DEFAULT_SERIALIZER
/** @description parent form - we could use `context` instead */
@observable.ref
formState?: FormState
/** @description this is used for serializing */
@observable
propertyName: string = ''
/** @todo typings */
store: InputStateStore = observable.map()
/**
* using this to check if an input value has been changed
* this gets reset when the state is `reset`
*/
@computed
get isDirty(): boolean {
if (this.store.has('isDirty')) {
return this.store.get('isDirty')
} else {
return this.value !== '' && this.value !== undefined
}
}
@action
setIsDirty(isDirty: boolean): void {
this.store.set('isDirty', isDirty)
}
/** should not use this public-ally or at all with context */
@action
setFormState(formState: FormState) {
this.formState = formState
}
@action
setType(type: Type) {
/**
* @todo need to use `toType` too to detect which type
* @example this.type = toType(type)
*/
this.type = type
}
@action
setValidator(validator: ValidatorFunction) {
this.validator = validator
}
@action
setSerializer(serializer: SerializerFunction) {
this.serializer = serializer
}
/**
* @description this is computed only when changed
* @see https://mobx.js.org/refguide/computed-decorator.html
*
* @todo may want to trigger validation at different points
* @example will cover in stories
*
* @todo this seems flawed at its core
*
* 1. we could `set` isValid
* 2. we could `inherit` showing isValid
* since we would only show invalid inputs when the whole form shows them?
*
* @todo at very least, pass in just `this`...
* we would want just `this.value`...
*
* @event blur
* - in this case, we validate on blur
* - strategy: this will not work
*
* @event submit
* - in this case, we validate on submit only
* - strategy: this will not work
*
* @event onChange
* - in this case, we validate on every keystroke or change
* - strategy: this computed value works
*/
@computed
get isValid() {
if (this.store.has('isValid') === true) {
return this.store.get('isValid')
} else {
return this.validator(this.value) === true
}
}
@action
setIsValid(isValid: boolean) {
this.store.set('isValid', isValid)
}
/**
* @description this in the initial POC of minimalism was
* `return string if error, true if valid`
* @todo may want to change
*/
@computed
get errorText() {
return this.validator(this.value) as string
}
@action
setAttribute(key: string, value: any) {
this.attributes[key] = value
}
@action
setPropertyName(named: string) {
this.propertyName = named
}
@action
setIdentifier(identifier: string) {
this.identifier = identifier
}
toJSON() {
return this.serializer(this)
}
merge(obj: AnyObj) {
Object.keys(obj).forEach(key => {
const value = obj[key]
switch (key) {
case 'propertyName':
case 'name':
return this.setPropertyName(value)
case 'value':
return this.setValue(value)
case 'identifier':
return this.setIdentifier(value)
case 'label':
return this.setLabel(value)
case 'type':
return this.setType(value)
case 'validator':
return this.setValidator(value)
case 'serializer':
return this.setSerializer(value)
case 'formState':
return this.setFormState(value)
case 'isActive':
return this.setIsActive(value)
case 'isSelected':
return this.setIsSelected(value)
case 'isDisabled':
return this.setIsDisabled(value)
// isRequired
case 'attributes':
// or could curry the method
const setAttribute = (attribute: string) =>
this.setAttribute(attribute, value)
return Object.keys(value).forEach(setAttribute)
default:
return this.setAttribute(key, value)
}
})
}
static from(obj: AnyObj) {
const state = new InputState()
state.merge(obj)
return state
}
}