diff --git a/client/src/features/editor/components/TypeOptionModal/TypeOptionModal.tsx b/client/src/features/editor/components/TypeOptionModal/TypeOptionModal.tsx new file mode 100644 index 00000000..377dc580 --- /dev/null +++ b/client/src/features/editor/components/TypeOptionModal/TypeOptionModal.tsx @@ -0,0 +1,92 @@ +import { motion } from "framer-motion"; +import { useEffect, useRef, useState } from "react"; +import { createPortal } from "react-dom"; +import { OPTION_CATEGORIES } from "@src/constants/option"; +import { modalContainer, optionModal, optionTypeButton } from "../OptionModal/OptionModal.style"; +import { modal } from "../OptionModal/OptionModal.animaiton"; +import { ElementType } from "node_modules/@noctaCrdt/Interfaces"; + +interface TypeOptionModalProps { + isOpen: boolean; + onClose: () => void; + onTypeSelect: (type: ElementType) => void; + position: { top: number; left: number }; +} + +export const TypeOptionModal = ({ + isOpen, + onClose, + onTypeSelect, + position, +}: TypeOptionModalProps) => { + const modalRef = useRef(null); + const [selectedIndex, setSelectedIndex] = useState(0); + const options = OPTION_CATEGORIES.TYPE.options; + + const handleKeyDown = (e: React.KeyboardEvent) => { + switch (e.key) { + case "ArrowUp": + e.preventDefault(); + setSelectedIndex((prev) => (prev <= 0 ? options.length - 1 : prev - 1)); + break; + + case "ArrowDown": + e.preventDefault(); + setSelectedIndex((prev) => (prev >= options.length - 1 ? 0 : prev + 1)); + break; + + case "Enter": + e.preventDefault(); + onTypeSelect(options[selectedIndex].id); + + onClose(); + break; + + case "Escape": + e.preventDefault(); + onClose(); + break; + } + }; + + useEffect(() => { + if (isOpen && modalRef.current) { + modalRef.current.focus(); + } + }, [isOpen]); + + if (!isOpen) return null; + + return createPortal( + +
+ {options.map((option, index) => ( + + ))} +
+
, + document.body, + ); +};