You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

form.tsx 2.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import { useAppForm } from '@/app/components/base/form'
  2. import BaseField from '@/app/components/base/form/form-scenarios/base/field'
  3. import type { BaseConfiguration } from '@/app/components/base/form/form-scenarios/base/types'
  4. import Toast from '@/app/components/base/toast'
  5. import { useCallback, useImperativeHandle } from 'react'
  6. import type { ZodSchema } from 'zod'
  7. import Header from './header'
  8. type OptionsProps = {
  9. initialData: Record<string, any>
  10. configurations: BaseConfiguration[]
  11. schema: ZodSchema
  12. onSubmit: (data: Record<string, any>) => void
  13. onPreview: () => void
  14. ref: React.RefObject<any>
  15. isRunning: boolean
  16. }
  17. const Form = ({
  18. initialData,
  19. configurations,
  20. schema,
  21. onSubmit,
  22. onPreview,
  23. ref,
  24. isRunning,
  25. }: OptionsProps) => {
  26. const form = useAppForm({
  27. defaultValues: initialData,
  28. validators: {
  29. onSubmit: ({ value }) => {
  30. const result = schema.safeParse(value)
  31. if (!result.success) {
  32. const issues = result.error.issues
  33. const firstIssue = issues[0]
  34. const errorMessage = `"${firstIssue.path.join('.')}" ${firstIssue.message}`
  35. Toast.notify({
  36. type: 'error',
  37. message: errorMessage,
  38. })
  39. return errorMessage
  40. }
  41. return undefined
  42. },
  43. },
  44. onSubmit: ({ value }) => {
  45. onSubmit(value)
  46. },
  47. })
  48. useImperativeHandle(ref, () => {
  49. return {
  50. submit: () => {
  51. form.handleSubmit()
  52. },
  53. }
  54. }, [form])
  55. const handleReset = useCallback(() => {
  56. form.reset()
  57. }, [form])
  58. return (
  59. <form
  60. className='flex w-full flex-col rounded-lg border border-components-panel-border bg-components-panel-bg'
  61. onSubmit={(e) => {
  62. e.preventDefault()
  63. e.stopPropagation()
  64. form.handleSubmit()
  65. }}
  66. >
  67. <form.Subscribe
  68. selector={state => state.isDirty}
  69. children={isDirty => (
  70. <Header
  71. onReset={handleReset}
  72. resetDisabled={!isDirty}
  73. onPreview={onPreview}
  74. previewDisabled={isRunning}
  75. />
  76. )}
  77. />
  78. <div className='flex flex-col gap-3 border-t border-divider-subtle px-4 py-3'>
  79. {configurations.map((config, index) => {
  80. const FieldComponent = BaseField({
  81. initialData,
  82. config,
  83. })
  84. return <FieldComponent key={index} form={form} />
  85. })}
  86. </div>
  87. </form>
  88. )
  89. }
  90. export default Form