@adyen/kyc-components
Version:
This guide assumes that you have already an account with Adyen. A legalEntity needs to be created, and you need to have a `legalEntityId` to instatiate a Component.
776 lines (775 loc) • 27.4 kB
JavaScript
try {
let e = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof globalThis ? globalThis : "undefined" != typeof self ? self : {}, n = new e.Error().stack;
n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "79ba8d2d-8832-4e6d-8966-8f64fab0f197", e._sentryDebugIdIdentifier = "sentry-dbid-79ba8d2d-8832-4e6d-8966-8f64fab0f197");
} catch (e) {}
import { i as Typography, o as createLogger, r as useTranslation } from "./translation-BFxyJ1c5.js";
import { r as Loader } from "./Button-oj6H8OrC.js";
import { s as noop, t as useAnalyticsContext } from "./useAnalyticsContext-BVFDMrVE.js";
import { t as KEYBOARD_KEYS } from "./keys-fzUbt2xF.js";
import { t as useId } from "./useId-eJSYfA6i.js";
import { t as debouncedInputEvent } from "./debouncedInputEvent-Dxv4-RAv.js";
import { t as Checkbox } from "./Checkbox-BCYjFPa4.js";
import { useCallback, useEffect, useMemo, useRef, useState } from "preact/hooks";
import cx from "classnames";
import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
import { computed, signal, useSignal, useSignalEffect } from "@preact/signals";
//#region src/components/ui/atoms/Select/useSelectFocus.ts
var createSelectFocus = ({ activeItems, filteredItems: items }) => {
const numberOfItems = computed(() => items.value.length - 1);
const focused = signal();
const getDefaultFocusedItem = () => {
const firstActiveOption = activeItems.value.find((option) => !option.disabled);
const firstEnabledOption = items.value.find((option) => !option.disabled);
return firstActiveOption ?? firstEnabledOption;
};
const focusedItemIndex = computed(() => {
const currFocus = focused.value;
const toFind = focusedIsItem(currFocus) ? currFocus : getDefaultFocusedItem();
const itemsValue = items.value;
if (!toFind) {
console.warn("[Select] No existing focused item, and none of the filtered items are enabled");
return 0;
}
return itemsValue.findIndex((i) => i.id === toFind.id);
});
const findPotentialIndex = (currentIndex, direction) => {
const potentialIndex = direction === "down" ? currentIndex + 1 : currentIndex - 1;
const potentialItem = items.value[potentialIndex];
if (potentialIndex < 0 || potentialIndex > numberOfItems.value) return "exceedsBounds";
if (potentialItem.disabled) return findPotentialIndex(potentialIndex, direction);
return potentialIndex;
};
const moveFocusToNextItem = () => {
const nextIndex = findPotentialIndex(focusedItemIndex.value, "down");
if (nextIndex !== "exceedsBounds") focused.value = items.value[nextIndex];
};
const moveFocusToPreviousItem = () => {
const prevIndex = findPotentialIndex(focusedItemIndex.value, "up");
if (prevIndex !== "exceedsBounds") focused.value = items.value[prevIndex];
};
const moveFocusToFirstItem = () => {
let currIndex = focusedItemIndex.value;
while (true) {
const before = findPotentialIndex(currIndex, "up");
if (before === "exceedsBounds") break;
currIndex = before;
}
focused.value = items.value[currIndex];
};
const moveFocusToLastItem = () => {
let currIndex = focusedItemIndex.value;
while (true) {
const after = findPotentialIndex(currIndex, "down");
if (after === "exceedsBounds") break;
currIndex = after;
}
focused.value = items.value[currIndex];
};
const focusInput = () => {
focused.value = "input";
};
const focusItem = (item) => {
focused.value = item;
};
const focusDefaultItem = () => {
focused.value = getDefaultFocusedItem();
};
const focusNothing = () => {
focused.value = void 0;
};
return {
focused,
moveFocusToFirstItem,
moveFocusToLastItem,
moveFocusToNextItem,
moveFocusToPreviousItem,
focusInput,
focusItem,
focusDefaultItem,
focusNothing
};
};
var useSelectFocus = (options) => {
const [selectFocus] = useState(createSelectFocus(options));
return selectFocus;
};
var focusedIsItem = (focused) => Boolean(focused) && typeof focused === "object";
//#endregion
//#region src/utils/signals/useWatchingSignal.ts
/**
* Returns a signal "watching" its given value. Whenever the value changes, the signal will update.
*
* @param value
*/
var useWatchingSignal = (value) => {
const signal = useSignal(value);
if (signal.peek() !== value) signal.value = value;
return signal;
};
//#endregion
//#region src/utils/simulateTextChangeFromKeyboardEvent.ts
/**
* This is useful for when you want to figure out what effect a keyboard event would have on
* some arbitrary existing text, as if it were happening on an `<input>`'s value.
*
* This is not a perfect simulation, real input handling is much more complex.
*
* @param keyDownEvent the actual event which occurred, probably on some other element
* @param value the existing value of the input
*/
var simulateTextChangeFromKeyboardEvent = (keyDownEvent, value) => {
const { key } = keyDownEvent;
let newValue = value;
if (key === KEYBOARD_KEYS.backspace) newValue = newValue.slice(0, newValue.length - 1);
else if (key.length === 1) newValue += key;
return newValue;
};
//#endregion
//#region src/utils/useMirrorProp.ts
/**
* This is a 'hacky' way to handle components which may have props either controlled or uncontrolled.
*
* Avoid using it if at all possible - generally it's better to write a controlled inner component
* and simply make an uncontrolled version which wraps it.
*
* * If controlled, we mirror the prop state and propagate changes upwards via {@link propOnChange}.
* * If uncontrolled, it is effectively a simple {@link useState()}.
*
* *Note: if mirroring, both `prop` and `propOnChange` **must** be provided.*
*
* @param prop the prop to be mirrored
* @param propOnChange the handler to propagate changes from below back upwards
* @param defaultVal the default value, only used if we aren't mirroring
*/
function useMirrorProp(prop, propOnChange, defaultVal) {
const [isMirroring] = useState(propOnChange !== void 0);
const [local, setLocal] = useState(defaultVal);
if (isMirroring) return [prop, propOnChange];
else return [local, setLocal];
}
var SelectButton_module_default = {
"select-input-wrapper": "adyen-kyc-select-input-wrapper",
selectInputWrapper: "adyen-kyc-select-input-wrapper",
"select-input__icon": "adyen-kyc-select-input__icon",
selectInputIcon: "adyen-kyc-select-input__icon",
"select-input": "adyen-kyc-select-input",
selectInput: "adyen-kyc-select-input",
"select-input--active": "adyen-kyc-select-input--active",
selectInputActive: "adyen-kyc-select-input--active",
"select-input--readonly": "adyen-kyc-select-input--readonly",
selectInputReadonly: "adyen-kyc-select-input--readonly",
"select-input--invalid": "adyen-kyc-select-input--invalid",
selectInputInvalid: "adyen-kyc-select-input--invalid",
"select-input--minimal": "adyen-kyc-select-input--minimal",
selectInputMinimal: "adyen-kyc-select-input--minimal",
"select-input-search": "adyen-kyc-select-input-search",
selectInputSearch: "adyen-kyc-select-input-search",
"select-input-search__icon": "adyen-kyc-select-input-search__icon",
selectInputSearchIcon: "adyen-kyc-select-input-search__icon",
"select-input--filterable": "adyen-kyc-select-input--filterable",
selectInputFilterable: "adyen-kyc-select-input--filterable",
"select-input-wrapper--filterable": "adyen-kyc-select-input-wrapper--filterable",
selectInputWrapperFilterable: "adyen-kyc-select-input-wrapper--filterable",
"select-input-wrapper--non-filterable": "adyen-kyc-select-input-wrapper--non-filterable",
selectInputWrapperNonFilterable: "adyen-kyc-select-input-wrapper--non-filterable",
"select-input--list-open": "adyen-kyc-select-input--list-open",
selectInputListOpen: "adyen-kyc-select-input--list-open",
"select-input--non-filterable": "adyen-kyc-select-input--non-filterable",
selectInputNonFilterable: "adyen-kyc-select-input--non-filterable",
"select-input--placeholder": "adyen-kyc-select-input--placeholder",
selectInputPlaceholder: "adyen-kyc-select-input--placeholder",
"ignore-pointer-events": "adyen-kyc-ignore-pointer-events",
ignorePointerEvents: "adyen-kyc-ignore-pointer-events",
"handle-pointer-events": "adyen-kyc-handle-pointer-events",
handlePointerEvents: "adyen-kyc-handle-pointer-events"
};
//#endregion
//#region src/components/ui/atoms/SelectButton/SelectButton.tsx
var isAppleMobile = /iPhone|iPad/.test(navigator.platform);
function SelectButton({ id, name, active, readonly, showList, isInvalid, onClick, onButtonKeyDown, filterable, isFiltering, placeholder, focusProps: { focused }, selectListId, onInput, behaviour, loading, text, enableTracking = false, onFocus, inputRef, minimal, "aria-describedby": ariaDescribedBy, "aria-labelledby": ariaLabelledBy, "aria-label": ariaLabel }) {
const userEvents = useAnalyticsContext();
const wrapperRef = useRef(null);
useSignalEffect(() => {
if (focused.value === "input") inputRef.current?.focus();
});
const handleFocus = (e) => {
if (readonly) return;
if (enableTracking) userEvents.addFieldEvent("Interacted with form field", {
actionType: "focus",
field: name
});
onFocus(e);
};
const handleClick = (e) => {
e.preventDefault();
e.stopPropagation();
if (readonly) return;
onClick();
};
const handleKeyDown = (e) => {
if (!readonly) onButtonKeyDown(e);
};
const handleInput = (e) => {
if (filterable && !readonly) onInput(e.currentTarget.value);
};
const truncatedDisplayValue = useMemo(() => {
if (active.length === 0) return void 0;
const activeText = active.map(({ selectedOptionName, name }) => selectedOptionName ?? name).join(", ");
return activeText.length > 50 ? activeText.substring(0, 50) + "..." : activeText;
}, [active]);
const title = active?.length ? active.map(({ name }) => name).join(",") : placeholder;
const isFocused = focused.value === "input";
const value = useMemo(() => {
if (behaviour === "textIsData" || focused.value !== void 0 && filterable && isFiltering) return text;
return truncatedDisplayValue;
}, [
behaviour,
text,
focused.value,
filterable,
isFiltering,
truncatedDisplayValue
]);
return /* @__PURE__ */ jsxs("div", {
className: cx({
[SelectButton_module_default.selectInputWrapper]: true,
[SelectButton_module_default.selectInputMinimal]: minimal,
[SelectButton_module_default.selectInputReadonly]: readonly,
[SelectButton_module_default.selectInputActive]: isFocused,
[SelectButton_module_default.selectInputListOpen]: showList,
[SelectButton_module_default.selectInputInvalid]: isInvalid,
[SelectButton_module_default.selectInputSearch]: filterable === "prefiltered",
[SelectButton_module_default.selectInputWrapperFilterable]: filterable,
[SelectButton_module_default.selectInputWrapperNonFilterable]: !filterable,
[SelectButton_module_default.ignorePointerEvents]: isAppleMobile
}),
ref: wrapperRef,
onClick: handleClick,
onFocusIn: handleFocus,
children: [
/* @__PURE__ */ jsx("input", {
id,
name,
title,
ref: inputRef,
type: filterable ? "text" : "button",
role: "combobox",
"aria-haspopup": "listbox",
"aria-owns": selectListId,
"aria-controls": selectListId,
"aria-expanded": showList,
"aria-describedby": ariaDescribedBy,
"aria-disabled": readonly,
"aria-label": ariaLabel,
"aria-labelledby": ariaLabelledBy,
"aria-autocomplete": filterable ? "list" : void 0,
autoComplete: "off",
className: cx({
[SelectButton_module_default.selectInput]: true,
[SelectButton_module_default.selectInputFilterable]: filterable,
[SelectButton_module_default.selectInputNonFilterable]: !filterable,
[SelectButton_module_default.selectInputMinimal]: minimal,
[SelectButton_module_default.selectInputReadonly]: readonly,
[SelectButton_module_default.handlePointerEvents]: isAppleMobile
}),
placeholder,
value,
disabled: readonly,
onClick: handleClick,
onInput: handleInput,
onKeyDown: handleKeyDown
}),
!filterable && !value ? /* @__PURE__ */ jsx("span", {
className: SelectButton_module_default.selectInputPlaceholder,
children: placeholder
}) : void 0,
loading && /* @__PURE__ */ jsx(Loader, { size: "small" })
]
});
}
//#endregion
//#region src/components/ui/atoms/SelectListItem/SelectListItem.tsx
var SelectListItem = ({ id, item, selected, isMulti, isSearch, children, onSelect, onKeyDown, onFocus, className }) => /* @__PURE__ */ jsx("li", {
"aria-disabled": !!item.disabled,
"aria-selected": selected,
className: cx([
"adyen-kyc-dropdown-element",
{
"adyen-kyc-dropdown-element--active": selected,
"adyen-kyc-dropdown-element--disabled": !!item.disabled,
"adyen-kyc-dropdown-multi-element": isMulti,
"adyen-kyc-dropdown-element-description": !isMulti && !isSearch && item.description,
"adyen-kyc-dropdown-search-element": isSearch
},
className
]),
"data-disabled": !!item.disabled,
"data-value": item.id,
onFocus: (event) => {
if (item.disabled) return;
onFocus?.(event);
},
onClick: (e) => {
e.preventDefault();
if (item.disabled) return;
onSelect(!selected);
},
onKeyDown: (e) => {
if (item.disabled) return;
onKeyDown(e);
},
role: "option",
tabIndex: -1,
children: isMulti ? /* @__PURE__ */ jsx("span", {
style: {
pointerEvents: "none",
width: "100%"
},
children: /* @__PURE__ */ jsx(Checkbox, {
id,
onChange: noop,
label: item.name,
disabled: item.disabled,
helper: item.description,
checked: selected,
name: `adyen-kyc-dropdown-element__checkbox adyen-kyc-dropdown-element__checkbox--${item.id}`
})
}) : children ? [children] : /* @__PURE__ */ jsxs(Fragment, { children: [item.icon && /* @__PURE__ */ jsx("span", {
className: "adyen-kyc-dropdown-element__icon",
children: item.icon
}), item.description ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Typography, {
el: "span",
variant: "body-strongest",
className: "adyen-kyc-dropdown-element__label",
children: item.name
}), /* @__PURE__ */ jsx(Typography, {
el: "span",
color: "secondary",
className: "adyen-kyc-dropdown-element-description__text",
children: item.description
})] }) : /* @__PURE__ */ jsx("span", {
className: "adyen-kyc-dropdown-element__label",
children: item.name
})] })
});
//#endregion
//#region src/components/ui/atoms/SelectList/SelectList.tsx
var logger = createLogger();
var DEFAULT_ITEM_HEIGHT = 36;
function SelectList({ selectListId, active, filteredItems, showList, isMulti, inputRef, isSearch, loading, getSearchItem, onKeyDown, onSelect, minimal, maxVisibleItems = 5, focusProps: { focused, focusItem, focusDefaultItem, moveFocusToNextItem, moveFocusToPreviousItem, moveFocusToFirstItem, moveFocusToLastItem } }) {
const { t } = useTranslation("ui");
const [startIndex, setStartIndex] = useState(0);
const [endIndex, setEndIndex] = useState(maxVisibleItems);
const listRef = useRef(null);
const itemHeight = (() => {
if (listRef.current && showList) {
const height = (listRef.current.firstElementChild?.firstElementChild)?.clientHeight;
if (height !== void 0) return height;
}
})() || DEFAULT_ITEM_HEIGHT;
const totalItems = filteredItems.length;
const visibleItems = useMemo(() => filteredItems.slice(startIndex, endIndex), [
endIndex,
filteredItems,
startIndex
]);
const findElementForItem = useCallback((item) => {
const listContainer = listRef.current;
if (!listContainer) return;
return listContainer.querySelector(`[role="option"][data-value="${item.id}"]`);
}, [listRef]);
/**
* When we scroll, we need to figure out which items are now in view.
* This also needs to be done whenever the filtered items change.
*/
const recalculateScrollIndexes = useCallback(() => {
if (!listRef.current) return;
const { scrollTop } = listRef.current;
const maxStartIndex = totalItems < maxVisibleItems ? totalItems : totalItems - maxVisibleItems;
const newStartIndex = Math.min(Math.max(0, Math.floor(scrollTop / itemHeight)), maxStartIndex);
const newEndIndex = Math.min(newStartIndex + maxVisibleItems, totalItems);
setStartIndex(newStartIndex);
setEndIndex(newEndIndex);
}, [
totalItems,
maxVisibleItems,
itemHeight,
listRef
]);
useEffect(() => {
recalculateScrollIndexes();
}, [recalculateScrollIndexes]);
const scrollItemIntoView = useCallback((item) => {
if (!listRef.current) return;
const itemIndex = filteredItems.indexOf(item);
if (itemIndex === -1) return;
const maxScrollTop = totalItems * itemHeight;
listRef.current.scrollTop = Math.min(itemHeight * (itemIndex + .5), maxScrollTop);
}, [
filteredItems,
itemHeight,
totalItems,
listRef
]);
const acquireFocusForItem = useCallback((item) => {
let itemEl = findElementForItem(item);
if (!itemEl) scrollItemIntoView(item);
setTimeout(() => {
itemEl = itemEl ?? findElementForItem(item);
if (itemEl) itemEl.focus();
else logger.warn("[SelectList] still cannot see the option", item.name);
}, 20);
}, [findElementForItem, scrollItemIntoView]);
useSignalEffect(() => {
const currFocused = focused.value;
if (focusedIsItem(currFocused)) acquireFocusForItem(currFocused);
});
const listListeners = {
[KEYBOARD_KEYS.arrowUp]: moveFocusToPreviousItem,
[KEYBOARD_KEYS.arrowDown]: moveFocusToNextItem,
[KEYBOARD_KEYS.home]: moveFocusToFirstItem,
[KEYBOARD_KEYS.end]: moveFocusToLastItem
};
const onListKeydown = (event) => {
const listener = listListeners[event.key];
if (listener) {
event.preventDefault();
listener();
}
};
const getItems = () => {
if (loading) return /* @__PURE__ */ jsx("div", {
className: "adyen-kyc-dropdown-element adyen-kyc-dropdown-element--no-options",
children: t(($) => $["loading"])
});
if (!filteredItems.length) {
if (startIndex !== 0 || endIndex !== maxVisibleItems) {
setStartIndex(0);
setEndIndex(maxVisibleItems);
}
return /* @__PURE__ */ jsx("div", {
className: "adyen-kyc-dropdown-element adyen-kyc-dropdown-element--no-options",
children: t(($) => $["noOptionsFound"])
});
}
if (visibleItems.length) return visibleItems.map((item) => /* @__PURE__ */ jsx(SelectListItem, {
item,
id: item.id,
onKeyDown: (event) => {
onKeyDown(event, item);
},
onFocus: (event) => {
focusItem(item);
event.stopPropagation();
},
onSelect: () => {
onSelect(item);
},
selected: !!active.find((activeItem) => activeItem.id === item.id),
isMulti,
isSearch,
children: getSearchItem && getSearchItem(item)
}, item.id));
return null;
};
const height = useMemo(() => {
if (loading) return "auto";
return totalItems > maxVisibleItems && itemHeight ? `${itemHeight * maxVisibleItems}px` : "auto";
}, [
itemHeight,
totalItems,
loading,
maxVisibleItems
]);
const allItemsVisible = totalItems <= maxVisibleItems;
const [showAbove, setShowAbove] = useState(false);
useEffect(() => {
const list = listRef.current;
const input = inputRef.current;
const resizeObserver = new ResizeObserver(() => {
const dropdownHeight = list.offsetHeight;
const windowHeight = window.innerHeight;
const position = input.getBoundingClientRect().bottom;
setShowAbove(!(windowHeight - position > dropdownHeight) && position > dropdownHeight);
});
resizeObserver.observe(list);
return () => {
resizeObserver.disconnect();
};
}, [inputRef, listRef]);
return /* @__PURE__ */ jsx("div", {
className: cx({
"adyen-kyc-dropdown-list": true,
"adyen-kyc-dropdown-list--minimal": minimal,
"adyen-kyc-dropdown-list--active": showList,
"adyen-kyc-dropdown-list--above": showAbove
}),
id: selectListId,
role: "listbox",
onScroll: recalculateScrollIndexes,
style: {
overflowY: allItemsVisible ? "hidden" : "auto",
position: "absolute",
height
},
onKeyDown: onListKeydown,
onFocus: focusDefaultItem,
tabIndex: -1,
ref: listRef,
children: /* @__PURE__ */ jsxs("ul", {
className: "adyen-kyc-dropdown-list__transformer",
style: { transform: `translateY(${startIndex * itemHeight}px)` },
children: [getItems(), /* @__PURE__ */ jsx("div", { style: { height: `${(totalItems - endIndex) * itemHeight}px` } })]
})
});
}
//#endregion
//#region src/components/ui/atoms/Select/Select.tsx
var Select = ({ id, items, filterable = true, readonly = false, onChange, onSelect, selected, name, isInvalid = false, placeholder = "", isMulti, enableTracking = false, maxVisibleItems, getSearchItem, onBlur = noop, text: filterTextProp, setText: setFilterTextProp, showList: showListProp, setShowList: setShowListProp, behaviour = "textIsFilter", loading = false, minimal = false, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, excludedValues }) => {
const userEvents = useAnalyticsContext();
const selectContainerRef = useRef(null);
const inputRef = useRef(null);
const selectListId = useId("select");
const [textFilter, setTextFilter] = useMirrorProp(filterTextProp, setFilterTextProp, "");
const [isShowList, setIsShowList] = useMirrorProp(showListProp, setShowListProp, false);
const [isFiltering, setIsFiltering] = useState(false);
const active = useMemo(() => {
if (selected === void 0) return [];
return (Array.isArray(selected) ? selected : [selected]).map((s) => {
if (typeof s === "object") return s;
return items.find((i) => i.id === s);
}).filter(Boolean);
}, [items, selected]);
const filtered = useMemo(() => {
return items.filter((item) => !excludedValues?.includes(item)).filter((item) => {
if (filterable === "prefiltered" || !isFiltering) return true;
return item.name.toLowerCase().includes(textFilter.toLowerCase());
});
}, [
filterable,
isFiltering,
items,
textFilter,
excludedValues
]);
const focusProps = useSelectFocus({
activeItems: useWatchingSignal(active),
filteredItems: useWatchingSignal(filtered)
});
const { focusInput, focusItem, focusDefaultItem, focusNothing, moveFocusToNextItem, moveFocusToPreviousItem } = focusProps;
/**
* Closes the selectList, empties the text filter and focuses the button element
*/
const closeList = useCallback((shiftFocusTo) => {
if (behaviour === "textIsFilter") setTextFilter("");
setIsShowList(false);
switch (shiftFocusTo) {
case "input":
focusInput();
break;
case "nothing":
focusNothing();
break;
}
}, [
behaviour,
focusInput,
focusNothing,
setIsShowList,
setTextFilter
]);
const showList = useCallback(() => {
if (!isShowList) setIsShowList(true);
}, [isShowList, setIsShowList]);
const handleSelect = useCallback((selectedItem) => {
setIsFiltering(false);
if (readonly) return;
if (isMulti) {
const newActiveItems = active.find((item) => item.id === selectedItem.id) ? active.filter((i) => i.id !== selectedItem.id) : [...active, selectedItem];
onSelect?.(newActiveItems);
onChange?.({ target: {
value: newActiveItems.map((i) => i.id),
name: name ?? ""
} });
focusItem(selectedItem);
} else {
const handleOnSelect = onSelect;
const handleOnChange = onChange;
handleOnSelect?.(selectedItem);
handleOnChange?.({ target: {
value: selectedItem.id,
name: name ?? ""
} });
closeList("input");
}
if (enableTracking) userEvents.addFieldEvent("Interacted with form field", {
actionType: "select",
field: name,
returnValue: selectedItem.id
});
}, [
readonly,
isMulti,
enableTracking,
active,
onSelect,
onChange,
name,
focusItem,
closeList,
userEvents
]);
const handleBlur = (e) => {
onBlur(e);
};
/**
* Handle keyDown events on the selectList button
* Opens the selectList and focuses the first element if available
* @param e - KeyboardEvent
*/
const handleInputKeyDown = async (e) => {
if (e.key === KEYBOARD_KEYS.enter && isShowList) if (filtered.length === 1) handleSelect(filtered[0]);
else e.preventDefault();
else if (e.key === KEYBOARD_KEYS.escape) closeList("nothing");
else if (e.key === KEYBOARD_KEYS.enter || e.key === KEYBOARD_KEYS.space && (!filterable || !isShowList)) {
e.preventDefault();
e.stopPropagation();
showList();
focusDefaultItem();
} else if (e.key === KEYBOARD_KEYS.arrowUp || e.key === KEYBOARD_KEYS.arrowDown) {
e.preventDefault();
e.stopPropagation();
showList();
focusDefaultItem();
switch (e.key) {
case KEYBOARD_KEYS.arrowUp:
moveFocusToPreviousItem();
break;
case KEYBOARD_KEYS.arrowDown:
moveFocusToNextItem();
break;
}
} else if (e.key === KEYBOARD_KEYS.tab) closeList("nothing");
};
/**
* Close the select list when clicking outside the list
* @param e - MouseEvent
*/
const handleClickOutside = (e) => {
if (!selectContainerRef.current?.contains(e.target)) closeList("nothing");
};
/**
* Handle keyDown events on the list elements
* Navigates through the list, or select an element, or focus the filter input, or close the menu.
* @param e - KeyDownEvent
* @param item - the item for which the keyboard event was triggered
*/
const handleListItemKeyDown = (e, item) => {
switch (e.key) {
case KEYBOARD_KEYS.escape:
e.preventDefault();
closeList("input");
break;
case KEYBOARD_KEYS.space:
case KEYBOARD_KEYS.enter:
handleSelect(item);
break;
case KEYBOARD_KEYS.tab:
closeList("nothing");
break;
case KEYBOARD_KEYS.arrowUp:
case KEYBOARD_KEYS.arrowDown:
case KEYBOARD_KEYS.home:
case KEYBOARD_KEYS.end: break;
default:
e.preventDefault();
e.stopPropagation();
handleTextFilter(simulateTextChangeFromKeyboardEvent(e, textFilter));
focusInput();
}
};
/**
* Updates the state with the current text filter value
*/
const handleTextFilter = useCallback((value) => {
showList();
setIsFiltering(true);
setTextFilter(value);
if (enableTracking) debouncedInputEvent(userEvents, {
actionType: "input",
field: name
});
}, [
enableTracking,
name,
setTextFilter,
showList
]);
useEffect(() => {
document.addEventListener("click", handleClickOutside, true);
return () => {
document.removeEventListener("click", handleClickOutside, true);
};
}, []);
return /* @__PURE__ */ jsxs("div", {
className: cx({
"adyen-kyc-dropdown": true,
"adyen-kyc-dropdown--minimal": minimal
}),
ref: selectContainerRef,
onFocusOut: handleBlur,
children: [/* @__PURE__ */ jsx(SelectButton, {
id,
name,
active,
filterable,
isFiltering,
isInvalid,
onButtonKeyDown: handleInputKeyDown,
onInput: handleTextFilter,
placeholder,
readonly,
selectListId,
showList: isShowList,
focusProps,
"aria-describedby": ariaDescribedBy || "",
"aria-label": ariaLabel || "",
"aria-labelledby": ariaLabelledBy || "",
isMulti: Boolean(isMulti),
loading,
behaviour,
text: textFilter,
inputRef,
enableTracking,
minimal,
onClick: () => {
if (isShowList) closeList("input");
else {
showList();
focusInput();
}
},
onFocus: () => {
focusInput();
}
}), /* @__PURE__ */ jsx(SelectList, {
active,
filteredItems: filtered,
onKeyDown: handleListItemKeyDown,
onSelect: handleSelect,
selectListId,
showList: isShowList,
textFilter,
isMulti: Boolean(isMulti),
isSearch: filterable === "prefiltered",
getSearchItem,
loading,
maxVisibleItems,
focusProps,
inputRef,
minimal
})]
});
};
//#endregion
export { SelectListItem as n, Select as t };