@saas-ui/forms 2.0.0-next.0 → 2.0.0-next.10

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.
Files changed (61) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/README.md +53 -6
  3. package/dist/ajv/index.d.ts +359 -11
  4. package/dist/ajv/index.js +7 -9
  5. package/dist/ajv/index.js.map +1 -1
  6. package/dist/ajv/index.mjs +7 -10
  7. package/dist/ajv/index.mjs.map +1 -1
  8. package/dist/index.d.ts +448 -247
  9. package/dist/index.js +708 -682
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +688 -662
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/yup/index.d.ts +581 -21
  14. package/dist/yup/index.js +6 -10
  15. package/dist/yup/index.js.map +1 -1
  16. package/dist/yup/index.mjs +4 -8
  17. package/dist/yup/index.mjs.map +1 -1
  18. package/dist/zod/index.d.ts +581 -11
  19. package/dist/zod/index.js +5 -0
  20. package/dist/zod/index.js.map +1 -1
  21. package/dist/zod/index.mjs +5 -1
  22. package/dist/zod/index.mjs.map +1 -1
  23. package/package.json +24 -15
  24. package/src/array-field.tsx +82 -45
  25. package/src/auto-form.tsx +7 -3
  26. package/src/base-field.tsx +54 -0
  27. package/src/create-field.tsx +144 -0
  28. package/src/create-form.tsx +54 -0
  29. package/src/default-fields.tsx +163 -0
  30. package/src/display-field.tsx +9 -11
  31. package/src/display-if.tsx +20 -13
  32. package/src/field-resolver.ts +10 -8
  33. package/src/field.tsx +18 -445
  34. package/src/fields-context.tsx +23 -0
  35. package/src/fields.tsx +34 -21
  36. package/src/form-context.tsx +84 -0
  37. package/src/form.tsx +72 -52
  38. package/src/index.ts +44 -4
  39. package/src/input-right-button/input-right-button.stories.tsx +2 -2
  40. package/src/input-right-button/input-right-button.tsx +0 -2
  41. package/src/layout.tsx +16 -11
  42. package/src/number-input/number-input.stories.tsx +3 -3
  43. package/src/number-input/number-input.tsx +9 -5
  44. package/src/object-field.tsx +13 -8
  45. package/src/password-input/password-input.stories.tsx +23 -2
  46. package/src/password-input/password-input.tsx +6 -6
  47. package/src/pin-input/pin-input.tsx +1 -5
  48. package/src/radio/radio-input.stories.tsx +1 -1
  49. package/src/radio/radio-input.tsx +12 -10
  50. package/src/select/native-select.tsx +1 -4
  51. package/src/select/select-context.tsx +130 -0
  52. package/src/select/select.stories.tsx +117 -86
  53. package/src/select/select.test.tsx +1 -1
  54. package/src/select/select.tsx +162 -146
  55. package/src/step-form.tsx +29 -11
  56. package/src/submit-button.tsx +5 -1
  57. package/src/types.ts +144 -0
  58. package/src/use-array-field.tsx +9 -3
  59. package/src/utils.ts +23 -1
  60. package/src/watch-field.tsx +2 -6
  61. /package/src/radio/{radio.test.tsx → radio-input.test.tsx} +0 -0
@@ -0,0 +1,130 @@
1
+ import {
2
+ HTMLChakraProps,
3
+ useControllableState,
4
+ useFormControl,
5
+ } from '@chakra-ui/react'
6
+ import { createContext } from '@chakra-ui/react-utils'
7
+ import React from 'react'
8
+ import { FieldOptions } from '../types'
9
+ import { mapOptions } from '../utils'
10
+ import { SelectOption } from './select'
11
+
12
+ export const [SelectProvider, useSelectContext] = createContext<
13
+ ReturnType<typeof useSelect>
14
+ >({
15
+ strict: true,
16
+ })
17
+
18
+ export interface SelectOptions {
19
+ /**
20
+ * The name of the input field in a native form.
21
+ */
22
+ name: string
23
+ /**
24
+ * The value of the select field.
25
+ */
26
+ value?: string | string[]
27
+ /**
28
+ * The initial value of the select field.
29
+ */
30
+ defaultValue?: string | string[]
31
+ /**
32
+ * The callback invoked when the value of the select field changes.
33
+ * @param value The value of the select field.
34
+ */
35
+ onChange?: (value: string | string[]) => void
36
+ /**
37
+ * The placeholder text when there's no value.
38
+ */
39
+ placeholder?: string
40
+ /**
41
+ * If `true`, the select will be disabled.
42
+ */
43
+ isDisabled?: boolean
44
+ /**
45
+ * An array of options
46
+ * If you leave this empty the children prop will be rendered.
47
+ */
48
+ options?: FieldOptions<SelectOption>
49
+ /**
50
+ * Enable multiple select.
51
+ */
52
+ multiple?: boolean
53
+ /**
54
+ * The function used to render the value of the select field.
55
+ * @param value The value of the select field.
56
+ * @returns The rendered value.
57
+ */
58
+ renderValue?: (value: string | string[]) => React.ReactNode
59
+ }
60
+
61
+ export const useSelect = (props: SelectOptions) => {
62
+ const {
63
+ name,
64
+ value,
65
+ defaultValue,
66
+ onChange,
67
+ multiple,
68
+ placeholder,
69
+ options: optionsProp,
70
+ isDisabled,
71
+ renderValue = (value) =>
72
+ typeof value === 'string' ? value : value?.join(', '),
73
+ } = props
74
+ const [currentValue, setCurrentValue] = useControllableState({
75
+ value,
76
+ defaultValue,
77
+ onChange,
78
+ })
79
+
80
+ const controlProps = useFormControl({ name } as HTMLChakraProps<'input'>)
81
+
82
+ const options = React.useMemo(
83
+ () => optionsProp && mapOptions(optionsProp),
84
+ [optionsProp]
85
+ )
86
+
87
+ const handleChange = (value: string | string[]) => {
88
+ setCurrentValue(value)
89
+ }
90
+
91
+ const getDisplayValue = React.useCallback(
92
+ (value: string) => {
93
+ if (!options) {
94
+ return value
95
+ }
96
+
97
+ for (const option of options) {
98
+ if (option.value === value) {
99
+ return option.label || option.value
100
+ }
101
+ }
102
+
103
+ return value
104
+ },
105
+ [options]
106
+ )
107
+
108
+ const displayValue = React.useMemo(
109
+ () =>
110
+ currentValue
111
+ ? (Array.isArray(currentValue) ? currentValue : [currentValue]).map(
112
+ getDisplayValue
113
+ )
114
+ : [],
115
+ [currentValue, getDisplayValue]
116
+ )
117
+
118
+ return {
119
+ defaultValue,
120
+ value: currentValue,
121
+ displayValue,
122
+ renderValue,
123
+ onChange: handleChange,
124
+ options,
125
+ multiple,
126
+ controlProps,
127
+ placeholder,
128
+ isDisabled,
129
+ }
130
+ }
@@ -8,15 +8,23 @@ import {
8
8
  } from '@chakra-ui/react'
9
9
  import * as React from 'react'
10
10
 
11
- import { ComponentStory } from '@storybook/react'
11
+ import { StoryFn } from '@storybook/react'
12
12
 
13
- import { Select } from '../src/select'
14
- import { NativeSelect } from '../src/native-select'
13
+ import { Select, SelectButton, SelectList, SelectOption } from './select'
14
+ import { NativeSelect } from './native-select'
15
15
 
16
16
  import { FiSmile } from 'react-icons/fi'
17
17
 
18
+ const Template: StoryFn<typeof Select> = (args) => (
19
+ <Select placeholder="Select an option..." {...args}>
20
+ <SelectButton />
21
+ <SelectList />
22
+ </Select>
23
+ )
24
+
18
25
  export default {
19
26
  title: 'Components/Forms/Select',
27
+ component: Template,
20
28
  decorators: [
21
29
  (Story: any) => (
22
30
  <Container mt="40px" maxW="320px">
@@ -34,111 +42,134 @@ const getOptions = (length = 6) =>
34
42
 
35
43
  const options = getOptions()
36
44
 
37
- const Template: ComponentStory<typeof Select> = (args) => (
38
- <Select placeholder="Select an option..." {...args} />
39
- )
40
-
41
- export const Basic = Template.bind({})
42
- Basic.args = {
43
- name: 'select',
44
- options,
45
+ export const Basic = {
46
+ args: {
47
+ name: 'select',
48
+ options,
49
+ },
45
50
  }
46
51
 
47
- export const DefaultValue = Template.bind({})
48
- DefaultValue.args = {
49
- name: 'select',
50
- options,
51
- defaultValue: 1,
52
+ export const DefaultValue = {
53
+ args: {
54
+ name: 'select',
55
+ options,
56
+ defaultValue: '1',
57
+ },
52
58
  }
53
59
 
54
- export const Placeholder = Template.bind({})
55
- Placeholder.args = {
56
- name: 'select',
57
- options,
58
- placeholder: 'Select an option...',
60
+ export const Placeholder = {
61
+ args: {
62
+ name: 'select',
63
+ options,
64
+ placeholder: 'Select an option...',
65
+ },
59
66
  }
60
-
61
- export const Disabled = Template.bind({})
62
- Disabled.args = {
63
- name: 'select',
64
- options,
65
- placeholder: 'Disabled.',
66
- isDisabled: true,
67
+ export const Disabled = {
68
+ args: {
69
+ name: 'select',
70
+ options,
71
+ placeholder: 'Disabled.',
72
+ isDisabled: true,
73
+ },
67
74
  }
68
75
 
69
- export const Multi = Template.bind({})
70
- Multi.args = {
71
- name: 'select',
72
- options,
73
- placeholder: 'Multiple.',
74
- multiple: true,
76
+ export const Multi = {
77
+ args: {
78
+ name: 'select',
79
+ options,
80
+ placeholder: 'Multiple.',
81
+ multiple: true,
82
+ },
75
83
  }
76
84
 
77
- export const MultiWithDefaultValue = Template.bind({})
78
- MultiWithDefaultValue.args = {
79
- name: 'select',
80
- options,
81
- placeholder: 'Select an option...',
82
- multiple: true,
83
- defaultValue: ['1'],
85
+ export const MultiWithDefaultValue = {
86
+ args: {
87
+ name: 'select',
88
+ options,
89
+ placeholder: 'Select an option...',
90
+ multiple: true,
91
+ defaultValue: ['1'],
92
+ },
84
93
  }
85
94
 
86
- export const MultiWithTags = Template.bind({})
87
- MultiWithTags.args = {
88
- name: 'select',
89
- options,
90
- placeholder: 'Select options...',
91
- multiple: true,
92
- renderValue: (selected) => {
93
- if (selected?.length) {
94
- return (
95
- <Wrap py="1">
96
- {selected.map((value) => (
97
- <WrapItem>
98
- <Tag>{value}</Tag>
99
- </WrapItem>
100
- ))}
101
- </Wrap>
102
- )
103
- }
95
+ export const MultiWithTags = {
96
+ args: {
97
+ name: 'select',
98
+ options,
99
+ placeholder: 'Select options...',
100
+ multiple: true,
101
+ renderValue: (selected) => {
102
+ if (selected?.length) {
103
+ return (
104
+ <Wrap py="1">
105
+ {selected.map((value) => (
106
+ <WrapItem>
107
+ <Tag variant="solid">{value}</Tag>
108
+ </WrapItem>
109
+ ))}
110
+ </Wrap>
111
+ )
112
+ }
113
+ },
104
114
  },
105
115
  }
106
116
 
107
- export const WithIcons = Template.bind({})
108
- WithIcons.args = {
109
- name: 'select',
110
- options,
111
- value: 1,
112
- leftIcon: <Icon as={FiSmile} />,
117
+ export const Test = {
118
+ render: () => (
119
+ <Tag variant="outline" colorScheme="teal">
120
+ Test
121
+ </Tag>
122
+ ),
113
123
  }
114
124
 
115
- export const MaxHeight = Template.bind({})
116
- MaxHeight.args = {
117
- name: 'select',
118
- options: getOptions(100),
125
+ export const WithIcons = {
126
+ render: (args) => (
127
+ <Select placeholder="Select an option..." {...args}>
128
+ <SelectButton leftIcon={<Icon as={FiSmile} />} />
129
+ <SelectList />
130
+ </Select>
131
+ ),
132
+ args: {
133
+ name: 'select',
134
+ options,
135
+ value: '1',
136
+ },
119
137
  }
120
138
 
121
- export const WithChildren = () => {
122
- return (
123
- <Select name="select" value="1">
124
- <MenuItemOption value="1">Option 1</MenuItemOption>
125
- <MenuItemOption value="2">Option 1</MenuItemOption>
139
+ export const MaxHeight = {
140
+ args: {
141
+ name: 'select',
142
+ options: getOptions(100),
143
+ },
144
+ }
145
+
146
+ export const WithChildren = {
147
+ render: () => (
148
+ <Select name="select" defaultValue="1">
149
+ <SelectButton />
150
+ <SelectList>
151
+ <SelectOption value="1">Option 1</SelectOption>
152
+ <SelectOption value="2">Option 2</SelectOption>
153
+ </SelectList>
126
154
  </Select>
127
- )
155
+ ),
128
156
  }
129
157
 
130
- export const WithEmptyOption = () => {
131
- return (
132
- <Select name="select" value="1">
133
- <MenuItemOption value="">None</MenuItemOption>
134
- <MenuItemOption value="1">Option 1</MenuItemOption>
135
- <MenuItemOption value="2">Option 1</MenuItemOption>
158
+ export const WithEmptyOption = {
159
+ render: () => (
160
+ <Select name="select" defaultValue="1">
161
+ <SelectButton />
162
+ <SelectList>
163
+ <SelectOption value="">None</SelectOption>
164
+ <SelectOption value="1">Option 1</SelectOption>
165
+ <SelectOption value="2">Option 2</SelectOption>
166
+ </SelectList>
136
167
  </Select>
137
- )
168
+ ),
138
169
  }
139
170
 
140
- export const WithNativeSelect = () => (
141
- <>
171
+ export const WithNativeSelect = {
172
+ render: () => (
142
173
  <NativeSelect name="select" options={options} aria-label="Select" />
143
- </>
144
- )
174
+ ),
175
+ }
@@ -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