UNPKG

@sitecore/sc-contenthub-blok

Version:

A UI library for [Sitecore Content Hub](https://doc.sitecore.com/ch/).

196 lines 9.68 kB
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 { CircularProgress, 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", isLoading, loadingText = "Loading...", }) { 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 && !isLoading) { 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, onKeyDown: (e) => e.stopPropagation(), variant: "menu", style: style, transformOrigin: getPopoverOrigin(anchorEl, "transform"), autoFocus: options.length < searchBoxMinAmountOfItems, slotProps: { paper: { style: { maxHeight, overflow: "auto" } }, list: menuListProps, }, children: [isOpen && searchBox, value === null && (_jsx(MenuItem, { value: undefined, className: classes.hidden, children: _jsx("em", { className: classes.lightText, children: placeholder }) })), emptyValueSelectable && searchFilter === "" && (_jsx(MenuItem, { value: null, children: _jsx("em", { children: noneLabel }) })), !filteredOptions.length && !isLoading && noResultsText, isLoading && (_jsxs(MenuItem, { disabled: true, children: [_jsx(CircularProgress, { size: 20 }), "\u00A0 ", loadingText] })), filteredItems] })] })) })); } export { BlokButtonSelect }; //# sourceMappingURL=BlokButtonSelect.js.map