262 lines
6.7 KiB
Vue
262 lines
6.7 KiB
Vue
import set from 'lodash/set'
|
|
import createFormField, { isFormField } from './createFormField'
|
|
import {
|
|
flattenFields,
|
|
getErrorStrs,
|
|
startsWith,
|
|
} from './utils'
|
|
|
|
function partOf (a, b) {
|
|
return b.indexOf(a) === 0 && ['.', '['].indexOf(b[a.length]) !== -1
|
|
}
|
|
|
|
class FieldsStore {
|
|
constructor (fields) {
|
|
this.fields = this.flattenFields(fields)
|
|
this.fieldsMeta = {}
|
|
}
|
|
|
|
updateFields (fields) {
|
|
this.fields = this.flattenFields(fields)
|
|
}
|
|
|
|
flattenFields (fields) {
|
|
return flattenFields(
|
|
fields,
|
|
(_, node) => isFormField(node),
|
|
'You must wrap field data with `createFormField`.'
|
|
)
|
|
}
|
|
|
|
flattenRegisteredFields (fields) {
|
|
const validFieldsName = this.getAllFieldsName()
|
|
return flattenFields(
|
|
fields,
|
|
path => validFieldsName.indexOf(path) >= 0,
|
|
'You cannot set field before registering it.'
|
|
)
|
|
}
|
|
|
|
setFieldsInitialValue = (initialValues) => {
|
|
const flattenedInitialValues = this.flattenRegisteredFields(initialValues)
|
|
const fieldsMeta = this.fieldsMeta
|
|
Object.keys(flattenedInitialValues).forEach(name => {
|
|
if (fieldsMeta[name]) {
|
|
this.setFieldMeta(name, {
|
|
...this.getFieldMeta(name),
|
|
initialValue: flattenedInitialValues[name],
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
setFields (fields) {
|
|
const fieldsMeta = this.fieldsMeta
|
|
const nowFields = {
|
|
...this.fields,
|
|
...fields,
|
|
}
|
|
const nowValues = {}
|
|
Object.keys(fieldsMeta)
|
|
.forEach((f) => { nowValues[f] = this.getValueFromFields(f, nowFields) })
|
|
Object.keys(nowValues).forEach((f) => {
|
|
const value = nowValues[f]
|
|
const fieldMeta = this.getFieldMeta(f)
|
|
if (fieldMeta && fieldMeta.normalize) {
|
|
const nowValue =
|
|
fieldMeta.normalize(value, this.getValueFromFields(f, this.fields), nowValues)
|
|
if (nowValue !== value) {
|
|
nowFields[f] = {
|
|
...nowFields[f],
|
|
value: nowValue,
|
|
}
|
|
}
|
|
}
|
|
})
|
|
this.fields = nowFields
|
|
}
|
|
|
|
resetFields (ns) {
|
|
const { fields } = this
|
|
const names = ns
|
|
? this.getValidFieldsFullName(ns)
|
|
: this.getAllFieldsName()
|
|
return names.reduce((acc, name) => {
|
|
const field = fields[name]
|
|
if (field && 'value' in field) {
|
|
acc[name] = {}
|
|
}
|
|
return acc
|
|
}, {})
|
|
}
|
|
|
|
setFieldMeta (name, meta) {
|
|
this.fieldsMeta[name] = meta
|
|
}
|
|
|
|
getFieldMeta (name) {
|
|
this.fieldsMeta[name] = this.fieldsMeta[name] || {}
|
|
return this.fieldsMeta[name]
|
|
}
|
|
|
|
getValueFromFields (name, fields) {
|
|
const field = fields[name]
|
|
if (field && 'value' in field) {
|
|
return field.value
|
|
}
|
|
const fieldMeta = this.getFieldMeta(name)
|
|
return fieldMeta && fieldMeta.initialValue
|
|
}
|
|
|
|
getAllValues = () => {
|
|
const { fieldsMeta, fields } = this
|
|
return Object.keys(fieldsMeta)
|
|
.reduce((acc, name) => set(acc, name, this.getValueFromFields(name, fields)), {})
|
|
}
|
|
|
|
getValidFieldsName () {
|
|
const { fieldsMeta } = this
|
|
return fieldsMeta
|
|
? Object.keys(fieldsMeta).filter(name => !this.getFieldMeta(name).hidden)
|
|
: []
|
|
}
|
|
|
|
getAllFieldsName () {
|
|
const { fieldsMeta } = this
|
|
return fieldsMeta ? Object.keys(fieldsMeta) : []
|
|
}
|
|
|
|
getValidFieldsFullName (maybePartialName) {
|
|
const maybePartialNames = Array.isArray(maybePartialName)
|
|
? maybePartialName : [maybePartialName]
|
|
return this.getValidFieldsName()
|
|
.filter(fullName => maybePartialNames.some(partialName => (
|
|
fullName === partialName || (
|
|
startsWith(fullName, partialName) &&
|
|
['.', '['].indexOf(fullName[partialName.length]) >= 0
|
|
)
|
|
)))
|
|
}
|
|
|
|
getFieldValuePropValue (fieldMeta) {
|
|
const { name, getValueProps, valuePropName } = fieldMeta
|
|
const field = this.getField(name)
|
|
const fieldValue = 'value' in field
|
|
? field.value : fieldMeta.initialValue
|
|
if (getValueProps) {
|
|
return getValueProps(fieldValue)
|
|
}
|
|
return { [valuePropName]: fieldValue }
|
|
}
|
|
|
|
getField (name) {
|
|
return {
|
|
...this.fields[name],
|
|
name,
|
|
}
|
|
}
|
|
|
|
getNotCollectedFields () {
|
|
return this.getValidFieldsName()
|
|
.filter(name => !this.fields[name])
|
|
.map(name => ({
|
|
name,
|
|
dirty: false,
|
|
value: this.getFieldMeta(name).initialValue,
|
|
}))
|
|
.reduce((acc, field) => set(acc, field.name, createFormField(field)), {})
|
|
}
|
|
|
|
getNestedAllFields () {
|
|
return Object.keys(this.fields)
|
|
.reduce(
|
|
(acc, name) => set(acc, name, createFormField(this.fields[name])),
|
|
this.getNotCollectedFields()
|
|
)
|
|
}
|
|
|
|
getFieldMember (name, member) {
|
|
return this.getField(name)[member]
|
|
}
|
|
|
|
getNestedFields (names, getter) {
|
|
const fields = names || this.getValidFieldsName()
|
|
return fields.reduce((acc, f) => set(acc, f, getter(f)), {})
|
|
}
|
|
|
|
getNestedField (name, getter) {
|
|
const fullNames = this.getValidFieldsFullName(name)
|
|
if (
|
|
fullNames.length === 0 || // Not registered
|
|
(fullNames.length === 1 && fullNames[0] === name) // Name already is full name.
|
|
) {
|
|
return getter(name)
|
|
}
|
|
const isArrayValue = fullNames[0][name.length] === '['
|
|
const suffixNameStartIndex = isArrayValue ? name.length : name.length + 1
|
|
return fullNames
|
|
.reduce(
|
|
(acc, fullName) => set(
|
|
acc,
|
|
fullName.slice(suffixNameStartIndex),
|
|
getter(fullName)
|
|
),
|
|
isArrayValue ? [] : {}
|
|
)
|
|
}
|
|
|
|
getFieldsValue = (names) => {
|
|
return this.getNestedFields(names, this.getFieldValue)
|
|
}
|
|
|
|
getFieldValue = (name) => {
|
|
const { fields } = this
|
|
return this.getNestedField(name, (fullName) => this.getValueFromFields(fullName, fields))
|
|
}
|
|
|
|
getFieldsError = (names) => {
|
|
return this.getNestedFields(names, this.getFieldError)
|
|
}
|
|
|
|
getFieldError = (name) => {
|
|
return this.getNestedField(
|
|
name,
|
|
(fullName) => getErrorStrs(this.getFieldMember(fullName, 'errors'))
|
|
)
|
|
}
|
|
|
|
isFieldValidating = (name) => {
|
|
return this.getFieldMember(name, 'validating')
|
|
}
|
|
|
|
isFieldsValidating = (ns) => {
|
|
const names = ns || this.getValidFieldsName()
|
|
return names.some((n) => this.isFieldValidating(n))
|
|
}
|
|
|
|
isFieldTouched = (name) => {
|
|
return this.getFieldMember(name, 'touched')
|
|
}
|
|
|
|
isFieldsTouched = (ns) => {
|
|
const names = ns || this.getValidFieldsName()
|
|
return names.some((n) => this.isFieldTouched(n))
|
|
}
|
|
|
|
// @private
|
|
// BG: `a` and `a.b` cannot be use in the same form
|
|
isValidNestedFieldName (name) {
|
|
const names = this.getAllFieldsName()
|
|
return names.every(n => !partOf(n, name) && !partOf(name, n))
|
|
}
|
|
|
|
clearField (name) {
|
|
delete this.fields[name]
|
|
delete this.fieldsMeta[name]
|
|
}
|
|
}
|
|
|
|
export default function createFieldsStore (fields) {
|
|
return new FieldsStore(fields)
|
|
}
|