@jobber/components 8.4.0 → 8.6.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.d.ts +6 -4
- package/dist/Autocomplete/Autocomplete.types.d.ts +3 -137
- package/dist/Autocomplete/components/FloatingMenu.d.ts +6 -6
- package/dist/Autocomplete/components/MenuList.d.ts +4 -4
- package/dist/Autocomplete/components/PersistentRegion.d.ts +3 -3
- package/dist/Autocomplete/index.cjs +25 -1658
- package/dist/Autocomplete/index.d.ts +3 -30
- package/dist/Autocomplete/index.mjs +25 -1647
- package/dist/Autocomplete/tests/Autocomplete.setup.d.ts +15 -15
- package/dist/Autocomplete/useAutocomplete.d.ts +2 -2
- package/dist/Autocomplete-cjs.js +1306 -0
- package/dist/Autocomplete-es.js +1304 -0
- package/dist/FormField/FormFieldTypes.d.ts +0 -7
- package/dist/InputDate/InputDate.types.d.ts +1 -1
- package/dist/InputEmail/InputEmail.types.d.ts +1 -1
- package/dist/InputNumber/InputNumber.types.d.ts +1 -1
- package/dist/InputPhoneNumber/InputPhoneNumber.types.d.ts +1 -1
- package/dist/InputText/InputText.types.d.ts +1 -5
- package/dist/InputTime/InputTime.types.d.ts +1 -1
- package/dist/Menu-es.js +1 -1
- package/dist/MenuSubmenuTrigger-es.js +1 -1
- package/dist/Select/OptionGroup.d.ts +2 -2
- package/dist/Select/Select.types.d.ts +1 -1
- package/dist/docs/Autocomplete/{AutocompleteV2.md → Autocomplete.md} +13 -107
- package/dist/docs/Banner/Banner.md +3 -3
- package/dist/docs/Box/Box.md +2 -2
- package/dist/docs/Chips/Chips.md +1 -1
- package/dist/docs/DataList/DataList.md +1 -1
- package/dist/docs/FormField/FormField.md +0 -1
- package/dist/docs/Icon/Icon.md +1 -1
- package/dist/docs/InputGroup/InputGroup.md +1 -1
- package/dist/docs/InputPassword/InputPassword.md +0 -1
- package/dist/docs/Menu/Menu.md +1 -1
- package/dist/docs/Modal/Modal.md +3 -3
- package/dist/docs/MultiSelect/MultiSelect.md +1 -1
- package/dist/docs/index.md +1 -2
- package/dist/docs/usage-guidelines/usage-guidelines.md +13 -44
- package/dist/floating-ui.react-dom-es.js +1 -1
- package/dist/floating-ui.react-es.js +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.esm-es.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/sharedHelpers/types.d.ts +0 -4
- package/dist/styles.css +29 -153
- package/package.json +2 -2
- package/dist/Autocomplete/Autocomplete.rebuilt.d.ts +0 -8
- package/dist/Autocomplete/Autocomplete.utils.d.ts +0 -8
- package/dist/Autocomplete/Menu/DefaultMenu.d.ts +0 -16
- package/dist/Autocomplete/Menu/Menu.d.ts +0 -3
- package/dist/Autocomplete/Menu/MenuWrapper.d.ts +0 -23
- package/dist/Autocomplete/Option.d.ts +0 -78
- package/dist/Autocomplete/V1.docgen.d.ts +0 -4
- package/dist/Autocomplete/V2.docgen.d.ts +0 -6
- package/dist/Autocomplete/useKeyboardNavigation.d.ts +0 -35
- package/dist/Autocomplete/useRepositionMenu.d.ts +0 -10
- package/dist/docs/Autocomplete/AutocompleteV1.md +0 -1328
|
@@ -1,1644 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import { tokens } from '@jobber/design';
|
|
5
|
-
import { useCallbackRef, useDebounce, useSafeLayoutEffect, useIsMounted, useOnKeyDown } from '@jobber/hooks';
|
|
6
|
-
import { c as calculateMaxHeight } from '../maxHeight-es.js';
|
|
7
|
-
import { b as autoUpdate, o as offset, f as flip, d as size } from '../floating-ui.react-dom-es.js';
|
|
8
|
-
import { H as Heading } from '../Heading-es.js';
|
|
9
|
-
import { T as Text } from '../Text-es.js';
|
|
10
|
-
import { T as Typography } from '../Typography-es.js';
|
|
11
|
-
import { I as Icon } from '../Icon-es.js';
|
|
12
|
-
import { G as Glimmer } from '../Glimmer-es.js';
|
|
13
|
-
import { I as InputText } from '../InputText-es.js';
|
|
14
|
-
import { c as FormFieldWrapper } from '../FormFieldWrapper-es.js';
|
|
15
|
-
import { _ as __rest, a as __awaiter } from '../tslib.es6-es.js';
|
|
16
|
-
import 'react-hook-form';
|
|
17
|
-
import { m as mergeRefs } from '../mergeRefs-es.js';
|
|
18
|
-
import '../Button-es.js';
|
|
19
|
-
import { f as filterDataAttributes } from '../filterDataAttributes-es.js';
|
|
1
|
+
export { A as Autocomplete } from '../Autocomplete-es.js';
|
|
2
|
+
import 'react';
|
|
3
|
+
import '../floating-ui.react-es.js';
|
|
20
4
|
import '../floating-ui.utils.dom-es.js';
|
|
21
5
|
import '../index.esm-es.js';
|
|
22
6
|
import 'react/jsx-runtime';
|
|
23
7
|
import 'react-dom';
|
|
8
|
+
import '../floating-ui.react-dom-es.js';
|
|
9
|
+
import 'classnames';
|
|
10
|
+
import '@jobber/design';
|
|
11
|
+
import '@jobber/hooks';
|
|
12
|
+
import '../maxHeight-es.js';
|
|
13
|
+
import '../Heading-es.js';
|
|
14
|
+
import '../Typography-es.js';
|
|
15
|
+
import '../Text-es.js';
|
|
16
|
+
import '../Icon-es.js';
|
|
17
|
+
import '../Glimmer-es.js';
|
|
18
|
+
import '../tslib.es6-es.js';
|
|
24
19
|
import '../Content-es.js';
|
|
25
|
-
import '../
|
|
26
|
-
import '../
|
|
27
|
-
import '../useAtlantisFormFieldName-es.js';
|
|
20
|
+
import '../InputText-es.js';
|
|
21
|
+
import '../FormFieldWrapper-es.js';
|
|
28
22
|
import 'framer-motion';
|
|
23
|
+
import '../Button-es.js';
|
|
24
|
+
import 'react-router-dom';
|
|
29
25
|
import '../useFormFieldFocus-es.js';
|
|
30
26
|
import '../InputValidation-es.js';
|
|
31
|
-
import '
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
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-"};
|
|
38
|
-
|
|
39
|
-
function flattenMenu(menu) {
|
|
40
|
-
const optionItems = [];
|
|
41
|
-
const sections = [];
|
|
42
|
-
const persistentsHeaders = [];
|
|
43
|
-
const persistentsFooters = [];
|
|
44
|
-
menu.forEach(item => {
|
|
45
|
-
if (item.type === "header") {
|
|
46
|
-
persistentsHeaders.push(item);
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
if (item.type === "footer") {
|
|
50
|
-
persistentsFooters.push(item);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
const group = item;
|
|
54
|
-
sections.push(group);
|
|
55
|
-
optionItems.push(...group.options);
|
|
56
|
-
});
|
|
57
|
-
return { optionItems, sections, persistentsHeaders, persistentsFooters };
|
|
58
|
-
}
|
|
59
|
-
function buildItemsForGroup(group, optionsFilter) {
|
|
60
|
-
var _a;
|
|
61
|
-
const isSection = group.type === "section";
|
|
62
|
-
const filtered = optionsFilter ? optionsFilter(group.options) : group.options;
|
|
63
|
-
const actions = (_a = group.actions) !== null && _a !== void 0 ? _a : [];
|
|
64
|
-
const result = [];
|
|
65
|
-
// Only render a section header when that section has at least one option after filtering
|
|
66
|
-
if (isSection && filtered.length > 0) {
|
|
67
|
-
result.push({
|
|
68
|
-
kind: "section",
|
|
69
|
-
section: group,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
if (filtered.length > 0) {
|
|
73
|
-
result.push(...filtered.map(o => ({
|
|
74
|
-
kind: "option",
|
|
75
|
-
value: o,
|
|
76
|
-
})));
|
|
77
|
-
}
|
|
78
|
-
// Only render actions for a group when that group has at least one option after filtering
|
|
79
|
-
if (actions.length > 0 && filtered.length > 0) {
|
|
80
|
-
result.push(...actions.map(action => ({
|
|
81
|
-
kind: "action",
|
|
82
|
-
action,
|
|
83
|
-
origin: "menu",
|
|
84
|
-
})));
|
|
85
|
-
}
|
|
86
|
-
return result;
|
|
87
|
-
}
|
|
88
|
-
function buildRenderableList(sections, optionsFilter) {
|
|
89
|
-
const items = [];
|
|
90
|
-
for (const group of sections) {
|
|
91
|
-
items.push(...buildItemsForGroup(group, optionsFilter));
|
|
92
|
-
}
|
|
93
|
-
return items;
|
|
94
|
-
}
|
|
95
|
-
function getNavigableItemAtIndex(activeIndex, renderable) {
|
|
96
|
-
if (activeIndex == null)
|
|
97
|
-
return null;
|
|
98
|
-
let navigableIndex = -1;
|
|
99
|
-
for (const item of renderable) {
|
|
100
|
-
// Ignore sections
|
|
101
|
-
if (item.kind === "section")
|
|
102
|
-
continue;
|
|
103
|
-
navigableIndex += 1;
|
|
104
|
-
if (navigableIndex === activeIndex)
|
|
105
|
-
return item;
|
|
106
|
-
}
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
function findNavigableIndexForValue(renderable, equals, selectedValue) {
|
|
110
|
-
let navigableIndex = -1;
|
|
111
|
-
for (const item of renderable) {
|
|
112
|
-
// Ignore sections
|
|
113
|
-
if (item.kind === "section")
|
|
114
|
-
continue;
|
|
115
|
-
navigableIndex += 1;
|
|
116
|
-
if (item.kind === "option" && equals(item.value, selectedValue)) {
|
|
117
|
-
return navigableIndex;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return null;
|
|
121
|
-
}
|
|
122
|
-
function invokeActiveItemOnEnter(event, activeIndex, renderable, onSelect, onAction) {
|
|
123
|
-
const activeItem = getNavigableItemAtIndex(activeIndex, renderable);
|
|
124
|
-
if (!activeItem)
|
|
125
|
-
return;
|
|
126
|
-
event.preventDefault();
|
|
127
|
-
if (activeItem.kind === "option") {
|
|
128
|
-
onSelect(activeItem.value);
|
|
129
|
-
}
|
|
130
|
-
else if (activeItem.kind === "action") {
|
|
131
|
-
onAction({
|
|
132
|
-
run: activeItem.action.onClick,
|
|
133
|
-
closeOnRun: activeItem.action.shouldClose,
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const MENU_OFFSET = tokens["space-small"];
|
|
139
|
-
const AUTOCOMPLETE_MAX_HEIGHT = 300;
|
|
140
|
-
function useStableSetReference(refs) {
|
|
141
|
-
const latestRefs = useRef(refs);
|
|
142
|
-
latestRefs.current = refs;
|
|
143
|
-
return useCallback((el) => latestRefs.current.setReference(el), []);
|
|
144
|
-
}
|
|
145
|
-
function useAutocompleteListNav({ navigableCount, shouldResetActiveIndexOnClose, onMenuOpen, onMenuClose, selectedIndex, readOnly = false, disabled = false, outsidePressExcludeSelector, }) {
|
|
146
|
-
const [open, setOpen] = useState(false);
|
|
147
|
-
const [activeIndex, setActiveIndex] = useState(null);
|
|
148
|
-
const listRef = useRef([]);
|
|
149
|
-
const { refs, floatingStyles, context } = useFloating({
|
|
150
|
-
placement: "bottom",
|
|
151
|
-
whileElementsMounted: autoUpdate,
|
|
152
|
-
open,
|
|
153
|
-
onOpenChange: (isOpen, _event, reason) => {
|
|
154
|
-
setOpen(isOpen);
|
|
155
|
-
if (isOpen) {
|
|
156
|
-
onMenuOpen === null || onMenuOpen === void 0 ? void 0 : onMenuOpen();
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
if (shouldResetActiveIndexOnClose === null || shouldResetActiveIndexOnClose === void 0 ? void 0 : shouldResetActiveIndexOnClose()) {
|
|
160
|
-
setActiveIndex(null);
|
|
161
|
-
}
|
|
162
|
-
onMenuClose === null || onMenuClose === void 0 ? void 0 : onMenuClose(String(reason !== null && reason !== void 0 ? reason : ""));
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
middleware: [
|
|
166
|
-
offset(MENU_OFFSET),
|
|
167
|
-
flip({ fallbackPlacements: ["top"] }),
|
|
168
|
-
size({
|
|
169
|
-
apply({ availableHeight, rects, elements }) {
|
|
170
|
-
const maxHeight = calculateMaxHeight(availableHeight, {
|
|
171
|
-
maxHeight: AUTOCOMPLETE_MAX_HEIGHT,
|
|
172
|
-
});
|
|
173
|
-
Object.assign(elements.floating.style, {
|
|
174
|
-
maxHeight: `${maxHeight}px`,
|
|
175
|
-
width: `${rects.reference.width}px`,
|
|
176
|
-
});
|
|
177
|
-
},
|
|
178
|
-
}),
|
|
179
|
-
],
|
|
180
|
-
});
|
|
181
|
-
const enabled = !readOnly && !disabled;
|
|
182
|
-
const click = useClick(context, {
|
|
183
|
-
enabled,
|
|
184
|
-
toggle: false, // Only open, never close on click
|
|
185
|
-
});
|
|
186
|
-
const listNav = useListNavigation(context, {
|
|
187
|
-
listRef,
|
|
188
|
-
activeIndex,
|
|
189
|
-
selectedIndex,
|
|
190
|
-
scrollItemIntoView: {
|
|
191
|
-
behavior: "smooth",
|
|
192
|
-
block: "end",
|
|
193
|
-
},
|
|
194
|
-
loop: true,
|
|
195
|
-
onNavigate: setActiveIndex,
|
|
196
|
-
virtual: true,
|
|
197
|
-
enabled: open,
|
|
198
|
-
openOnArrowKeyDown: false,
|
|
199
|
-
focusItemOnOpen: "auto",
|
|
200
|
-
focusItemOnHover: false,
|
|
201
|
-
});
|
|
202
|
-
const dismiss = useDismiss(context, {
|
|
203
|
-
outsidePress: outsidePressExcludeSelector
|
|
204
|
-
? (event) => {
|
|
205
|
-
var _a, _b, _c, _d;
|
|
206
|
-
const target = event.target;
|
|
207
|
-
const insideRef = (_b = (_a = context.elements.domReference) === null || _a === void 0 ? void 0 : _a.contains(target)) !== null && _b !== void 0 ? _b : false;
|
|
208
|
-
const insideFloating = (_d = (_c = context.elements.floating) === null || _c === void 0 ? void 0 : _c.contains(target)) !== null && _d !== void 0 ? _d : false;
|
|
209
|
-
const insideExclude = event.target.closest(outsidePressExcludeSelector);
|
|
210
|
-
return !(insideRef || insideFloating || insideExclude);
|
|
211
|
-
}
|
|
212
|
-
: true,
|
|
213
|
-
escapeKey: true,
|
|
214
|
-
outsidePressEvent: "click",
|
|
215
|
-
});
|
|
216
|
-
const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([click, listNav, dismiss]);
|
|
217
|
-
useEffect(() => {
|
|
218
|
-
listRef.current.length = navigableCount;
|
|
219
|
-
setActiveIndex(prev => {
|
|
220
|
-
if (navigableCount <= 0)
|
|
221
|
-
return null;
|
|
222
|
-
if (prev == null)
|
|
223
|
-
return null;
|
|
224
|
-
return prev >= navigableCount ? navigableCount - 1 : prev;
|
|
225
|
-
});
|
|
226
|
-
}, [navigableCount, setActiveIndex, listRef]);
|
|
227
|
-
const setReferenceElement = useStableSetReference(refs);
|
|
228
|
-
return {
|
|
229
|
-
refs,
|
|
230
|
-
floatingStyles,
|
|
231
|
-
context,
|
|
232
|
-
getReferenceProps,
|
|
233
|
-
getFloatingProps,
|
|
234
|
-
getItemProps,
|
|
235
|
-
activeIndex,
|
|
236
|
-
setActiveIndex,
|
|
237
|
-
listRef,
|
|
238
|
-
open,
|
|
239
|
-
setOpen,
|
|
240
|
-
setReferenceElement,
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Handler that prevents default pointer behavior.
|
|
246
|
-
* Used to prevent blur/focus issues when clicking on non-interactive menu elements.
|
|
247
|
-
*/
|
|
248
|
-
function preventDefaultPointerDown(e) {
|
|
249
|
-
e.preventDefault();
|
|
250
|
-
}
|
|
251
|
-
/**
|
|
252
|
-
* Creates a handler for pointer down events on interactive menu items (options/actions).
|
|
253
|
-
* Prevents default to avoid blur and sets flag for focus management.
|
|
254
|
-
*
|
|
255
|
-
* @param isHandlingMenuInteractionRef - Ref to track if a menu interaction is in progress
|
|
256
|
-
* @returns A pointer down event handler
|
|
257
|
-
*/
|
|
258
|
-
function createInteractionPointerDownHandler(isHandlingMenuInteractionRef) {
|
|
259
|
-
return (e) => {
|
|
260
|
-
e.preventDefault();
|
|
261
|
-
// Set flag to prevent blur/focus handlers from interfering
|
|
262
|
-
isHandlingMenuInteractionRef.current = true;
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// Keeping this hook cohesive improves readability by centralizing related
|
|
267
|
-
// interactions and state transitions.
|
|
268
|
-
// eslint-disable-next-line max-statements
|
|
269
|
-
function useAutocomplete(props, inputRef) {
|
|
270
|
-
const { menu, emptyActions, getOptionLabel: getOptionLabelProp, isOptionEqualToValue, inputValue, onInputChange, value, onChange, multiple, openOnFocus = true, readOnly = false, disabled = false, debounce: debounceMs = 300, } = props;
|
|
271
|
-
const isHandlingMenuInteractionRef = useRef(false);
|
|
272
|
-
// TODO: Clean up the types in these refs by enhancing the type system in useCallbackRef
|
|
273
|
-
const getOptionLabelPropRef = useCallbackRef((opt) => getOptionLabelProp === null || getOptionLabelProp === void 0 ? void 0 : getOptionLabelProp(opt));
|
|
274
|
-
const getOptionLabel = useCallback((opt) => {
|
|
275
|
-
const maybe = getOptionLabelPropRef(opt);
|
|
276
|
-
return maybe !== null && maybe !== void 0 ? maybe : opt.label;
|
|
277
|
-
}, [getOptionLabelPropRef]);
|
|
278
|
-
const isOptionEqualToValueRef = useCallbackRef((a, b) => isOptionEqualToValue === null || isOptionEqualToValue === void 0 ? void 0 : isOptionEqualToValue(a, b));
|
|
279
|
-
const equals = useCallback((a, b) => {
|
|
280
|
-
const custom = isOptionEqualToValueRef(a, b);
|
|
281
|
-
return custom != null ? custom : getOptionLabel(a) === getOptionLabel(b);
|
|
282
|
-
}, [isOptionEqualToValueRef, getOptionLabel]);
|
|
283
|
-
const isOptionSelected = useCallback((opt) => {
|
|
284
|
-
var _a;
|
|
285
|
-
if (multiple) {
|
|
286
|
-
const current = (_a = value) !== null && _a !== void 0 ? _a : EMPTY_SELECTED_VALUES;
|
|
287
|
-
return current.some(v => equals(v, opt));
|
|
288
|
-
}
|
|
289
|
-
const current = value;
|
|
290
|
-
return current != null ? equals(current, opt) : false;
|
|
291
|
-
}, [multiple, value, equals]);
|
|
292
|
-
const flatInitial = useMemo(() => flattenMenu(menu), [menu]);
|
|
293
|
-
const sections = flatInitial.sections;
|
|
294
|
-
const optionItems = flatInitial.optionItems;
|
|
295
|
-
const persistentsHeaders = flatInitial.persistentsHeaders;
|
|
296
|
-
const persistentsFooters = flatInitial.persistentsFooters;
|
|
297
|
-
// Stable wrappers for function props
|
|
298
|
-
const inputEqualsOptionRef = useCallbackRef((text, o) => {
|
|
299
|
-
const fn = props.inputEqualsOption;
|
|
300
|
-
return fn ? fn(text, o) : undefined;
|
|
301
|
-
});
|
|
302
|
-
const inputEquals = useCallback((text, o) => {
|
|
303
|
-
const custom = inputEqualsOptionRef(text, o);
|
|
304
|
-
return custom != null ? custom : getOptionLabel(o) === text;
|
|
305
|
-
}, [inputEqualsOptionRef, getOptionLabel]);
|
|
306
|
-
// inputValue changes very often, is this worth memoizing?
|
|
307
|
-
const exactLabelMatch = useMemo(() => {
|
|
308
|
-
return optionItems.some(o => inputEquals(inputValue, o));
|
|
309
|
-
}, [optionItems, inputEquals, inputValue]);
|
|
310
|
-
const lastInputWasUser = useRef(false);
|
|
311
|
-
const [debouncedInputValue, setDebouncedInputValue] = useState(inputValue);
|
|
312
|
-
const debouncedSetter = useDebounce(setDebouncedInputValue, debounceMs);
|
|
313
|
-
useEffect(() => {
|
|
314
|
-
// Skip debounce when clearing input for immediate feedback, preventing flickering of last selected item
|
|
315
|
-
if (debounceMs === 0 || inputValue === "") {
|
|
316
|
-
setDebouncedInputValue(inputValue);
|
|
317
|
-
// Cancel any pending debounced call so a stale intermediate value
|
|
318
|
-
// (e.g. "P" while deleting "Pipe…") doesn't overwrite the immediate set.
|
|
319
|
-
debouncedSetter.cancel();
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
debouncedSetter(inputValue);
|
|
323
|
-
}, [inputValue, debounceMs, debouncedSetter]);
|
|
324
|
-
const filterOptionsRef = useCallbackRef((opts, term) => {
|
|
325
|
-
const fn = (typeof props.filterOptions === "function"
|
|
326
|
-
? props.filterOptions
|
|
327
|
-
: undefined) || undefined;
|
|
328
|
-
return fn ? fn(opts, term) : undefined;
|
|
329
|
-
});
|
|
330
|
-
const applyFilter = useCallback((opts, term) => {
|
|
331
|
-
if (props.filterOptions === false)
|
|
332
|
-
return opts;
|
|
333
|
-
const override = filterOptionsRef(opts, term);
|
|
334
|
-
if (override)
|
|
335
|
-
return override;
|
|
336
|
-
const lowered = term.toLowerCase();
|
|
337
|
-
return opts.filter(opt => getOptionLabel(opt).toLowerCase().includes(lowered));
|
|
338
|
-
}, [props.filterOptions, filterOptionsRef, getOptionLabel]);
|
|
339
|
-
const renderable = useMemo(() => {
|
|
340
|
-
const filter = (opts) => {
|
|
341
|
-
if (exactLabelMatch && !lastInputWasUser.current)
|
|
342
|
-
return opts;
|
|
343
|
-
return applyFilter(opts, debouncedInputValue);
|
|
344
|
-
};
|
|
345
|
-
const items = buildRenderableList(sections, filter);
|
|
346
|
-
const hasAnyOptions = items.some(i => i.kind === "option");
|
|
347
|
-
if (!hasAnyOptions) {
|
|
348
|
-
if (emptyActions) {
|
|
349
|
-
const derived = typeof emptyActions === "function"
|
|
350
|
-
? emptyActions({ inputValue })
|
|
351
|
-
: emptyActions;
|
|
352
|
-
return derived.map(act => ({
|
|
353
|
-
kind: "action",
|
|
354
|
-
action: act,
|
|
355
|
-
origin: "empty",
|
|
356
|
-
}));
|
|
357
|
-
}
|
|
358
|
-
// No options and no emptyActions: render empty state
|
|
359
|
-
return [];
|
|
360
|
-
}
|
|
361
|
-
return items;
|
|
362
|
-
}, [
|
|
363
|
-
sections,
|
|
364
|
-
applyFilter,
|
|
365
|
-
debouncedInputValue,
|
|
366
|
-
exactLabelMatch,
|
|
367
|
-
emptyActions,
|
|
368
|
-
inputValue,
|
|
369
|
-
]);
|
|
370
|
-
// This is only options
|
|
371
|
-
const optionCount = renderable.reduce((count, item) => count + (item.kind === "option" ? 1 : 0), 0);
|
|
372
|
-
const hasSelection = useMemo(() => {
|
|
373
|
-
var _a;
|
|
374
|
-
if (multiple) {
|
|
375
|
-
const current = (_a = value) !== null && _a !== void 0 ? _a : EMPTY_SELECTED_VALUES;
|
|
376
|
-
return Array.isArray(current) && current.length > 0;
|
|
377
|
-
}
|
|
378
|
-
return value != null;
|
|
379
|
-
}, [multiple, value]);
|
|
380
|
-
const headerInteractivePersistents = persistentsHeaders.filter(p => Boolean(p.onClick));
|
|
381
|
-
const footerInteractivePersistents = persistentsFooters.filter(p => Boolean(p.onClick));
|
|
382
|
-
const mainNavigableCount = renderable.reduce((c, i) => c + (i.kind === "section" ? 0 : 1), 0);
|
|
383
|
-
const totalNavigableCount = headerInteractivePersistents.length +
|
|
384
|
-
mainNavigableCount +
|
|
385
|
-
footerInteractivePersistents.length;
|
|
386
|
-
// Compute the currently selected index in the global navigable list (header -> middle -> footer).
|
|
387
|
-
// In multiple mode this is always null — there is no single "selected" index, and
|
|
388
|
-
// floating-ui would otherwise jump the highlight whenever the value array changes.
|
|
389
|
-
const selectedIndex = useMemo(() => {
|
|
390
|
-
if (multiple)
|
|
391
|
-
return null;
|
|
392
|
-
const selectedValue = value;
|
|
393
|
-
if (!selectedValue)
|
|
394
|
-
return null;
|
|
395
|
-
const middleIndex = findNavigableIndexForValue(renderable, equals, selectedValue);
|
|
396
|
-
if (middleIndex == null)
|
|
397
|
-
return null;
|
|
398
|
-
return headerInteractivePersistents.length + middleIndex;
|
|
399
|
-
}, [
|
|
400
|
-
multiple,
|
|
401
|
-
value,
|
|
402
|
-
renderable,
|
|
403
|
-
equals,
|
|
404
|
-
headerInteractivePersistents.length,
|
|
405
|
-
]);
|
|
406
|
-
const { refs, floatingStyles, context, getReferenceProps, getFloatingProps, getItemProps, activeIndex, setActiveIndex, listRef, open, setOpen, setReferenceElement, } = useAutocompleteListNav({
|
|
407
|
-
navigableCount: totalNavigableCount,
|
|
408
|
-
shouldResetActiveIndexOnClose: () => !hasSelection,
|
|
409
|
-
selectedIndex,
|
|
410
|
-
readOnly,
|
|
411
|
-
disabled,
|
|
412
|
-
outsidePressExcludeSelector: multiple
|
|
413
|
-
? "[data-testid='ATL-AutocompleteRebuilt-chipArea']"
|
|
414
|
-
: undefined,
|
|
415
|
-
onMenuOpen: () => {
|
|
416
|
-
if (multiple)
|
|
417
|
-
return;
|
|
418
|
-
const selectedValue = value;
|
|
419
|
-
if (selectedValue) {
|
|
420
|
-
const selectedNavigableIndex = findNavigableIndexForValue(renderable, equals, selectedValue);
|
|
421
|
-
if (selectedNavigableIndex != null) {
|
|
422
|
-
setActiveIndex(selectedNavigableIndex);
|
|
423
|
-
return;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
setActiveIndex(null);
|
|
427
|
-
},
|
|
428
|
-
onMenuClose: () => {
|
|
429
|
-
if (props.allowFreeForm !== true) {
|
|
430
|
-
const hasText = inputValue.trim().length > 0;
|
|
431
|
-
if (hasText && !hasSelection) {
|
|
432
|
-
lastInputWasUser.current = false;
|
|
433
|
-
onInputChange === null || onInputChange === void 0 ? void 0 : onInputChange("");
|
|
434
|
-
setActiveIndex(null);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
},
|
|
438
|
-
});
|
|
439
|
-
const prevOpenRef = useRef(false);
|
|
440
|
-
// TODO: Leverage FloatingUI useFocus, and onArrowKeyDown to manage open state and allow onOpenChange to be the source of truth
|
|
441
|
-
// JOB-154442
|
|
442
|
-
useEffect(() => {
|
|
443
|
-
var _a, _b;
|
|
444
|
-
if (open && !prevOpenRef.current) {
|
|
445
|
-
(_a = props.onOpen) === null || _a === void 0 ? void 0 : _a.call(props);
|
|
446
|
-
}
|
|
447
|
-
else if (!open && prevOpenRef.current) {
|
|
448
|
-
(_b = props.onClose) === null || _b === void 0 ? void 0 : _b.call(props);
|
|
449
|
-
}
|
|
450
|
-
prevOpenRef.current = open;
|
|
451
|
-
}, [open, props.onOpen, props.onClose]);
|
|
452
|
-
function selectOption(option) {
|
|
453
|
-
var _a;
|
|
454
|
-
if (multiple) {
|
|
455
|
-
const current = (_a = value) !== null && _a !== void 0 ? _a : EMPTY_SELECTED_VALUES;
|
|
456
|
-
const exists = current.some(v => equals(v, option));
|
|
457
|
-
const next = exists
|
|
458
|
-
? current.filter(v => !equals(v, option))
|
|
459
|
-
: [...current, option];
|
|
460
|
-
onChange((next.length === 0 ? EMPTY_SELECTED_VALUES : next));
|
|
461
|
-
lastInputWasUser.current = false;
|
|
462
|
-
onInputChange === null || onInputChange === void 0 ? void 0 : onInputChange("");
|
|
463
|
-
}
|
|
464
|
-
else {
|
|
465
|
-
onChange(option);
|
|
466
|
-
lastInputWasUser.current = false;
|
|
467
|
-
onInputChange === null || onInputChange === void 0 ? void 0 : onInputChange(getOptionLabel(option));
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
function reHighlightSelectedItem() {
|
|
471
|
-
const selectedValue = value;
|
|
472
|
-
if (!selectedValue)
|
|
473
|
-
return;
|
|
474
|
-
const idx = findNavigableIndexForValue(renderable, equals, selectedValue);
|
|
475
|
-
if (idx != null)
|
|
476
|
-
setActiveIndex(idx);
|
|
477
|
-
}
|
|
478
|
-
function tryCommitFreeFormOnEnter() {
|
|
479
|
-
if (props.allowFreeForm !== true)
|
|
480
|
-
return false;
|
|
481
|
-
if (open && activeIndex != null)
|
|
482
|
-
return false;
|
|
483
|
-
const inputText = inputValue.trim();
|
|
484
|
-
if (inputText.length === 0)
|
|
485
|
-
return false;
|
|
486
|
-
commitFromInputText(inputText);
|
|
487
|
-
return true;
|
|
488
|
-
}
|
|
489
|
-
const onSelection = useCallback((option) => {
|
|
490
|
-
var _a;
|
|
491
|
-
selectOption(option);
|
|
492
|
-
if (!multiple) {
|
|
493
|
-
setOpen(false);
|
|
494
|
-
}
|
|
495
|
-
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
496
|
-
}, [selectOption, setOpen, multiple, inputRef]);
|
|
497
|
-
const onAction = useCallback((action) => {
|
|
498
|
-
var _a;
|
|
499
|
-
action.run();
|
|
500
|
-
setActiveIndex(null);
|
|
501
|
-
if (action.closeOnRun !== false)
|
|
502
|
-
setOpen(false);
|
|
503
|
-
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
504
|
-
}, [setOpen, setActiveIndex, inputRef]);
|
|
505
|
-
/**
|
|
506
|
-
* Handler for mousedown on interactive menu items (options/actions)
|
|
507
|
-
* Prevents default to avoid blur and sets flag for focus management
|
|
508
|
-
*/
|
|
509
|
-
const onInteractionPointerDown = useMemo(() => createInteractionPointerDownHandler(isHandlingMenuInteractionRef), []);
|
|
510
|
-
function applyFreeFormValue(freeFormCreated) {
|
|
511
|
-
var _a;
|
|
512
|
-
const nextValue = multiple
|
|
513
|
-
? [...((_a = value) !== null && _a !== void 0 ? _a : EMPTY_SELECTED_VALUES), freeFormCreated]
|
|
514
|
-
: freeFormCreated;
|
|
515
|
-
props.onChange(nextValue);
|
|
516
|
-
if (multiple) {
|
|
517
|
-
lastInputWasUser.current = false;
|
|
518
|
-
onInputChange === null || onInputChange === void 0 ? void 0 : onInputChange("");
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
function commitFromInputText(inputText) {
|
|
522
|
-
var _a;
|
|
523
|
-
if (inputText.length === 0)
|
|
524
|
-
return false;
|
|
525
|
-
const match = optionItems.find(o => inputEquals(inputText, o));
|
|
526
|
-
if (match) {
|
|
527
|
-
onSelection(match);
|
|
528
|
-
return true;
|
|
529
|
-
}
|
|
530
|
-
setOpen(false);
|
|
531
|
-
if (props.allowFreeForm !== true)
|
|
532
|
-
return false;
|
|
533
|
-
const freeFormCreated = (_a = props.createFreeFormValue) === null || _a === void 0 ? void 0 : _a.call(props, inputText);
|
|
534
|
-
if (!freeFormCreated)
|
|
535
|
-
return false;
|
|
536
|
-
applyFreeFormValue(freeFormCreated);
|
|
537
|
-
return true;
|
|
538
|
-
}
|
|
539
|
-
const tryRestoreInputToSelectedLabel = useCallback(() => {
|
|
540
|
-
if (props.allowFreeForm === true)
|
|
541
|
-
return;
|
|
542
|
-
if (multiple) {
|
|
543
|
-
if (inputValue.trim().length > 0) {
|
|
544
|
-
lastInputWasUser.current = false;
|
|
545
|
-
onInputChange === null || onInputChange === void 0 ? void 0 : onInputChange("");
|
|
546
|
-
}
|
|
547
|
-
return;
|
|
548
|
-
}
|
|
549
|
-
const selectedValue = value;
|
|
550
|
-
if (!selectedValue)
|
|
551
|
-
return;
|
|
552
|
-
const selectedLabel = getOptionLabel(selectedValue);
|
|
553
|
-
if (inputValue === selectedLabel)
|
|
554
|
-
return;
|
|
555
|
-
lastInputWasUser.current = false;
|
|
556
|
-
onInputChange === null || onInputChange === void 0 ? void 0 : onInputChange(selectedLabel);
|
|
557
|
-
}, [
|
|
558
|
-
props.allowFreeForm,
|
|
559
|
-
getOptionLabel,
|
|
560
|
-
inputValue,
|
|
561
|
-
onInputChange,
|
|
562
|
-
multiple,
|
|
563
|
-
value,
|
|
564
|
-
]);
|
|
565
|
-
const onInputFocus = useCallback((event) => {
|
|
566
|
-
var _a;
|
|
567
|
-
if (!readOnly && openOnFocus && !isHandlingMenuInteractionRef.current) {
|
|
568
|
-
setOpen(true);
|
|
569
|
-
}
|
|
570
|
-
// Only call user's onFocus for genuine focus events, not programmatic restorations
|
|
571
|
-
if (!isHandlingMenuInteractionRef.current) {
|
|
572
|
-
(_a = props.onFocus) === null || _a === void 0 ? void 0 : _a.call(props, event);
|
|
573
|
-
}
|
|
574
|
-
isHandlingMenuInteractionRef.current = false;
|
|
575
|
-
}, [props.onFocus, readOnly, openOnFocus, setOpen]);
|
|
576
|
-
const onInputBlur = useCallback((event) => {
|
|
577
|
-
var _a, _b;
|
|
578
|
-
if (isHandlingMenuInteractionRef.current) {
|
|
579
|
-
return;
|
|
580
|
-
}
|
|
581
|
-
if (readOnly) {
|
|
582
|
-
(_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, event);
|
|
583
|
-
return;
|
|
584
|
-
}
|
|
585
|
-
if (props.allowFreeForm === true) {
|
|
586
|
-
const inputText = inputValue.trim();
|
|
587
|
-
if (inputText.length > 0)
|
|
588
|
-
commitFromInputText(inputText);
|
|
589
|
-
}
|
|
590
|
-
else {
|
|
591
|
-
tryRestoreInputToSelectedLabel();
|
|
592
|
-
}
|
|
593
|
-
lastInputWasUser.current = false;
|
|
594
|
-
(_b = props.onBlur) === null || _b === void 0 ? void 0 : _b.call(props, event);
|
|
595
|
-
}, [
|
|
596
|
-
readOnly,
|
|
597
|
-
props.allowFreeForm,
|
|
598
|
-
inputValue,
|
|
599
|
-
props.onBlur,
|
|
600
|
-
tryRestoreInputToSelectedLabel,
|
|
601
|
-
setOpen,
|
|
602
|
-
]);
|
|
603
|
-
function getRegionByActiveIndex(index) {
|
|
604
|
-
const headerCount = headerInteractivePersistents.length;
|
|
605
|
-
const mainCount = mainNavigableCount;
|
|
606
|
-
if (index < headerCount)
|
|
607
|
-
return { region: "header", regionIndex: index };
|
|
608
|
-
const middleIndex = index - headerCount;
|
|
609
|
-
if (middleIndex < mainCount) {
|
|
610
|
-
return { region: "middle", regionIndex: middleIndex };
|
|
611
|
-
}
|
|
612
|
-
return {
|
|
613
|
-
region: "footer",
|
|
614
|
-
regionIndex: middleIndex - mainCount,
|
|
615
|
-
};
|
|
616
|
-
}
|
|
617
|
-
function computeInitialIndexForArrowUp() {
|
|
618
|
-
// If there are interactive footers, prefer the very last navigable item
|
|
619
|
-
if (footerInteractivePersistents.length > 0) {
|
|
620
|
-
return totalNavigableCount > 0 ? totalNavigableCount - 1 : null;
|
|
621
|
-
}
|
|
622
|
-
// Otherwise, prefer the last OPTION (not action), matching legacy behavior
|
|
623
|
-
let navigable = -1;
|
|
624
|
-
let lastOptionIdx = -1;
|
|
625
|
-
for (const item of renderable) {
|
|
626
|
-
if (item.kind === "section")
|
|
627
|
-
continue;
|
|
628
|
-
navigable += 1;
|
|
629
|
-
if (item.kind === "option")
|
|
630
|
-
lastOptionIdx = navigable;
|
|
631
|
-
}
|
|
632
|
-
if (lastOptionIdx >= 0) {
|
|
633
|
-
return headerInteractivePersistents.length + lastOptionIdx;
|
|
634
|
-
}
|
|
635
|
-
return totalNavigableCount > 0 ? totalNavigableCount - 1 : null;
|
|
636
|
-
}
|
|
637
|
-
function handleArrowNavigation(key, event) {
|
|
638
|
-
if (!open) {
|
|
639
|
-
setOpen(true);
|
|
640
|
-
return;
|
|
641
|
-
}
|
|
642
|
-
if (activeIndex == null) {
|
|
643
|
-
setActiveIndex(key === "ArrowDown" ? 0 : computeInitialIndexForArrowUp());
|
|
644
|
-
event.preventDefault();
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
function handleEnterKey(event) {
|
|
648
|
-
if (tryCommitFreeFormOnEnter()) {
|
|
649
|
-
event.preventDefault();
|
|
650
|
-
return;
|
|
651
|
-
}
|
|
652
|
-
if (!open || activeIndex == null)
|
|
653
|
-
return;
|
|
654
|
-
const { region, regionIndex } = getRegionByActiveIndex(activeIndex);
|
|
655
|
-
if (region === "middle") {
|
|
656
|
-
invokeActiveItemOnEnter(event, regionIndex, renderable, onSelection, onAction);
|
|
657
|
-
return;
|
|
658
|
-
}
|
|
659
|
-
const persistent = region === "header"
|
|
660
|
-
? headerInteractivePersistents[regionIndex]
|
|
661
|
-
: footerInteractivePersistents[regionIndex];
|
|
662
|
-
if (persistent === null || persistent === void 0 ? void 0 : persistent.onClick) {
|
|
663
|
-
onAction({
|
|
664
|
-
run: persistent.onClick,
|
|
665
|
-
closeOnRun: persistent.shouldClose,
|
|
666
|
-
});
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
const removeLastSelection = useCallback(() => {
|
|
670
|
-
var _a;
|
|
671
|
-
if (!multiple || readOnly)
|
|
672
|
-
return;
|
|
673
|
-
const current = (_a = value) !== null && _a !== void 0 ? _a : EMPTY_SELECTED_VALUES;
|
|
674
|
-
if (current.length > 0) {
|
|
675
|
-
const next = current.slice(0, -1);
|
|
676
|
-
onChange((next.length === 0 ? EMPTY_SELECTED_VALUES : next));
|
|
677
|
-
}
|
|
678
|
-
}, [multiple, readOnly, value, onChange]);
|
|
679
|
-
const removeSelection = useCallback((option) => {
|
|
680
|
-
var _a;
|
|
681
|
-
if (readOnly)
|
|
682
|
-
return;
|
|
683
|
-
if (multiple) {
|
|
684
|
-
const current = (_a = value) !== null && _a !== void 0 ? _a : EMPTY_SELECTED_VALUES;
|
|
685
|
-
const next = current.filter(v => !equals(v, option));
|
|
686
|
-
onChange((next.length === 0
|
|
687
|
-
? EMPTY_SELECTED_VALUES
|
|
688
|
-
: next));
|
|
689
|
-
}
|
|
690
|
-
else {
|
|
691
|
-
const current = value;
|
|
692
|
-
if (current && equals(current, option)) {
|
|
693
|
-
onChange(undefined);
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
}, [readOnly, multiple, value, equals, onChange]);
|
|
697
|
-
const onInputKeyDown = useCallback((event) => {
|
|
698
|
-
const key = event.key;
|
|
699
|
-
if (key === "Backspace" && multiple && inputValue === "") {
|
|
700
|
-
removeLastSelection();
|
|
701
|
-
return;
|
|
702
|
-
}
|
|
703
|
-
if (key !== "ArrowDown" && key !== "ArrowUp" && key !== "Enter")
|
|
704
|
-
return;
|
|
705
|
-
if (key === "ArrowDown" || key === "ArrowUp") {
|
|
706
|
-
handleArrowNavigation(key, event);
|
|
707
|
-
return;
|
|
708
|
-
}
|
|
709
|
-
handleEnterKey(event);
|
|
710
|
-
}, [open, onSelection, onAction, multiple, inputValue, removeLastSelection]);
|
|
711
|
-
const onInputChangeFromUser = useCallback((val) => {
|
|
712
|
-
lastInputWasUser.current = true;
|
|
713
|
-
const isEmpty = val.trim().length === 0;
|
|
714
|
-
if (isEmpty) {
|
|
715
|
-
setActiveIndex(null);
|
|
716
|
-
if (!multiple && hasSelection) {
|
|
717
|
-
onChange === null || onChange === void 0 ? void 0 : onChange(undefined);
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
else if (val.length > inputValue.length) {
|
|
721
|
-
setActiveIndex(null);
|
|
722
|
-
}
|
|
723
|
-
else if (val.length < inputValue.length &&
|
|
724
|
-
!multiple &&
|
|
725
|
-
open &&
|
|
726
|
-
hasSelection) {
|
|
727
|
-
reHighlightSelectedItem();
|
|
728
|
-
}
|
|
729
|
-
if (!readOnly) {
|
|
730
|
-
setOpen((!isEmpty && !props.allowFreeForm) || openOnFocus);
|
|
731
|
-
}
|
|
732
|
-
onInputChange === null || onInputChange === void 0 ? void 0 : onInputChange(val);
|
|
733
|
-
}, [
|
|
734
|
-
onInputChange,
|
|
735
|
-
inputValue,
|
|
736
|
-
setActiveIndex,
|
|
737
|
-
readOnly,
|
|
738
|
-
props.allowFreeForm,
|
|
739
|
-
openOnFocus,
|
|
740
|
-
setOpen,
|
|
741
|
-
multiple,
|
|
742
|
-
hasSelection,
|
|
743
|
-
onChange,
|
|
744
|
-
open,
|
|
745
|
-
]);
|
|
746
|
-
const clearAll = useCallback(() => {
|
|
747
|
-
if (multiple) {
|
|
748
|
-
onChange(EMPTY_SELECTED_VALUES);
|
|
749
|
-
}
|
|
750
|
-
else {
|
|
751
|
-
onChange(undefined);
|
|
752
|
-
}
|
|
753
|
-
onInputChangeFromUser("");
|
|
754
|
-
}, [multiple, onChange, onInputChangeFromUser]);
|
|
755
|
-
return {
|
|
756
|
-
// rendering data
|
|
757
|
-
renderable,
|
|
758
|
-
optionCount,
|
|
759
|
-
persistentsHeaders,
|
|
760
|
-
persistentsFooters,
|
|
761
|
-
headerInteractiveCount: headerInteractivePersistents.length,
|
|
762
|
-
middleNavigableCount: mainNavigableCount,
|
|
763
|
-
getOptionLabel,
|
|
764
|
-
isOptionSelected,
|
|
765
|
-
// floating-ui
|
|
766
|
-
refs,
|
|
767
|
-
floatingStyles,
|
|
768
|
-
context,
|
|
769
|
-
getReferenceProps,
|
|
770
|
-
getFloatingProps,
|
|
771
|
-
getItemProps,
|
|
772
|
-
// state
|
|
773
|
-
open,
|
|
774
|
-
setOpen,
|
|
775
|
-
activeIndex,
|
|
776
|
-
setActiveIndex,
|
|
777
|
-
listRef,
|
|
778
|
-
// actions
|
|
779
|
-
onSelection,
|
|
780
|
-
onAction,
|
|
781
|
-
onInteractionPointerDown,
|
|
782
|
-
removeSelection,
|
|
783
|
-
clearAll,
|
|
784
|
-
// input handlers
|
|
785
|
-
onInputChangeFromUser,
|
|
786
|
-
onInputBlur,
|
|
787
|
-
onInputFocus,
|
|
788
|
-
onInputKeyDown,
|
|
789
|
-
// ref attachment
|
|
790
|
-
setReferenceElement,
|
|
791
|
-
};
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
function useChipNavigation({ selectedValues, inputValue, readOnly, removeSelection, onInputKeyDown, onInputBlur, }) {
|
|
795
|
-
const [rawActiveChipIndex, setRawActiveChipIndex] = useState(null);
|
|
796
|
-
const [previousInputValue, setPreviousInputValue] = useState(inputValue);
|
|
797
|
-
const activeChipIndex = clampActiveIndex(rawActiveChipIndex, selectedValues.length);
|
|
798
|
-
if (previousInputValue !== inputValue) {
|
|
799
|
-
setPreviousInputValue(inputValue);
|
|
800
|
-
if (inputValue !== "" && rawActiveChipIndex !== null) {
|
|
801
|
-
setRawActiveChipIndex(null);
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
const handleActiveChipKey = useCallback(
|
|
805
|
-
// eslint-disable-next-line max-statements
|
|
806
|
-
(event) => {
|
|
807
|
-
if (activeChipIndex === null)
|
|
808
|
-
return false;
|
|
809
|
-
const { key } = event;
|
|
810
|
-
if (key === "ArrowLeft") {
|
|
811
|
-
event.preventDefault();
|
|
812
|
-
setRawActiveChipIndex(i => Math.max(0, (i !== null && i !== void 0 ? i : 0) - 1));
|
|
813
|
-
return true;
|
|
814
|
-
}
|
|
815
|
-
if (key === "ArrowRight") {
|
|
816
|
-
event.preventDefault();
|
|
817
|
-
setRawActiveChipIndex(activeChipIndex + 1 >= selectedValues.length
|
|
818
|
-
? null
|
|
819
|
-
: activeChipIndex + 1);
|
|
820
|
-
return true;
|
|
821
|
-
}
|
|
822
|
-
if (key === "Backspace" || key === "Delete") {
|
|
823
|
-
event.preventDefault();
|
|
824
|
-
const option = selectedValues[activeChipIndex];
|
|
825
|
-
const newLen = selectedValues.length - 1;
|
|
826
|
-
if (newLen === 0) {
|
|
827
|
-
setRawActiveChipIndex(null);
|
|
828
|
-
}
|
|
829
|
-
else if (activeChipIndex >= newLen) {
|
|
830
|
-
setRawActiveChipIndex(newLen - 1);
|
|
831
|
-
}
|
|
832
|
-
removeSelection(option);
|
|
833
|
-
return true;
|
|
834
|
-
}
|
|
835
|
-
if (key === "Escape") {
|
|
836
|
-
setRawActiveChipIndex(null);
|
|
837
|
-
return true;
|
|
838
|
-
}
|
|
839
|
-
setRawActiveChipIndex(null);
|
|
840
|
-
return false;
|
|
841
|
-
}, [activeChipIndex, selectedValues, removeSelection]);
|
|
842
|
-
const onKeyDown = useCallback((event) => {
|
|
843
|
-
if (handleActiveChipKey(event))
|
|
844
|
-
return;
|
|
845
|
-
if (event.key === "ArrowLeft" &&
|
|
846
|
-
!readOnly &&
|
|
847
|
-
inputValue === "" &&
|
|
848
|
-
selectedValues.length > 0) {
|
|
849
|
-
event.preventDefault();
|
|
850
|
-
setRawActiveChipIndex(selectedValues.length - 1);
|
|
851
|
-
return;
|
|
852
|
-
}
|
|
853
|
-
onInputKeyDown(event);
|
|
854
|
-
}, [handleActiveChipKey, selectedValues, inputValue, readOnly, onInputKeyDown]);
|
|
855
|
-
const onBlur = useCallback((event) => {
|
|
856
|
-
setRawActiveChipIndex(null);
|
|
857
|
-
onInputBlur(event);
|
|
858
|
-
}, [onInputBlur]);
|
|
859
|
-
return { activeChipIndex, onKeyDown, onBlur };
|
|
860
|
-
}
|
|
861
|
-
function clampActiveIndex(index, length) {
|
|
862
|
-
if (index === null)
|
|
863
|
-
return null;
|
|
864
|
-
if (length === 0)
|
|
865
|
-
return null;
|
|
866
|
-
if (index >= length)
|
|
867
|
-
return length - 1;
|
|
868
|
-
return index;
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
function MenuList({ items, activeIndex, indexOffset = 0, getItemProps, listRef, listboxId, customRenderOption, customRenderSection, customRenderAction, getOptionLabel, onSelect, onAction, onInteractionPointerDown, isOptionSelected, slotOverrides, }) {
|
|
872
|
-
let navigableIndex = -1;
|
|
873
|
-
function renderItemNode(item) {
|
|
874
|
-
var _a, _b, _c, _d, _e, _f;
|
|
875
|
-
if (item.kind === "section") {
|
|
876
|
-
return handleSectionRendering({
|
|
877
|
-
section: item.section,
|
|
878
|
-
customRenderSection,
|
|
879
|
-
sectionClassName: (_a = slotOverrides === null || slotOverrides === void 0 ? void 0 : slotOverrides.section) === null || _a === void 0 ? void 0 : _a.className,
|
|
880
|
-
sectionStyle: (_b = slotOverrides === null || slotOverrides === void 0 ? void 0 : slotOverrides.section) === null || _b === void 0 ? void 0 : _b.style,
|
|
881
|
-
});
|
|
882
|
-
}
|
|
883
|
-
if (item.kind === "option") {
|
|
884
|
-
const result = handleOptionRendering({
|
|
885
|
-
option: item.value,
|
|
886
|
-
activeIndex,
|
|
887
|
-
navigableIndex,
|
|
888
|
-
getItemProps,
|
|
889
|
-
listRef,
|
|
890
|
-
listboxId,
|
|
891
|
-
isOptionSelected,
|
|
892
|
-
customRenderOption,
|
|
893
|
-
getOptionLabel,
|
|
894
|
-
onSelect,
|
|
895
|
-
onInteractionPointerDown,
|
|
896
|
-
indexOffset,
|
|
897
|
-
optionClassName: (_c = slotOverrides === null || slotOverrides === void 0 ? void 0 : slotOverrides.option) === null || _c === void 0 ? void 0 : _c.className,
|
|
898
|
-
optionStyle: (_d = slotOverrides === null || slotOverrides === void 0 ? void 0 : slotOverrides.option) === null || _d === void 0 ? void 0 : _d.style,
|
|
899
|
-
});
|
|
900
|
-
navigableIndex = result.nextNavigableIndex;
|
|
901
|
-
return result.node;
|
|
902
|
-
}
|
|
903
|
-
const result = handleActionRendering({
|
|
904
|
-
action: item.action,
|
|
905
|
-
activeIndex,
|
|
906
|
-
navigableIndex,
|
|
907
|
-
getItemProps,
|
|
908
|
-
listRef,
|
|
909
|
-
listboxId,
|
|
910
|
-
customRenderAction,
|
|
911
|
-
onAction,
|
|
912
|
-
onInteractionPointerDown,
|
|
913
|
-
indexOffset,
|
|
914
|
-
actionClassName: (_e = slotOverrides === null || slotOverrides === void 0 ? void 0 : slotOverrides.action) === null || _e === void 0 ? void 0 : _e.className,
|
|
915
|
-
actionStyle: (_f = slotOverrides === null || slotOverrides === void 0 ? void 0 : slotOverrides.action) === null || _f === void 0 ? void 0 : _f.style,
|
|
916
|
-
origin: item.origin,
|
|
917
|
-
});
|
|
918
|
-
navigableIndex = result.nextNavigableIndex;
|
|
919
|
-
return result.node;
|
|
920
|
-
}
|
|
921
|
-
return React__default.createElement(React__default.Fragment, null, items.map(renderItemNode));
|
|
922
|
-
}
|
|
923
|
-
function handleSectionRendering({ customRenderSection, section, sectionClassName, sectionStyle, }) {
|
|
924
|
-
var _a;
|
|
925
|
-
const headerContent = customRenderSection ? (customRenderSection(section)) : (React__default.createElement(DefaultSectionContent, { section: section }));
|
|
926
|
-
return (React__default.createElement("div", { key: `section-${String((_a = section.key) !== null && _a !== void 0 ? _a : section.label)}`, role: "presentation", tabIndex: -1, "data-testid": "ATL-AutocompleteRebuilt-Section", className: classnames(styles$1.section, styles$1.stickyTop, sectionClassName), style: sectionStyle, onPointerDown: preventDefaultPointerDown }, headerContent));
|
|
927
|
-
}
|
|
928
|
-
function DefaultSectionContent({ section, }) {
|
|
929
|
-
return React__default.createElement(Heading, { level: 5 }, section.label);
|
|
930
|
-
}
|
|
931
|
-
function handleOptionRendering({ option, activeIndex, navigableIndex, getItemProps, listRef, listboxId, isOptionSelected, customRenderOption, getOptionLabel, onSelect, onInteractionPointerDown, indexOffset = 0, optionClassName, optionStyle, }) {
|
|
932
|
-
var _a;
|
|
933
|
-
const nextNavigableIndex = navigableIndex + 1;
|
|
934
|
-
const isActive = activeIndex === nextNavigableIndex;
|
|
935
|
-
const isSelected = isOptionSelected(option);
|
|
936
|
-
const optionContent = customRenderOption ? (customRenderOption({ value: option, isActive, isSelected })) : (React__default.createElement(DefaultOptionContent, { isSelected: isSelected, text: getOptionLabel(option) }));
|
|
937
|
-
return {
|
|
938
|
-
node: (React__default.createElement("div", Object.assign({ key: `option-${String((_a = option.key) !== null && _a !== void 0 ? _a : getOptionLabel(option))}` }, getItemProps({
|
|
939
|
-
ref(node) {
|
|
940
|
-
const idx = nextNavigableIndex + indexOffset;
|
|
941
|
-
if (node)
|
|
942
|
-
listRef.current[idx] = node;
|
|
943
|
-
},
|
|
944
|
-
onClick: () => onSelect(option),
|
|
945
|
-
onPointerDown: onInteractionPointerDown,
|
|
946
|
-
className: classnames(styles$1.option, isActive && styles$1.optionActive, optionClassName),
|
|
947
|
-
style: optionStyle,
|
|
948
|
-
}), { role: "option", tabIndex: -1, "aria-selected": isSelected ? true : false, id: `${listboxId}-item-${nextNavigableIndex + indexOffset}`, "data-index": nextNavigableIndex + indexOffset, "data-active": isActive ? true : undefined }), optionContent)),
|
|
949
|
-
nextNavigableIndex,
|
|
950
|
-
};
|
|
951
|
-
}
|
|
952
|
-
function DefaultOptionContent({ isSelected, text, }) {
|
|
953
|
-
return (React__default.createElement("div", { className: styles$1.defaultOptionContent },
|
|
954
|
-
React__default.createElement("div", { className: styles$1.icon, style: isSelected ? undefined : { visibility: "hidden" } },
|
|
955
|
-
React__default.createElement(Icon, { name: "checkmark", size: "small" })),
|
|
956
|
-
React__default.createElement(Text, null, text)));
|
|
957
|
-
}
|
|
958
|
-
function handleActionRendering({ action, activeIndex, navigableIndex, getItemProps, listRef, listboxId, customRenderAction, onAction, onInteractionPointerDown, indexOffset = 0, actionClassName, actionStyle, origin, }) {
|
|
959
|
-
var _a;
|
|
960
|
-
const nextNavigableIndex = navigableIndex + 1;
|
|
961
|
-
const isActive = activeIndex === nextNavigableIndex;
|
|
962
|
-
const actionContent = customRenderAction ? (customRenderAction({ value: action, isActive, origin })) : (React__default.createElement(DefaultActionContent, { textContent: action.label }));
|
|
963
|
-
const computedIndex = nextNavigableIndex + indexOffset;
|
|
964
|
-
const itemProps = getItemProps({
|
|
965
|
-
ref(node) {
|
|
966
|
-
if (node) {
|
|
967
|
-
listRef.current[computedIndex] = node;
|
|
968
|
-
}
|
|
969
|
-
},
|
|
970
|
-
onClick: () => {
|
|
971
|
-
onAction({
|
|
972
|
-
run: action.onClick,
|
|
973
|
-
closeOnRun: action.shouldClose,
|
|
974
|
-
});
|
|
975
|
-
},
|
|
976
|
-
onPointerDown: onInteractionPointerDown,
|
|
977
|
-
className: classnames(styles$1.action, isActive && styles$1.actionActive, actionClassName),
|
|
978
|
-
style: actionStyle,
|
|
979
|
-
});
|
|
980
|
-
return {
|
|
981
|
-
node: (React__default.createElement("div", Object.assign({ key: `action-${String((origin !== null && origin !== void 0 ? origin : "menu") + "-" + ((_a = action.key) !== null && _a !== void 0 ? _a : action.label))}` }, itemProps, { role: "button", tabIndex: -1, "data-testid": "ATL-AutocompleteRebuilt-Action", id: `${listboxId}-item-${computedIndex}`, "data-index": computedIndex, "data-origin": origin, "data-active": isActive ? true : undefined }), actionContent)),
|
|
982
|
-
nextNavigableIndex,
|
|
983
|
-
};
|
|
984
|
-
}
|
|
985
|
-
function DefaultActionContent({ textContent, }) {
|
|
986
|
-
return (React__default.createElement(Typography, { textColor: "interactive", fontWeight: "semiBold", underline: "solid color-interactive", UNSAFE_style: {
|
|
987
|
-
textStyle: {
|
|
988
|
-
textDecorationThickness: "var(--border-thick)",
|
|
989
|
-
textUnderlineOffset: "var(--space-smallest)",
|
|
990
|
-
},
|
|
991
|
-
} }, textContent));
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
function PersistentRegion({ items, position, activeIndex, indexOffset, getItemProps, listRef, customRenderHeader, customRenderFooter, className, style, onAction, onInteractionPointerDown, }) {
|
|
995
|
-
if (!items || items.length === 0)
|
|
996
|
-
return null;
|
|
997
|
-
let navigableIndex = -1;
|
|
998
|
-
return (React__default.createElement("div", { className: className, style: style, "data-region": position, "data-testid": `ATL-AutocompleteRebuilt-${position}` }, items.map(persistent => {
|
|
999
|
-
const result = handlePersistentRendering({
|
|
1000
|
-
persistent,
|
|
1001
|
-
position,
|
|
1002
|
-
activeIndex,
|
|
1003
|
-
indexOffset,
|
|
1004
|
-
getItemProps,
|
|
1005
|
-
customRenderHeader,
|
|
1006
|
-
customRenderFooter,
|
|
1007
|
-
listRef,
|
|
1008
|
-
onAction,
|
|
1009
|
-
onInteractionPointerDown,
|
|
1010
|
-
navigableIndex,
|
|
1011
|
-
});
|
|
1012
|
-
navigableIndex = result.nextNavigableIndex;
|
|
1013
|
-
return result.node;
|
|
1014
|
-
})));
|
|
1015
|
-
}
|
|
1016
|
-
function handlePersistentRendering({ persistent, position, activeIndex, indexOffset, getItemProps, customRenderHeader, customRenderFooter, listRef, onAction, onInteractionPointerDown, navigableIndex, }) {
|
|
1017
|
-
const interactive = Boolean(persistent.onClick);
|
|
1018
|
-
if (!interactive) {
|
|
1019
|
-
const node = handleTextPersistentRendering({
|
|
1020
|
-
persistent,
|
|
1021
|
-
position,
|
|
1022
|
-
customRenderHeader,
|
|
1023
|
-
customRenderFooter,
|
|
1024
|
-
});
|
|
1025
|
-
return { node, nextNavigableIndex: navigableIndex };
|
|
1026
|
-
}
|
|
1027
|
-
return handleActionPersistentRendering({
|
|
1028
|
-
persistent,
|
|
1029
|
-
position,
|
|
1030
|
-
activeIndex,
|
|
1031
|
-
indexOffset,
|
|
1032
|
-
getItemProps,
|
|
1033
|
-
customRenderHeader,
|
|
1034
|
-
customRenderFooter,
|
|
1035
|
-
listRef,
|
|
1036
|
-
onAction,
|
|
1037
|
-
onInteractionPointerDown,
|
|
1038
|
-
navigableIndex,
|
|
1039
|
-
});
|
|
1040
|
-
}
|
|
1041
|
-
function handleTextPersistentRendering({ persistent, position, customRenderHeader, customRenderFooter, }) {
|
|
1042
|
-
var _a;
|
|
1043
|
-
let content;
|
|
1044
|
-
if (position === "header" && customRenderHeader) {
|
|
1045
|
-
content = customRenderHeader({ value: persistent });
|
|
1046
|
-
}
|
|
1047
|
-
else if (position === "footer" && customRenderFooter) {
|
|
1048
|
-
content = customRenderFooter({ value: persistent });
|
|
1049
|
-
}
|
|
1050
|
-
else {
|
|
1051
|
-
content = React__default.createElement(DefaultTextPersistentContent, { persistent: persistent });
|
|
1052
|
-
}
|
|
1053
|
-
return (React__default.createElement("div", { key: `persistent-${position}-${String((_a = persistent.key) !== null && _a !== void 0 ? _a : persistent.label)}`, role: "presentation", tabIndex: -1, className: styles$1.textPersistent, onPointerDown: preventDefaultPointerDown }, content));
|
|
1054
|
-
}
|
|
1055
|
-
function handleActionPersistentRendering({ persistent, position, activeIndex, indexOffset, getItemProps, customRenderHeader, customRenderFooter, listRef, onAction, onInteractionPointerDown, navigableIndex, }) {
|
|
1056
|
-
var _a;
|
|
1057
|
-
const nextNavigableIndex = navigableIndex + 1;
|
|
1058
|
-
const isActive = activeIndex === indexOffset + nextNavigableIndex;
|
|
1059
|
-
let content;
|
|
1060
|
-
if (position === "header" && customRenderHeader) {
|
|
1061
|
-
content = customRenderHeader({
|
|
1062
|
-
value: persistent,
|
|
1063
|
-
isActive,
|
|
1064
|
-
});
|
|
1065
|
-
}
|
|
1066
|
-
else if (position === "footer" && customRenderFooter) {
|
|
1067
|
-
content = customRenderFooter({
|
|
1068
|
-
value: persistent,
|
|
1069
|
-
isActive,
|
|
1070
|
-
});
|
|
1071
|
-
}
|
|
1072
|
-
else {
|
|
1073
|
-
content = React__default.createElement(DefaultActionContent, { textContent: persistent.label });
|
|
1074
|
-
}
|
|
1075
|
-
return {
|
|
1076
|
-
node: (React__default.createElement("div", Object.assign({ key: `persistent-${position}-${String((_a = persistent.key) !== null && _a !== void 0 ? _a : persistent.label)}`, "data-index": indexOffset + nextNavigableIndex, id: `${position}-persistent-${indexOffset + nextNavigableIndex}`, "data-active": isActive ? true : undefined }, getItemProps({
|
|
1077
|
-
ref(persistNode) {
|
|
1078
|
-
const idx = indexOffset + nextNavigableIndex;
|
|
1079
|
-
if (persistNode)
|
|
1080
|
-
listRef.current[idx] = persistNode;
|
|
1081
|
-
},
|
|
1082
|
-
onClick: () => {
|
|
1083
|
-
onAction({
|
|
1084
|
-
run: () => {
|
|
1085
|
-
var _a;
|
|
1086
|
-
(_a = persistent.onClick) === null || _a === void 0 ? void 0 : _a.call(persistent);
|
|
1087
|
-
},
|
|
1088
|
-
closeOnRun: persistent.shouldClose,
|
|
1089
|
-
});
|
|
1090
|
-
},
|
|
1091
|
-
onPointerDown: onInteractionPointerDown,
|
|
1092
|
-
className: classnames(styles$1.action, isActive && styles$1.actionActive),
|
|
1093
|
-
}), { role: "button", tabIndex: -1 }), content)),
|
|
1094
|
-
nextNavigableIndex,
|
|
1095
|
-
};
|
|
1096
|
-
}
|
|
1097
|
-
function DefaultTextPersistentContent({ persistent, }) {
|
|
1098
|
-
return React__default.createElement("div", { className: styles$1.textPersistent }, persistent.label);
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
function FloatingMenu({ context, getFloatingProps, refs, listboxId, className, floatingStyles, transitionStyles, menuStyle, renderable, persistentsHeaders, persistentsFooters, activeIndex, headerInteractiveCount, middleNavigableCount, getItemProps, listRef, onSelection, onAction, onInteractionPointerDown, getOptionLabel, isOptionSelected, loading, showEmptyStateMessage, emptyStateMessage, customRenderLoading, customRenderOption, customRenderSection, customRenderAction, customRenderHeader, customRenderFooter, slotOverrides, }) {
|
|
1102
|
-
var _a, _b, _c, _d;
|
|
1103
|
-
const activeIndexForMiddle = activeIndex != null ? activeIndex - headerInteractiveCount : null;
|
|
1104
|
-
return (React__default.createElement(FloatingPortal, null,
|
|
1105
|
-
React__default.createElement(FloatingFocusManager, { context: context, modal: false, initialFocus: -1, closeOnFocusOut: true, returnFocus: false },
|
|
1106
|
-
React__default.createElement("div", Object.assign({}, getFloatingProps({
|
|
1107
|
-
ref(node) {
|
|
1108
|
-
if (node)
|
|
1109
|
-
refs.setFloating(node);
|
|
1110
|
-
},
|
|
1111
|
-
id: listboxId,
|
|
1112
|
-
role: "listbox",
|
|
1113
|
-
className,
|
|
1114
|
-
style: Object.assign(Object.assign(Object.assign({}, floatingStyles), transitionStyles), menuStyle),
|
|
1115
|
-
})),
|
|
1116
|
-
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 }),
|
|
1117
|
-
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,
|
|
1118
|
-
showEmptyStateMessage && (React__default.createElement(EmptyStateMessage, { emptyState: emptyStateMessage })),
|
|
1119
|
-
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 }))))),
|
|
1120
|
-
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 })))));
|
|
1121
|
-
}
|
|
1122
|
-
function LoadingContent() {
|
|
1123
|
-
return (React__default.createElement("div", { className: styles$1.loadingList, onPointerDown: preventDefaultPointerDown },
|
|
1124
|
-
React__default.createElement(Glimmer, { shape: "rectangle", size: "base" }),
|
|
1125
|
-
React__default.createElement(Glimmer, { shape: "rectangle", size: "base" }),
|
|
1126
|
-
React__default.createElement(Glimmer, { shape: "rectangle", size: "base" })));
|
|
1127
|
-
}
|
|
1128
|
-
function EmptyStateMessage({ emptyState, }) {
|
|
1129
|
-
const emptyStateDefault = "No options";
|
|
1130
|
-
const emptyStateContent = emptyState !== null && emptyState !== void 0 ? emptyState : emptyStateDefault;
|
|
1131
|
-
return (React__default.createElement("div", { className: styles$1.emptyStateMessage, onPointerDown: preventDefaultPointerDown }, emptyStateContent));
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
const AutocompleteRebuilt = forwardRef(AutocompleteRebuiltInternal);
|
|
1135
|
-
// eslint-disable-next-line max-statements
|
|
1136
|
-
function AutocompleteRebuiltInternal(props, forwardedRef) {
|
|
1137
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
1138
|
-
const { inputValue, placeholder, disabled, error, invalid, description, size: sizeProp, loading = false, } = props;
|
|
1139
|
-
const formFieldRef = useRef(null);
|
|
1140
|
-
// Ref for the multi-select input element, where we cannot use refs.domReference
|
|
1141
|
-
// since it is not attached to the literal input element, whereas single + InputText only offers a
|
|
1142
|
-
// ref to the literal input element.
|
|
1143
|
-
const internalInputRef = useRef(null);
|
|
1144
|
-
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);
|
|
1145
|
-
const listboxId = React__default.useId();
|
|
1146
|
-
// Provides mount/unmount-aware transition styles for the floating element
|
|
1147
|
-
const { isMounted, styles: transitionStyles } = useTransitionStyles(context, {
|
|
1148
|
-
initial: { opacity: 0 },
|
|
1149
|
-
open: { opacity: 1 },
|
|
1150
|
-
close: { opacity: 0 },
|
|
1151
|
-
duration: { open: tokens["timing-base"], close: tokens["timing-base"] },
|
|
1152
|
-
});
|
|
1153
|
-
const [positionRefEl, setPositionRefEl] = React__default.useState(null);
|
|
1154
|
-
const inputId = React__default.useId();
|
|
1155
|
-
const descriptionId = `descriptionUUID--${inputId}`;
|
|
1156
|
-
const selectedValues = props.multiple
|
|
1157
|
-
? ((_a = props.value) !== null && _a !== void 0 ? _a : EMPTY_SELECTED_VALUES)
|
|
1158
|
-
: EMPTY_SELECTED_VALUES;
|
|
1159
|
-
const { activeChipIndex, onKeyDown: chipKeyDown, onBlur: chipBlur, } = useChipNavigation({
|
|
1160
|
-
selectedValues,
|
|
1161
|
-
inputValue,
|
|
1162
|
-
readOnly: props.readOnly,
|
|
1163
|
-
removeSelection,
|
|
1164
|
-
onInputKeyDown,
|
|
1165
|
-
onInputBlur,
|
|
1166
|
-
});
|
|
1167
|
-
const focusInputOnPointerDown = useCallback((e) => {
|
|
1168
|
-
var _a;
|
|
1169
|
-
const target = e.target;
|
|
1170
|
-
if (target.closest("input, button"))
|
|
1171
|
-
return;
|
|
1172
|
-
e.preventDefault();
|
|
1173
|
-
(_a = internalInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
1174
|
-
}, []);
|
|
1175
|
-
const composedReferenceProps = getReferenceProps(Object.assign({ onKeyDown: props.multiple ? chipKeyDown : onInputKeyDown, onFocus: onInputFocus, onBlur: props.multiple ? chipBlur : onInputBlur }, (props.multiple ? { onPointerDown: focusInputOnPointerDown } : {})));
|
|
1176
|
-
const dataAttrs = filterDataAttributes(props);
|
|
1177
|
-
const ariaProps = {
|
|
1178
|
-
role: "combobox",
|
|
1179
|
-
"aria-autocomplete": "list",
|
|
1180
|
-
"aria-expanded": open ? true : false,
|
|
1181
|
-
"aria-controls": listboxId,
|
|
1182
|
-
"aria-activedescendant": activeChipIndex === null && open && activeIndex != null
|
|
1183
|
-
? `${listboxId}-item-${activeIndex}`
|
|
1184
|
-
: undefined,
|
|
1185
|
-
};
|
|
1186
|
-
const inputProps = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ value: inputValue, onChange: props.readOnly ? undefined : onInputChangeFromUser }, (props.readOnly ? { onFocus: onInputFocus, onBlur: onInputBlur } : {})), { placeholder,
|
|
1187
|
-
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);
|
|
1188
|
-
const chipAreaRef = useCallback((node) => {
|
|
1189
|
-
if (!node)
|
|
1190
|
-
return;
|
|
1191
|
-
// In multiple mode, we use the input as the reference so floating-ui's
|
|
1192
|
-
// useListNavigation treats it as a typeable combobox and allows Space.
|
|
1193
|
-
// Only use chipArea for positioning/sizing.
|
|
1194
|
-
if (!props.multiple) {
|
|
1195
|
-
setReferenceElement(node);
|
|
1196
|
-
}
|
|
1197
|
-
const multiContainer = node.closest("[data-testid='ATL-AutocompleteRebuilt-multiSelectContainer']");
|
|
1198
|
-
if (multiContainer) {
|
|
1199
|
-
setPositionRefEl(multiContainer);
|
|
1200
|
-
}
|
|
1201
|
-
}, [setReferenceElement, props.multiple]);
|
|
1202
|
-
const referenceInputRef = useCallback((node) => {
|
|
1203
|
-
setReferenceElement(node);
|
|
1204
|
-
if (!props.multiple) {
|
|
1205
|
-
// Workaround to get the visual InputText element, which is not the same
|
|
1206
|
-
// as the literal input reference element when props like suffix/prefix
|
|
1207
|
-
// are present. Used to set the position reference for the floating menu.
|
|
1208
|
-
const visualInputTextElement = node === null || node === void 0 ? void 0 : node.closest("[data-testid='Form-Field-Wrapper']");
|
|
1209
|
-
if (visualInputTextElement) {
|
|
1210
|
-
setPositionRefEl(visualInputTextElement);
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
}, [setReferenceElement, props.multiple]);
|
|
1214
|
-
const mergedInputRef = useMemo(() => mergeRefs([
|
|
1215
|
-
referenceInputRef,
|
|
1216
|
-
internalInputRef,
|
|
1217
|
-
forwardedRef,
|
|
1218
|
-
]), [referenceInputRef, forwardedRef]);
|
|
1219
|
-
useEffect(() => {
|
|
1220
|
-
if (!positionRefEl)
|
|
1221
|
-
return;
|
|
1222
|
-
refs.setPositionReference(positionRefEl);
|
|
1223
|
-
}, [positionRefEl, refs]);
|
|
1224
|
-
const menuClassName = classnames(styles$1.list, (_b = props.UNSAFE_className) === null || _b === void 0 ? void 0 : _b.menu);
|
|
1225
|
-
const showEmptyStateMessage = optionCount === 0 && props.emptyStateMessage !== false;
|
|
1226
|
-
const floatingMenu = isMounted && !props.readOnly && !disabled && (React__default.createElement(FloatingMenu, { context: context, getFloatingProps: getFloatingProps, refs: refs, listboxId: listboxId, className: menuClassName, floatingStyles: floatingStyles, transitionStyles: transitionStyles, 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: {
|
|
1227
|
-
option: {
|
|
1228
|
-
className: (_d = props.UNSAFE_className) === null || _d === void 0 ? void 0 : _d.option,
|
|
1229
|
-
style: (_e = props.UNSAFE_styles) === null || _e === void 0 ? void 0 : _e.option,
|
|
1230
|
-
},
|
|
1231
|
-
action: {
|
|
1232
|
-
className: (_f = props.UNSAFE_className) === null || _f === void 0 ? void 0 : _f.action,
|
|
1233
|
-
style: (_g = props.UNSAFE_styles) === null || _g === void 0 ? void 0 : _g.action,
|
|
1234
|
-
},
|
|
1235
|
-
section: {
|
|
1236
|
-
className: (_h = props.UNSAFE_className) === null || _h === void 0 ? void 0 : _h.section,
|
|
1237
|
-
style: (_j = props.UNSAFE_styles) === null || _j === void 0 ? void 0 : _j.section,
|
|
1238
|
-
},
|
|
1239
|
-
header: {
|
|
1240
|
-
className: (_k = props.UNSAFE_className) === null || _k === void 0 ? void 0 : _k.header,
|
|
1241
|
-
style: (_l = props.UNSAFE_styles) === null || _l === void 0 ? void 0 : _l.header,
|
|
1242
|
-
},
|
|
1243
|
-
footer: {
|
|
1244
|
-
className: (_m = props.UNSAFE_className) === null || _m === void 0 ? void 0 : _m.footer,
|
|
1245
|
-
style: (_o = props.UNSAFE_styles) === null || _o === void 0 ? void 0 : _o.footer,
|
|
1246
|
-
},
|
|
1247
|
-
} }));
|
|
1248
|
-
if (props.customRenderInput) {
|
|
1249
|
-
return (React__default.createElement("div", { "data-testid": "ATL-AutocompleteRebuilt" },
|
|
1250
|
-
props.customRenderInput({ inputRef: mergedInputRef, inputProps }),
|
|
1251
|
-
floatingMenu));
|
|
1252
|
-
}
|
|
1253
|
-
if (props.multiple) {
|
|
1254
|
-
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
|
|
1255
|
-
? { onFocus: onInputFocus, onBlur: chipBlur }
|
|
1256
|
-
: 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));
|
|
1257
|
-
}
|
|
1258
|
-
return (React__default.createElement("div", { "data-testid": "ATL-AutocompleteRebuilt" },
|
|
1259
|
-
React__default.createElement(InputText, Object.assign({ ref: mergedInputRef }, inputProps)),
|
|
1260
|
-
floatingMenu));
|
|
1261
|
-
}
|
|
1262
|
-
function SelectionChip({ value, active, disabled, getOptionLabel, customRenderValue, canDismiss, onDismiss, unsafeClassName, unsafeStyle, }) {
|
|
1263
|
-
return (React__default.createElement("span", { "data-selection-disabled": disabled, className: classnames(styles$1.selectionChip, {
|
|
1264
|
-
[styles$1.selectionChipActive]: active,
|
|
1265
|
-
[styles$1.selectionChipDisabled]: disabled,
|
|
1266
|
-
}, unsafeClassName), style: unsafeStyle, "data-testid": "ATL-AutocompleteRebuilt-chip", onPointerDown: preventDefaultPointerDown },
|
|
1267
|
-
customRenderValue ? (customRenderValue({ value, getOptionLabel })) : (React__default.createElement(Typography, { size: "small" }, getOptionLabel(value))),
|
|
1268
|
-
canDismiss && (React__default.createElement("button", { type: "button", disabled: disabled, className: styles$1.chipDismiss, onClick: onDismiss, onPointerDown: preventDefaultPointerDown, "aria-label": `Remove ${getOptionLabel(value)}`, tabIndex: -1 },
|
|
1269
|
-
React__default.createElement(Icon, { size: "small", name: "remove" })))));
|
|
1270
|
-
}
|
|
1271
|
-
function SelectionChipsList({ selectedValues, isFocused, limitVisibleSelections = 6, limitSelectionText = (count) => `+${count}`, activeChipIndex, disabled, getOptionLabel, customRenderValue, canDismiss, removeSelection, unsafeClassName, unsafeStyle, }) {
|
|
1272
|
-
const shouldLimit = !isFocused &&
|
|
1273
|
-
limitVisibleSelections !== -1 &&
|
|
1274
|
-
selectedValues.length > limitVisibleSelections;
|
|
1275
|
-
const visibleValues = shouldLimit
|
|
1276
|
-
? selectedValues.slice(0, limitVisibleSelections)
|
|
1277
|
-
: selectedValues;
|
|
1278
|
-
const hiddenCount = shouldLimit
|
|
1279
|
-
? selectedValues.length - limitVisibleSelections
|
|
1280
|
-
: 0;
|
|
1281
|
-
return (React__default.createElement(React__default.Fragment, null,
|
|
1282
|
-
visibleValues.map((v, i) => {
|
|
1283
|
-
var _a;
|
|
1284
|
-
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 }));
|
|
1285
|
-
}),
|
|
1286
|
-
hiddenCount > 0 && (React__default.createElement("span", { className: styles$1.limitText, "data-testid": "ATL-AutocompleteRebuilt-limitText" },
|
|
1287
|
-
React__default.createElement(Typography, { size: "small", element: "span" }, limitSelectionText(hiddenCount))))));
|
|
1288
|
-
}
|
|
1289
|
-
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, }) {
|
|
1290
|
-
var _a;
|
|
1291
|
-
const [isFocused, setIsFocused] = useState(false);
|
|
1292
|
-
const hasValue = selectedValues.length > 0 || inputValue;
|
|
1293
|
-
const canDismissChip = !disabled && !readOnly;
|
|
1294
|
-
const handleFocusIn = useCallback(() => {
|
|
1295
|
-
setIsFocused(true);
|
|
1296
|
-
}, []);
|
|
1297
|
-
const handleFocusOut = useCallback((e) => {
|
|
1298
|
-
if (!e.currentTarget.contains(e.relatedTarget)) {
|
|
1299
|
-
setIsFocused(false);
|
|
1300
|
-
}
|
|
1301
|
-
}, []);
|
|
1302
|
-
return (React__default.createElement("div", { "data-testid": "ATL-AutocompleteRebuilt", onFocus: handleFocusIn, onBlur: handleFocusOut },
|
|
1303
|
-
React__default.createElement("div", { className: styles$1.multiSelectField, "data-testid": "ATL-AutocompleteRebuilt-multiSelectContainer" },
|
|
1304
|
-
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: () => {
|
|
1305
|
-
var _a;
|
|
1306
|
-
clearAll();
|
|
1307
|
-
(_a = internalInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
1308
|
-
}, type: "text", placeholder: placeholder, value: hasValue ? "has-value" : "", prefix: prefix, suffix: suffix, wrapperRef: formFieldRef },
|
|
1309
|
-
React__default.createElement("div", Object.assign({ ref: chipAreaRef, className: styles$1.chipArea, "data-testid": "ATL-AutocompleteRebuilt-chipArea" }, chipAreaEventProps),
|
|
1310
|
-
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 }),
|
|
1311
|
-
React__default.createElement("input", Object.assign({ ref: mergedInputRef, className: styles$1.inlineInput, id: inputId, value: inputValue, onChange: readOnly
|
|
1312
|
-
? undefined
|
|
1313
|
-
: e => onInputChangeFromUser(e.target.value), disabled: disabled, readOnly: readOnly, name: name, autoComplete: "off", autoFocus: autoFocus }, inputAriaProps, inputDataAttrs))))),
|
|
1314
|
-
children));
|
|
1315
|
-
}
|
|
1316
|
-
|
|
1317
|
-
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-"};
|
|
1318
|
-
|
|
1319
|
-
function useRepositionMenu(attachTo, visible, cssManagedVisibility) {
|
|
1320
|
-
const { refs, floatingStyles, update } = useFloating(Object.assign({ placement: "bottom", middleware: [
|
|
1321
|
-
offset(8),
|
|
1322
|
-
flip({ fallbackPlacements: ["top"] }),
|
|
1323
|
-
size({
|
|
1324
|
-
apply({ availableHeight, elements }) {
|
|
1325
|
-
const maxHeight = calculateMaxHeight(availableHeight, {
|
|
1326
|
-
maxHeight: AUTOCOMPLETE_MAX_HEIGHT$1,
|
|
1327
|
-
});
|
|
1328
|
-
Object.assign(elements.floating.style, {
|
|
1329
|
-
maxHeight: `${maxHeight}px`,
|
|
1330
|
-
});
|
|
1331
|
-
},
|
|
1332
|
-
}),
|
|
1333
|
-
], elements: {
|
|
1334
|
-
reference: attachTo,
|
|
1335
|
-
} }, (!cssManagedVisibility
|
|
1336
|
-
? {
|
|
1337
|
-
whileElementsMounted: autoUpdate,
|
|
1338
|
-
}
|
|
1339
|
-
: {})));
|
|
1340
|
-
// While DefaultMenu leverages conditional rendering, CustomMenu is hidden with CSS
|
|
1341
|
-
// We need to apply the correct update method to each case
|
|
1342
|
-
useSafeLayoutEffect(() => {
|
|
1343
|
-
if (cssManagedVisibility && visible && attachTo && refs.floating.current) {
|
|
1344
|
-
const cleanup = autoUpdate(attachTo, refs.floating.current, update);
|
|
1345
|
-
return cleanup;
|
|
1346
|
-
}
|
|
1347
|
-
return undefined;
|
|
1348
|
-
}, [
|
|
1349
|
-
cssManagedVisibility,
|
|
1350
|
-
visible,
|
|
1351
|
-
attachTo,
|
|
1352
|
-
refs.floating.current,
|
|
1353
|
-
update,
|
|
1354
|
-
autoUpdate,
|
|
1355
|
-
]);
|
|
1356
|
-
const targetWidth = attachTo === null || attachTo === void 0 ? void 0 : attachTo.clientWidth;
|
|
1357
|
-
return {
|
|
1358
|
-
menuRef: refs.floating.current,
|
|
1359
|
-
setMenuRef: refs.setFloating,
|
|
1360
|
-
targetWidth,
|
|
1361
|
-
styles: {
|
|
1362
|
-
float: floatingStyles,
|
|
1363
|
-
},
|
|
1364
|
-
};
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
function BaseAutocompleteMenuWrapperInternal({ setMenuRef, floatStyles, targetWidth, visible, children, }) {
|
|
1368
|
-
return (React__default.createElement("div", { className: classnames(styles.options, { [styles.visible]: visible }), ref: setMenuRef, style: Object.assign(Object.assign({}, floatStyles.float), { width: targetWidth }), "data-elevation": "elevated" }, children));
|
|
1369
|
-
}
|
|
1370
|
-
/**
|
|
1371
|
-
* Provides a wrapper for the Autocomplete menu that handles positioning and visibility.
|
|
1372
|
-
* @param attachTo - The element that the menu should be attached to.
|
|
1373
|
-
*/
|
|
1374
|
-
function useAutocompleteMenu({ attachTo, }) {
|
|
1375
|
-
const [menuRef, setMenuRef] = React__default.useState(null);
|
|
1376
|
-
const AutocompleteMenuWrapper = useCallback(({ children, visible, }) => {
|
|
1377
|
-
const menuFloatProps = useRepositionMenu(attachTo, visible, true);
|
|
1378
|
-
useEffect(() => {
|
|
1379
|
-
setMenuRef(menuFloatProps.menuRef);
|
|
1380
|
-
}, [menuFloatProps.menuRef]);
|
|
1381
|
-
return (React__default.createElement(BaseAutocompleteMenuWrapper, { floatStyles: menuFloatProps.styles, setMenuRef: menuFloatProps.setMenuRef, targetWidth: menuFloatProps.targetWidth, visible: visible }, children));
|
|
1382
|
-
}, [attachTo]);
|
|
1383
|
-
return { MenuWrapper: AutocompleteMenuWrapper, menuRef };
|
|
1384
|
-
}
|
|
1385
|
-
function BaseAutocompleteMenuWrapper(props) {
|
|
1386
|
-
const mounted = useIsMounted();
|
|
1387
|
-
const menu = React__default.createElement(BaseAutocompleteMenuWrapperInternal, Object.assign({}, props));
|
|
1388
|
-
return mounted.current ? React__default.createElement(FloatingPortal, null, menu) : menu;
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
function isOptionSelected(selectedOption, option) {
|
|
1392
|
-
return Boolean(selectedOption && selectedOption.value === option.value);
|
|
1393
|
-
}
|
|
1394
|
-
/**
|
|
1395
|
-
* Helper function to determine if the option is a group. This is used to
|
|
1396
|
-
* determine if the option contains a list of options for rendering Section
|
|
1397
|
-
* Labels in the Autocomplete component.
|
|
1398
|
-
*/
|
|
1399
|
-
function isOptionGroup(option) {
|
|
1400
|
-
return "options" in option;
|
|
1401
|
-
}
|
|
1402
|
-
|
|
1403
|
-
/**
|
|
1404
|
-
* The rendering of the default MenuOption
|
|
1405
|
-
*/
|
|
1406
|
-
function MenuOption({ isHighlighted, option, onOptionSelect, isSelected, addSeparators, UNSAFE_className = {}, UNSAFE_style = {}, }) {
|
|
1407
|
-
if (isOptionGroup(option)) {
|
|
1408
|
-
return (React__default.createElement(MenuGroupOptions, { UNSAFE_className: UNSAFE_className.groupOption, option: option, UNSAFE_style: UNSAFE_style.groupOption }));
|
|
1409
|
-
}
|
|
1410
|
-
return (React__default.createElement(BaseMenuOption, { UNSAFE_className: UNSAFE_className.option, UNSAFE_style: UNSAFE_style.option, option: option, isHighlighted: isHighlighted, onOptionSelect: onOptionSelect, addSeparators: addSeparators },
|
|
1411
|
-
React__default.createElement(MenuOptionContent, { option: option, isSelected: isSelected, UNSAFE_className: UNSAFE_className.content, UNSAFE_style: UNSAFE_style.content })));
|
|
1412
|
-
}
|
|
1413
|
-
function MenuOptionContent({ option, isSelected, UNSAFE_className = {}, UNSAFE_style = {}, }) {
|
|
1414
|
-
const iconClassName = classnames(styles.icon, UNSAFE_className.icon);
|
|
1415
|
-
const textClassName = classnames(styles.text, UNSAFE_className.text);
|
|
1416
|
-
const labelClassName = classnames(styles.label, UNSAFE_className.label);
|
|
1417
|
-
const detailsClassName = classnames(styles.details, UNSAFE_className.details);
|
|
1418
|
-
return (React__default.createElement(React__default.Fragment, null,
|
|
1419
|
-
React__default.createElement("div", { className: iconClassName, style: UNSAFE_style.icon }, isSelected && React__default.createElement(Icon, { name: "checkmark", size: "small" })),
|
|
1420
|
-
React__default.createElement("div", { className: textClassName, style: UNSAFE_style.text },
|
|
1421
|
-
React__default.createElement("div", { className: labelClassName, style: UNSAFE_style.label },
|
|
1422
|
-
React__default.createElement(Text, null, option.label),
|
|
1423
|
-
option.description !== undefined && (React__default.createElement(Text, { variation: "subdued" }, option.description))),
|
|
1424
|
-
option.details !== undefined && (React__default.createElement("div", { className: detailsClassName, style: UNSAFE_style.details },
|
|
1425
|
-
React__default.createElement(Text, null, option.details))))));
|
|
1426
|
-
}
|
|
1427
|
-
/**
|
|
1428
|
-
* The rendering of the default MenuGroupOption
|
|
1429
|
-
*/
|
|
1430
|
-
function MenuGroupOptions({ option, UNSAFE_className = {}, UNSAFE_style = {}, }) {
|
|
1431
|
-
return (React__default.createElement(BaseMenuGroupOption, { UNSAFE_className: UNSAFE_className.heading, UNSAFE_style: UNSAFE_style.heading },
|
|
1432
|
-
React__default.createElement(Heading, { level: 5 }, option.label)));
|
|
1433
|
-
}
|
|
1434
|
-
function BaseMenuGroupOption({ children, UNSAFE_className = "", UNSAFE_style = {}, }) {
|
|
1435
|
-
const headingClassName = classnames(styles.heading, UNSAFE_className);
|
|
1436
|
-
return (React__default.createElement("div", { className: headingClassName, style: UNSAFE_style }, children));
|
|
1437
|
-
}
|
|
1438
|
-
/**
|
|
1439
|
-
* Renders the base option component. The component takes children and renders them inside a button.
|
|
1440
|
-
*/
|
|
1441
|
-
function BaseMenuOption({ option, isHighlighted, onOptionSelect, addSeparators, children, UNSAFE_className = "", UNSAFE_style = {}, }) {
|
|
1442
|
-
const optionClass = classnames(styles.option, {
|
|
1443
|
-
[styles.active]: isHighlighted,
|
|
1444
|
-
[styles.separator]: addSeparators,
|
|
1445
|
-
}, UNSAFE_className);
|
|
1446
|
-
return (React__default.createElement("button", { role: "option", type: "button", className: optionClass, onMouseDown: onOptionSelect === null || onOptionSelect === void 0 ? void 0 : onOptionSelect.bind(undefined, option), style: UNSAFE_style }, children));
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1449
|
-
var KeyboardAction;
|
|
1450
|
-
(function (KeyboardAction) {
|
|
1451
|
-
KeyboardAction[KeyboardAction["Previous"] = -1] = "Previous";
|
|
1452
|
-
KeyboardAction[KeyboardAction["Next"] = 1] = "Next";
|
|
1453
|
-
KeyboardAction[KeyboardAction["Select"] = 0] = "Select";
|
|
1454
|
-
})(KeyboardAction || (KeyboardAction = {}));
|
|
1455
|
-
/**
|
|
1456
|
-
* Hook to handle custom keyboard navigation for the Autocomplete component.
|
|
1457
|
-
* Use this hook if you are using components in the menu that aren't MenuOption or BaseMenuOption.
|
|
1458
|
-
*/
|
|
1459
|
-
function useCustomKeyboardNavigation({ onRequestHighlightChange, }) {
|
|
1460
|
-
useOnKeyDown(event => {
|
|
1461
|
-
onRequestHighlightChange === null || onRequestHighlightChange === void 0 ? void 0 : onRequestHighlightChange(event, KeyboardAction.Next);
|
|
1462
|
-
}, "ArrowDown");
|
|
1463
|
-
useOnKeyDown(event => {
|
|
1464
|
-
onRequestHighlightChange === null || onRequestHighlightChange === void 0 ? void 0 : onRequestHighlightChange(event, KeyboardAction.Previous);
|
|
1465
|
-
}, "ArrowUp");
|
|
1466
|
-
useOnKeyDown(event => {
|
|
1467
|
-
onRequestHighlightChange === null || onRequestHighlightChange === void 0 ? void 0 : onRequestHighlightChange(event, KeyboardAction.Select);
|
|
1468
|
-
}, "Enter");
|
|
1469
|
-
}
|
|
1470
|
-
/**
|
|
1471
|
-
* Hook to handle keyboard navigation for the Menu in the Autocomplete component.
|
|
1472
|
-
* If using components in the menu that aren't MenuOption or BaseMenuOption, you should use the `useCustomKeyboardNavigation` hook.
|
|
1473
|
-
*/
|
|
1474
|
-
function useKeyboardNavigation({ options, onOptionSelect, menuRef, visible, }) {
|
|
1475
|
-
const [highlightedIndex, setHighlightedIndex] = useState(0);
|
|
1476
|
-
const initialHighlight = options.some(isOptionGroup) ? 1 : 0;
|
|
1477
|
-
useEffect(() => setHighlightedIndex(initialHighlight), [options]);
|
|
1478
|
-
useEffect(() => {
|
|
1479
|
-
var _a, _b;
|
|
1480
|
-
(_b = (_a = menuRef === null || menuRef === void 0 ? void 0 : menuRef.children[highlightedIndex]) === null || _a === void 0 ? void 0 : _a.scrollIntoView) === null || _b === void 0 ? void 0 : _b.call(_a, {
|
|
1481
|
-
behavior: "smooth",
|
|
1482
|
-
block: "nearest",
|
|
1483
|
-
inline: "start",
|
|
1484
|
-
});
|
|
1485
|
-
}, [highlightedIndex]);
|
|
1486
|
-
const onRequestHighlightChange = useCallback((event, direction) => {
|
|
1487
|
-
if (!visible)
|
|
1488
|
-
return;
|
|
1489
|
-
const indexChange = getRequestedIndexChange({
|
|
1490
|
-
event,
|
|
1491
|
-
options,
|
|
1492
|
-
direction,
|
|
1493
|
-
highlightedIndex,
|
|
1494
|
-
});
|
|
1495
|
-
switch (direction) {
|
|
1496
|
-
case KeyboardAction.Previous:
|
|
1497
|
-
setHighlightedIndex(prev => Math.max(0, prev + indexChange));
|
|
1498
|
-
break;
|
|
1499
|
-
case KeyboardAction.Next:
|
|
1500
|
-
setHighlightedIndex(prev => Math.min(options.length - 1, prev + indexChange));
|
|
1501
|
-
break;
|
|
1502
|
-
case KeyboardAction.Select:
|
|
1503
|
-
if (isOptionGroup(options[highlightedIndex]))
|
|
1504
|
-
return;
|
|
1505
|
-
onOptionSelect(options[highlightedIndex]);
|
|
1506
|
-
break;
|
|
1507
|
-
}
|
|
1508
|
-
}, [highlightedIndex, options, onOptionSelect, visible]);
|
|
1509
|
-
useCustomKeyboardNavigation({ onRequestHighlightChange });
|
|
1510
|
-
return { highlightedIndex };
|
|
1511
|
-
}
|
|
1512
|
-
/**
|
|
1513
|
-
* Function to get the requested index change based on the current highlighted index and the direction of the keyboard action.
|
|
1514
|
-
* Accounts for groups in the options array.
|
|
1515
|
-
*/
|
|
1516
|
-
function getRequestedIndexChange({ event, options, direction, highlightedIndex, }) {
|
|
1517
|
-
event.preventDefault();
|
|
1518
|
-
const requestedIndex = options[highlightedIndex + direction];
|
|
1519
|
-
return requestedIndex && isOptionGroup(requestedIndex)
|
|
1520
|
-
? direction + direction
|
|
1521
|
-
: direction;
|
|
1522
|
-
}
|
|
1523
|
-
|
|
1524
|
-
/**
|
|
1525
|
-
* Renders the default Menu for the Autocomplete component.
|
|
1526
|
-
*/
|
|
1527
|
-
function DefaultMenu({ options, selectedOption, onOptionSelect, attachTo, visible, }) {
|
|
1528
|
-
const { menuRef, setMenuRef, styles: floatStyles, targetWidth, } = useRepositionMenu(attachTo, visible, false);
|
|
1529
|
-
const detectSeparatorCondition = (option) => option.description || option.details;
|
|
1530
|
-
const addSeparators = options.some(detectSeparatorCondition);
|
|
1531
|
-
const { highlightedIndex } = useKeyboardNavigation({
|
|
1532
|
-
onOptionSelect,
|
|
1533
|
-
options,
|
|
1534
|
-
visible,
|
|
1535
|
-
menuRef,
|
|
1536
|
-
});
|
|
1537
|
-
return (React__default.createElement(BaseAutocompleteMenuWrapper, { setMenuRef, floatStyles, targetWidth, visible }, options === null || options === void 0 ? void 0 : options.map((option, index) => {
|
|
1538
|
-
return (React__default.createElement(MenuOption, { key: index, option: option, isHighlighted: index === highlightedIndex, onOptionSelect: onOptionSelect, isSelected: isOptionSelected(selectedOption, option), addSeparators: addSeparators }));
|
|
1539
|
-
})));
|
|
1540
|
-
}
|
|
1541
|
-
|
|
1542
|
-
function Menu({ options, selectedOption, onOptionSelect, inputFocused, attachTo, inputRef, customRenderMenu, }) {
|
|
1543
|
-
if (customRenderMenu) {
|
|
1544
|
-
return (React__default.createElement(CustomMenu, { attachTo: attachTo, inputFocused: inputFocused, inputRef: inputRef, customRenderMenu: customRenderMenu, options: options, onOptionSelect: onOptionSelect, selectedOption: selectedOption }));
|
|
1545
|
-
}
|
|
1546
|
-
if (!inputFocused || !options.length)
|
|
1547
|
-
return null;
|
|
1548
|
-
return (React__default.createElement(DefaultMenu, { attachTo: attachTo, options: options, onOptionSelect: onOptionSelect, selectedOption: selectedOption, visible: inputFocused }));
|
|
1549
|
-
}
|
|
1550
|
-
/**
|
|
1551
|
-
* Renders the custom Menu for the Autocomplete component.
|
|
1552
|
-
* Provides the menuRef and MenuWrapper to the customRenderMenu function.
|
|
1553
|
-
*/
|
|
1554
|
-
function CustomMenu({ options, selectedOption, onOptionSelect, customRenderMenu, attachTo, inputFocused, inputRef, }) {
|
|
1555
|
-
const { MenuWrapper, menuRef } = useAutocompleteMenu({ attachTo });
|
|
1556
|
-
const menuContent = customRenderMenu({
|
|
1557
|
-
options,
|
|
1558
|
-
menuRef,
|
|
1559
|
-
onOptionSelect,
|
|
1560
|
-
selectedOption,
|
|
1561
|
-
inputFocused,
|
|
1562
|
-
MenuWrapper,
|
|
1563
|
-
inputRef,
|
|
1564
|
-
});
|
|
1565
|
-
return menuContent;
|
|
1566
|
-
}
|
|
1567
|
-
|
|
1568
|
-
// Max statements increased to make room for the debounce functions
|
|
1569
|
-
// eslint-disable-next-line max-statements
|
|
1570
|
-
function AutocompleteInternal(_a, ref) {
|
|
1571
|
-
var _b;
|
|
1572
|
-
var { initialOptions = [], value, allowFreeForm = true, size = undefined, debounce: debounceRate = 300, onChange, getOptions, placeholder, onBlur, onFocus, customRenderMenu } = _a, inputProps = __rest(_a, ["initialOptions", "value", "allowFreeForm", "size", "debounce", "onChange", "getOptions", "placeholder", "onBlur", "onFocus", "customRenderMenu"]);
|
|
1573
|
-
const initialOptionsMemo = useMemo(() => mapToOptions(initialOptions), [initialOptions]);
|
|
1574
|
-
const [options, setOptions] = useState(initialOptionsMemo);
|
|
1575
|
-
const [inputFocused, setInputFocused] = useState(false);
|
|
1576
|
-
const [inputText, setInputText] = useState((_b = value === null || value === void 0 ? void 0 : value.label) !== null && _b !== void 0 ? _b : "");
|
|
1577
|
-
const [autocompleteRef, setAutocompleteRef] = useState(null);
|
|
1578
|
-
const delayedSearch = useDebounce(updateSearch, debounceRate);
|
|
1579
|
-
const inputRef = useRef(null);
|
|
1580
|
-
useEffect(() => {
|
|
1581
|
-
delayedSearch();
|
|
1582
|
-
}, [inputText]);
|
|
1583
|
-
useEffect(() => {
|
|
1584
|
-
var _a;
|
|
1585
|
-
updateInput((_a = value === null || value === void 0 ? void 0 : value.label) !== null && _a !== void 0 ? _a : "");
|
|
1586
|
-
}, [value]);
|
|
1587
|
-
return (React__default.createElement("div", { className: styles.autocomplete, ref: setAutocompleteRef },
|
|
1588
|
-
React__default.createElement(InputText, Object.assign({ ref: mergeRefs([ref, inputRef]), autoComplete: "off", size: size, value: inputText, onChange: handleInputChange, placeholder: placeholder, onFocus: handleInputFocus, onBlur: handleInputBlur }, inputProps)),
|
|
1589
|
-
React__default.createElement(Menu, { attachTo: autocompleteRef, inputRef: inputRef, inputFocused: inputFocused, options: options, customRenderMenu: customRenderMenu, selectedOption: value, onOptionSelect: handleMenuChange })));
|
|
1590
|
-
function updateInput(newText) {
|
|
1591
|
-
setInputText(newText);
|
|
1592
|
-
if (newText === "") {
|
|
1593
|
-
setOptions(mapToOptions(initialOptions));
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
function updateSearch() {
|
|
1597
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1598
|
-
const updatedOptions = yield getOptions(inputText);
|
|
1599
|
-
const filteredOptions = updatedOptions.filter(option => isOptionGroup(option) ? option.options.length > 0 : true);
|
|
1600
|
-
setOptions(mapToOptions(filteredOptions));
|
|
1601
|
-
});
|
|
1602
|
-
}
|
|
1603
|
-
function handleMenuChange(chosenOption) {
|
|
1604
|
-
var _a;
|
|
1605
|
-
onChange(chosenOption);
|
|
1606
|
-
updateInput((_a = chosenOption === null || chosenOption === void 0 ? void 0 : chosenOption.label) !== null && _a !== void 0 ? _a : "");
|
|
1607
|
-
setInputFocused(false);
|
|
1608
|
-
}
|
|
1609
|
-
function handleInputChange(newText) {
|
|
1610
|
-
updateInput(newText);
|
|
1611
|
-
if (allowFreeForm) {
|
|
1612
|
-
onChange({ label: newText });
|
|
1613
|
-
}
|
|
1614
|
-
}
|
|
1615
|
-
function handleInputBlur() {
|
|
1616
|
-
setInputFocused(false);
|
|
1617
|
-
if (value == undefined || value.label !== inputText) {
|
|
1618
|
-
setInputText("");
|
|
1619
|
-
onChange(undefined);
|
|
1620
|
-
}
|
|
1621
|
-
onBlur && onBlur();
|
|
1622
|
-
}
|
|
1623
|
-
function handleInputFocus() {
|
|
1624
|
-
setInputFocused(true);
|
|
1625
|
-
if (onFocus) {
|
|
1626
|
-
onFocus();
|
|
1627
|
-
}
|
|
1628
|
-
}
|
|
1629
|
-
}
|
|
1630
|
-
function mapToOptions(items) {
|
|
1631
|
-
const retVal = items.reduce((result, item) => {
|
|
1632
|
-
result.push(item);
|
|
1633
|
-
if (isOptionGroup(item) && item.options) {
|
|
1634
|
-
result = result.concat(item.options);
|
|
1635
|
-
}
|
|
1636
|
-
return result;
|
|
1637
|
-
}, []);
|
|
1638
|
-
return retVal;
|
|
1639
|
-
}
|
|
1640
|
-
// Casts the Generics to the forward ref so autocomplete works as expected for consumers
|
|
1641
|
-
const Autocomplete$1 = forwardRef(AutocompleteInternal);
|
|
27
|
+
import '../FormFieldPostFix-es.js';
|
|
28
|
+
import '../Spinner-es.js';
|
|
29
|
+
import '../useAtlantisFormFieldName-es.js';
|
|
30
|
+
import 'react-hook-form';
|
|
31
|
+
import '../mergeRefs-es.js';
|
|
32
|
+
import '../filterDataAttributes-es.js';
|
|
1642
33
|
|
|
1643
34
|
// Convenience builder helpers (optional usage)
|
|
1644
35
|
// Helper to improve inference from inline menu literals
|
|
@@ -1646,17 +37,4 @@ function defineMenu(menu) {
|
|
|
1646
37
|
return menu;
|
|
1647
38
|
}
|
|
1648
39
|
|
|
1649
|
-
|
|
1650
|
-
return props.version === 2;
|
|
1651
|
-
}
|
|
1652
|
-
function AutocompleteShim(props, ref) {
|
|
1653
|
-
if (isNewAutocompleteProps(props)) {
|
|
1654
|
-
return (React__default.createElement(AutocompleteRebuilt, Object.assign({}, props, { ref: ref })));
|
|
1655
|
-
}
|
|
1656
|
-
return (React__default.createElement(Autocomplete$1, Object.assign({}, props, { ref: ref })));
|
|
1657
|
-
}
|
|
1658
|
-
const AutocompleteForwarded = forwardRef(AutocompleteShim);
|
|
1659
|
-
AutocompleteForwarded.displayName = "Autocomplete";
|
|
1660
|
-
const Autocomplete = AutocompleteForwarded;
|
|
1661
|
-
|
|
1662
|
-
export { Autocomplete, BaseAutocompleteMenuWrapper, BaseMenuGroupOption, BaseMenuOption, KeyboardAction, MenuOption, defineMenu, getRequestedIndexChange, isOptionGroup, isOptionSelected, useAutocompleteMenu, useCustomKeyboardNavigation, useKeyboardNavigation, useRepositionMenu };
|
|
40
|
+
export { defineMenu };
|