import { HTMLAttributes, RefObject, useEffect, useRef, useState } from "react"; import { selectedOnChange } from "./selectorItem"; import { createPortal } from "react-dom"; import { Icon } from "@iconify/react"; import React from "react"; export type selectionType = string; interface PickerProps extends HTMLAttributes { selected: selectionType; selectionOnChange: selectedOnChange; displayContent: string; selectionItems: PickedItem; } export interface PickedItem { [key: string]: selectionType; } export default function Picker(props: PickerProps) { const itemListRef: RefObject = useRef(null); const buttonRef: RefObject = useRef(null); const [displayList, setDisplayList] = useState(false); const updatePosition = () => { if (itemListRef.current == null || buttonRef.current == null) { return; } const buttonRect = buttonRef.current.getBoundingClientRect(); const listRect = itemListRef.current.getBoundingClientRect(); // Align to center itemListRef.current.style.left = Math.max( Math.min( buttonRect.x + buttonRect.width / 2 - listRect.width / 2, window.screen.width - listRect.width - 16 ), 0 ) + "px"; if (window.screen.height - buttonRect.top < 192) { itemListRef.current.style.transformOrigin = "bottom center"; itemListRef.current.style.top = buttonRect.top - listRect.height - 16 + "px"; } else { itemListRef.current.style.top = buttonRect.y + buttonRect.height + 16 + "px"; } if (listRect.top + listRect.height > window.screen.height - 16) { itemListRef.current.style.height = window.screen.height - listRect.top - 12 + "px"; } else { itemListRef.current.style.height = "fit-content"; } }; useEffect(() => { updatePosition(); const handleResize = () => { updatePosition(); }; window.addEventListener("resize", handleResize); // Cleanup event listener on component unmount return () => { window.removeEventListener("resize", handleResize); }; }, [itemListRef, buttonRef]); function toggleDisplay(targetState?: boolean) { function hideList() { if (itemListRef.current) { itemListRef.current.style.transitionDuration = "200ms"; itemListRef.current.style.opacity = "0%"; } setTimeout(() => { setDisplayList(false); }, 200); } function showList() { setDisplayList(true); setTimeout(() => { if (!itemListRef.current || !buttonRef.current) { return; } updatePosition(); if (window.screen.height - buttonRef.current.getBoundingClientRect().top < 128) { itemListRef.current.style.transformOrigin = "bottom center"; } itemListRef.current.style.transitionDuration = "100ms"; itemListRef.current.style.opacity = "100%"; updatePosition(); const listRect = itemListRef.current.getBoundingClientRect(); if (listRect.top < 8) { itemListRef.current.style.height = window.screen.height - 8 + "px"; itemListRef.current.style.top = "8px"; } }, 20); } if (targetState === true) { showList(); } else if (targetState === false) { hideList(); } else if (displayList === true) { hideList(); } else { showList(); } } const { displayContent, selectionOnChange, selectionItems, selected, ...rest } = props; return ( { toggleDisplay(); }} > {displayContent} {displayList && ( )} ); } interface PickerListProps { selected: selectionType; selectionOnChange: selectedOnChange; selectionItems: PickedItem; toggleDisplay: Function; } const PickerList = React.forwardRef((props, ref) => { const { selected, selectionOnChange, selectionItems, toggleDisplay } = props; return createPortal( {toggleDisplay(false)}}> {Object.keys(selectionItems).map((key: string, index) => { return ( { selectionOnChange(key); toggleDisplay(false); }} > {selectionItems[key]} {key === selected && ( )} ); })} , document.body ); }); PickerList.displayName = "PickerList";