@basic-ui/material 1.0.0-alpha.33 → 1.0.0-alpha.35
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 +83 -14
- 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 +1 -1
- package/build/esm/Select/Select.d.ts +19 -7
- package/build/esm/Select/Select.js +49 -8
- 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/TextField/TextField.d.ts +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/Select.story.tsx +47 -69
- package/src/Select/Select.tsx +93 -25
- 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/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.35",
|
|
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": "273a88616867cc95b2b6b686f96fcb5a702234ed"
|
|
56
56
|
}
|
|
@@ -12,7 +12,7 @@ export default {
|
|
|
12
12
|
title: 'components/Select',
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
const SearchIcon = (props) => (
|
|
15
|
+
const SearchIcon = (props: any) => (
|
|
16
16
|
<svg
|
|
17
17
|
xmlns="http://www.w3.org/2000/svg"
|
|
18
18
|
height={24}
|
|
@@ -25,6 +25,22 @@ const SearchIcon = (props) => (
|
|
|
25
25
|
</svg>
|
|
26
26
|
);
|
|
27
27
|
|
|
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
|
+
|
|
28
44
|
const Example = ({
|
|
29
45
|
variant,
|
|
30
46
|
native = false,
|
|
@@ -32,51 +48,51 @@ const Example = ({
|
|
|
32
48
|
}: {
|
|
33
49
|
variant?: 'outlined' | 'filled';
|
|
34
50
|
native?: boolean;
|
|
35
|
-
CustomContainer?: SelectProps['CustomContainer'];
|
|
51
|
+
CustomContainer?: SelectProps<OptionType>['CustomContainer'];
|
|
36
52
|
}) => {
|
|
37
53
|
const [error, setError] = useState<boolean | string>(false);
|
|
38
54
|
const [color, setColor] = useState<'primary' | 'secondary'>('primary');
|
|
39
55
|
|
|
40
56
|
/* eslint-disable react/no-children-prop */
|
|
41
57
|
const children = [
|
|
42
|
-
<SelectItem key={0} aria-label="None" value=""></SelectItem>,
|
|
43
|
-
<SelectItem key={1} value={10}>
|
|
58
|
+
<SelectItem<OptionType> key={0} aria-label="None" value=""></SelectItem>,
|
|
59
|
+
<SelectItem<OptionType> key={1} value={'10'}>
|
|
44
60
|
Ten
|
|
45
61
|
</SelectItem>,
|
|
46
|
-
<SelectItem key={2} value={20}>
|
|
62
|
+
<SelectItem<OptionType> key={2} value={'20'}>
|
|
47
63
|
Twenty
|
|
48
64
|
</SelectItem>,
|
|
49
|
-
<SelectItem key={3} value={30}>
|
|
65
|
+
<SelectItem<OptionType> key={3} value={'30'}>
|
|
50
66
|
Thirty
|
|
51
67
|
</SelectItem>,
|
|
52
|
-
<SelectItem key={4} value={40}>
|
|
68
|
+
<SelectItem<OptionType> key={4} value={'40'}>
|
|
53
69
|
Fourty
|
|
54
70
|
</SelectItem>,
|
|
55
|
-
<SelectItem key={5} value={50}>
|
|
71
|
+
<SelectItem<OptionType> key={5} value={'50'}>
|
|
56
72
|
Fifty
|
|
57
73
|
</SelectItem>,
|
|
58
|
-
<SelectItem key={6} value={60}>
|
|
74
|
+
<SelectItem<OptionType> key={6} value={'60'}>
|
|
59
75
|
Sixty
|
|
60
76
|
</SelectItem>,
|
|
61
|
-
<SelectItem key={7} value={70}>
|
|
77
|
+
<SelectItem<OptionType> key={7} value={'70'}>
|
|
62
78
|
Seventy
|
|
63
79
|
</SelectItem>,
|
|
64
|
-
<SelectItem key={8} value={80}>
|
|
80
|
+
<SelectItem<OptionType> key={8} value={'80'}>
|
|
65
81
|
Eighty
|
|
66
82
|
</SelectItem>,
|
|
67
|
-
<SelectItem key={9} value={90}>
|
|
83
|
+
<SelectItem<OptionType> key={9} value={'90'}>
|
|
68
84
|
Ninety
|
|
69
85
|
</SelectItem>,
|
|
70
|
-
<SelectItem key={10} value={100}>
|
|
86
|
+
<SelectItem<OptionType> key={10} value={'100'}>
|
|
71
87
|
One Hundred
|
|
72
88
|
</SelectItem>,
|
|
73
|
-
<SelectItem key={11} value={110}>
|
|
89
|
+
<SelectItem<OptionType> key={11} value={'110'}>
|
|
74
90
|
One Hundred Ten
|
|
75
91
|
</SelectItem>,
|
|
76
|
-
<SelectItem key={12} value={120}>
|
|
92
|
+
<SelectItem<OptionType> key={12} value={'120'}>
|
|
77
93
|
One Hundred Twenty
|
|
78
94
|
</SelectItem>,
|
|
79
|
-
<SelectItem key={13} value={130}>
|
|
95
|
+
<SelectItem<OptionType> key={13} value={'130'}>
|
|
80
96
|
One Hundred Thirty
|
|
81
97
|
</SelectItem>,
|
|
82
98
|
];
|
|
@@ -92,61 +108,23 @@ const Example = ({
|
|
|
92
108
|
Has Error
|
|
93
109
|
</CheckBox>
|
|
94
110
|
<Box width={230} display="inline-block">
|
|
95
|
-
<Select
|
|
111
|
+
<Select<'primary' | 'secondary'>
|
|
96
112
|
value={color}
|
|
97
|
-
onChange={(e, value) => setColor(value
|
|
113
|
+
onChange={(e, value) => setColor(value)}
|
|
98
114
|
label="Color"
|
|
99
115
|
CustomContainer={Container}
|
|
100
116
|
>
|
|
101
|
-
<SelectItem value="primary">
|
|
102
|
-
|
|
117
|
+
<SelectItem<'primary' | 'secondary'> value="primary">
|
|
118
|
+
Primary
|
|
119
|
+
</SelectItem>
|
|
120
|
+
<SelectItem<'primary' | 'secondary'> value="secondary">
|
|
121
|
+
Secondary
|
|
122
|
+
</SelectItem>
|
|
103
123
|
</Select>
|
|
104
124
|
</Box>
|
|
105
|
-
<select defaultValue="">
|
|
106
|
-
<option key={0} aria-label="None" value="" disabled />
|
|
107
|
-
<option key={1} value={10}>
|
|
108
|
-
Ten
|
|
109
|
-
</option>
|
|
110
|
-
<option key={2} value={20}>
|
|
111
|
-
Twenty
|
|
112
|
-
</option>
|
|
113
|
-
<option key={3} value={30}>
|
|
114
|
-
Thirty
|
|
115
|
-
</option>
|
|
116
|
-
<option key={4} value={40}>
|
|
117
|
-
Fourty
|
|
118
|
-
</option>
|
|
119
|
-
<option key={5} value={50}>
|
|
120
|
-
Fifty
|
|
121
|
-
</option>
|
|
122
|
-
<option key={6} value={60}>
|
|
123
|
-
Sixty
|
|
124
|
-
</option>
|
|
125
|
-
<option key={7} value={70}>
|
|
126
|
-
Seventy
|
|
127
|
-
</option>
|
|
128
|
-
<option key={8} value={80}>
|
|
129
|
-
Eighty
|
|
130
|
-
</option>
|
|
131
|
-
<option key={9} value={90}>
|
|
132
|
-
Ninety
|
|
133
|
-
</option>
|
|
134
|
-
<option key={10} value={100}>
|
|
135
|
-
One Hundred
|
|
136
|
-
</option>
|
|
137
|
-
<option key={11} value={110}>
|
|
138
|
-
One Hundred Ten
|
|
139
|
-
</option>
|
|
140
|
-
<option key={12} value={120}>
|
|
141
|
-
One Hundred Twenty
|
|
142
|
-
</option>
|
|
143
|
-
<option key={13} value={130}>
|
|
144
|
-
One Hundred Thirty
|
|
145
|
-
</option>
|
|
146
|
-
</select>
|
|
147
125
|
<Box m={3} bg="surface">
|
|
148
126
|
<Box mb={3} width={230}>
|
|
149
|
-
<Select
|
|
127
|
+
<Select<OptionType>
|
|
150
128
|
error={error}
|
|
151
129
|
color={color}
|
|
152
130
|
native={native}
|
|
@@ -159,7 +137,7 @@ const Example = ({
|
|
|
159
137
|
/>
|
|
160
138
|
</Box>
|
|
161
139
|
<Box mb={3} width={230}>
|
|
162
|
-
<Select
|
|
140
|
+
<Select<OptionType>
|
|
163
141
|
error={error}
|
|
164
142
|
color={color}
|
|
165
143
|
native={native}
|
|
@@ -172,7 +150,7 @@ const Example = ({
|
|
|
172
150
|
/>
|
|
173
151
|
</Box>
|
|
174
152
|
<Box mb={3} width={230}>
|
|
175
|
-
<Select
|
|
153
|
+
<Select<OptionType>
|
|
176
154
|
error={error}
|
|
177
155
|
color={color}
|
|
178
156
|
native={native}
|
|
@@ -183,7 +161,7 @@ const Example = ({
|
|
|
183
161
|
/>
|
|
184
162
|
</Box>
|
|
185
163
|
<Box mb={3} width={230}>
|
|
186
|
-
<Select
|
|
164
|
+
<Select<OptionType>
|
|
187
165
|
error={error}
|
|
188
166
|
color={color}
|
|
189
167
|
native={native}
|
|
@@ -196,7 +174,7 @@ const Example = ({
|
|
|
196
174
|
/>
|
|
197
175
|
</Box>
|
|
198
176
|
<Box mb={3} width={230}>
|
|
199
|
-
<Select
|
|
177
|
+
<Select<OptionType>
|
|
200
178
|
error={error}
|
|
201
179
|
color={color}
|
|
202
180
|
native={native}
|
|
@@ -208,7 +186,7 @@ const Example = ({
|
|
|
208
186
|
/>
|
|
209
187
|
</Box>
|
|
210
188
|
<Box mb={3} width={230}>
|
|
211
|
-
<Select
|
|
189
|
+
<Select<OptionType>
|
|
212
190
|
error={error}
|
|
213
191
|
color={color}
|
|
214
192
|
native={native}
|
|
@@ -221,7 +199,7 @@ const Example = ({
|
|
|
221
199
|
/>
|
|
222
200
|
</Box>
|
|
223
201
|
<Box mb={3} width={230}>
|
|
224
|
-
<Select
|
|
202
|
+
<Select<OptionType>
|
|
225
203
|
error={error}
|
|
226
204
|
color={color}
|
|
227
205
|
native={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,
|
|
@@ -21,7 +24,7 @@ import { OutlinedContainer } from '../TextField/OutlinedContainer';
|
|
|
21
24
|
import { SelectProvider } from './context';
|
|
22
25
|
import { Menu, MenuPopover, MenuList } from '../Menu';
|
|
23
26
|
import { SelectIcon } from './SelectIcon';
|
|
24
|
-
import { makeDefaultRender } from './defaultRender';
|
|
27
|
+
import { makeDefaultMultipleRender, makeDefaultRender } from './defaultRender';
|
|
25
28
|
import type { BoxProps } from '../Box';
|
|
26
29
|
import { Box } from '../Box';
|
|
27
30
|
import { IconContainer } from '../TextField/IconContainer';
|
|
@@ -32,7 +35,7 @@ const componentMap = {
|
|
|
32
35
|
filled: FilledContainer,
|
|
33
36
|
};
|
|
34
37
|
|
|
35
|
-
|
|
38
|
+
interface BaseSelectProps
|
|
36
39
|
extends Omit<
|
|
37
40
|
BoxProps<HTMLSelectElement, SelectHTMLAttributes<HTMLSelectElement>>,
|
|
38
41
|
'value' | 'defaultValue' | 'onChange'
|
|
@@ -41,28 +44,48 @@ export interface SelectProps
|
|
|
41
44
|
color?: 'primary' | 'secondary';
|
|
42
45
|
label?: ReactNode;
|
|
43
46
|
helperText?: string;
|
|
44
|
-
defaultValue?: string;
|
|
45
|
-
value?: string;
|
|
46
47
|
native?: boolean;
|
|
47
48
|
theme?: Theme;
|
|
48
49
|
error?: boolean | string;
|
|
49
|
-
onChange?: (e: ChangeEvent<HTMLSelectElement>, value: string) => void;
|
|
50
|
-
renderValue?: (value?: string) => string | undefined;
|
|
51
50
|
leadingIcon?: ReactNode;
|
|
52
51
|
CustomContainer?: ComponentType<FilledContainerProps>;
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
export
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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;
|
|
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
|
+
) {
|
|
59
82
|
const {
|
|
60
83
|
id: idProp,
|
|
61
84
|
name,
|
|
62
85
|
variant = 'outlined',
|
|
63
86
|
color = 'primary',
|
|
64
87
|
value: valueProp,
|
|
65
|
-
defaultValue = '',
|
|
88
|
+
defaultValue = props.multiple ? [] : ('' as const),
|
|
66
89
|
disabled,
|
|
67
90
|
error = false,
|
|
68
91
|
label = null,
|
|
@@ -72,6 +95,7 @@ export const Select = forwardRef<
|
|
|
72
95
|
onFocus,
|
|
73
96
|
onBlur,
|
|
74
97
|
native = false,
|
|
98
|
+
multiple = false,
|
|
75
99
|
children,
|
|
76
100
|
renderValue: renderValueProp,
|
|
77
101
|
leadingIcon = null,
|
|
@@ -80,12 +104,21 @@ export const Select = forwardRef<
|
|
|
80
104
|
} = props;
|
|
81
105
|
const [value, onChange] = useControlledState(
|
|
82
106
|
valueProp,
|
|
83
|
-
onChangeProp
|
|
107
|
+
onChangeProp as (
|
|
108
|
+
e: ChangeEvent<HTMLSelectElement>,
|
|
109
|
+
value: ValueType | ValueType[]
|
|
110
|
+
) => void,
|
|
84
111
|
defaultValue,
|
|
85
|
-
(setState) => (e, v) => {
|
|
112
|
+
(setState) => (e, v: ValueType | ValueType[]) => {
|
|
86
113
|
setState(v);
|
|
87
114
|
}
|
|
88
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
|
+
|
|
89
122
|
const [hasFocus, setHasFocus] = useState(false);
|
|
90
123
|
const buttonRef = useRef<HTMLButtonElement | HTMLSelectElement>();
|
|
91
124
|
const [open, setOpen] = useState(false);
|
|
@@ -108,11 +141,25 @@ export const Select = forwardRef<
|
|
|
108
141
|
};
|
|
109
142
|
|
|
110
143
|
const handleOnChange = (e: any) => {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
+
}
|
|
116
163
|
};
|
|
117
164
|
|
|
118
165
|
const hasError = Boolean(error);
|
|
@@ -122,9 +169,23 @@ export const Select = forwardRef<
|
|
|
122
169
|
const inputId = `${id}-text-field`;
|
|
123
170
|
const helperTextId = helperText ? `${id}-helper-text` : undefined;
|
|
124
171
|
|
|
125
|
-
const
|
|
172
|
+
const defaultRenderFn = useMemo(
|
|
173
|
+
() =>
|
|
174
|
+
multiple
|
|
175
|
+
? makeDefaultMultipleRender(children)
|
|
176
|
+
: makeDefaultRender<ValueType>(children),
|
|
177
|
+
[children, multiple]
|
|
178
|
+
);
|
|
179
|
+
const renderValue = renderValueProp || defaultRenderFn;
|
|
126
180
|
|
|
127
|
-
|
|
181
|
+
function hasAnySelected() {
|
|
182
|
+
if (multiple) {
|
|
183
|
+
return value.length > 0;
|
|
184
|
+
} else {
|
|
185
|
+
return value !== '';
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const labelIsFloating = hasFocus || open || hasAnySelected();
|
|
128
189
|
|
|
129
190
|
const Comp: ComponentType<any> = native
|
|
130
191
|
? (SelectComp as any)
|
|
@@ -135,13 +196,15 @@ export const Select = forwardRef<
|
|
|
135
196
|
// is different than the value we have stored in state we need to
|
|
136
197
|
// update our state to reflect that.
|
|
137
198
|
if (native && buttonRef.current && buttonRef.current.value !== value) {
|
|
138
|
-
onChange && onChange({} as any, buttonRef.current.value);
|
|
199
|
+
onChange && onChange({} as any, buttonRef.current.value as ValueType);
|
|
139
200
|
}
|
|
140
201
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
141
202
|
}, []);
|
|
142
203
|
|
|
143
204
|
return (
|
|
144
|
-
<SelectProvider
|
|
205
|
+
<SelectProvider
|
|
206
|
+
value={{ native, onSelect: handleOnChange, value, multiple }}
|
|
207
|
+
>
|
|
145
208
|
<Box display="inline-flex" flexDirection="column" width="100%">
|
|
146
209
|
{!native && <input type="hidden" name={name} value={value} />}
|
|
147
210
|
<Container
|
|
@@ -176,10 +239,11 @@ export const Select = forwardRef<
|
|
|
176
239
|
hasLabel={!!label}
|
|
177
240
|
leadingIcon={Boolean(leadingIcon)}
|
|
178
241
|
name={native ? name : undefined}
|
|
242
|
+
multiple={native ? multiple : undefined}
|
|
179
243
|
trailingIcon={true}
|
|
180
244
|
{...otherProps}
|
|
181
245
|
>
|
|
182
|
-
{native ? children : renderValue(value)}
|
|
246
|
+
{native ? children : renderValue(value as any)}
|
|
183
247
|
</Comp>
|
|
184
248
|
{!native && (
|
|
185
249
|
<MenuPopover usePortal>
|
|
@@ -212,4 +276,8 @@ export const Select = forwardRef<
|
|
|
212
276
|
</Box>
|
|
213
277
|
</SelectProvider>
|
|
214
278
|
);
|
|
215
|
-
})
|
|
279
|
+
}) as <ValueType extends string>(
|
|
280
|
+
p: SelectProps<ValueType> & {
|
|
281
|
+
ref?: Ref<HTMLSelectElement | HTMLButtonElement>;
|
|
282
|
+
}
|
|
283
|
+
) => ReactElement;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Select } from './';
|
|
4
|
+
import { SelectItem } from '../SelectItem';
|
|
5
|
+
import { Box } from '../Box';
|
|
6
|
+
import { CheckBox } from '../CheckBox';
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
title: 'components/Select/Multiple',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const SearchIcon = (props: any) => (
|
|
13
|
+
<svg
|
|
14
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
15
|
+
height={24}
|
|
16
|
+
width={24}
|
|
17
|
+
viewBox="0 0 48 48"
|
|
18
|
+
fill="currentColor"
|
|
19
|
+
{...props}
|
|
20
|
+
>
|
|
21
|
+
<path d="M39.8 41.95 26.65 28.8q-1.5 1.3-3.5 2.025-2 .725-4.25.725-5.4 0-9.15-3.75T6 18.75q0-5.3 3.75-9.05 3.75-3.75 9.1-3.75 5.3 0 9.025 3.75 3.725 3.75 3.725 9.05 0 2.15-.7 4.15-.7 2-2.1 3.75L42 39.75Zm-20.95-13.4q4.05 0 6.9-2.875Q28.6 22.8 28.6 18.75t-2.85-6.925Q22.9 8.95 18.85 8.95q-4.1 0-6.975 2.875T9 18.75q0 4.05 2.875 6.925t6.975 2.875Z" />
|
|
22
|
+
</svg>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
type OptionType =
|
|
26
|
+
| ''
|
|
27
|
+
| '10'
|
|
28
|
+
| '20'
|
|
29
|
+
| '30'
|
|
30
|
+
| '40'
|
|
31
|
+
| '50'
|
|
32
|
+
| '60'
|
|
33
|
+
| '70'
|
|
34
|
+
| '80'
|
|
35
|
+
| '90'
|
|
36
|
+
| '100'
|
|
37
|
+
| '110'
|
|
38
|
+
| '120'
|
|
39
|
+
| '130';
|
|
40
|
+
|
|
41
|
+
const Example = ({
|
|
42
|
+
variant,
|
|
43
|
+
native = false,
|
|
44
|
+
}: {
|
|
45
|
+
variant?: 'outlined' | 'filled';
|
|
46
|
+
native?: boolean;
|
|
47
|
+
}) => {
|
|
48
|
+
const [error, setError] = useState<boolean | string>(false);
|
|
49
|
+
const [color, setColor] = useState<'primary' | 'secondary'>('primary');
|
|
50
|
+
|
|
51
|
+
/* eslint-disable react/no-children-prop */
|
|
52
|
+
const children = [
|
|
53
|
+
<SelectItem<OptionType> key={1} value={'10'}>
|
|
54
|
+
Ten
|
|
55
|
+
</SelectItem>,
|
|
56
|
+
<SelectItem<OptionType> key={2} value={'20'}>
|
|
57
|
+
Twenty
|
|
58
|
+
</SelectItem>,
|
|
59
|
+
<SelectItem<OptionType> key={3} value={'30'}>
|
|
60
|
+
Thirty
|
|
61
|
+
</SelectItem>,
|
|
62
|
+
<SelectItem<OptionType> key={4} value={'40'}>
|
|
63
|
+
Fourty
|
|
64
|
+
</SelectItem>,
|
|
65
|
+
<SelectItem<OptionType> key={5} value={'50'}>
|
|
66
|
+
Fifty
|
|
67
|
+
</SelectItem>,
|
|
68
|
+
<SelectItem<OptionType> key={6} value={'60'}>
|
|
69
|
+
Sixty
|
|
70
|
+
</SelectItem>,
|
|
71
|
+
<SelectItem<OptionType> key={7} value={'70'}>
|
|
72
|
+
Seventy
|
|
73
|
+
</SelectItem>,
|
|
74
|
+
<SelectItem<OptionType> key={8} value={'80'}>
|
|
75
|
+
Eighty
|
|
76
|
+
</SelectItem>,
|
|
77
|
+
<SelectItem<OptionType> key={9} value={'90'}>
|
|
78
|
+
Ninety
|
|
79
|
+
</SelectItem>,
|
|
80
|
+
<SelectItem<OptionType> key={10} value={'100'}>
|
|
81
|
+
One Hundred
|
|
82
|
+
</SelectItem>,
|
|
83
|
+
<SelectItem<OptionType> key={11} value={'110'}>
|
|
84
|
+
One Hundred Ten
|
|
85
|
+
</SelectItem>,
|
|
86
|
+
<SelectItem<OptionType> key={12} value={'120'}>
|
|
87
|
+
One Hundred Twenty
|
|
88
|
+
</SelectItem>,
|
|
89
|
+
<SelectItem<OptionType> key={13} value={'130'}>
|
|
90
|
+
One Hundred Thirty
|
|
91
|
+
</SelectItem>,
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<Box p={3}>
|
|
96
|
+
{/* <CheckBox
|
|
97
|
+
checked={Boolean(error)}
|
|
98
|
+
onChange={(e) =>
|
|
99
|
+
setError(e.target.checked ? 'This field is required' : false)
|
|
100
|
+
}
|
|
101
|
+
>
|
|
102
|
+
Has Error
|
|
103
|
+
</CheckBox>
|
|
104
|
+
<Box width={230} display="inline-block">
|
|
105
|
+
<Select<'primary' | 'secondary'>
|
|
106
|
+
value={color}
|
|
107
|
+
onChange={(e, value) => setColor(value)}
|
|
108
|
+
label="Color"
|
|
109
|
+
>
|
|
110
|
+
<SelectItem<'primary' | 'secondary'> value="primary">
|
|
111
|
+
Primary
|
|
112
|
+
</SelectItem>
|
|
113
|
+
<SelectItem<'primary' | 'secondary'> value="secondary">
|
|
114
|
+
Secondary
|
|
115
|
+
</SelectItem>
|
|
116
|
+
</Select>
|
|
117
|
+
</Box> */}
|
|
118
|
+
<Box m={3} bg="surface">
|
|
119
|
+
<Box mb={3} width={230}>
|
|
120
|
+
<Select<OptionType>
|
|
121
|
+
error={error}
|
|
122
|
+
color={color}
|
|
123
|
+
native={native}
|
|
124
|
+
multiple
|
|
125
|
+
variant={variant}
|
|
126
|
+
label="Standard"
|
|
127
|
+
helperText="Helper text"
|
|
128
|
+
children={children}
|
|
129
|
+
placeholder="Empty"
|
|
130
|
+
/>
|
|
131
|
+
</Box>
|
|
132
|
+
<Box mb={3} width={230}>
|
|
133
|
+
<Select<OptionType>
|
|
134
|
+
error={error}
|
|
135
|
+
color={color}
|
|
136
|
+
native={native}
|
|
137
|
+
multiple
|
|
138
|
+
variant={variant}
|
|
139
|
+
label="Standard"
|
|
140
|
+
defaultValue={['20']}
|
|
141
|
+
helperText="Helper text"
|
|
142
|
+
children={children}
|
|
143
|
+
/>
|
|
144
|
+
</Box>
|
|
145
|
+
<Box mb={3} width={230}>
|
|
146
|
+
<Select<OptionType>
|
|
147
|
+
error={error}
|
|
148
|
+
color={color}
|
|
149
|
+
native={native}
|
|
150
|
+
multiple
|
|
151
|
+
variant={variant}
|
|
152
|
+
label="Standard"
|
|
153
|
+
children={children}
|
|
154
|
+
/>
|
|
155
|
+
</Box>
|
|
156
|
+
<Box mb={3} width={230}>
|
|
157
|
+
<Select<OptionType>
|
|
158
|
+
error={error}
|
|
159
|
+
color={color}
|
|
160
|
+
native={native}
|
|
161
|
+
multiple
|
|
162
|
+
variant={variant}
|
|
163
|
+
label="Disabled"
|
|
164
|
+
helperText="Helper text"
|
|
165
|
+
disabled
|
|
166
|
+
children={children}
|
|
167
|
+
/>
|
|
168
|
+
</Box>
|
|
169
|
+
<Box mb={3} width={230}>
|
|
170
|
+
<Select<OptionType>
|
|
171
|
+
error={error}
|
|
172
|
+
color={color}
|
|
173
|
+
native={native}
|
|
174
|
+
multiple
|
|
175
|
+
variant={variant}
|
|
176
|
+
label=""
|
|
177
|
+
helperText="Helper text"
|
|
178
|
+
children={children}
|
|
179
|
+
/>
|
|
180
|
+
</Box>
|
|
181
|
+
<Box mb={3} width={230}>
|
|
182
|
+
<Select<OptionType>
|
|
183
|
+
error={error}
|
|
184
|
+
color={color}
|
|
185
|
+
native={native}
|
|
186
|
+
multiple
|
|
187
|
+
variant={variant}
|
|
188
|
+
label=""
|
|
189
|
+
helperText="Helper text"
|
|
190
|
+
children={children}
|
|
191
|
+
leadingIcon={<SearchIcon />}
|
|
192
|
+
/>
|
|
193
|
+
</Box>
|
|
194
|
+
<Box mb={3} width={230}>
|
|
195
|
+
<Select<OptionType>
|
|
196
|
+
error={error}
|
|
197
|
+
color={color}
|
|
198
|
+
native={native}
|
|
199
|
+
multiple
|
|
200
|
+
variant={variant}
|
|
201
|
+
label="Standard"
|
|
202
|
+
helperText="Helper text"
|
|
203
|
+
children={children}
|
|
204
|
+
leadingIcon={<SearchIcon />}
|
|
205
|
+
/>
|
|
206
|
+
</Box>
|
|
207
|
+
</Box>
|
|
208
|
+
</Box>
|
|
209
|
+
);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
export const Filled = () => <Example variant="filled" />;
|
|
213
|
+
export const Outlined = () => <Example variant="outlined" />;
|
|
214
|
+
export const FilledNative = () => <Example variant="filled" native />;
|
|
215
|
+
export const OutlinedNative = () => <Example variant="outlined" native />;
|
package/src/Select/context.ts
CHANGED
|
@@ -2,19 +2,21 @@ import type { MouseEvent, KeyboardEvent } from 'react';
|
|
|
2
2
|
import { useContext, createContext } from 'react';
|
|
3
3
|
|
|
4
4
|
// Select Component
|
|
5
|
-
export interface SelectContextProps {
|
|
5
|
+
export interface SelectContextProps<ValueType extends string> {
|
|
6
6
|
native: boolean;
|
|
7
|
-
value?:
|
|
7
|
+
value?: ValueType | ValueType[];
|
|
8
8
|
onSelect: (
|
|
9
9
|
e: MouseEvent<HTMLLIElement> | KeyboardEvent<HTMLLIElement>
|
|
10
10
|
) => void;
|
|
11
|
+
multiple: boolean;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
const selectContext = createContext<SelectContextProps
|
|
14
|
+
const selectContext = createContext<SelectContextProps<string>>({
|
|
14
15
|
native: false,
|
|
15
16
|
onSelect: () => {
|
|
16
17
|
// noop
|
|
17
18
|
},
|
|
19
|
+
multiple: false,
|
|
18
20
|
});
|
|
19
21
|
export const { Provider: SelectProvider } = selectContext;
|
|
20
22
|
export const useSelectContext = () => useContext(selectContext);
|