useForm.ts 4.2 KB

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