useForm.ts 4.2 KB

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