@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.
- package/CHANGELOG.md +95 -0
- package/README.md +53 -6
- package/dist/ajv/index.d.ts +359 -11
- package/dist/ajv/index.js +7 -9
- package/dist/ajv/index.js.map +1 -1
- package/dist/ajv/index.mjs +7 -10
- package/dist/ajv/index.mjs.map +1 -1
- package/dist/index.d.ts +448 -247
- package/dist/index.js +708 -682
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +688 -662
- package/dist/index.mjs.map +1 -1
- package/dist/yup/index.d.ts +581 -21
- package/dist/yup/index.js +6 -10
- package/dist/yup/index.js.map +1 -1
- package/dist/yup/index.mjs +4 -8
- package/dist/yup/index.mjs.map +1 -1
- package/dist/zod/index.d.ts +581 -11
- package/dist/zod/index.js +5 -0
- package/dist/zod/index.js.map +1 -1
- package/dist/zod/index.mjs +5 -1
- package/dist/zod/index.mjs.map +1 -1
- package/package.json +24 -15
- package/src/array-field.tsx +82 -45
- package/src/auto-form.tsx +7 -3
- package/src/base-field.tsx +54 -0
- package/src/create-field.tsx +144 -0
- package/src/create-form.tsx +54 -0
- package/src/default-fields.tsx +163 -0
- package/src/display-field.tsx +9 -11
- package/src/display-if.tsx +20 -13
- package/src/field-resolver.ts +10 -8
- package/src/field.tsx +18 -445
- package/src/fields-context.tsx +23 -0
- package/src/fields.tsx +34 -21
- package/src/form-context.tsx +84 -0
- package/src/form.tsx +72 -52
- package/src/index.ts +44 -4
- package/src/input-right-button/input-right-button.stories.tsx +2 -2
- package/src/input-right-button/input-right-button.tsx +0 -2
- package/src/layout.tsx +16 -11
- package/src/number-input/number-input.stories.tsx +3 -3
- package/src/number-input/number-input.tsx +9 -5
- package/src/object-field.tsx +13 -8
- package/src/password-input/password-input.stories.tsx +23 -2
- package/src/password-input/password-input.tsx +6 -6
- package/src/pin-input/pin-input.tsx +1 -5
- package/src/radio/radio-input.stories.tsx +1 -1
- package/src/radio/radio-input.tsx +12 -10
- package/src/select/native-select.tsx +1 -4
- package/src/select/select-context.tsx +130 -0
- package/src/select/select.stories.tsx +117 -86
- package/src/select/select.test.tsx +1 -1
- package/src/select/select.tsx +162 -146
- package/src/step-form.tsx +29 -11
- package/src/submit-button.tsx +5 -1
- package/src/types.ts +144 -0
- package/src/use-array-field.tsx +9 -3
- package/src/utils.ts +23 -1
- package/src/watch-field.tsx +2 -6
- /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 {
|
11
|
+
import { StoryFn } from '@storybook/react'
|
12
12
|
|
13
|
-
import { Select } from '
|
14
|
-
import { NativeSelect } from '
|
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
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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 =
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
+
export const DefaultValue = {
|
53
|
+
args: {
|
54
|
+
name: 'select',
|
55
|
+
options,
|
56
|
+
defaultValue: '1',
|
57
|
+
},
|
52
58
|
}
|
53
59
|
|
54
|
-
export const Placeholder =
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
60
|
+
export const Placeholder = {
|
61
|
+
args: {
|
62
|
+
name: 'select',
|
63
|
+
options,
|
64
|
+
placeholder: 'Select an option...',
|
65
|
+
},
|
59
66
|
}
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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 =
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
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 =
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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 =
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
117
|
+
export const Test = {
|
118
|
+
render: () => (
|
119
|
+
<Tag variant="outline" colorScheme="teal">
|
120
|
+
Test
|
121
|
+
</Tag>
|
122
|
+
),
|
113
123
|
}
|
114
124
|
|
115
|
-
export const
|
116
|
-
|
117
|
-
|
118
|
-
|
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
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
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
|
-
|
132
|
-
<Select name="select"
|
133
|
-
<
|
134
|
-
<
|
135
|
-
|
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
|
+
}
|