mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
feat: add useForm composable
This commit is contained in:
parent
3054c3ec5c
commit
85b4980bee
3 changed files with 114 additions and 3 deletions
|
@ -1,6 +1,17 @@
|
|||
import type { BaseValidation } from '@vuelidate/core'
|
||||
import type { InjectionKey, MaybeRefOrGetter } from 'vue'
|
||||
import { inject, provide, toValue } from 'vue'
|
||||
import type {
|
||||
BaseValidation,
|
||||
ServerErrors,
|
||||
ValidationArgs,
|
||||
ValidationRuleCollection,
|
||||
} from '@vuelidate/core'
|
||||
import useVuelidate from '@vuelidate/core'
|
||||
import type { InjectionKey, MaybeRefOrGetter, Ref } from 'vue'
|
||||
import { inject, provide, reactive, toValue } from 'vue'
|
||||
import { computedWithControl } from '@vueuse/core'
|
||||
|
||||
import { APIBadRequestError, type APIError } from '@/api/errors'
|
||||
import type { Obj } from '@/types/commons'
|
||||
import type { FormField, FormFieldDict } from '@/types/form'
|
||||
|
||||
export const clearServerErrorsSymbol = Symbol() as InjectionKey<
|
||||
(key?: string) => void
|
||||
|
@ -30,3 +41,90 @@ export function useTouch(
|
|||
|
||||
return touch
|
||||
}
|
||||
|
||||
export function useForm<
|
||||
MV extends Obj,
|
||||
FFD extends FormFieldDict<MV> = FormFieldDict<MV>,
|
||||
>(form: Ref<MV>, fields: MaybeRefOrGetter<FFD>) {
|
||||
const serverErrors = reactive<ServerErrors>({})
|
||||
const validByDefault: ValidationRuleCollection = { true: () => true }
|
||||
const rules = computedWithControl(
|
||||
() => toValue(fields),
|
||||
() => {
|
||||
const fs = toValue(fields)
|
||||
const validations = Object.keys(form.value).map((key: keyof MV) => [
|
||||
key,
|
||||
(fs[key] as FormField).rules ?? validByDefault,
|
||||
])
|
||||
const rules: ValidationArgs<MV> = Object.fromEntries(validations)
|
||||
return {
|
||||
// create a fake validation rule for global state to be able to add $externalResult errors to it
|
||||
global: { true: () => true },
|
||||
form: rules,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const v = useVuelidate(
|
||||
rules,
|
||||
{ form, global: null },
|
||||
{ $externalResults: serverErrors },
|
||||
)
|
||||
|
||||
function onErrorFn(err: APIError, errorMessage?: string) {
|
||||
if (!(err instanceof APIBadRequestError)) throw err
|
||||
if (errorMessage || !err.data.name) {
|
||||
serverErrors.global = [errorMessage || err.message]
|
||||
} else {
|
||||
deepSetErrors(
|
||||
serverErrors,
|
||||
[err.message],
|
||||
'form',
|
||||
...err.data.name.split('.'),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function onSubmit(
|
||||
fn: (onError: typeof onErrorFn, serverErrors: ServerErrors) => void,
|
||||
) {
|
||||
// FIXME add option to ask confirmation (with param text confirm)
|
||||
return async (e: SubmitEvent) => {
|
||||
e.preventDefault()
|
||||
if (!(await v.value.$validate())) return
|
||||
fn(onErrorFn, serverErrors)
|
||||
}
|
||||
}
|
||||
|
||||
provide(clearServerErrorsSymbol, (key?: string) => {
|
||||
const keys = key?.split('.')
|
||||
if (keys?.length) {
|
||||
deepSetErrors(serverErrors, [], ...keys)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
v,
|
||||
serverErrors,
|
||||
onSubmit,
|
||||
}
|
||||
}
|
||||
|
||||
export function deepSetErrors(
|
||||
serverErrors: ServerErrors,
|
||||
value: string[],
|
||||
...keys: string[]
|
||||
) {
|
||||
const [k, ...ks] = keys
|
||||
if (ks.length) {
|
||||
if (!(k in serverErrors) && !value.length) {
|
||||
serverErrors[k] = {}
|
||||
deepSetErrors(serverErrors[k] as ServerErrors, value, ...ks)
|
||||
} else if (k in serverErrors) {
|
||||
deepSetErrors(serverErrors[k] as ServerErrors, value, ...ks)
|
||||
}
|
||||
} else {
|
||||
if (!(k in serverErrors) && !value.length) return
|
||||
serverErrors[k] = value
|
||||
}
|
||||
}
|
||||
|
|
|
@ -162,3 +162,7 @@ export function omit<T extends Obj, K extends (keyof T)[]>(
|
|||
.map((key) => [key, obj[key]]),
|
||||
) as Omit<T, K[number]>
|
||||
}
|
||||
|
||||
export function asUnreffed<T>(value: T): UnwrapRef<T> {
|
||||
return value as UnwrapRef<T>
|
||||
}
|
||||
|
|
|
@ -275,3 +275,12 @@ export type FormFieldDict<T extends Obj = Obj> = {
|
|||
| FormFieldReadonly<AnyWritableComponents>
|
||||
| FormFieldDisplay<AnyDisplayComponents>
|
||||
}
|
||||
|
||||
export type FieldProps<
|
||||
C extends AnyItemComponents = 'InputItem',
|
||||
MV extends any = never,
|
||||
> = C extends AnyWritableComponents
|
||||
? FormField<C, MV> | FormFieldReadonly<C>
|
||||
: C extends AnyDisplayComponents
|
||||
? FormFieldDisplay<C>
|
||||
: never
|
||||
|
|
Loading…
Reference in a new issue