@sanity/form-toolkit 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,31 +1,33 @@
1
1
  import {LuTextCursorInput} from 'react-icons/lu'
2
2
  import {defineField, defineType} from 'sanity'
3
3
 
4
+ import {ValidationType} from '../components/validation-type'
4
5
  interface ValidationContextDocument {
5
6
  fields?: Array<{
6
7
  name: string
7
8
  type?: string
8
9
  }>
9
10
  }
11
+
10
12
  // Validation options by field type
11
- export const validationTypesByFieldType = {
12
- checkbox: ['minSelectedCount', 'maxSelectedCount', 'custom'],
13
- color: ['custom'],
14
- date: ['minDate', 'maxDate', 'custom'],
15
- 'datetime-local': ['minDate', 'maxDate', 'custom'],
16
- email: ['pattern', 'custom'],
17
- file: ['maxSize', 'fileType', 'custom'],
18
- hidden: ['custom'],
19
- number: ['min', 'max', 'custom'],
20
- // password: ['minLength', 'pattern', 'custom'],
21
- radio: ['custom'],
22
- range: ['min', 'max', 'step', 'custom'],
23
- select: ['custom'],
24
- tel: ['pattern', 'custom'],
25
- text: ['minLength', 'maxLength', 'pattern', 'custom'],
26
- textarea: ['minLength', 'maxLength', 'custom'],
27
- time: ['custom'],
28
- url: ['pattern', 'custom'],
13
+ export const validationTypesByFieldType: Record<string, string[]> = {
14
+ checkbox: ['minSelectedCount', 'maxSelectedCount'],
15
+ color: [],
16
+ date: ['minDate', 'maxDate'],
17
+ 'datetime-local': ['minDate', 'maxDate'],
18
+ email: ['pattern'],
19
+ file: ['maxSize', 'fileType'],
20
+ hidden: [],
21
+ number: ['min', 'max'],
22
+ // password: ['minLength', 'pattern'],
23
+ radio: [],
24
+ range: ['min', 'max', 'step'],
25
+ select: [],
26
+ tel: ['pattern'],
27
+ text: ['minLength', 'maxLength', 'pattern'],
28
+ textarea: ['minLength', 'maxLength'],
29
+ time: [],
30
+ url: ['pattern'],
29
31
  }
30
32
  export const formFieldType = defineType({
31
33
  name: 'formField',
@@ -114,40 +116,52 @@ export const formFieldType = defineType({
114
116
  type: 'boolean',
115
117
  initialValue: false,
116
118
  }),
117
- // defineField({
118
- // name: 'validation',
119
- // title: 'Validation Rules',
120
- // type: 'array',
121
- // of: [
122
- // {
123
- // type: 'object',
124
- // fields: [
125
- // defineField({
126
- // name: 'type',
127
- // title: 'Validation Type',
128
- // type: 'string',
129
-
130
- // hidden: ({parent}) => !parent?.type,
131
- // options: {
132
- // // TODO: I think this needs to be a custom input component?
133
- // // list: ({parent}) => (parent?.type ? validationTypesByFieldType[parent.type] : []),
134
- // list: [],
135
- // },
136
- // }),
137
- // defineField({
138
- // name: 'value',
139
- // title: 'Value',
140
- // type: 'string',
141
- // }),
142
- // defineField({
143
- // name: 'message',
144
- // title: 'Error Message',
145
- // type: 'string',
146
- // }),
147
- // ],
148
- // },
149
- // ],
150
- // }),
119
+ defineField({
120
+ name: 'validation',
121
+ title: 'Validation Rules',
122
+ type: 'array',
123
+ hidden: ({parent}) => {
124
+ if (!parent?.type) return true
125
+ const validationTypes = validationTypesByFieldType[parent.type]
126
+ return !validationTypes || validationTypes.length === 0
127
+ },
128
+ of: [
129
+ {
130
+ type: 'object',
131
+ fields: [
132
+ defineField({
133
+ name: 'type',
134
+ title: 'Validation Type',
135
+ type: 'string',
136
+ options: {
137
+ // TODO: I think this needs to be a custom input component?
138
+ // list: ({parent}) => (parent?.type ? validationTypesByFieldType[parent.type] : []),
139
+ list: [],
140
+ },
141
+ components: {
142
+ input: ValidationType,
143
+ },
144
+ }),
145
+ defineField({
146
+ name: 'value',
147
+ title: 'Value',
148
+ type: 'string',
149
+ }),
150
+ defineField({
151
+ name: 'message',
152
+ title: 'Error Message',
153
+ type: 'string',
154
+ }),
155
+ ],
156
+ preview: {
157
+ select: {
158
+ title: 'type',
159
+ subtitle: 'value',
160
+ },
161
+ },
162
+ },
163
+ ],
164
+ }),
151
165
  defineField({
152
166
  name: 'choices',
153
167
  title: 'Choices',
@@ -1,55 +1,52 @@
1
1
  import {FaWpforms} from 'react-icons/fa'
2
- import {defineField, defineType} from 'sanity'
2
+ import {defineField, defineType, type SchemaTypeDefinition} from 'sanity'
3
3
 
4
- export const formType = defineType({
5
- name: 'form',
6
- title: 'Form',
7
- type: 'document',
8
- icon: FaWpforms,
9
- fields: [
10
- defineField({
11
- name: 'title',
12
- title: 'Form Title',
13
- type: 'string',
14
- description: 'Internal title for the form',
15
- validation: (Rule) => Rule.required(),
16
- }),
17
- defineField({
18
- name: 'id',
19
- title: 'Form ID',
20
- type: 'slug',
21
- options: {
22
- source: 'title',
23
- },
24
- // validation: (Rule) => Rule.required(),
25
- }),
26
- defineField({
27
- name: 'fields',
28
- title: 'Form Fields',
29
- type: 'array',
30
- of: [{type: 'formField'}],
31
- }),
32
- defineField({
33
- name: 'submitButton',
34
- title: 'Submit Button',
35
- type: 'object',
36
- fields: [
37
- defineField({
38
- name: 'text',
39
- title: 'Button Text',
40
- type: 'string',
41
- initialValue: 'Submit',
42
- }),
43
- // defineField({
44
- // name: 'position',
45
- // title: 'Button Position',
46
- // type: 'string',
47
- // options: {
48
- // list: ['left', 'center', 'right'],
49
- // },
50
- // initialValue: 'center',
51
- // }),
52
- ],
53
- }),
54
- ],
55
- })
4
+ import type {FieldsOption} from '..'
5
+
6
+ export const formType = (fields: FieldsOption): SchemaTypeDefinition => {
7
+ // const fieldsOf =
8
+ // fields && fields.length ? [{type: 'formField'}, ...fields] : [{type: 'formField'}]
9
+ return defineType({
10
+ name: 'form',
11
+ title: 'Form',
12
+ type: 'document',
13
+ icon: FaWpforms,
14
+ fields: [
15
+ defineField({
16
+ name: 'title',
17
+ title: 'Form Title',
18
+ type: 'string',
19
+ description: 'Internal title for the form',
20
+ validation: (Rule) => Rule.required(),
21
+ }),
22
+ defineField({
23
+ name: 'id',
24
+ title: 'Form ID',
25
+ type: 'slug',
26
+ options: {
27
+ source: 'title',
28
+ },
29
+ // validation: (Rule) => Rule.required(),
30
+ }),
31
+ defineField({
32
+ name: 'fields',
33
+ title: 'Form Fields',
34
+ type: 'array',
35
+ of: [{type: 'formField'}, ...fields],
36
+ }),
37
+ defineField({
38
+ name: 'submitButton',
39
+ title: 'Submit Button',
40
+ type: 'object',
41
+ fields: [
42
+ defineField({
43
+ name: 'text',
44
+ title: 'Button Text',
45
+ type: 'string',
46
+ initialValue: 'Submit',
47
+ }),
48
+ ],
49
+ }),
50
+ ],
51
+ })
52
+ }
@@ -1,8 +1,9 @@
1
1
  import type {SchemaTypeDefinition} from 'sanity'
2
2
 
3
+ import type {FieldsOption} from '..'
3
4
  import {formType} from './form'
4
5
  import {formFieldType} from './form-field'
5
6
 
6
- export const schema: {types: SchemaTypeDefinition[]} = {
7
- types: [formType, formFieldType],
7
+ export const schema = (fields: FieldsOption): {types: SchemaTypeDefinition[]} => {
8
+ return {types: [formType(fields), formFieldType]}
8
9
  }
@@ -1,78 +0,0 @@
1
- import {type FC, type FormEvent, useState} from 'react'
2
-
3
- import {FormRenderer} from '../components/form-renderer'
4
- import type {FieldState, FormDataProps} from '../components/types'
5
-
6
- interface UseStateExampleProps {
7
- formData: FormDataProps
8
- onSubmit?: (data: Record<string, unknown>) => void
9
- }
10
-
11
- const UseStateExample: FC<UseStateExampleProps> = ({formData, onSubmit = () => null}) => {
12
- const [values, setValues] = useState<Record<string, FieldState['value']>>({})
13
- const [errors, setErrors] = useState<Record<string, string | undefined>>({})
14
-
15
- const getFieldState = (fieldName: string) => ({
16
- value: values[fieldName],
17
- onChange: (value: unknown) => {
18
- // @ts-expect-error todo: fix this
19
- setValues((prev) => ({
20
- ...prev,
21
- [fieldName]: value,
22
- }))
23
- // Clear error when value changes
24
- if (errors[fieldName]) {
25
- setErrors((prev) => ({
26
- ...prev,
27
- [fieldName]: undefined,
28
- }))
29
- }
30
- },
31
- onBlur: () => {
32
- // Example validation on blur
33
- const field = formData.fields
34
- ?.flatMap((formField) => formField)
35
- .find((formField) => formField?.name === fieldName)
36
-
37
- if (field?.required && !values[fieldName]) {
38
- setErrors((prev) => ({
39
- ...prev,
40
- [fieldName]: 'This field is required',
41
- }))
42
- }
43
- },
44
- })
45
-
46
- const getFieldError = (fieldName: string) => errors[fieldName]
47
-
48
- const handleSubmit = (e: FormEvent) => {
49
- e.preventDefault()
50
- onSubmit(values)
51
- }
52
-
53
- return (
54
- <>
55
- <style>
56
- {`
57
- .form-field > label {
58
- display: block;
59
- }
60
- .form-field {
61
- margin-bottom: 1rem;
62
- }
63
-
64
- `}
65
- </style>
66
- <FormRenderer
67
- formData={formData}
68
- onSubmit={handleSubmit}
69
- getFieldState={getFieldState}
70
- getFieldError={getFieldError}
71
- />
72
- </>
73
- )
74
- }
75
- export const DocumentView = (props: {document: {displayed: FormDataProps}}) => {
76
- // console.log('props', props)
77
- return <UseStateExample formData={props.document.displayed} />
78
- }
@@ -1,11 +0,0 @@
1
- import type {DefaultDocumentNodeResolver} from 'sanity/structure'
2
-
3
- import {DocumentView} from './document-view'
4
-
5
- export const defaultDocumentNode: DefaultDocumentNodeResolver = (S, {schemaType}) => {
6
- // Conditionally return a different configuration based on the schema type
7
- if (schemaType === 'form') {
8
- return S.document().views([S.view.form(), S.view.component(DocumentView).title('Web')])
9
- }
10
- return S.document().views([S.view.form()])
11
- }