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 Normal View History

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'
2020-10-09 22:58:28 +03:00
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;
}
2020-05-22 14:22:56 +03:00
2020-10-09 22:58:28 +03:00
// noinspection JSUnusedGlobalSymbols
2020-05-22 14:22:56 +03:00
/**
* The base formulario library.
*/
export default class Formulario {
public options: FormularioOptions
public defaults: FormularioOptions
public registry: Map<string, FormularioForm>
public idRegistry: { [name: string]: number }
2020-05-22 14:22:56 +03:00
/**
* Instantiate our base options.
*/
constructor () {
this.options = {}
this.defaults = {
components: {
FormularioForm,
FormularioInput,
FormularioGrouping,
2020-05-22 14:22:56 +03:00
},
library,
rules,
mimes,
locale: false,
uploader: fauxUploader,
uploadUrl: false,
fileUrlKey: 'url',
uploadJustCompleteDuration: 1000,
errorHandler: (error: any) => error,
2020-10-09 22:58:28 +03:00
plugins: [RuleValidationMessages],
validationMessages: {},
2020-05-22 14:22:56 +03:00
idPrefix: 'formulario-'
}
this.registry = new Map()
this.idRegistry = {}
}
/**
* Install vue formulario, and register its components.
*/
install (Vue: VueConstructor, options?: FormularioOptions) {
2020-05-22 14:22:56 +03:00
Vue.prototype.$formulario = this
this.options = this.defaults
let plugins = this.defaults.plugins as any[]
2020-05-22 14:22:56 +03:00
if (options && Array.isArray(options.plugins) && options.plugins.length) {
2020-10-09 22:58:28 +03:00
plugins = plugins.concat(options.plugins)
2020-05-22 14:22:56 +03:00
}
plugins.forEach(plugin => (typeof plugin === 'function') ? plugin(this) : null)
this.extend(options || {})
2020-10-09 22:58:28 +03:00
for (const componentName in this.options.components) {
if (Object.prototype.hasOwnProperty.call(this.options.components, componentName)) {
Vue.component(componentName, this.options.components[componentName])
}
2020-05-22 14:22:56 +03:00
}
}
/**
* 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
2020-05-22 14:22:56 +03:00
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)) {
2020-05-22 14:22:56 +03:00
this.idRegistry[pathPrefix] = 0
}
return `${options.idPrefix}${pathPrefix}-${++this.idRegistry[pathPrefix]}`
2020-05-22 14:22:56 +03:00
}
/**
* Given a set of options, apply them to the pre-existing options.
*/
extend (extendWith: FormularioOptions) {
2020-05-22 14:22:56 +03:00
if (typeof extendWith === 'object') {
this.options = merge(this.options as FormularioOptions, extendWith)
2020-05-22 14:22:56 +03:00
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> = {}) {
2020-05-22 14:22:56 +03:00
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)
}
2020-05-22 14:22:56 +03:00
}
/**
* Given an instance of a FormularioForm register it.
*/
register (form: FormularioForm) {
// @ts-ignore
2020-05-22 14:22:56 +03:00
if (form.$options.name === 'FormularioForm' && form.name) {
// @ts-ignore
2020-05-22 14:22:56 +03:00
this.registry.set(form.name, form)
}
}
/**
* Given an instance of a form, remove it from the registry.
*/
deregister (form: FormularioForm) {
2020-05-22 14:22:56 +03:00
if (
form.$options.name === 'FormularioForm' &&
// @ts-ignore
2020-05-22 14:22:56 +03:00
form.name &&
// @ts-ignore
this.registry.has(form.name as string)
2020-05-22 14:22:56 +03:00
) {
// @ts-ignore
this.registry.delete(form.name as string)
2020-05-22 14:22:56 +03:00
}
}
/**
* 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)
2020-05-22 14:22:56 +03:00
if (formName && this.registry.has(formName)) {
const form = this.registry.get(formName) as FormularioForm
// @ts-ignore
form.applyErrors({
2020-05-22 14:22:56 +03:00
formErrors: arrayify(e.formErrors),
inputErrors: e.inputErrors || {}
})
}
return e
}
/**
* Reset a form.
*/
reset (formName: string, initialValue: Object = {}) {
2020-05-22 14:22:56 +03:00
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
2020-05-22 14:22:56 +03:00
form.hideErrors(formName)
// @ts-ignore
2020-05-22 14:22:56 +03:00
form.namedErrors = []
// @ts-ignore
2020-05-22 14:22:56 +03:00
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
2020-05-22 14:22:56 +03:00
form.setValues({ ...values })
}
}
/**
* Get the file uploader.
*/
getUploader () {
return this.options.uploader || false
}
/**
* Get the global upload url.
*/
getUploadUrl (): string | boolean {
2020-05-22 14:22:56 +03:00
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)
2020-05-22 14:22:56 +03:00
}
}