@itwin/itwinui-react 2.0.3 → 2.1.0
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 +14 -1
- package/cjs/core/ComboBox/ComboBox.d.ts +25 -8
- package/cjs/core/ComboBox/ComboBox.js +141 -44
- package/cjs/core/ComboBox/ComboBoxDropdown.d.ts +1 -1
- package/cjs/core/ComboBox/ComboBoxDropdown.js +2 -2
- package/cjs/core/ComboBox/ComboBoxEndIcon.js +1 -1
- package/cjs/core/ComboBox/ComboBoxInput.d.ts +2 -0
- package/cjs/core/ComboBox/ComboBoxInput.js +40 -23
- package/cjs/core/ComboBox/ComboBoxMenuItem.js +1 -1
- package/cjs/core/ComboBox/ComboBoxMultipleContainer.d.ts +4 -0
- package/cjs/core/ComboBox/ComboBoxMultipleContainer.js +17 -0
- package/cjs/core/ComboBox/helpers.d.ts +20 -5
- package/cjs/core/ComboBox/helpers.js +24 -6
- package/cjs/core/Select/Select.js +2 -7
- package/cjs/core/Select/SelectTagContainer.d.ts +16 -0
- package/cjs/core/Select/SelectTagContainer.js +27 -0
- package/cjs/core/Table/Table.js +4 -2
- package/cjs/core/Table/actionHandlers/index.d.ts +1 -1
- package/cjs/core/Table/actionHandlers/index.js +2 -2
- package/cjs/core/Table/actionHandlers/selectHandler.d.ts +2 -2
- package/cjs/core/Table/actionHandlers/selectHandler.js +20 -6
- package/cjs/core/ThemeProvider/ThemeProvider.d.ts +25 -6
- package/cjs/core/ThemeProvider/ThemeProvider.js +29 -12
- package/cjs/core/utils/components/Popover.d.ts +1 -1
- package/cjs/core/utils/hooks/index.d.ts +1 -0
- package/cjs/core/utils/hooks/index.js +1 -0
- package/cjs/core/utils/hooks/useIsThemeAlreadySet.d.ts +7 -0
- package/cjs/core/utils/hooks/useIsThemeAlreadySet.js +34 -0
- package/cjs/core/utils/hooks/useTheme.js +4 -9
- package/esm/core/ComboBox/ComboBox.d.ts +25 -8
- package/esm/core/ComboBox/ComboBox.js +141 -44
- package/esm/core/ComboBox/ComboBoxDropdown.d.ts +1 -1
- package/esm/core/ComboBox/ComboBoxDropdown.js +2 -2
- package/esm/core/ComboBox/ComboBoxEndIcon.js +1 -1
- package/esm/core/ComboBox/ComboBoxInput.d.ts +2 -0
- package/esm/core/ComboBox/ComboBoxInput.js +41 -24
- package/esm/core/ComboBox/ComboBoxMenuItem.js +1 -1
- package/esm/core/ComboBox/ComboBoxMultipleContainer.d.ts +4 -0
- package/esm/core/ComboBox/ComboBoxMultipleContainer.js +11 -0
- package/esm/core/ComboBox/helpers.d.ts +20 -5
- package/esm/core/ComboBox/helpers.js +24 -6
- package/esm/core/Select/Select.js +3 -8
- package/esm/core/Select/SelectTagContainer.d.ts +16 -0
- package/esm/core/Select/SelectTagContainer.js +21 -0
- package/esm/core/Table/Table.js +5 -3
- package/esm/core/Table/actionHandlers/index.d.ts +1 -1
- package/esm/core/Table/actionHandlers/index.js +1 -1
- package/esm/core/Table/actionHandlers/selectHandler.d.ts +2 -2
- package/esm/core/Table/actionHandlers/selectHandler.js +17 -3
- package/esm/core/ThemeProvider/ThemeProvider.d.ts +25 -6
- package/esm/core/ThemeProvider/ThemeProvider.js +30 -13
- package/esm/core/utils/components/Popover.d.ts +1 -1
- package/esm/core/utils/hooks/index.d.ts +1 -0
- package/esm/core/utils/hooks/index.js +1 -0
- package/esm/core/utils/hooks/useIsThemeAlreadySet.d.ts +7 -0
- package/esm/core/utils/hooks/useIsThemeAlreadySet.js +27 -0
- package/esm/core/utils/hooks/useTheme.js +4 -6
- package/package.json +10 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.1.0](https://www.github.com/iTwin/iTwinUI-react/compare/v2.0.2...v2.1.0) (2022-12-12)
|
|
4
|
+
|
|
5
|
+
### What's new
|
|
6
|
+
|
|
7
|
+
* **Combobox:** Add support for multi-selection through `multiple` prop ([#830](https://www.github.com/iTwin/iTwinUI-react/issues/830)) ([ced7588](https://www.github.com/iTwin/iTwinUI-react/commit/ced7588acee5273e83c0b0d05a732f260997b9bb))
|
|
8
|
+
* **ThemeProvider:** Add `applyBackground` to `themeOptions` prop. ([#974](https://www.github.com/iTwin/iTwinUI-react/issues/974)) ([13cff7f](https://www.github.com/iTwin/iTwinUI-react/commit/13cff7fab356e1015e2e7c361f15c6a7c8fe4d6b))
|
|
9
|
+
* Defaults to true for the topmost ThemeProvider in the tree.
|
|
10
|
+
* **Table:** Ctrl + Shift click now keeps previous selection & also shift clicks ([#888](https://www.github.com/iTwin/iTwinUI-react/issues/888)) ([13edfb5](https://www.github.com/iTwin/iTwinUI-react/commit/13edfb5ecc62de284ac03cc2fc27b8a41b4f7b61))
|
|
11
|
+
|
|
12
|
+
### Fixes
|
|
13
|
+
|
|
14
|
+
* **Table:** Ctrl and checkbox clicks update start row of shift selection ([#889](https://www.github.com/iTwin/iTwinUI-react/issues/889)) ([21900d4](https://www.github.com/iTwin/iTwinUI-react/commit/21900d42900f7a03305bf2040cc64ddb29361b2d))
|
|
15
|
+
|
|
3
16
|
### 2.0.3 (2022-12-12)
|
|
4
17
|
|
|
5
18
|
### Fixes
|
|
6
19
|
|
|
7
|
-
* **ComboBox:** Move max-height to outer element to fix virtual scroll
|
|
20
|
+
* **ComboBox:** Move max-height to outer element to fix virtual scroll ([#986](https://www.github.com/iTwin/iTwinUI-react/issues/986)) ([596559e](https://www.github.com/iTwin/iTwinUI-react/commit/596559e7158877e09c98c5c672e8a58a9507a33d))
|
|
8
21
|
|
|
9
22
|
### [2.0.2](https://www.github.com/iTwin/iTwinUI-react/compare/v2.0.1...v2.0.2) (2022-12-07)
|
|
10
23
|
|
|
@@ -3,24 +3,41 @@ import { InputProps } from '../Input';
|
|
|
3
3
|
import { SelectOption } from '../Select';
|
|
4
4
|
import { PopoverProps, CommonProps, InputContainerProps } from '../utils';
|
|
5
5
|
import 'tippy.js/animations/shift-away.css';
|
|
6
|
-
|
|
6
|
+
declare type ActionType = 'added' | 'removed';
|
|
7
|
+
declare type MultipleOnChangeProps<T> = {
|
|
8
|
+
value: T;
|
|
9
|
+
type: ActionType;
|
|
10
|
+
};
|
|
11
|
+
export declare type ComboboxMultipleTypeProps<T> = {
|
|
7
12
|
/**
|
|
8
|
-
*
|
|
13
|
+
* Enable multiple selection.
|
|
14
|
+
* @default false
|
|
9
15
|
*/
|
|
10
|
-
|
|
16
|
+
multiple?: false;
|
|
11
17
|
/**
|
|
12
18
|
* Controlled value of ComboBox.
|
|
19
|
+
* If `multiple` is enabled, it is an array of values.
|
|
13
20
|
*/
|
|
14
21
|
value?: T;
|
|
22
|
+
/**
|
|
23
|
+
* Callback fired when selected value changes.
|
|
24
|
+
*/
|
|
25
|
+
onChange?: (value: T) => void;
|
|
26
|
+
} | {
|
|
27
|
+
multiple: true;
|
|
28
|
+
value?: T[];
|
|
29
|
+
onChange?: (value: T[], event: MultipleOnChangeProps<T>) => void;
|
|
30
|
+
};
|
|
31
|
+
export declare type ComboBoxProps<T> = {
|
|
32
|
+
/**
|
|
33
|
+
* Array of options that populate the dropdown list.
|
|
34
|
+
*/
|
|
35
|
+
options: SelectOption<T>[];
|
|
15
36
|
/**
|
|
16
37
|
* Message shown below the combobox.
|
|
17
38
|
* Use `StatusMessage` component.
|
|
18
39
|
*/
|
|
19
40
|
message?: React.ReactNode;
|
|
20
|
-
/**
|
|
21
|
-
* Callback fired when selected value changes.
|
|
22
|
-
*/
|
|
23
|
-
onChange?: (value: T) => void;
|
|
24
41
|
/**
|
|
25
42
|
* Function to customize the default filtering logic.
|
|
26
43
|
*/
|
|
@@ -67,7 +84,7 @@ export declare type ComboBoxProps<T> = {
|
|
|
67
84
|
* Callback fired when dropdown menu is closed.
|
|
68
85
|
*/
|
|
69
86
|
onHide?: () => void;
|
|
70
|
-
} & Pick<InputContainerProps, 'status'> & Omit<CommonProps, 'title'>;
|
|
87
|
+
} & ComboboxMultipleTypeProps<T> & Pick<InputContainerProps, 'status'> & Omit<CommonProps, 'title'>;
|
|
71
88
|
/**
|
|
72
89
|
* ComboBox component that allows typing a value to filter the options in dropdown list.
|
|
73
90
|
* Values can be selected either using mouse clicks or using the Enter key.
|
|
@@ -11,6 +11,7 @@ exports.ComboBox = void 0;
|
|
|
11
11
|
const react_1 = __importDefault(require("react"));
|
|
12
12
|
const classnames_1 = __importDefault(require("classnames"));
|
|
13
13
|
const Menu_1 = require("../Menu");
|
|
14
|
+
const SelectTag_1 = __importDefault(require("../Select/SelectTag"));
|
|
14
15
|
const Typography_1 = require("../Typography");
|
|
15
16
|
const utils_1 = require("../utils");
|
|
16
17
|
require("tippy.js/animations/shift-away.css");
|
|
@@ -21,6 +22,14 @@ const ComboBoxInput_1 = require("./ComboBoxInput");
|
|
|
21
22
|
const ComboBoxInputContainer_1 = require("./ComboBoxInputContainer");
|
|
22
23
|
const ComboBoxMenu_1 = require("./ComboBoxMenu");
|
|
23
24
|
const ComboBoxMenuItem_1 = require("./ComboBoxMenuItem");
|
|
25
|
+
// Type guard for enabling multiple
|
|
26
|
+
const isMultipleEnabled = (variable, multiple) => {
|
|
27
|
+
return multiple && (Array.isArray(variable) || variable === undefined);
|
|
28
|
+
};
|
|
29
|
+
// Type guard for user onChange
|
|
30
|
+
const isSingleOnChange = (onChange, multiple) => {
|
|
31
|
+
return !multiple;
|
|
32
|
+
};
|
|
24
33
|
/** Returns either `option.id` or derives a stable id using `idPrefix` and `option.label` (without whitespace) */
|
|
25
34
|
const getOptionId = (option, idPrefix) => {
|
|
26
35
|
var _a;
|
|
@@ -41,7 +50,7 @@ const getOptionId = (option, idPrefix) => {
|
|
|
41
50
|
*/
|
|
42
51
|
const ComboBox = (props) => {
|
|
43
52
|
var _a, _b;
|
|
44
|
-
const { options, value: valueProp, onChange, filterFunction, inputProps, dropdownMenuProps, emptyStateMessage = 'No options found', itemRenderer, enableVirtualization = false, onShow, onHide, ...rest } = props;
|
|
53
|
+
const { options, value: valueProp, onChange, filterFunction, inputProps, dropdownMenuProps, emptyStateMessage = 'No options found', itemRenderer, enableVirtualization = false, multiple = false, onShow, onHide, ...rest } = props;
|
|
45
54
|
// Generate a stateful random id if not specified
|
|
46
55
|
const [id] = react_1.default.useState(() => {
|
|
47
56
|
var _a, _b;
|
|
@@ -52,8 +61,6 @@ const ComboBox = (props) => {
|
|
|
52
61
|
const inputRef = react_1.default.useRef(null);
|
|
53
62
|
const menuRef = react_1.default.useRef(null);
|
|
54
63
|
const toggleButtonRef = react_1.default.useRef(null);
|
|
55
|
-
const mounted = react_1.default.useRef(false);
|
|
56
|
-
const valuePropRef = (0, utils_1.useLatestRef)(valueProp);
|
|
57
64
|
const onChangeProp = (0, utils_1.useLatestRef)(onChange);
|
|
58
65
|
const optionsRef = (0, utils_1.useLatestRef)(options);
|
|
59
66
|
// Record to store all extra information (e.g. original indexes), where the key is the id of the option
|
|
@@ -71,12 +78,23 @@ const ComboBox = (props) => {
|
|
|
71
78
|
};
|
|
72
79
|
});
|
|
73
80
|
}
|
|
81
|
+
// Get indices of selected elements in options array when we have selected values.
|
|
82
|
+
const getSelectedIndexes = react_1.default.useCallback(() => {
|
|
83
|
+
if (isMultipleEnabled(valueProp, multiple)) {
|
|
84
|
+
const indexArray = [];
|
|
85
|
+
valueProp === null || valueProp === void 0 ? void 0 : valueProp.forEach((value) => {
|
|
86
|
+
indexArray.push(options.findIndex((option) => option.value === value));
|
|
87
|
+
});
|
|
88
|
+
return indexArray;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
return options.findIndex((option) => option.value === valueProp);
|
|
92
|
+
}
|
|
93
|
+
}, [multiple, options, valueProp]);
|
|
74
94
|
// Reducer where all the component-wide state is stored
|
|
75
|
-
const [{ isOpen,
|
|
95
|
+
const [{ isOpen, selected, focusedIndex }, dispatch] = react_1.default.useReducer(helpers_1.comboBoxReducer, {
|
|
76
96
|
isOpen: false,
|
|
77
|
-
|
|
78
|
-
? optionsRef.current.findIndex((option) => option.value === valueProp)
|
|
79
|
-
: -1,
|
|
97
|
+
selected: getSelectedIndexes(),
|
|
80
98
|
focusedIndex: -1,
|
|
81
99
|
});
|
|
82
100
|
(0, utils_1.useIsomorphicLayoutEffect)(() => {
|
|
@@ -84,19 +102,24 @@ const ComboBox = (props) => {
|
|
|
84
102
|
// When the dropdown opens
|
|
85
103
|
if (isOpen) {
|
|
86
104
|
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); // Focus the input
|
|
87
|
-
|
|
88
|
-
|
|
105
|
+
// Reset the filtered list (does not reset when multiple enabled)
|
|
106
|
+
if (!multiple) {
|
|
107
|
+
setFilteredOptions(optionsRef.current);
|
|
108
|
+
dispatch({ type: 'focus', value: undefined });
|
|
109
|
+
}
|
|
89
110
|
}
|
|
90
111
|
// When the dropdown closes
|
|
91
112
|
else {
|
|
92
113
|
// Reset the focused index
|
|
93
|
-
dispatch(
|
|
94
|
-
// Reset the input value
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
114
|
+
dispatch({ type: 'focus', value: undefined });
|
|
115
|
+
// Reset the input value if not multiple
|
|
116
|
+
if (!isMultipleEnabled(selected, multiple)) {
|
|
117
|
+
setInputValue(selected != undefined && selected >= 0
|
|
118
|
+
? (_b = optionsRef.current[selected]) === null || _b === void 0 ? void 0 : _b.label
|
|
119
|
+
: '');
|
|
120
|
+
}
|
|
98
121
|
}
|
|
99
|
-
}, [isOpen, optionsRef,
|
|
122
|
+
}, [isOpen, multiple, optionsRef, selected]);
|
|
100
123
|
// Set min-width of menu to be same as input
|
|
101
124
|
const [minWidth, setMinWidth] = react_1.default.useState(0);
|
|
102
125
|
react_1.default.useEffect(() => {
|
|
@@ -114,7 +137,7 @@ const ComboBox = (props) => {
|
|
|
114
137
|
else {
|
|
115
138
|
setFilteredOptions(options);
|
|
116
139
|
}
|
|
117
|
-
dispatch(
|
|
140
|
+
dispatch({ type: 'focus', value: undefined });
|
|
118
141
|
// Only need to call on options update
|
|
119
142
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
120
143
|
}, [options]);
|
|
@@ -124,41 +147,101 @@ const ComboBox = (props) => {
|
|
|
124
147
|
var _a, _b;
|
|
125
148
|
const { value } = event.currentTarget;
|
|
126
149
|
setInputValue(value);
|
|
127
|
-
dispatch(
|
|
150
|
+
dispatch({ type: 'open' }); // reopen when typing
|
|
128
151
|
setFilteredOptions((_a = filterFunction === null || filterFunction === void 0 ? void 0 : filterFunction(optionsRef.current, value)) !== null && _a !== void 0 ? _a : optionsRef.current.filter((option) => option.label.toLowerCase().includes(value.toLowerCase())));
|
|
129
152
|
if (focusedIndex != -1) {
|
|
130
|
-
dispatch(
|
|
153
|
+
dispatch({ type: 'focus', value: -1 });
|
|
131
154
|
}
|
|
132
155
|
(_b = inputProps === null || inputProps === void 0 ? void 0 : inputProps.onChange) === null || _b === void 0 ? void 0 : _b.call(inputProps, event);
|
|
133
156
|
}, [filterFunction, focusedIndex, inputProps, optionsRef]);
|
|
134
|
-
// When the value prop changes, update the
|
|
157
|
+
// When the value prop changes, update the selected index/indices
|
|
135
158
|
react_1.default.useEffect(() => {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
159
|
+
if (isMultipleEnabled(valueProp, multiple)) {
|
|
160
|
+
if (valueProp) {
|
|
161
|
+
// If user provided array of selected values
|
|
162
|
+
const indexes = valueProp.map((value) => {
|
|
163
|
+
return options.findIndex((option) => option.value === value);
|
|
164
|
+
});
|
|
165
|
+
dispatch({
|
|
166
|
+
type: 'multiselect',
|
|
167
|
+
value: indexes.filter((index) => index !== -1), // Add available options
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
// if user provided one value or undefined
|
|
172
|
+
dispatch({
|
|
173
|
+
type: 'multiselect',
|
|
174
|
+
value: [], // Add empty list
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
dispatch({
|
|
180
|
+
type: 'select',
|
|
181
|
+
value: options.findIndex((option) => option.value === valueProp),
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}, [valueProp, options, multiple]);
|
|
185
|
+
const isMenuItemSelected = react_1.default.useCallback((index) => {
|
|
186
|
+
if (isMultipleEnabled(selected, multiple)) {
|
|
187
|
+
return !!selected.includes(index);
|
|
148
188
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
189
|
+
else {
|
|
190
|
+
return selected === index;
|
|
191
|
+
}
|
|
192
|
+
}, [multiple, selected]);
|
|
193
|
+
// Generates new array when item is added or removed
|
|
194
|
+
const selectedChangeHandler = react_1.default.useCallback((__originalIndex, action) => {
|
|
195
|
+
if (action === 'added') {
|
|
196
|
+
return [...selected, __originalIndex];
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
return selected.filter((index) => index !== __originalIndex);
|
|
200
|
+
}
|
|
201
|
+
}, [selected]);
|
|
202
|
+
// Calls user defined onChange
|
|
203
|
+
const onChangeHandler = react_1.default.useCallback((__originalIndex, actionType, newArray) => {
|
|
204
|
+
var _a, _b, _c, _d;
|
|
205
|
+
if (isSingleOnChange(onChangeProp.current, multiple)) {
|
|
206
|
+
(_a = onChangeProp.current) === null || _a === void 0 ? void 0 : _a.call(onChangeProp, (_b = optionsRef.current[__originalIndex]) === null || _b === void 0 ? void 0 : _b.value);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
actionType &&
|
|
210
|
+
newArray &&
|
|
211
|
+
((_c = onChangeProp.current) === null || _c === void 0 ? void 0 : _c.call(onChangeProp, newArray === null || newArray === void 0 ? void 0 : newArray.map((item) => { var _a; return (_a = optionsRef.current[item]) === null || _a === void 0 ? void 0 : _a.value; }), {
|
|
212
|
+
value: (_d = optionsRef.current[__originalIndex]) === null || _d === void 0 ? void 0 : _d.value,
|
|
213
|
+
type: actionType,
|
|
214
|
+
}));
|
|
215
|
+
}
|
|
216
|
+
}, [multiple, onChangeProp, optionsRef]);
|
|
217
|
+
const onClickHandler = react_1.default.useCallback((__originalIndex) => {
|
|
218
|
+
if (isMultipleEnabled(selected, multiple)) {
|
|
219
|
+
const actionType = isMenuItemSelected(__originalIndex)
|
|
220
|
+
? 'removed'
|
|
221
|
+
: 'added';
|
|
222
|
+
const newArray = selectedChangeHandler(__originalIndex, actionType);
|
|
223
|
+
dispatch({ type: 'multiselect', value: newArray });
|
|
224
|
+
onChangeHandler(__originalIndex, actionType, newArray);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
dispatch({ type: 'select', value: __originalIndex });
|
|
228
|
+
dispatch({ type: 'close' });
|
|
229
|
+
onChangeHandler(__originalIndex);
|
|
152
230
|
}
|
|
153
|
-
|
|
154
|
-
|
|
231
|
+
}, [
|
|
232
|
+
selectedChangeHandler,
|
|
233
|
+
isMenuItemSelected,
|
|
234
|
+
multiple,
|
|
235
|
+
onChangeHandler,
|
|
236
|
+
selected,
|
|
237
|
+
]);
|
|
155
238
|
const getMenuItem = react_1.default.useCallback((option, filteredIndex) => {
|
|
156
239
|
const optionId = getOptionId(option, id);
|
|
157
240
|
const { __originalIndex } = optionsExtraInfoRef.current[optionId];
|
|
158
241
|
const customItem = itemRenderer
|
|
159
242
|
? itemRenderer(option, {
|
|
160
243
|
isFocused: focusedIndex === __originalIndex,
|
|
161
|
-
isSelected:
|
|
244
|
+
isSelected: selected === __originalIndex,
|
|
162
245
|
index: __originalIndex,
|
|
163
246
|
id: optionId,
|
|
164
247
|
})
|
|
@@ -166,8 +249,7 @@ const ComboBox = (props) => {
|
|
|
166
249
|
return customItem ? (react_1.default.cloneElement(customItem, {
|
|
167
250
|
onClick: (e) => {
|
|
168
251
|
var _a, _b;
|
|
169
|
-
|
|
170
|
-
dispatch(['close']);
|
|
252
|
+
onClickHandler(__originalIndex);
|
|
171
253
|
(_b = (_a = customItem.props).onClick) === null || _b === void 0 ? void 0 : _b.call(_a, e);
|
|
172
254
|
},
|
|
173
255
|
// ComboBox.MenuItem handles scrollIntoView, data-iui-index and iui-focused through context
|
|
@@ -182,11 +264,18 @@ const ComboBox = (props) => {
|
|
|
182
264
|
el === null || el === void 0 ? void 0 : el.scrollIntoView({ block: 'nearest' });
|
|
183
265
|
}
|
|
184
266
|
}),
|
|
185
|
-
})) : (react_1.default.createElement(ComboBoxMenuItem_1.ComboBoxMenuItem, { key: optionId, id: optionId, ...option, isSelected:
|
|
186
|
-
|
|
187
|
-
dispatch(['close']);
|
|
267
|
+
})) : (react_1.default.createElement(ComboBoxMenuItem_1.ComboBoxMenuItem, { key: optionId, id: optionId, ...option, isSelected: isMenuItemSelected(__originalIndex), onClick: () => {
|
|
268
|
+
onClickHandler(__originalIndex);
|
|
188
269
|
}, index: __originalIndex, "data-iui-filtered-index": filteredIndex }, option.label));
|
|
189
|
-
}, [
|
|
270
|
+
}, [
|
|
271
|
+
enableVirtualization,
|
|
272
|
+
focusedIndex,
|
|
273
|
+
id,
|
|
274
|
+
isMenuItemSelected,
|
|
275
|
+
itemRenderer,
|
|
276
|
+
onClickHandler,
|
|
277
|
+
selected,
|
|
278
|
+
]);
|
|
190
279
|
const emptyContent = react_1.default.useMemo(() => (react_1.default.createElement(react_1.default.Fragment, null, react_1.default.isValidElement(emptyStateMessage) ? (emptyStateMessage) : (react_1.default.createElement(Menu_1.MenuExtraContent, null,
|
|
191
280
|
react_1.default.createElement(Typography_1.Text, { isMuted: true }, emptyStateMessage))))), [emptyStateMessage]);
|
|
192
281
|
return (react_1.default.createElement(helpers_1.ComboBoxRefsContext.Provider, { value: { inputRef, menuRef, toggleButtonRef, optionsExtraInfoRef } },
|
|
@@ -196,12 +285,20 @@ const ComboBox = (props) => {
|
|
|
196
285
|
minWidth,
|
|
197
286
|
isOpen,
|
|
198
287
|
focusedIndex,
|
|
288
|
+
onClickHandler,
|
|
199
289
|
enableVirtualization,
|
|
200
290
|
filteredOptions,
|
|
201
291
|
getMenuItem,
|
|
292
|
+
multiple,
|
|
202
293
|
} },
|
|
203
294
|
react_1.default.createElement(ComboBoxInputContainer_1.ComboBoxInputContainer, { disabled: inputProps === null || inputProps === void 0 ? void 0 : inputProps.disabled, ...rest },
|
|
204
|
-
react_1.default.createElement(
|
|
295
|
+
react_1.default.createElement(react_1.default.Fragment, null,
|
|
296
|
+
react_1.default.createElement(ComboBoxInput_1.ComboBoxInput, { value: inputValue, ...inputProps, onChange: handleOnInput, selectTags: isMultipleEnabled(selected, multiple)
|
|
297
|
+
? selected.map((index) => {
|
|
298
|
+
const item = optionsRef.current[index];
|
|
299
|
+
return (react_1.default.createElement(SelectTag_1.default, { key: item.label, label: item.label }));
|
|
300
|
+
})
|
|
301
|
+
: undefined })),
|
|
205
302
|
react_1.default.createElement(ComboBoxEndIcon_1.ComboBoxEndIcon, { disabled: inputProps === null || inputProps === void 0 ? void 0 : inputProps.disabled, isOpen: isOpen })),
|
|
206
303
|
react_1.default.createElement(ComboBoxDropdown_1.ComboBoxDropdown, { ...dropdownMenuProps, onShow: onShow, onHide: onHide },
|
|
207
304
|
react_1.default.createElement(ComboBoxMenu_1.ComboBoxMenu, null, filteredOptions.length > 0 && !enableVirtualization
|
|
@@ -3,5 +3,5 @@ import { PopoverProps } from '../utils';
|
|
|
3
3
|
declare type ComboBoxDropdownProps = PopoverProps & {
|
|
4
4
|
children: JSX.Element;
|
|
5
5
|
};
|
|
6
|
-
export declare const ComboBoxDropdown: React.ForwardRefExoticComponent<Pick<ComboBoxDropdownProps, "disabled" | "theme" | "children" | "className" | "role" | "
|
|
6
|
+
export declare const ComboBoxDropdown: React.ForwardRefExoticComponent<Pick<ComboBoxDropdownProps, "disabled" | "theme" | "children" | "className" | "role" | "offset" | "content" | "plugins" | "placement" | "trigger" | "visible" | "render" | "animateFill" | "appendTo" | "aria" | "delay" | "duration" | "followCursor" | "getReferenceClientRect" | "hideOnClick" | "ignoreAttributes" | "inlinePositioning" | "interactive" | "interactiveBorder" | "interactiveDebounce" | "moveTransition" | "popperOptions" | "showOnCreate" | "sticky" | "touch" | "triggerTarget" | "onAfterUpdate" | "onBeforeUpdate" | "onCreate" | "onDestroy" | "onHidden" | "onHide" | "onMount" | "onShow" | "onShown" | "onTrigger" | "onUntrigger" | "onClickOutside" | "allowHTML" | "animation" | "arrow" | "inertia" | "maxWidth" | "zIndex" | "singleton" | "reference"> & React.RefAttributes<Element>>;
|
|
7
7
|
export {};
|
|
@@ -19,13 +19,13 @@ exports.ComboBoxDropdown = react_1.default.forwardRef((props, forwardedRef) => {
|
|
|
19
19
|
// sync internal isOpen state with user's visible prop
|
|
20
20
|
react_1.default.useEffect(() => {
|
|
21
21
|
if (props.visible != undefined) {
|
|
22
|
-
dispatch(
|
|
22
|
+
dispatch({ type: props.visible ? 'open' : 'close' });
|
|
23
23
|
}
|
|
24
24
|
}, [dispatch, props.visible]);
|
|
25
25
|
return (react_1.default.createElement(utils_1.Popover, { placement: 'bottom-start', visible: isOpen, onClickOutside: react_1.default.useCallback((_, { target }) => {
|
|
26
26
|
var _a;
|
|
27
27
|
if (!((_a = toggleButtonRef.current) === null || _a === void 0 ? void 0 : _a.contains(target))) {
|
|
28
|
-
dispatch(
|
|
28
|
+
dispatch({ type: 'close' });
|
|
29
29
|
}
|
|
30
30
|
}, [dispatch, toggleButtonRef]), animation: 'shift-away', duration: 200, reference: inputRef, ref: forwardedRef, content: children, ...rest }));
|
|
31
31
|
});
|
|
@@ -24,7 +24,7 @@ exports.ComboBoxEndIcon = react_1.default.forwardRef((props, forwardedRef) => {
|
|
|
24
24
|
}, className), onClick: (e) => {
|
|
25
25
|
onClickProp === null || onClickProp === void 0 ? void 0 : onClickProp(e);
|
|
26
26
|
if (!e.isDefaultPrevented()) {
|
|
27
|
-
dispatch(
|
|
27
|
+
dispatch({ type: isOpen ? 'close' : 'open' });
|
|
28
28
|
}
|
|
29
29
|
}, ...rest }, children !== null && children !== void 0 ? children : react_1.default.createElement(utils_1.SvgCaretDownSmall, { "aria-hidden": true })));
|
|
30
30
|
});
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
export declare const ComboBoxInput: React.ForwardRefExoticComponent<{
|
|
3
|
+
selectTags?: JSX.Element[] | undefined;
|
|
4
|
+
} & {
|
|
3
5
|
setFocus?: boolean | undefined;
|
|
4
6
|
size?: "small" | "large" | undefined;
|
|
5
7
|
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> & React.RefAttributes<HTMLInputElement>>;
|
|
@@ -11,10 +11,11 @@ exports.ComboBoxInput = void 0;
|
|
|
11
11
|
const react_1 = __importDefault(require("react"));
|
|
12
12
|
const Input_1 = require("../Input");
|
|
13
13
|
const utils_1 = require("../utils");
|
|
14
|
+
const ComboBoxMultipleContainer_1 = require("./ComboBoxMultipleContainer");
|
|
14
15
|
const helpers_1 = require("./helpers");
|
|
15
16
|
exports.ComboBoxInput = react_1.default.forwardRef((props, forwardedRef) => {
|
|
16
|
-
const { onKeyDown: onKeyDownProp, onFocus: onFocusProp, ...rest } = props;
|
|
17
|
-
const { isOpen, id, focusedIndex, enableVirtualization } = (0, utils_1.useSafeContext)(helpers_1.ComboBoxStateContext);
|
|
17
|
+
const { onKeyDown: onKeyDownProp, onFocus: onFocusProp, selectTags, ...rest } = props;
|
|
18
|
+
const { isOpen, id, focusedIndex, enableVirtualization, multiple, onClickHandler, } = (0, utils_1.useSafeContext)(helpers_1.ComboBoxStateContext);
|
|
18
19
|
const dispatch = (0, utils_1.useSafeContext)(helpers_1.ComboBoxActionContext);
|
|
19
20
|
const { inputRef, menuRef, optionsExtraInfoRef } = (0, utils_1.useSafeContext)(helpers_1.ComboBoxRefsContext);
|
|
20
21
|
const refs = (0, utils_1.useMergedRefs)(inputRef, forwardedRef);
|
|
@@ -34,17 +35,17 @@ exports.ComboBoxInput = react_1.default.forwardRef((props, forwardedRef) => {
|
|
|
34
35
|
case 'ArrowDown': {
|
|
35
36
|
event.preventDefault();
|
|
36
37
|
if (!isOpen) {
|
|
37
|
-
return dispatch(
|
|
38
|
+
return dispatch({ type: 'open' });
|
|
38
39
|
}
|
|
39
40
|
if (length === 0) {
|
|
40
41
|
return;
|
|
41
42
|
}
|
|
42
43
|
if (focusedIndexRef.current === -1) {
|
|
43
44
|
const currentElement = (_b = menuRef.current) === null || _b === void 0 ? void 0 : _b.querySelector('[data-iui-index]');
|
|
44
|
-
return dispatch(
|
|
45
|
-
'focus',
|
|
46
|
-
Number((_c = currentElement === null || currentElement === void 0 ? void 0 : currentElement.getAttribute('data-iui-index')) !== null && _c !== void 0 ? _c : 0),
|
|
47
|
-
|
|
45
|
+
return dispatch({
|
|
46
|
+
type: 'focus',
|
|
47
|
+
value: Number((_c = currentElement === null || currentElement === void 0 ? void 0 : currentElement.getAttribute('data-iui-index')) !== null && _c !== void 0 ? _c : 0),
|
|
48
|
+
});
|
|
48
49
|
}
|
|
49
50
|
// If virtualization is enabled, dont let round scrolling
|
|
50
51
|
if (enableVirtualization &&
|
|
@@ -57,7 +58,7 @@ exports.ComboBoxInput = react_1.default.forwardRef((props, forwardedRef) => {
|
|
|
57
58
|
const nextElement = (_g = currentElement === null || currentElement === void 0 ? void 0 : currentElement.nextElementSibling) !== null && _g !== void 0 ? _g : (_h = menuRef.current) === null || _h === void 0 ? void 0 : _h.querySelector('[data-iui-index]');
|
|
58
59
|
nextIndex = Number(nextElement === null || nextElement === void 0 ? void 0 : nextElement.getAttribute('data-iui-index'));
|
|
59
60
|
if ((nextElement === null || nextElement === void 0 ? void 0 : nextElement.ariaDisabled) !== 'true') {
|
|
60
|
-
return dispatch(
|
|
61
|
+
return dispatch({ type: 'focus', value: nextIndex });
|
|
61
62
|
}
|
|
62
63
|
} while (nextIndex !== focusedIndexRef.current);
|
|
63
64
|
break;
|
|
@@ -65,7 +66,7 @@ exports.ComboBoxInput = react_1.default.forwardRef((props, forwardedRef) => {
|
|
|
65
66
|
case 'ArrowUp': {
|
|
66
67
|
event.preventDefault();
|
|
67
68
|
if (!isOpen) {
|
|
68
|
-
return dispatch(
|
|
69
|
+
return dispatch({ type: 'open' });
|
|
69
70
|
}
|
|
70
71
|
if (length === 0) {
|
|
71
72
|
return;
|
|
@@ -76,10 +77,10 @@ exports.ComboBoxInput = react_1.default.forwardRef((props, forwardedRef) => {
|
|
|
76
77
|
return;
|
|
77
78
|
}
|
|
78
79
|
if (focusedIndexRef.current === -1) {
|
|
79
|
-
return dispatch(
|
|
80
|
-
'focus',
|
|
81
|
-
(_m = (_l = Object.values(optionsExtraInfoRef.current)) === null || _l === void 0 ? void 0 : _l[length - 1].__originalIndex) !== null && _m !== void 0 ? _m : -1,
|
|
82
|
-
|
|
80
|
+
return dispatch({
|
|
81
|
+
type: 'focus',
|
|
82
|
+
value: (_m = (_l = Object.values(optionsExtraInfoRef.current)) === null || _l === void 0 ? void 0 : _l[length - 1].__originalIndex) !== null && _m !== void 0 ? _m : -1,
|
|
83
|
+
});
|
|
83
84
|
}
|
|
84
85
|
let prevIndex = focusedIndexRef.current;
|
|
85
86
|
do {
|
|
@@ -87,7 +88,7 @@ exports.ComboBoxInput = react_1.default.forwardRef((props, forwardedRef) => {
|
|
|
87
88
|
const prevElement = (_p = currentElement === null || currentElement === void 0 ? void 0 : currentElement.previousElementSibling) !== null && _p !== void 0 ? _p : (_q = menuRef.current) === null || _q === void 0 ? void 0 : _q.querySelector('[data-iui-index]:last-of-type');
|
|
88
89
|
prevIndex = Number(prevElement === null || prevElement === void 0 ? void 0 : prevElement.getAttribute('data-iui-index'));
|
|
89
90
|
if ((prevElement === null || prevElement === void 0 ? void 0 : prevElement.ariaDisabled) !== 'true') {
|
|
90
|
-
return dispatch(
|
|
91
|
+
return dispatch({ type: 'focus', value: prevIndex });
|
|
91
92
|
}
|
|
92
93
|
} while (prevIndex !== focusedIndexRef.current);
|
|
93
94
|
break;
|
|
@@ -95,21 +96,32 @@ exports.ComboBoxInput = react_1.default.forwardRef((props, forwardedRef) => {
|
|
|
95
96
|
case 'Enter': {
|
|
96
97
|
event.preventDefault();
|
|
97
98
|
if (isOpen) {
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
if (multiple) {
|
|
100
|
+
// Keep menu open when multiselect is enabled and user selects an item
|
|
101
|
+
if (focusedIndexRef.current > -1) {
|
|
102
|
+
onClickHandler === null || onClickHandler === void 0 ? void 0 : onClickHandler(focusedIndexRef.current);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
dispatch({ type: 'close' });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
onClickHandler === null || onClickHandler === void 0 ? void 0 : onClickHandler(focusedIndexRef.current);
|
|
110
|
+
dispatch({ type: 'close' });
|
|
111
|
+
}
|
|
100
112
|
}
|
|
101
113
|
else {
|
|
102
|
-
dispatch(
|
|
114
|
+
dispatch({ type: 'open' });
|
|
103
115
|
}
|
|
104
116
|
break;
|
|
105
117
|
}
|
|
106
118
|
case 'Escape': {
|
|
107
119
|
event.preventDefault();
|
|
108
|
-
dispatch(
|
|
120
|
+
dispatch({ type: 'close' });
|
|
109
121
|
break;
|
|
110
122
|
}
|
|
111
123
|
case 'Tab':
|
|
112
|
-
dispatch(
|
|
124
|
+
dispatch({ type: 'close' });
|
|
113
125
|
break;
|
|
114
126
|
}
|
|
115
127
|
}, [
|
|
@@ -117,15 +129,20 @@ exports.ComboBoxInput = react_1.default.forwardRef((props, forwardedRef) => {
|
|
|
117
129
|
enableVirtualization,
|
|
118
130
|
isOpen,
|
|
119
131
|
menuRef,
|
|
132
|
+
multiple,
|
|
133
|
+
onClickHandler,
|
|
120
134
|
onKeyDownProp,
|
|
121
135
|
optionsExtraInfoRef,
|
|
122
136
|
]);
|
|
123
137
|
const handleFocus = react_1.default.useCallback((event) => {
|
|
124
|
-
dispatch(
|
|
138
|
+
dispatch({ type: 'open' });
|
|
125
139
|
onFocusProp === null || onFocusProp === void 0 ? void 0 : onFocusProp(event);
|
|
126
140
|
}, [dispatch, onFocusProp]);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
141
|
+
const [tagContainerWidthRef, tagContainerWidth] = (0, utils_1.useContainerWidth)();
|
|
142
|
+
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
143
|
+
react_1.default.createElement(Input_1.Input, { ref: refs, onKeyDown: handleKeyDown, onFocus: handleFocus, "aria-activedescendant": isOpen && focusedIndex != undefined && focusedIndex > -1
|
|
144
|
+
? getIdFromIndex(focusedIndex)
|
|
145
|
+
: undefined, role: 'combobox', "aria-controls": isOpen ? `${id}-list` : undefined, "aria-autocomplete": 'list', spellCheck: false, autoCapitalize: 'none', autoCorrect: 'off', style: multiple ? { paddingLeft: tagContainerWidth + 18 } : {}, ...rest }),
|
|
146
|
+
multiple && selectTags && (react_1.default.createElement(ComboBoxMultipleContainer_1.ComboBoxMultipleContainer, { ref: tagContainerWidthRef, selectedItems: selectTags }))));
|
|
130
147
|
});
|
|
131
148
|
exports.ComboBoxInput.displayName = 'ComboBoxInput';
|
|
@@ -13,7 +13,7 @@ const react_1 = __importDefault(require("react"));
|
|
|
13
13
|
const utils_1 = require("../utils");
|
|
14
14
|
const helpers_1 = require("./helpers");
|
|
15
15
|
exports.ComboBoxMenuItem = react_1.default.memo(react_1.default.forwardRef((props, forwardedRef) => {
|
|
16
|
-
const { children, isSelected, disabled, value, onClick, sublabel, size = !!sublabel ? 'large' : 'default', icon, badge, className, role = '
|
|
16
|
+
const { children, isSelected, disabled, value, onClick, sublabel, size = !!sublabel ? 'large' : 'default', icon, badge, className, role = 'option', index, ...rest } = props;
|
|
17
17
|
const { focusedIndex, enableVirtualization } = (0, utils_1.useSafeContext)(helpers_1.ComboBoxStateContext);
|
|
18
18
|
const focusRef = (el) => {
|
|
19
19
|
if (!enableVirtualization && focusedIndex === index) {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export declare const ComboBoxMultipleContainer: React.ForwardRefExoticComponent<{
|
|
3
|
+
selectedItems?: React.ReactNode[] | undefined;
|
|
4
|
+
} & Omit<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>>, "children"> & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ComboBoxMultipleContainer = void 0;
|
|
7
|
+
/*---------------------------------------------------------------------------------------------
|
|
8
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
9
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
10
|
+
*--------------------------------------------------------------------------------------------*/
|
|
11
|
+
const react_1 = __importDefault(require("react"));
|
|
12
|
+
const SelectTagContainer_1 = __importDefault(require("../Select/SelectTagContainer"));
|
|
13
|
+
exports.ComboBoxMultipleContainer = react_1.default.forwardRef((props, ref) => {
|
|
14
|
+
const { selectedItems = [], ...rest } = props;
|
|
15
|
+
return react_1.default.createElement(SelectTagContainer_1.default, { ref: ref, tags: selectedItems, ...rest });
|
|
16
|
+
});
|
|
17
|
+
exports.ComboBoxMultipleContainer.displayName = 'ComboBoxMultipleContainer';
|
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { SelectOption } from '../Select/Select';
|
|
3
|
-
declare type ComboBoxAction =
|
|
3
|
+
declare type ComboBoxAction = {
|
|
4
|
+
type: 'multiselect';
|
|
5
|
+
value: number[];
|
|
6
|
+
} | {
|
|
7
|
+
type: 'open';
|
|
8
|
+
} | {
|
|
9
|
+
type: 'close';
|
|
10
|
+
} | {
|
|
11
|
+
type: 'select';
|
|
12
|
+
value: number;
|
|
13
|
+
} | {
|
|
14
|
+
type: 'focus';
|
|
15
|
+
value: number | undefined;
|
|
16
|
+
};
|
|
4
17
|
export declare const comboBoxReducer: (state: {
|
|
5
18
|
isOpen: boolean;
|
|
6
|
-
|
|
19
|
+
selected: number | number[];
|
|
7
20
|
focusedIndex: number;
|
|
8
|
-
},
|
|
21
|
+
}, action: ComboBoxAction) => {
|
|
9
22
|
isOpen: boolean;
|
|
10
|
-
|
|
23
|
+
selected: number | number[];
|
|
11
24
|
focusedIndex: number;
|
|
12
25
|
};
|
|
13
26
|
export declare const ComboBoxRefsContext: React.Context<{
|
|
@@ -24,9 +37,11 @@ declare type ComboBoxStateContextProps<T = unknown> = {
|
|
|
24
37
|
minWidth: number;
|
|
25
38
|
enableVirtualization: boolean;
|
|
26
39
|
filteredOptions: SelectOption<T>[];
|
|
40
|
+
onClickHandler?: (prop: number) => void;
|
|
27
41
|
getMenuItem: (option: SelectOption<T>, filteredIndex?: number) => JSX.Element;
|
|
28
42
|
focusedIndex?: number;
|
|
43
|
+
multiple?: boolean;
|
|
29
44
|
};
|
|
30
45
|
export declare const ComboBoxStateContext: React.Context<ComboBoxStateContextProps<unknown> | undefined>;
|
|
31
|
-
export declare const ComboBoxActionContext: React.Context<((x:
|
|
46
|
+
export declare const ComboBoxActionContext: React.Context<((x: ComboBoxAction) => void) | undefined>;
|
|
32
47
|
export {};
|