208 lines
5.3 KiB
JavaScript
208 lines
5.3 KiB
JavaScript
import isEqual from 'lodash.isequal'
|
|
import { reactive, watch } from 'vue'
|
|
import cloneDeep from 'lodash.clonedeep'
|
|
import { Inertia } from '@inertiajs/inertia'
|
|
|
|
export default function useForm(...args) {
|
|
const rememberKey = typeof args[0] === 'string' ? args[0] : null
|
|
const data = (typeof args[0] === 'string' ? args[1] : args[0]) || {}
|
|
const restored = rememberKey ? Inertia.restore(rememberKey) : null
|
|
let defaults = cloneDeep(data)
|
|
let cancelToken = null
|
|
let recentlySuccessfulTimeoutId = null
|
|
let transform = data => data
|
|
|
|
let form = reactive({
|
|
...restored ? restored.data : data,
|
|
isDirty: false,
|
|
errors: restored ? restored.errors : {},
|
|
hasErrors: false,
|
|
processing: false,
|
|
progress: null,
|
|
wasSuccessful: false,
|
|
recentlySuccessful: false,
|
|
data() {
|
|
return Object
|
|
.keys(data)
|
|
.reduce((carry, key) => {
|
|
carry[key] = this[key]
|
|
return carry
|
|
}, {})
|
|
},
|
|
transform(callback) {
|
|
transform = callback
|
|
|
|
return this
|
|
},
|
|
defaults(key, value) {
|
|
if (typeof key === 'undefined') {
|
|
defaults = this.data()
|
|
} else {
|
|
defaults = Object.assign(
|
|
{},
|
|
cloneDeep(defaults),
|
|
value ? ({ [key]: value }) : key,
|
|
)
|
|
}
|
|
|
|
return this
|
|
},
|
|
reset(...fields) {
|
|
let clonedDefaults = cloneDeep(defaults)
|
|
if (fields.length === 0) {
|
|
Object.assign(this, clonedDefaults)
|
|
} else {
|
|
Object.assign(
|
|
this,
|
|
Object
|
|
.keys(clonedDefaults)
|
|
.filter(key => fields.includes(key))
|
|
.reduce((carry, key) => {
|
|
carry[key] = clonedDefaults[key]
|
|
return carry
|
|
}, {}),
|
|
)
|
|
}
|
|
|
|
return this
|
|
},
|
|
setError(key, value) {
|
|
Object.assign(this.errors, (value ? { [key]: value } : key))
|
|
|
|
this.hasErrors = Object.keys(this.errors).length > 0
|
|
|
|
return this
|
|
},
|
|
clearErrors(...fields) {
|
|
this.errors = Object
|
|
.keys(this.errors)
|
|
.reduce((carry, field) => ({
|
|
...carry,
|
|
...(fields.length > 0 && !fields.includes(field) ? { [field] : this.errors[field] } : {}),
|
|
}), {})
|
|
|
|
this.hasErrors = Object.keys(this.errors).length > 0
|
|
|
|
return this
|
|
},
|
|
submit(method, url, options = {}) {
|
|
const data = transform(this.data())
|
|
const _options = {
|
|
...options,
|
|
onCancelToken: (token) => {
|
|
cancelToken = token
|
|
|
|
if (options.onCancelToken) {
|
|
return options.onCancelToken(token)
|
|
}
|
|
},
|
|
onBefore: visit => {
|
|
this.wasSuccessful = false
|
|
this.recentlySuccessful = false
|
|
clearTimeout(recentlySuccessfulTimeoutId)
|
|
|
|
if (options.onBefore) {
|
|
return options.onBefore(visit)
|
|
}
|
|
},
|
|
onStart: visit => {
|
|
this.processing = true
|
|
|
|
if (options.onStart) {
|
|
return options.onStart(visit)
|
|
}
|
|
},
|
|
onProgress: event => {
|
|
this.progress = event
|
|
|
|
if (options.onProgress) {
|
|
return options.onProgress(event)
|
|
}
|
|
},
|
|
onSuccess: async page => {
|
|
this.processing = false
|
|
this.progress = null
|
|
this.clearErrors()
|
|
this.wasSuccessful = true
|
|
this.recentlySuccessful = true
|
|
recentlySuccessfulTimeoutId = setTimeout(() => this.recentlySuccessful = false, 2000)
|
|
|
|
const onSuccess = options.onSuccess ? await options.onSuccess(page) : null
|
|
defaults = cloneDeep(this.data())
|
|
this.isDirty = false
|
|
return onSuccess
|
|
},
|
|
onError: errors => {
|
|
this.processing = false
|
|
this.progress = null
|
|
this.clearErrors().setError(errors)
|
|
|
|
if (options.onError) {
|
|
return options.onError(errors)
|
|
}
|
|
},
|
|
onCancel: () => {
|
|
this.processing = false
|
|
this.progress = null
|
|
|
|
if (options.onCancel) {
|
|
return options.onCancel()
|
|
}
|
|
},
|
|
onFinish: () => {
|
|
this.processing = false
|
|
this.progress = null
|
|
cancelToken = null
|
|
|
|
if (options.onFinish) {
|
|
return options.onFinish()
|
|
}
|
|
},
|
|
}
|
|
|
|
if (method === 'delete') {
|
|
Inertia.delete(url, { ..._options, data })
|
|
} else {
|
|
Inertia[method](url, data, _options)
|
|
}
|
|
},
|
|
get(url, options) {
|
|
this.submit('get', url, options)
|
|
},
|
|
post(url, options) {
|
|
this.submit('post', url, options)
|
|
},
|
|
put(url, options) {
|
|
this.submit('put', url, options)
|
|
},
|
|
patch(url, options) {
|
|
this.submit('patch', url, options)
|
|
},
|
|
delete(url, options) {
|
|
this.submit('delete', url, options)
|
|
},
|
|
cancel() {
|
|
if (cancelToken) {
|
|
cancelToken.cancel()
|
|
}
|
|
},
|
|
__rememberable: rememberKey === null,
|
|
__remember() {
|
|
return { data: this.data(), errors: this.errors }
|
|
},
|
|
__restore(restored) {
|
|
Object.assign(this, restored.data)
|
|
this.setError(restored.errors)
|
|
},
|
|
})
|
|
|
|
watch(form, newValue => {
|
|
form.isDirty = !isEqual(form.data(), defaults)
|
|
if (rememberKey) {
|
|
Inertia.remember(cloneDeep(newValue.__remember()), rememberKey)
|
|
}
|
|
}, { immediate: true, deep: true })
|
|
|
|
return form
|
|
}
|