1
0
mirror of synced 2024-11-23 05:46:05 +03:00
vue-formulario/src/Formulario.ts

245 lines
7.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { VueConstructor } from 'vue'
import library from '@/libs/library'
import rules from '@/validation/rules'
import mimes from '@/libs/mimes'
import FileUpload from '@/FileUpload'
import RuleValidationMessages from '@/RuleValidationMessages'
import { arrayify, has } from '@/libs/utils'
import fauxUploader from '@/libs/faux-uploader'
import merge from '@/utils/merge'
import FormularioForm from '@/FormularioForm.vue'
import FormularioInput from '@/FormularioInput.vue'
import FormularioGrouping from '@/FormularioGrouping.vue'
import { ValidationContext } from '@/validation/types'
interface ErrorHandler {
(error: any, formName?: string): any;
}
interface FormularioOptions {
components?: { [name: string]: VueConstructor };
plugins?: any[];
library?: any;
rules?: any;
mimes?: any;
locale?: any;
uploader?: any;
uploadUrl?: any;
fileUrlKey?: any;
errorHandler?: ErrorHandler;
uploadJustCompleteDuration?: any;
validationMessages?: any;
idPrefix?: string;
}
// noinspection JSUnusedGlobalSymbols
/**
* The base formulario library.
*/
export default class Formulario {
public options: FormularioOptions
public defaults: FormularioOptions
public registry: Map<string, FormularioForm>
public idRegistry: { [name: string]: number }
/**
* Instantiate our base options.
*/
constructor () {
this.options = {}
this.defaults = {
components: {
FormularioForm,
FormularioInput,
FormularioGrouping,
},
library,
rules,
mimes,
locale: false,
uploader: fauxUploader,
uploadUrl: false,
fileUrlKey: 'url',
uploadJustCompleteDuration: 1000,
errorHandler: (error: any) => error,
plugins: [RuleValidationMessages],
validationMessages: {},
idPrefix: 'formulario-'
}
this.registry = new Map()
this.idRegistry = {}
}
/**
* Install vue formulario, and register its components.
*/
install (Vue: VueConstructor, options?: FormularioOptions) {
Vue.prototype.$formulario = this
this.options = this.defaults
let plugins = this.defaults.plugins as any[]
if (options && Array.isArray(options.plugins) && options.plugins.length) {
plugins = plugins.concat(options.plugins)
}
plugins.forEach(plugin => (typeof plugin === 'function') ? plugin(this) : null)
this.extend(options || {})
for (const componentName in this.options.components) {
if (Object.prototype.hasOwnProperty.call(this.options.components, componentName)) {
Vue.component(componentName, this.options.components[componentName])
}
}
}
/**
* Produce a deterministically generated id based on the sequence by which it
* was requested. This should be *theoretically* the same SSR as client side.
* However, SSR and deterministic ids can be very challenging, so this
* implementation is open to community review.
*/
nextId (vm: Vue) {
const options = this.options as FormularioOptions
const path = vm.$route && vm.$route.path ? vm.$route.path : false
const pathPrefix = path ? vm.$route.path.replace(/[/\\.\s]/g, '-') : 'global'
if (!has(this.idRegistry, pathPrefix)) {
this.idRegistry[pathPrefix] = 0
}
return `${options.idPrefix}${pathPrefix}-${++this.idRegistry[pathPrefix]}`
}
/**
* Given a set of options, apply them to the pre-existing options.
*/
extend (extendWith: FormularioOptions) {
if (typeof extendWith === 'object') {
this.options = merge(this.options as FormularioOptions, extendWith)
return this
}
throw new Error(`VueFormulario extend() should be passed an object (was ${typeof extendWith})`)
}
/**
* Get validation rules by merging any passed in with global rules.
*/
rules (rules: Record<string, any> = {}) {
return { ...this.options.rules, ...rules }
}
/**
* Get the validation message for a particular error.
*/
validationMessage (rule: string, context: ValidationContext, vm: Vue) {
if (has(this.options.validationMessages, rule)) {
return this.options.validationMessages[rule](vm, context)
} else {
return this.options.validationMessages.default(vm, context)
}
}
/**
* Given an instance of a FormularioForm register it.
*/
register (form: FormularioForm) {
// @ts-ignore
if (form.$options.name === 'FormularioForm' && form.name) {
// @ts-ignore
this.registry.set(form.name, form)
}
}
/**
* Given an instance of a form, remove it from the registry.
*/
deregister (form: FormularioForm) {
if (
form.$options.name === 'FormularioForm' &&
// @ts-ignore
form.name &&
// @ts-ignore
this.registry.has(form.name as string)
) {
// @ts-ignore
this.registry.delete(form.name as string)
}
}
/**
* Given an array, this function will attempt to make sense of the given error
* and hydrate a form with the resulting errors.
*/
handle (error: any, formName: string, skip: boolean = false) {
// @ts-ignore
const e = skip ? error : this.options.errorHandler(error, formName)
if (formName && this.registry.has(formName)) {
const form = this.registry.get(formName) as FormularioForm
// @ts-ignore
form.applyErrors({
formErrors: arrayify(e.formErrors),
inputErrors: e.inputErrors || {}
})
}
return e
}
/**
* Reset a form.
*/
reset (formName: string, initialValue: Object = {}) {
this.resetValidation(formName)
this.setValues(formName, initialValue)
}
/**
* Reset the form's validation messages.
*/
resetValidation (formName: string) {
const form = this.registry.get(formName) as FormularioForm
// @ts-ignore
form.hideErrors(formName)
// @ts-ignore
form.namedErrors = []
// @ts-ignore
form.namedFieldErrors = {}
}
/**
* Set the form values.
*/
setValues (formName: string, values?: Record<string, any>) {
if (values) {
const form = this.registry.get(formName) as FormularioForm
// @ts-ignore
form.setValues({ ...values })
}
}
/**
* Get the file uploader.
*/
getUploader () {
return this.options.uploader || false
}
/**
* Get the global upload url.
*/
getUploadUrl (): string | boolean {
return this.options.uploadUrl || false
}
/**
* When re-hydrating a file uploader with an array, get the sub-object key to
* access the url of the file. Usually this is just "url".
*/
getFileUrlKey () {
return this.options.fileUrlKey || 'url'
}
/**
* Create a new instance of an upload.
*/
createUpload (data: DataTransfer, context: Record<string, any>) {
return new FileUpload(data, context, this.options)
}
}