@basic-ui/material 1.0.0-alpha.32 → 1.0.0-alpha.34
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/build/cjs/index.js +92 -21
- package/build/cjs/index.js.map +1 -1
- package/build/esm/BottomSheet/BottomSheet.d.ts +1 -1
- package/build/esm/BottomSheet/BottomSheetSurface.d.ts +1 -1
- package/build/esm/Button/Button.d.ts +1 -1
- package/build/esm/CheckBox/CheckBox.d.ts +1 -1
- package/build/esm/Chip/ButtonChip.d.ts +1 -1
- package/build/esm/Chip/ChoiceChip.d.ts +1 -1
- package/build/esm/Combobox/Combobox.d.ts +7 -7
- package/build/esm/Dialog/Dialog.d.ts +1 -1
- package/build/esm/Dialog/DialogBackdrop.d.ts +1 -1
- package/build/esm/Dialog/DialogSurface.d.ts +1 -1
- package/build/esm/Menu/Menu.d.ts +5 -5
- package/build/esm/NavRail/NavRailItem.d.ts +3 -3
- package/build/esm/RadioButton/RadioGroup.d.ts +1 -1
- package/build/esm/Select/CustomContainerExample.d.ts +3 -0
- package/build/esm/Select/CustomContainerExample.js +59 -0
- package/build/esm/Select/CustomContainerExample.js.map +1 -0
- package/build/esm/Select/Select.d.ts +21 -7
- package/build/esm/Select/Select.js +52 -10
- package/build/esm/Select/Select.js.map +1 -1
- package/build/esm/Select/context.d.ts +5 -4
- package/build/esm/Select/context.js +2 -1
- package/build/esm/Select/context.js.map +1 -1
- package/build/esm/Select/defaultRender.d.ts +2 -1
- package/build/esm/Select/defaultRender.js +33 -4
- package/build/esm/Select/defaultRender.js.map +1 -1
- package/build/esm/SelectItem/SelectItem.d.ts +7 -3
- package/build/esm/SelectItem/SelectItem.js +14 -3
- package/build/esm/SelectItem/SelectItem.js.map +1 -1
- package/build/esm/Slider/Slider.d.ts +6 -6
- package/build/esm/Snackbar/Snackbar.d.ts +1 -1
- package/build/esm/Switch/Switch.d.ts +1 -1
- package/build/esm/Tab/Tab.d.ts +1 -1
- package/build/esm/Tab/TabList.d.ts +1 -1
- package/build/esm/Tab/TabPanel.d.ts +1 -1
- package/build/esm/TabIndicator/TabIndicator.d.ts +1 -1
- package/build/esm/Table/TableHead.d.ts +1 -1
- package/build/esm/TextField/FilledContainer.d.ts +4 -1
- package/build/esm/TextField/FilledContainer.js +5 -5
- package/build/esm/TextField/FilledContainer.js.map +1 -1
- package/build/esm/TextField/TextField.d.ts +1 -1
- package/build/esm/ThemeExplorer/ThemeBuilder.js +13 -2
- package/build/esm/ThemeExplorer/ThemeBuilder.js.map +1 -1
- package/build/esm/ThemeExplorer/ThemeColors.js +33 -15
- package/build/esm/ThemeExplorer/ThemeColors.js.map +1 -1
- package/build/esm/ThemeExplorer/components.d.ts +1 -1
- package/build/esm/ThemeExplorer/components.js +11 -13
- package/build/esm/ThemeExplorer/components.js.map +1 -1
- package/build/esm/ThemeExplorer/makeColorScheme.d.ts +32 -4
- package/build/esm/ThemeExplorer/makeColorScheme.js +41 -8
- package/build/esm/ThemeExplorer/makeColorScheme.js.map +1 -1
- package/build/esm/Tooltip/Tooltip.d.ts +1 -1
- package/build/esm/theme/theme.js +2 -2
- package/build/esm/theme/theme.js.map +1 -1
- package/build/tsconfig-build.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/Select/CustomContainerExample.tsx +59 -0
- package/src/Select/Select.story.tsx +68 -69
- package/src/Select/Select.tsx +99 -27
- package/src/Select/SelectMultiple.story.tsx +215 -0
- package/src/Select/context.ts +5 -3
- package/src/Select/defaultRender.tsx +49 -0
- package/src/SelectItem/SelectItem.tsx +68 -46
- package/src/TextField/FilledContainer.tsx +6 -5
- package/src/ThemeExplorer/ThemeBuilder.tsx +16 -5
- package/src/ThemeExplorer/ThemeColors.tsx +39 -15
- package/src/ThemeExplorer/components.tsx +12 -20
- package/src/ThemeExplorer/makeColorScheme.tsx +39 -6
- package/src/theme/theme.ts +2 -2
- package/src/Select/defaultRender.ts +0 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@basic-ui/material",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.34",
|
|
4
4
|
"description": "Accessible React Components used as building blocks for UI patterns",
|
|
5
5
|
"author": "Lucas Terra <lucasterra7@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@basic-ui/core": "^0.0.
|
|
30
|
+
"@basic-ui/core": "^0.0.51",
|
|
31
31
|
"@basic-ui/dynamic-theme": "^0.0.8",
|
|
32
32
|
"@styled-system/should-forward-prop": "5.1.5",
|
|
33
33
|
"@types/styled-system": "^5.1.10",
|
|
@@ -52,5 +52,5 @@
|
|
|
52
52
|
"react": "^16.14.0 || ^17.0.0 || ^18.0.0",
|
|
53
53
|
"react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0"
|
|
54
54
|
},
|
|
55
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "5442c3e57c390b7f69adab60118d7d6df9f55df0"
|
|
56
56
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { forwardRef } from 'react';
|
|
2
|
+
|
|
3
|
+
import { alpha } from '../color';
|
|
4
|
+
import { Box } from '../Box';
|
|
5
|
+
import type { FilledContainerProps } from '../';
|
|
6
|
+
import { FilledContainerOverlay } from '../';
|
|
7
|
+
|
|
8
|
+
export const CustomContainer = forwardRef<HTMLDivElement, FilledContainerProps>(
|
|
9
|
+
function CustomContainer(props, forwardedRef) {
|
|
10
|
+
const {
|
|
11
|
+
label,
|
|
12
|
+
labelIsFloating,
|
|
13
|
+
inputId,
|
|
14
|
+
hasFocus,
|
|
15
|
+
color: colorProp,
|
|
16
|
+
children,
|
|
17
|
+
error = false,
|
|
18
|
+
disabled = false,
|
|
19
|
+
forceActive = false,
|
|
20
|
+
leadingIcon,
|
|
21
|
+
...otherProps
|
|
22
|
+
} = props;
|
|
23
|
+
|
|
24
|
+
const active = hasFocus || forceActive;
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Box
|
|
28
|
+
ref={forwardedRef}
|
|
29
|
+
disabled={disabled}
|
|
30
|
+
active={active || error}
|
|
31
|
+
__css={{
|
|
32
|
+
variant: 'text.label-small',
|
|
33
|
+
position: 'relative',
|
|
34
|
+
lineHeight: 0,
|
|
35
|
+
width: '100%',
|
|
36
|
+
height: 32,
|
|
37
|
+
overflow: 'hidden',
|
|
38
|
+
boxSizing: 'border-box',
|
|
39
|
+
borderRadius: 'full',
|
|
40
|
+
color: alpha('on.surface-variant', 0.87),
|
|
41
|
+
...(disabled && {
|
|
42
|
+
backgroundColor: alpha('on.surface-variant', 0.08),
|
|
43
|
+
color: alpha('on.surface-variant', 0.38),
|
|
44
|
+
}),
|
|
45
|
+
...(active && { color: 'primary' }),
|
|
46
|
+
'& > [role="button"]': {
|
|
47
|
+
variant: 'text.label-medium',
|
|
48
|
+
minHeight: 32,
|
|
49
|
+
py: 0,
|
|
50
|
+
},
|
|
51
|
+
}}
|
|
52
|
+
{...otherProps}
|
|
53
|
+
>
|
|
54
|
+
{children}
|
|
55
|
+
<FilledContainerOverlay forceActive={active} />
|
|
56
|
+
</Box>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
);
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
|
|
3
|
+
import type { SelectProps } from './';
|
|
3
4
|
import { Select } from './';
|
|
4
5
|
import { SelectItem } from '../SelectItem';
|
|
5
6
|
import { Box } from '../Box';
|
|
6
7
|
import { CheckBox } from '../CheckBox';
|
|
8
|
+
import { CustomContainer as CustomContainerExample } from './CustomContainerExample';
|
|
7
9
|
// import './styles.css';
|
|
8
10
|
|
|
9
11
|
export default {
|
|
10
12
|
title: 'components/Select',
|
|
11
13
|
};
|
|
12
14
|
|
|
13
|
-
const SearchIcon = (props) => (
|
|
15
|
+
const SearchIcon = (props: any) => (
|
|
14
16
|
<svg
|
|
15
17
|
xmlns="http://www.w3.org/2000/svg"
|
|
16
18
|
height={24}
|
|
@@ -23,50 +25,74 @@ const SearchIcon = (props) => (
|
|
|
23
25
|
</svg>
|
|
24
26
|
);
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
type OptionType =
|
|
29
|
+
| ''
|
|
30
|
+
| '10'
|
|
31
|
+
| '20'
|
|
32
|
+
| '30'
|
|
33
|
+
| '40'
|
|
34
|
+
| '50'
|
|
35
|
+
| '60'
|
|
36
|
+
| '70'
|
|
37
|
+
| '80'
|
|
38
|
+
| '90'
|
|
39
|
+
| '100'
|
|
40
|
+
| '110'
|
|
41
|
+
| '120'
|
|
42
|
+
| '130';
|
|
43
|
+
|
|
44
|
+
const Example = ({
|
|
45
|
+
variant,
|
|
46
|
+
native = false,
|
|
47
|
+
CustomContainer: Container = undefined,
|
|
48
|
+
}: {
|
|
49
|
+
variant?: 'outlined' | 'filled';
|
|
50
|
+
native?: boolean;
|
|
51
|
+
CustomContainer?: SelectProps<OptionType>['CustomContainer'];
|
|
52
|
+
}) => {
|
|
27
53
|
const [error, setError] = useState<boolean | string>(false);
|
|
28
54
|
const [color, setColor] = useState<'primary' | 'secondary'>('primary');
|
|
29
55
|
|
|
30
56
|
/* eslint-disable react/no-children-prop */
|
|
31
57
|
const children = [
|
|
32
|
-
<SelectItem key={0} aria-label="None" value=""></SelectItem>,
|
|
33
|
-
<SelectItem key={1} value={10}>
|
|
58
|
+
<SelectItem<OptionType> key={0} aria-label="None" value=""></SelectItem>,
|
|
59
|
+
<SelectItem<OptionType> key={1} value={'10'}>
|
|
34
60
|
Ten
|
|
35
61
|
</SelectItem>,
|
|
36
|
-
<SelectItem key={2} value={20}>
|
|
62
|
+
<SelectItem<OptionType> key={2} value={'20'}>
|
|
37
63
|
Twenty
|
|
38
64
|
</SelectItem>,
|
|
39
|
-
<SelectItem key={3} value={30}>
|
|
65
|
+
<SelectItem<OptionType> key={3} value={'30'}>
|
|
40
66
|
Thirty
|
|
41
67
|
</SelectItem>,
|
|
42
|
-
<SelectItem key={4} value={40}>
|
|
68
|
+
<SelectItem<OptionType> key={4} value={'40'}>
|
|
43
69
|
Fourty
|
|
44
70
|
</SelectItem>,
|
|
45
|
-
<SelectItem key={5} value={50}>
|
|
71
|
+
<SelectItem<OptionType> key={5} value={'50'}>
|
|
46
72
|
Fifty
|
|
47
73
|
</SelectItem>,
|
|
48
|
-
<SelectItem key={6} value={60}>
|
|
74
|
+
<SelectItem<OptionType> key={6} value={'60'}>
|
|
49
75
|
Sixty
|
|
50
76
|
</SelectItem>,
|
|
51
|
-
<SelectItem key={7} value={70}>
|
|
77
|
+
<SelectItem<OptionType> key={7} value={'70'}>
|
|
52
78
|
Seventy
|
|
53
79
|
</SelectItem>,
|
|
54
|
-
<SelectItem key={8} value={80}>
|
|
80
|
+
<SelectItem<OptionType> key={8} value={'80'}>
|
|
55
81
|
Eighty
|
|
56
82
|
</SelectItem>,
|
|
57
|
-
<SelectItem key={9} value={90}>
|
|
83
|
+
<SelectItem<OptionType> key={9} value={'90'}>
|
|
58
84
|
Ninety
|
|
59
85
|
</SelectItem>,
|
|
60
|
-
<SelectItem key={10} value={100}>
|
|
86
|
+
<SelectItem<OptionType> key={10} value={'100'}>
|
|
61
87
|
One Hundred
|
|
62
88
|
</SelectItem>,
|
|
63
|
-
<SelectItem key={11} value={110}>
|
|
89
|
+
<SelectItem<OptionType> key={11} value={'110'}>
|
|
64
90
|
One Hundred Ten
|
|
65
91
|
</SelectItem>,
|
|
66
|
-
<SelectItem key={12} value={120}>
|
|
92
|
+
<SelectItem<OptionType> key={12} value={'120'}>
|
|
67
93
|
One Hundred Twenty
|
|
68
94
|
</SelectItem>,
|
|
69
|
-
<SelectItem key={13} value={130}>
|
|
95
|
+
<SelectItem<OptionType> key={13} value={'130'}>
|
|
70
96
|
One Hundred Thirty
|
|
71
97
|
</SelectItem>,
|
|
72
98
|
];
|
|
@@ -82,60 +108,23 @@ const Example = ({ variant, native = false }) => {
|
|
|
82
108
|
Has Error
|
|
83
109
|
</CheckBox>
|
|
84
110
|
<Box width={230} display="inline-block">
|
|
85
|
-
<Select
|
|
111
|
+
<Select<'primary' | 'secondary'>
|
|
86
112
|
value={color}
|
|
87
|
-
onChange={(e, value) => setColor(value
|
|
113
|
+
onChange={(e, value) => setColor(value)}
|
|
88
114
|
label="Color"
|
|
115
|
+
CustomContainer={Container}
|
|
89
116
|
>
|
|
90
|
-
<SelectItem value="primary">
|
|
91
|
-
|
|
117
|
+
<SelectItem<'primary' | 'secondary'> value="primary">
|
|
118
|
+
Primary
|
|
119
|
+
</SelectItem>
|
|
120
|
+
<SelectItem<'primary' | 'secondary'> value="secondary">
|
|
121
|
+
Secondary
|
|
122
|
+
</SelectItem>
|
|
92
123
|
</Select>
|
|
93
124
|
</Box>
|
|
94
|
-
<select defaultValue="">
|
|
95
|
-
<option key={0} aria-label="None" value="" disabled />
|
|
96
|
-
<option key={1} value={10}>
|
|
97
|
-
Ten
|
|
98
|
-
</option>
|
|
99
|
-
<option key={2} value={20}>
|
|
100
|
-
Twenty
|
|
101
|
-
</option>
|
|
102
|
-
<option key={3} value={30}>
|
|
103
|
-
Thirty
|
|
104
|
-
</option>
|
|
105
|
-
<option key={4} value={40}>
|
|
106
|
-
Fourty
|
|
107
|
-
</option>
|
|
108
|
-
<option key={5} value={50}>
|
|
109
|
-
Fifty
|
|
110
|
-
</option>
|
|
111
|
-
<option key={6} value={60}>
|
|
112
|
-
Sixty
|
|
113
|
-
</option>
|
|
114
|
-
<option key={7} value={70}>
|
|
115
|
-
Seventy
|
|
116
|
-
</option>
|
|
117
|
-
<option key={8} value={80}>
|
|
118
|
-
Eighty
|
|
119
|
-
</option>
|
|
120
|
-
<option key={9} value={90}>
|
|
121
|
-
Ninety
|
|
122
|
-
</option>
|
|
123
|
-
<option key={10} value={100}>
|
|
124
|
-
One Hundred
|
|
125
|
-
</option>
|
|
126
|
-
<option key={11} value={110}>
|
|
127
|
-
One Hundred Ten
|
|
128
|
-
</option>
|
|
129
|
-
<option key={12} value={120}>
|
|
130
|
-
One Hundred Twenty
|
|
131
|
-
</option>
|
|
132
|
-
<option key={13} value={130}>
|
|
133
|
-
One Hundred Thirty
|
|
134
|
-
</option>
|
|
135
|
-
</select>
|
|
136
125
|
<Box m={3} bg="surface">
|
|
137
126
|
<Box mb={3} width={230}>
|
|
138
|
-
<Select
|
|
127
|
+
<Select<OptionType>
|
|
139
128
|
error={error}
|
|
140
129
|
color={color}
|
|
141
130
|
native={native}
|
|
@@ -144,10 +133,11 @@ const Example = ({ variant, native = false }) => {
|
|
|
144
133
|
helperText="Helper text"
|
|
145
134
|
children={children}
|
|
146
135
|
placeholder="Empty"
|
|
136
|
+
CustomContainer={Container}
|
|
147
137
|
/>
|
|
148
138
|
</Box>
|
|
149
139
|
<Box mb={3} width={230}>
|
|
150
|
-
<Select
|
|
140
|
+
<Select<OptionType>
|
|
151
141
|
error={error}
|
|
152
142
|
color={color}
|
|
153
143
|
native={native}
|
|
@@ -156,20 +146,22 @@ const Example = ({ variant, native = false }) => {
|
|
|
156
146
|
defaultValue="20"
|
|
157
147
|
helperText="Helper text"
|
|
158
148
|
children={children}
|
|
149
|
+
CustomContainer={Container}
|
|
159
150
|
/>
|
|
160
151
|
</Box>
|
|
161
152
|
<Box mb={3} width={230}>
|
|
162
|
-
<Select
|
|
153
|
+
<Select<OptionType>
|
|
163
154
|
error={error}
|
|
164
155
|
color={color}
|
|
165
156
|
native={native}
|
|
166
157
|
variant={variant}
|
|
167
158
|
label="Standard"
|
|
168
159
|
children={children}
|
|
160
|
+
CustomContainer={Container}
|
|
169
161
|
/>
|
|
170
162
|
</Box>
|
|
171
163
|
<Box mb={3} width={230}>
|
|
172
|
-
<Select
|
|
164
|
+
<Select<OptionType>
|
|
173
165
|
error={error}
|
|
174
166
|
color={color}
|
|
175
167
|
native={native}
|
|
@@ -178,10 +170,11 @@ const Example = ({ variant, native = false }) => {
|
|
|
178
170
|
helperText="Helper text"
|
|
179
171
|
disabled
|
|
180
172
|
children={children}
|
|
173
|
+
CustomContainer={Container}
|
|
181
174
|
/>
|
|
182
175
|
</Box>
|
|
183
176
|
<Box mb={3} width={230}>
|
|
184
|
-
<Select
|
|
177
|
+
<Select<OptionType>
|
|
185
178
|
error={error}
|
|
186
179
|
color={color}
|
|
187
180
|
native={native}
|
|
@@ -189,10 +182,11 @@ const Example = ({ variant, native = false }) => {
|
|
|
189
182
|
label=""
|
|
190
183
|
helperText="Helper text"
|
|
191
184
|
children={children}
|
|
185
|
+
CustomContainer={Container}
|
|
192
186
|
/>
|
|
193
187
|
</Box>
|
|
194
188
|
<Box mb={3} width={230}>
|
|
195
|
-
<Select
|
|
189
|
+
<Select<OptionType>
|
|
196
190
|
error={error}
|
|
197
191
|
color={color}
|
|
198
192
|
native={native}
|
|
@@ -201,10 +195,11 @@ const Example = ({ variant, native = false }) => {
|
|
|
201
195
|
helperText="Helper text"
|
|
202
196
|
children={children}
|
|
203
197
|
leadingIcon={<SearchIcon />}
|
|
198
|
+
CustomContainer={Container}
|
|
204
199
|
/>
|
|
205
200
|
</Box>
|
|
206
201
|
<Box mb={3} width={230}>
|
|
207
|
-
<Select
|
|
202
|
+
<Select<OptionType>
|
|
208
203
|
error={error}
|
|
209
204
|
color={color}
|
|
210
205
|
native={native}
|
|
@@ -213,6 +208,7 @@ const Example = ({ variant, native = false }) => {
|
|
|
213
208
|
helperText="Helper text"
|
|
214
209
|
children={children}
|
|
215
210
|
leadingIcon={<SearchIcon />}
|
|
211
|
+
CustomContainer={Container}
|
|
216
212
|
/>
|
|
217
213
|
</Box>
|
|
218
214
|
</Box>
|
|
@@ -221,6 +217,9 @@ const Example = ({ variant, native = false }) => {
|
|
|
221
217
|
};
|
|
222
218
|
|
|
223
219
|
export const Filled = () => <Example variant="filled" />;
|
|
220
|
+
export const CustomContainer = () => (
|
|
221
|
+
<Example CustomContainer={CustomContainerExample} />
|
|
222
|
+
);
|
|
224
223
|
export const Outlined = () => <Example variant="outlined" />;
|
|
225
224
|
export const FilledNative = () => <Example variant="filled" native />;
|
|
226
225
|
export const OutlinedNative = () => <Example variant="outlined" native />;
|
package/src/Select/Select.tsx
CHANGED
|
@@ -3,8 +3,11 @@ import type {
|
|
|
3
3
|
ReactNode,
|
|
4
4
|
ChangeEvent,
|
|
5
5
|
ComponentType,
|
|
6
|
+
ForwardedRef,
|
|
7
|
+
ReactElement,
|
|
8
|
+
Ref,
|
|
6
9
|
} from 'react';
|
|
7
|
-
import { forwardRef, useState, useRef, useEffect, useId } from 'react';
|
|
10
|
+
import { useMemo, forwardRef, useState, useRef, useEffect, useId } from 'react';
|
|
8
11
|
import {
|
|
9
12
|
wrapEvent,
|
|
10
13
|
assignMultipleRefs,
|
|
@@ -14,13 +17,14 @@ import {
|
|
|
14
17
|
import type { Theme } from '../theme';
|
|
15
18
|
import { useTheme } from '../theme';
|
|
16
19
|
import { Select as SelectComp, SelectButton } from './styledComponents';
|
|
20
|
+
import type { FilledContainerProps } from '../TextField/FilledContainer';
|
|
17
21
|
import { FilledContainer } from '../TextField/FilledContainer';
|
|
18
22
|
import { HelperText } from '../TextField/HelperText';
|
|
19
23
|
import { OutlinedContainer } from '../TextField/OutlinedContainer';
|
|
20
24
|
import { SelectProvider } from './context';
|
|
21
25
|
import { Menu, MenuPopover, MenuList } from '../Menu';
|
|
22
26
|
import { SelectIcon } from './SelectIcon';
|
|
23
|
-
import { makeDefaultRender } from './defaultRender';
|
|
27
|
+
import { makeDefaultMultipleRender, makeDefaultRender } from './defaultRender';
|
|
24
28
|
import type { BoxProps } from '../Box';
|
|
25
29
|
import { Box } from '../Box';
|
|
26
30
|
import { IconContainer } from '../TextField/IconContainer';
|
|
@@ -31,7 +35,7 @@ const componentMap = {
|
|
|
31
35
|
filled: FilledContainer,
|
|
32
36
|
};
|
|
33
37
|
|
|
34
|
-
|
|
38
|
+
interface BaseSelectProps
|
|
35
39
|
extends Omit<
|
|
36
40
|
BoxProps<HTMLSelectElement, SelectHTMLAttributes<HTMLSelectElement>>,
|
|
37
41
|
'value' | 'defaultValue' | 'onChange'
|
|
@@ -40,27 +44,48 @@ export interface SelectProps
|
|
|
40
44
|
color?: 'primary' | 'secondary';
|
|
41
45
|
label?: ReactNode;
|
|
42
46
|
helperText?: string;
|
|
43
|
-
defaultValue?: string;
|
|
44
|
-
value?: string;
|
|
45
47
|
native?: boolean;
|
|
46
48
|
theme?: Theme;
|
|
47
49
|
error?: boolean | string;
|
|
48
|
-
onChange?: (e: ChangeEvent<HTMLSelectElement>, value: string) => void;
|
|
49
|
-
renderValue?: (value?: string) => string | undefined;
|
|
50
50
|
leadingIcon?: ReactNode;
|
|
51
|
+
CustomContainer?: ComponentType<FilledContainerProps>;
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
export
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
export type SelectProps<ValueType extends string> = BaseSelectProps &
|
|
55
|
+
(
|
|
56
|
+
| {
|
|
57
|
+
multiple?: false | undefined;
|
|
58
|
+
value?: ValueType;
|
|
59
|
+
defaultValue?: ValueType;
|
|
60
|
+
onChange?: (
|
|
61
|
+
e: ChangeEvent<HTMLSelectElement>,
|
|
62
|
+
value: ValueType
|
|
63
|
+
) => void;
|
|
64
|
+
renderValue?: (value?: ValueType | '') => ReactNode | undefined;
|
|
65
|
+
}
|
|
66
|
+
| {
|
|
67
|
+
multiple: true;
|
|
68
|
+
value?: ValueType[];
|
|
69
|
+
defaultValue?: ValueType[];
|
|
70
|
+
onChange?: (
|
|
71
|
+
e: ChangeEvent<HTMLSelectElement>,
|
|
72
|
+
value: ValueType[]
|
|
73
|
+
) => void;
|
|
74
|
+
renderValue?: (value?: ValueType[]) => ReactNode[];
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
export const Select = forwardRef(function Select<ValueType extends string>(
|
|
79
|
+
props: SelectProps<ValueType>,
|
|
80
|
+
forwardedRef: ForwardedRef<HTMLSelectElement | HTMLButtonElement>
|
|
81
|
+
) {
|
|
57
82
|
const {
|
|
58
83
|
id: idProp,
|
|
59
84
|
name,
|
|
60
85
|
variant = 'outlined',
|
|
61
86
|
color = 'primary',
|
|
62
87
|
value: valueProp,
|
|
63
|
-
defaultValue = '',
|
|
88
|
+
defaultValue = props.multiple ? [] : ('' as const),
|
|
64
89
|
disabled,
|
|
65
90
|
error = false,
|
|
66
91
|
label = null,
|
|
@@ -70,26 +95,38 @@ export const Select = forwardRef<
|
|
|
70
95
|
onFocus,
|
|
71
96
|
onBlur,
|
|
72
97
|
native = false,
|
|
98
|
+
multiple = false,
|
|
73
99
|
children,
|
|
74
100
|
renderValue: renderValueProp,
|
|
75
101
|
leadingIcon = null,
|
|
102
|
+
CustomContainer: overwrittenContainer,
|
|
76
103
|
...otherProps
|
|
77
104
|
} = props;
|
|
78
105
|
const [value, onChange] = useControlledState(
|
|
79
106
|
valueProp,
|
|
80
|
-
onChangeProp
|
|
107
|
+
onChangeProp as (
|
|
108
|
+
e: ChangeEvent<HTMLSelectElement>,
|
|
109
|
+
value: ValueType | ValueType[]
|
|
110
|
+
) => void,
|
|
81
111
|
defaultValue,
|
|
82
|
-
(setState) => (e, v) => {
|
|
112
|
+
(setState) => (e, v: ValueType | ValueType[]) => {
|
|
83
113
|
setState(v);
|
|
84
114
|
}
|
|
85
115
|
);
|
|
116
|
+
if (multiple && !Array.isArray(value)) {
|
|
117
|
+
console.warn(
|
|
118
|
+
'Warning: The `value` prop supplied to <Select> must be an array if `multiple` is true.'
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
86
122
|
const [hasFocus, setHasFocus] = useState(false);
|
|
87
123
|
const buttonRef = useRef<HTMLButtonElement | HTMLSelectElement>();
|
|
88
124
|
const [open, setOpen] = useState(false);
|
|
89
125
|
const fallbackId = useId();
|
|
90
126
|
const theme = useTheme();
|
|
91
127
|
|
|
92
|
-
const Container =
|
|
128
|
+
const Container =
|
|
129
|
+
overwrittenContainer || componentMap[variant] || OutlinedContainer;
|
|
93
130
|
|
|
94
131
|
const handleFocus = () => {
|
|
95
132
|
setHasFocus(true);
|
|
@@ -104,11 +141,25 @@ export const Select = forwardRef<
|
|
|
104
141
|
};
|
|
105
142
|
|
|
106
143
|
const handleOnChange = (e: any) => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
144
|
+
const selectedValue = native
|
|
145
|
+
? e.target.value
|
|
146
|
+
: e.currentTarget.dataset.value;
|
|
147
|
+
|
|
148
|
+
if (multiple && Array.isArray(value)) {
|
|
149
|
+
if (value.find((c) => c === selectedValue)) {
|
|
150
|
+
onChange &&
|
|
151
|
+
onChange(e as any, value.filter((c) => c !== selectedValue) as any);
|
|
152
|
+
} else {
|
|
153
|
+
onChange && onChange(e as any, [...value, selectedValue] as any);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const isMac = Boolean(/mac os|ios/i.test(navigator.userAgent));
|
|
157
|
+
if (e.key === ' ' || (isMac && e.metaKey) || (!isMac && e.ctrlKey)) {
|
|
158
|
+
e.preventDefault();
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
onChange && onChange(e as any, selectedValue);
|
|
162
|
+
}
|
|
112
163
|
};
|
|
113
164
|
|
|
114
165
|
const hasError = Boolean(error);
|
|
@@ -118,9 +169,23 @@ export const Select = forwardRef<
|
|
|
118
169
|
const inputId = `${id}-text-field`;
|
|
119
170
|
const helperTextId = helperText ? `${id}-helper-text` : undefined;
|
|
120
171
|
|
|
121
|
-
const
|
|
172
|
+
const defaultRenderFn = useMemo(
|
|
173
|
+
() =>
|
|
174
|
+
multiple
|
|
175
|
+
? makeDefaultMultipleRender(children)
|
|
176
|
+
: makeDefaultRender<ValueType>(children),
|
|
177
|
+
[children, multiple]
|
|
178
|
+
);
|
|
179
|
+
const renderValue = renderValueProp || defaultRenderFn;
|
|
122
180
|
|
|
123
|
-
|
|
181
|
+
function hasAnySelected() {
|
|
182
|
+
if (multiple) {
|
|
183
|
+
return value.length > 0;
|
|
184
|
+
} else {
|
|
185
|
+
return value !== '';
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const labelIsFloating = hasFocus || open || hasAnySelected();
|
|
124
189
|
|
|
125
190
|
const Comp: ComponentType<any> = native
|
|
126
191
|
? (SelectComp as any)
|
|
@@ -131,13 +196,15 @@ export const Select = forwardRef<
|
|
|
131
196
|
// is different than the value we have stored in state we need to
|
|
132
197
|
// update our state to reflect that.
|
|
133
198
|
if (native && buttonRef.current && buttonRef.current.value !== value) {
|
|
134
|
-
onChange && onChange({} as any, buttonRef.current.value);
|
|
199
|
+
onChange && onChange({} as any, buttonRef.current.value as ValueType);
|
|
135
200
|
}
|
|
136
201
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
137
202
|
}, []);
|
|
138
203
|
|
|
139
204
|
return (
|
|
140
|
-
<SelectProvider
|
|
205
|
+
<SelectProvider
|
|
206
|
+
value={{ native, onSelect: handleOnChange, value, multiple }}
|
|
207
|
+
>
|
|
141
208
|
<Box display="inline-flex" flexDirection="column" width="100%">
|
|
142
209
|
{!native && <input type="hidden" name={name} value={value} />}
|
|
143
210
|
<Container
|
|
@@ -147,7 +214,7 @@ export const Select = forwardRef<
|
|
|
147
214
|
labelIsFloating={labelIsFloating}
|
|
148
215
|
inputId={inputId}
|
|
149
216
|
hasFocus={hasFocus}
|
|
150
|
-
disabled={disabled}
|
|
217
|
+
disabled={disabled ?? false}
|
|
151
218
|
forceActive={open}
|
|
152
219
|
error={hasError}
|
|
153
220
|
leadingIcon={Boolean(leadingIcon)}
|
|
@@ -172,10 +239,11 @@ export const Select = forwardRef<
|
|
|
172
239
|
hasLabel={!!label}
|
|
173
240
|
leadingIcon={Boolean(leadingIcon)}
|
|
174
241
|
name={native ? name : undefined}
|
|
242
|
+
multiple={native ? multiple : undefined}
|
|
175
243
|
trailingIcon={true}
|
|
176
244
|
{...otherProps}
|
|
177
245
|
>
|
|
178
|
-
{native ? children : renderValue(value)}
|
|
246
|
+
{native ? children : renderValue(value as any)}
|
|
179
247
|
</Comp>
|
|
180
248
|
{!native && (
|
|
181
249
|
<MenuPopover usePortal>
|
|
@@ -208,4 +276,8 @@ export const Select = forwardRef<
|
|
|
208
276
|
</Box>
|
|
209
277
|
</SelectProvider>
|
|
210
278
|
);
|
|
211
|
-
})
|
|
279
|
+
}) as <ValueType extends string>(
|
|
280
|
+
p: SelectProps<ValueType> & {
|
|
281
|
+
ref?: Ref<HTMLSelectElement | HTMLButtonElement>;
|
|
282
|
+
}
|
|
283
|
+
) => ReactElement;
|