@mongodb-js/compass-components 1.1.1 → 1.2.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.
Files changed (47) hide show
  1. package/lib/components/item-action-controls.js +2 -2
  2. package/lib/components/item-action-controls.js.map +1 -1
  3. package/lib/components/leafygreen.d.ts +3 -1
  4. package/lib/components/leafygreen.d.ts.map +1 -1
  5. package/lib/components/leafygreen.js +6 -0
  6. package/lib/components/leafygreen.js.map +1 -1
  7. package/lib/index.d.ts +0 -1
  8. package/lib/index.d.ts.map +1 -1
  9. package/lib/index.js +2 -6
  10. package/lib/index.js.map +1 -1
  11. package/package.json +3 -8
  12. package/lib/components/leafy-green/combobox/chip.d.ts +0 -4
  13. package/lib/components/leafy-green/combobox/chip.d.ts.map +0 -1
  14. package/lib/components/leafy-green/combobox/chip.js +0 -182
  15. package/lib/components/leafy-green/combobox/chip.js.map +0 -1
  16. package/lib/components/leafy-green/combobox/combobox-context.d.ts +0 -15
  17. package/lib/components/leafy-green/combobox/combobox-context.d.ts.map +0 -1
  18. package/lib/components/leafy-green/combobox/combobox-context.js +0 -12
  19. package/lib/components/leafy-green/combobox/combobox-context.js.map +0 -1
  20. package/lib/components/leafy-green/combobox/combobox-group.d.ts +0 -9
  21. package/lib/components/leafy-green/combobox/combobox-group.d.ts.map +0 -1
  22. package/lib/components/leafy-green/combobox/combobox-group.js +0 -69
  23. package/lib/components/leafy-green/combobox/combobox-group.js.map +0 -1
  24. package/lib/components/leafy-green/combobox/combobox-option.d.ts +0 -10
  25. package/lib/components/leafy-green/combobox/combobox-option.d.ts.map +0 -1
  26. package/lib/components/leafy-green/combobox/combobox-option.js +0 -142
  27. package/lib/components/leafy-green/combobox/combobox-option.js.map +0 -1
  28. package/lib/components/leafy-green/combobox/combobox-types.d.ts +0 -94
  29. package/lib/components/leafy-green/combobox/combobox-types.d.ts.map +0 -1
  30. package/lib/components/leafy-green/combobox/combobox-types.js +0 -36
  31. package/lib/components/leafy-green/combobox/combobox-types.js.map +0 -1
  32. package/lib/components/leafy-green/combobox/combobox.d.ts +0 -4
  33. package/lib/components/leafy-green/combobox/combobox.d.ts.map +0 -1
  34. package/lib/components/leafy-green/combobox/combobox.js +0 -725
  35. package/lib/components/leafy-green/combobox/combobox.js.map +0 -1
  36. package/lib/components/leafy-green/combobox/combobox.styles.d.ts +0 -39
  37. package/lib/components/leafy-green/combobox/combobox.styles.d.ts.map +0 -1
  38. package/lib/components/leafy-green/combobox/combobox.styles.js +0 -299
  39. package/lib/components/leafy-green/combobox/combobox.styles.js.map +0 -1
  40. package/lib/components/leafy-green/combobox/index.d.ts +0 -4
  41. package/lib/components/leafy-green/combobox/index.d.ts.map +0 -1
  42. package/lib/components/leafy-green/combobox/index.js +0 -13
  43. package/lib/components/leafy-green/combobox/index.js.map +0 -1
  44. package/lib/components/leafy-green/combobox/util.d.ts +0 -27
  45. package/lib/components/leafy-green/combobox/util.d.ts.map +0 -1
  46. package/lib/components/leafy-green/combobox/util.js +0 -65
  47. package/lib/components/leafy-green/combobox/util.js.map +0 -1
@@ -1,725 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
- Object.defineProperty(exports, "__esModule", { value: true });
29
- const react_1 = __importStar(require("react"));
30
- const lodash_1 = require("lodash");
31
- const typography_1 = require("@leafygreen-ui/typography");
32
- const popover_1 = __importDefault(require("@leafygreen-ui/popover"));
33
- const hooks_1 = require("@leafygreen-ui/hooks");
34
- const interaction_ring_1 = __importDefault(require("@leafygreen-ui/interaction-ring"));
35
- const icon_1 = __importDefault(require("@leafygreen-ui/icon"));
36
- const icon_button_1 = __importDefault(require("@leafygreen-ui/icon-button"));
37
- const emotion_1 = require("@leafygreen-ui/emotion");
38
- const palette_1 = require("@leafygreen-ui/palette");
39
- const lib_1 = require("@leafygreen-ui/lib");
40
- const combobox_types_1 = require("./combobox-types");
41
- const combobox_context_1 = require("./combobox-context");
42
- const combobox_option_1 = require("./combobox-option");
43
- const chip_1 = require("./chip");
44
- const combobox_styles_1 = require("./combobox.styles");
45
- const combobox_group_1 = require("./combobox-group");
46
- const util_1 = require("./util");
47
- function Combobox({ children, label, description, placeholder = 'Select', 'aria-label': ariaLabel, disabled = false, size = 'default', darkMode = false, state = 'none', errorMessage, searchState = 'unset', searchEmptyMessage = 'No results found', searchErrorMessage = 'Could not get results!', searchLoadingMessage = 'Loading results...', filteredOptions, onFilter, clearable = true, onClear, overflow = 'expand-y', multiselect = false, initialValue, onChange, value, chipTruncationLocation, chipCharacterLimit = 12, className, ...rest }) {
48
- const getOptionRef = (0, hooks_1.useDynamicRefs)({ prefix: 'option' });
49
- const getChipRef = (0, hooks_1.useDynamicRefs)({ prefix: 'chip' });
50
- const inputId = (0, hooks_1.useIdAllocator)({ prefix: 'combobox-input' });
51
- const labelId = (0, hooks_1.useIdAllocator)({ prefix: 'combobox-label' });
52
- const menuId = (0, hooks_1.useIdAllocator)({ prefix: 'combobox-menu' });
53
- const comboboxRef = (0, react_1.useRef)(null);
54
- const clearButtonRef = (0, react_1.useRef)(null);
55
- const inputWrapperRef = (0, react_1.useRef)(null);
56
- const inputRef = (0, react_1.useRef)(null);
57
- const menuRef = (0, react_1.useRef)(null);
58
- const [isOpen, setOpen] = (0, react_1.useState)(false);
59
- const prevOpenState = (0, hooks_1.usePrevious)(isOpen);
60
- const [focusedOption, setFocusedOption] = (0, react_1.useState)(null);
61
- const [selection, setSelection] = (0, react_1.useState)(null);
62
- const prevSelection = (0, hooks_1.usePrevious)(selection);
63
- const [inputValue, setInputValue] = (0, react_1.useState)('');
64
- const prevValue = (0, hooks_1.usePrevious)(inputValue);
65
- const [focusedChip, setFocusedChip] = (0, react_1.useState)(null);
66
- const doesSelectionExist = !(0, lodash_1.isNull)(selection) &&
67
- (((0, lodash_1.isArray)(selection) && selection.length > 0) || (0, lodash_1.isString)(selection));
68
- const isMultiselect = (0, react_1.useCallback)((test) => multiselect && (0, lodash_1.isArray)(test), [multiselect]);
69
- const setInputFocus = (0, react_1.useCallback)((cursorPos) => {
70
- if (!disabled && inputRef && inputRef.current) {
71
- inputRef.current.focus();
72
- if (!(0, lodash_1.isUndefined)(cursorPos)) {
73
- inputRef.current.setSelectionRange(cursorPos, cursorPos);
74
- }
75
- }
76
- }, [disabled]);
77
- const updateSelection = (0, react_1.useCallback)((value) => {
78
- if (isMultiselect(selection)) {
79
- const newSelection = (0, lodash_1.clone)(selection);
80
- if ((0, lodash_1.isNull)(value)) {
81
- newSelection.length = 0;
82
- }
83
- else {
84
- if (selection.includes(value)) {
85
- newSelection.splice(newSelection.indexOf(value), 1);
86
- }
87
- else {
88
- newSelection.push(value);
89
- setInputValue('');
90
- }
91
- }
92
- setSelection(newSelection);
93
- onChange === null || onChange === void 0 ? void 0 : onChange(newSelection);
94
- }
95
- else {
96
- const newSelection = value;
97
- setSelection(newSelection);
98
- onChange === null || onChange === void 0 ? void 0 : onChange(newSelection);
99
- }
100
- }, [isMultiselect, onChange, selection]);
101
- const scrollToEnd = () => {
102
- if (inputWrapperRef && inputWrapperRef.current) {
103
- inputWrapperRef.current.scrollLeft = inputWrapperRef.current.scrollWidth;
104
- }
105
- };
106
- const placeholderValue = multiselect && (0, lodash_1.isArray)(selection) && selection.length > 0
107
- ? undefined
108
- : placeholder;
109
- const allOptions = (0, react_1.useMemo)(() => (0, util_1.flattenChildren)(children), [children]);
110
- const getDisplayNameForValue = (0, react_1.useCallback)((value) => {
111
- var _a, _b;
112
- return value
113
- ? (_b = (_a = allOptions.find((opt) => opt.value === value)) === null || _a === void 0 ? void 0 : _a.displayName) !== null && _b !== void 0 ? _b : value
114
- : '';
115
- }, [allOptions]);
116
- const isOptionVisible = (0, react_1.useCallback)((option) => {
117
- const value = typeof option === 'string' ? option : option.value;
118
- if (filteredOptions && filteredOptions.length > 0) {
119
- return filteredOptions.includes(value);
120
- }
121
- const displayName = typeof option === 'string'
122
- ? getDisplayNameForValue(value)
123
- : option.displayName;
124
- return displayName.toLowerCase().includes(inputValue.toLowerCase());
125
- }, [filteredOptions, getDisplayNameForValue, inputValue]);
126
- const visibleOptions = (0, react_1.useMemo)(() => allOptions.filter(isOptionVisible), [allOptions, isOptionVisible]);
127
- const isValueValid = (0, react_1.useCallback)((value) => {
128
- return value ? !!allOptions.find((opt) => opt.value === value) : false;
129
- }, [allOptions]);
130
- const getIndexOfValue = (0, react_1.useCallback)((value) => {
131
- return visibleOptions
132
- ? visibleOptions.findIndex((option) => option.value === value)
133
- : -1;
134
- }, [visibleOptions]);
135
- const getValueAtIndex = (0, react_1.useCallback)((index) => {
136
- if (visibleOptions && visibleOptions.length >= index) {
137
- const option = visibleOptions[index];
138
- return option ? option.value : undefined;
139
- }
140
- }, [visibleOptions]);
141
- const getActiveChipIndex = (0, react_1.useCallback)(() => isMultiselect(selection)
142
- ? selection.findIndex((value) => { var _a, _b; return (_b = (_a = getChipRef(value)) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.contains(document.activeElement); })
143
- : -1, [getChipRef, isMultiselect, selection]);
144
- const getFocusedElementName = (0, react_1.useCallback)(() => {
145
- var _a, _b, _c;
146
- const isFocusOn = {
147
- Input: (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.contains(document.activeElement),
148
- ClearButton: (_b = clearButtonRef.current) === null || _b === void 0 ? void 0 : _b.contains(document.activeElement),
149
- Chip: isMultiselect(selection) &&
150
- selection.some((value) => { var _a, _b; return (_b = (_a = getChipRef(value)) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.contains(document.activeElement); }),
151
- };
152
- const getActiveChipIndex = () => isMultiselect(selection)
153
- ? selection.findIndex((value) => { var _a, _b; return (_b = (_a = getChipRef(value)) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.contains(document.activeElement); })
154
- : -1;
155
- if (isMultiselect(selection) && isFocusOn.Chip) {
156
- if (getActiveChipIndex() === 0) {
157
- return 'FirstChip';
158
- }
159
- else if (getActiveChipIndex() === selection.length - 1) {
160
- return 'LastChip';
161
- }
162
- return 'MiddleChip';
163
- }
164
- else if (isFocusOn.Input) {
165
- return 'Input';
166
- }
167
- else if (isFocusOn.ClearButton) {
168
- return 'ClearButton';
169
- }
170
- else if ((_c = comboboxRef.current) === null || _c === void 0 ? void 0 : _c.contains(document.activeElement)) {
171
- return 'Combobox';
172
- }
173
- }, [getChipRef, isMultiselect, selection]);
174
- const updateFocusedOption = (0, react_1.useCallback)((direction) => {
175
- var _a;
176
- const optionsCount = (_a = visibleOptions === null || visibleOptions === void 0 ? void 0 : visibleOptions.length) !== null && _a !== void 0 ? _a : 0;
177
- const lastIndex = optionsCount - 1 > 0 ? optionsCount - 1 : 0;
178
- const indexOfFocus = getIndexOfValue(focusedOption);
179
- if (direction && isOpen) {
180
- setFocusedChip(null);
181
- setInputFocus();
182
- }
183
- switch (direction) {
184
- case 'next': {
185
- const newValue = indexOfFocus + 1 < optionsCount
186
- ? getValueAtIndex(indexOfFocus + 1)
187
- : getValueAtIndex(0);
188
- setFocusedOption(newValue !== null && newValue !== void 0 ? newValue : null);
189
- break;
190
- }
191
- case 'prev': {
192
- const newValue = indexOfFocus - 1 >= 0
193
- ? getValueAtIndex(indexOfFocus - 1)
194
- : getValueAtIndex(lastIndex);
195
- setFocusedOption(newValue !== null && newValue !== void 0 ? newValue : null);
196
- break;
197
- }
198
- case 'last': {
199
- const newValue = getValueAtIndex(lastIndex);
200
- setFocusedOption(newValue !== null && newValue !== void 0 ? newValue : null);
201
- break;
202
- }
203
- case 'first':
204
- default: {
205
- const newValue = getValueAtIndex(0);
206
- setFocusedOption(newValue !== null && newValue !== void 0 ? newValue : null);
207
- }
208
- }
209
- }, [
210
- focusedOption,
211
- getIndexOfValue,
212
- getValueAtIndex,
213
- isOpen,
214
- setInputFocus,
215
- visibleOptions === null || visibleOptions === void 0 ? void 0 : visibleOptions.length,
216
- ]);
217
- const updateFocusedChip = (0, react_1.useCallback)((direction, relativeToIndex) => {
218
- if (isMultiselect(selection)) {
219
- switch (direction) {
220
- case 'next': {
221
- const referenceChipIndex = relativeToIndex !== null && relativeToIndex !== void 0 ? relativeToIndex : getActiveChipIndex();
222
- const nextChipIndex = referenceChipIndex + 1 < selection.length
223
- ? referenceChipIndex + 1
224
- : selection.length - 1;
225
- const nextChipValue = selection[nextChipIndex];
226
- setFocusedChip(nextChipValue);
227
- break;
228
- }
229
- case 'prev': {
230
- const referenceChipIndex = relativeToIndex !== null && relativeToIndex !== void 0 ? relativeToIndex : getActiveChipIndex();
231
- const prevChipIndex = referenceChipIndex > 0
232
- ? referenceChipIndex - 1
233
- : referenceChipIndex < 0
234
- ? selection.length - 1
235
- : 0;
236
- const prevChipValue = selection[prevChipIndex];
237
- setFocusedChip(prevChipValue);
238
- break;
239
- }
240
- case 'first': {
241
- const firstChipValue = selection[0];
242
- setFocusedChip(firstChipValue);
243
- break;
244
- }
245
- case 'last': {
246
- const lastChipValue = selection[selection.length - 1];
247
- setFocusedChip(lastChipValue);
248
- break;
249
- }
250
- default:
251
- setFocusedChip(null);
252
- break;
253
- }
254
- }
255
- }, [getActiveChipIndex, isMultiselect, selection]);
256
- const handleArrowKey = (0, react_1.useCallback)((direction, event) => {
257
- var _a, _b, _c, _d, _e;
258
- if (direction)
259
- setFocusedOption(null);
260
- const focusedElementName = getFocusedElementName();
261
- switch (direction) {
262
- case 'right':
263
- switch (focusedElementName) {
264
- case 'Input': {
265
- if (((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.selectionEnd) ===
266
- ((_b = inputRef.current) === null || _b === void 0 ? void 0 : _b.value.length)) {
267
- (_c = clearButtonRef.current) === null || _c === void 0 ? void 0 : _c.focus();
268
- }
269
- break;
270
- }
271
- case 'LastChip': {
272
- event.preventDefault();
273
- setInputFocus(0);
274
- updateFocusedChip(null);
275
- break;
276
- }
277
- case 'FirstChip':
278
- case 'MiddleChip': {
279
- updateFocusedChip('next');
280
- break;
281
- }
282
- case 'ClearButton':
283
- default:
284
- break;
285
- }
286
- break;
287
- case 'left':
288
- switch (focusedElementName) {
289
- case 'ClearButton': {
290
- event.preventDefault();
291
- setInputFocus((_d = inputRef === null || inputRef === void 0 ? void 0 : inputRef.current) === null || _d === void 0 ? void 0 : _d.value.length);
292
- break;
293
- }
294
- case 'Input':
295
- case 'MiddleChip':
296
- case 'LastChip': {
297
- if (isMultiselect(selection)) {
298
- if (focusedElementName === 'Input' &&
299
- ((_e = inputRef.current) === null || _e === void 0 ? void 0 : _e.selectionStart) !== 0) {
300
- break;
301
- }
302
- updateFocusedChip('prev');
303
- }
304
- break;
305
- }
306
- case 'FirstChip':
307
- default:
308
- break;
309
- }
310
- break;
311
- default:
312
- updateFocusedChip(null);
313
- break;
314
- }
315
- }, [
316
- getFocusedElementName,
317
- isMultiselect,
318
- selection,
319
- setInputFocus,
320
- updateFocusedChip,
321
- ]);
322
- (0, react_1.useEffect)(() => {
323
- if (inputValue !== prevValue) {
324
- updateFocusedOption('first');
325
- }
326
- }, [inputValue, isOpen, prevValue, updateFocusedOption]);
327
- (0, react_1.useEffect)(() => {
328
- if (focusedOption) {
329
- const focusedElementRef = getOptionRef(focusedOption);
330
- if (focusedElementRef && focusedElementRef.current && menuRef.current) {
331
- const { offsetTop: optionTop } = focusedElementRef.current;
332
- const { scrollTop: menuScroll, offsetHeight: menuHeight } = menuRef.current;
333
- if (optionTop > menuHeight || optionTop < menuScroll) {
334
- menuRef.current.scrollTop = optionTop;
335
- }
336
- }
337
- }
338
- }, [focusedOption, getOptionRef]);
339
- const renderInternalOptions = (0, react_1.useCallback)((_children) => {
340
- return react_1.default.Children.map(_children, (child) => {
341
- if ((0, lib_1.isComponentType)(child, 'ComboboxOption')) {
342
- const { value, displayName } = (0, util_1.getNameAndValue)(child.props);
343
- if (isOptionVisible(value)) {
344
- const { className, glyph } = child.props;
345
- const index = allOptions.findIndex((opt) => opt.value === value);
346
- const isFocused = focusedOption === value;
347
- const isSelected = isMultiselect(selection)
348
- ? selection.includes(value)
349
- : selection === value;
350
- const setSelected = () => {
351
- setFocusedOption(value);
352
- updateSelection(value);
353
- setInputFocus();
354
- if (value === selection) {
355
- closeMenu();
356
- }
357
- };
358
- const optionRef = getOptionRef(value);
359
- return (react_1.default.createElement(combobox_option_1.InternalComboboxOption, { value: value, displayName: displayName, isFocused: isFocused, isSelected: isSelected, setSelected: setSelected, glyph: glyph, className: className, index: index, ref: optionRef }));
360
- }
361
- }
362
- else if ((0, lib_1.isComponentType)(child, 'ComboboxGroup')) {
363
- const nestedChildren = renderInternalOptions(child.props.children);
364
- if (nestedChildren && (nestedChildren === null || nestedChildren === void 0 ? void 0 : nestedChildren.length) > 0) {
365
- return (react_1.default.createElement(combobox_group_1.InternalComboboxGroup, { label: child.props.label, className: child.props.className }, renderInternalOptions(nestedChildren)));
366
- }
367
- }
368
- });
369
- }, [
370
- allOptions,
371
- focusedOption,
372
- getOptionRef,
373
- isMultiselect,
374
- isOptionVisible,
375
- selection,
376
- setInputFocus,
377
- updateSelection,
378
- ]);
379
- const renderedOptions = (0, react_1.useMemo)(() => renderInternalOptions(children), [children, renderInternalOptions]);
380
- const renderedChips = (0, react_1.useMemo)(() => {
381
- if (isMultiselect(selection)) {
382
- return selection.filter(isValueValid).map((value, index) => {
383
- const displayName = getDisplayNameForValue(value);
384
- const onRemove = () => {
385
- updateFocusedChip('next', index);
386
- updateSelection(value);
387
- };
388
- const isFocused = focusedChip === value;
389
- const chipRef = getChipRef(value);
390
- const onFocus = () => {
391
- setFocusedChip(value);
392
- };
393
- return (react_1.default.createElement(chip_1.Chip, { key: value, displayName: displayName, isFocused: isFocused, onRemove: onRemove, onFocus: onFocus, ref: chipRef }));
394
- });
395
- }
396
- }, [
397
- isMultiselect,
398
- selection,
399
- isValueValid,
400
- getDisplayNameForValue,
401
- focusedChip,
402
- getChipRef,
403
- updateFocusedChip,
404
- updateSelection,
405
- ]);
406
- const renderedInputIcons = (0, react_1.useMemo)(() => {
407
- const handleClearButtonClick = (e) => {
408
- if (!disabled) {
409
- updateSelection(null);
410
- onClear === null || onClear === void 0 ? void 0 : onClear(e);
411
- onFilter === null || onFilter === void 0 ? void 0 : onFilter('');
412
- if (!isOpen) {
413
- openMenu();
414
- }
415
- }
416
- };
417
- return (react_1.default.createElement(react_1.default.Fragment, null,
418
- clearable && doesSelectionExist && (react_1.default.createElement(icon_button_1.default, { "aria-label": "Clear selection", "aria-disabled": disabled, disabled: disabled, ref: clearButtonRef, onClick: handleClearButtonClick, onFocus: handleClearButtonFocus, className: combobox_styles_1.clearButton },
419
- react_1.default.createElement(icon_1.default, { glyph: "XWithCircle" }))),
420
- state === 'error' ? (react_1.default.createElement(icon_1.default, { glyph: "Warning", color: palette_1.uiColors.red.base, className: combobox_styles_1.endIcon })) : (react_1.default.createElement(icon_1.default, { glyph: "CaretDown", className: combobox_styles_1.endIcon }))));
421
- }, [
422
- clearable,
423
- doesSelectionExist,
424
- disabled,
425
- state,
426
- updateSelection,
427
- onClear,
428
- onFilter,
429
- isOpen,
430
- ]);
431
- const withIcons = (0, react_1.useMemo)(() => allOptions.some((opt) => opt.hasGlyph), [allOptions]);
432
- const onCloseMenu = (0, react_1.useCallback)(() => {
433
- var _a;
434
- if (!isMultiselect(selection) && selection === prevSelection) {
435
- const exactMatchedOption = visibleOptions.find((option) => option.displayName === inputValue || option.value === inputValue);
436
- if (exactMatchedOption && !value) {
437
- setSelection(exactMatchedOption.value);
438
- }
439
- else {
440
- const displayName = (_a = getDisplayNameForValue(selection)) !== null && _a !== void 0 ? _a : '';
441
- setInputValue(displayName);
442
- }
443
- }
444
- }, [
445
- getDisplayNameForValue,
446
- inputValue,
447
- isMultiselect,
448
- prevSelection,
449
- selection,
450
- value,
451
- visibleOptions,
452
- ]);
453
- const onSelect = (0, react_1.useCallback)(() => {
454
- var _a;
455
- if (doesSelectionExist) {
456
- if (isMultiselect(selection)) {
457
- scrollToEnd();
458
- }
459
- else if (!isMultiselect(selection)) {
460
- const displayName = (_a = getDisplayNameForValue(selection)) !== null && _a !== void 0 ? _a : '';
461
- setInputValue(displayName);
462
- closeMenu();
463
- }
464
- }
465
- else {
466
- setInputValue('');
467
- }
468
- }, [doesSelectionExist, getDisplayNameForValue, isMultiselect, selection]);
469
- (0, react_1.useEffect)(() => {
470
- var _a;
471
- if (initialValue) {
472
- if ((0, lodash_1.isArray)(initialValue)) {
473
- const filteredValue = (_a = initialValue.filter((value) => isValueValid(value))) !== null && _a !== void 0 ? _a : [];
474
- setSelection(filteredValue);
475
- }
476
- else {
477
- if (isValueValid(initialValue)) {
478
- setSelection(initialValue);
479
- }
480
- }
481
- }
482
- else {
483
- setSelection((0, combobox_types_1.getNullSelection)(multiselect));
484
- }
485
- }, []);
486
- (0, react_1.useEffect)(() => {
487
- if (!(0, lodash_1.isUndefined)(value) && value !== prevValue) {
488
- if ((0, lodash_1.isNull)(value)) {
489
- setSelection(null);
490
- }
491
- else if (isMultiselect(value)) {
492
- const newSelection = value.filter(isValueValid);
493
- setSelection(newSelection);
494
- }
495
- else {
496
- setSelection(isValueValid(value) ? value : null);
497
- }
498
- }
499
- }, [isMultiselect, isValueValid, prevValue, value]);
500
- (0, react_1.useEffect)(() => {
501
- if (selection !== prevSelection) {
502
- onSelect();
503
- }
504
- }, [onSelect, prevSelection, selection]);
505
- (0, react_1.useEffect)(() => {
506
- if (!isOpen && prevOpenState !== isOpen) {
507
- onCloseMenu();
508
- }
509
- }, [isOpen, prevOpenState, onCloseMenu]);
510
- const closeMenu = () => setOpen(false);
511
- const openMenu = () => setOpen(true);
512
- const [menuWidth, setMenuWidth] = (0, react_1.useState)(0);
513
- (0, react_1.useEffect)(() => {
514
- var _a, _b;
515
- setMenuWidth((_b = (_a = comboboxRef.current) === null || _a === void 0 ? void 0 : _a.clientWidth) !== null && _b !== void 0 ? _b : 0);
516
- }, [comboboxRef, isOpen, focusedOption, selection]);
517
- const handleTransitionEnd = () => {
518
- var _a, _b;
519
- setMenuWidth((_b = (_a = comboboxRef.current) === null || _a === void 0 ? void 0 : _a.clientWidth) !== null && _b !== void 0 ? _b : 0);
520
- };
521
- const renderedMenuContents = (0, react_1.useMemo)(() => {
522
- switch (searchState) {
523
- case 'loading': {
524
- return (react_1.default.createElement("span", { className: combobox_styles_1.menuMessage },
525
- react_1.default.createElement(icon_1.default, { glyph: "Refresh", color: palette_1.uiColors.blue.base, className: combobox_styles_1.loadingIconStyle }),
526
- searchLoadingMessage));
527
- }
528
- case 'error': {
529
- return (react_1.default.createElement("span", { className: combobox_styles_1.menuMessage },
530
- react_1.default.createElement(icon_1.default, { glyph: "Warning", color: palette_1.uiColors.red.base }),
531
- searchErrorMessage));
532
- }
533
- case 'unset':
534
- default: {
535
- if (renderedOptions && renderedOptions.length > 0) {
536
- return react_1.default.createElement("ul", { className: combobox_styles_1.menuList }, renderedOptions);
537
- }
538
- return react_1.default.createElement("span", { className: combobox_styles_1.menuMessage }, searchEmptyMessage);
539
- }
540
- }
541
- }, [
542
- renderedOptions,
543
- searchEmptyMessage,
544
- searchErrorMessage,
545
- searchLoadingMessage,
546
- searchState,
547
- ]);
548
- const viewportSize = (0, hooks_1.useViewportSize)();
549
- const maxHeight = (0, react_1.useMemo)(() => {
550
- const maxMenuHeight = 274;
551
- const menuMargin = 8;
552
- if (viewportSize && comboboxRef.current && menuRef.current) {
553
- const { top: triggerTop, bottom: triggerBottom } = comboboxRef.current.getBoundingClientRect();
554
- const safeSpace = Math.max(viewportSize.height - triggerBottom, triggerTop);
555
- return Math.min(maxMenuHeight, safeSpace - menuMargin);
556
- }
557
- return maxMenuHeight;
558
- }, [viewportSize, comboboxRef, menuRef]);
559
- (0, react_1.useEffect)(() => {
560
- }, [focusedOption]);
561
- const handleInputWrapperMousedown = (e) => {
562
- if (e.target !== inputRef.current) {
563
- e.preventDefault();
564
- }
565
- };
566
- const handleInputWrapperClick = (e) => {
567
- if (e.target !== inputRef.current) {
568
- let cursorPos = 0;
569
- if (inputRef.current) {
570
- const mouseX = e.nativeEvent.offsetX;
571
- const inputRight = inputRef.current.offsetLeft + inputRef.current.clientWidth;
572
- cursorPos = mouseX > inputRight ? inputValue.length : 0;
573
- }
574
- setInputFocus(cursorPos);
575
- }
576
- openMenu();
577
- };
578
- const handleInputWrapperFocus = () => {
579
- scrollToEnd();
580
- };
581
- const handleInputChange = ({ target: { value }, }) => {
582
- setInputValue(value);
583
- onFilter === null || onFilter === void 0 ? void 0 : onFilter(value);
584
- };
585
- const handleClearButtonFocus = () => {
586
- setFocusedOption(null);
587
- };
588
- const handleKeyDown = (event) => {
589
- var _a, _b, _c;
590
- const isFocusInMenu = (_a = menuRef.current) === null || _a === void 0 ? void 0 : _a.contains(document.activeElement);
591
- const isFocusOnCombobox = (_b = comboboxRef.current) === null || _b === void 0 ? void 0 : _b.contains(document.activeElement);
592
- const isFocusInComponent = isFocusOnCombobox || isFocusInMenu;
593
- if (isFocusInComponent) {
594
- if (event.ctrlKey || event.shiftKey || event.altKey) {
595
- return;
596
- }
597
- const focusedElement = getFocusedElementName();
598
- switch (event.keyCode) {
599
- case util_1.keyMap.Tab: {
600
- switch (focusedElement) {
601
- case 'Input': {
602
- if (!doesSelectionExist) {
603
- closeMenu();
604
- updateFocusedOption('first');
605
- updateFocusedChip(null);
606
- }
607
- break;
608
- }
609
- case 'LastChip': {
610
- updateFocusedChip(null);
611
- break;
612
- }
613
- case 'FirstChip':
614
- case 'MiddleChip': {
615
- break;
616
- }
617
- case 'ClearButton':
618
- default:
619
- break;
620
- }
621
- break;
622
- }
623
- case util_1.keyMap.Escape: {
624
- closeMenu();
625
- updateFocusedOption('first');
626
- break;
627
- }
628
- case util_1.keyMap.Enter:
629
- case util_1.keyMap.Space: {
630
- if (isOpen) {
631
- event.preventDefault();
632
- }
633
- if (document.activeElement === inputRef.current &&
634
- isOpen &&
635
- !(0, lodash_1.isNull)(focusedOption)) {
636
- updateSelection(focusedOption);
637
- }
638
- else if (document.activeElement === clearButtonRef.current) {
639
- updateSelection(null);
640
- setInputFocus();
641
- }
642
- break;
643
- }
644
- case util_1.keyMap.Backspace: {
645
- if (isMultiselect(selection) &&
646
- ((_c = inputRef.current) === null || _c === void 0 ? void 0 : _c.selectionStart) === 0) {
647
- updateFocusedChip('last');
648
- }
649
- else {
650
- openMenu();
651
- }
652
- break;
653
- }
654
- case util_1.keyMap.ArrowDown: {
655
- if (isOpen) {
656
- event.preventDefault();
657
- }
658
- openMenu();
659
- updateFocusedOption('next');
660
- break;
661
- }
662
- case util_1.keyMap.ArrowUp: {
663
- if (isOpen) {
664
- event.preventDefault();
665
- }
666
- updateFocusedOption('prev');
667
- break;
668
- }
669
- case util_1.keyMap.ArrowRight: {
670
- handleArrowKey('right', event);
671
- break;
672
- }
673
- case util_1.keyMap.ArrowLeft: {
674
- handleArrowKey('left', event);
675
- break;
676
- }
677
- default: {
678
- if (!isOpen) {
679
- openMenu();
680
- }
681
- }
682
- }
683
- }
684
- };
685
- const handleBackdropClick = ({ target }) => {
686
- var _a, _b;
687
- const isChildFocused = ((_a = menuRef.current) === null || _a === void 0 ? void 0 : _a.contains(target)) ||
688
- ((_b = comboboxRef.current) === null || _b === void 0 ? void 0 : _b.contains(target)) ||
689
- false;
690
- if (!isChildFocused) {
691
- setOpen(false);
692
- }
693
- };
694
- (0, hooks_1.useEventListener)('mousedown', handleBackdropClick);
695
- return (react_1.default.createElement(combobox_context_1.ComboboxContext.Provider, { value: {
696
- multiselect,
697
- darkMode,
698
- size,
699
- withIcons,
700
- disabled,
701
- chipTruncationLocation,
702
- chipCharacterLimit,
703
- inputValue,
704
- } },
705
- react_1.default.createElement("div", { className: (0, emotion_1.cx)((0, combobox_styles_1.comboboxParentStyle)({ darkMode, size, overflow }), className), ...rest },
706
- react_1.default.createElement("div", null,
707
- label && (react_1.default.createElement(typography_1.Label, { id: labelId, htmlFor: inputId }, label)),
708
- description && react_1.default.createElement(typography_1.Description, null, description)),
709
- react_1.default.createElement(interaction_ring_1.default, { className: combobox_styles_1.interactionRingStyle, disabled: disabled, color: (0, combobox_styles_1.interactionRingColor)({ state, darkMode }) },
710
- react_1.default.createElement("div", { ref: comboboxRef, role: "combobox", "aria-expanded": isOpen, "aria-controls": menuId, "aria-owns": menuId, tabIndex: -1, className: combobox_styles_1.comboboxStyle, onMouseDown: handleInputWrapperMousedown, onClick: handleInputWrapperClick, onFocus: handleInputWrapperFocus, onKeyDown: handleKeyDown, onTransitionEnd: handleTransitionEnd, "data-disabled": disabled, "data-state": state },
711
- react_1.default.createElement("div", { ref: inputWrapperRef, className: (0, combobox_styles_1.inputWrapperStyle)({
712
- overflow,
713
- isOpen,
714
- selection,
715
- value: inputValue,
716
- }) },
717
- renderedChips,
718
- react_1.default.createElement("input", { "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : label, "aria-autocomplete": "list", "aria-controls": menuId, "aria-labelledby": labelId, ref: inputRef, id: inputId, className: combobox_styles_1.inputElementStyle, placeholder: placeholderValue, disabled: disabled !== null && disabled !== void 0 ? disabled : undefined, onChange: handleInputChange, value: inputValue, autoComplete: "off" })),
719
- renderedInputIcons)),
720
- state === 'error' && errorMessage && (react_1.default.createElement("div", { className: combobox_styles_1.errorMessageStyle }, errorMessage)),
721
- react_1.default.createElement(popover_1.default, { active: isOpen && !disabled, spacing: 4, align: "bottom", justify: "middle", refEl: comboboxRef, adjustOnMutation: true, className: (0, emotion_1.cx)((0, combobox_styles_1.popoverStyle)(), (0, combobox_styles_1.menuWrapperStyle)({ darkMode, size, width: menuWidth })), usePortal: false, popoverZIndex: 999999 },
722
- react_1.default.createElement("div", { id: menuId, role: "listbox", "aria-labelledby": labelId, "aria-expanded": isOpen, ref: menuRef, className: (0, combobox_styles_1.menuStyle)({ maxHeight }), onMouseDownCapture: (e) => e.preventDefault() }, renderedMenuContents)))));
723
- }
724
- exports.default = Combobox;
725
- //# sourceMappingURL=combobox.js.map