@sanity/form-toolkit 1.1.0 → 1.2.1
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.
- package/README.md +79 -1
- package/dist/index.d.mts +66 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +332 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +335 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -3
- package/src/form-schema/components/default-field.tsx +113 -0
- package/src/form-schema/components/form-renderer.tsx +58 -0
- package/src/form-schema/components/types.ts +53 -0
- package/src/form-schema/index.ts +9 -8
- package/src/form-schema/schema-types/form-field.ts +173 -109
- package/src/form-schema/schema-types/form.ts +43 -1
- package/src/form-schema/schema-types/index.ts +1 -1
- package/src/form-schema/structure/document-view.tsx +78 -0
- package/src/form-schema/structure/index.ts +11 -0
- package/src/index.ts +5 -2
- package/src/shared/create-handler.ts +1 -1
package/README.md
CHANGED
|
@@ -158,6 +158,85 @@ export default defineType({
|
|
|
158
158
|
})
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
+
## formSchema and FormRenderer
|
|
162
|
+
|
|
163
|
+
The `formSchema` plugin and `FormRenderer` React component are designed to be used together to build and render forms in Sanity. `formSchema` provides a Sanity schema for creating `form` documents made up of various `formFields`, then `FormRenderer` takes those `form` documents and renders them as React components. The `formSchema` plugin can be used by itself with your own logic for rendering the form. The `/examples` directory of this repository shows `FormRenderer` being used with popular form libraries.
|
|
164
|
+
|
|
165
|
+
First add `formSchema` it as a plugin in `sanity.config.ts` (or .js):
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import {defineConfig} from 'sanity'
|
|
169
|
+
import {formSchema} from '@sanity/form-toolkit'
|
|
170
|
+
|
|
171
|
+
export default defineConfig({
|
|
172
|
+
//...
|
|
173
|
+
plugins: [formSchema()],
|
|
174
|
+
})
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Then pass a `form` document to the `FormRenderer` component
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
import React, {type FC} from 'react'
|
|
181
|
+
import {FormRenderer, type FormDataProps} from '@sanity/form-toolkit'
|
|
182
|
+
|
|
183
|
+
interface NativeFormExampleProps {
|
|
184
|
+
formData: FormDataProps
|
|
185
|
+
action?: string
|
|
186
|
+
method?: 'get' | 'post'
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Example of using the `FormRenderer` as a native HTML form element.
|
|
190
|
+
*/
|
|
191
|
+
export const NativeFormExample: FC<NativeFormExampleProps> = ({
|
|
192
|
+
formData, // form document from Sanity
|
|
193
|
+
action = '/api/submit',
|
|
194
|
+
method = 'post',
|
|
195
|
+
}) => {
|
|
196
|
+
return (
|
|
197
|
+
<FormRenderer
|
|
198
|
+
formData={formData}
|
|
199
|
+
action={action}
|
|
200
|
+
method={method}
|
|
201
|
+
encType="multipart/form-data"
|
|
202
|
+
/>
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### FormRenderer
|
|
208
|
+
|
|
209
|
+
The `FormRenderer` component takes documents created with the `formSchema` plugin and renders a form for your front-end. The `formSchema` plugin can be used by itself with your own logic for rendering the form. The `/examples` directory of this repository shows `FormRenderer` being used with popular form libraries. `FormRenderer` takes the following props
|
|
210
|
+
|
|
211
|
+
#### All props for native `form` element
|
|
212
|
+
|
|
213
|
+
`FormRenderer` can take all the typical props passed to the `form` element in React like `action`, `onSubmit`, `className`, etc.
|
|
214
|
+
|
|
215
|
+
#### formData
|
|
216
|
+
|
|
217
|
+
A `form` document created with the `formSchema` plugin.
|
|
218
|
+
|
|
219
|
+
#### fieldComponents
|
|
220
|
+
|
|
221
|
+
An object where the keys are possible input field type names and the values are components for that field's input.
|
|
222
|
+
|
|
223
|
+
```tsx
|
|
224
|
+
const fieldComponents = {
|
|
225
|
+
select: MyCustomSelectComponent
|
|
226
|
+
}
|
|
227
|
+
<FormRenderer
|
|
228
|
+
fieldComponents={fieldComponents}
|
|
229
|
+
/>
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
#### getFieldState
|
|
233
|
+
|
|
234
|
+
Function for managing each field as a piece of state (see `react-hook-form.tsx` and `tanstack-form.tsx` in the `/examples` directory)
|
|
235
|
+
|
|
236
|
+
#### getFieldError
|
|
237
|
+
|
|
238
|
+
Similar to `getFieldState`, a function for managing each field's errors as a piece of state (see `react-hook-form.tsx` and `tanstack-form.tsx` in the `/examples` directory)
|
|
239
|
+
|
|
161
240
|
## License
|
|
162
241
|
|
|
163
242
|
[MIT](LICENSE) © Chris LaRocque
|
|
@@ -170,7 +249,6 @@ with default configuration for build & watch scripts.
|
|
|
170
249
|
See [Testing a plugin in Sanity Studio](https://github.com/sanity-io/plugin-kit#testing-a-plugin-in-sanity-studio)
|
|
171
250
|
on how to run this plugin with hotreload in the studio.
|
|
172
251
|
|
|
173
|
-
|
|
174
252
|
### Release new version
|
|
175
253
|
|
|
176
254
|
Run ["CI & Release" workflow](https://github.com/sanity-io/form-toolkit/actions/workflows/main.yml).
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import type {ComponentType} from 'react'
|
|
1
2
|
import {EventHandler} from 'h3'
|
|
2
3
|
import {EventHandlerRequest} from 'h3'
|
|
4
|
+
import type {FC} from 'react'
|
|
5
|
+
import type {HTMLProps} from 'react'
|
|
3
6
|
import {IncomingMessage} from 'http'
|
|
4
7
|
import {Plugin as Plugin_2} from 'sanity'
|
|
5
8
|
import {ServerResponse} from 'http'
|
|
@@ -14,6 +17,63 @@ export declare function fetchMailchimpData({
|
|
|
14
17
|
server: string
|
|
15
18
|
}): Promise<unknown>
|
|
16
19
|
|
|
20
|
+
declare type FieldChoice = {
|
|
21
|
+
label: string
|
|
22
|
+
value: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
declare interface FieldComponentProps {
|
|
26
|
+
field: FormField
|
|
27
|
+
fieldState: FieldState
|
|
28
|
+
error?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
declare type FieldOptions = {
|
|
32
|
+
placeholder?: string
|
|
33
|
+
defaultValue?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
declare interface FieldState {
|
|
37
|
+
value?: string | number | readonly string[]
|
|
38
|
+
onChange: (value: unknown) => void
|
|
39
|
+
onBlur?: () => void
|
|
40
|
+
ref?: unknown
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export declare type FormDataProps = {
|
|
44
|
+
title: string
|
|
45
|
+
id: {
|
|
46
|
+
current: string
|
|
47
|
+
}
|
|
48
|
+
fields?: FormField[]
|
|
49
|
+
submitButton?: {
|
|
50
|
+
text: string
|
|
51
|
+
position: 'left' | 'center' | 'right'
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
declare type FormField = {
|
|
56
|
+
type: string
|
|
57
|
+
label?: string
|
|
58
|
+
name: string
|
|
59
|
+
required: boolean
|
|
60
|
+
validation?: ValidationRule[]
|
|
61
|
+
options?: FieldOptions
|
|
62
|
+
choices?: FieldChoice[]
|
|
63
|
+
_key: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export declare const FormRenderer: FC<FormRendererProps>
|
|
67
|
+
|
|
68
|
+
declare interface FormRendererProps extends HTMLProps<HTMLFormElement> {
|
|
69
|
+
formData?: FormDataProps
|
|
70
|
+
getFieldState?: (fieldName: string) => FieldState
|
|
71
|
+
getFieldError?: (fieldName: string) => string | undefined
|
|
72
|
+
fieldComponents?: Record<string, ComponentType<FieldComponentProps>>
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export declare const formSchema: Plugin_2<void>
|
|
76
|
+
|
|
17
77
|
declare type HubSpotForm = {
|
|
18
78
|
id: string
|
|
19
79
|
name: string
|
|
@@ -97,4 +157,10 @@ declare type MappedResult = HubSpotForm & {
|
|
|
97
157
|
value: string
|
|
98
158
|
}
|
|
99
159
|
|
|
160
|
+
declare type ValidationRule = {
|
|
161
|
+
type: string
|
|
162
|
+
value: string
|
|
163
|
+
message: string
|
|
164
|
+
}
|
|
165
|
+
|
|
100
166
|
export {}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import type {ComponentType} from 'react'
|
|
1
2
|
import {EventHandler} from 'h3'
|
|
2
3
|
import {EventHandlerRequest} from 'h3'
|
|
4
|
+
import type {FC} from 'react'
|
|
5
|
+
import type {HTMLProps} from 'react'
|
|
3
6
|
import {IncomingMessage} from 'http'
|
|
4
7
|
import {Plugin as Plugin_2} from 'sanity'
|
|
5
8
|
import {ServerResponse} from 'http'
|
|
@@ -14,6 +17,63 @@ export declare function fetchMailchimpData({
|
|
|
14
17
|
server: string
|
|
15
18
|
}): Promise<unknown>
|
|
16
19
|
|
|
20
|
+
declare type FieldChoice = {
|
|
21
|
+
label: string
|
|
22
|
+
value: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
declare interface FieldComponentProps {
|
|
26
|
+
field: FormField
|
|
27
|
+
fieldState: FieldState
|
|
28
|
+
error?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
declare type FieldOptions = {
|
|
32
|
+
placeholder?: string
|
|
33
|
+
defaultValue?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
declare interface FieldState {
|
|
37
|
+
value?: string | number | readonly string[]
|
|
38
|
+
onChange: (value: unknown) => void
|
|
39
|
+
onBlur?: () => void
|
|
40
|
+
ref?: unknown
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export declare type FormDataProps = {
|
|
44
|
+
title: string
|
|
45
|
+
id: {
|
|
46
|
+
current: string
|
|
47
|
+
}
|
|
48
|
+
fields?: FormField[]
|
|
49
|
+
submitButton?: {
|
|
50
|
+
text: string
|
|
51
|
+
position: 'left' | 'center' | 'right'
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
declare type FormField = {
|
|
56
|
+
type: string
|
|
57
|
+
label?: string
|
|
58
|
+
name: string
|
|
59
|
+
required: boolean
|
|
60
|
+
validation?: ValidationRule[]
|
|
61
|
+
options?: FieldOptions
|
|
62
|
+
choices?: FieldChoice[]
|
|
63
|
+
_key: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export declare const FormRenderer: FC<FormRendererProps>
|
|
67
|
+
|
|
68
|
+
declare interface FormRendererProps extends HTMLProps<HTMLFormElement> {
|
|
69
|
+
formData?: FormDataProps
|
|
70
|
+
getFieldState?: (fieldName: string) => FieldState
|
|
71
|
+
getFieldError?: (fieldName: string) => string | undefined
|
|
72
|
+
fieldComponents?: Record<string, ComponentType<FieldComponentProps>>
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export declare const formSchema: Plugin_2<void>
|
|
76
|
+
|
|
17
77
|
declare type HubSpotForm = {
|
|
18
78
|
id: string
|
|
19
79
|
name: string
|
|
@@ -97,4 +157,10 @@ declare type MappedResult = HubSpotForm & {
|
|
|
97
157
|
value: string
|
|
98
158
|
}
|
|
99
159
|
|
|
160
|
+
declare type ValidationRule = {
|
|
161
|
+
type: string
|
|
162
|
+
value: string
|
|
163
|
+
message: string
|
|
164
|
+
}
|
|
165
|
+
|
|
100
166
|
export {}
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,339 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: !0 });
|
|
3
|
-
var
|
|
3
|
+
var sanity = require("sanity"), fa = require("react-icons/fa"), lu = require("react-icons/lu"), sanityPluginAsyncList = require("@sanity/sanity-plugin-async-list"), jsxRuntime = require("react/jsx-runtime"), ui = require("@sanity/ui"), h3 = require("h3"), mailchimp = require("@mailchimp/mailchimp_marketing");
|
|
4
4
|
function _interopDefaultCompat(e) {
|
|
5
5
|
return e && typeof e == "object" && "default" in e ? e : { default: e };
|
|
6
6
|
}
|
|
7
7
|
var mailchimp__default = /* @__PURE__ */ _interopDefaultCompat(mailchimp);
|
|
8
|
-
const
|
|
8
|
+
const DefaultField = ({ field, fieldState, error }) => {
|
|
9
|
+
const { type, label, name, options = {}, choices = [] } = field;
|
|
10
|
+
if (!type || !name) return null;
|
|
11
|
+
const { value, onChange, onBlur, ref } = fieldState, handleChange = (e) => {
|
|
12
|
+
onChange(e.target.value);
|
|
13
|
+
}, handleCheckboxChange = (e, choiceValue) => {
|
|
14
|
+
if (Array.isArray(value)) {
|
|
15
|
+
const newValue = e.target.checked ? [...value, choiceValue] : value.filter((v) => v !== choiceValue);
|
|
16
|
+
onChange(newValue);
|
|
17
|
+
} else
|
|
18
|
+
onChange(e.target.checked ? choiceValue : "");
|
|
19
|
+
}, renderInput = () => {
|
|
20
|
+
switch (type) {
|
|
21
|
+
case "textarea":
|
|
22
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
23
|
+
"textarea",
|
|
24
|
+
{
|
|
25
|
+
ref,
|
|
26
|
+
name,
|
|
27
|
+
value: value ?? "",
|
|
28
|
+
onChange: handleChange,
|
|
29
|
+
onBlur,
|
|
30
|
+
placeholder: options.placeholder
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
case "select":
|
|
34
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
35
|
+
"select",
|
|
36
|
+
{
|
|
37
|
+
ref,
|
|
38
|
+
name,
|
|
39
|
+
value: value ?? "",
|
|
40
|
+
onChange: handleChange,
|
|
41
|
+
onBlur,
|
|
42
|
+
children: choices?.map((choice, i) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: choice.value, children: choice.label }, i))
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
case "radio":
|
|
46
|
+
return choices?.map((choice, i) => /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
47
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
48
|
+
"input",
|
|
49
|
+
{
|
|
50
|
+
type: "radio",
|
|
51
|
+
name,
|
|
52
|
+
ref,
|
|
53
|
+
value: choice.value,
|
|
54
|
+
checked: value === choice.value,
|
|
55
|
+
onChange: handleChange,
|
|
56
|
+
onBlur
|
|
57
|
+
}
|
|
58
|
+
),
|
|
59
|
+
choice.label
|
|
60
|
+
] }, i));
|
|
61
|
+
case "checkbox":
|
|
62
|
+
return choices?.map((choice, i) => /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
63
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
64
|
+
"input",
|
|
65
|
+
{
|
|
66
|
+
type: "checkbox",
|
|
67
|
+
name,
|
|
68
|
+
ref,
|
|
69
|
+
value: choice.value,
|
|
70
|
+
checked: Array.isArray(value) ? value.includes(choice.value) : value === choice.value,
|
|
71
|
+
onChange: (e) => handleCheckboxChange(e, choice.value),
|
|
72
|
+
onBlur
|
|
73
|
+
}
|
|
74
|
+
),
|
|
75
|
+
choice.label
|
|
76
|
+
] }, i));
|
|
77
|
+
default:
|
|
78
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
79
|
+
"input",
|
|
80
|
+
{
|
|
81
|
+
type,
|
|
82
|
+
ref,
|
|
83
|
+
name,
|
|
84
|
+
value: value ?? options.defaultValue ?? "",
|
|
85
|
+
onChange: handleChange,
|
|
86
|
+
onBlur,
|
|
87
|
+
placeholder: options.placeholder
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
93
|
+
label && type != "hidden" && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: name, children: label }),
|
|
94
|
+
renderInput(),
|
|
95
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "error", children: error })
|
|
96
|
+
] });
|
|
97
|
+
}, FormRenderer = (props) => {
|
|
98
|
+
const {
|
|
99
|
+
formData,
|
|
100
|
+
getFieldState = (name) => ({
|
|
101
|
+
value: void 0,
|
|
102
|
+
onChange: () => {
|
|
103
|
+
},
|
|
104
|
+
name
|
|
105
|
+
// Pass name to field for native form handling
|
|
106
|
+
}),
|
|
107
|
+
getFieldError,
|
|
108
|
+
fieldComponents = {},
|
|
109
|
+
children
|
|
110
|
+
} = props, renderField = (field) => {
|
|
111
|
+
const CustomComponent = fieldComponents[field.type], fieldState = getFieldState(field.name), error = getFieldError?.(field.name);
|
|
112
|
+
return CustomComponent ? /* @__PURE__ */ jsxRuntime.jsx(CustomComponent, { field, fieldState, error }) : /* @__PURE__ */ jsxRuntime.jsx(DefaultField, { field, fieldState, error });
|
|
113
|
+
}, elProps = Object.assign({}, props);
|
|
114
|
+
return delete elProps.formData, delete elProps.getFieldState, delete elProps.getFieldError, delete elProps.fieldComponents, /* @__PURE__ */ jsxRuntime.jsxs("form", { ...elProps, id: elProps.id ?? formData?.id?.current, children: [
|
|
115
|
+
formData?.fields?.map((field) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "form-field", children: renderField(field) }, field._key)),
|
|
116
|
+
children,
|
|
117
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", children: formData?.submitButton?.text || "Submit" })
|
|
118
|
+
] });
|
|
119
|
+
}, formType = sanity.defineType({
|
|
120
|
+
name: "form",
|
|
121
|
+
title: "Form",
|
|
122
|
+
type: "document",
|
|
123
|
+
icon: fa.FaWpforms,
|
|
124
|
+
fields: [
|
|
125
|
+
sanity.defineField({
|
|
126
|
+
name: "title",
|
|
127
|
+
title: "Form Title",
|
|
128
|
+
type: "string",
|
|
129
|
+
description: "Internal title for the form",
|
|
130
|
+
validation: (Rule) => Rule.required()
|
|
131
|
+
}),
|
|
132
|
+
sanity.defineField({
|
|
133
|
+
name: "id",
|
|
134
|
+
title: "Form ID",
|
|
135
|
+
type: "slug",
|
|
136
|
+
options: {
|
|
137
|
+
source: "title"
|
|
138
|
+
}
|
|
139
|
+
// validation: (Rule) => Rule.required(),
|
|
140
|
+
}),
|
|
141
|
+
sanity.defineField({
|
|
142
|
+
name: "fields",
|
|
143
|
+
title: "Form Fields",
|
|
144
|
+
type: "array",
|
|
145
|
+
of: [{ type: "formField" }]
|
|
146
|
+
}),
|
|
147
|
+
sanity.defineField({
|
|
148
|
+
name: "submitButton",
|
|
149
|
+
title: "Submit Button",
|
|
150
|
+
type: "object",
|
|
151
|
+
fields: [
|
|
152
|
+
sanity.defineField({
|
|
153
|
+
name: "text",
|
|
154
|
+
title: "Button Text",
|
|
155
|
+
type: "string",
|
|
156
|
+
initialValue: "Submit"
|
|
157
|
+
})
|
|
158
|
+
// defineField({
|
|
159
|
+
// name: 'position',
|
|
160
|
+
// title: 'Button Position',
|
|
161
|
+
// type: 'string',
|
|
162
|
+
// options: {
|
|
163
|
+
// list: ['left', 'center', 'right'],
|
|
164
|
+
// },
|
|
165
|
+
// initialValue: 'center',
|
|
166
|
+
// }),
|
|
167
|
+
]
|
|
168
|
+
})
|
|
169
|
+
]
|
|
170
|
+
}), validationTypesByFieldType = {
|
|
171
|
+
checkbox: ["minSelectedCount", "maxSelectedCount", "custom"],
|
|
172
|
+
color: ["custom"],
|
|
173
|
+
date: ["minDate", "maxDate", "custom"],
|
|
174
|
+
"datetime-local": ["minDate", "maxDate", "custom"],
|
|
175
|
+
email: ["pattern", "custom"],
|
|
176
|
+
file: ["maxSize", "fileType", "custom"],
|
|
177
|
+
hidden: ["custom"],
|
|
178
|
+
number: ["min", "max", "custom"],
|
|
179
|
+
// password: ['minLength', 'pattern', 'custom'],
|
|
180
|
+
radio: ["custom"],
|
|
181
|
+
range: ["min", "max", "step", "custom"],
|
|
182
|
+
select: ["custom"],
|
|
183
|
+
tel: ["pattern", "custom"],
|
|
184
|
+
text: ["minLength", "maxLength", "pattern", "custom"],
|
|
185
|
+
textarea: ["minLength", "maxLength", "custom"],
|
|
186
|
+
time: ["custom"],
|
|
187
|
+
url: ["pattern", "custom"]
|
|
188
|
+
}, formFieldType = sanity.defineType({
|
|
189
|
+
name: "formField",
|
|
190
|
+
title: "Form Field",
|
|
191
|
+
type: "object",
|
|
192
|
+
icon: lu.LuTextCursorInput,
|
|
193
|
+
fields: [
|
|
194
|
+
sanity.defineField({
|
|
195
|
+
name: "type",
|
|
196
|
+
title: "Field Type",
|
|
197
|
+
type: "string",
|
|
198
|
+
options: {
|
|
199
|
+
list: Object.keys(validationTypesByFieldType).map((type) => ({ title: ((fieldType) => {
|
|
200
|
+
switch (fieldType) {
|
|
201
|
+
case "datetime-local":
|
|
202
|
+
return "Date & Time";
|
|
203
|
+
case "textarea":
|
|
204
|
+
return "Text Area";
|
|
205
|
+
case "tel":
|
|
206
|
+
return "Phone Number";
|
|
207
|
+
default:
|
|
208
|
+
return fieldType.charAt(0).toUpperCase() + fieldType.slice(1);
|
|
209
|
+
}
|
|
210
|
+
})(type), value: type }))
|
|
211
|
+
}
|
|
212
|
+
}),
|
|
213
|
+
sanity.defineField({
|
|
214
|
+
name: "label",
|
|
215
|
+
title: "Field Label",
|
|
216
|
+
type: "string"
|
|
217
|
+
}),
|
|
218
|
+
sanity.defineField({
|
|
219
|
+
name: "name",
|
|
220
|
+
title: "Field Name",
|
|
221
|
+
type: "string",
|
|
222
|
+
description: "Must start with a letter and contain only letters, numbers, underscores, or hyphens. Must be unique within the form.",
|
|
223
|
+
validation: (Rule) => Rule.required().custom((name, context) => name ? /^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name) ? (context.document?.fields?.map((field) => field.name) || []).filter((n) => n === name).length > 1 ? "Field name must be unique across all form fields" : [
|
|
224
|
+
"action",
|
|
225
|
+
"method",
|
|
226
|
+
"target",
|
|
227
|
+
"enctype",
|
|
228
|
+
"accept-charset",
|
|
229
|
+
"autocomplete",
|
|
230
|
+
"novalidate",
|
|
231
|
+
"rel",
|
|
232
|
+
"submit",
|
|
233
|
+
"reset"
|
|
234
|
+
].includes(name.toLowerCase()) ? "This name is reserved for HTML form attributes. Please choose a different name." : !0 : "Field name must start with a letter and contain only letters, numbers, underscores, or hyphens" : "Required")
|
|
235
|
+
}),
|
|
236
|
+
sanity.defineField({
|
|
237
|
+
name: "required",
|
|
238
|
+
title: "Required",
|
|
239
|
+
type: "boolean",
|
|
240
|
+
initialValue: !1
|
|
241
|
+
}),
|
|
242
|
+
// defineField({
|
|
243
|
+
// name: 'validation',
|
|
244
|
+
// title: 'Validation Rules',
|
|
245
|
+
// type: 'array',
|
|
246
|
+
// of: [
|
|
247
|
+
// {
|
|
248
|
+
// type: 'object',
|
|
249
|
+
// fields: [
|
|
250
|
+
// defineField({
|
|
251
|
+
// name: 'type',
|
|
252
|
+
// title: 'Validation Type',
|
|
253
|
+
// type: 'string',
|
|
254
|
+
// hidden: ({parent}) => !parent?.type,
|
|
255
|
+
// options: {
|
|
256
|
+
// // TODO: I think this needs to be a custom input component?
|
|
257
|
+
// // list: ({parent}) => (parent?.type ? validationTypesByFieldType[parent.type] : []),
|
|
258
|
+
// list: [],
|
|
259
|
+
// },
|
|
260
|
+
// }),
|
|
261
|
+
// defineField({
|
|
262
|
+
// name: 'value',
|
|
263
|
+
// title: 'Value',
|
|
264
|
+
// type: 'string',
|
|
265
|
+
// }),
|
|
266
|
+
// defineField({
|
|
267
|
+
// name: 'message',
|
|
268
|
+
// title: 'Error Message',
|
|
269
|
+
// type: 'string',
|
|
270
|
+
// }),
|
|
271
|
+
// ],
|
|
272
|
+
// },
|
|
273
|
+
// ],
|
|
274
|
+
// }),
|
|
275
|
+
sanity.defineField({
|
|
276
|
+
name: "choices",
|
|
277
|
+
title: "Choices",
|
|
278
|
+
type: "array",
|
|
279
|
+
hidden: ({ parent }) => !["select", "radio", "checkbox"].includes(parent?.type),
|
|
280
|
+
of: [
|
|
281
|
+
{
|
|
282
|
+
type: "object",
|
|
283
|
+
fields: [
|
|
284
|
+
sanity.defineField({
|
|
285
|
+
name: "label",
|
|
286
|
+
title: "Label",
|
|
287
|
+
type: "string"
|
|
288
|
+
}),
|
|
289
|
+
sanity.defineField({
|
|
290
|
+
name: "value",
|
|
291
|
+
title: "Value",
|
|
292
|
+
type: "string"
|
|
293
|
+
})
|
|
294
|
+
]
|
|
295
|
+
}
|
|
296
|
+
]
|
|
297
|
+
}),
|
|
298
|
+
sanity.defineField({
|
|
299
|
+
name: "options",
|
|
300
|
+
title: "Field Options",
|
|
301
|
+
type: "object",
|
|
302
|
+
hidden: ({ parent }) => ["select", "radio", "checkbox", "file"].includes(parent?.type),
|
|
303
|
+
fields: [
|
|
304
|
+
sanity.defineField({
|
|
305
|
+
name: "placeholder",
|
|
306
|
+
title: "Placeholder",
|
|
307
|
+
type: "string"
|
|
308
|
+
}),
|
|
309
|
+
sanity.defineField({
|
|
310
|
+
name: "defaultValue",
|
|
311
|
+
title: "Default Value",
|
|
312
|
+
type: "string"
|
|
313
|
+
})
|
|
314
|
+
]
|
|
315
|
+
})
|
|
316
|
+
],
|
|
317
|
+
preview: {
|
|
318
|
+
select: {
|
|
319
|
+
label: "label",
|
|
320
|
+
name: "name",
|
|
321
|
+
type: "type"
|
|
322
|
+
},
|
|
323
|
+
prepare({ label, name, type }) {
|
|
324
|
+
return {
|
|
325
|
+
title: label || name,
|
|
326
|
+
subtitle: type
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}), schema = {
|
|
331
|
+
types: [formType, formFieldType]
|
|
332
|
+
}, formSchema = sanity.definePlugin(() => ({
|
|
333
|
+
name: "form-toolkit_form-schema",
|
|
334
|
+
schema
|
|
335
|
+
// plugins: [structureTool({defaultDocumentNode})],
|
|
336
|
+
})), Option$1 = (option) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Card, { "data-as": "button", padding: 3, radius: 2, tone: "inherit", children: [
|
|
9
337
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, textOverflow: "ellipsis", children: option.name }),
|
|
10
338
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Card, { paddingTop: 2, tone: "inherit", style: { background: "inherit" }, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, textOverflow: "ellipsis", children: `ID: ${option.value}` }) })
|
|
11
339
|
] }), hubSpotInput = sanity.definePlugin((options) => ({
|
|
@@ -141,8 +469,10 @@ async function fetchMailchimpData({
|
|
|
141
469
|
return signupForms;
|
|
142
470
|
}
|
|
143
471
|
const mailchimpHandler = (keys) => createHandler(() => fetchMailchimpData(keys));
|
|
472
|
+
exports.FormRenderer = FormRenderer;
|
|
144
473
|
exports.fetchHubSpotData = fetchHubSpotData;
|
|
145
474
|
exports.fetchMailchimpData = fetchMailchimpData;
|
|
475
|
+
exports.formSchema = formSchema;
|
|
146
476
|
exports.hubSpotHandler = hubSpotHandler;
|
|
147
477
|
exports.hubSpotInput = hubSpotInput;
|
|
148
478
|
exports.mailchimpHandler = mailchimpHandler;
|