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