98 lines
2.5 KiB
TypeScript
98 lines
2.5 KiB
TypeScript
|
import React, { useEffect, useRef, useState } from "react";
|
||
|
import { theme } from "../../theme";
|
||
|
|
||
|
export interface DropdownProps {
|
||
|
onChange: (value: string) => void;
|
||
|
options: Array<{ label: string; value: string }>;
|
||
|
selected?: string;
|
||
|
open?: boolean;
|
||
|
}
|
||
|
|
||
|
const DropdownOptions: React.FC<DropdownProps> = (props) => {
|
||
|
return (
|
||
|
<div style={{}}>
|
||
|
{props.options.map(({ label, value }) => (
|
||
|
<div
|
||
|
style={{ display: "flex", padding: "4px 8px", cursor: "pointer" }}
|
||
|
onClick={() => {
|
||
|
props.onChange(value);
|
||
|
}}
|
||
|
>
|
||
|
<div style={{ marginRight: 10, width: 10 }}>
|
||
|
{props.selected === value ? "X" : ""}
|
||
|
</div>
|
||
|
<div>{label}</div>
|
||
|
</div>
|
||
|
))}
|
||
|
</div>
|
||
|
);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Temp comp until @nocodelytics/components is ready
|
||
|
* We'll use <Timeframe /> which will also include types
|
||
|
*/
|
||
|
export const Dropdown: React.FC<DropdownProps> = (props) => {
|
||
|
if (!props.options.length) {
|
||
|
throw new Error(`Dropdown requires options`);
|
||
|
}
|
||
|
const [isOpen, setIsOpen] = useState<boolean>(props.open || false);
|
||
|
const [selected, setSelected] = useState<string>(
|
||
|
props.selected || props.options[0].value
|
||
|
);
|
||
|
const ref = useRef<HTMLDivElement>(null);
|
||
|
|
||
|
useEffect(() => {
|
||
|
function handleClickOutside(event: Event) {
|
||
|
if (ref.current && !ref.current.contains(event.target as Node)) {
|
||
|
setIsOpen(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
document.addEventListener("mousedown", handleClickOutside);
|
||
|
return () => {
|
||
|
document.removeEventListener("mousedown", handleClickOutside);
|
||
|
};
|
||
|
}, [ref]);
|
||
|
|
||
|
return (
|
||
|
<div
|
||
|
ref={ref}
|
||
|
onClick={() => setIsOpen(!isOpen)}
|
||
|
style={{
|
||
|
position: "relative",
|
||
|
...theme.text.primary,
|
||
|
...theme.background.secondary,
|
||
|
...theme.border.primary,
|
||
|
cursor: "pointer",
|
||
|
maxWidth: 150,
|
||
|
}}
|
||
|
>
|
||
|
<div
|
||
|
style={{
|
||
|
padding: "0px 4px 0px 8px",
|
||
|
height: 24,
|
||
|
justifyContent: "unset",
|
||
|
gridAutoFlow: "column",
|
||
|
display: "grid",
|
||
|
alignItems: "center",
|
||
|
gap: 2,
|
||
|
}}
|
||
|
>
|
||
|
{props.options.find(({ value }) => selected === value)!.label}
|
||
|
</div>
|
||
|
{isOpen && (
|
||
|
<DropdownOptions
|
||
|
{...props}
|
||
|
selected={selected}
|
||
|
onChange={(value) => {
|
||
|
setIsOpen(false);
|
||
|
setSelected(value);
|
||
|
props.onChange(value);
|
||
|
}}
|
||
|
/>
|
||
|
)}
|
||
|
</div>
|
||
|
);
|
||
|
};
|