@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 CHANGED
@@ -170,15 +170,45 @@ import {formSchema} from '@sanity/form-toolkit/form-schema'
170
170
 
171
171
  export default defineConfig({
172
172
  //...
173
- plugins: [formSchema()],
173
+ plugins: [
174
+ formSchema({
175
+ // Optionally, use your own schemas for additional formFields
176
+ fields: [
177
+ defineField({
178
+ name: 'myField',
179
+ type: 'myObjectType',
180
+ }),
181
+ ],
182
+ }),
183
+ ],
184
+ })
185
+ ```
186
+
187
+ This will create a "form" document type.
188
+ Then, add a field to your schema with type `form`
189
+
190
+ ```ts
191
+ // ./src/schemaTypes/page.ts
192
+ import {defineField, defineType} from 'sanity'
193
+
194
+ export default defineType({
195
+ name: 'page',
196
+ type: 'document',
197
+ fields: [
198
+ defineField({
199
+ name: 'form',
200
+ type: 'reference',
201
+ to: [{type: 'form'}],
202
+ }),
203
+ ],
174
204
  })
175
205
  ```
176
206
 
177
- Then pass a `form` document to the `FormRenderer` component
207
+ Finally, pass a `form` document to the `FormRenderer` component
178
208
 
179
209
  ```tsx
180
210
  import React, {type FC} from 'react'
181
- import {FormRenderer, type FormDataProps} from '@sanity/form-toolkit/form-schema'
211
+ import {FormRenderer, type FormDataProps} from '@sanity/form-toolkit/form-renderer'
182
212
 
183
213
  interface NativeFormExampleProps {
184
214
  formData: FormDataProps
@@ -0,0 +1,66 @@
1
+ import type {ComponentType} from 'react'
2
+ import type {FC} from 'react'
3
+ import type {HTMLProps} from 'react'
4
+
5
+ declare type FieldChoice = {
6
+ label: string
7
+ value: string
8
+ }
9
+
10
+ declare interface FieldComponentProps {
11
+ field: FormField
12
+ fieldState: FieldState
13
+ error?: string
14
+ }
15
+
16
+ declare type FieldOptions = {
17
+ placeholder?: string
18
+ defaultValue?: string
19
+ }
20
+
21
+ declare interface FieldState {
22
+ value?: string | number | readonly string[]
23
+ onChange: (value: unknown) => void
24
+ onBlur?: () => void
25
+ ref?: unknown
26
+ }
27
+
28
+ export declare type FormDataProps = {
29
+ title: string
30
+ id: {
31
+ current: string
32
+ }
33
+ fields?: FormField[]
34
+ submitButton?: {
35
+ text: string
36
+ position: 'left' | 'center' | 'right'
37
+ }
38
+ }
39
+
40
+ declare type FormField = {
41
+ type: string
42
+ label?: string
43
+ name: string
44
+ required?: boolean
45
+ validation?: ValidationRule[]
46
+ options?: FieldOptions
47
+ choices?: FieldChoice[]
48
+ _key?: string
49
+ }
50
+
51
+ export declare const FormRenderer: FC<FormRendererProps>
52
+
53
+ declare interface FormRendererProps extends HTMLProps<HTMLFormElement> {
54
+ formData?: FormDataProps
55
+ getFieldState?: (fieldName: string) => FieldState
56
+ getFieldError?: (fieldName: string) => string | undefined
57
+ fieldComponents?: Record<string, ComponentType<FieldComponentProps>>
58
+ }
59
+
60
+ declare type ValidationRule = {
61
+ type: string
62
+ value: string
63
+ message: string
64
+ }
65
+
66
+ export {}
@@ -0,0 +1,66 @@
1
+ import type {ComponentType} from 'react'
2
+ import type {FC} from 'react'
3
+ import type {HTMLProps} from 'react'
4
+
5
+ declare type FieldChoice = {
6
+ label: string
7
+ value: string
8
+ }
9
+
10
+ declare interface FieldComponentProps {
11
+ field: FormField
12
+ fieldState: FieldState
13
+ error?: string
14
+ }
15
+
16
+ declare type FieldOptions = {
17
+ placeholder?: string
18
+ defaultValue?: string
19
+ }
20
+
21
+ declare interface FieldState {
22
+ value?: string | number | readonly string[]
23
+ onChange: (value: unknown) => void
24
+ onBlur?: () => void
25
+ ref?: unknown
26
+ }
27
+
28
+ export declare type FormDataProps = {
29
+ title: string
30
+ id: {
31
+ current: string
32
+ }
33
+ fields?: FormField[]
34
+ submitButton?: {
35
+ text: string
36
+ position: 'left' | 'center' | 'right'
37
+ }
38
+ }
39
+
40
+ declare type FormField = {
41
+ type: string
42
+ label?: string
43
+ name: string
44
+ required?: boolean
45
+ validation?: ValidationRule[]
46
+ options?: FieldOptions
47
+ choices?: FieldChoice[]
48
+ _key?: string
49
+ }
50
+
51
+ export declare const FormRenderer: FC<FormRendererProps>
52
+
53
+ declare interface FormRendererProps extends HTMLProps<HTMLFormElement> {
54
+ formData?: FormDataProps
55
+ getFieldState?: (fieldName: string) => FieldState
56
+ getFieldError?: (fieldName: string) => string | undefined
57
+ fieldComponents?: Record<string, ComponentType<FieldComponentProps>>
58
+ }
59
+
60
+ declare type ValidationRule = {
61
+ type: string
62
+ value: string
63
+ message: string
64
+ }
65
+
66
+ export {}
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: !0 });
3
+ var jsxRuntime = require("react/jsx-runtime");
4
+ const DefaultField = ({ field, fieldState, error }) => {
5
+ const { type, label, name, options = {}, choices = [], validation = [] } = field;
6
+ if (!type || !name) return null;
7
+ const validationRules = validation.reduce((acc, v) => (acc[v.type] = v.value, acc), {}), { value, onChange, onBlur, ref } = fieldState, handleChange = (e) => {
8
+ onChange(e.target.value);
9
+ }, handleCheckboxChange = (e, choiceValue) => {
10
+ if (Array.isArray(value)) {
11
+ const newValue = e.target.checked ? [...value, choiceValue] : value.filter((v) => v !== choiceValue);
12
+ onChange(newValue);
13
+ } else
14
+ onChange(e.target.checked ? choiceValue : "");
15
+ }, renderInput = () => {
16
+ switch (type) {
17
+ case "submit":
18
+ return /* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", children: label || "Submit" });
19
+ case "textarea":
20
+ return /* @__PURE__ */ jsxRuntime.jsx(
21
+ "textarea",
22
+ {
23
+ ref,
24
+ name,
25
+ onChange: handleChange,
26
+ onBlur,
27
+ placeholder: options.placeholder,
28
+ ...validationRules,
29
+ value: value ?? ""
30
+ }
31
+ );
32
+ case "select":
33
+ return /* @__PURE__ */ jsxRuntime.jsx(
34
+ "select",
35
+ {
36
+ ref,
37
+ name,
38
+ value: value ?? "",
39
+ onChange: handleChange,
40
+ ...validationRules,
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
+ ...validationRules
58
+ }
59
+ ),
60
+ choice.label
61
+ ] }, i));
62
+ case "checkbox":
63
+ return choices?.map((choice, i) => /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
64
+ /* @__PURE__ */ jsxRuntime.jsx(
65
+ "input",
66
+ {
67
+ type: "checkbox",
68
+ name,
69
+ ref,
70
+ value: choice.value,
71
+ checked: Array.isArray(value) ? value.includes(choice.value) : value === choice.value,
72
+ onChange: (e) => handleCheckboxChange(e, choice.value),
73
+ onBlur,
74
+ ...validationRules
75
+ }
76
+ ),
77
+ choice.label
78
+ ] }, i));
79
+ default:
80
+ return /* @__PURE__ */ jsxRuntime.jsx(
81
+ "input",
82
+ {
83
+ type,
84
+ ref,
85
+ name,
86
+ value: value ?? options.defaultValue ?? "",
87
+ onChange: handleChange,
88
+ ...validationRules,
89
+ onBlur,
90
+ placeholder: options.placeholder
91
+ }
92
+ );
93
+ }
94
+ };
95
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
96
+ label && !["hidden", "submit"].includes(type) && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: name, children: label }),
97
+ renderInput(),
98
+ error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "error", children: error })
99
+ ] });
100
+ }, FormRenderer = (props) => {
101
+ const {
102
+ formData,
103
+ getFieldState = (name) => ({
104
+ value: void 0,
105
+ onChange: () => {
106
+ },
107
+ name
108
+ // Pass name to field for native form handling
109
+ }),
110
+ getFieldError,
111
+ fieldComponents = {},
112
+ children
113
+ } = props, renderField = (field) => {
114
+ const CustomComponent = fieldComponents[field.type], fieldState = getFieldState(field.name), error = getFieldError?.(field.name);
115
+ return CustomComponent ? /* @__PURE__ */ jsxRuntime.jsx(CustomComponent, { field, fieldState, error }) : /* @__PURE__ */ jsxRuntime.jsx(DefaultField, { field, fieldState, error });
116
+ }, elProps = Object.assign({}, props);
117
+ 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: [
118
+ formData?.fields?.map((field) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "form-field", children: renderField(field) }, field._key)),
119
+ children,
120
+ renderField({
121
+ type: "submit",
122
+ name: "submit",
123
+ label: formData?.submitButton?.text || "Submit"
124
+ })
125
+ ] });
126
+ };
127
+ exports.FormRenderer = FormRenderer;
128
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../src/form-renderer/components/default-field.tsx","../../src/form-renderer/components/form-renderer.tsx"],"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 = [], validation = []} = field\n if (!type || !name) return null\n const validationRules = validation.reduce((acc: Record<string, string>, v) => {\n acc[v.type] = v.value\n return acc\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 onChange={handleChange}\n onBlur={onBlur}\n placeholder={options.placeholder}\n {...validationRules}\n value={value ?? ''}\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 {...validationRules}\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 {...validationRules}\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 {...validationRules}\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 {...validationRules}\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"],"names":["jsx","jsxs","Fragment"],"mappings":"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"}
@@ -0,0 +1,128 @@
1
+ import { jsxs, Fragment, jsx } from "react/jsx-runtime";
2
+ const DefaultField = ({ field, fieldState, error }) => {
3
+ const { type, label, name, options = {}, choices = [], validation = [] } = field;
4
+ if (!type || !name) return null;
5
+ const validationRules = validation.reduce((acc, v) => (acc[v.type] = v.value, acc), {}), { value, onChange, onBlur, ref } = fieldState, handleChange = (e) => {
6
+ onChange(e.target.value);
7
+ }, handleCheckboxChange = (e, choiceValue) => {
8
+ if (Array.isArray(value)) {
9
+ const newValue = e.target.checked ? [...value, choiceValue] : value.filter((v) => v !== choiceValue);
10
+ onChange(newValue);
11
+ } else
12
+ onChange(e.target.checked ? choiceValue : "");
13
+ }, renderInput = () => {
14
+ switch (type) {
15
+ case "submit":
16
+ return /* @__PURE__ */ jsx("button", { type: "submit", children: label || "Submit" });
17
+ case "textarea":
18
+ return /* @__PURE__ */ jsx(
19
+ "textarea",
20
+ {
21
+ ref,
22
+ name,
23
+ onChange: handleChange,
24
+ onBlur,
25
+ placeholder: options.placeholder,
26
+ ...validationRules,
27
+ value: value ?? ""
28
+ }
29
+ );
30
+ case "select":
31
+ return /* @__PURE__ */ jsx(
32
+ "select",
33
+ {
34
+ ref,
35
+ name,
36
+ value: value ?? "",
37
+ onChange: handleChange,
38
+ ...validationRules,
39
+ onBlur,
40
+ children: choices?.map((choice, i) => /* @__PURE__ */ jsx("option", { value: choice.value, children: choice.label }, i))
41
+ }
42
+ );
43
+ case "radio":
44
+ return choices?.map((choice, i) => /* @__PURE__ */ jsxs("label", { children: [
45
+ /* @__PURE__ */ jsx(
46
+ "input",
47
+ {
48
+ type: "radio",
49
+ name,
50
+ ref,
51
+ value: choice.value,
52
+ checked: value === choice.value,
53
+ onChange: handleChange,
54
+ onBlur,
55
+ ...validationRules
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
+ ...validationRules
73
+ }
74
+ ),
75
+ choice.label
76
+ ] }, i));
77
+ default:
78
+ return /* @__PURE__ */ jsx(
79
+ "input",
80
+ {
81
+ type,
82
+ ref,
83
+ name,
84
+ value: value ?? options.defaultValue ?? "",
85
+ onChange: handleChange,
86
+ ...validationRules,
87
+ onBlur,
88
+ placeholder: options.placeholder
89
+ }
90
+ );
91
+ }
92
+ };
93
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
94
+ label && !["hidden", "submit"].includes(type) && /* @__PURE__ */ jsx("label", { htmlFor: name, children: label }),
95
+ renderInput(),
96
+ error && /* @__PURE__ */ jsx("span", { className: "error", children: error })
97
+ ] });
98
+ }, FormRenderer = (props) => {
99
+ const {
100
+ formData,
101
+ getFieldState = (name) => ({
102
+ value: void 0,
103
+ onChange: () => {
104
+ },
105
+ name
106
+ // Pass name to field for native form handling
107
+ }),
108
+ getFieldError,
109
+ fieldComponents = {},
110
+ children
111
+ } = props, renderField = (field) => {
112
+ const CustomComponent = fieldComponents[field.type], fieldState = getFieldState(field.name), error = getFieldError?.(field.name);
113
+ return CustomComponent ? /* @__PURE__ */ jsx(CustomComponent, { field, fieldState, error }) : /* @__PURE__ */ jsx(DefaultField, { field, fieldState, error });
114
+ }, elProps = Object.assign({}, props);
115
+ return delete elProps.formData, delete elProps.getFieldState, delete elProps.getFieldError, delete elProps.fieldComponents, /* @__PURE__ */ jsxs("form", { ...elProps, id: elProps.id ?? formData?.id?.current, children: [
116
+ formData?.fields?.map((field) => /* @__PURE__ */ jsx("div", { className: "form-field", children: renderField(field) }, field._key)),
117
+ children,
118
+ renderField({
119
+ type: "submit",
120
+ name: "submit",
121
+ label: formData?.submitButton?.text || "Submit"
122
+ })
123
+ ] });
124
+ };
125
+ export {
126
+ FormRenderer
127
+ };
128
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":["../../src/form-renderer/components/default-field.tsx","../../src/form-renderer/components/form-renderer.tsx"],"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 = [], validation = []} = field\n if (!type || !name) return null\n const validationRules = validation.reduce((acc: Record<string, string>, v) => {\n acc[v.type] = v.value\n return acc\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 onChange={handleChange}\n onBlur={onBlur}\n placeholder={options.placeholder}\n {...validationRules}\n value={value ?? ''}\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 {...validationRules}\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 {...validationRules}\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 {...validationRules}\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 {...validationRules}\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"],"names":[],"mappings":"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"}
@@ -1,69 +1,28 @@
1
- import type {ComponentType} from 'react'
2
- import type {FC} from 'react'
3
- import type {HTMLProps} from 'react'
1
+ import {FieldDefinition} from 'sanity'
4
2
  import {Plugin as Plugin_2} from 'sanity'
5
3
 
6
- declare type FieldChoice = {
7
- label: string
8
- value: string
9
- }
10
-
11
- declare interface FieldComponentProps {
12
- field: FormField
13
- fieldState: FieldState
14
- error?: string
15
- }
16
-
17
- declare type FieldOptions = {
18
- placeholder?: string
19
- defaultValue?: string
20
- }
21
-
22
- declare interface FieldState {
23
- value?: string | number | readonly string[]
24
- onChange: (value: unknown) => void
25
- onBlur?: () => void
26
- ref?: unknown
27
- }
28
-
29
- export declare type FormDataProps = {
30
- title: string
31
- id: {
32
- current: string
33
- }
34
- fields?: FormField[]
35
- submitButton?: {
36
- text: string
37
- position: 'left' | 'center' | 'right'
38
- }
39
- }
40
-
41
- declare type FormField = {
42
- type: string
43
- label?: string
44
- name: string
45
- required?: boolean
46
- validation?: ValidationRule[]
47
- options?: FieldOptions
48
- choices?: FieldChoice[]
49
- _key?: string
50
- }
51
-
52
- export declare const FormRenderer: FC<FormRendererProps>
53
-
54
- declare interface FormRendererProps extends HTMLProps<HTMLFormElement> {
55
- formData?: FormDataProps
56
- getFieldState?: (fieldName: string) => FieldState
57
- getFieldError?: (fieldName: string) => string | undefined
58
- fieldComponents?: Record<string, ComponentType<FieldComponentProps>>
59
- }
60
-
61
- export declare const formSchema: Plugin_2<void>
62
-
63
- declare type ValidationRule = {
64
- type: string
65
- value: string
66
- message: string
4
+ /**
5
+ * Usage in `sanity.config.ts` (or .js)
6
+ *
7
+ * ```ts
8
+ * import {defineConfig} from 'sanity'
9
+ * import {formSchema} from '@sanity/form-toolkit'
10
+ *
11
+ * export default defineConfig({
12
+ * // ...
13
+ * plugins: [formSchema()],
14
+ * })
15
+ * ```
16
+ */
17
+ export declare type FieldsOption = Array<FieldDefinition>
18
+
19
+ export declare const formSchema: Plugin_2<FormSchemaPluginOptions | undefined>
20
+
21
+ declare interface FormSchemaPluginOptions {
22
+ /**
23
+ * Array of field definitions to be used in the form schema.
24
+ */
25
+ fields?: FieldsOption
67
26
  }
68
27
 
69
28
  export {}
@@ -1,69 +1,28 @@
1
- import type {ComponentType} from 'react'
2
- import type {FC} from 'react'
3
- import type {HTMLProps} from 'react'
1
+ import {FieldDefinition} from 'sanity'
4
2
  import {Plugin as Plugin_2} from 'sanity'
5
3
 
6
- declare type FieldChoice = {
7
- label: string
8
- value: string
9
- }
10
-
11
- declare interface FieldComponentProps {
12
- field: FormField
13
- fieldState: FieldState
14
- error?: string
15
- }
16
-
17
- declare type FieldOptions = {
18
- placeholder?: string
19
- defaultValue?: string
20
- }
21
-
22
- declare interface FieldState {
23
- value?: string | number | readonly string[]
24
- onChange: (value: unknown) => void
25
- onBlur?: () => void
26
- ref?: unknown
27
- }
28
-
29
- export declare type FormDataProps = {
30
- title: string
31
- id: {
32
- current: string
33
- }
34
- fields?: FormField[]
35
- submitButton?: {
36
- text: string
37
- position: 'left' | 'center' | 'right'
38
- }
39
- }
40
-
41
- declare type FormField = {
42
- type: string
43
- label?: string
44
- name: string
45
- required?: boolean
46
- validation?: ValidationRule[]
47
- options?: FieldOptions
48
- choices?: FieldChoice[]
49
- _key?: string
50
- }
51
-
52
- export declare const FormRenderer: FC<FormRendererProps>
53
-
54
- declare interface FormRendererProps extends HTMLProps<HTMLFormElement> {
55
- formData?: FormDataProps
56
- getFieldState?: (fieldName: string) => FieldState
57
- getFieldError?: (fieldName: string) => string | undefined
58
- fieldComponents?: Record<string, ComponentType<FieldComponentProps>>
59
- }
60
-
61
- export declare const formSchema: Plugin_2<void>
62
-
63
- declare type ValidationRule = {
64
- type: string
65
- value: string
66
- message: string
4
+ /**
5
+ * Usage in `sanity.config.ts` (or .js)
6
+ *
7
+ * ```ts
8
+ * import {defineConfig} from 'sanity'
9
+ * import {formSchema} from '@sanity/form-toolkit'
10
+ *
11
+ * export default defineConfig({
12
+ * // ...
13
+ * plugins: [formSchema()],
14
+ * })
15
+ * ```
16
+ */
17
+ export declare type FieldsOption = Array<FieldDefinition>
18
+
19
+ export declare const formSchema: Plugin_2<FormSchemaPluginOptions | undefined>
20
+
21
+ declare interface FormSchemaPluginOptions {
22
+ /**
23
+ * Array of field definitions to be used in the form schema.
24
+ */
25
+ fields?: FieldsOption
67
26
  }
68
27
 
69
28
  export {}