useForm.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. import { computed, shallowRef } from 'vue'
  3. import type { MutationSendError } from '#shared/types/error.ts'
  4. import type { ObjectLike } from '#shared/types/utils.ts'
  5. import { setErrors } from './utils.ts'
  6. import type {
  7. FormRef,
  8. FormResetOptions,
  9. FormFieldValue,
  10. FormValues,
  11. FormSchemaField,
  12. } from './types.ts'
  13. import type { FormKitNode } from '@formkit/core'
  14. import type { ShallowRef, Ref } from 'vue'
  15. export const useForm = <T = FormValues>(formRef?: Ref<FormRef | undefined>) => {
  16. const form: ShallowRef<FormRef | undefined> = formRef || shallowRef()
  17. const node = computed(() => form.value?.formNode)
  18. const context = computed(() => node.value?.context)
  19. const nodeValues = computed<FormValues>(() => context.value?.value)
  20. const state = computed(() => context.value?.state)
  21. const isValid = computed(() => !!state.value?.valid)
  22. const isDirty = computed(() => !!state.value?.dirty)
  23. const isComplete = computed(() => !!state.value?.complete)
  24. const isSubmitted = computed(() => !!state.value?.submitted)
  25. const isDisabled = computed(() => {
  26. return !!context.value?.disabled || !!state.value?.formUpdaterProcessing
  27. })
  28. const formNodeId = computed(() => {
  29. return context.value?.id
  30. })
  31. /**
  32. * User can submit form, if it is:
  33. * - not disabled
  34. * - has dirty values
  35. * After submit, the values should be reset to new values, so "dirty" state can update.
  36. * It is done automaticaly, if async `@submit` event is used. Otherwise, `formReset` should be used.
  37. */
  38. const canSubmit = computed(() => {
  39. if (isDisabled.value) return false
  40. return isDirty.value
  41. })
  42. const formReset = (
  43. values?: FormValues,
  44. object?: ObjectLike,
  45. options?: FormResetOptions,
  46. ) => {
  47. form.value?.resetForm(values, object, options)
  48. }
  49. const formGroupReset = (
  50. groupNode: FormKitNode,
  51. values?: FormValues,
  52. object?: ObjectLike,
  53. options?: FormResetOptions,
  54. ) => {
  55. form.value?.resetForm(values, object, options, groupNode)
  56. }
  57. const formSubmit = () => {
  58. node.value?.submit()
  59. }
  60. const waitForFormSettled = () => {
  61. return new Promise<FormKitNode>((resolve) => {
  62. const interval = setInterval(() => {
  63. if (!node.value) return
  64. const formNode = node.value
  65. clearInterval(interval)
  66. formNode.settled.then(() => resolve(formNode))
  67. })
  68. })
  69. }
  70. const onChangedField = (
  71. name: string,
  72. callback: (
  73. newValue: FormFieldValue,
  74. oldValue: FormFieldValue,
  75. node: FormKitNode,
  76. ) => void,
  77. ) => {
  78. const registerChangeEvent = (node: FormKitNode) => {
  79. node.on(`changed:${name}`, ({ payload }) => {
  80. callback(payload.newValue, payload.oldValue, payload.fieldNode)
  81. })
  82. }
  83. if (node.value) {
  84. registerChangeEvent(node.value)
  85. } else {
  86. waitForFormSettled().then((node) => {
  87. registerChangeEvent(node)
  88. })
  89. }
  90. }
  91. const updateFieldValues = (fieldValues: Record<string, FormFieldValue>) => {
  92. const changedFieldValues: Record<
  93. string,
  94. Pick<FormSchemaField, 'value'>
  95. > = {}
  96. Object.keys(fieldValues).forEach((fieldName) => {
  97. changedFieldValues[fieldName] = {
  98. value: fieldValues[fieldName],
  99. }
  100. })
  101. form.value?.updateChangedFields(changedFieldValues)
  102. }
  103. const values = computed<T>(() => {
  104. return (form.value?.values || {}) as T
  105. })
  106. const formSetErrors = (errors: MutationSendError) => {
  107. if (!node.value) return
  108. setErrors(node.value, errors)
  109. }
  110. return {
  111. form,
  112. node,
  113. context,
  114. nodeValues,
  115. values,
  116. state,
  117. isValid,
  118. isDirty,
  119. isComplete,
  120. isSubmitted,
  121. isDisabled,
  122. formNodeId,
  123. canSubmit,
  124. formSetErrors,
  125. formReset,
  126. formGroupReset,
  127. formSubmit,
  128. waitForFormSettled,
  129. updateFieldValues,
  130. onChangedField,
  131. }
  132. }