@saas-ui/forms 3.0.0-alpha.1 → 3.0.0-alpha.3
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/CHANGELOG.md +21 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -28
- package/dist/ajv/index.d.mts +0 -35
- package/dist/ajv/index.d.ts +0 -35
- package/dist/ajv/index.js +0 -57
- package/dist/ajv/index.js.map +0 -1
- package/dist/ajv/index.mjs +0 -30
- package/dist/ajv/index.mjs.map +0 -1
- package/src/array-field.tsx +0 -266
- package/src/auto-form.tsx +0 -71
- package/src/base-field.tsx +0 -69
- package/src/create-field.tsx +0 -171
- package/src/create-form.tsx +0 -100
- package/src/create-step-form.tsx +0 -118
- package/src/default-fields.tsx +0 -233
- package/src/display-field.tsx +0 -46
- package/src/display-if.tsx +0 -69
- package/src/field-resolver.ts +0 -66
- package/src/field.tsx +0 -44
- package/src/fields-context.tsx +0 -33
- package/src/fields.tsx +0 -93
- package/src/form-context.tsx +0 -80
- package/src/form-layout.tsx +0 -59
- package/src/form.tsx +0 -252
- package/src/index.ts +0 -310
- package/src/object-field.tsx +0 -58
- package/src/step-form.tsx +0 -191
- package/src/submit-button.tsx +0 -71
- package/src/types.ts +0 -242
- package/src/use-array-field.tsx +0 -158
- package/src/use-form.tsx +0 -24
- package/src/use-step-form.tsx +0 -201
- package/src/utils.ts +0 -35
- package/src/watch-field.tsx +0 -37
package/dist/ajv/index.js.map
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"sources":["../../ajv/src/index.ts","../../ajv/src/ajv-resolver.ts","../../ajv/src/create-ajv-form.ts"],"sourcesContent":["export { ajvFieldResolver, ajvResolver } from './ajv-resolver'\nexport { createAjvForm } from './create-ajv-form'\nexport type { CreateAjvFormProps } from './create-ajv-form'\nexport type { JTDDataType, JTDSchemaType } from 'ajv/dist/jtd'\n\nimport { createAjvForm } from './create-ajv-form'\n\nexport const Form = createAjvForm()\n","import { ajvResolver } from '@hookform/resolvers/ajv'\nimport { objectFieldResolver } from '@saas-ui/forms'\nimport { JSONSchemaType } from 'ajv'\n\nexport { ajvResolver }\n\nexport const ajvFieldResolver = (schema: JSONSchemaType<unknown>) => {\n return objectFieldResolver(schema.properties)\n}\n","import {\n CreateFormProps,\n FormProps,\n WithFields,\n createForm,\n} from '@saas-ui/forms'\nimport { JTDDataType } from 'ajv/dist/jtd'\n\nimport { ajvFieldResolver, ajvResolver } from './ajv-resolver'\n\ntype ResolverArgs = Parameters<typeof ajvResolver>\n\nexport interface CreateAjvFormProps<FieldDefs>\n extends CreateFormProps<FieldDefs> {\n schemaOptions?: ResolverArgs[1]\n resolverOptions?: ResolverArgs[2]\n}\n\ntype ParseJsonSchema<T> = T extends { type: 'object' }\n ? JTDDataType<T> extends infer R\n ? R extends object\n ? R\n : never\n : never\n : never\n\nexport type AjvFormType<\n FieldDefs,\n ExtraProps = object,\n JsonSchema extends Record<string, any> = Record<string, any>,\n> = (<\n TSchema extends JsonSchema = JsonSchema,\n TFieldValues extends ParseJsonSchema<TSchema> = ParseJsonSchema<TSchema>,\n TContext extends object = object,\n>(\n props: WithFields<FormProps<TSchema, TFieldValues, TContext>, FieldDefs> & {\n ref?: React.ForwardedRef<HTMLFormElement>\n } & ExtraProps,\n) => React.ReactElement) & {\n displayName?: string\n id?: string\n}\n\n/**\n * Create a Form component with AJV validation that accepts JSON Type Definition schema\n *\n * @see Docs https://saas-ui.dev/docs/components/forms/form\n * @see https://ajv.js.org/json-type-definition.html\n */\nexport function createAjvForm<FieldDefs>(\n options?: CreateAjvFormProps<FieldDefs>,\n) {\n return createForm({\n resolver: (schema: any) =>\n ajvResolver(schema, options?.schemaOptions, options?.resolverOptions),\n fieldResolver: ajvFieldResolver,\n ...options,\n }) as AjvFormType<FieldDefs>\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,iBAA4B;AAC5B,mBAAoC;AAK7B,IAAM,mBAAmB,CAAC,WAAoC;AACnE,aAAO,kCAAoB,OAAO,UAAU;AAC9C;;;ACRA,IAAAA,gBAKO;AA4CA,SAAS,cACd,SACA;AACA,aAAO,0BAAW;AAAA,IAChB,UAAU,CAAC,eACT,wBAAY,QAAQ,mCAAS,eAAe,mCAAS,eAAe;AAAA,IACtE,eAAe;AAAA,IACf,GAAG;AAAA,EACL,CAAC;AACH;;;AFnDO,IAAM,OAAO,cAAc;","names":["import_forms"]}
|
package/dist/ajv/index.mjs
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
'use client'
|
2
|
-
|
3
|
-
// ajv/src/ajv-resolver.ts
|
4
|
-
import { ajvResolver } from "@hookform/resolvers/ajv";
|
5
|
-
import { objectFieldResolver } from "@saas-ui/forms";
|
6
|
-
var ajvFieldResolver = (schema) => {
|
7
|
-
return objectFieldResolver(schema.properties);
|
8
|
-
};
|
9
|
-
|
10
|
-
// ajv/src/create-ajv-form.ts
|
11
|
-
import {
|
12
|
-
createForm
|
13
|
-
} from "@saas-ui/forms";
|
14
|
-
function createAjvForm(options) {
|
15
|
-
return createForm({
|
16
|
-
resolver: (schema) => ajvResolver(schema, options == null ? void 0 : options.schemaOptions, options == null ? void 0 : options.resolverOptions),
|
17
|
-
fieldResolver: ajvFieldResolver,
|
18
|
-
...options
|
19
|
-
});
|
20
|
-
}
|
21
|
-
|
22
|
-
// ajv/src/index.ts
|
23
|
-
var Form = createAjvForm();
|
24
|
-
export {
|
25
|
-
Form,
|
26
|
-
ajvFieldResolver,
|
27
|
-
ajvResolver,
|
28
|
-
createAjvForm
|
29
|
-
};
|
30
|
-
//# sourceMappingURL=index.mjs.map
|
package/dist/ajv/index.mjs.map
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"sources":["../../ajv/src/ajv-resolver.ts","../../ajv/src/create-ajv-form.ts","../../ajv/src/index.ts"],"sourcesContent":["import { ajvResolver } from '@hookform/resolvers/ajv'\nimport { objectFieldResolver } from '@saas-ui/forms'\nimport { JSONSchemaType } from 'ajv'\n\nexport { ajvResolver }\n\nexport const ajvFieldResolver = (schema: JSONSchemaType<unknown>) => {\n return objectFieldResolver(schema.properties)\n}\n","import {\n CreateFormProps,\n FormProps,\n WithFields,\n createForm,\n} from '@saas-ui/forms'\nimport { JTDDataType } from 'ajv/dist/jtd'\n\nimport { ajvFieldResolver, ajvResolver } from './ajv-resolver'\n\ntype ResolverArgs = Parameters<typeof ajvResolver>\n\nexport interface CreateAjvFormProps<FieldDefs>\n extends CreateFormProps<FieldDefs> {\n schemaOptions?: ResolverArgs[1]\n resolverOptions?: ResolverArgs[2]\n}\n\ntype ParseJsonSchema<T> = T extends { type: 'object' }\n ? JTDDataType<T> extends infer R\n ? R extends object\n ? R\n : never\n : never\n : never\n\nexport type AjvFormType<\n FieldDefs,\n ExtraProps = object,\n JsonSchema extends Record<string, any> = Record<string, any>,\n> = (<\n TSchema extends JsonSchema = JsonSchema,\n TFieldValues extends ParseJsonSchema<TSchema> = ParseJsonSchema<TSchema>,\n TContext extends object = object,\n>(\n props: WithFields<FormProps<TSchema, TFieldValues, TContext>, FieldDefs> & {\n ref?: React.ForwardedRef<HTMLFormElement>\n } & ExtraProps,\n) => React.ReactElement) & {\n displayName?: string\n id?: string\n}\n\n/**\n * Create a Form component with AJV validation that accepts JSON Type Definition schema\n *\n * @see Docs https://saas-ui.dev/docs/components/forms/form\n * @see https://ajv.js.org/json-type-definition.html\n */\nexport function createAjvForm<FieldDefs>(\n options?: CreateAjvFormProps<FieldDefs>,\n) {\n return createForm({\n resolver: (schema: any) =>\n ajvResolver(schema, options?.schemaOptions, options?.resolverOptions),\n fieldResolver: ajvFieldResolver,\n ...options,\n }) as AjvFormType<FieldDefs>\n}\n","export { ajvFieldResolver, ajvResolver } from './ajv-resolver'\nexport { createAjvForm } from './create-ajv-form'\nexport type { CreateAjvFormProps } from './create-ajv-form'\nexport type { JTDDataType, JTDSchemaType } from 'ajv/dist/jtd'\n\nimport { createAjvForm } from './create-ajv-form'\n\nexport const Form = createAjvForm()\n"],"mappings":";;;AAAA,SAAS,mBAAmB;AAC5B,SAAS,2BAA2B;AAK7B,IAAM,mBAAmB,CAAC,WAAoC;AACnE,SAAO,oBAAoB,OAAO,UAAU;AAC9C;;;ACRA;AAAA,EAIE;AAAA,OACK;AA4CA,SAAS,cACd,SACA;AACA,SAAO,WAAW;AAAA,IAChB,UAAU,CAAC,WACT,YAAY,QAAQ,mCAAS,eAAe,mCAAS,eAAe;AAAA,IACtE,eAAe;AAAA,IACf,GAAG;AAAA,EACL,CAAC;AACH;;;ACnDO,IAAM,OAAO,cAAc;","names":[]}
|
package/src/array-field.tsx
DELETED
@@ -1,266 +0,0 @@
|
|
1
|
-
import React, { forwardRef } from 'react'
|
2
|
-
|
3
|
-
import { Button, ButtonProps, chakra } from '@chakra-ui/react'
|
4
|
-
import type { MaybeRenderProp } from '@saas-ui/core/utils'
|
5
|
-
import { MinusIcon, PlusIcon } from '@saas-ui/react/icons'
|
6
|
-
import { FieldPath, FieldValues } from 'react-hook-form'
|
7
|
-
|
8
|
-
import { BaseField } from './base-field'
|
9
|
-
import { useFieldProps } from './form-context'
|
10
|
-
import { FormLayout, FormLayoutProps } from './form-layout'
|
11
|
-
import { BaseFieldProps } from './types'
|
12
|
-
import {
|
13
|
-
ArrayFieldOptions,
|
14
|
-
ArrayFieldProvider,
|
15
|
-
ArrayFieldRowProvider,
|
16
|
-
UseArrayFieldReturn,
|
17
|
-
useArrayField,
|
18
|
-
useArrayFieldAddButton,
|
19
|
-
useArrayFieldContext,
|
20
|
-
useArrayFieldRemoveButton,
|
21
|
-
useArrayFieldRow,
|
22
|
-
useArrayFieldRowContext,
|
23
|
-
} from './use-array-field'
|
24
|
-
import { mapNestedFields } from './utils'
|
25
|
-
|
26
|
-
export interface ArrayFieldButtonProps extends ButtonProps {}
|
27
|
-
|
28
|
-
interface ArrayField {
|
29
|
-
id: string
|
30
|
-
[key: string]: unknown
|
31
|
-
}
|
32
|
-
|
33
|
-
interface ArrayFieldRowProps extends FormLayoutProps {
|
34
|
-
/**
|
35
|
-
* The array index
|
36
|
-
*/
|
37
|
-
index: number
|
38
|
-
/**
|
39
|
-
* The fields
|
40
|
-
*/
|
41
|
-
children: React.ReactNode
|
42
|
-
}
|
43
|
-
|
44
|
-
/**
|
45
|
-
* Render prop component, to get access to the internal fields state. Must be a child of ArrayFieldContainer.
|
46
|
-
*
|
47
|
-
* @see Docs https://saas-ui.dev/docs/components/forms/array-field
|
48
|
-
*/
|
49
|
-
export const ArrayFieldRow: React.FC<ArrayFieldRowProps> = ({
|
50
|
-
children,
|
51
|
-
index,
|
52
|
-
...rowFieldsProps
|
53
|
-
}) => {
|
54
|
-
return (
|
55
|
-
<ArrayFieldRowContainer index={index}>
|
56
|
-
<ArrayFieldRowFields {...rowFieldsProps}>{children}</ArrayFieldRowFields>
|
57
|
-
<ArrayFieldRemoveButton />
|
58
|
-
</ArrayFieldRowContainer>
|
59
|
-
)
|
60
|
-
}
|
61
|
-
|
62
|
-
ArrayFieldRow.displayName = 'ArrayFieldRow'
|
63
|
-
|
64
|
-
export interface ArrayFieldRowFieldsProps extends FormLayoutProps {
|
65
|
-
/**
|
66
|
-
* The fields
|
67
|
-
*/
|
68
|
-
children: React.ReactNode
|
69
|
-
}
|
70
|
-
/**
|
71
|
-
* Add the name prefix to the fields and acts as a horizontal form layout by default.
|
72
|
-
*
|
73
|
-
* @see Docs https://saas-ui.dev/docs/components/forms/array-field
|
74
|
-
*/
|
75
|
-
export const ArrayFieldRowFields: React.FC<ArrayFieldRowFieldsProps> = ({
|
76
|
-
children,
|
77
|
-
...layoutProps
|
78
|
-
}) => {
|
79
|
-
const { name } = useArrayFieldRowContext()
|
80
|
-
return (
|
81
|
-
<FormLayout flex="1" mr="2" {...layoutProps}>
|
82
|
-
{mapNestedFields(name, children)}
|
83
|
-
</FormLayout>
|
84
|
-
)
|
85
|
-
}
|
86
|
-
|
87
|
-
ArrayFieldRowFields.displayName = 'ArrayFieldRowFields'
|
88
|
-
|
89
|
-
/**
|
90
|
-
* The row container component providers row context.
|
91
|
-
*
|
92
|
-
* @see Docs https://saas-ui.dev/docs/components/forms/array-field
|
93
|
-
*/
|
94
|
-
export const ArrayFieldRowContainer: React.FC<ArrayFieldRowProps> = ({
|
95
|
-
children,
|
96
|
-
index,
|
97
|
-
...rest
|
98
|
-
}) => {
|
99
|
-
const context = useArrayFieldRow({ index })
|
100
|
-
|
101
|
-
const styles = {
|
102
|
-
display: 'flex',
|
103
|
-
flexDirection: 'row',
|
104
|
-
alignItems: 'flex-end',
|
105
|
-
width: '100%',
|
106
|
-
mb: 4,
|
107
|
-
}
|
108
|
-
|
109
|
-
return (
|
110
|
-
<ArrayFieldRowProvider value={context}>
|
111
|
-
<chakra.div {...rest} css={styles}>
|
112
|
-
{children}
|
113
|
-
</chakra.div>
|
114
|
-
</ArrayFieldRowProvider>
|
115
|
-
)
|
116
|
-
}
|
117
|
-
|
118
|
-
ArrayFieldRowContainer.displayName = 'ArrayFieldRowContainer'
|
119
|
-
|
120
|
-
/**
|
121
|
-
* The default remove button.
|
122
|
-
*
|
123
|
-
* @see Docs https://saas-ui.dev/docs/components/forms/array-field
|
124
|
-
*/
|
125
|
-
export const ArrayFieldRemoveButton: React.FC<ArrayFieldButtonProps> = (
|
126
|
-
props,
|
127
|
-
) => {
|
128
|
-
return (
|
129
|
-
<Button aria-label="Remove row" {...useArrayFieldRemoveButton()} {...props}>
|
130
|
-
{props.children || <MinusIcon />}
|
131
|
-
</Button>
|
132
|
-
)
|
133
|
-
}
|
134
|
-
|
135
|
-
ArrayFieldRemoveButton.displayName = 'ArrayFieldRemoveButton'
|
136
|
-
|
137
|
-
/**
|
138
|
-
* The default add button.
|
139
|
-
*
|
140
|
-
* @see Docs https://saas-ui.dev/docs/components/forms/array-field
|
141
|
-
*/
|
142
|
-
export const ArrayFieldAddButton: React.FC<ArrayFieldButtonProps> = (props) => {
|
143
|
-
return (
|
144
|
-
<Button
|
145
|
-
aria-label="Add row"
|
146
|
-
float="right"
|
147
|
-
{...useArrayFieldAddButton()}
|
148
|
-
{...props}
|
149
|
-
>
|
150
|
-
{props.children || <PlusIcon />}
|
151
|
-
</Button>
|
152
|
-
)
|
153
|
-
}
|
154
|
-
|
155
|
-
ArrayFieldAddButton.displayName = 'ArrayFieldAddButton'
|
156
|
-
|
157
|
-
export interface ArrayFieldProps<
|
158
|
-
TFieldValues extends FieldValues = FieldValues,
|
159
|
-
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
160
|
-
> extends ArrayFieldOptions<TFieldValues, TName>,
|
161
|
-
Omit<
|
162
|
-
BaseFieldProps<TFieldValues, TName>,
|
163
|
-
'name' | 'defaultValue' | 'children'
|
164
|
-
> {
|
165
|
-
children: MaybeRenderProp<ArrayField[]>
|
166
|
-
}
|
167
|
-
|
168
|
-
/**
|
169
|
-
* The wrapper component that composes the default ArrayField functionality.
|
170
|
-
*
|
171
|
-
* @see Docs https://saas-ui.dev/docs/components/forms/array-field
|
172
|
-
*/
|
173
|
-
export const ArrayField = forwardRef(
|
174
|
-
(props: ArrayFieldProps, ref: React.ForwardedRef<UseArrayFieldReturn>) => {
|
175
|
-
const { children, ...containerProps } = props
|
176
|
-
|
177
|
-
const rowFn =
|
178
|
-
typeof children === 'function'
|
179
|
-
? children
|
180
|
-
: (fields: ArrayField[]) => (
|
181
|
-
<>
|
182
|
-
{fields.map(({ id }, index: number) => (
|
183
|
-
<ArrayFieldRow key={id} index={index}>
|
184
|
-
{children}
|
185
|
-
</ArrayFieldRow>
|
186
|
-
)) || null}
|
187
|
-
</>
|
188
|
-
)
|
189
|
-
|
190
|
-
return (
|
191
|
-
<ArrayFieldContainer ref={ref} {...containerProps}>
|
192
|
-
<ArrayFieldRows>{rowFn as any}</ArrayFieldRows>
|
193
|
-
<ArrayFieldAddButton />
|
194
|
-
</ArrayFieldContainer>
|
195
|
-
)
|
196
|
-
},
|
197
|
-
) as ((
|
198
|
-
props: ArrayFieldProps & {
|
199
|
-
ref?: React.ForwardedRef<UseArrayFieldReturn>
|
200
|
-
},
|
201
|
-
) => React.ReactElement) & {
|
202
|
-
displayName: string
|
203
|
-
}
|
204
|
-
|
205
|
-
ArrayField.displayName = 'ArrayField'
|
206
|
-
|
207
|
-
export interface ArrayFieldRowsProps {
|
208
|
-
children: (fields: ArrayField[]) => React.ReactElement | null
|
209
|
-
}
|
210
|
-
|
211
|
-
export const ArrayFieldRows = ({
|
212
|
-
children,
|
213
|
-
}: ArrayFieldRowsProps): React.ReactElement | null => {
|
214
|
-
const { fields } = useArrayFieldContext()
|
215
|
-
return children(fields)
|
216
|
-
}
|
217
|
-
|
218
|
-
ArrayFieldRows.displayName = 'ArrayFieldRows'
|
219
|
-
|
220
|
-
export interface ArrayFieldContainerProps
|
221
|
-
extends Omit<ArrayFieldProps, 'children'> {
|
222
|
-
children: React.ReactNode
|
223
|
-
}
|
224
|
-
|
225
|
-
/**
|
226
|
-
* The container component provides context and state management.
|
227
|
-
*
|
228
|
-
* @see Docs https://saas-ui.dev/docs/components/forms/array-field
|
229
|
-
*/
|
230
|
-
export const ArrayFieldContainer = React.forwardRef(
|
231
|
-
(
|
232
|
-
{
|
233
|
-
name,
|
234
|
-
defaultValue,
|
235
|
-
keyName,
|
236
|
-
min,
|
237
|
-
max,
|
238
|
-
children,
|
239
|
-
...fieldProps
|
240
|
-
}: ArrayFieldContainerProps,
|
241
|
-
ref: React.ForwardedRef<UseArrayFieldReturn>,
|
242
|
-
) => {
|
243
|
-
const overrides = useFieldProps(name)
|
244
|
-
|
245
|
-
const context = useArrayField({
|
246
|
-
name,
|
247
|
-
defaultValue,
|
248
|
-
keyName,
|
249
|
-
min: min || (overrides as any)?.min,
|
250
|
-
max: max || (overrides as any)?.max,
|
251
|
-
})
|
252
|
-
|
253
|
-
// This exposes the useArrayField api through the forwarded ref
|
254
|
-
React.useImperativeHandle(ref, () => context, [ref, context])
|
255
|
-
|
256
|
-
return (
|
257
|
-
<ArrayFieldProvider value={context}>
|
258
|
-
<BaseField name={name} {...fieldProps} {...overrides}>
|
259
|
-
{children}
|
260
|
-
</BaseField>
|
261
|
-
</ArrayFieldProvider>
|
262
|
-
)
|
263
|
-
},
|
264
|
-
)
|
265
|
-
|
266
|
-
ArrayFieldContainer.displayName = 'ArrayFieldContainer'
|
package/src/auto-form.tsx
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
import React, { forwardRef } from 'react'
|
2
|
-
|
3
|
-
import { FieldValues } from 'react-hook-form'
|
4
|
-
|
5
|
-
import { AutoFields } from './fields'
|
6
|
-
import { Form, FormProps } from './form'
|
7
|
-
import { FormLayout } from './form-layout'
|
8
|
-
import { SubmitButton } from './submit-button'
|
9
|
-
|
10
|
-
interface AutoFormOptions {
|
11
|
-
/**
|
12
|
-
* The submit button label.
|
13
|
-
* Pass `null` to render no submit button.
|
14
|
-
*/
|
15
|
-
submitLabel?: React.ReactNode
|
16
|
-
/**
|
17
|
-
* The schema.
|
18
|
-
* Supports object schema, Zod, Yup or Ajv (JSON Schema).
|
19
|
-
* @see https://www.saas-ui.dev/docs/forms/auto-form
|
20
|
-
*/
|
21
|
-
schema: any
|
22
|
-
/**
|
23
|
-
* The field resolver.
|
24
|
-
*/
|
25
|
-
fieldResolver?: any
|
26
|
-
}
|
27
|
-
|
28
|
-
export interface AutoFormProps<
|
29
|
-
TFieldValues extends FieldValues,
|
30
|
-
TContext extends object = object,
|
31
|
-
> extends Omit<
|
32
|
-
FormProps<TFieldValues, TContext>,
|
33
|
-
'schema' | 'children' | 'fieldResolver'
|
34
|
-
>,
|
35
|
-
AutoFormOptions {
|
36
|
-
children?: React.ReactNode
|
37
|
-
}
|
38
|
-
/**
|
39
|
-
* The wrapper component that manages context and state.
|
40
|
-
*
|
41
|
-
* @see Docs https://saas-ui.dev/docs/components/forms/auto-form
|
42
|
-
*/
|
43
|
-
export const AutoForm = forwardRef(
|
44
|
-
<
|
45
|
-
TFieldValues extends FieldValues = FieldValues,
|
46
|
-
TContext extends object = object,
|
47
|
-
>(
|
48
|
-
props: AutoFormProps<TFieldValues, TContext>,
|
49
|
-
ref: React.ForwardedRef<HTMLFormElement>,
|
50
|
-
) => {
|
51
|
-
const {
|
52
|
-
schema,
|
53
|
-
submitLabel = 'Submit',
|
54
|
-
fieldResolver,
|
55
|
-
children,
|
56
|
-
...rest
|
57
|
-
} = props
|
58
|
-
|
59
|
-
return (
|
60
|
-
<Form {...rest} schema={schema} ref={ref}>
|
61
|
-
<FormLayout>
|
62
|
-
{<AutoFields schema={schema} fieldResolver={fieldResolver} />}
|
63
|
-
{submitLabel && <SubmitButton>{submitLabel}</SubmitButton>}
|
64
|
-
{children}
|
65
|
-
</FormLayout>
|
66
|
-
</Form>
|
67
|
-
)
|
68
|
-
},
|
69
|
-
)
|
70
|
-
|
71
|
-
AutoForm.displayName = 'AutoForm'
|
package/src/base-field.tsx
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
import * as React from 'react'
|
2
|
-
|
3
|
-
import { Box, Field } from '@chakra-ui/react'
|
4
|
-
import { splitProps } from '@saas-ui/core/utils'
|
5
|
-
import { FormState, get } from 'react-hook-form'
|
6
|
-
|
7
|
-
import { useFormContext } from './form-context'
|
8
|
-
import type { BaseFieldProps } from './types'
|
9
|
-
|
10
|
-
const getError = (name: string, formState: FormState<{ [x: string]: any }>) => {
|
11
|
-
return get(formState.errors, name)
|
12
|
-
}
|
13
|
-
|
14
|
-
const isTouched = (
|
15
|
-
name: string,
|
16
|
-
formState: FormState<{ [x: string]: any }>,
|
17
|
-
) => {
|
18
|
-
return get(formState.touchedFields, name)
|
19
|
-
}
|
20
|
-
|
21
|
-
export const useBaseField = (props: BaseFieldProps) => {
|
22
|
-
const [fieldProps] = splitProps(props, ['name', 'label', 'help', 'hideLabel'])
|
23
|
-
|
24
|
-
const [controlProps] = splitProps(props, [
|
25
|
-
// 'id',
|
26
|
-
// 'orientation',
|
27
|
-
// 'disabled',
|
28
|
-
// 'invalid',
|
29
|
-
// 'readOnly',
|
30
|
-
// 'required',
|
31
|
-
])
|
32
|
-
|
33
|
-
const { formState } = useFormContext()
|
34
|
-
|
35
|
-
const error = getError(fieldProps.name, formState)
|
36
|
-
const touched = isTouched(fieldProps.name, formState)
|
37
|
-
|
38
|
-
return {
|
39
|
-
...fieldProps,
|
40
|
-
controlProps,
|
41
|
-
error,
|
42
|
-
touched,
|
43
|
-
}
|
44
|
-
}
|
45
|
-
|
46
|
-
/**
|
47
|
-
* The default BaseField component
|
48
|
-
* Composes the Chakra UI FormControl component, with FormLabel, FormHelperText and FormErrorMessage.
|
49
|
-
*/
|
50
|
-
export const BaseField: React.FC<BaseFieldProps> = (props) => {
|
51
|
-
const { label, help, hideLabel, error } = useBaseField(props)
|
52
|
-
|
53
|
-
const isInvalid = !!error //|| controlProps.
|
54
|
-
|
55
|
-
return (
|
56
|
-
<Field.Root invalid={isInvalid} {...props}>
|
57
|
-
{label && !hideLabel ? <Field.Label>{label}</Field.Label> : null}
|
58
|
-
<Box width="full">
|
59
|
-
{props.children}
|
60
|
-
{help && !error?.message ? (
|
61
|
-
<Field.HelperText>{help}</Field.HelperText>
|
62
|
-
) : null}
|
63
|
-
{error?.message && <Field.ErrorText>{error?.message}</Field.ErrorText>}
|
64
|
-
</Box>
|
65
|
-
</Field.Root>
|
66
|
-
)
|
67
|
-
}
|
68
|
-
|
69
|
-
BaseField.displayName = 'BaseField'
|
package/src/create-field.tsx
DELETED
@@ -1,171 +0,0 @@
|
|
1
|
-
import React, {
|
2
|
-
type ForwardRefRenderFunction,
|
3
|
-
type PropsWithoutRef,
|
4
|
-
forwardRef,
|
5
|
-
} from 'react'
|
6
|
-
|
7
|
-
import { callAll, mergeRefs, splitProps } from '@saas-ui/core/utils'
|
8
|
-
import { Controller, type RegisterOptions } from 'react-hook-form'
|
9
|
-
|
10
|
-
import { BaseField } from './base-field'
|
11
|
-
import { useFieldsContext } from './fields-context'
|
12
|
-
import { useFormContext } from './form-context'
|
13
|
-
import { BaseFieldProps, GetBaseField } from './types'
|
14
|
-
|
15
|
-
interface CreateFieldProps<ExtraFieldProps extends object = object> {
|
16
|
-
displayName: string
|
17
|
-
hideLabel?: boolean
|
18
|
-
getBaseField: GetBaseField<ExtraFieldProps>
|
19
|
-
}
|
20
|
-
|
21
|
-
const _createField = (
|
22
|
-
InputComponent: React.FC<any>,
|
23
|
-
{ displayName, hideLabel, getBaseField: getBaseFieldProp }: CreateFieldProps,
|
24
|
-
) => {
|
25
|
-
const Field = forwardRef<HTMLDivElement, any>((props, ref) => {
|
26
|
-
const { id, name, label, isRequired, rules } = props
|
27
|
-
|
28
|
-
const inputRules = {
|
29
|
-
required: isRequired,
|
30
|
-
...rules,
|
31
|
-
}
|
32
|
-
|
33
|
-
const fieldContext = useFieldsContext()
|
34
|
-
|
35
|
-
const getBaseField = fieldContext?.getBaseField ?? getBaseFieldProp
|
36
|
-
|
37
|
-
const { extraProps, BaseField } = React.useMemo(
|
38
|
-
() => getBaseField(),
|
39
|
-
[getBaseField],
|
40
|
-
)
|
41
|
-
|
42
|
-
const [, inputProps] = splitProps(
|
43
|
-
props,
|
44
|
-
[
|
45
|
-
'children',
|
46
|
-
'name',
|
47
|
-
'label',
|
48
|
-
'required',
|
49
|
-
'disabled',
|
50
|
-
'invalid',
|
51
|
-
'readOnly',
|
52
|
-
'help',
|
53
|
-
'hideLabel',
|
54
|
-
].concat(extraProps),
|
55
|
-
)
|
56
|
-
|
57
|
-
return (
|
58
|
-
<BaseField name={name} hideLabel={hideLabel} {...props}>
|
59
|
-
<InputComponent
|
60
|
-
ref={ref}
|
61
|
-
id={id}
|
62
|
-
name={name}
|
63
|
-
label={hideLabel ? label : undefined} // Only pass down the label when it should be inline.
|
64
|
-
{...inputProps}
|
65
|
-
rules={inputRules}
|
66
|
-
/>
|
67
|
-
</BaseField>
|
68
|
-
)
|
69
|
-
})
|
70
|
-
|
71
|
-
Field.displayName = displayName
|
72
|
-
|
73
|
-
return Field
|
74
|
-
}
|
75
|
-
|
76
|
-
const withControlledInput = (InputComponent: React.FC<any>) => {
|
77
|
-
return forwardRef<typeof InputComponent, ControlProps>((props, ref) => {
|
78
|
-
const { name, rules, ...inputProps } = props
|
79
|
-
const { control } = useFormContext()
|
80
|
-
|
81
|
-
const onChange = inputProps.onChange as (...event: any[]) => void
|
82
|
-
|
83
|
-
return (
|
84
|
-
<Controller
|
85
|
-
name={name}
|
86
|
-
control={control}
|
87
|
-
rules={rules}
|
88
|
-
render={({ field: { ref: _ref, ...field } }) => (
|
89
|
-
<InputComponent
|
90
|
-
{...field}
|
91
|
-
{...inputProps}
|
92
|
-
onChange={callAll(onChange, field.onChange)}
|
93
|
-
onBlur={callAll(inputProps.onBlur, field.onBlur)}
|
94
|
-
ref={mergeRefs(ref, _ref)}
|
95
|
-
/>
|
96
|
-
)}
|
97
|
-
/>
|
98
|
-
)
|
99
|
-
})
|
100
|
-
}
|
101
|
-
|
102
|
-
const withUncontrolledInput = (InputComponent: React.FC<any>) => {
|
103
|
-
return forwardRef<typeof InputComponent, ControlProps>(
|
104
|
-
({ name, rules, ...inputProps }, ref) => {
|
105
|
-
const { register } = useFormContext()
|
106
|
-
|
107
|
-
const { ref: _ref, ...field } = register(name, rules)
|
108
|
-
|
109
|
-
const onChange = inputProps.onChange as (...event: any[]) => void
|
110
|
-
|
111
|
-
return (
|
112
|
-
<InputComponent
|
113
|
-
{...field}
|
114
|
-
{...inputProps}
|
115
|
-
onChange={callAll(onChange, field.onChange)}
|
116
|
-
onBlur={callAll(inputProps.onBlur, field.onBlur)}
|
117
|
-
ref={mergeRefs(ref, _ref)}
|
118
|
-
/>
|
119
|
-
)
|
120
|
-
},
|
121
|
-
)
|
122
|
-
}
|
123
|
-
|
124
|
-
export interface CreateFieldOptions {
|
125
|
-
isControlled?: boolean
|
126
|
-
hideLabel?: boolean
|
127
|
-
BaseField?: React.FC<any>
|
128
|
-
}
|
129
|
-
|
130
|
-
interface ControlProps {
|
131
|
-
name: string
|
132
|
-
onChange: (...event: any[]) => void
|
133
|
-
onBlur: (...event: any[]) => void
|
134
|
-
value: unknown
|
135
|
-
disabled?: boolean
|
136
|
-
rules?: RegisterOptions
|
137
|
-
}
|
138
|
-
|
139
|
-
/**
|
140
|
-
* Register a new field type
|
141
|
-
* @param type The name for this field in kebab-case, eg `email` or `array-field`
|
142
|
-
* @param component The React component
|
143
|
-
* @param options
|
144
|
-
* @param options.isControlled Set this to true if this is a controlled field.
|
145
|
-
* @param options.hideLabel Hide the field label, for example for the checkbox field.
|
146
|
-
*/
|
147
|
-
export const createField = <TType = unknown, TProps extends object = object>(
|
148
|
-
component: ForwardRefRenderFunction<
|
149
|
-
TType,
|
150
|
-
PropsWithoutRef<TProps & ControlProps>
|
151
|
-
>,
|
152
|
-
options?: CreateFieldOptions,
|
153
|
-
) => {
|
154
|
-
let InputComponent
|
155
|
-
if (options?.isControlled) {
|
156
|
-
InputComponent = withControlledInput(forwardRef(component))
|
157
|
-
} else {
|
158
|
-
InputComponent = withUncontrolledInput(forwardRef(component))
|
159
|
-
}
|
160
|
-
|
161
|
-
const Field = _createField(InputComponent, {
|
162
|
-
displayName: `${component.displayName ?? 'Custom'}Field`,
|
163
|
-
hideLabel: options?.hideLabel,
|
164
|
-
getBaseField: () => ({
|
165
|
-
extraProps: [],
|
166
|
-
BaseField,
|
167
|
-
}),
|
168
|
-
}) as React.FC<Omit<BaseFieldProps, keyof TProps> & TProps>
|
169
|
-
|
170
|
-
return Field
|
171
|
-
}
|