CoastalCommitsPastes/client/components/button-dropdown/index.tsx
2022-04-09 17:48:19 -07:00

116 lines
2.7 KiB
TypeScript

import Button from "@components/button"
import React, { useCallback, useEffect } from "react"
import { useState } from "react"
import styles from "./dropdown.module.css"
import DownIcon from "@geist-ui/icons/arrowDown"
type Props = {
type?: "primary" | "secondary"
loading?: boolean
disabled?: boolean
className?: string
iconHeight?: number
}
type Attrs = Omit<React.HTMLAttributes<any>, keyof Props>
type ButtonDropdownProps = Props & Attrs
const ButtonDropdown: React.FC<
React.PropsWithChildren<ButtonDropdownProps>
> = ({ type, className, disabled, loading, iconHeight = 24, ...props }) => {
const [visible, setVisible] = useState(false)
const [dropdown, setDropdown] = useState<HTMLDivElement | null>(null)
const onClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
setVisible(!visible)
}
const onBlur = () => {
setVisible(false)
}
const onMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
}
const onMouseUp = (e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
}
const onMouseLeave = (e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
setVisible(false)
}
const onKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.key === "Escape") {
setVisible(false)
}
}
const onClickOutside = useCallback(
() => (e: React.MouseEvent<HTMLDivElement>) => {
if (dropdown && !dropdown.contains(e.target as Node)) {
setVisible(false)
}
},
[dropdown]
)
useEffect(() => {
if (visible) {
document.addEventListener("mousedown", onClickOutside)
} else {
document.removeEventListener("mousedown", onClickOutside)
}
return () => {
document.removeEventListener("mousedown", onClickOutside)
}
}, [visible, onClickOutside])
if (!Array.isArray(props.children)) {
return null
}
return (
<div
className={`${styles.main} ${className}`}
onMouseDown={onMouseDown}
onMouseUp={onMouseUp}
onMouseLeave={onMouseLeave}
onKeyDown={onKeyDown}
onBlur={onBlur}
>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "flex-end"
}}
>
{props.children[0]}
<Button
style={{ height: iconHeight, width: iconHeight }}
className={styles.icon}
onClick={() => setVisible(!visible)}
>
<DownIcon />
</Button>
</div>
{visible && (
<div className={`${styles.dropdown}`}>
<div className={`${styles.dropdownContent}`}>
{props.children.slice(1)}
</div>
</div>
)}
</div>
)
}
export default ButtonDropdown