@saas-ui/forms 2.0.0-next.2 → 2.0.0-next.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +53 -6
  3. package/dist/ajv/index.d.ts +1 -1
  4. package/dist/ajv/index.js.map +1 -1
  5. package/dist/ajv/index.mjs.map +1 -1
  6. package/dist/index.d.ts +265 -166
  7. package/dist/index.js +2821 -556
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +2814 -555
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/yup/index.d.ts +98 -6
  12. package/dist/yup/index.js.map +1 -1
  13. package/dist/yup/index.mjs.map +1 -1
  14. package/dist/zod/index.d.ts +97 -4
  15. package/dist/zod/index.js.map +1 -1
  16. package/dist/zod/index.mjs.map +1 -1
  17. package/package.json +5 -3
  18. package/src/array-field.tsx +50 -30
  19. package/src/auto-form.tsx +7 -3
  20. package/src/base-field.tsx +59 -0
  21. package/src/create-field.tsx +143 -0
  22. package/src/create-form.tsx +31 -0
  23. package/src/default-fields.tsx +146 -0
  24. package/src/display-field.tsx +8 -9
  25. package/src/display-if.tsx +6 -5
  26. package/src/field-resolver.ts +1 -1
  27. package/src/field.tsx +14 -444
  28. package/src/fields-context.tsx +23 -0
  29. package/src/fields.tsx +18 -8
  30. package/src/form.tsx +27 -37
  31. package/src/index.ts +38 -0
  32. package/src/input-right-button/input-right-button.stories.tsx +1 -1
  33. package/src/input-right-button/input-right-button.tsx +0 -2
  34. package/src/layout.tsx +16 -11
  35. package/src/number-input/number-input.tsx +9 -5
  36. package/src/object-field.tsx +8 -7
  37. package/src/password-input/password-input.stories.tsx +23 -2
  38. package/src/password-input/password-input.tsx +5 -5
  39. package/src/pin-input/pin-input.tsx +1 -5
  40. package/src/radio/radio-input.stories.tsx +1 -1
  41. package/src/radio/radio-input.tsx +12 -10
  42. package/src/select/native-select.tsx +1 -4
  43. package/src/select/select.test.tsx +1 -1
  44. package/src/select/select.tsx +18 -14
  45. package/src/step-form.tsx +29 -11
  46. package/src/submit-button.tsx +5 -1
  47. package/src/types.ts +91 -0
  48. package/src/utils.ts +15 -0
  49. /package/src/radio/{radio.test.tsx → radio-input.test.tsx} +0 -0
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './display-field'
2
2
  export * from './field'
3
3
  export * from './fields'
4
+ export * from './fields-context'
4
5
  export * from './form'
5
6
  export * from './auto-form'
6
7
  export * from './layout'
@@ -16,6 +17,43 @@ export * from './watch-field'
16
17
  export * from './input-right-button'
17
18
  export * from './select'
18
19
  export * from './password-input'
20
+ export * from './radio'
21
+
22
+ export * from './base-field'
23
+
24
+ export {
25
+ CheckboxField,
26
+ InputField,
27
+ NativeSelectField,
28
+ NumberInputField,
29
+ PasswordInputField,
30
+ PinField,
31
+ RadioField,
32
+ SelectField,
33
+ SwitchField,
34
+ TextareaField,
35
+ defaultFieldTypes,
36
+ } from './default-fields'
37
+
38
+ export type {
39
+ DefaultFields,
40
+ InputFieldProps,
41
+ NumberInputFieldProps,
42
+ PinFieldProps,
43
+ } from './default-fields'
44
+
45
+ export type {
46
+ FieldProps,
47
+ WithFields,
48
+ BaseFieldProps,
49
+ FieldOptions,
50
+ } from './types'
51
+
52
+ export { createForm } from './create-form'
53
+ export type { CreateFormProps } from './create-form'
54
+
55
+ export { createField } from './create-field'
56
+ export type { CreateFieldOptions } from './create-field'
19
57
 
20
58
  export type {
21
59
  BatchFieldArrayUpdate,
@@ -37,7 +37,7 @@ const Template: ComponentStory<typeof InputRightButton> = (args) => (
37
37
 
38
38
  export const Basic = Template.bind({})
39
39
  Basic.args = {
40
- label: 'Save',
40
+ children: 'Save',
41
41
  }
42
42
 
43
43
  export const WithIcon = Template.bind({})
@@ -7,8 +7,6 @@ import {
7
7
  InputRightElement,
8
8
  } from '@chakra-ui/react'
9
9
 
10
- import { __DEV__ } from '@chakra-ui/utils'
11
-
12
10
  export type InputRightButtonProps = ButtonProps
13
11
 
14
12
  export const InputRightButton = forwardRef<InputRightButtonProps, 'div'>(
package/src/layout.tsx CHANGED
@@ -1,9 +1,15 @@
1
1
  import * as React from 'react'
2
2
 
3
- import { chakra, SimpleGrid, SimpleGridProps, useTheme } from '@chakra-ui/react'
4
- import { cx, __DEV__ } from '@chakra-ui/utils'
3
+ import {
4
+ chakra,
5
+ ResponsiveValue,
6
+ SimpleGrid,
7
+ SimpleGridProps,
8
+ useTheme,
9
+ } from '@chakra-ui/react'
10
+ import { cx } from '@chakra-ui/utils'
5
11
 
6
- export type FormLayoutProps = SimpleGridProps
12
+ export interface FormLayoutProps extends SimpleGridProps {}
7
13
 
8
14
  interface FormLayoutItemProps {
9
15
  children: React.ReactNode
@@ -13,20 +19,21 @@ const FormLayoutItem: React.FC<FormLayoutItemProps> = ({ children }) => {
13
19
  return <chakra.div>{children}</chakra.div>
14
20
  }
15
21
 
16
- if (__DEV__) {
17
- FormLayoutItem.displayName = 'FormLayoutItem'
18
- }
22
+ FormLayoutItem.displayName = 'FormLayoutItem'
19
23
 
20
24
  /**
21
- * FormLayout
25
+ * Create consistent field spacing and positioning.
26
+ *
22
27
  *
23
28
  * Renders form items in a `SimpleGrid`
24
29
  * @see https://chakra-ui.com/docs/layout/simple-grid
30
+ *
31
+ * @see https://saas-ui.dev/docs/components/forms/form
25
32
  */
26
33
  export const FormLayout = ({ children, ...props }: FormLayoutProps) => {
27
34
  const theme = useTheme()
28
35
 
29
- const defaultProps = theme.components?.FormLayout?.defaultProps ?? {
36
+ const defaultProps = theme.components?.SuiFormLayout?.defaultProps ?? {
30
37
  spacing: 4,
31
38
  }
32
39
 
@@ -50,6 +57,4 @@ export const FormLayout = ({ children, ...props }: FormLayoutProps) => {
50
57
  )
51
58
  }
52
59
 
53
- if (__DEV__) {
54
- FormLayout.displayName = 'FormLayout'
55
- }
60
+ FormLayout.displayName = 'FormLayout'
@@ -9,7 +9,8 @@ import {
9
9
  NumberDecrementStepper,
10
10
  NumberInputProps as ChakraNumberInputProps,
11
11
  } from '@chakra-ui/react'
12
- import { __DEV__ } from '@chakra-ui/utils'
12
+
13
+ import { ChevronDownIcon, ChevronUpIcon } from '@saas-ui/core'
13
14
 
14
15
  interface NumberInputOptions {
15
16
  /**
@@ -31,7 +32,12 @@ export interface NumberInputProps
31
32
  NumberInputOptions {}
32
33
 
33
34
  export const NumberInput = forwardRef<NumberInputProps, 'div'>((props, ref) => {
34
- const { hideStepper, incrementIcon, decrementIcon, ...rest } = props
35
+ const {
36
+ hideStepper,
37
+ incrementIcon = <ChevronUpIcon />,
38
+ decrementIcon = <ChevronDownIcon />,
39
+ ...rest
40
+ } = props
35
41
 
36
42
  return (
37
43
  <ChakraNumberInput {...rest} ref={ref}>
@@ -51,6 +57,4 @@ NumberInput.defaultProps = {
51
57
  hideStepper: false,
52
58
  }
53
59
 
54
- if (__DEV__) {
55
- NumberInput.displayName = 'NumberInput'
56
- }
60
+ NumberInput.displayName = 'NumberInput'
@@ -6,14 +6,13 @@ import {
6
6
  ResponsiveValue,
7
7
  useStyleConfig,
8
8
  } from '@chakra-ui/react'
9
- import { __DEV__ } from '@chakra-ui/utils'
10
9
 
11
10
  import { FormLayout } from './layout'
12
- import { FieldProps } from './field'
11
+ import { BaseFieldProps } from './types'
13
12
 
14
13
  import { mapNestedFields } from './utils'
15
14
 
16
- export interface ObjectFieldProps extends FieldProps {
15
+ export interface ObjectFieldProps extends BaseFieldProps {
17
16
  name: string
18
17
  children: React.ReactNode
19
18
  columns?: ResponsiveValue<number>
@@ -24,7 +23,11 @@ export const FormLegend = (props: FormLabelProps) => {
24
23
  const styles = useStyleConfig('SuiFormLegend')
25
24
  return <FormLabel as="legend" sx={styles} {...props} />
26
25
  }
27
-
26
+ /**
27
+ * The object field component.
28
+ *
29
+ * @see Docs https://saas-ui.dev/docs/components/forms/object-field
30
+ */
28
31
  export const ObjectField: React.FC<ObjectFieldProps> = (props) => {
29
32
  const { name, label, hideLabel, children, columns, spacing, ...fieldProps } =
30
33
  props
@@ -39,6 +42,4 @@ export const ObjectField: React.FC<ObjectFieldProps> = (props) => {
39
42
  )
40
43
  }
41
44
 
42
- if (__DEV__) {
43
- ObjectField.displayName = 'ObjectField'
44
- }
45
+ ObjectField.displayName = 'ObjectField'
@@ -1,8 +1,15 @@
1
- import { Container, FormControl, FormLabel, Icon } from '@chakra-ui/react'
1
+ import {
2
+ Container,
3
+ FormControl,
4
+ FormLabel,
5
+ Icon,
6
+ InputLeftAddon,
7
+ InputLeftElement,
8
+ } from '@chakra-ui/react'
2
9
  import { Story } from '@storybook/react'
3
10
  import * as React from 'react'
4
11
 
5
- import { FiEye, FiEyeOff } from 'react-icons/fi'
12
+ import { FiEye, FiEyeOff, FiLock } from 'react-icons/fi'
6
13
 
7
14
  import { PasswordInput } from './password-input'
8
15
 
@@ -48,3 +55,17 @@ export const CustomWidth: Story = () => (
48
55
  <PasswordInput name="password" width="200px" />
49
56
  </FormControl>
50
57
  )
58
+
59
+ export const LeftAddon: Story = () => (
60
+ <FormControl>
61
+ <FormLabel>Password</FormLabel>
62
+ <PasswordInput
63
+ name="password"
64
+ leftAddon={
65
+ <InputLeftElement>
66
+ <FiLock />
67
+ </InputLeftElement>
68
+ }
69
+ />
70
+ </FormControl>
71
+ )
@@ -1,7 +1,7 @@
1
1
  import React, { useState } from 'react'
2
2
 
3
3
  import { forwardRef, InputGroup, Input, InputProps } from '@chakra-ui/react'
4
- import { __DEV__ } from '@chakra-ui/utils'
4
+
5
5
  import { ViewIcon, ViewOffIcon } from '@chakra-ui/icons'
6
6
 
7
7
  import { InputRightButton } from '../input-right-button'
@@ -9,6 +9,7 @@ import { InputRightButton } from '../input-right-button'
9
9
  interface PasswordOptions {
10
10
  viewIcon?: React.ReactNode
11
11
  viewOffIcon?: React.ReactNode
12
+ leftAddon?: React.ReactNode
12
13
  }
13
14
 
14
15
  export interface PasswordInputProps extends InputProps, PasswordOptions {}
@@ -23,6 +24,7 @@ export const PasswordInput = forwardRef<PasswordInputProps, 'div'>(
23
24
  width,
24
25
  size,
25
26
  variant,
27
+ leftAddon,
26
28
  ...inputProps
27
29
  } = props
28
30
  const [show, setShow] = useState(false)
@@ -45,13 +47,13 @@ export const PasswordInput = forwardRef<PasswordInputProps, 'div'>(
45
47
 
46
48
  return (
47
49
  <InputGroup {...groupProps}>
50
+ {leftAddon}
48
51
  <Input
49
52
  {...inputProps}
50
53
  ref={ref}
51
54
  type={show ? 'text' : 'password'}
52
55
  autoComplete={show ? 'off' : autoComplete}
53
56
  />
54
-
55
57
  <InputRightButton
56
58
  onClick={handleClick}
57
59
  aria-label={label}
@@ -64,6 +66,4 @@ export const PasswordInput = forwardRef<PasswordInputProps, 'div'>(
64
66
  }
65
67
  )
66
68
 
67
- if (__DEV__) {
68
- PasswordInput.displayName = 'PasswordInput'
69
- }
69
+ PasswordInput.displayName = 'PasswordInput'
@@ -8,8 +8,6 @@ import {
8
8
  SystemProps,
9
9
  } from '@chakra-ui/react'
10
10
 
11
- import { __DEV__ } from '@chakra-ui/utils'
12
-
13
11
  interface PinInputOptions {
14
12
  /**
15
13
  * The pin length.
@@ -45,6 +43,4 @@ PinInput.defaultProps = {
45
43
  pinLength: 4,
46
44
  }
47
45
 
48
- if (__DEV__) {
49
- PinInput.displayName = 'PinInput'
50
- }
46
+ PinInput.displayName = 'PinInput'
@@ -6,7 +6,7 @@ import { ComponentStory } from '@storybook/react'
6
6
  import { RadioInput } from './'
7
7
 
8
8
  export default {
9
- title: 'Components/Forms/Radio',
9
+ title: 'Components/Forms/RadioInput',
10
10
  decorators: [
11
11
  (Story: any) => (
12
12
  <Container mt="40px">
@@ -10,15 +10,17 @@ import {
10
10
  SystemProps,
11
11
  StackDirection,
12
12
  } from '@chakra-ui/react'
13
- import { __DEV__ } from '@chakra-ui/utils'
13
+ import { FieldOptions, FieldOption } from '../types'
14
+ import { mapOptions } from '../utils'
14
15
 
15
- interface Option extends RadioProps {
16
- value: string
17
- label?: string
18
- }
16
+ export interface RadioOption
17
+ extends Omit<RadioProps, 'value' | 'label'>,
18
+ FieldOption {}
19
+
20
+ export type RadioOptions = FieldOptions<RadioOption>
19
21
 
20
22
  interface RadioInputOptions {
21
- options: Option[]
23
+ options: RadioOptions
22
24
  spacing?: SystemProps['margin']
23
25
  direction?: StackDirection
24
26
  }
@@ -28,9 +30,11 @@ export interface RadioInputProps
28
30
  RadioInputOptions {}
29
31
 
30
32
  export const RadioInput = forwardRef<RadioInputProps, 'div'>(
31
- ({ options, spacing, direction, ...props }, ref) => {
33
+ ({ options: optionsProp, spacing, direction, ...props }, ref) => {
32
34
  const { onBlur, onChange, ...groupProps } = props
33
35
 
36
+ const options = mapOptions(optionsProp)
37
+
34
38
  return (
35
39
  <RadioGroup onChange={onChange} {...groupProps}>
36
40
  <Stack spacing={spacing} direction={direction}>
@@ -53,6 +57,4 @@ export const RadioInput = forwardRef<RadioInputProps, 'div'>(
53
57
  }
54
58
  )
55
59
 
56
- if (__DEV__) {
57
- RadioInput.displayName = 'RadioInput'
58
- }
60
+ RadioInput.displayName = 'RadioInput'
@@ -5,7 +5,6 @@ import {
5
5
  Select as ChakraSelect,
6
6
  SelectProps as ChakraSelectProps,
7
7
  } from '@chakra-ui/react'
8
- import { __DEV__ } from '@chakra-ui/utils'
9
8
 
10
9
  interface Option {
11
10
  value: string
@@ -37,6 +36,4 @@ export const NativeSelect = forwardRef<NativeSelectProps, 'select'>(
37
36
  }
38
37
  )
39
38
 
40
- if (__DEV__) {
41
- NativeSelect.displayName = 'NativeSelect'
42
- }
39
+ NativeSelect.displayName = 'NativeSelect'
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react'
2
2
 
3
3
  import { render, testStories } from '@saas-ui/test-utils'
4
- import * as stories from '../stories/select.stories'
4
+ import * as stories from './select.stories'
5
5
 
6
6
  const { MaxHeight, ...rest } = stories
7
7
 
@@ -18,21 +18,24 @@ import {
18
18
  SystemStyleObject,
19
19
  useFormControl,
20
20
  HTMLChakraProps,
21
+ MenuItemOptionProps,
21
22
  } from '@chakra-ui/react'
22
- import { ChevronDownIcon } from '@chakra-ui/icons'
23
- import { cx, __DEV__ } from '@chakra-ui/utils'
23
+ import { cx } from '@chakra-ui/utils'
24
+ import { ChevronDownIcon } from '@saas-ui/core'
24
25
 
25
- interface Option {
26
- value: string
27
- label?: string
28
- }
26
+ import { FieldOptions, FieldOption } from '../types'
27
+ import { mapOptions } from '../utils'
28
+
29
+ export interface SelectOption
30
+ extends Omit<MenuItemOptionProps, 'value'>,
31
+ FieldOption {}
29
32
 
30
33
  interface SelectOptions {
31
34
  /**
32
35
  * An array of options
33
36
  * If you leave this empty the children prop will be rendered.
34
37
  */
35
- options?: Option[]
38
+ options?: FieldOptions<SelectOption>
36
39
  /**
37
40
  * Props passed to the MenuList.
38
41
  */
@@ -80,14 +83,12 @@ const SelectButton = forwardRef((props, ref) => {
80
83
  return <MenuButton as={Button} {...props} ref={ref} sx={buttonStyles} />
81
84
  })
82
85
 
83
- if (__DEV__) {
84
- SelectButton.displayName = 'SelectButton'
85
- }
86
+ SelectButton.displayName = 'SelectButton'
86
87
 
87
88
  export const Select = forwardRef<SelectProps, 'select'>((props, ref) => {
88
89
  const {
89
90
  name,
90
- options,
91
+ options: optionsProp,
91
92
  children,
92
93
  onChange,
93
94
  defaultValue,
@@ -109,6 +110,11 @@ export const Select = forwardRef<SelectProps, 'select'>((props, ref) => {
109
110
 
110
111
  const controlProps = useFormControl({ name } as HTMLChakraProps<'input'>)
111
112
 
113
+ const options = React.useMemo(
114
+ () => optionsProp && mapOptions(optionsProp),
115
+ [optionsProp]
116
+ )
117
+
112
118
  const handleChange = (value: string | string[]) => {
113
119
  setCurrentValue(value)
114
120
  onChange?.(value)
@@ -180,6 +186,4 @@ export const Select = forwardRef<SelectProps, 'select'>((props, ref) => {
180
186
  )
181
187
  })
182
188
 
183
- if (__DEV__) {
184
- Select.displayName = 'Select'
185
- }
189
+ Select.displayName = 'Select'
package/src/step-form.tsx CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  ThemingProps,
11
11
  } from '@chakra-ui/react'
12
12
 
13
- import { callAllHandlers, runIfFn, cx, __DEV__ } from '@chakra-ui/utils'
13
+ import { callAllHandlers, runIfFn, cx } from '@chakra-ui/utils'
14
14
 
15
15
  import {
16
16
  StepperProvider,
@@ -38,6 +38,11 @@ export interface StepFormProps<
38
38
  TContext extends object = object
39
39
  > extends UseStepFormProps<TFieldValues> {}
40
40
 
41
+ /**
42
+ * The wrapper component provides context, state, and focus management.
43
+ *
44
+ * @see Docs https://saas-ui.dev/docs/components/forms/step-form
45
+ */
41
46
  export const StepForm = React.forwardRef(
42
47
  <
43
48
  TFieldValues extends FieldValues = FieldValues,
@@ -89,6 +94,11 @@ export interface FormStepperProps
89
94
  extends StepperStepsProps,
90
95
  ThemingProps<'Stepper'> {}
91
96
 
97
+ /**
98
+ * Renders a stepper that displays progress above the form.
99
+ *
100
+ * @see Docs https://saas-ui.dev/docs/components/forms/step-form
101
+ */
92
102
  export const FormStepper: React.FC<FormStepperProps> = (props) => {
93
103
  const { activeIndex, setIndex } = useStepperContext()
94
104
 
@@ -139,7 +149,11 @@ export interface FormStepProps
139
149
  Omit<HTMLChakraProps<'div'>, 'onSubmit'> {
140
150
  onSubmit?: FormStepSubmitHandler
141
151
  }
142
-
152
+ /**
153
+ * The form step containing fields for a specific step.
154
+ *
155
+ * @see Docs https://saas-ui.dev/docs/components/forms/step-form
156
+ */
143
157
  export const FormStep: React.FC<FormStepProps> = (props) => {
144
158
  const { name, schema, resolver, children, className, onSubmit, ...rest } =
145
159
  props
@@ -154,10 +168,13 @@ export const FormStep: React.FC<FormStepProps> = (props) => {
154
168
  ) : null
155
169
  }
156
170
 
157
- if (__DEV__) {
158
- FormStep.displayName = 'FormStep'
159
- }
171
+ FormStep.displayName = 'FormStep'
160
172
 
173
+ /**
174
+ * A button that this opens the previous step when clicked. Disabled on the first step.
175
+ *
176
+ * @see Docs https://saas-ui.dev/docs/components/forms/step-form
177
+ */
161
178
  export const PrevButton: React.FC<ButtonProps> = (props) => {
162
179
  const { isFirstStep, isCompleted, prevStep } = useStepperContext()
163
180
 
@@ -172,15 +189,18 @@ export const PrevButton: React.FC<ButtonProps> = (props) => {
172
189
  )
173
190
  }
174
191
 
175
- if (__DEV__) {
176
- PrevButton.displayName = 'PrevButton'
177
- }
192
+ PrevButton.displayName = 'PrevButton'
178
193
 
179
194
  export interface NextButtonProps extends Omit<ButtonProps, 'children'> {
180
195
  submitLabel?: string
181
196
  label?: string
182
197
  }
183
198
 
199
+ /**
200
+ * A button that submits the active step.
201
+ *
202
+ * @see Docs https://saas-ui.dev/docs/components/forms/step-form
203
+ */
184
204
  export const NextButton: React.FC<NextButtonProps> = (props) => {
185
205
  const { label = 'Next', submitLabel = 'Complete', ...rest } = props
186
206
  const { isLastStep, isCompleted } = useStepperContext()
@@ -196,6 +216,4 @@ export const NextButton: React.FC<NextButtonProps> = (props) => {
196
216
  )
197
217
  }
198
218
 
199
- if (__DEV__) {
200
- NextButton.displayName = 'NextButton'
201
- }
219
+ NextButton.displayName = 'NextButton'
@@ -20,7 +20,11 @@ export interface SubmitButtonProps extends ButtonProps {
20
20
  */
21
21
  disableIfInvalid?: boolean
22
22
  }
23
-
23
+ /**
24
+ * A button with type submit and default color scheme primary and isLoading state when the form is submitting.
25
+ *
26
+ * @see Docs https://saas-ui.dev/docs/components/forms/form
27
+ */
24
28
  export const SubmitButton = forwardRef<SubmitButtonProps, 'button'>(
25
29
  (props, ref) => {
26
30
  const {
package/src/types.ts ADDED
@@ -0,0 +1,91 @@
1
+ import { FormControlProps } from '@chakra-ui/react'
2
+ import { MaybeRenderProp } from '@chakra-ui/react-utils'
3
+ import { FieldPath, FieldValues, RegisterOptions } from 'react-hook-form'
4
+ import { DefaultFields } from './default-fields'
5
+ import { FormProps, FormRenderContext } from './form'
6
+
7
+ export type FieldOption = { label: string; value: string }
8
+ export type FieldOptions<TOption extends FieldOption = FieldOption> =
9
+ | Array<string>
10
+ | Array<TOption>
11
+
12
+ export type ValueOf<T> = T[keyof T]
13
+ export type ShallowMerge<A, B> = Omit<A, keyof B> & B
14
+
15
+ export interface BaseFieldProps<
16
+ TFieldValues extends FieldValues = FieldValues,
17
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
18
+ > extends Omit<FormControlProps, 'label' | 'type'> {
19
+ /**
20
+ * The field name
21
+ */
22
+ name: TName
23
+ /**
24
+ * The field label
25
+ */
26
+ label?: string
27
+ /**
28
+ * Hide the field label
29
+ */
30
+ hideLabel?: boolean
31
+ /**
32
+ * Field help text
33
+ */
34
+ help?: string
35
+ /**
36
+ * React hook form rules
37
+ */
38
+ rules?: Omit<
39
+ RegisterOptions<TFieldValues, TName>,
40
+ 'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'
41
+ >
42
+ /**
43
+ * Build-in types:
44
+ * text, number, password, textarea, select, native-select, checkbox, radio, switch, pin
45
+ *
46
+ * Will default to a text field if there is no matching type.
47
+ */
48
+ type?: string
49
+ /**
50
+ * The input placeholder
51
+ */
52
+ placeholder?: string
53
+ }
54
+
55
+ type MergeFieldProps<
56
+ FieldDefs,
57
+ TFieldValues extends FieldValues = FieldValues
58
+ > = ValueOf<{
59
+ [K in keyof FieldDefs]: FieldDefs[K] extends React.FC<infer Props>
60
+ ? { type?: K } & ShallowMerge<Props, BaseFieldProps<TFieldValues>>
61
+ : never
62
+ }>
63
+
64
+ export type FieldProps<TFieldValues extends FieldValues = FieldValues> =
65
+ MergeFieldProps<DefaultFields, TFieldValues>
66
+
67
+ export type FormChildren<
68
+ FieldDefs,
69
+ TFieldValues extends FieldValues = FieldValues,
70
+ TContext extends object = object
71
+ > = MaybeRenderProp<
72
+ FormRenderContext<
73
+ TFieldValues,
74
+ TContext,
75
+ MergeFieldProps<
76
+ FieldDefs extends never
77
+ ? DefaultFields
78
+ : ShallowMerge<DefaultFields, FieldDefs>,
79
+ TFieldValues
80
+ >
81
+ >
82
+ >
83
+
84
+ export type WithFields<
85
+ TFormProps extends FormProps<any, any, any, any>,
86
+ FieldDefs
87
+ > = TFormProps extends FormProps<infer TFieldValues, infer TContext>
88
+ ? Omit<TFormProps, 'children'> & {
89
+ children: FormChildren<FieldDefs, TFieldValues, TContext>
90
+ }
91
+ : never
package/src/utils.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as React from 'react'
2
+ import { FieldOption, FieldOptions } from './types'
2
3
 
3
4
  export const mapNestedFields = (name: string, children: React.ReactNode) => {
4
5
  return React.Children.map(children, (child) => {
@@ -11,3 +12,17 @@ export const mapNestedFields = (name: string, children: React.ReactNode) => {
11
12
  return child
12
13
  })
13
14
  }
15
+
16
+ export const mapOptions = <TOption extends FieldOption = FieldOption>(
17
+ options: FieldOptions<TOption>
18
+ ) => {
19
+ return options.map((option) => {
20
+ if (typeof option === 'string') {
21
+ return {
22
+ label: option,
23
+ value: option,
24
+ }
25
+ }
26
+ return option
27
+ })
28
+ }