@sitecore/sc-contenthub-blok
Version:
A UI library for [Sitecore Content Hub](https://doc.sitecore.com/ch/).
195 lines • 9.53 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { css } from "@emotion/css";
import { useCallback, useEffect, useMemo, useState, } from "react";
import useIsMounted from "../../customHooks/useIsMounted";
import { blackAlpha } from "../../Foundations";
import { isLTR, isRTL } from "../../Foundations/utils";
import { getEmptyImmutableArray, isFunction, } from "../../Foundations/utils";
import { BlokButton } from "../BlokButton/BlokButton";
import { BlokIcon } from "../BlokIcon/BlokIcon";
import { BlokIconButton } from "../BlokIconButton/BlokIconButton";
import { BlokSearchBox } from "../BlokSearchBox/BlokSearchBox";
import { BlokTooltip } from "../BlokTooltip/BlokTooltip";
import { ListItemIcon, ListItemText, Menu, MenuItem, Typography, } from "../CoreComponents";
const classes = {
dropdownItemIcon: css({
color: blackAlpha[600],
}),
iconLabel: css({
marginLeft: "8px",
verticalAlign: "middle",
maxWidth: "100%",
overflow: "hidden",
textOverflow: "ellipsis",
}),
dropdownItemIconMenu: css({
marginRight: isLTR ? "24px" : 0,
marginLeft: isRTL ? "24px" : 0,
color: blackAlpha[600],
}),
hidden: css({
display: "none",
}),
lightText: css({ color: blackAlpha["500"] }),
chevronIcon: css({
svg: {
padding: "0.125rem",
marginInlineStart: "-0.375rem",
marginInlineEnd: "-0.375rem",
},
}),
};
const menuListProps = { component: "div" };
export const getPopoverOrigin = (element, type) => {
if (!element) {
return type === "anchor"
? { vertical: "bottom", horizontal: "center" }
: { vertical: "top", horizontal: "right" };
}
const { right, left } = element.getBoundingClientRect();
const { innerWidth: viewportWidth } = window;
const spaceOnRight = viewportWidth - right;
const spaceOnLeft = left;
const horizontal = spaceOnRight > spaceOnLeft ? "left" : "right";
const vertical = type === "anchor" ? "bottom" : "top";
return { horizontal, vertical };
};
const style = {
zIndex: 2001 /* Z-index of AppointmentToolTip is 2000 so this needs to be larger */,
};
function BlokButtonSelect({ id, options = getEmptyImmutableArray(), onChange: onChange_, value, labelMapper = (item) => item, valueMapper = (item) => item, returnValue, placeholder = "Select an option", emptyValueSelectable = false, className, isReadOnly, searchBoxMinAmountOfItems = 8, showIconAndText = false, hideItemIcons = false, maxHeight = 250, itemsToDisable = getEmptyImmutableArray(), showEmptyDropdown = false, noneLabel = "None", noResultsText = "No results found", variant, colorScheme, size, clearMessage = "Clear", searchMessage = "Search", }) {
const [anchorEl, setAnchorEl] = useState(null);
const isOpen = Boolean(anchorEl);
const [filteredOptions, setFilteredOptions] = useState(options);
const [searchFilter, setSearchFilter] = useState("");
const searchBoxRef = useCallback((node) => {
if (node) {
node.focus();
}
}, []);
const isMountedRef = useIsMounted();
// When clicking the clear button on the searchbox clear search value and set the list back to the original values.
const onClearSearch = useCallback(() => {
setFilteredOptions(options);
setSearchFilter("");
}, [options]);
/**
* Open the operation's dropdown.
* @param event - the mouse event
*/
const handleClick = useCallback((event) => {
event.stopPropagation();
event.preventDefault();
setAnchorEl(event.currentTarget);
}, []);
/**
* Close the menu.
*/
const handleClose = useCallback(() => {
setAnchorEl(null);
setSearchFilter("");
}, []);
// When selecting a value in the dropdown.
const onChange = useCallback(async (newValue) => {
if (isFunction(onChange_)) {
if (newValue) {
if (isFunction(returnValue)) {
await onChange_(returnValue(newValue));
}
else {
await onChange_(newValue);
}
}
if (newValue == null) {
if (emptyValueSelectable) {
await onChange_(newValue);
}
}
}
handleClose();
}, [onChange_, handleClose, returnValue, emptyValueSelectable]);
// When clicking the enter key in the search box select the first value in the list and close the menu.
const onEnterPress = useCallback(async () => {
if (filteredOptions.length) {
// Grab the first value in the filtered options list.
const valueToSelect = filteredOptions[0];
if (isFunction(returnValue)) {
await onChange_(returnValue(valueToSelect));
}
else {
await onChange_(valueToSelect);
}
if (isMountedRef.current) {
// Restore the filtered options to the options.
setFilteredOptions(options);
handleClose();
}
}
}, [
filteredOptions,
handleClose,
isMountedRef,
onChange_,
options,
returnValue,
]);
useEffect(() => {
if (!options.length) {
return;
}
if (searchFilter !== "") {
setFilteredOptions(options.filter((option) => labelMapper(option)
.toLocaleLowerCase()
.includes(searchFilter.toLocaleLowerCase())));
}
else {
setFilteredOptions(options);
}
}, [options, searchFilter, labelMapper]);
const selectedValue = (value && valueMapper(value)) || "";
const filteredItems = useMemo(() => {
return filteredOptions?.map((option, index) => {
return (option && (_jsxs(MenuItem, { onKeyDown: (e) => {
if (e.key === "Escape") {
setSearchFilter("");
handleClose();
}
}, disabled: itemsToDisable.some((item) => valueMapper(option) === valueMapper(item)), onClick: () => onChange(option), selected: valueMapper(option) === selectedValue, children: [Object.prototype.hasOwnProperty.call(option, "icon") &&
!hideItemIcons && (_jsx(ListItemIcon, { children: _jsx(BlokIcon, { icon: option.icon }) })), _jsx(ListItemText, { primary: labelMapper(option) })] }, `${valueMapper(option)}-${index}`)));
});
}, [
filteredOptions,
valueMapper,
itemsToDisable,
selectedValue,
hideItemIcons,
labelMapper,
handleClose,
onChange,
]);
const searchBox = useMemo(() => options.length > searchBoxMinAmountOfItems && (_jsx(BlokSearchBox, { onSearch: setSearchFilter, onClearSearch: onClearSearch, value: searchFilter, ref: searchBoxRef, onEnterPress: onEnterPress, isInsideDropdown: true, hasGutterBottom: false, clearMessage: clearMessage, searchMessage: searchMessage })), [
clearMessage,
onClearSearch,
onEnterPress,
options.length,
searchBoxMinAmountOfItems,
searchBoxRef,
searchFilter,
searchMessage,
]);
if (!options.length && !showEmptyDropdown) {
return null;
}
const dropdownMenuId = `button-select-menu-${id}`;
const icon = value?.icon;
const labelToDisplay = value
? (labelMapper(value) ?? placeholder)
: placeholder;
return (_jsx(_Fragment, { children: isReadOnly ? (_jsx("div", { "data-testid": id, children: _jsx(Typography, { variant: "body1", children: typeof value === "object"
? labelMapper(value)
: (value ?? "") }) })) : (_jsxs(_Fragment, { children: [!showIconAndText && icon ? (_jsx(BlokTooltip, { title: labelToDisplay ?? "", children: _jsx(BlokIconButton, { id: id, onClick: handleClick, className: className, variant: variant, colorScheme: colorScheme, size: size, children: _jsx(BlokIcon, { icon: icon }) }) })) : (_jsx(BlokButton, { id: id, onClick: handleClick, startIcon: icon ? _jsx(BlokIcon, { icon: icon }) : undefined, endIcon: _jsx(BlokIcon, { icon: "chevron-down", classes: [classes.chevronIcon] }), className: className, variant: variant, colorScheme: colorScheme, size: size, children: labelToDisplay })), _jsxs(Menu, { keepMounted: true, anchorOrigin: getPopoverOrigin(anchorEl, "anchor"), id: dropdownMenuId, anchorEl: anchorEl, open: isOpen, onClose: handleClose, MenuListProps: menuListProps, onKeyDown: (e) => e.stopPropagation(), variant: "menu", style: style, transformOrigin: getPopoverOrigin(anchorEl, "transform"), autoFocus: options.length < searchBoxMinAmountOfItems, slotProps: { paper: { style: { maxHeight, overflow: "auto" } } }, children: [isOpen && searchBox, value === null && (_jsx(MenuItem, { value: undefined, className: classes.hidden, children: _jsx("em", { className: classes.lightText, children: placeholder }) })), emptyValueSelectable && searchFilter === "" && (
// We need to set the value to null so that it is picked up correctly.
_jsx(MenuItem, { value: null, children: _jsx("em", { children: noneLabel }) })), !filteredOptions.length && noResultsText, filteredItems] })] })) }));
}
export { BlokButtonSelect };
//# sourceMappingURL=BlokButtonSelect.js.map