@coinmeca/ui
Version:
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
263 lines • 12.9 kB
JSX
"use client";
import Image from "next/image";
import { useCallback, useEffect, useRef, useState } from "react";
import { Controls, Elements, Layouts } from "../../../components";
import { BottomSheet } from "../../../containers";
import { usePortal, usePositionTracker, useWindowSize } from "../../../hooks";
import Style, { Item, Option, Options } from "./Dropdown.styled";
export default function Dropdown(props) {
const { windowSize } = useWindowSize();
const dropdown = useRef(null);
const dropbox = useRef(null);
const theme = props?.theme === "light" || props?.theme === "dark" ? props?.theme : "light";
const type = props?.type;
const height = props?.height || 16;
const fit = props?.fit || false;
const scale = props?.scale || 1;
const chevron = typeof props?.chevron === "boolean" ? props?.chevron : true;
const placeholder = props?.placeholder || "Select";
const [options, setOptions] = useState(props?.options);
const [option, setOption] = useState(props?.option);
const [open, setOpen] = useState(props?.open || false);
const keyName = props?.keyName || "value";
const keyIndex = props?.keyName || 0;
const imgName = props?.imgName || "img";
const disabled = props?.disabled || false;
const device = props?.show;
const width = dropdown?.current?.offsetWidth;
// const width = dropdown?.current?.offsetWidth > dropbox?.current?.offsetWidth ? dropdown?.current?.offsetWidth : dropbox?.current?.offsetWidth;
const position = usePositionTracker(dropbox);
const handleSelect = (e, v, k) => {
if (disabled)
return;
setOpen(false);
closeSelectOnSheet();
if (!props?.fix)
setOption(v);
// typeof v?.[keyIndex] !== "undefined" ? option = v?.[keyIndex] : typeof v?.[keyName] !== "undefined" ? option = v?.[keyName] : option = v;
if (typeof v?.event === "function")
v.event(e);
if (typeof props?.onClickItem === "function")
props?.onClickItem(e, v, k);
};
const handleOpen = (e) => {
if (disabled)
return;
setOpen(!open);
openSelect(e);
if (!option)
return;
if (typeof props?.onClick === "function")
props?.onClick(e, option);
};
const handleClose = (e) => {
setOpen(false);
if (typeof props?.onClick === "function")
props?.onClick(e, option);
};
const calculatedPosition = useCallback((e) => {
const size = dropdown?.current?.getBoundingClientRect();
const horizon = windowSize.width < size?.left + position?.width;
const vertical = windowSize.height < size?.bottom + position?.height;
return {
...(vertical ? { bottom: windowSize?.height - size?.top } : { top: size?.bottom }),
...(horizon ? { right: windowSize?.width - size?.right } : { left: size?.left }),
};
}, [windowSize, dropbox, position]);
const Select = (visible, e) => (<Options $chevron={chevron} ref={visible === "hidden" ? dropbox : null} onBlur={() => visible !== "hidden" && closeSelect()} style={{
...(visible === "popup"
? {
...(theme && {
"--white": theme === "light" ? "255,255,255" : "0,0,0",
"--black": theme === "light" ? "0,0,0" : "255,255,255",
}),
position: "fixed",
fontSize: `${scale}em`,
background: `rgba(var(--white), var(--o0075))`,
color: `rgb(var(--white))`,
width: type !== "more" && width && `${width / (8 * scale)}em`,
backdropFilter: "blur(4em)",
zIndex: 200,
...calculatedPosition(),
...(open ? { maxHeight: "100em", overflowY: "hidden" } : { maxHeight: 0, overflowY: "scroll" }),
...props?.style?.options,
}
: visible === "hidden"
? {
visibility: "hidden",
pointerEvents: "none",
width: "100%",
minWidth: "min-content",
height: 0,
opacity: 0,
...(type === "more" && { position: "absolute" }),
}
: {
fontSize: `${scale}em`,
}),
transition: "max-height .3s ease",
// transitionProperty: "top, max-height",
// transitionDuration: ".3s",
// transitionTimingFunction: "ease-out",
}}>
{/* <div> */}
{options &&
options.length > 0 &&
options.map((v, k) => v && (<Item key={k} onClick={(e) => handleSelect(e, v, k)} data-disabled={typeof option !== "undefined" &&
(v?.[keyIndex] === option || v?.[keyName] === option || v === option)}>
<>
{typeof v?.[imgName] !== "undefined" && v?.[imgName] !== "" ? (<>
<Image src={v?.[imgName]} width={0} height={0} alt={""}/>
<span title={typeof v?.[keyIndex] !== "undefined"
? v?.[keyIndex]
: typeof v?.[keyName] !== "undefined"
? v?.[keyName]
: typeof v === "object"
? v?.toString()
: v}>
{typeof v?.[keyIndex] !== "undefined"
? v?.[keyIndex]
: typeof v?.[keyName] !== "undefined"
? v?.[keyName]
: typeof v === "object"
? v?.toString()
: v}
</span>
</>) : v.icon !== "" && typeof v.icon !== "undefined" ? (<>
<Elements.Icon icon={v?.icon}/>
<span title={typeof v?.[keyIndex] !== "undefined"
? v?.[keyIndex]
: typeof v?.[keyName] !== "undefined"
? v?.[keyName]
: typeof v === "object"
? v?.toString()
: v}>
{typeof v?.[keyIndex] !== "undefined"
? v?.[keyIndex]
: typeof v?.[keyName] !== "undefined"
? v?.[keyName]
: typeof v === "object"
? v?.toString()
: v}
</span>
</>) : (<span title={typeof v === "object"
? typeof v?.[keyIndex] !== "undefined"
? v?.[keyIndex]
: typeof v?.[keyName] !== "undefined"
? v?.[keyName]
: typeof v === "object"
? v?.toString()
: v
: typeof v === "object"
? v?.toString()
: v}>
{typeof v === "object"
? typeof v?.[keyIndex] !== "undefined"
? v?.[keyIndex]
: typeof v?.[keyName] !== "undefined"
? v?.[keyName]
: typeof v === "object"
? v?.toString()
: v
: typeof v === "object"
? v?.toString()
: v}
</span>)}
</>
</Item>))}
{/* </div> */}
</Options>);
const [openSelect, closeSelect] = usePortal((e) => Select("popup", e));
const [openSelectOnSheet, closeSelectOnSheet] = usePortal(<BottomSheet height={{ max: "60vh" }} onBlur={() => closeSelectOnSheet()} onClose={() => closeSelectOnSheet()} swipe>
<Layouts.Col style={{ padding: "2em" }} gap={2}>
<Layouts.Contents.InnerContent>{Select("sheet")}</Layouts.Contents.InnerContent>
<Controls.Button scale={scale} onClick={() => closeSelectOnSheet()}>
Close
</Controls.Button>
</Layouts.Col>
</BottomSheet>);
useEffect(() => {
return () => closeSelect();
}, []);
useEffect(() => {
setOption(props?.option);
}, [props?.option]);
useEffect(() => {
const options = props?.options;
if (Array.isArray(options) && !options[0]) {
let _options = [];
for (let i = 0; i < options.length; i++) {
if (Array.isArray(options[i])) {
let _option = {};
for (let j = 0; j < options[i].length; j++) {
_option = {
..._option,
[j]: options[i][j],
};
}
_options.push({ ..._option });
}
else {
_options.push({
[keyName]: options[i],
});
if (option === options[i]) {
setOption({
[keyName]: options[i],
});
}
}
}
setOptions(_options);
}
else {
setOptions(options);
}
}, [props?.options, option, keyName]);
return (<Style ref={dropdown} $open={open} $height={height} $fit={fit} $scale={scale} $disabled={disabled} $type={type} tabIndex={5} style={{
zIndex: open ? 10 : 1,
// maxWidth: width && `${width / 8}em`,
...props?.style,
}} onClick={(e) => (!props?.responsive ? handleOpen(e) : openSelectOnSheet())} onBlur={handleClose} title={props?.title} data-active={open} data-show={props?.show} data-hide={props?.hide}>
<Option $chevron={chevron} $type={type}>
<Item>
{type === "more" ? (<Controls.Button icon="more"/>) : type === "icon" ? (<Controls.Button icon={option?.icon}/>) : (<>
{option &&
(typeof option?.[imgName] === "string" ? (<Image src={option?.[imgName]} width={0} height={0} alt={""}/>) : (typeof option?.icon === "string" && <Elements.Icon icon={option?.icon}/>))}
<span title={typeof option === "undefined"
? undefined
: typeof option === "object"
? typeof option?.[keyIndex] !== "undefined"
? option?.[keyIndex]
: typeof option?.[keyName] !== "undefined"
? option?.[keyName]
: typeof option === "object"
? option?.toString()
: option
: typeof option?.alt === "undefined"
? typeof option === "object"
? option?.toString()
: option
: option?.title}>
{typeof option === "undefined"
? placeholder
: typeof option === "object"
? typeof option?.[keyIndex] !== "undefined"
? option?.[keyIndex]
: typeof option?.[keyName] !== "undefined"
? option?.[keyName]
: typeof option === "object"
? option?.toString()
: option
: typeof option === "object"
? option?.toString()
: option}
</span>
{chevron && <Elements.Icon icon="chevron-down-small"/>}
</>)}
</Item>
</Option>
{!props?.responsive && <>{Select("hidden")}</>}
</Style>);
}
//# sourceMappingURL=Dropdown.jsx.map