flowbite-react
Version:
Official React components built for Flowbite and Tailwind CSS
165 lines (162 loc) • 5.37 kB
JavaScript
'use client';
import { jsxs, jsx } from 'react/jsx-runtime';
import { useListNavigation, useTypeahead, FloatingFocusManager, FloatingList } from '@floating-ui/react';
import { useState, useRef, useCallback, useMemo, useEffect, cloneElement } from 'react';
import { HiOutlineChevronDown, HiOutlineChevronUp, HiOutlineChevronRight, HiOutlineChevronLeft } from 'react-icons/hi';
import { twMerge } from 'tailwind-merge';
import { mergeDeep } from '../../helpers/merge-deep.mjs';
import { useBaseFLoating, useFloatingInteractions } from '../../hooks/use-floating.mjs';
import { getTheme } from '../../theme-store/index.mjs';
import { Button } from '../Button/Button.mjs';
import '../Button/ButtonGroup.mjs';
import { DropdownContext } from './DropdownContext.mjs';
import { DropdownDivider } from './DropdownDivider.mjs';
import { DropdownHeader } from './DropdownHeader.mjs';
import { DropdownItem } from './DropdownItem.mjs';
const icons = {
top: HiOutlineChevronUp,
right: HiOutlineChevronRight,
bottom: HiOutlineChevronDown,
left: HiOutlineChevronLeft
};
const Trigger = ({
refs,
children,
inline,
theme,
disabled,
setButtonWidth,
getReferenceProps,
renderTrigger,
...buttonProps
}) => {
const ref = refs.reference;
const a11yProps = getReferenceProps();
useEffect(() => {
if (ref.current) {
setButtonWidth?.(ref.current.clientWidth);
}
}, [ref, setButtonWidth]);
if (renderTrigger) {
const triggerElement = renderTrigger(theme);
return cloneElement(triggerElement, { ref: refs.setReference, disabled, ...a11yProps, ...triggerElement.props });
}
return inline ? /* @__PURE__ */ jsx("button", { type: "button", ref: refs.setReference, className: theme?.inlineWrapper, disabled, ...a11yProps, children }) : /* @__PURE__ */ jsx(Button, { ...buttonProps, disabled, type: "button", ref: refs.setReference, ...a11yProps, children });
};
const DropdownComponent = ({
children,
className,
dismissOnClick = true,
theme: customTheme = {},
renderTrigger,
...props
}) => {
const [open, setOpen] = useState(false);
const [activeIndex, setActiveIndex] = useState(null);
const [selectedIndex, setSelectedIndex] = useState(null);
const [buttonWidth, setButtonWidth] = useState(void 0);
const elementsRef = useRef([]);
const labelsRef = useRef([]);
const theme = mergeDeep(getTheme().dropdown, customTheme);
const theirProps = props;
const dataTestId = props["data-testid"] || "flowbite-dropdown-target";
const {
placement = props.inline ? "bottom-start" : "bottom",
trigger = "click",
label,
inline,
arrowIcon = true,
...buttonProps
} = theirProps;
const handleSelect = useCallback((index) => {
setSelectedIndex(index);
setOpen(false);
}, []);
const handleTypeaheadMatch = useCallback(
(index) => {
if (open) {
setActiveIndex(index);
} else {
handleSelect(index);
}
},
[open, handleSelect]
);
const { context, floatingStyles, refs } = useBaseFLoating({
open,
setOpen,
placement
});
const listNav = useListNavigation(context, {
listRef: elementsRef,
activeIndex,
selectedIndex,
onNavigate: setActiveIndex
});
const typeahead = useTypeahead(context, {
listRef: labelsRef,
activeIndex,
selectedIndex,
onMatch: handleTypeaheadMatch
});
const { getReferenceProps, getFloatingProps, getItemProps } = useFloatingInteractions({
context,
role: "menu",
trigger,
interactions: [listNav, typeahead]
});
const Icon = useMemo(() => {
const [p] = placement.split("-");
return icons[p] ?? HiOutlineChevronDown;
}, [placement]);
return /* @__PURE__ */ jsxs(DropdownContext.Provider, { value: { theme, activeIndex, dismissOnClick, getItemProps, handleSelect }, children: [
/* @__PURE__ */ jsxs(
Trigger,
{
...buttonProps,
refs,
inline,
theme,
"data-testid": dataTestId,
className: twMerge(theme.floating.target, buttonProps.className),
setButtonWidth,
getReferenceProps,
renderTrigger,
children: [
label,
arrowIcon && /* @__PURE__ */ jsx(Icon, { className: theme.arrowIcon })
]
}
),
open && /* @__PURE__ */ jsx(FloatingFocusManager, { context, modal: false, children: /* @__PURE__ */ jsx(
"div",
{
ref: refs.setFloating,
style: { ...floatingStyles, minWidth: buttonWidth },
"data-testid": "flowbite-dropdown",
"aria-expanded": open,
...getFloatingProps({
className: twMerge(
theme.floating.base,
theme.floating.animation,
"duration-100",
!open && theme.floating.hidden,
theme.floating.style.auto,
className
)
}),
children: /* @__PURE__ */ jsx(FloatingList, { elementsRef, labelsRef, children: /* @__PURE__ */ jsx("ul", { className: theme.content, tabIndex: -1, children }) })
}
) })
] });
};
DropdownComponent.displayName = "Dropdown";
DropdownHeader.displayName = "Dropdown.Header";
DropdownDivider.displayName = "Dropdown.Divider";
const Dropdown = Object.assign(DropdownComponent, {
Item: DropdownItem,
Header: DropdownHeader,
Divider: DropdownDivider
});
export { Dropdown };
//# sourceMappingURL=Dropdown.mjs.map