@saas-ui/forms 3.0.0-alpha.1 → 3.0.0-alpha.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
}
|