@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.
Files changed (71) hide show
  1. package/build/cjs/index.js +92 -21
  2. package/build/cjs/index.js.map +1 -1
  3. package/build/esm/BottomSheet/BottomSheet.d.ts +1 -1
  4. package/build/esm/BottomSheet/BottomSheetSurface.d.ts +1 -1
  5. package/build/esm/Button/Button.d.ts +1 -1
  6. package/build/esm/CheckBox/CheckBox.d.ts +1 -1
  7. package/build/esm/Chip/ButtonChip.d.ts +1 -1
  8. package/build/esm/Chip/ChoiceChip.d.ts +1 -1
  9. package/build/esm/Combobox/Combobox.d.ts +7 -7
  10. package/build/esm/Dialog/Dialog.d.ts +1 -1
  11. package/build/esm/Dialog/DialogBackdrop.d.ts +1 -1
  12. package/build/esm/Dialog/DialogSurface.d.ts +1 -1
  13. package/build/esm/Menu/Menu.d.ts +5 -5
  14. package/build/esm/NavRail/NavRailItem.d.ts +3 -3
  15. package/build/esm/RadioButton/RadioGroup.d.ts +1 -1
  16. package/build/esm/Select/CustomContainerExample.d.ts +3 -0
  17. package/build/esm/Select/CustomContainerExample.js +59 -0
  18. package/build/esm/Select/CustomContainerExample.js.map +1 -0
  19. package/build/esm/Select/Select.d.ts +21 -7
  20. package/build/esm/Select/Select.js +52 -10
  21. package/build/esm/Select/Select.js.map +1 -1
  22. package/build/esm/Select/context.d.ts +5 -4
  23. package/build/esm/Select/context.js +2 -1
  24. package/build/esm/Select/context.js.map +1 -1
  25. package/build/esm/Select/defaultRender.d.ts +2 -1
  26. package/build/esm/Select/defaultRender.js +33 -4
  27. package/build/esm/Select/defaultRender.js.map +1 -1
  28. package/build/esm/SelectItem/SelectItem.d.ts +7 -3
  29. package/build/esm/SelectItem/SelectItem.js +14 -3
  30. package/build/esm/SelectItem/SelectItem.js.map +1 -1
  31. package/build/esm/Slider/Slider.d.ts +6 -6
  32. package/build/esm/Snackbar/Snackbar.d.ts +1 -1
  33. package/build/esm/Switch/Switch.d.ts +1 -1
  34. package/build/esm/Tab/Tab.d.ts +1 -1
  35. package/build/esm/Tab/TabList.d.ts +1 -1
  36. package/build/esm/Tab/TabPanel.d.ts +1 -1
  37. package/build/esm/TabIndicator/TabIndicator.d.ts +1 -1
  38. package/build/esm/Table/TableHead.d.ts +1 -1
  39. package/build/esm/TextField/FilledContainer.d.ts +4 -1
  40. package/build/esm/TextField/FilledContainer.js +5 -5
  41. package/build/esm/TextField/FilledContainer.js.map +1 -1
  42. package/build/esm/TextField/TextField.d.ts +1 -1
  43. package/build/esm/ThemeExplorer/ThemeBuilder.js +13 -2
  44. package/build/esm/ThemeExplorer/ThemeBuilder.js.map +1 -1
  45. package/build/esm/ThemeExplorer/ThemeColors.js +33 -15
  46. package/build/esm/ThemeExplorer/ThemeColors.js.map +1 -1
  47. package/build/esm/ThemeExplorer/components.d.ts +1 -1
  48. package/build/esm/ThemeExplorer/components.js +11 -13
  49. package/build/esm/ThemeExplorer/components.js.map +1 -1
  50. package/build/esm/ThemeExplorer/makeColorScheme.d.ts +32 -4
  51. package/build/esm/ThemeExplorer/makeColorScheme.js +41 -8
  52. package/build/esm/ThemeExplorer/makeColorScheme.js.map +1 -1
  53. package/build/esm/Tooltip/Tooltip.d.ts +1 -1
  54. package/build/esm/theme/theme.js +2 -2
  55. package/build/esm/theme/theme.js.map +1 -1
  56. package/build/tsconfig-build.tsbuildinfo +1 -1
  57. package/package.json +3 -3
  58. package/src/Select/CustomContainerExample.tsx +59 -0
  59. package/src/Select/Select.story.tsx +68 -69
  60. package/src/Select/Select.tsx +99 -27
  61. package/src/Select/SelectMultiple.story.tsx +215 -0
  62. package/src/Select/context.ts +5 -3
  63. package/src/Select/defaultRender.tsx +49 -0
  64. package/src/SelectItem/SelectItem.tsx +68 -46
  65. package/src/TextField/FilledContainer.tsx +6 -5
  66. package/src/ThemeExplorer/ThemeBuilder.tsx +16 -5
  67. package/src/ThemeExplorer/ThemeColors.tsx +39 -15
  68. package/src/ThemeExplorer/components.tsx +12 -20
  69. package/src/ThemeExplorer/makeColorScheme.tsx +39 -6
  70. package/src/theme/theme.ts +2 -2
  71. 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.32",
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.50",
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": "42c1e44b1429227fe589e0f7a4bfeeb15b5e395a"
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
- const Example = ({ variant, native = false }) => {
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 as 'primary' | 'secondary')}
113
+ onChange={(e, value) => setColor(value)}
88
114
  label="Color"
115
+ CustomContainer={Container}
89
116
  >
90
- <SelectItem value="primary">Primary</SelectItem>
91
- <SelectItem value="secondary">Secondary</SelectItem>
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 />;
@@ -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
- export interface SelectProps
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 const Select = forwardRef<
54
- HTMLSelectElement | HTMLButtonElement,
55
- SelectProps
56
- >(function Select(props, forwardedRef) {
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 = componentMap[variant] || OutlinedContainer;
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
- onChange &&
108
- onChange(
109
- e as any,
110
- native ? e.target.value : e.currentTarget.dataset.value
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 renderValue = renderValueProp || makeDefaultRender(children);
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
- const labelIsFloating = hasFocus || open || renderValue(value) !== undefined;
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 value={{ native, onSelect: handleOnChange, value }}>
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;