nudge-components-library
Version:
A library of nudge UI components
114 lines (111 loc) • 6.92 kB
JavaScript
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
import { useContext, useState, useRef, useEffect } from 'react';
import { FiChevronDown, FiCheck } from '../../node_modules/react-icons/fi/index.js';
import { ThemeContext } from '../../theme/ThemeContext.js';
import '../../styles/tokens.css.js';
import '../../styles/globals.css.js';
import styles from './DropdownMenu.module.css.js';
function DropdownMenu({ dropdownLabel, options, selected, defaultSelected, onChange, placeholder = "Select an option", disabled = false, id, ariaLabel, onFocus, onBlur, onCommit, nudgeVisible = true, nudgePosition = "bottom", renderNudge, }) {
const theme = useContext(ThemeContext);
const [isOpen, setIsOpen] = useState(false);
const [internalSelected, setInternalSelected] = useState(defaultSelected || "");
const dropdownRef = useRef(null);
const selectedValue = selected !== undefined ? selected : internalSelected;
const selectedOption = options.find((opt) => opt.value === selectedValue);
const toggleOpen = () => {
if (!disabled)
setIsOpen(!isOpen);
};
const handleSelect = (value) => {
if (!disabled) {
setInternalSelected(value);
onChange?.(value);
onCommit?.(value);
setIsOpen(false);
}
};
useEffect(() => {
const handleClickOutside = (event) => {
if (dropdownRef.current &&
!dropdownRef.current.contains(event.target)) {
setIsOpen(false);
if (onBlur) {
const dummyEvent = {
target: dropdownRef.current,
};
onBlur(dummyEvent);
}
onCommit?.(selectedValue);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, [dropdownRef, onBlur, onCommit, selectedValue]);
useEffect(() => {
const handleTouchOutside = (event) => {
if (dropdownRef.current &&
!dropdownRef.current.contains(event.target)) {
setIsOpen(false);
if (onBlur) {
const dummyEvent = {
target: dropdownRef.current,
};
onBlur(dummyEvent);
}
onCommit?.(selectedValue);
}
};
document.addEventListener("touchstart", handleTouchOutside);
return () => document.removeEventListener("touchstart", handleTouchOutside);
}, [dropdownRef, onBlur, onCommit, selectedValue]);
const handleTouchStart = (e) => {
if (onFocus) {
onFocus(e);
}
};
return (jsxs("div", { ref: dropdownRef, style: {
...theme.dropdown.wrapper,
...(disabled ? theme.dropdown.disabled : {}),
}, children: [dropdownLabel && (jsx("div", { style: theme.dropdown.dropdownLabel, children: dropdownLabel })), jsxs("button", { id: id, "aria-label": ariaLabel || placeholder, style: {
...theme.dropdown.button,
"--base-border": theme.dropdown.button.baseBorder,
"--hover-border": theme.dropdown.hover.hoverBorder,
}, onClick: toggleOpen, onFocus: onFocus, onBlur: (e) => {
onBlur?.(e);
onCommit?.(selectedValue);
}, onTouchStart: handleTouchStart, className: styles.dropdownButton, children: [jsx("span", { children: selectedOption ? selectedOption.label : placeholder }), jsx(FiChevronDown, { style: {
...theme.dropdown.chevron,
...(isOpen ? theme.dropdown.chevronOpen : {}),
} })] }), isOpen && (jsx("ul", { style: theme.dropdown.list, children: options.map((option) => {
const isOptionSelected = option.value === selectedValue;
let optionNudgeElement = null;
if (nudgeVisible) {
if (renderNudge) {
optionNudgeElement = (jsx("div", { id: id ? `${id}-${option.value}-nudge` : undefined, style: theme.dropdown.nudgeText, children: renderNudge(option) }));
}
else if (option.nudgeText) {
optionNudgeElement = (jsx("div", { id: id ? `${id}-${option.value}-nudge` : undefined, style: theme.dropdown.nudgeText, children: jsx("span", { children: option.nudgeText }) }));
}
}
let optionContent;
if (nudgePosition === "top") {
optionContent = (jsxs(Fragment, { children: [optionNudgeElement, jsxs("div", { style: theme.dropdown.content, children: [jsx("span", { style: theme.dropdown.label, children: option.label }), isOptionSelected && (jsx(FiCheck, { style: theme.dropdown.checkIcon, strokeWidth: 2.5 }))] })] }));
}
else if (nudgePosition === "bottom") {
optionContent = (jsxs(Fragment, { children: [jsxs("div", { style: theme.dropdown.content, children: [jsx("span", { style: theme.dropdown.label, children: option.label }), isOptionSelected && (jsx(FiCheck, { style: theme.dropdown.checkIcon, strokeWidth: 2.5 }))] }), optionNudgeElement] }));
}
else if (nudgePosition === "left" || nudgePosition === "right") {
optionContent = (jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [nudgePosition === "left" && optionNudgeElement, jsxs("div", { style: theme.dropdown.content, children: [jsx("span", { style: theme.dropdown.label, children: option.label }), isOptionSelected && (jsx(FiCheck, { style: theme.dropdown.checkIcon, strokeWidth: 2.5 }))] }), nudgePosition === "right" && optionNudgeElement] }));
}
else {
optionContent = (jsxs(Fragment, { children: [jsxs("div", { style: theme.dropdown.content, children: [jsx("span", { style: theme.dropdown.label, children: option.label }), isOptionSelected && (jsx(FiCheck, { style: theme.dropdown.checkIcon, strokeWidth: 2.5 }))] }), optionNudgeElement] }));
}
return (jsx("li", { style: {
...theme.dropdown.option,
"--item-hover-bg": theme.dropdown.optionHover.background,
...(isOptionSelected ? theme.dropdown.optionSelected : {}),
}, onClick: () => handleSelect(option.value), className: styles.dropdownItem, children: optionContent }, option.value));
}) }))] }));
}
export { DropdownMenu };
//# sourceMappingURL=DropdownMenu.js.map