@jobber/components 6.112.2 → 6.113.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/dist/Autocomplete/Autocomplete.types.d.ts +41 -3
- package/dist/Autocomplete/components/FloatingMenu.d.ts +61 -0
- package/dist/Autocomplete/constants.d.ts +3 -0
- package/dist/Autocomplete/hooks/useAutocompleteListNav.d.ts +8 -1
- package/dist/Autocomplete/hooks/useChipNavigation.d.ts +17 -0
- package/dist/Autocomplete/index.cjs +437 -172
- package/dist/Autocomplete/index.d.ts +1 -7
- package/dist/Autocomplete/index.mjs +439 -174
- package/dist/Autocomplete/tests/Autocomplete.setup.d.ts +37 -2
- package/dist/Autocomplete/useAutocomplete.d.ts +3 -1
- package/dist/floating-ui.react-es.js +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/styles.css +377 -242
- package/package.json +2 -2
|
@@ -10,23 +10,27 @@ var Heading = require('../Heading-cjs.js');
|
|
|
10
10
|
var Text = require('../Text-cjs.js');
|
|
11
11
|
var Typography = require('../Typography-cjs.js');
|
|
12
12
|
var Icon = require('../Icon-cjs.js');
|
|
13
|
-
var InputText_index = require('../InputText/index.cjs');
|
|
14
13
|
var Glimmer = require('../Glimmer-cjs.js');
|
|
14
|
+
var InputText_index = require('../InputText/index.cjs');
|
|
15
15
|
var FormField = require('../FormField-cjs.js');
|
|
16
|
-
var filterDataAttributes = require('../filterDataAttributes-cjs.js');
|
|
17
16
|
var tslib_es6 = require('../tslib.es6-cjs.js');
|
|
17
|
+
require('react-hook-form');
|
|
18
|
+
require('../Button-cjs.js');
|
|
19
|
+
var filterDataAttributes = require('../filterDataAttributes-cjs.js');
|
|
18
20
|
require('react/jsx-runtime');
|
|
19
21
|
require('react-dom');
|
|
20
|
-
require('
|
|
22
|
+
require('../Content-cjs.js');
|
|
21
23
|
require('framer-motion');
|
|
22
|
-
require('../Button-cjs.js');
|
|
23
|
-
require('react-router-dom');
|
|
24
24
|
require('../useFormFieldFocus-cjs.js');
|
|
25
25
|
require('../InputValidation-cjs.js');
|
|
26
26
|
require('../Spinner-cjs.js');
|
|
27
|
-
require('
|
|
27
|
+
require('react-router-dom');
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
const AUTOCOMPLETE_MAX_HEIGHT$1 = 300;
|
|
30
|
+
/** Stable empty array for cleared/empty selection state. Reuse to avoid reference churn. */
|
|
31
|
+
const EMPTY_SELECTED_VALUES = [];
|
|
32
|
+
|
|
33
|
+
var styles$1 = {"list":"_37kZB-nYE08-","loadingList":"ULib3TUQja0-","option":"h5-1Pp0eRyo-","defaultOptionContent":"iBkyj85vd-E-","icon":"I6wAbSJJHNQ-","optionActive":"_4yhnonWAWRY-","actionActive":"oGLMF6n8C74-","action":"LBrwH6TEUYA-","section":"DDOv4DR8bJQ-","emptyStateMessage":"Twgjn26oldE-","stickyTop":"mc1-CEwZtHE-","scrollRegion":"kOR88SFNOn0-","persistentHeader":"_0O-kEf3h9ZI-","persistentFooter":"rQ9ZS7Rb7Z4-","textPersistent":"vxk57ZhP8GU-","multiSelectField":"AWoK6-P8fFE-","chipArea":"zJm0oR5x4qY-","inlineInput":"Oa26ZaVEi3g-","selectionChip":"IarOrfyYExE-","selectionChipActive":"pmtAk9HEhF4-","selectionChipDisabled":"nmXqlAV-DJo-","chipDismiss":"SmrmwoOlZDo-","limitText":"pqwAJ8VczCU-","spinning":"_0d8hyvaCPAw-"};
|
|
30
34
|
|
|
31
35
|
function flattenMenu(menu) {
|
|
32
36
|
const optionItems = [];
|
|
@@ -128,8 +132,8 @@ function invokeActiveItemOnEnter(event, activeIndex, renderable, onSelect, onAct
|
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
const MENU_OFFSET = design.tokens["space-small"];
|
|
131
|
-
const AUTOCOMPLETE_MAX_HEIGHT
|
|
132
|
-
function useAutocompleteListNav({ navigableCount, shouldResetActiveIndexOnClose, onMenuClose, selectedIndex, readOnly = false, }) {
|
|
135
|
+
const AUTOCOMPLETE_MAX_HEIGHT = 300;
|
|
136
|
+
function useAutocompleteListNav({ navigableCount, shouldResetActiveIndexOnClose, onMenuOpen, onMenuClose, selectedIndex, readOnly = false, disabled = false, outsidePressExcludeSelector, }) {
|
|
133
137
|
const [open, setOpen] = React.useState(false);
|
|
134
138
|
const [activeIndex, setActiveIndex] = React.useState(null);
|
|
135
139
|
const listRef = React.useRef([]);
|
|
@@ -139,7 +143,10 @@ function useAutocompleteListNav({ navigableCount, shouldResetActiveIndexOnClose,
|
|
|
139
143
|
open,
|
|
140
144
|
onOpenChange: (isOpen, _event, reason) => {
|
|
141
145
|
setOpen(isOpen);
|
|
142
|
-
if (isOpen
|
|
146
|
+
if (isOpen) {
|
|
147
|
+
onMenuOpen === null || onMenuOpen === void 0 ? void 0 : onMenuOpen();
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
143
150
|
if (shouldResetActiveIndexOnClose === null || shouldResetActiveIndexOnClose === void 0 ? void 0 : shouldResetActiveIndexOnClose()) {
|
|
144
151
|
setActiveIndex(null);
|
|
145
152
|
}
|
|
@@ -152,7 +159,7 @@ function useAutocompleteListNav({ navigableCount, shouldResetActiveIndexOnClose,
|
|
|
152
159
|
floatingUi_react.size({
|
|
153
160
|
apply({ availableHeight, elements }) {
|
|
154
161
|
const maxHeight$1 = maxHeight.calculateMaxHeight(availableHeight, {
|
|
155
|
-
maxHeight: AUTOCOMPLETE_MAX_HEIGHT
|
|
162
|
+
maxHeight: AUTOCOMPLETE_MAX_HEIGHT,
|
|
156
163
|
});
|
|
157
164
|
Object.assign(elements.floating.style, {
|
|
158
165
|
maxHeight: `${maxHeight$1}px`,
|
|
@@ -161,8 +168,9 @@ function useAutocompleteListNav({ navigableCount, shouldResetActiveIndexOnClose,
|
|
|
161
168
|
}),
|
|
162
169
|
],
|
|
163
170
|
});
|
|
171
|
+
const enabled = !readOnly && !disabled;
|
|
164
172
|
const click = floatingUi_react.useClick(context, {
|
|
165
|
-
enabled
|
|
173
|
+
enabled,
|
|
166
174
|
toggle: false, // Only open, never close on click
|
|
167
175
|
});
|
|
168
176
|
const listNav = floatingUi_react.useListNavigation(context, {
|
|
@@ -182,7 +190,16 @@ function useAutocompleteListNav({ navigableCount, shouldResetActiveIndexOnClose,
|
|
|
182
190
|
focusItemOnHover: false,
|
|
183
191
|
});
|
|
184
192
|
const dismiss = floatingUi_react.useDismiss(context, {
|
|
185
|
-
outsidePress:
|
|
193
|
+
outsidePress: outsidePressExcludeSelector
|
|
194
|
+
? (event) => {
|
|
195
|
+
var _a, _b, _c, _d;
|
|
196
|
+
const target = event.target;
|
|
197
|
+
const insideRef = (_b = (_a = context.elements.domReference) === null || _a === void 0 ? void 0 : _a.contains(target)) !== null && _b !== void 0 ? _b : false;
|
|
198
|
+
const insideFloating = (_d = (_c = context.elements.floating) === null || _c === void 0 ? void 0 : _c.contains(target)) !== null && _d !== void 0 ? _d : false;
|
|
199
|
+
const insideExclude = event.target.closest(outsidePressExcludeSelector);
|
|
200
|
+
return !(insideRef || insideFloating || insideExclude);
|
|
201
|
+
}
|
|
202
|
+
: true,
|
|
186
203
|
escapeKey: true,
|
|
187
204
|
outsidePressEvent: "click",
|
|
188
205
|
});
|
|
@@ -239,8 +256,8 @@ function createInteractionPointerDownHandler(isHandlingMenuInteractionRef) {
|
|
|
239
256
|
// Keeping this hook cohesive improves readability by centralizing related
|
|
240
257
|
// interactions and state transitions.
|
|
241
258
|
// eslint-disable-next-line max-statements
|
|
242
|
-
function useAutocomplete(props) {
|
|
243
|
-
const { menu, emptyActions, getOptionLabel: getOptionLabelProp, isOptionEqualToValue, inputValue, onInputChange, value, onChange, multiple, openOnFocus = true, readOnly = false, debounce: debounceMs = 300, } = props;
|
|
259
|
+
function useAutocomplete(props, inputRef) {
|
|
260
|
+
const { menu, emptyActions, getOptionLabel: getOptionLabelProp, isOptionEqualToValue, inputValue, onInputChange, value, onChange, multiple, openOnFocus = true, readOnly = false, disabled = false, debounce: debounceMs = 300, } = props;
|
|
244
261
|
const isHandlingMenuInteractionRef = React.useRef(false);
|
|
245
262
|
// TODO: Clean up the types in these refs by enhancing the type system in useCallbackRef
|
|
246
263
|
const getOptionLabelPropRef = jobberHooks.useCallbackRef((opt) => getOptionLabelProp === null || getOptionLabelProp === void 0 ? void 0 : getOptionLabelProp(opt));
|
|
@@ -256,7 +273,7 @@ function useAutocomplete(props) {
|
|
|
256
273
|
const isOptionSelected = React.useCallback((opt) => {
|
|
257
274
|
var _a;
|
|
258
275
|
if (multiple) {
|
|
259
|
-
const current = (_a = value) !== null && _a !== void 0 ? _a :
|
|
276
|
+
const current = (_a = value) !== null && _a !== void 0 ? _a : EMPTY_SELECTED_VALUES;
|
|
260
277
|
return current.some(v => equals(v, opt));
|
|
261
278
|
}
|
|
262
279
|
const current = value;
|
|
@@ -287,6 +304,9 @@ function useAutocomplete(props) {
|
|
|
287
304
|
// Skip debounce when clearing input for immediate feedback, preventing flickering of last selected item
|
|
288
305
|
if (debounceMs === 0 || inputValue === "") {
|
|
289
306
|
setDebouncedInputValue(inputValue);
|
|
307
|
+
// Cancel any pending debounced call so a stale intermediate value
|
|
308
|
+
// (e.g. "P" while deleting "Pipe…") doesn't overwrite the immediate set.
|
|
309
|
+
debouncedSetter.cancel();
|
|
290
310
|
return;
|
|
291
311
|
}
|
|
292
312
|
debouncedSetter(inputValue);
|
|
@@ -342,7 +362,7 @@ function useAutocomplete(props) {
|
|
|
342
362
|
const hasSelection = React.useMemo(() => {
|
|
343
363
|
var _a;
|
|
344
364
|
if (multiple) {
|
|
345
|
-
const current = (_a = value) !== null && _a !== void 0 ? _a :
|
|
365
|
+
const current = (_a = value) !== null && _a !== void 0 ? _a : EMPTY_SELECTED_VALUES;
|
|
346
366
|
return Array.isArray(current) && current.length > 0;
|
|
347
367
|
}
|
|
348
368
|
return value != null;
|
|
@@ -353,11 +373,13 @@ function useAutocomplete(props) {
|
|
|
353
373
|
const totalNavigableCount = headerInteractivePersistents.length +
|
|
354
374
|
mainNavigableCount +
|
|
355
375
|
footerInteractivePersistents.length;
|
|
356
|
-
// Compute the currently selected index in the global navigable list (header -> middle -> footer)
|
|
376
|
+
// Compute the currently selected index in the global navigable list (header -> middle -> footer).
|
|
377
|
+
// In multiple mode this is always null — there is no single "selected" index, and
|
|
378
|
+
// floating-ui would otherwise jump the highlight whenever the value array changes.
|
|
357
379
|
const selectedIndex = React.useMemo(() => {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
380
|
+
if (multiple)
|
|
381
|
+
return null;
|
|
382
|
+
const selectedValue = value;
|
|
361
383
|
if (!selectedValue)
|
|
362
384
|
return null;
|
|
363
385
|
const middleIndex = findNavigableIndexForValue(renderable, equals, selectedValue);
|
|
@@ -376,6 +398,23 @@ function useAutocomplete(props) {
|
|
|
376
398
|
shouldResetActiveIndexOnClose: () => !hasSelection,
|
|
377
399
|
selectedIndex,
|
|
378
400
|
readOnly,
|
|
401
|
+
disabled,
|
|
402
|
+
outsidePressExcludeSelector: multiple
|
|
403
|
+
? "[data-testid='ATL-AutocompleteRebuilt-chipArea']"
|
|
404
|
+
: undefined,
|
|
405
|
+
onMenuOpen: () => {
|
|
406
|
+
if (multiple)
|
|
407
|
+
return;
|
|
408
|
+
const selectedValue = value;
|
|
409
|
+
if (selectedValue) {
|
|
410
|
+
const selectedNavigableIndex = findNavigableIndexForValue(renderable, equals, selectedValue);
|
|
411
|
+
if (selectedNavigableIndex != null) {
|
|
412
|
+
setActiveIndex(selectedNavigableIndex);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
setActiveIndex(null);
|
|
417
|
+
},
|
|
379
418
|
onMenuClose: () => {
|
|
380
419
|
if (props.allowFreeForm !== true) {
|
|
381
420
|
const hasText = inputValue.trim().length > 0;
|
|
@@ -387,30 +426,30 @@ function useAutocomplete(props) {
|
|
|
387
426
|
}
|
|
388
427
|
},
|
|
389
428
|
});
|
|
390
|
-
|
|
429
|
+
const prevOpenRef = React.useRef(false);
|
|
430
|
+
// TODO: Leverage FloatingUI useFocus, and onArrowKeyDown to manage open state and allow onOpenChange to be the source of truth
|
|
431
|
+
// JOB-154442
|
|
391
432
|
React.useEffect(() => {
|
|
392
|
-
|
|
393
|
-
if (
|
|
394
|
-
|
|
395
|
-
// Always reset highlight when input is empty
|
|
396
|
-
setActiveIndex(null);
|
|
397
|
-
// In multiple mode, clearing the input should NOT clear the selection
|
|
398
|
-
if (multiple)
|
|
399
|
-
return;
|
|
400
|
-
// For single-select, treat clearing input as clearing the selection
|
|
401
|
-
if (hasSelection) {
|
|
402
|
-
onChange === null || onChange === void 0 ? void 0 : onChange(undefined);
|
|
433
|
+
var _a, _b;
|
|
434
|
+
if (open && !prevOpenRef.current) {
|
|
435
|
+
(_a = props.onOpen) === null || _a === void 0 ? void 0 : _a.call(props);
|
|
403
436
|
}
|
|
404
|
-
|
|
437
|
+
else if (!open && prevOpenRef.current) {
|
|
438
|
+
(_b = props.onClose) === null || _b === void 0 ? void 0 : _b.call(props);
|
|
439
|
+
}
|
|
440
|
+
prevOpenRef.current = open;
|
|
441
|
+
}, [open, props.onOpen, props.onClose]);
|
|
405
442
|
function selectOption(option) {
|
|
406
443
|
var _a;
|
|
407
444
|
if (multiple) {
|
|
408
|
-
const current = (_a = value) !== null && _a !== void 0 ? _a :
|
|
445
|
+
const current = (_a = value) !== null && _a !== void 0 ? _a : EMPTY_SELECTED_VALUES;
|
|
409
446
|
const exists = current.some(v => equals(v, option));
|
|
410
447
|
const next = exists
|
|
411
448
|
? current.filter(v => !equals(v, option))
|
|
412
449
|
: [...current, option];
|
|
413
|
-
onChange(next);
|
|
450
|
+
onChange((next.length === 0 ? EMPTY_SELECTED_VALUES : next));
|
|
451
|
+
lastInputWasUser.current = false;
|
|
452
|
+
onInputChange === null || onInputChange === void 0 ? void 0 : onInputChange("");
|
|
414
453
|
}
|
|
415
454
|
else {
|
|
416
455
|
onChange(option);
|
|
@@ -418,6 +457,14 @@ function useAutocomplete(props) {
|
|
|
418
457
|
onInputChange === null || onInputChange === void 0 ? void 0 : onInputChange(getOptionLabel(option));
|
|
419
458
|
}
|
|
420
459
|
}
|
|
460
|
+
function reHighlightSelectedItem() {
|
|
461
|
+
const selectedValue = value;
|
|
462
|
+
if (!selectedValue)
|
|
463
|
+
return;
|
|
464
|
+
const idx = findNavigableIndexForValue(renderable, equals, selectedValue);
|
|
465
|
+
if (idx != null)
|
|
466
|
+
setActiveIndex(idx);
|
|
467
|
+
}
|
|
421
468
|
function tryCommitFreeFormOnEnter() {
|
|
422
469
|
if (props.allowFreeForm !== true)
|
|
423
470
|
return false;
|
|
@@ -429,77 +476,38 @@ function useAutocomplete(props) {
|
|
|
429
476
|
commitFromInputText(inputText);
|
|
430
477
|
return true;
|
|
431
478
|
}
|
|
432
|
-
// Keep the selected item highlighted when deleting characters from the input
|
|
433
|
-
const prevInputLengthRef = React.useRef(inputValue.length);
|
|
434
|
-
React.useEffect(() => {
|
|
435
|
-
const previousLength = prevInputLengthRef.current;
|
|
436
|
-
prevInputLengthRef.current = inputValue.length;
|
|
437
|
-
if (!open)
|
|
438
|
-
return;
|
|
439
|
-
if (!lastInputWasUser.current)
|
|
440
|
-
return;
|
|
441
|
-
if (previousLength <= inputValue.length)
|
|
442
|
-
return; // only on deletion
|
|
443
|
-
if (!hasSelection)
|
|
444
|
-
return;
|
|
445
|
-
const selectedValue = multiple
|
|
446
|
-
? value === null || value === void 0 ? void 0 : value[0]
|
|
447
|
-
: value;
|
|
448
|
-
if (!selectedValue)
|
|
449
|
-
return;
|
|
450
|
-
const idx = findNavigableIndexForValue(renderable, equals, selectedValue);
|
|
451
|
-
if (idx != null)
|
|
452
|
-
setActiveIndex(idx);
|
|
453
|
-
}, [
|
|
454
|
-
inputValue,
|
|
455
|
-
renderable,
|
|
456
|
-
equals,
|
|
457
|
-
value,
|
|
458
|
-
open,
|
|
459
|
-
hasSelection,
|
|
460
|
-
multiple,
|
|
461
|
-
setActiveIndex,
|
|
462
|
-
]);
|
|
463
|
-
React.useEffect(() => {
|
|
464
|
-
if (!open)
|
|
465
|
-
return;
|
|
466
|
-
// When opening the menu, initialize the highlight consistently:
|
|
467
|
-
// - If there is a current selection, highlight that option
|
|
468
|
-
// - Otherwise, leave the highlight unset (null)
|
|
469
|
-
const selectedValue = multiple
|
|
470
|
-
? value === null || value === void 0 ? void 0 : value[0]
|
|
471
|
-
: value;
|
|
472
|
-
if (selectedValue) {
|
|
473
|
-
const selectedNavigableIndex = findNavigableIndexForValue(renderable, equals, selectedValue);
|
|
474
|
-
if (selectedNavigableIndex != null) {
|
|
475
|
-
setActiveIndex(selectedNavigableIndex);
|
|
476
|
-
return;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
setActiveIndex(null);
|
|
480
|
-
}, [open, multiple, value, renderable, equals, setActiveIndex]);
|
|
481
479
|
const onSelection = React.useCallback((option) => {
|
|
480
|
+
var _a;
|
|
482
481
|
selectOption(option);
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
if (refs.domReference.current instanceof HTMLElement) {
|
|
486
|
-
refs.domReference.current.focus();
|
|
482
|
+
if (!multiple) {
|
|
483
|
+
setOpen(false);
|
|
487
484
|
}
|
|
488
|
-
|
|
485
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
486
|
+
}, [selectOption, setOpen, multiple, inputRef]);
|
|
489
487
|
const onAction = React.useCallback((action) => {
|
|
488
|
+
var _a;
|
|
490
489
|
action.run();
|
|
491
490
|
setActiveIndex(null);
|
|
492
491
|
if (action.closeOnRun !== false)
|
|
493
492
|
setOpen(false);
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
}
|
|
497
|
-
}, [setOpen, setActiveIndex]);
|
|
493
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
494
|
+
}, [setOpen, setActiveIndex, inputRef]);
|
|
498
495
|
/**
|
|
499
496
|
* Handler for mousedown on interactive menu items (options/actions)
|
|
500
497
|
* Prevents default to avoid blur and sets flag for focus management
|
|
501
498
|
*/
|
|
502
499
|
const onInteractionPointerDown = React.useMemo(() => createInteractionPointerDownHandler(isHandlingMenuInteractionRef), []);
|
|
500
|
+
function applyFreeFormValue(freeFormCreated) {
|
|
501
|
+
var _a;
|
|
502
|
+
const nextValue = multiple
|
|
503
|
+
? [...((_a = value) !== null && _a !== void 0 ? _a : EMPTY_SELECTED_VALUES), freeFormCreated]
|
|
504
|
+
: freeFormCreated;
|
|
505
|
+
props.onChange(nextValue);
|
|
506
|
+
if (multiple) {
|
|
507
|
+
lastInputWasUser.current = false;
|
|
508
|
+
onInputChange === null || onInputChange === void 0 ? void 0 : onInputChange("");
|
|
509
|
+
}
|
|
510
|
+
}
|
|
503
511
|
function commitFromInputText(inputText) {
|
|
504
512
|
var _a;
|
|
505
513
|
if (inputText.length === 0)
|
|
@@ -515,15 +523,20 @@ function useAutocomplete(props) {
|
|
|
515
523
|
const freeFormCreated = (_a = props.createFreeFormValue) === null || _a === void 0 ? void 0 : _a.call(props, inputText);
|
|
516
524
|
if (!freeFormCreated)
|
|
517
525
|
return false;
|
|
518
|
-
|
|
526
|
+
applyFreeFormValue(freeFormCreated);
|
|
519
527
|
return true;
|
|
520
528
|
}
|
|
521
529
|
const tryRestoreInputToSelectedLabel = React.useCallback(() => {
|
|
522
530
|
if (props.allowFreeForm === true)
|
|
523
531
|
return;
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
532
|
+
if (multiple) {
|
|
533
|
+
if (inputValue.trim().length > 0) {
|
|
534
|
+
lastInputWasUser.current = false;
|
|
535
|
+
onInputChange === null || onInputChange === void 0 ? void 0 : onInputChange("");
|
|
536
|
+
}
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
const selectedValue = value;
|
|
527
540
|
if (!selectedValue)
|
|
528
541
|
return;
|
|
529
542
|
const selectedLabel = getOptionLabel(selectedValue);
|
|
@@ -643,8 +656,40 @@ function useAutocomplete(props) {
|
|
|
643
656
|
});
|
|
644
657
|
}
|
|
645
658
|
}
|
|
659
|
+
const removeLastSelection = React.useCallback(() => {
|
|
660
|
+
var _a;
|
|
661
|
+
if (!multiple || readOnly)
|
|
662
|
+
return;
|
|
663
|
+
const current = (_a = value) !== null && _a !== void 0 ? _a : EMPTY_SELECTED_VALUES;
|
|
664
|
+
if (current.length > 0) {
|
|
665
|
+
const next = current.slice(0, -1);
|
|
666
|
+
onChange((next.length === 0 ? EMPTY_SELECTED_VALUES : next));
|
|
667
|
+
}
|
|
668
|
+
}, [multiple, readOnly, value, onChange]);
|
|
669
|
+
const removeSelection = React.useCallback((option) => {
|
|
670
|
+
var _a;
|
|
671
|
+
if (readOnly)
|
|
672
|
+
return;
|
|
673
|
+
if (multiple) {
|
|
674
|
+
const current = (_a = value) !== null && _a !== void 0 ? _a : EMPTY_SELECTED_VALUES;
|
|
675
|
+
const next = current.filter(v => !equals(v, option));
|
|
676
|
+
onChange((next.length === 0
|
|
677
|
+
? EMPTY_SELECTED_VALUES
|
|
678
|
+
: next));
|
|
679
|
+
}
|
|
680
|
+
else {
|
|
681
|
+
const current = value;
|
|
682
|
+
if (current && equals(current, option)) {
|
|
683
|
+
onChange(undefined);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}, [readOnly, multiple, value, equals, onChange]);
|
|
646
687
|
const onInputKeyDown = React.useCallback((event) => {
|
|
647
688
|
const key = event.key;
|
|
689
|
+
if (key === "Backspace" && multiple && inputValue === "") {
|
|
690
|
+
removeLastSelection();
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
648
693
|
if (key !== "ArrowDown" && key !== "ArrowUp" && key !== "Enter")
|
|
649
694
|
return;
|
|
650
695
|
if (key === "ArrowDown" || key === "ArrowUp") {
|
|
@@ -652,20 +697,27 @@ function useAutocomplete(props) {
|
|
|
652
697
|
return;
|
|
653
698
|
}
|
|
654
699
|
handleEnterKey(event);
|
|
655
|
-
}, [open, onSelection, onAction]);
|
|
700
|
+
}, [open, onSelection, onAction, multiple, inputValue, removeLastSelection]);
|
|
656
701
|
const onInputChangeFromUser = React.useCallback((val) => {
|
|
657
702
|
lastInputWasUser.current = true;
|
|
658
|
-
|
|
659
|
-
if (
|
|
703
|
+
const isEmpty = val.trim().length === 0;
|
|
704
|
+
if (isEmpty) {
|
|
705
|
+
setActiveIndex(null);
|
|
706
|
+
if (!multiple && hasSelection) {
|
|
707
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(undefined);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
else if (val.length > inputValue.length) {
|
|
660
711
|
setActiveIndex(null);
|
|
661
712
|
}
|
|
662
|
-
|
|
663
|
-
|
|
713
|
+
else if (val.length < inputValue.length &&
|
|
714
|
+
!multiple &&
|
|
715
|
+
open &&
|
|
716
|
+
hasSelection) {
|
|
717
|
+
reHighlightSelectedItem();
|
|
718
|
+
}
|
|
664
719
|
if (!readOnly) {
|
|
665
|
-
|
|
666
|
-
const mustSelectFromOptions = hasText && !props.allowFreeForm;
|
|
667
|
-
const keepOpenOnEmpty = openOnFocus;
|
|
668
|
-
setOpen(mustSelectFromOptions || keepOpenOnEmpty);
|
|
720
|
+
setOpen((!isEmpty && !props.allowFreeForm) || openOnFocus);
|
|
669
721
|
}
|
|
670
722
|
onInputChange === null || onInputChange === void 0 ? void 0 : onInputChange(val);
|
|
671
723
|
}, [
|
|
@@ -676,7 +728,20 @@ function useAutocomplete(props) {
|
|
|
676
728
|
props.allowFreeForm,
|
|
677
729
|
openOnFocus,
|
|
678
730
|
setOpen,
|
|
731
|
+
multiple,
|
|
732
|
+
hasSelection,
|
|
733
|
+
onChange,
|
|
734
|
+
open,
|
|
679
735
|
]);
|
|
736
|
+
const clearAll = React.useCallback(() => {
|
|
737
|
+
if (multiple) {
|
|
738
|
+
onChange(EMPTY_SELECTED_VALUES);
|
|
739
|
+
}
|
|
740
|
+
else {
|
|
741
|
+
onChange(undefined);
|
|
742
|
+
}
|
|
743
|
+
onInputChangeFromUser("");
|
|
744
|
+
}, [multiple, onChange, onInputChangeFromUser]);
|
|
680
745
|
return {
|
|
681
746
|
// rendering data
|
|
682
747
|
renderable,
|
|
@@ -704,6 +769,8 @@ function useAutocomplete(props) {
|
|
|
704
769
|
onSelection,
|
|
705
770
|
onAction,
|
|
706
771
|
onInteractionPointerDown,
|
|
772
|
+
removeSelection,
|
|
773
|
+
clearAll,
|
|
707
774
|
// input handlers
|
|
708
775
|
onInputChangeFromUser,
|
|
709
776
|
onInputBlur,
|
|
@@ -714,6 +781,83 @@ function useAutocomplete(props) {
|
|
|
714
781
|
};
|
|
715
782
|
}
|
|
716
783
|
|
|
784
|
+
function useChipNavigation({ selectedValues, inputValue, readOnly, removeSelection, onInputKeyDown, onInputBlur, }) {
|
|
785
|
+
const [rawActiveChipIndex, setRawActiveChipIndex] = React.useState(null);
|
|
786
|
+
const [previousInputValue, setPreviousInputValue] = React.useState(inputValue);
|
|
787
|
+
const activeChipIndex = clampActiveIndex(rawActiveChipIndex, selectedValues.length);
|
|
788
|
+
if (previousInputValue !== inputValue) {
|
|
789
|
+
setPreviousInputValue(inputValue);
|
|
790
|
+
if (inputValue !== "" && rawActiveChipIndex !== null) {
|
|
791
|
+
setRawActiveChipIndex(null);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
const handleActiveChipKey = React.useCallback(
|
|
795
|
+
// eslint-disable-next-line max-statements
|
|
796
|
+
(event) => {
|
|
797
|
+
if (activeChipIndex === null)
|
|
798
|
+
return false;
|
|
799
|
+
const { key } = event;
|
|
800
|
+
if (key === "ArrowLeft") {
|
|
801
|
+
event.preventDefault();
|
|
802
|
+
setRawActiveChipIndex(i => Math.max(0, (i !== null && i !== void 0 ? i : 0) - 1));
|
|
803
|
+
return true;
|
|
804
|
+
}
|
|
805
|
+
if (key === "ArrowRight") {
|
|
806
|
+
event.preventDefault();
|
|
807
|
+
setRawActiveChipIndex(activeChipIndex + 1 >= selectedValues.length
|
|
808
|
+
? null
|
|
809
|
+
: activeChipIndex + 1);
|
|
810
|
+
return true;
|
|
811
|
+
}
|
|
812
|
+
if (key === "Backspace" || key === "Delete") {
|
|
813
|
+
event.preventDefault();
|
|
814
|
+
const option = selectedValues[activeChipIndex];
|
|
815
|
+
const newLen = selectedValues.length - 1;
|
|
816
|
+
if (newLen === 0) {
|
|
817
|
+
setRawActiveChipIndex(null);
|
|
818
|
+
}
|
|
819
|
+
else if (activeChipIndex >= newLen) {
|
|
820
|
+
setRawActiveChipIndex(newLen - 1);
|
|
821
|
+
}
|
|
822
|
+
removeSelection(option);
|
|
823
|
+
return true;
|
|
824
|
+
}
|
|
825
|
+
if (key === "Escape") {
|
|
826
|
+
setRawActiveChipIndex(null);
|
|
827
|
+
return true;
|
|
828
|
+
}
|
|
829
|
+
setRawActiveChipIndex(null);
|
|
830
|
+
return false;
|
|
831
|
+
}, [activeChipIndex, selectedValues, removeSelection]);
|
|
832
|
+
const onKeyDown = React.useCallback((event) => {
|
|
833
|
+
if (handleActiveChipKey(event))
|
|
834
|
+
return;
|
|
835
|
+
if (event.key === "ArrowLeft" &&
|
|
836
|
+
!readOnly &&
|
|
837
|
+
inputValue === "" &&
|
|
838
|
+
selectedValues.length > 0) {
|
|
839
|
+
event.preventDefault();
|
|
840
|
+
setRawActiveChipIndex(selectedValues.length - 1);
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
onInputKeyDown(event);
|
|
844
|
+
}, [handleActiveChipKey, selectedValues, inputValue, readOnly, onInputKeyDown]);
|
|
845
|
+
const onBlur = React.useCallback((event) => {
|
|
846
|
+
setRawActiveChipIndex(null);
|
|
847
|
+
onInputBlur(event);
|
|
848
|
+
}, [onInputBlur]);
|
|
849
|
+
return { activeChipIndex, onKeyDown, onBlur };
|
|
850
|
+
}
|
|
851
|
+
function clampActiveIndex(index, length) {
|
|
852
|
+
if (index === null)
|
|
853
|
+
return null;
|
|
854
|
+
if (length === 0)
|
|
855
|
+
return null;
|
|
856
|
+
if (index >= length)
|
|
857
|
+
return length - 1;
|
|
858
|
+
return index;
|
|
859
|
+
}
|
|
860
|
+
|
|
717
861
|
function MenuList({ items, activeIndex, indexOffset = 0, getItemProps, listRef, listboxId, customRenderOption, customRenderSection, customRenderAction, getOptionLabel, onSelect, onAction, onInteractionPointerDown, isOptionSelected, slotOverrides, }) {
|
|
718
862
|
let navigableIndex = -1;
|
|
719
863
|
function renderItemNode(item) {
|
|
@@ -797,7 +941,8 @@ function handleOptionRendering({ option, activeIndex, navigableIndex, getItemPro
|
|
|
797
941
|
}
|
|
798
942
|
function DefaultOptionContent({ isSelected, text, }) {
|
|
799
943
|
return (React.createElement("div", { className: styles$1.defaultOptionContent },
|
|
800
|
-
React.createElement("div", { className: styles$1.icon
|
|
944
|
+
React.createElement("div", { className: styles$1.icon, style: isSelected ? undefined : { visibility: "hidden" } },
|
|
945
|
+
React.createElement(Icon.Icon, { name: "checkmark", size: "small" })),
|
|
801
946
|
React.createElement(Text.Text, null, text)));
|
|
802
947
|
}
|
|
803
948
|
function handleActionRendering({ action, activeIndex, navigableIndex, getItemProps, listRef, listboxId, customRenderAction, onAction, onInteractionPointerDown, indexOffset = 0, actionClassName, actionStyle, origin, }) {
|
|
@@ -943,12 +1088,50 @@ function DefaultTextPersistentContent({ persistent, }) {
|
|
|
943
1088
|
return React.createElement("div", { className: styles$1.textPersistent }, persistent.label);
|
|
944
1089
|
}
|
|
945
1090
|
|
|
1091
|
+
function FloatingMenu({ context, getFloatingProps, refs, listboxId, className, floatingStyles, transitionStyles, menuWidth, menuStyle, renderable, persistentsHeaders, persistentsFooters, activeIndex, headerInteractiveCount, middleNavigableCount, getItemProps, listRef, onSelection, onAction, onInteractionPointerDown, getOptionLabel, isOptionSelected, loading, showEmptyStateMessage, emptyStateMessage, customRenderLoading, customRenderOption, customRenderSection, customRenderAction, customRenderHeader, customRenderFooter, slotOverrides, }) {
|
|
1092
|
+
var _a, _b, _c, _d;
|
|
1093
|
+
const activeIndexForMiddle = activeIndex != null ? activeIndex - headerInteractiveCount : null;
|
|
1094
|
+
return (React.createElement(floatingUi_react.FloatingPortal, null,
|
|
1095
|
+
React.createElement(floatingUi_react.FloatingFocusManager, { context: context, modal: false, initialFocus: -1, closeOnFocusOut: true, returnFocus: false },
|
|
1096
|
+
React.createElement("div", Object.assign({}, getFloatingProps({
|
|
1097
|
+
ref(node) {
|
|
1098
|
+
if (node)
|
|
1099
|
+
refs.setFloating(node);
|
|
1100
|
+
},
|
|
1101
|
+
id: listboxId,
|
|
1102
|
+
role: "listbox",
|
|
1103
|
+
className,
|
|
1104
|
+
style: Object.assign(Object.assign(Object.assign(Object.assign({}, floatingStyles), transitionStyles), menuStyle), (menuWidth ? { width: menuWidth, maxWidth: menuWidth } : {})),
|
|
1105
|
+
})),
|
|
1106
|
+
React.createElement(PersistentRegion, { items: persistentsHeaders, position: "header", activeIndex: activeIndex, indexOffset: 0, listboxId: listboxId, getItemProps: getItemProps, listRef: listRef, customRenderHeader: customRenderHeader, customRenderFooter: customRenderFooter, onAction: onAction, onInteractionPointerDown: onInteractionPointerDown, className: classnames(styles$1.persistentHeader, (_a = slotOverrides === null || slotOverrides === void 0 ? void 0 : slotOverrides.header) === null || _a === void 0 ? void 0 : _a.className), style: (_b = slotOverrides === null || slotOverrides === void 0 ? void 0 : slotOverrides.header) === null || _b === void 0 ? void 0 : _b.style }),
|
|
1107
|
+
React.createElement("div", { className: styles$1.scrollRegion }, loading ? (customRenderLoading !== null && customRenderLoading !== void 0 ? customRenderLoading : React.createElement(LoadingContent, null)) : (React.createElement(React.Fragment, null,
|
|
1108
|
+
showEmptyStateMessage && (React.createElement(EmptyStateMessage, { emptyState: emptyStateMessage })),
|
|
1109
|
+
renderable.length > 0 && (React.createElement(MenuList, { items: renderable, activeIndex: activeIndexForMiddle, indexOffset: headerInteractiveCount, listboxId: listboxId, getItemProps: getItemProps, listRef: listRef, customRenderOption: customRenderOption, customRenderSection: customRenderSection, customRenderAction: customRenderAction, getOptionLabel: getOptionLabel, onSelect: onSelection, onAction: onAction, onInteractionPointerDown: onInteractionPointerDown, isOptionSelected: isOptionSelected, slotOverrides: slotOverrides }))))),
|
|
1110
|
+
React.createElement(PersistentRegion, { items: persistentsFooters, position: "footer", activeIndex: activeIndex, indexOffset: headerInteractiveCount + middleNavigableCount, listboxId: listboxId, getItemProps: getItemProps, listRef: listRef, customRenderHeader: customRenderHeader, customRenderFooter: customRenderFooter, onAction: onAction, onInteractionPointerDown: onInteractionPointerDown, className: classnames(styles$1.persistentFooter, (_c = slotOverrides === null || slotOverrides === void 0 ? void 0 : slotOverrides.footer) === null || _c === void 0 ? void 0 : _c.className), style: (_d = slotOverrides === null || slotOverrides === void 0 ? void 0 : slotOverrides.footer) === null || _d === void 0 ? void 0 : _d.style })))));
|
|
1111
|
+
}
|
|
1112
|
+
function LoadingContent() {
|
|
1113
|
+
return (React.createElement("div", { className: styles$1.loadingList, onPointerDown: preventDefaultPointerDown },
|
|
1114
|
+
React.createElement(Glimmer.Glimmer, { shape: "rectangle", size: "base" }),
|
|
1115
|
+
React.createElement(Glimmer.Glimmer, { shape: "rectangle", size: "base" }),
|
|
1116
|
+
React.createElement(Glimmer.Glimmer, { shape: "rectangle", size: "base" })));
|
|
1117
|
+
}
|
|
1118
|
+
function EmptyStateMessage({ emptyState, }) {
|
|
1119
|
+
const emptyStateDefault = "No options";
|
|
1120
|
+
const emptyStateContent = emptyState !== null && emptyState !== void 0 ? emptyState : emptyStateDefault;
|
|
1121
|
+
return (React.createElement("div", { className: styles$1.emptyStateMessage, onPointerDown: preventDefaultPointerDown }, emptyStateContent));
|
|
1122
|
+
}
|
|
1123
|
+
|
|
946
1124
|
const AutocompleteRebuilt = React.forwardRef(AutocompleteRebuiltInternal);
|
|
947
1125
|
// eslint-disable-next-line max-statements
|
|
948
1126
|
function AutocompleteRebuiltInternal(props, forwardedRef) {
|
|
949
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
1127
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
950
1128
|
const { inputValue, placeholder, disabled, error, invalid, description, size: sizeProp, loading = false, } = props;
|
|
951
|
-
const
|
|
1129
|
+
const formFieldRef = React.useRef(null);
|
|
1130
|
+
// Ref for the multi-select input element, where we cannot use refs.domReference
|
|
1131
|
+
// since it is not attached to the literal input element, whereas single + InputText only offers a
|
|
1132
|
+
// ref to the literal input element.
|
|
1133
|
+
const internalInputRef = React.useRef(null);
|
|
1134
|
+
const { renderable, optionCount, persistentsHeaders, persistentsFooters, headerInteractiveCount, middleNavigableCount, getOptionLabel, isOptionSelected, refs, floatingStyles, context, getReferenceProps, getFloatingProps, getItemProps, activeIndex, open, listRef, onSelection, onAction, onInteractionPointerDown, removeSelection, onInputChangeFromUser, onInputBlur, onInputFocus, onInputKeyDown, setReferenceElement, clearAll, } = useAutocomplete(props, internalInputRef);
|
|
952
1135
|
const listboxId = React.useId();
|
|
953
1136
|
// Provides mount/unmount-aware transition styles for the floating element
|
|
954
1137
|
const { isMounted, styles: transitionStyles } = floatingUi_react.useTransitionStyles(context, {
|
|
@@ -959,90 +1142,172 @@ function AutocompleteRebuiltInternal(props, forwardedRef) {
|
|
|
959
1142
|
});
|
|
960
1143
|
const [menuWidth, setMenuWidth] = React.useState(undefined);
|
|
961
1144
|
const [positionRefEl, setPositionRefEl] = React.useState(null);
|
|
962
|
-
const
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1145
|
+
const inputId = React.useId();
|
|
1146
|
+
const descriptionId = `descriptionUUID--${inputId}`;
|
|
1147
|
+
const selectedValues = props.multiple
|
|
1148
|
+
? ((_a = props.value) !== null && _a !== void 0 ? _a : EMPTY_SELECTED_VALUES)
|
|
1149
|
+
: EMPTY_SELECTED_VALUES;
|
|
1150
|
+
const { activeChipIndex, onKeyDown: chipKeyDown, onBlur: chipBlur, } = useChipNavigation({
|
|
1151
|
+
selectedValues,
|
|
1152
|
+
inputValue,
|
|
1153
|
+
readOnly: props.readOnly,
|
|
1154
|
+
removeSelection,
|
|
1155
|
+
onInputKeyDown,
|
|
1156
|
+
onInputBlur,
|
|
966
1157
|
});
|
|
1158
|
+
const focusInputOnPointerDown = React.useCallback((e) => {
|
|
1159
|
+
var _a;
|
|
1160
|
+
const target = e.target;
|
|
1161
|
+
if (target.closest("input, button"))
|
|
1162
|
+
return;
|
|
1163
|
+
e.preventDefault();
|
|
1164
|
+
(_a = internalInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
1165
|
+
}, []);
|
|
1166
|
+
const composedReferenceProps = getReferenceProps(Object.assign({ onKeyDown: props.multiple ? chipKeyDown : onInputKeyDown, onFocus: onInputFocus, onBlur: props.multiple ? chipBlur : onInputBlur }, (props.multiple ? { onPointerDown: focusInputOnPointerDown } : {})));
|
|
967
1167
|
const dataAttrs = filterDataAttributes.filterDataAttributes(props);
|
|
968
|
-
const
|
|
969
|
-
|
|
1168
|
+
const ariaProps = {
|
|
1169
|
+
role: "combobox",
|
|
1170
|
+
"aria-autocomplete": "list",
|
|
1171
|
+
"aria-expanded": open ? true : false,
|
|
1172
|
+
"aria-controls": listboxId,
|
|
1173
|
+
"aria-activedescendant": activeChipIndex === null && open && activeIndex != null
|
|
970
1174
|
? `${listboxId}-item-${activeIndex}`
|
|
971
|
-
: undefined
|
|
1175
|
+
: undefined,
|
|
1176
|
+
};
|
|
1177
|
+
const inputProps = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ version: 2, value: inputValue, onChange: props.readOnly ? undefined : onInputChangeFromUser }, (props.readOnly ? { onFocus: onInputFocus, onBlur: onInputBlur } : {})), { placeholder,
|
|
1178
|
+
disabled, readOnly: props.readOnly, error: error !== null && error !== void 0 ? error : undefined, name: props.name, invalid, clearable: props.clearable, autoComplete: "off", autoFocus: props.autoFocus, description, size: sizeProp ? sizeProp : undefined, prefix: props.prefix, suffix: props.suffix }), (props.readOnly ? {} : composedReferenceProps)), ariaProps), dataAttrs);
|
|
1179
|
+
const chipAreaRef = React.useCallback((node) => {
|
|
1180
|
+
if (!node)
|
|
1181
|
+
return;
|
|
1182
|
+
// In multiple mode, we use the input as the reference so floating-ui's
|
|
1183
|
+
// useListNavigation treats it as a typeable combobox and allows Space.
|
|
1184
|
+
// Only use chipArea for positioning/sizing.
|
|
1185
|
+
if (!props.multiple) {
|
|
1186
|
+
setReferenceElement(node);
|
|
1187
|
+
}
|
|
1188
|
+
const multiContainer = node.closest("[data-testid='ATL-AutocompleteRebuilt-multiSelectContainer']");
|
|
1189
|
+
if (multiContainer) {
|
|
1190
|
+
setMenuWidth(multiContainer.clientWidth);
|
|
1191
|
+
setPositionRefEl(multiContainer);
|
|
1192
|
+
}
|
|
1193
|
+
}, [setReferenceElement, props.multiple]);
|
|
972
1194
|
const referenceInputRef = React.useCallback((node) => {
|
|
973
1195
|
setReferenceElement(node);
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1196
|
+
if (!props.multiple) {
|
|
1197
|
+
// Workaround to get the width of the visual InputText element, which is not the same as
|
|
1198
|
+
// the literal input reference element.
|
|
1199
|
+
const visualInputTextElement = node === null || node === void 0 ? void 0 : node.closest("[data-testid='Form-Field-Wrapper']");
|
|
1200
|
+
if (visualInputTextElement) {
|
|
1201
|
+
setMenuWidth(visualInputTextElement.clientWidth);
|
|
1202
|
+
setPositionRefEl(visualInputTextElement);
|
|
1203
|
+
}
|
|
980
1204
|
}
|
|
981
|
-
}, [setReferenceElement]);
|
|
1205
|
+
}, [setReferenceElement, props.multiple]);
|
|
982
1206
|
const mergedInputRef = React.useMemo(() => FormField.mergeRefs([
|
|
983
1207
|
referenceInputRef,
|
|
1208
|
+
internalInputRef,
|
|
984
1209
|
forwardedRef,
|
|
985
1210
|
]), [referenceInputRef, forwardedRef]);
|
|
986
1211
|
React.useEffect(() => {
|
|
987
1212
|
if (!positionRefEl)
|
|
988
1213
|
return;
|
|
989
|
-
// Set the reference element to the visual InputText element so the menu aligns with the input.
|
|
990
1214
|
refs.setPositionReference(positionRefEl);
|
|
991
1215
|
}, [positionRefEl, refs]);
|
|
992
|
-
const menuClassName = classnames(styles$1.list, (
|
|
1216
|
+
const menuClassName = classnames(styles$1.list, (_b = props.UNSAFE_className) === null || _b === void 0 ? void 0 : _b.menu);
|
|
993
1217
|
const showEmptyStateMessage = optionCount === 0 && props.emptyStateMessage !== false;
|
|
994
|
-
const
|
|
1218
|
+
const floatingMenu = isMounted && !props.readOnly && !disabled && (React.createElement(FloatingMenu, { context: context, getFloatingProps: getFloatingProps, refs: refs, listboxId: listboxId, className: menuClassName, floatingStyles: floatingStyles, transitionStyles: transitionStyles, menuWidth: menuWidth, menuStyle: (_c = props.UNSAFE_styles) === null || _c === void 0 ? void 0 : _c.menu, renderable: renderable, persistentsHeaders: persistentsHeaders, persistentsFooters: persistentsFooters, activeIndex: activeIndex, headerInteractiveCount: headerInteractiveCount, middleNavigableCount: middleNavigableCount, getItemProps: getItemProps, listRef: listRef, onSelection: onSelection, onAction: onAction, onInteractionPointerDown: onInteractionPointerDown, getOptionLabel: getOptionLabel, isOptionSelected: isOptionSelected, loading: loading, showEmptyStateMessage: showEmptyStateMessage, emptyStateMessage: props.emptyStateMessage, customRenderLoading: props.customRenderLoading, customRenderOption: props.customRenderOption, customRenderSection: props.customRenderSection, customRenderAction: props.customRenderAction, customRenderHeader: props.customRenderHeader, customRenderFooter: props.customRenderFooter, slotOverrides: {
|
|
1219
|
+
option: {
|
|
1220
|
+
className: (_d = props.UNSAFE_className) === null || _d === void 0 ? void 0 : _d.option,
|
|
1221
|
+
style: (_e = props.UNSAFE_styles) === null || _e === void 0 ? void 0 : _e.option,
|
|
1222
|
+
},
|
|
1223
|
+
action: {
|
|
1224
|
+
className: (_f = props.UNSAFE_className) === null || _f === void 0 ? void 0 : _f.action,
|
|
1225
|
+
style: (_g = props.UNSAFE_styles) === null || _g === void 0 ? void 0 : _g.action,
|
|
1226
|
+
},
|
|
1227
|
+
section: {
|
|
1228
|
+
className: (_h = props.UNSAFE_className) === null || _h === void 0 ? void 0 : _h.section,
|
|
1229
|
+
style: (_j = props.UNSAFE_styles) === null || _j === void 0 ? void 0 : _j.section,
|
|
1230
|
+
},
|
|
1231
|
+
header: {
|
|
1232
|
+
className: (_k = props.UNSAFE_className) === null || _k === void 0 ? void 0 : _k.header,
|
|
1233
|
+
style: (_l = props.UNSAFE_styles) === null || _l === void 0 ? void 0 : _l.header,
|
|
1234
|
+
},
|
|
1235
|
+
footer: {
|
|
1236
|
+
className: (_m = props.UNSAFE_className) === null || _m === void 0 ? void 0 : _m.footer,
|
|
1237
|
+
style: (_o = props.UNSAFE_styles) === null || _o === void 0 ? void 0 : _o.footer,
|
|
1238
|
+
},
|
|
1239
|
+
} }));
|
|
1240
|
+
if (props.customRenderInput) {
|
|
1241
|
+
return (React.createElement("div", { "data-testid": "ATL-AutocompleteRebuilt" },
|
|
1242
|
+
props.customRenderInput({ inputRef: mergedInputRef, inputProps }),
|
|
1243
|
+
floatingMenu));
|
|
1244
|
+
}
|
|
1245
|
+
if (props.multiple) {
|
|
1246
|
+
return (React.createElement(MultipleSelectionLayout, { selectedValues: selectedValues, inputValue: inputValue, disabled: disabled, error: error, invalid: invalid, sizeProp: sizeProp, inputId: inputId, descriptionId: descriptionId, description: description, placeholder: placeholder, clearable: props.clearable, prefix: props.prefix, suffix: props.suffix, readOnly: props.readOnly, name: props.name, autoFocus: props.autoFocus, formFieldRef: formFieldRef, chipAreaRef: chipAreaRef, mergedInputRef: mergedInputRef, internalInputRef: internalInputRef, activeChipIndex: activeChipIndex, getOptionLabel: getOptionLabel, customRenderValue: props.customRenderValue, removeSelection: removeSelection, clearAll: clearAll, onInputChangeFromUser: onInputChangeFromUser, chipAreaEventProps: props.readOnly
|
|
1247
|
+
? { onFocus: onInputFocus, onBlur: chipBlur }
|
|
1248
|
+
: composedReferenceProps, inputAriaProps: ariaProps, inputDataAttrs: dataAttrs, limitVisibleSelections: props.limitVisibleSelections, limitSelectionText: props.limitSelectionText, unsafeClassName: (_p = props.UNSAFE_className) === null || _p === void 0 ? void 0 : _p.selection, unsafeStyle: (_q = props.UNSAFE_styles) === null || _q === void 0 ? void 0 : _q.selection }, floatingMenu));
|
|
1249
|
+
}
|
|
995
1250
|
return (React.createElement("div", { "data-testid": "ATL-AutocompleteRebuilt" },
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
React.createElement(floatingUi_react.FloatingFocusManager, { context: context, modal: false, initialFocus: -1, closeOnFocusOut: true, returnFocus: false },
|
|
999
|
-
React.createElement("div", Object.assign({}, getFloatingProps({
|
|
1000
|
-
ref(node) {
|
|
1001
|
-
if (node)
|
|
1002
|
-
refs.setFloating(node);
|
|
1003
|
-
},
|
|
1004
|
-
id: listboxId,
|
|
1005
|
-
role: "listbox",
|
|
1006
|
-
className: menuClassName,
|
|
1007
|
-
style: Object.assign(Object.assign(Object.assign(Object.assign({}, floatingStyles), transitionStyles), (_b = props.UNSAFE_styles) === null || _b === void 0 ? void 0 : _b.menu), (menuWidth
|
|
1008
|
-
? { width: menuWidth, maxWidth: menuWidth }
|
|
1009
|
-
: {})),
|
|
1010
|
-
})),
|
|
1011
|
-
React.createElement(PersistentRegion, { items: persistentsHeaders, position: "header", activeIndex: activeIndex, indexOffset: 0, listboxId: listboxId, getItemProps: getItemProps, listRef: listRef, customRenderHeader: props.customRenderHeader, customRenderFooter: props.customRenderFooter, onAction: onAction, onInteractionPointerDown: onInteractionPointerDown, className: classnames(styles$1.persistentHeader, (_c = props.UNSAFE_className) === null || _c === void 0 ? void 0 : _c.header), style: (_d = props.UNSAFE_styles) === null || _d === void 0 ? void 0 : _d.header }),
|
|
1012
|
-
React.createElement("div", { className: styles$1.scrollRegion }, loading ? ((_e = props.customRenderLoading) !== null && _e !== void 0 ? _e : React.createElement(LoadingContent, null)) : (React.createElement(React.Fragment, null,
|
|
1013
|
-
showEmptyStateMessage && (React.createElement(EmptyStateMessage, { emptyState: props.emptyStateMessage })),
|
|
1014
|
-
renderable.length > 0 && (React.createElement(MenuList, { items: renderable, activeIndex: activeIndexForMiddle, indexOffset: headerInteractiveCount, listboxId: listboxId, getItemProps: getItemProps, listRef: listRef, customRenderOption: props.customRenderOption, customRenderSection: props.customRenderSection, customRenderAction: props.customRenderAction, getOptionLabel: getOptionLabel, onSelect: onSelection, onAction: onAction, onInteractionPointerDown: onInteractionPointerDown, isOptionSelected: isOptionSelected, slotOverrides: {
|
|
1015
|
-
option: {
|
|
1016
|
-
className: (_f = props.UNSAFE_className) === null || _f === void 0 ? void 0 : _f.option,
|
|
1017
|
-
style: (_g = props.UNSAFE_styles) === null || _g === void 0 ? void 0 : _g.option,
|
|
1018
|
-
},
|
|
1019
|
-
action: {
|
|
1020
|
-
className: (_h = props.UNSAFE_className) === null || _h === void 0 ? void 0 : _h.action,
|
|
1021
|
-
style: (_j = props.UNSAFE_styles) === null || _j === void 0 ? void 0 : _j.action,
|
|
1022
|
-
},
|
|
1023
|
-
section: {
|
|
1024
|
-
className: (_k = props.UNSAFE_className) === null || _k === void 0 ? void 0 : _k.section,
|
|
1025
|
-
style: (_l = props.UNSAFE_styles) === null || _l === void 0 ? void 0 : _l.section,
|
|
1026
|
-
},
|
|
1027
|
-
} }))))),
|
|
1028
|
-
React.createElement(PersistentRegion, { items: persistentsFooters, position: "footer", activeIndex: activeIndex, indexOffset: headerInteractiveCount + middleNavigableCount, listboxId: listboxId, getItemProps: getItemProps, listRef: listRef, customRenderHeader: props.customRenderHeader, customRenderFooter: props.customRenderFooter, onAction: onAction, onInteractionPointerDown: onInteractionPointerDown, className: classnames(styles$1.persistentFooter, (_m = props.UNSAFE_className) === null || _m === void 0 ? void 0 : _m.footer), style: (_o = props.UNSAFE_styles) === null || _o === void 0 ? void 0 : _o.footer })))))));
|
|
1251
|
+
React.createElement(InputText_index.InputText, Object.assign({ ref: mergedInputRef }, inputProps)),
|
|
1252
|
+
floatingMenu));
|
|
1029
1253
|
}
|
|
1030
|
-
function
|
|
1031
|
-
return (React.createElement("
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1254
|
+
function SelectionChip({ value, active, disabled, getOptionLabel, customRenderValue, canDismiss, onDismiss, unsafeClassName, unsafeStyle, }) {
|
|
1255
|
+
return (React.createElement("span", { "data-selection-disabled": disabled, className: classnames(styles$1.selectionChip, {
|
|
1256
|
+
[styles$1.selectionChipActive]: active,
|
|
1257
|
+
[styles$1.selectionChipDisabled]: disabled,
|
|
1258
|
+
}, unsafeClassName), style: unsafeStyle, "data-testid": "ATL-AutocompleteRebuilt-chip", onPointerDown: preventDefaultPointerDown },
|
|
1259
|
+
customRenderValue ? (customRenderValue({ value, getOptionLabel })) : (React.createElement(Typography.Typography, { size: "small" }, getOptionLabel(value))),
|
|
1260
|
+
canDismiss && (React.createElement("button", { type: "button", disabled: disabled, className: styles$1.chipDismiss, onClick: onDismiss, onPointerDown: preventDefaultPointerDown, "aria-label": `Remove ${getOptionLabel(value)}`, tabIndex: -1 },
|
|
1261
|
+
React.createElement(Icon.Icon, { size: "small", name: "remove" })))));
|
|
1035
1262
|
}
|
|
1036
|
-
function
|
|
1037
|
-
const
|
|
1038
|
-
|
|
1039
|
-
|
|
1263
|
+
function SelectionChipsList({ selectedValues, isFocused, limitVisibleSelections = 6, limitSelectionText = (count) => `+${count}`, activeChipIndex, disabled, getOptionLabel, customRenderValue, canDismiss, removeSelection, unsafeClassName, unsafeStyle, }) {
|
|
1264
|
+
const shouldLimit = !isFocused &&
|
|
1265
|
+
limitVisibleSelections !== -1 &&
|
|
1266
|
+
selectedValues.length > limitVisibleSelections;
|
|
1267
|
+
const visibleValues = shouldLimit
|
|
1268
|
+
? selectedValues.slice(0, limitVisibleSelections)
|
|
1269
|
+
: selectedValues;
|
|
1270
|
+
const hiddenCount = shouldLimit
|
|
1271
|
+
? selectedValues.length - limitVisibleSelections
|
|
1272
|
+
: 0;
|
|
1273
|
+
return (React.createElement(React.Fragment, null,
|
|
1274
|
+
visibleValues.map((v, i) => {
|
|
1275
|
+
var _a;
|
|
1276
|
+
return (React.createElement(SelectionChip, { key: (_a = v.key) !== null && _a !== void 0 ? _a : getOptionLabel(v), value: v, active: activeChipIndex === i, disabled: disabled, getOptionLabel: getOptionLabel, customRenderValue: customRenderValue, canDismiss: canDismiss, onDismiss: () => removeSelection(v), unsafeClassName: unsafeClassName, unsafeStyle: unsafeStyle }));
|
|
1277
|
+
}),
|
|
1278
|
+
hiddenCount > 0 && (React.createElement("span", { className: styles$1.limitText, "data-testid": "ATL-AutocompleteRebuilt-limitText" },
|
|
1279
|
+
React.createElement(Typography.Typography, { size: "small", element: "span" }, limitSelectionText(hiddenCount))))));
|
|
1280
|
+
}
|
|
1281
|
+
function MultipleSelectionLayout({ selectedValues, inputValue, disabled, error, invalid, sizeProp, inputId, descriptionId, description, placeholder, clearable, prefix, suffix, readOnly, name, autoFocus, formFieldRef, chipAreaRef, mergedInputRef, internalInputRef, activeChipIndex, getOptionLabel, customRenderValue, removeSelection, clearAll, onInputChangeFromUser, chipAreaEventProps, inputAriaProps, inputDataAttrs, limitVisibleSelections, limitSelectionText, unsafeClassName, unsafeStyle, children, }) {
|
|
1282
|
+
var _a;
|
|
1283
|
+
const [isFocused, setIsFocused] = React.useState(false);
|
|
1284
|
+
const hasValue = selectedValues.length > 0 || inputValue;
|
|
1285
|
+
const canDismissChip = !disabled && !readOnly;
|
|
1286
|
+
const handleFocusIn = React.useCallback(() => {
|
|
1287
|
+
setIsFocused(true);
|
|
1288
|
+
}, []);
|
|
1289
|
+
const handleFocusOut = React.useCallback((e) => {
|
|
1290
|
+
if (!e.currentTarget.contains(e.relatedTarget)) {
|
|
1291
|
+
setIsFocused(false);
|
|
1292
|
+
}
|
|
1293
|
+
}, []);
|
|
1294
|
+
return (React.createElement("div", { "data-testid": "ATL-AutocompleteRebuilt", onFocus: handleFocusIn, onBlur: handleFocusOut },
|
|
1295
|
+
React.createElement("div", { className: styles$1.multiSelectField, "data-testid": "ATL-AutocompleteRebuilt-multiSelectContainer" },
|
|
1296
|
+
React.createElement(FormField.FormFieldWrapper, { disabled: disabled, size: sizeProp ? sizeProp : undefined, error: (_a = error) !== null && _a !== void 0 ? _a : "", invalid: Boolean(error || invalid), identifier: inputId, descriptionIdentifier: descriptionId, description: description, clearable: clearable !== null && clearable !== void 0 ? clearable : "never", onClear: () => {
|
|
1297
|
+
var _a;
|
|
1298
|
+
clearAll();
|
|
1299
|
+
(_a = internalInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
1300
|
+
}, type: "text", placeholder: placeholder, value: hasValue ? "has-value" : "", prefix: prefix, suffix: suffix, wrapperRef: formFieldRef },
|
|
1301
|
+
React.createElement("div", Object.assign({ ref: chipAreaRef, className: styles$1.chipArea, "data-testid": "ATL-AutocompleteRebuilt-chipArea" }, chipAreaEventProps),
|
|
1302
|
+
React.createElement(SelectionChipsList, { selectedValues: selectedValues, isFocused: isFocused, limitVisibleSelections: limitVisibleSelections, limitSelectionText: limitSelectionText, activeChipIndex: activeChipIndex, disabled: disabled, getOptionLabel: getOptionLabel, customRenderValue: customRenderValue, canDismiss: canDismissChip, removeSelection: removeSelection, unsafeClassName: unsafeClassName, unsafeStyle: unsafeStyle }),
|
|
1303
|
+
React.createElement("input", Object.assign({ ref: mergedInputRef, className: styles$1.inlineInput, id: inputId, value: inputValue, onChange: readOnly
|
|
1304
|
+
? undefined
|
|
1305
|
+
: e => onInputChangeFromUser(e.target.value), disabled: disabled, readOnly: readOnly, name: name, autoComplete: "off", autoFocus: autoFocus }, inputAriaProps, inputDataAttrs))))),
|
|
1306
|
+
children));
|
|
1040
1307
|
}
|
|
1041
1308
|
|
|
1042
1309
|
var styles = {"autocomplete":"_7mObJiwfPh4-","options":"dL5JShAJlKM-","heading":"PWZL-94hH7k-","visible":"_2RzcnTdaPyc-","option":"y9zhi8Wr8QA-","active":"_3Xg49dtL1Q8-","separator":"LIeh390F3W8-","icon":"K2phy6IC3TY-","text":"a6-LbUm5WnY-","label":"tQNbuxcE9nU-","details":"qacStG9-XbE-","spinning":"P9cQDL4MZ-s-"};
|
|
1043
1310
|
|
|
1044
|
-
const AUTOCOMPLETE_MAX_HEIGHT = 300;
|
|
1045
|
-
|
|
1046
1311
|
function useRepositionMenu(attachTo, visible, cssManagedVisibility) {
|
|
1047
1312
|
const { refs, floatingStyles, update } = floatingUi_react.useFloating(Object.assign({ placement: "bottom", middleware: [
|
|
1048
1313
|
floatingUi_react.offset(8),
|
|
@@ -1050,7 +1315,7 @@ function useRepositionMenu(attachTo, visible, cssManagedVisibility) {
|
|
|
1050
1315
|
floatingUi_react.size({
|
|
1051
1316
|
apply({ availableHeight, elements }) {
|
|
1052
1317
|
const maxHeight$1 = maxHeight.calculateMaxHeight(availableHeight, {
|
|
1053
|
-
maxHeight: AUTOCOMPLETE_MAX_HEIGHT,
|
|
1318
|
+
maxHeight: AUTOCOMPLETE_MAX_HEIGHT$1,
|
|
1054
1319
|
});
|
|
1055
1320
|
Object.assign(elements.floating.style, {
|
|
1056
1321
|
maxHeight: `${maxHeight$1}px`,
|