@jobber/components 6.14.1-aidenfix--e9ec00c.0 → 6.15.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 +4 -48
- package/dist/Autocomplete/Autocomplete.types.d.ts +111 -0
- package/dist/Autocomplete/Autocomplete.utils.d.ts +8 -0
- package/dist/Autocomplete/Menu/DefaultMenu.d.ts +16 -0
- package/dist/Autocomplete/Menu/Menu.d.ts +2 -0
- package/dist/Autocomplete/Menu/MenuWrapper.d.ts +23 -0
- package/dist/Autocomplete/Option.d.ts +73 -11
- package/dist/Autocomplete/index.cjs +20 -5
- package/dist/Autocomplete/index.d.ts +6 -1
- package/dist/Autocomplete/index.mjs +6 -6
- package/dist/Autocomplete/useKeyboardNavigation.d.ts +35 -0
- package/dist/Autocomplete/useRepositionMenu.d.ts +23 -0
- package/dist/Autocomplete-cjs.js +238 -105
- package/dist/Autocomplete-es.js +229 -107
- package/dist/Popover/index.cjs +0 -1
- package/dist/Popover/index.mjs +0 -1
- package/dist/Popover-cjs.js +2 -7
- package/dist/Popover-es.js +2 -7
- package/dist/index.cjs +12 -12
- package/dist/index.mjs +9 -9
- package/dist/styles.css +10 -10
- package/package.json +2 -2
- package/dist/Autocomplete/Menu.d.ts +0 -14
package/dist/Autocomplete-cjs.js
CHANGED
|
@@ -3,107 +3,23 @@
|
|
|
3
3
|
var tslib_es6 = require('./tslib.es6-cjs.js');
|
|
4
4
|
var React = require('react');
|
|
5
5
|
var classnames = require('classnames');
|
|
6
|
+
var useIsMounted = require('./useIsMounted-cjs.js');
|
|
6
7
|
var ReactDOM = require('react-dom');
|
|
7
|
-
var reactPopper = require('react-popper');
|
|
8
|
-
var useOnKeyDown = require('./useOnKeyDown-cjs.js');
|
|
9
8
|
var useSafeLayoutEffect = require('./useSafeLayoutEffect-cjs.js');
|
|
10
|
-
var
|
|
9
|
+
var reactPopper = require('react-popper');
|
|
10
|
+
var Heading = require('./Heading-cjs.js');
|
|
11
11
|
var Text = require('./Text-cjs.js');
|
|
12
12
|
var Icon = require('./Icon-cjs.js');
|
|
13
|
-
var
|
|
13
|
+
var useOnKeyDown = require('./useOnKeyDown-cjs.js');
|
|
14
14
|
var InputText_index = require('./InputText/index.cjs');
|
|
15
15
|
var debounce = require('./debounce-cjs.js');
|
|
16
|
+
var FormField = require('./FormField-cjs.js');
|
|
16
17
|
|
|
17
|
-
var styles = {"autocomplete":"_7mObJiwfPh4-","options":"dL5JShAJlKM-","visible":"_2RzcnTdaPyc-","option":"y9zhi8Wr8QA-","active":"_3Xg49dtL1Q8-","separator":"LIeh390F3W8-","
|
|
18
|
+
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-"};
|
|
18
19
|
|
|
19
|
-
var IndexChange;
|
|
20
|
-
(function (IndexChange) {
|
|
21
|
-
IndexChange[IndexChange["Previous"] = -1] = "Previous";
|
|
22
|
-
IndexChange[IndexChange["Next"] = 1] = "Next";
|
|
23
|
-
})(IndexChange || (IndexChange = {}));
|
|
24
|
-
// Adding useIsMounted is what tipped this to 13 statements.
|
|
25
|
-
// Any additions beyond useIsMounted should probably see this component refactored a bit
|
|
26
|
-
// eslint-disable-next-line max-statements
|
|
27
|
-
function Menu({ visible, options, selectedOption, onOptionSelect, attachTo, }) {
|
|
28
|
-
const [highlightedIndex, setHighlightedIndex] = React.useState(0);
|
|
29
|
-
const { menuRef, setMenuRef, styles: popperStyles, attributes, targetWidth, } = useRepositionMenu(attachTo, visible);
|
|
30
|
-
const detectSeparatorCondition = (option) => option.description || option.details;
|
|
31
|
-
const detectGroups = (option) => option.options;
|
|
32
|
-
const addSeparators = options.some(detectSeparatorCondition);
|
|
33
|
-
const initialHighlight = options.some(detectGroups) ? 1 : 0;
|
|
34
|
-
setupKeyListeners();
|
|
35
|
-
React.useEffect(() => setHighlightedIndex(initialHighlight), [options]);
|
|
36
|
-
const mounted = useIsMounted.useIsMounted_2();
|
|
37
|
-
const menu = (React.createElement("div", Object.assign({ className: classnames(styles.options, { [styles.visible]: visible }), ref: setMenuRef, style: Object.assign(Object.assign({}, popperStyles.popper), { width: targetWidth }), "data-elevation": "elevated" }, attributes.popper), options.map((option, index) => {
|
|
38
|
-
const optionClass = classnames(styles.option, {
|
|
39
|
-
[styles.active]: index === highlightedIndex,
|
|
40
|
-
[styles.separator]: addSeparators,
|
|
41
|
-
});
|
|
42
|
-
if (isGroup(option)) {
|
|
43
|
-
return (React.createElement("div", { key: option.label, className: styles.heading },
|
|
44
|
-
React.createElement(Heading.Heading, { level: 5 }, option.label)));
|
|
45
|
-
}
|
|
46
|
-
return (React.createElement("button", { className: optionClass, key: option.value, onMouseDown: onOptionSelect.bind(undefined, option) },
|
|
47
|
-
React.createElement("div", { className: styles.icon }, isOptionSelected(selectedOption, option) && (React.createElement(Icon.Icon, { name: "checkmark", size: "small" }))),
|
|
48
|
-
React.createElement("div", { className: styles.text },
|
|
49
|
-
React.createElement("div", { className: styles.label },
|
|
50
|
-
React.createElement(Text.Text, null, option.label),
|
|
51
|
-
option.description !== undefined && (React.createElement(Text.Text, { variation: "subdued" }, option.description))),
|
|
52
|
-
option.details !== undefined && (React.createElement("div", { className: styles.details },
|
|
53
|
-
React.createElement(Text.Text, null, option.details))))));
|
|
54
|
-
})));
|
|
55
|
-
return mounted.current ? ReactDOM.createPortal(menu, document.body) : menu;
|
|
56
|
-
function setupKeyListeners() {
|
|
57
|
-
React.useEffect(() => {
|
|
58
|
-
var _a, _b;
|
|
59
|
-
(_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, {
|
|
60
|
-
behavior: "smooth",
|
|
61
|
-
block: "nearest",
|
|
62
|
-
inline: "start",
|
|
63
|
-
});
|
|
64
|
-
}, [highlightedIndex]);
|
|
65
|
-
useOnKeyDown.useOnKeyDown_2((event) => {
|
|
66
|
-
const indexChange = arrowKeyPress(event, IndexChange.Next);
|
|
67
|
-
if (indexChange) {
|
|
68
|
-
setHighlightedIndex(Math.min(options.length - 1, highlightedIndex + indexChange));
|
|
69
|
-
}
|
|
70
|
-
}, "ArrowDown");
|
|
71
|
-
useOnKeyDown.useOnKeyDown_2((event) => {
|
|
72
|
-
const indexChange = arrowKeyPress(event, IndexChange.Previous);
|
|
73
|
-
if (indexChange) {
|
|
74
|
-
setHighlightedIndex(Math.max(0, highlightedIndex + indexChange));
|
|
75
|
-
}
|
|
76
|
-
}, "ArrowUp");
|
|
77
|
-
useOnKeyDown.useOnKeyDown_2((event) => {
|
|
78
|
-
if (!visible)
|
|
79
|
-
return;
|
|
80
|
-
if (isGroup(options[highlightedIndex]))
|
|
81
|
-
return;
|
|
82
|
-
event.preventDefault();
|
|
83
|
-
onOptionSelect(options[highlightedIndex]);
|
|
84
|
-
}, "Enter");
|
|
85
|
-
}
|
|
86
|
-
function arrowKeyPress(event, direction) {
|
|
87
|
-
if (!visible)
|
|
88
|
-
return;
|
|
89
|
-
event.preventDefault();
|
|
90
|
-
const requestedIndex = options[highlightedIndex + direction];
|
|
91
|
-
return requestedIndex && isGroup(requestedIndex)
|
|
92
|
-
? direction + direction
|
|
93
|
-
: direction;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
function isOptionSelected(selectedOption, option) {
|
|
97
|
-
return selectedOption && selectedOption.value === option.value;
|
|
98
|
-
}
|
|
99
|
-
function isGroup(option) {
|
|
100
|
-
if (option.options)
|
|
101
|
-
return true;
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
20
|
function useRepositionMenu(attachTo, visible = false) {
|
|
105
21
|
var _a;
|
|
106
|
-
const [menuRef, setMenuRef] = React.useState();
|
|
22
|
+
const [menuRef, setMenuRef] = React.useState(null);
|
|
107
23
|
const popper = reactPopper.usePopper(attachTo.current, menuRef, {
|
|
108
24
|
modifiers: [
|
|
109
25
|
{ name: "offset", options: { offset: [0, 8] } },
|
|
@@ -118,6 +34,207 @@ function useRepositionMenu(attachTo, visible = false) {
|
|
|
118
34
|
return Object.assign(Object.assign({}, popper), { menuRef, setMenuRef, targetWidth });
|
|
119
35
|
}
|
|
120
36
|
|
|
37
|
+
function BaseAutocompleteMenuWrapperInternal({ setMenuRef, popperStyles, attributes, targetWidth, visible, children, }) {
|
|
38
|
+
return (React.createElement("div", Object.assign({ className: classnames(styles.options, { [styles.visible]: visible }), ref: setMenuRef, style: Object.assign(Object.assign({}, popperStyles.popper), { width: targetWidth }), "data-elevation": "elevated" }, attributes.popper), children));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Provides a wrapper for the Autocomplete menu that handles positioning and visibility.
|
|
42
|
+
* @param attachTo - The element that the menu should be attached to.
|
|
43
|
+
*/
|
|
44
|
+
function useAutocompleteMenu({ attachTo, }) {
|
|
45
|
+
const [menuRef, setMenuRef] = React.useState(null);
|
|
46
|
+
const AutocompleteMenuWrapper = React.useCallback(({ children, visible, }) => {
|
|
47
|
+
const menuPopperProps = useRepositionMenu(attachTo, visible);
|
|
48
|
+
React.useEffect(() => {
|
|
49
|
+
setMenuRef(menuPopperProps.menuRef);
|
|
50
|
+
}, [menuPopperProps.menuRef]);
|
|
51
|
+
return (React.createElement(BaseAutocompleteMenuWrapper, { attributes: menuPopperProps.attributes, popperStyles: menuPopperProps.styles, setMenuRef: menuPopperProps.setMenuRef, targetWidth: menuPopperProps.targetWidth, visible: visible }, children));
|
|
52
|
+
}, [attachTo]);
|
|
53
|
+
return { MenuWrapper: AutocompleteMenuWrapper, menuRef };
|
|
54
|
+
}
|
|
55
|
+
function BaseAutocompleteMenuWrapper(props) {
|
|
56
|
+
const mounted = useIsMounted.useIsMounted_2();
|
|
57
|
+
const menu = React.createElement(BaseAutocompleteMenuWrapperInternal, Object.assign({}, props));
|
|
58
|
+
return mounted.current ? ReactDOM.createPortal(menu, document.body) : menu;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function isOptionSelected(selectedOption, option) {
|
|
62
|
+
return Boolean(selectedOption && selectedOption.value === option.value);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Helper function to determine if the option is a group. This is used to
|
|
66
|
+
* determine if the option contains a list of options for rendering Section
|
|
67
|
+
* Labels in the Autocomplete component.
|
|
68
|
+
*/
|
|
69
|
+
function isOptionGroup(option) {
|
|
70
|
+
return "options" in option;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The rendering of the default MenuOption
|
|
75
|
+
*/
|
|
76
|
+
function MenuOption({ isHighlighted, option, onOptionSelect, isSelected, addSeparators, UNSAFE_className = {}, UNSAFE_style = {}, }) {
|
|
77
|
+
if (isOptionGroup(option)) {
|
|
78
|
+
return (React.createElement(MenuGroupOptions, { UNSAFE_className: UNSAFE_className.groupOption, option: option, UNSAFE_style: UNSAFE_style.groupOption }));
|
|
79
|
+
}
|
|
80
|
+
return (React.createElement(BaseMenuOption, { UNSAFE_className: UNSAFE_className.option, UNSAFE_style: UNSAFE_style.option, option: option, isHighlighted: isHighlighted, onOptionSelect: onOptionSelect, addSeparators: addSeparators },
|
|
81
|
+
React.createElement(MenuOptionContent, { option: option, isSelected: isSelected, UNSAFE_className: UNSAFE_className.content, UNSAFE_style: UNSAFE_style.content })));
|
|
82
|
+
}
|
|
83
|
+
function MenuOptionContent({ option, isSelected, UNSAFE_className = {}, UNSAFE_style = {}, }) {
|
|
84
|
+
const iconClassName = classnames(styles.icon, UNSAFE_className.icon);
|
|
85
|
+
const textClassName = classnames(styles.text, UNSAFE_className.text);
|
|
86
|
+
const labelClassName = classnames(styles.label, UNSAFE_className.label);
|
|
87
|
+
const detailsClassName = classnames(styles.details, UNSAFE_className.details);
|
|
88
|
+
return (React.createElement(React.Fragment, null,
|
|
89
|
+
React.createElement("div", { className: iconClassName, style: UNSAFE_style.icon }, isSelected && React.createElement(Icon.Icon, { name: "checkmark", size: "small" })),
|
|
90
|
+
React.createElement("div", { className: textClassName, style: UNSAFE_style.text },
|
|
91
|
+
React.createElement("div", { className: labelClassName, style: UNSAFE_style.label },
|
|
92
|
+
React.createElement(Text.Text, null, option.label),
|
|
93
|
+
option.description !== undefined && (React.createElement(Text.Text, { variation: "subdued" }, option.description))),
|
|
94
|
+
option.details !== undefined && (React.createElement("div", { className: detailsClassName, style: UNSAFE_style.details },
|
|
95
|
+
React.createElement(Text.Text, null, option.details))))));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The rendering of the default MenuGroupOption
|
|
99
|
+
*/
|
|
100
|
+
function MenuGroupOptions({ option, UNSAFE_className = {}, UNSAFE_style = {}, }) {
|
|
101
|
+
return (React.createElement(BaseMenuGroupOption, { UNSAFE_className: UNSAFE_className.heading, UNSAFE_style: UNSAFE_style.heading },
|
|
102
|
+
React.createElement(Heading.Heading, { level: 5 }, option.label)));
|
|
103
|
+
}
|
|
104
|
+
function BaseMenuGroupOption({ children, UNSAFE_className = "", UNSAFE_style = {}, }) {
|
|
105
|
+
const headingClassName = classnames(styles.heading, UNSAFE_className);
|
|
106
|
+
return (React.createElement("div", { className: headingClassName, style: UNSAFE_style }, children));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Renders the base option component. The component takes children and renders them inside a button.
|
|
110
|
+
*/
|
|
111
|
+
function BaseMenuOption({ option, isHighlighted, onOptionSelect, addSeparators, children, UNSAFE_className = "", UNSAFE_style = {}, }) {
|
|
112
|
+
const optionClass = classnames(styles.option, {
|
|
113
|
+
[styles.active]: isHighlighted,
|
|
114
|
+
[styles.separator]: addSeparators,
|
|
115
|
+
}, UNSAFE_className);
|
|
116
|
+
return (React.createElement("button", { role: "option", type: "button", className: optionClass, onMouseDown: onOptionSelect === null || onOptionSelect === void 0 ? void 0 : onOptionSelect.bind(undefined, option), style: UNSAFE_style }, children));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
exports.KeyboardAction = void 0;
|
|
120
|
+
(function (KeyboardAction) {
|
|
121
|
+
KeyboardAction[KeyboardAction["Previous"] = -1] = "Previous";
|
|
122
|
+
KeyboardAction[KeyboardAction["Next"] = 1] = "Next";
|
|
123
|
+
KeyboardAction[KeyboardAction["Select"] = 0] = "Select";
|
|
124
|
+
})(exports.KeyboardAction || (exports.KeyboardAction = {}));
|
|
125
|
+
/**
|
|
126
|
+
* Hook to handle custom keyboard navigation for the Autocomplete component.
|
|
127
|
+
* Use this hook if you are using components in the menu that aren't MenuOption or BaseMenuOption.
|
|
128
|
+
*/
|
|
129
|
+
function useCustomKeyboardNavigation({ onRequestHighlightChange, }) {
|
|
130
|
+
useOnKeyDown.useOnKeyDown_2((event) => {
|
|
131
|
+
onRequestHighlightChange === null || onRequestHighlightChange === void 0 ? void 0 : onRequestHighlightChange(event, exports.KeyboardAction.Next);
|
|
132
|
+
}, "ArrowDown");
|
|
133
|
+
useOnKeyDown.useOnKeyDown_2((event) => {
|
|
134
|
+
onRequestHighlightChange === null || onRequestHighlightChange === void 0 ? void 0 : onRequestHighlightChange(event, exports.KeyboardAction.Previous);
|
|
135
|
+
}, "ArrowUp");
|
|
136
|
+
useOnKeyDown.useOnKeyDown_2((event) => {
|
|
137
|
+
onRequestHighlightChange === null || onRequestHighlightChange === void 0 ? void 0 : onRequestHighlightChange(event, exports.KeyboardAction.Select);
|
|
138
|
+
}, "Enter");
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Hook to handle keyboard navigation for the Menu in the Autocomplete component.
|
|
142
|
+
* If using components in the menu that aren't MenuOption or BaseMenuOption, you should use the `useCustomKeyboardNavigation` hook.
|
|
143
|
+
*/
|
|
144
|
+
function useKeyboardNavigation({ options, onOptionSelect, menuRef, visible, }) {
|
|
145
|
+
const [highlightedIndex, setHighlightedIndex] = React.useState(0);
|
|
146
|
+
const initialHighlight = options.some(isOptionGroup) ? 1 : 0;
|
|
147
|
+
React.useEffect(() => setHighlightedIndex(initialHighlight), [options]);
|
|
148
|
+
React.useEffect(() => {
|
|
149
|
+
var _a, _b;
|
|
150
|
+
(_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, {
|
|
151
|
+
behavior: "smooth",
|
|
152
|
+
block: "nearest",
|
|
153
|
+
inline: "start",
|
|
154
|
+
});
|
|
155
|
+
}, [highlightedIndex]);
|
|
156
|
+
const onRequestHighlightChange = React.useCallback((event, direction) => {
|
|
157
|
+
if (!visible)
|
|
158
|
+
return;
|
|
159
|
+
const indexChange = getRequestedIndexChange({
|
|
160
|
+
event,
|
|
161
|
+
options,
|
|
162
|
+
direction,
|
|
163
|
+
highlightedIndex,
|
|
164
|
+
});
|
|
165
|
+
switch (direction) {
|
|
166
|
+
case exports.KeyboardAction.Previous:
|
|
167
|
+
setHighlightedIndex(prev => Math.max(0, prev + indexChange));
|
|
168
|
+
break;
|
|
169
|
+
case exports.KeyboardAction.Next:
|
|
170
|
+
setHighlightedIndex(prev => Math.min(options.length - 1, prev + indexChange));
|
|
171
|
+
break;
|
|
172
|
+
case exports.KeyboardAction.Select:
|
|
173
|
+
if (isOptionGroup(options[highlightedIndex]))
|
|
174
|
+
return;
|
|
175
|
+
onOptionSelect(options[highlightedIndex]);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}, [highlightedIndex, options, onOptionSelect, visible]);
|
|
179
|
+
useCustomKeyboardNavigation({ onRequestHighlightChange });
|
|
180
|
+
return { highlightedIndex };
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Function to get the requested index change based on the current highlighted index and the direction of the keyboard action.
|
|
184
|
+
* Accounts for groups in the options array.
|
|
185
|
+
*/
|
|
186
|
+
function getRequestedIndexChange({ event, options, direction, highlightedIndex, }) {
|
|
187
|
+
event.preventDefault();
|
|
188
|
+
const requestedIndex = options[highlightedIndex + direction];
|
|
189
|
+
return requestedIndex && isOptionGroup(requestedIndex)
|
|
190
|
+
? direction + direction
|
|
191
|
+
: direction;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Renders the default Menu for the Autocomplete component.
|
|
196
|
+
*/
|
|
197
|
+
function DefaultMenu({ options, selectedOption, onOptionSelect, attachTo, visible, }) {
|
|
198
|
+
const { menuRef, setMenuRef, styles: popperStyles, attributes, targetWidth, } = useRepositionMenu(attachTo, visible);
|
|
199
|
+
const detectSeparatorCondition = (option) => option.description || option.details;
|
|
200
|
+
const addSeparators = options.some(detectSeparatorCondition);
|
|
201
|
+
const { highlightedIndex } = useKeyboardNavigation({
|
|
202
|
+
onOptionSelect,
|
|
203
|
+
options,
|
|
204
|
+
visible,
|
|
205
|
+
menuRef,
|
|
206
|
+
});
|
|
207
|
+
return (React.createElement(BaseAutocompleteMenuWrapper, { setMenuRef, popperStyles, attributes, targetWidth, visible }, options === null || options === void 0 ? void 0 : options.map((option, index) => {
|
|
208
|
+
return (React.createElement(MenuOption, { key: index, option: option, isHighlighted: index === highlightedIndex, onOptionSelect: onOptionSelect, isSelected: isOptionSelected(selectedOption, option), addSeparators: addSeparators }));
|
|
209
|
+
})));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function Menu({ options, selectedOption, onOptionSelect, inputFocused, attachTo, inputRef, customRenderMenu, }) {
|
|
213
|
+
if (customRenderMenu) {
|
|
214
|
+
return (React.createElement(CustomMenu, { attachTo: attachTo, inputFocused: inputFocused, inputRef: inputRef, customRenderMenu: customRenderMenu, options: options, onOptionSelect: onOptionSelect, selectedOption: selectedOption }));
|
|
215
|
+
}
|
|
216
|
+
if (!inputFocused || !options.length)
|
|
217
|
+
return null;
|
|
218
|
+
return (React.createElement(DefaultMenu, { attachTo: attachTo, options: options, onOptionSelect: onOptionSelect, selectedOption: selectedOption, visible: inputFocused }));
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Renders the custom Menu for the Autocomplete component.
|
|
222
|
+
* Provides the menuRef and MenuWrapper to the customRenderMenu function.
|
|
223
|
+
*/
|
|
224
|
+
function CustomMenu({ options, selectedOption, onOptionSelect, customRenderMenu, attachTo, inputFocused, inputRef, }) {
|
|
225
|
+
const { MenuWrapper, menuRef } = useAutocompleteMenu({ attachTo });
|
|
226
|
+
const menuContent = customRenderMenu({
|
|
227
|
+
options,
|
|
228
|
+
menuRef,
|
|
229
|
+
onOptionSelect,
|
|
230
|
+
selectedOption,
|
|
231
|
+
inputFocused,
|
|
232
|
+
MenuWrapper,
|
|
233
|
+
inputRef,
|
|
234
|
+
});
|
|
235
|
+
return menuContent;
|
|
236
|
+
}
|
|
237
|
+
|
|
121
238
|
/**
|
|
122
239
|
* A hook to easily manage debounced functions, including their cleanup.
|
|
123
240
|
* @param func The function to debounce.
|
|
@@ -149,15 +266,17 @@ function useDebounce(func, wait, options) {
|
|
|
149
266
|
}
|
|
150
267
|
|
|
151
268
|
// Max statements increased to make room for the debounce functions
|
|
152
|
-
|
|
269
|
+
// eslint-disable-next-line max-statements
|
|
153
270
|
function AutocompleteInternal(_a, ref) {
|
|
154
271
|
var _b;
|
|
155
|
-
var { initialOptions = [], value, allowFreeForm = true, size = undefined, debounce: debounceRate = 300, onChange, getOptions, placeholder, onBlur, onFocus, validations } = _a, inputProps = tslib_es6.__rest(_a, ["initialOptions", "value", "allowFreeForm", "size", "debounce", "onChange", "getOptions", "placeholder", "onBlur", "onFocus", "validations"]);
|
|
156
|
-
const
|
|
157
|
-
const [
|
|
272
|
+
var { initialOptions = [], value, allowFreeForm = true, size = undefined, debounce: debounceRate = 300, onChange, getOptions, placeholder, onBlur, onFocus, validations, customRenderMenu } = _a, inputProps = tslib_es6.__rest(_a, ["initialOptions", "value", "allowFreeForm", "size", "debounce", "onChange", "getOptions", "placeholder", "onBlur", "onFocus", "validations", "customRenderMenu"]);
|
|
273
|
+
const initialOptionsMemo = React.useMemo(() => mapToOptions(initialOptions), [initialOptions]);
|
|
274
|
+
const [options, setOptions] = React.useState(initialOptionsMemo);
|
|
275
|
+
const [inputFocused, setInputFocused] = React.useState(false);
|
|
158
276
|
const [inputText, setInputText] = React.useState((_b = value === null || value === void 0 ? void 0 : value.label) !== null && _b !== void 0 ? _b : "");
|
|
159
277
|
const autocompleteRef = React.useRef(null);
|
|
160
278
|
const delayedSearch = useDebounce(updateSearch, debounceRate);
|
|
279
|
+
const inputRef = React.useRef(null);
|
|
161
280
|
React.useEffect(() => {
|
|
162
281
|
delayedSearch();
|
|
163
282
|
}, [inputText]);
|
|
@@ -166,8 +285,8 @@ function AutocompleteInternal(_a, ref) {
|
|
|
166
285
|
updateInput((_a = value === null || value === void 0 ? void 0 : value.label) !== null && _a !== void 0 ? _a : "");
|
|
167
286
|
}, [value]);
|
|
168
287
|
return (React.createElement("div", { className: styles.autocomplete, ref: autocompleteRef },
|
|
169
|
-
React.createElement(InputText_index.InputText, Object.assign({ ref: ref, autocomplete: false, size: size, value: inputText, onChange: handleInputChange, placeholder: placeholder, onFocus: handleInputFocus, onBlur: handleInputBlur, validations: validations }, inputProps)),
|
|
170
|
-
|
|
288
|
+
React.createElement(InputText_index.InputText, Object.assign({ ref: FormField.mergeRefs([ref, inputRef]), autocomplete: false, size: size, value: inputText, onChange: handleInputChange, placeholder: placeholder, onFocus: handleInputFocus, onBlur: handleInputBlur, validations: validations }, inputProps)),
|
|
289
|
+
React.createElement(Menu, { attachTo: autocompleteRef, inputRef: inputRef, inputFocused: inputFocused, options: options, customRenderMenu: customRenderMenu, selectedOption: value, onOptionSelect: handleMenuChange })));
|
|
171
290
|
function updateInput(newText) {
|
|
172
291
|
setInputText(newText);
|
|
173
292
|
if (newText === "") {
|
|
@@ -177,14 +296,15 @@ function AutocompleteInternal(_a, ref) {
|
|
|
177
296
|
function updateSearch() {
|
|
178
297
|
return tslib_es6.__awaiter(this, void 0, void 0, function* () {
|
|
179
298
|
const updatedOptions = yield getOptions(inputText);
|
|
180
|
-
const filteredOptions = updatedOptions.filter(
|
|
299
|
+
const filteredOptions = updatedOptions.filter(option => isOptionGroup(option) ? option.options.length > 0 : true);
|
|
181
300
|
setOptions(mapToOptions(filteredOptions));
|
|
182
301
|
});
|
|
183
302
|
}
|
|
184
303
|
function handleMenuChange(chosenOption) {
|
|
304
|
+
var _a;
|
|
185
305
|
onChange(chosenOption);
|
|
186
|
-
updateInput(chosenOption.label);
|
|
187
|
-
|
|
306
|
+
updateInput((_a = chosenOption === null || chosenOption === void 0 ? void 0 : chosenOption.label) !== null && _a !== void 0 ? _a : "");
|
|
307
|
+
setInputFocused(false);
|
|
188
308
|
}
|
|
189
309
|
function handleInputChange(newText) {
|
|
190
310
|
updateInput(newText);
|
|
@@ -193,7 +313,7 @@ function AutocompleteInternal(_a, ref) {
|
|
|
193
313
|
}
|
|
194
314
|
}
|
|
195
315
|
function handleInputBlur() {
|
|
196
|
-
|
|
316
|
+
setInputFocused(false);
|
|
197
317
|
if (value == undefined || value.label !== inputText) {
|
|
198
318
|
setInputText("");
|
|
199
319
|
onChange(undefined);
|
|
@@ -201,21 +321,34 @@ function AutocompleteInternal(_a, ref) {
|
|
|
201
321
|
onBlur && onBlur();
|
|
202
322
|
}
|
|
203
323
|
function handleInputFocus() {
|
|
204
|
-
|
|
324
|
+
setInputFocused(true);
|
|
205
325
|
if (onFocus) {
|
|
206
326
|
onFocus();
|
|
207
327
|
}
|
|
208
328
|
}
|
|
209
329
|
}
|
|
210
330
|
function mapToOptions(items) {
|
|
211
|
-
|
|
212
|
-
result
|
|
213
|
-
if (item.options) {
|
|
331
|
+
const retVal = items.reduce((result, item) => {
|
|
332
|
+
result.push(item);
|
|
333
|
+
if (isOptionGroup(item) && item.options) {
|
|
214
334
|
result = result.concat(item.options);
|
|
215
335
|
}
|
|
216
336
|
return result;
|
|
217
337
|
}, []);
|
|
338
|
+
return retVal;
|
|
218
339
|
}
|
|
340
|
+
// Casts the Generics to the forward ref so autocomplete works as expected for consumers
|
|
219
341
|
const Autocomplete = React.forwardRef(AutocompleteInternal);
|
|
220
342
|
|
|
221
343
|
exports.Autocomplete = Autocomplete;
|
|
344
|
+
exports.BaseAutocompleteMenuWrapper = BaseAutocompleteMenuWrapper;
|
|
345
|
+
exports.BaseMenuGroupOption = BaseMenuGroupOption;
|
|
346
|
+
exports.BaseMenuOption = BaseMenuOption;
|
|
347
|
+
exports.MenuOption = MenuOption;
|
|
348
|
+
exports.getRequestedIndexChange = getRequestedIndexChange;
|
|
349
|
+
exports.isOptionGroup = isOptionGroup;
|
|
350
|
+
exports.isOptionSelected = isOptionSelected;
|
|
351
|
+
exports.useAutocompleteMenu = useAutocompleteMenu;
|
|
352
|
+
exports.useCustomKeyboardNavigation = useCustomKeyboardNavigation;
|
|
353
|
+
exports.useKeyboardNavigation = useKeyboardNavigation;
|
|
354
|
+
exports.useRepositionMenu = useRepositionMenu;
|