@sanity/form-toolkit 2.1.0 → 2.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 +33 -3
- package/dist/form-renderer/index.d.mts +66 -0
- package/dist/form-renderer/index.d.ts +66 -0
- package/dist/form-renderer/index.js +128 -0
- package/dist/form-renderer/index.js.map +1 -0
- package/dist/form-renderer/index.mjs +128 -0
- package/dist/form-renderer/index.mjs.map +1 -0
- package/dist/form-schema/index.d.mts +23 -64
- package/dist/form-schema/index.d.ts +23 -64
- package/dist/form-schema/index.js +72 -185
- package/dist/form-schema/index.js.map +1 -1
- package/dist/form-schema/index.mjs +72 -186
- package/dist/form-schema/index.mjs.map +1 -1
- package/package.json +9 -1
- package/src/{form-schema → form-renderer}/components/default-field.tsx +11 -3
- package/src/{form-schema → form-renderer}/components/types.ts +0 -2
- package/src/form-renderer/index.ts +4 -0
- package/src/form-schema/components/validation-type.tsx +14 -0
- package/src/form-schema/index.ts +12 -6
- package/src/form-schema/schema-types/form-field.ts +66 -52
- package/src/form-schema/schema-types/form.ts +50 -53
- package/src/form-schema/schema-types/index.ts +3 -2
- package/src/form-schema/structure/document-view.tsx +0 -78
- package/src/form-schema/structure/index.ts +0 -11
- /package/src/{form-schema → form-renderer}/components/form-renderer.tsx +0 -0
|
@@ -1,125 +1,7 @@
|
|
|
1
|
-
import { defineType, defineField, definePlugin } from "sanity";
|
|
2
|
-
import { jsxs, Fragment, jsx } from "react/jsx-runtime";
|
|
1
|
+
import { defineType, defineField, useFormValue, definePlugin } from "sanity";
|
|
3
2
|
import { FaWpforms } from "react-icons/fa";
|
|
4
3
|
import { LuTextCursorInput } from "react-icons/lu";
|
|
5
|
-
const
|
|
6
|
-
const { type, label, name, options = {}, choices = [] } = field;
|
|
7
|
-
if (!type || !name) return null;
|
|
8
|
-
const { value, onChange, onBlur, ref } = fieldState, handleChange = (e) => {
|
|
9
|
-
onChange(e.target.value);
|
|
10
|
-
}, handleCheckboxChange = (e, choiceValue) => {
|
|
11
|
-
if (Array.isArray(value)) {
|
|
12
|
-
const newValue = e.target.checked ? [...value, choiceValue] : value.filter((v) => v !== choiceValue);
|
|
13
|
-
onChange(newValue);
|
|
14
|
-
} else
|
|
15
|
-
onChange(e.target.checked ? choiceValue : "");
|
|
16
|
-
}, renderInput = () => {
|
|
17
|
-
switch (type) {
|
|
18
|
-
case "submit":
|
|
19
|
-
return /* @__PURE__ */ jsx("button", { type: "submit", children: label || "Submit" });
|
|
20
|
-
case "textarea":
|
|
21
|
-
return /* @__PURE__ */ jsx(
|
|
22
|
-
"textarea",
|
|
23
|
-
{
|
|
24
|
-
ref,
|
|
25
|
-
name,
|
|
26
|
-
value: value ?? "",
|
|
27
|
-
onChange: handleChange,
|
|
28
|
-
onBlur,
|
|
29
|
-
placeholder: options.placeholder
|
|
30
|
-
}
|
|
31
|
-
);
|
|
32
|
-
case "select":
|
|
33
|
-
return /* @__PURE__ */ jsx(
|
|
34
|
-
"select",
|
|
35
|
-
{
|
|
36
|
-
ref,
|
|
37
|
-
name,
|
|
38
|
-
value: value ?? "",
|
|
39
|
-
onChange: handleChange,
|
|
40
|
-
onBlur,
|
|
41
|
-
children: choices?.map((choice, i) => /* @__PURE__ */ jsx("option", { value: choice.value, children: choice.label }, i))
|
|
42
|
-
}
|
|
43
|
-
);
|
|
44
|
-
case "radio":
|
|
45
|
-
return choices?.map((choice, i) => /* @__PURE__ */ jsxs("label", { children: [
|
|
46
|
-
/* @__PURE__ */ jsx(
|
|
47
|
-
"input",
|
|
48
|
-
{
|
|
49
|
-
type: "radio",
|
|
50
|
-
name,
|
|
51
|
-
ref,
|
|
52
|
-
value: choice.value,
|
|
53
|
-
checked: value === choice.value,
|
|
54
|
-
onChange: handleChange,
|
|
55
|
-
onBlur
|
|
56
|
-
}
|
|
57
|
-
),
|
|
58
|
-
choice.label
|
|
59
|
-
] }, i));
|
|
60
|
-
case "checkbox":
|
|
61
|
-
return choices?.map((choice, i) => /* @__PURE__ */ jsxs("label", { children: [
|
|
62
|
-
/* @__PURE__ */ jsx(
|
|
63
|
-
"input",
|
|
64
|
-
{
|
|
65
|
-
type: "checkbox",
|
|
66
|
-
name,
|
|
67
|
-
ref,
|
|
68
|
-
value: choice.value,
|
|
69
|
-
checked: Array.isArray(value) ? value.includes(choice.value) : value === choice.value,
|
|
70
|
-
onChange: (e) => handleCheckboxChange(e, choice.value),
|
|
71
|
-
onBlur
|
|
72
|
-
}
|
|
73
|
-
),
|
|
74
|
-
choice.label
|
|
75
|
-
] }, i));
|
|
76
|
-
default:
|
|
77
|
-
return /* @__PURE__ */ jsx(
|
|
78
|
-
"input",
|
|
79
|
-
{
|
|
80
|
-
type,
|
|
81
|
-
ref,
|
|
82
|
-
name,
|
|
83
|
-
value: value ?? options.defaultValue ?? "",
|
|
84
|
-
onChange: handleChange,
|
|
85
|
-
onBlur,
|
|
86
|
-
placeholder: options.placeholder
|
|
87
|
-
}
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
92
|
-
label && !["hidden", "submit"].includes(type) && /* @__PURE__ */ jsx("label", { htmlFor: name, children: label }),
|
|
93
|
-
renderInput(),
|
|
94
|
-
error && /* @__PURE__ */ jsx("span", { className: "error", children: error })
|
|
95
|
-
] });
|
|
96
|
-
}, FormRenderer = (props) => {
|
|
97
|
-
const {
|
|
98
|
-
formData,
|
|
99
|
-
getFieldState = (name) => ({
|
|
100
|
-
value: void 0,
|
|
101
|
-
onChange: () => {
|
|
102
|
-
},
|
|
103
|
-
name
|
|
104
|
-
// Pass name to field for native form handling
|
|
105
|
-
}),
|
|
106
|
-
getFieldError,
|
|
107
|
-
fieldComponents = {},
|
|
108
|
-
children
|
|
109
|
-
} = props, renderField = (field) => {
|
|
110
|
-
const CustomComponent = fieldComponents[field.type], fieldState = getFieldState(field.name), error = getFieldError?.(field.name);
|
|
111
|
-
return CustomComponent ? /* @__PURE__ */ jsx(CustomComponent, { field, fieldState, error }) : /* @__PURE__ */ jsx(DefaultField, { field, fieldState, error });
|
|
112
|
-
}, elProps = Object.assign({}, props);
|
|
113
|
-
return delete elProps.formData, delete elProps.getFieldState, delete elProps.getFieldError, delete elProps.fieldComponents, /* @__PURE__ */ jsxs("form", { ...elProps, id: elProps.id ?? formData?.id?.current, children: [
|
|
114
|
-
formData?.fields?.map((field) => /* @__PURE__ */ jsx("div", { className: "form-field", children: renderField(field) }, field._key)),
|
|
115
|
-
children,
|
|
116
|
-
renderField({
|
|
117
|
-
type: "submit",
|
|
118
|
-
name: "submit",
|
|
119
|
-
label: formData?.submitButton?.text || "Submit"
|
|
120
|
-
})
|
|
121
|
-
] });
|
|
122
|
-
}, formType = defineType({
|
|
4
|
+
const formType = (fields) => defineType({
|
|
123
5
|
name: "form",
|
|
124
6
|
title: "Form",
|
|
125
7
|
type: "document",
|
|
@@ -145,7 +27,7 @@ const DefaultField = ({ field, fieldState, error }) => {
|
|
|
145
27
|
name: "fields",
|
|
146
28
|
title: "Form Fields",
|
|
147
29
|
type: "array",
|
|
148
|
-
of: [{ type: "formField" }]
|
|
30
|
+
of: [{ type: "formField" }, ...fields]
|
|
149
31
|
}),
|
|
150
32
|
defineField({
|
|
151
33
|
name: "submitButton",
|
|
@@ -158,36 +40,30 @@ const DefaultField = ({ field, fieldState, error }) => {
|
|
|
158
40
|
type: "string",
|
|
159
41
|
initialValue: "Submit"
|
|
160
42
|
})
|
|
161
|
-
// defineField({
|
|
162
|
-
// name: 'position',
|
|
163
|
-
// title: 'Button Position',
|
|
164
|
-
// type: 'string',
|
|
165
|
-
// options: {
|
|
166
|
-
// list: ['left', 'center', 'right'],
|
|
167
|
-
// },
|
|
168
|
-
// initialValue: 'center',
|
|
169
|
-
// }),
|
|
170
43
|
]
|
|
171
44
|
})
|
|
172
45
|
]
|
|
173
|
-
}),
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
46
|
+
}), ValidationType = (props) => {
|
|
47
|
+
const { type } = useFormValue([...props.path.slice(0, 2)]);
|
|
48
|
+
return type && props.schemaType?.options && (props.schemaType.options.list = validationTypesByFieldType[type]), props.renderDefault(props);
|
|
49
|
+
}, validationTypesByFieldType = {
|
|
50
|
+
checkbox: ["minSelectedCount", "maxSelectedCount"],
|
|
51
|
+
color: [],
|
|
52
|
+
date: ["minDate", "maxDate"],
|
|
53
|
+
"datetime-local": ["minDate", "maxDate"],
|
|
54
|
+
email: ["pattern"],
|
|
55
|
+
file: ["maxSize", "fileType"],
|
|
56
|
+
hidden: [],
|
|
57
|
+
number: ["min", "max"],
|
|
58
|
+
// password: ['minLength', 'pattern'],
|
|
59
|
+
radio: [],
|
|
60
|
+
range: ["min", "max", "step"],
|
|
61
|
+
select: [],
|
|
62
|
+
tel: ["pattern"],
|
|
63
|
+
text: ["minLength", "maxLength", "pattern"],
|
|
64
|
+
textarea: ["minLength", "maxLength"],
|
|
65
|
+
time: [],
|
|
66
|
+
url: ["pattern"]
|
|
191
67
|
}, formFieldType = defineType({
|
|
192
68
|
name: "formField",
|
|
193
69
|
title: "Form Field",
|
|
@@ -242,39 +118,52 @@ const DefaultField = ({ field, fieldState, error }) => {
|
|
|
242
118
|
type: "boolean",
|
|
243
119
|
initialValue: !1
|
|
244
120
|
}),
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
121
|
+
defineField({
|
|
122
|
+
name: "validation",
|
|
123
|
+
title: "Validation Rules",
|
|
124
|
+
type: "array",
|
|
125
|
+
hidden: ({ parent }) => {
|
|
126
|
+
if (!parent?.type) return !0;
|
|
127
|
+
const validationTypes = validationTypesByFieldType[parent.type];
|
|
128
|
+
return !validationTypes || validationTypes.length === 0;
|
|
129
|
+
},
|
|
130
|
+
of: [
|
|
131
|
+
{
|
|
132
|
+
type: "object",
|
|
133
|
+
fields: [
|
|
134
|
+
defineField({
|
|
135
|
+
name: "type",
|
|
136
|
+
title: "Validation Type",
|
|
137
|
+
type: "string",
|
|
138
|
+
options: {
|
|
139
|
+
// TODO: I think this needs to be a custom input component?
|
|
140
|
+
// list: ({parent}) => (parent?.type ? validationTypesByFieldType[parent.type] : []),
|
|
141
|
+
list: []
|
|
142
|
+
},
|
|
143
|
+
components: {
|
|
144
|
+
input: ValidationType
|
|
145
|
+
}
|
|
146
|
+
}),
|
|
147
|
+
defineField({
|
|
148
|
+
name: "value",
|
|
149
|
+
title: "Value",
|
|
150
|
+
type: "string"
|
|
151
|
+
}),
|
|
152
|
+
defineField({
|
|
153
|
+
name: "message",
|
|
154
|
+
title: "Error Message",
|
|
155
|
+
type: "string"
|
|
156
|
+
})
|
|
157
|
+
],
|
|
158
|
+
preview: {
|
|
159
|
+
select: {
|
|
160
|
+
title: "type",
|
|
161
|
+
subtitle: "value"
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
]
|
|
166
|
+
}),
|
|
278
167
|
defineField({
|
|
279
168
|
name: "choices",
|
|
280
169
|
title: "Choices",
|
|
@@ -330,15 +219,12 @@ const DefaultField = ({ field, fieldState, error }) => {
|
|
|
330
219
|
};
|
|
331
220
|
}
|
|
332
221
|
}
|
|
333
|
-
}), schema = {
|
|
334
|
-
types: [formType, formFieldType]
|
|
335
|
-
}, formSchema = definePlugin(() => ({
|
|
222
|
+
}), schema = (fields) => ({ types: [formType(fields), formFieldType] }), formSchema = definePlugin((options) => ({
|
|
336
223
|
name: "form-toolkit_form-schema",
|
|
337
|
-
schema
|
|
224
|
+
schema: schema(options?.fields ?? [])
|
|
338
225
|
// plugins: [structureTool({defaultDocumentNode})],
|
|
339
226
|
}));
|
|
340
227
|
export {
|
|
341
|
-
FormRenderer,
|
|
342
228
|
formSchema
|
|
343
229
|
};
|
|
344
230
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../../src/form-schema/components/default-field.tsx","../../src/form-schema/components/form-renderer.tsx","../../src/form-schema/schema-types/form.ts","../../src/form-schema/schema-types/form-field.ts","../../src/form-schema/schema-types/index.ts","../../src/form-schema/index.ts"],"sourcesContent":["import type {ChangeEvent, FC, LegacyRef} from 'react'\n\nimport type {FieldComponentProps} from './types'\n\nexport const DefaultField: FC<FieldComponentProps> = ({field, fieldState, error}) => {\n const {type, label, name, options = {}, choices = []} = field\n if (!type || !name) return null\n\n const {value, onChange, onBlur, ref} = fieldState\n\n const handleChange = (\n e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>,\n ) => {\n onChange(e.target.value)\n }\n\n const handleCheckboxChange = (e: ChangeEvent<HTMLInputElement>, choiceValue: string) => {\n if (Array.isArray(value)) {\n const newValue = e.target.checked\n ? [...value, choiceValue]\n : value.filter((v: string) => v !== choiceValue)\n onChange(newValue)\n } else {\n onChange(e.target.checked ? choiceValue : '')\n }\n }\n\n const renderInput = () => {\n switch (type) {\n case 'submit':\n return <button type=\"submit\">{label || 'Submit'}</button>\n case 'textarea':\n return (\n <textarea\n ref={ref as LegacyRef<HTMLTextAreaElement>}\n name={name}\n value={value ?? ''}\n onChange={handleChange}\n onBlur={onBlur}\n placeholder={options.placeholder}\n />\n )\n\n case 'select':\n return (\n <select\n ref={ref as LegacyRef<HTMLSelectElement>}\n name={name}\n value={value ?? ''}\n onChange={handleChange}\n onBlur={onBlur}\n >\n {choices?.map((choice, i) => (\n <option key={i} value={choice.value}>\n {choice.label}\n </option>\n ))}\n </select>\n )\n\n case 'radio':\n return choices?.map((choice, i) => (\n <label key={i}>\n <input\n type=\"radio\"\n name={name}\n ref={ref as LegacyRef<HTMLInputElement>}\n value={choice.value}\n checked={value === choice.value}\n onChange={handleChange}\n onBlur={onBlur}\n />\n {choice.label}\n </label>\n ))\n\n case 'checkbox':\n return choices?.map((choice, i) => (\n <label key={i}>\n <input\n type=\"checkbox\"\n name={name}\n ref={ref as LegacyRef<HTMLInputElement>}\n value={choice.value}\n checked={Array.isArray(value) ? value.includes(choice.value) : value === choice.value}\n onChange={(e) => handleCheckboxChange(e, choice.value)}\n onBlur={onBlur}\n />\n {choice.label}\n </label>\n ))\n\n default:\n return (\n <input\n type={type}\n ref={ref as LegacyRef<HTMLInputElement>}\n name={name}\n value={value ?? options.defaultValue ?? ''}\n onChange={handleChange}\n onBlur={onBlur}\n placeholder={options.placeholder}\n />\n )\n }\n }\n\n return (\n <>\n {label && !['hidden', 'submit'].includes(type) && <label htmlFor={name}>{label}</label>}\n {renderInput()}\n {error && <span className=\"error\">{error}</span>}\n </>\n )\n}\n","import type {ComponentType, FC, HTMLProps} from 'react'\n\nimport {DefaultField} from './default-field'\nimport type {FieldComponentProps, FieldState, FormDataProps, FormField} from './types'\n\ninterface FormRendererProps extends HTMLProps<HTMLFormElement> {\n formData?: FormDataProps\n // Function to get field state for a given field name\n getFieldState?: (fieldName: string) => FieldState\n // Function to get field error for a given field name\n getFieldError?: (fieldName: string) => string | undefined\n // Override default field components\n fieldComponents?: Record<string, ComponentType<FieldComponentProps>>\n}\n\nexport const FormRenderer: FC<FormRendererProps> = (props) => {\n const {\n formData,\n getFieldState = (name) => ({\n value: undefined,\n onChange: () => {},\n name, // Pass name to field for native form handling\n }),\n getFieldError,\n fieldComponents = {},\n children,\n } = props\n const renderField = (field: FormField) => {\n const CustomComponent = fieldComponents[field.type]\n const fieldState = getFieldState(field.name)\n const error = getFieldError?.(field.name)\n\n if (CustomComponent) {\n return <CustomComponent field={field} fieldState={fieldState} error={error} />\n }\n\n return <DefaultField field={field} fieldState={fieldState} error={error} />\n }\n const elProps = Object.assign({}, props)\n delete elProps.formData\n delete elProps.getFieldState\n delete elProps.getFieldError\n delete elProps.fieldComponents\n\n return (\n <form {...elProps} id={elProps.id ?? formData?.id?.current}>\n {formData?.fields?.map((field) => (\n <div key={field._key} className=\"form-field\">\n {renderField(field)}\n </div>\n ))}\n\n {children}\n\n {renderField({\n type: 'submit',\n name: 'submit',\n label: formData?.submitButton?.text || 'Submit',\n })}\n </form>\n )\n}\n","import {FaWpforms} from 'react-icons/fa'\nimport {defineField, defineType} from 'sanity'\n\nexport const formType = defineType({\n name: 'form',\n title: 'Form',\n type: 'document',\n icon: FaWpforms,\n fields: [\n defineField({\n name: 'title',\n title: 'Form Title',\n type: 'string',\n description: 'Internal title for the form',\n validation: (Rule) => Rule.required(),\n }),\n defineField({\n name: 'id',\n title: 'Form ID',\n type: 'slug',\n options: {\n source: 'title',\n },\n // validation: (Rule) => Rule.required(),\n }),\n defineField({\n name: 'fields',\n title: 'Form Fields',\n type: 'array',\n of: [{type: 'formField'}],\n }),\n defineField({\n name: 'submitButton',\n title: 'Submit Button',\n type: 'object',\n fields: [\n defineField({\n name: 'text',\n title: 'Button Text',\n type: 'string',\n initialValue: 'Submit',\n }),\n // defineField({\n // name: 'position',\n // title: 'Button Position',\n // type: 'string',\n // options: {\n // list: ['left', 'center', 'right'],\n // },\n // initialValue: 'center',\n // }),\n ],\n }),\n ],\n})\n","import {LuTextCursorInput} from 'react-icons/lu'\nimport {defineField, defineType} from 'sanity'\n\ninterface ValidationContextDocument {\n fields?: Array<{\n name: string\n type?: string\n }>\n}\n// Validation options by field type\nexport const validationTypesByFieldType = {\n checkbox: ['minSelectedCount', 'maxSelectedCount', 'custom'],\n color: ['custom'],\n date: ['minDate', 'maxDate', 'custom'],\n 'datetime-local': ['minDate', 'maxDate', 'custom'],\n email: ['pattern', 'custom'],\n file: ['maxSize', 'fileType', 'custom'],\n hidden: ['custom'],\n number: ['min', 'max', 'custom'],\n // password: ['minLength', 'pattern', 'custom'],\n radio: ['custom'],\n range: ['min', 'max', 'step', 'custom'],\n select: ['custom'],\n tel: ['pattern', 'custom'],\n text: ['minLength', 'maxLength', 'pattern', 'custom'],\n textarea: ['minLength', 'maxLength', 'custom'],\n time: ['custom'],\n url: ['pattern', 'custom'],\n}\nexport const formFieldType = defineType({\n name: 'formField',\n title: 'Form Field',\n type: 'object',\n icon: LuTextCursorInput,\n fields: [\n defineField({\n name: 'type',\n title: 'Field Type',\n type: 'string',\n options: {\n list: Object.keys(validationTypesByFieldType).map((type) => {\n const title = (fieldType: string) => {\n switch (fieldType) {\n case 'datetime-local':\n return 'Date & Time'\n case 'textarea':\n return 'Text Area'\n case 'tel':\n return 'Phone Number'\n default:\n return fieldType.charAt(0).toUpperCase() + fieldType.slice(1)\n }\n }\n return {title: title(type), value: type}\n }),\n },\n }),\n defineField({\n name: 'label',\n title: 'Field Label',\n type: 'string',\n }),\n defineField({\n name: 'name',\n title: 'Field Name',\n type: 'string',\n description:\n 'Must start with a letter and contain only letters, numbers, underscores, or hyphens. Must be unique within the form.',\n validation: (Rule) =>\n Rule.required().custom((name, context) => {\n if (!name) {\n return 'Required'\n }\n // Check format (HTML ID/name rules)\n if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name)) {\n return 'Field name must start with a letter and contain only letters, numbers, underscores, or hyphens'\n }\n\n // Check uniqueness across all fields\n const doc = context.document as ValidationContextDocument\n const allFieldNames = doc?.fields?.map((field) => field.name) || []\n\n // Count occurrences of this name\n const nameCount = allFieldNames.filter((n) => n === name).length\n\n // If we find more than one occurrence (including current field), it's not unique\n if (nameCount > 1) {\n return 'Field name must be unique across all form fields'\n }\n\n // Check for reserved HTML form attributes\n const reservedNames = [\n 'action',\n 'method',\n 'target',\n 'enctype',\n 'accept-charset',\n 'autocomplete',\n 'novalidate',\n 'rel',\n 'submit',\n 'reset',\n ]\n if (reservedNames.includes(name.toLowerCase())) {\n return 'This name is reserved for HTML form attributes. Please choose a different name.'\n }\n\n return true\n }),\n }),\n defineField({\n name: 'required',\n title: 'Required',\n type: 'boolean',\n initialValue: false,\n }),\n // defineField({\n // name: 'validation',\n // title: 'Validation Rules',\n // type: 'array',\n // of: [\n // {\n // type: 'object',\n // fields: [\n // defineField({\n // name: 'type',\n // title: 'Validation Type',\n // type: 'string',\n\n // hidden: ({parent}) => !parent?.type,\n // options: {\n // // TODO: I think this needs to be a custom input component?\n // // list: ({parent}) => (parent?.type ? validationTypesByFieldType[parent.type] : []),\n // list: [],\n // },\n // }),\n // defineField({\n // name: 'value',\n // title: 'Value',\n // type: 'string',\n // }),\n // defineField({\n // name: 'message',\n // title: 'Error Message',\n // type: 'string',\n // }),\n // ],\n // },\n // ],\n // }),\n defineField({\n name: 'choices',\n title: 'Choices',\n type: 'array',\n hidden: ({parent}) => {\n return !['select', 'radio', 'checkbox'].includes(parent?.type)\n },\n of: [\n {\n type: 'object',\n fields: [\n defineField({\n name: 'label',\n title: 'Label',\n type: 'string',\n }),\n defineField({\n name: 'value',\n title: 'Value',\n type: 'string',\n }),\n ],\n },\n ],\n }),\n defineField({\n name: 'options',\n title: 'Field Options',\n type: 'object',\n hidden: ({parent}) => {\n return ['select', 'radio', 'checkbox', 'file'].includes(parent?.type)\n },\n fields: [\n defineField({\n name: 'placeholder',\n title: 'Placeholder',\n type: 'string',\n }),\n defineField({\n name: 'defaultValue',\n title: 'Default Value',\n type: 'string',\n }),\n ],\n }),\n ],\n preview: {\n select: {\n label: 'label',\n name: 'name',\n type: 'type',\n },\n prepare({label, name, type}) {\n return {\n title: label || name,\n subtitle: type,\n }\n },\n },\n})\n","import type {SchemaTypeDefinition} from 'sanity'\n\nimport {formType} from './form'\nimport {formFieldType} from './form-field'\n\nexport const schema: {types: SchemaTypeDefinition[]} = {\n types: [formType, formFieldType],\n}\n","import {definePlugin} from 'sanity'\n\n// import {structureTool} from 'sanity/structure'\nimport {FormRenderer} from './components/form-renderer'\nimport {schema} from './schema-types'\n// import {defaultDocumentNode} from './structure'\n\n/**\n * Usage in `sanity.config.ts` (or .js)\n *\n * ```ts\n * import {defineConfig} from 'sanity'\n * import {formSchema} from '@sanity/form-toolkit'\n *\n * export default defineConfig({\n * // ...\n * plugins: [formSchema()],\n * })\n * ```\n */\nexport type {FormDataProps} from './components/types'\nexport {FormRenderer}\nexport const formSchema = definePlugin(() => {\n return {\n name: 'form-toolkit_form-schema',\n schema,\n // plugins: [structureTool({defaultDocumentNode})],\n }\n})\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../../src/form-schema/schema-types/form.ts","../../src/form-schema/components/validation-type.tsx","../../src/form-schema/schema-types/form-field.ts","../../src/form-schema/schema-types/index.ts","../../src/form-schema/index.ts"],"sourcesContent":["import {FaWpforms} from 'react-icons/fa'\nimport {defineField, defineType, type SchemaTypeDefinition} from 'sanity'\n\nimport type {FieldsOption} from '..'\n\nexport const formType = (fields: FieldsOption): SchemaTypeDefinition => {\n // const fieldsOf =\n // fields && fields.length ? [{type: 'formField'}, ...fields] : [{type: 'formField'}]\n return defineType({\n name: 'form',\n title: 'Form',\n type: 'document',\n icon: FaWpforms,\n fields: [\n defineField({\n name: 'title',\n title: 'Form Title',\n type: 'string',\n description: 'Internal title for the form',\n validation: (Rule) => Rule.required(),\n }),\n defineField({\n name: 'id',\n title: 'Form ID',\n type: 'slug',\n options: {\n source: 'title',\n },\n // validation: (Rule) => Rule.required(),\n }),\n defineField({\n name: 'fields',\n title: 'Form Fields',\n type: 'array',\n of: [{type: 'formField'}, ...fields],\n }),\n defineField({\n name: 'submitButton',\n title: 'Submit Button',\n type: 'object',\n fields: [\n defineField({\n name: 'text',\n title: 'Button Text',\n type: 'string',\n initialValue: 'Submit',\n }),\n ],\n }),\n ],\n })\n}\n","import type {StringInputProps} from 'sanity'\nimport {useFormValue} from 'sanity'\n\nimport type {FormField} from '../../form-renderer/components/types'\nimport {validationTypesByFieldType} from '../schema-types/form-field'\n\nexport const ValidationType = (props: StringInputProps) => {\n const {type} = useFormValue([...props.path.slice(0, 2)]) as FormField\n if (!type) return props.renderDefault(props)\n if (props.schemaType?.options) {\n props.schemaType.options.list = validationTypesByFieldType[type]\n }\n return props.renderDefault(props)\n}\n","import {LuTextCursorInput} from 'react-icons/lu'\nimport {defineField, defineType} from 'sanity'\n\nimport {ValidationType} from '../components/validation-type'\ninterface ValidationContextDocument {\n fields?: Array<{\n name: string\n type?: string\n }>\n}\n\n// Validation options by field type\nexport const validationTypesByFieldType: Record<string, string[]> = {\n checkbox: ['minSelectedCount', 'maxSelectedCount'],\n color: [],\n date: ['minDate', 'maxDate'],\n 'datetime-local': ['minDate', 'maxDate'],\n email: ['pattern'],\n file: ['maxSize', 'fileType'],\n hidden: [],\n number: ['min', 'max'],\n // password: ['minLength', 'pattern'],\n radio: [],\n range: ['min', 'max', 'step'],\n select: [],\n tel: ['pattern'],\n text: ['minLength', 'maxLength', 'pattern'],\n textarea: ['minLength', 'maxLength'],\n time: [],\n url: ['pattern'],\n}\nexport const formFieldType = defineType({\n name: 'formField',\n title: 'Form Field',\n type: 'object',\n icon: LuTextCursorInput,\n fields: [\n defineField({\n name: 'type',\n title: 'Field Type',\n type: 'string',\n options: {\n list: Object.keys(validationTypesByFieldType).map((type) => {\n const title = (fieldType: string) => {\n switch (fieldType) {\n case 'datetime-local':\n return 'Date & Time'\n case 'textarea':\n return 'Text Area'\n case 'tel':\n return 'Phone Number'\n default:\n return fieldType.charAt(0).toUpperCase() + fieldType.slice(1)\n }\n }\n return {title: title(type), value: type}\n }),\n },\n }),\n defineField({\n name: 'label',\n title: 'Field Label',\n type: 'string',\n }),\n defineField({\n name: 'name',\n title: 'Field Name',\n type: 'string',\n description:\n 'Must start with a letter and contain only letters, numbers, underscores, or hyphens. Must be unique within the form.',\n validation: (Rule) =>\n Rule.required().custom((name, context) => {\n if (!name) {\n return 'Required'\n }\n // Check format (HTML ID/name rules)\n if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name)) {\n return 'Field name must start with a letter and contain only letters, numbers, underscores, or hyphens'\n }\n\n // Check uniqueness across all fields\n const doc = context.document as ValidationContextDocument\n const allFieldNames = doc?.fields?.map((field) => field.name) || []\n\n // Count occurrences of this name\n const nameCount = allFieldNames.filter((n) => n === name).length\n\n // If we find more than one occurrence (including current field), it's not unique\n if (nameCount > 1) {\n return 'Field name must be unique across all form fields'\n }\n\n // Check for reserved HTML form attributes\n const reservedNames = [\n 'action',\n 'method',\n 'target',\n 'enctype',\n 'accept-charset',\n 'autocomplete',\n 'novalidate',\n 'rel',\n 'submit',\n 'reset',\n ]\n if (reservedNames.includes(name.toLowerCase())) {\n return 'This name is reserved for HTML form attributes. Please choose a different name.'\n }\n\n return true\n }),\n }),\n defineField({\n name: 'required',\n title: 'Required',\n type: 'boolean',\n initialValue: false,\n }),\n defineField({\n name: 'validation',\n title: 'Validation Rules',\n type: 'array',\n hidden: ({parent}) => {\n if (!parent?.type) return true\n const validationTypes = validationTypesByFieldType[parent.type]\n return !validationTypes || validationTypes.length === 0\n },\n of: [\n {\n type: 'object',\n fields: [\n defineField({\n name: 'type',\n title: 'Validation Type',\n type: 'string',\n options: {\n // TODO: I think this needs to be a custom input component?\n // list: ({parent}) => (parent?.type ? validationTypesByFieldType[parent.type] : []),\n list: [],\n },\n components: {\n input: ValidationType,\n },\n }),\n defineField({\n name: 'value',\n title: 'Value',\n type: 'string',\n }),\n defineField({\n name: 'message',\n title: 'Error Message',\n type: 'string',\n }),\n ],\n preview: {\n select: {\n title: 'type',\n subtitle: 'value',\n },\n },\n },\n ],\n }),\n defineField({\n name: 'choices',\n title: 'Choices',\n type: 'array',\n hidden: ({parent}) => {\n return !['select', 'radio', 'checkbox'].includes(parent?.type)\n },\n of: [\n {\n type: 'object',\n fields: [\n defineField({\n name: 'label',\n title: 'Label',\n type: 'string',\n }),\n defineField({\n name: 'value',\n title: 'Value',\n type: 'string',\n }),\n ],\n },\n ],\n }),\n defineField({\n name: 'options',\n title: 'Field Options',\n type: 'object',\n hidden: ({parent}) => {\n return ['select', 'radio', 'checkbox', 'file'].includes(parent?.type)\n },\n fields: [\n defineField({\n name: 'placeholder',\n title: 'Placeholder',\n type: 'string',\n }),\n defineField({\n name: 'defaultValue',\n title: 'Default Value',\n type: 'string',\n }),\n ],\n }),\n ],\n preview: {\n select: {\n label: 'label',\n name: 'name',\n type: 'type',\n },\n prepare({label, name, type}) {\n return {\n title: label || name,\n subtitle: type,\n }\n },\n },\n})\n","import type {SchemaTypeDefinition} from 'sanity'\n\nimport type {FieldsOption} from '..'\nimport {formType} from './form'\nimport {formFieldType} from './form-field'\n\nexport const schema = (fields: FieldsOption): {types: SchemaTypeDefinition[]} => {\n return {types: [formType(fields), formFieldType]}\n}\n","import {definePlugin, type FieldDefinition} from 'sanity'\n\n// import {structureTool} from 'sanity/structure'\n// import {FormRenderer} from './components/form-renderer'\nimport {schema} from './schema-types'\n// import {defaultDocumentNode} from './structure'\n\n/**\n * Usage in `sanity.config.ts` (or .js)\n *\n * ```ts\n * import {defineConfig} from 'sanity'\n * import {formSchema} from '@sanity/form-toolkit'\n *\n * export default defineConfig({\n * // ...\n * plugins: [formSchema()],\n * })\n * ```\n */\nexport type FieldsOption = Array<FieldDefinition>\ninterface FormSchemaPluginOptions {\n /**\n * Array of field definitions to be used in the form schema.\n */\n fields?: FieldsOption\n}\n\nexport const formSchema = definePlugin((options: FormSchemaPluginOptions | undefined) => {\n return {\n name: 'form-toolkit_form-schema',\n schema: schema(options?.fields ?? []),\n // plugins: [structureTool({defaultDocumentNode})],\n }\n})\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/form-toolkit",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "Tool kit for integrating forms with a Sanity Studio",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -39,6 +39,11 @@
|
|
|
39
39
|
"import": "./dist/form-schema/index.mjs",
|
|
40
40
|
"default": "./dist/form-schema/index.js"
|
|
41
41
|
},
|
|
42
|
+
"./form-renderer": {
|
|
43
|
+
"source": "./src/form-renderer/index.ts",
|
|
44
|
+
"import": "./dist/form-renderer/index.mjs",
|
|
45
|
+
"default": "./dist/form-renderer/index.js"
|
|
46
|
+
},
|
|
42
47
|
"./package.json": "./package.json"
|
|
43
48
|
},
|
|
44
49
|
"main": "./dist/index.js",
|
|
@@ -55,6 +60,9 @@
|
|
|
55
60
|
],
|
|
56
61
|
"form-schema": [
|
|
57
62
|
"./dist/form-schema/index.d.ts"
|
|
63
|
+
],
|
|
64
|
+
"form-renderer": [
|
|
65
|
+
"./dist/form-renderer/index.d.ts"
|
|
58
66
|
]
|
|
59
67
|
}
|
|
60
68
|
},
|
|
@@ -3,9 +3,12 @@ import type {ChangeEvent, FC, LegacyRef} from 'react'
|
|
|
3
3
|
import type {FieldComponentProps} from './types'
|
|
4
4
|
|
|
5
5
|
export const DefaultField: FC<FieldComponentProps> = ({field, fieldState, error}) => {
|
|
6
|
-
const {type, label, name, options = {}, choices = []} = field
|
|
6
|
+
const {type, label, name, options = {}, choices = [], validation = []} = field
|
|
7
7
|
if (!type || !name) return null
|
|
8
|
-
|
|
8
|
+
const validationRules = validation.reduce((acc: Record<string, string>, v) => {
|
|
9
|
+
acc[v.type] = v.value
|
|
10
|
+
return acc
|
|
11
|
+
}, {})
|
|
9
12
|
const {value, onChange, onBlur, ref} = fieldState
|
|
10
13
|
|
|
11
14
|
const handleChange = (
|
|
@@ -34,10 +37,11 @@ export const DefaultField: FC<FieldComponentProps> = ({field, fieldState, error}
|
|
|
34
37
|
<textarea
|
|
35
38
|
ref={ref as LegacyRef<HTMLTextAreaElement>}
|
|
36
39
|
name={name}
|
|
37
|
-
value={value ?? ''}
|
|
38
40
|
onChange={handleChange}
|
|
39
41
|
onBlur={onBlur}
|
|
40
42
|
placeholder={options.placeholder}
|
|
43
|
+
{...validationRules}
|
|
44
|
+
value={value ?? ''}
|
|
41
45
|
/>
|
|
42
46
|
)
|
|
43
47
|
|
|
@@ -48,6 +52,7 @@ export const DefaultField: FC<FieldComponentProps> = ({field, fieldState, error}
|
|
|
48
52
|
name={name}
|
|
49
53
|
value={value ?? ''}
|
|
50
54
|
onChange={handleChange}
|
|
55
|
+
{...validationRules}
|
|
51
56
|
onBlur={onBlur}
|
|
52
57
|
>
|
|
53
58
|
{choices?.map((choice, i) => (
|
|
@@ -69,6 +74,7 @@ export const DefaultField: FC<FieldComponentProps> = ({field, fieldState, error}
|
|
|
69
74
|
checked={value === choice.value}
|
|
70
75
|
onChange={handleChange}
|
|
71
76
|
onBlur={onBlur}
|
|
77
|
+
{...validationRules}
|
|
72
78
|
/>
|
|
73
79
|
{choice.label}
|
|
74
80
|
</label>
|
|
@@ -85,6 +91,7 @@ export const DefaultField: FC<FieldComponentProps> = ({field, fieldState, error}
|
|
|
85
91
|
checked={Array.isArray(value) ? value.includes(choice.value) : value === choice.value}
|
|
86
92
|
onChange={(e) => handleCheckboxChange(e, choice.value)}
|
|
87
93
|
onBlur={onBlur}
|
|
94
|
+
{...validationRules}
|
|
88
95
|
/>
|
|
89
96
|
{choice.label}
|
|
90
97
|
</label>
|
|
@@ -98,6 +105,7 @@ export const DefaultField: FC<FieldComponentProps> = ({field, fieldState, error}
|
|
|
98
105
|
name={name}
|
|
99
106
|
value={value ?? options.defaultValue ?? ''}
|
|
100
107
|
onChange={handleChange}
|
|
108
|
+
{...validationRules}
|
|
101
109
|
onBlur={onBlur}
|
|
102
110
|
placeholder={options.placeholder}
|
|
103
111
|
/>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type {StringInputProps} from 'sanity'
|
|
2
|
+
import {useFormValue} from 'sanity'
|
|
3
|
+
|
|
4
|
+
import type {FormField} from '../../form-renderer/components/types'
|
|
5
|
+
import {validationTypesByFieldType} from '../schema-types/form-field'
|
|
6
|
+
|
|
7
|
+
export const ValidationType = (props: StringInputProps) => {
|
|
8
|
+
const {type} = useFormValue([...props.path.slice(0, 2)]) as FormField
|
|
9
|
+
if (!type) return props.renderDefault(props)
|
|
10
|
+
if (props.schemaType?.options) {
|
|
11
|
+
props.schemaType.options.list = validationTypesByFieldType[type]
|
|
12
|
+
}
|
|
13
|
+
return props.renderDefault(props)
|
|
14
|
+
}
|
package/src/form-schema/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {definePlugin} from 'sanity'
|
|
1
|
+
import {definePlugin, type FieldDefinition} from 'sanity'
|
|
2
2
|
|
|
3
3
|
// import {structureTool} from 'sanity/structure'
|
|
4
|
-
import {FormRenderer} from './components/form-renderer'
|
|
4
|
+
// import {FormRenderer} from './components/form-renderer'
|
|
5
5
|
import {schema} from './schema-types'
|
|
6
6
|
// import {defaultDocumentNode} from './structure'
|
|
7
7
|
|
|
@@ -18,12 +18,18 @@ import {schema} from './schema-types'
|
|
|
18
18
|
* })
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
|
-
export type
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
export type FieldsOption = Array<FieldDefinition>
|
|
22
|
+
interface FormSchemaPluginOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Array of field definitions to be used in the form schema.
|
|
25
|
+
*/
|
|
26
|
+
fields?: FieldsOption
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const formSchema = definePlugin((options: FormSchemaPluginOptions | undefined) => {
|
|
24
30
|
return {
|
|
25
31
|
name: 'form-toolkit_form-schema',
|
|
26
|
-
schema,
|
|
32
|
+
schema: schema(options?.fields ?? []),
|
|
27
33
|
// plugins: [structureTool({defaultDocumentNode})],
|
|
28
34
|
}
|
|
29
35
|
})
|