129 lines
4.4 KiB
JavaScript
129 lines
4.4 KiB
JavaScript
import React, { cloneElement, isValidElement, useEffect, useImperativeHandle, useRef, useState } from "react";
|
|
import { createPopper } from "@popperjs/core";
|
|
|
|
import tw, { styled } from "twin.macro"
|
|
import "styled-components/macro"
|
|
import { colors, colorsDisabled, colorsHover, textColors, textColorsHover } from "./Colors";
|
|
|
|
const DropDownItem = styled.button(({ type }) => [
|
|
// The common button styles added with the tw import
|
|
tw`text-sm text-left py-2 px-4 focus:outline-none block w-full whitespace-nowrap bg-transparent hover:bg-secondary disabled:bg-secondary text-primary hover:text-primary`,
|
|
|
|
// Use props to conditionally style your components
|
|
textColors[type], colorsHover[type]
|
|
])
|
|
export { DropDownItem }
|
|
|
|
let Dropdown = React.forwardRef(({ children, dropdownCss, onValueChanged, button, buttonProps, popper, placement, hover, ...props }, ref) => {
|
|
// dropdown props
|
|
const [overButton, setOverButton] = React.useState(false);
|
|
const [overDropDown, setOverDropDown] = React.useState(false);
|
|
|
|
const [dropdownPopoverShow, setDropdownPopoverShow] = React.useState(false);
|
|
|
|
const btnDropdownRef = React.createRef();
|
|
const popoverDropdownRef = React.createRef();
|
|
|
|
const openDropdownPopover = () => {
|
|
createPopper(btnDropdownRef.current, popoverDropdownRef.current, {
|
|
placement: placement ?? "bottom",
|
|
...popper
|
|
});
|
|
setDropdownPopoverShow(true);
|
|
|
|
onValueChanged && onValueChanged(true);
|
|
};
|
|
const closeDropdownPopover = () => {
|
|
setDropdownPopoverShow(false);
|
|
onValueChanged && onValueChanged(false);
|
|
};
|
|
|
|
function handleOutsideClick(event) {
|
|
let node = popoverDropdownRef?.current;
|
|
if (node && !node?.contains(event.target)) {
|
|
closeDropdownPopover();
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
if ("ontouchend" in window) {
|
|
document.addEventListener("touchend", handleOutsideClick)
|
|
} else {
|
|
document.addEventListener("click", handleOutsideClick)
|
|
}
|
|
// Specify how to clean up after this effect:
|
|
return function cleanup() {
|
|
document.removeEventListener("touchend", handleOutsideClick)
|
|
document.removeEventListener("click", handleOutsideClick)
|
|
};
|
|
});
|
|
|
|
useImperativeHandle(ref, () => ({
|
|
openDropdownPopover,
|
|
closeDropdownPopover
|
|
}))
|
|
|
|
let childrenWithProps = React.Children.map(children, (item) => {
|
|
if (!item)
|
|
return;
|
|
let click = item.props?.onClick;
|
|
let close = item.props?.closeOnClick ?? true;
|
|
return cloneElement(item, { key: children.indexOf ? children.indexOf(item) : 0, onClick: () => { close && closeDropdownPopover(); click && click() } });
|
|
});
|
|
|
|
return (
|
|
<>
|
|
<button
|
|
ref={btnDropdownRef}
|
|
onClick={(e) => {
|
|
e.preventDefault();
|
|
dropdownPopoverShow ? closeDropdownPopover() : openDropdownPopover();
|
|
}}
|
|
onMouseEnter={() => { setOverButton(true); hover && openDropdownPopover() }}
|
|
onMouseLeave={() => { setOverButton(false); (!overDropDown && hover) && closeDropdownPopover() }}
|
|
{...buttonProps}
|
|
>
|
|
{button}
|
|
</button>
|
|
<div
|
|
{...props}
|
|
ref={popoverDropdownRef}
|
|
onMouseEnter={() => { setOverDropDown(true); hover && openDropdownPopover() }}
|
|
onMouseLeave={() => { setOverDropDown(false); (!overButton && hover) && closeDropdownPopover() }}
|
|
css={[
|
|
(dropdownPopoverShow ? tw`block` : tw`hidden`),
|
|
tw`bg-trinary text-base z-50 float-left rounded-xl overflow-hidden text-left shadow-lg`,
|
|
dropdownCss,
|
|
props.css
|
|
]}
|
|
>
|
|
{dropdownPopoverShow && childrenWithProps}
|
|
</div>
|
|
</>
|
|
);
|
|
});
|
|
export default Dropdown;
|
|
|
|
export function DropdownMenu({ children, fallback, notSelectedOne = true, ...props }) {
|
|
let [selectedItem, setSelectedItem] = useState(0);
|
|
let ref = useRef()
|
|
|
|
let btn = fallback ?? "";
|
|
if (children && children.length > 0)
|
|
btn = children[selectedItem > children.length ? 0 : selectedItem]
|
|
|
|
return <>
|
|
<Dropdown ref={ref} button={btn} {...props}>
|
|
{
|
|
children && children.map((i) => {
|
|
if (children.indexOf(i) == selectedItem)
|
|
return;
|
|
return <DropDownItem key={children.indexOf(i)} onClick={() => setSelectedItem(children.indexOf(i))}>{i}</DropDownItem>
|
|
})
|
|
}
|
|
{
|
|
(children == null || children.length <= (notSelectedOne ? 1 : 0)) && <p tw="p-4 text-secondary">Nothing to show</p>
|
|
}
|
|
</Dropdown>
|
|
</>
|
|
} |