nej-react-components/Parts/DropDown.js
2022-03-04 13:57:28 +01:00

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>
</>
}