@sanity/form-toolkit 2.0.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,119 +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 DefaultField = ({ field, fieldState, error }) => {
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 "textarea":
19
- return /* @__PURE__ */ jsx(
20
- "textarea",
21
- {
22
- ref,
23
- name,
24
- value: value ?? "",
25
- onChange: handleChange,
26
- onBlur,
27
- placeholder: options.placeholder
28
- }
29
- );
30
- case "select":
31
- return /* @__PURE__ */ jsx(
32
- "select",
33
- {
34
- ref,
35
- name,
36
- value: value ?? "",
37
- onChange: handleChange,
38
- onBlur,
39
- children: choices?.map((choice, i) => /* @__PURE__ */ jsx("option", { value: choice.value, children: choice.label }, i))
40
- }
41
- );
42
- case "radio":
43
- return choices?.map((choice, i) => /* @__PURE__ */ jsxs("label", { children: [
44
- /* @__PURE__ */ jsx(
45
- "input",
46
- {
47
- type: "radio",
48
- name,
49
- ref,
50
- value: choice.value,
51
- checked: value === choice.value,
52
- onChange: handleChange,
53
- onBlur
54
- }
55
- ),
56
- choice.label
57
- ] }, i));
58
- case "checkbox":
59
- return choices?.map((choice, i) => /* @__PURE__ */ jsxs("label", { children: [
60
- /* @__PURE__ */ jsx(
61
- "input",
62
- {
63
- type: "checkbox",
64
- name,
65
- ref,
66
- value: choice.value,
67
- checked: Array.isArray(value) ? value.includes(choice.value) : value === choice.value,
68
- onChange: (e) => handleCheckboxChange(e, choice.value),
69
- onBlur
70
- }
71
- ),
72
- choice.label
73
- ] }, i));
74
- default:
75
- return /* @__PURE__ */ jsx(
76
- "input",
77
- {
78
- type,
79
- ref,
80
- name,
81
- value: value ?? options.defaultValue ?? "",
82
- onChange: handleChange,
83
- onBlur,
84
- placeholder: options.placeholder
85
- }
86
- );
87
- }
88
- };
89
- return /* @__PURE__ */ jsxs(Fragment, { children: [
90
- label && type != "hidden" && /* @__PURE__ */ jsx("label", { htmlFor: name, children: label }),
91
- renderInput(),
92
- error && /* @__PURE__ */ jsx("span", { className: "error", children: error })
93
- ] });
94
- }, FormRenderer = (props) => {
95
- const {
96
- formData,
97
- getFieldState = (name) => ({
98
- value: void 0,
99
- onChange: () => {
100
- },
101
- name
102
- // Pass name to field for native form handling
103
- }),
104
- getFieldError,
105
- fieldComponents = {},
106
- children
107
- } = props, renderField = (field) => {
108
- const CustomComponent = fieldComponents[field.type], fieldState = getFieldState(field.name), error = getFieldError?.(field.name);
109
- return CustomComponent ? /* @__PURE__ */ jsx(CustomComponent, { field, fieldState, error }) : /* @__PURE__ */ jsx(DefaultField, { field, fieldState, error });
110
- }, elProps = Object.assign({}, props);
111
- return delete elProps.formData, delete elProps.getFieldState, delete elProps.getFieldError, delete elProps.fieldComponents, /* @__PURE__ */ jsxs("form", { ...elProps, id: elProps.id ?? formData?.id?.current, children: [
112
- formData?.fields?.map((field) => /* @__PURE__ */ jsx("div", { className: "form-field", children: renderField(field) }, field._key)),
113
- children,
114
- /* @__PURE__ */ jsx("button", { type: "submit", children: formData?.submitButton?.text || "Submit" })
115
- ] });
116
- }, formType = defineType({
4
+ const formType = (fields) => defineType({
117
5
  name: "form",
118
6
  title: "Form",
119
7
  type: "document",
@@ -139,7 +27,7 @@ const DefaultField = ({ field, fieldState, error }) => {
139
27
  name: "fields",
140
28
  title: "Form Fields",
141
29
  type: "array",
142
- of: [{ type: "formField" }]
30
+ of: [{ type: "formField" }, ...fields]
143
31
  }),
144
32
  defineField({
145
33
  name: "submitButton",
@@ -152,36 +40,30 @@ const DefaultField = ({ field, fieldState, error }) => {
152
40
  type: "string",
153
41
  initialValue: "Submit"
154
42
  })
155
- // defineField({
156
- // name: 'position',
157
- // title: 'Button Position',
158
- // type: 'string',
159
- // options: {
160
- // list: ['left', 'center', 'right'],
161
- // },
162
- // initialValue: 'center',
163
- // }),
164
43
  ]
165
44
  })
166
45
  ]
167
- }), validationTypesByFieldType = {
168
- checkbox: ["minSelectedCount", "maxSelectedCount", "custom"],
169
- color: ["custom"],
170
- date: ["minDate", "maxDate", "custom"],
171
- "datetime-local": ["minDate", "maxDate", "custom"],
172
- email: ["pattern", "custom"],
173
- file: ["maxSize", "fileType", "custom"],
174
- hidden: ["custom"],
175
- number: ["min", "max", "custom"],
176
- // password: ['minLength', 'pattern', 'custom'],
177
- radio: ["custom"],
178
- range: ["min", "max", "step", "custom"],
179
- select: ["custom"],
180
- tel: ["pattern", "custom"],
181
- text: ["minLength", "maxLength", "pattern", "custom"],
182
- textarea: ["minLength", "maxLength", "custom"],
183
- time: ["custom"],
184
- url: ["pattern", "custom"]
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"]
185
67
  }, formFieldType = defineType({
186
68
  name: "formField",
187
69
  title: "Form Field",
@@ -236,39 +118,52 @@ const DefaultField = ({ field, fieldState, error }) => {
236
118
  type: "boolean",
237
119
  initialValue: !1
238
120
  }),
239
- // defineField({
240
- // name: 'validation',
241
- // title: 'Validation Rules',
242
- // type: 'array',
243
- // of: [
244
- // {
245
- // type: 'object',
246
- // fields: [
247
- // defineField({
248
- // name: 'type',
249
- // title: 'Validation Type',
250
- // type: 'string',
251
- // hidden: ({parent}) => !parent?.type,
252
- // options: {
253
- // // TODO: I think this needs to be a custom input component?
254
- // // list: ({parent}) => (parent?.type ? validationTypesByFieldType[parent.type] : []),
255
- // list: [],
256
- // },
257
- // }),
258
- // defineField({
259
- // name: 'value',
260
- // title: 'Value',
261
- // type: 'string',
262
- // }),
263
- // defineField({
264
- // name: 'message',
265
- // title: 'Error Message',
266
- // type: 'string',
267
- // }),
268
- // ],
269
- // },
270
- // ],
271
- // }),
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
+ }),
272
167
  defineField({
273
168
  name: "choices",
274
169
  title: "Choices",
@@ -324,15 +219,12 @@ const DefaultField = ({ field, fieldState, error }) => {
324
219
  };
325
220
  }
326
221
  }
327
- }), schema = {
328
- types: [formType, formFieldType]
329
- }, formSchema = definePlugin(() => ({
222
+ }), schema = (fields) => ({ types: [formType(fields), formFieldType] }), formSchema = definePlugin(({ fields = [] }) => ({
330
223
  name: "form-toolkit_form-schema",
331
- schema
224
+ schema: schema(fields)
332
225
  // plugins: [structureTool({defaultDocumentNode})],
333
226
  }));
334
227
  export {
335
- FormRenderer,
336
228
  formSchema
337
229
  };
338
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 '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 && type != 'hidden' && <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 <button type=\"submit\">{formData?.submitButton?.text || 'Submit'}</button>\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(({fields = []}: FormSchemaPluginOptions) => {\n return {\n name: 'form-toolkit_form-schema',\n schema: schema(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.0.0",
3
+ "version": "2.2.0",
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 = (
@@ -27,15 +30,18 @@ export const DefaultField: FC<FieldComponentProps> = ({field, fieldState, error}
27
30
 
28
31
  const renderInput = () => {
29
32
  switch (type) {
33
+ case 'submit':
34
+ return <button type="submit">{label || 'Submit'}</button>
30
35
  case 'textarea':
31
36
  return (
32
37
  <textarea
33
38
  ref={ref as LegacyRef<HTMLTextAreaElement>}
34
39
  name={name}
35
- value={value ?? ''}
36
40
  onChange={handleChange}
37
41
  onBlur={onBlur}
38
42
  placeholder={options.placeholder}
43
+ {...validationRules}
44
+ value={value ?? ''}
39
45
  />
40
46
  )
41
47
 
@@ -46,6 +52,7 @@ export const DefaultField: FC<FieldComponentProps> = ({field, fieldState, error}
46
52
  name={name}
47
53
  value={value ?? ''}
48
54
  onChange={handleChange}
55
+ {...validationRules}
49
56
  onBlur={onBlur}
50
57
  >
51
58
  {choices?.map((choice, i) => (
@@ -67,6 +74,7 @@ export const DefaultField: FC<FieldComponentProps> = ({field, fieldState, error}
67
74
  checked={value === choice.value}
68
75
  onChange={handleChange}
69
76
  onBlur={onBlur}
77
+ {...validationRules}
70
78
  />
71
79
  {choice.label}
72
80
  </label>
@@ -83,6 +91,7 @@ export const DefaultField: FC<FieldComponentProps> = ({field, fieldState, error}
83
91
  checked={Array.isArray(value) ? value.includes(choice.value) : value === choice.value}
84
92
  onChange={(e) => handleCheckboxChange(e, choice.value)}
85
93
  onBlur={onBlur}
94
+ {...validationRules}
86
95
  />
87
96
  {choice.label}
88
97
  </label>
@@ -96,6 +105,7 @@ export const DefaultField: FC<FieldComponentProps> = ({field, fieldState, error}
96
105
  name={name}
97
106
  value={value ?? options.defaultValue ?? ''}
98
107
  onChange={handleChange}
108
+ {...validationRules}
99
109
  onBlur={onBlur}
100
110
  placeholder={options.placeholder}
101
111
  />
@@ -105,7 +115,7 @@ export const DefaultField: FC<FieldComponentProps> = ({field, fieldState, error}
105
115
 
106
116
  return (
107
117
  <>
108
- {label && type != 'hidden' && <label htmlFor={name}>{label}</label>}
118
+ {label && !['hidden', 'submit'].includes(type) && <label htmlFor={name}>{label}</label>}
109
119
  {renderInput()}
110
120
  {error && <span className="error">{error}</span>}
111
121
  </>
@@ -52,7 +52,11 @@ export const FormRenderer: FC<FormRendererProps> = (props) => {
52
52
 
53
53
  {children}
54
54
 
55
- <button type="submit">{formData?.submitButton?.text || 'Submit'}</button>
55
+ {renderField({
56
+ type: 'submit',
57
+ name: 'submit',
58
+ label: formData?.submitButton?.text || 'Submit',
59
+ })}
56
60
  </form>
57
61
  )
58
62
  }
@@ -1,4 +1,3 @@
1
- // types.ts
2
1
  export type ValidationRule = {
3
2
  type: string
4
3
  value: string
@@ -19,11 +18,11 @@ export type FormField = {
19
18
  type: string
20
19
  label?: string
21
20
  name: string
22
- required: boolean
21
+ required?: boolean
23
22
  validation?: ValidationRule[]
24
23
  options?: FieldOptions
25
24
  choices?: FieldChoice[]
26
- _key: string
25
+ _key?: string
27
26
  }
28
27
 
29
28
  export type FormDataProps = {
@@ -32,7 +31,6 @@ export type FormDataProps = {
32
31
  current: string
33
32
  }
34
33
  fields?: FormField[]
35
-
36
34
  submitButton?: {
37
35
  text: string
38
36
  position: 'left' | 'center' | 'right'
@@ -0,0 +1,4 @@
1
+ import {FormRenderer} from './components/form-renderer'
2
+ import type {FormDataProps} from './components/types'
3
+ export type {FormDataProps}
4
+ export {FormRenderer}
@@ -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
+ }
@@ -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 {FormDataProps} from './components/types'
22
- export {FormRenderer}
23
- export const formSchema = definePlugin(() => {
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(({fields = []}: FormSchemaPluginOptions) => {
24
30
  return {
25
31
  name: 'form-toolkit_form-schema',
26
- schema,
32
+ schema: schema(fields),
27
33
  // plugins: [structureTool({defaultDocumentNode})],
28
34
  }
29
35
  })