patch slashcommand popup to be usePortal

This commit is contained in:
Timothy Carambat 2026-03-10 17:47:57 -07:00
parent 21ac874cfa
commit 31ffe941d8

View File

@ -1,4 +1,5 @@
import { useState, useRef, useEffect } from "react"; import { useState, useRef, useEffect } from "react";
import { createPortal } from "react-dom";
import { DotsThree } from "@phosphor-icons/react"; import { DotsThree } from "@phosphor-icons/react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@ -13,6 +14,7 @@ export default function SlashCommandRow({
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [menuOpen, setMenuOpen] = useState(false); const [menuOpen, setMenuOpen] = useState(false);
const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 });
const menuRef = useRef(null); const menuRef = useRef(null);
const menuBtnRef = useRef(null); const menuBtnRef = useRef(null);
@ -32,6 +34,16 @@ export default function SlashCommandRow({
return () => document.removeEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside);
}, [menuOpen]); }, [menuOpen]);
useEffect(() => {
if (menuOpen && menuBtnRef.current) {
const rect = menuBtnRef.current.getBoundingClientRect();
setMenuPosition({
top: rect.bottom + window.scrollY,
left: rect.right + window.scrollX - 120,
});
}
}, [menuOpen]);
return ( return (
<div <div
onClick={onClick} onClick={onClick}
@ -64,35 +76,42 @@ export default function SlashCommandRow({
<DotsThree size={16} weight="bold" /> <DotsThree size={16} weight="bold" />
</button> </button>
{menuOpen && ( {menuOpen &&
<div createPortal(
ref={menuRef} <div
className="absolute right-0 top-full z-50 bg-zinc-800 light:bg-white border border-zinc-700 light:border-slate-300 rounded-lg shadow-lg min-w-[120px] flex flex-col overflow-hidden" ref={menuRef}
> style={{
<button position: "fixed",
type="button" top: menuPosition.top,
className="border-none px-3 py-1.5 text-xs text-white light:text-slate-900 hover:bg-zinc-700 light:hover:bg-slate-100 cursor-pointer text-left" left: menuPosition.left,
onClick={(e) => {
e.stopPropagation();
setMenuOpen(false);
onEdit?.();
}} }}
className="z-[9999] bg-zinc-800 light:bg-white border border-zinc-700 light:border-slate-300 rounded-lg shadow-lg min-w-[120px] flex flex-col overflow-hidden"
> >
{t("chat_window.edit")} <button
</button> type="button"
<button className="border-none px-3 py-1.5 text-xs text-white light:text-slate-900 hover:bg-zinc-700 light:hover:bg-slate-100 cursor-pointer text-left"
type="button" onClick={(e) => {
className="border-none px-3 py-1.5 text-xs text-white light:text-slate-900 hover:bg-zinc-700 light:hover:bg-slate-100 cursor-pointer text-left" e.stopPropagation();
onClick={(e) => { setMenuOpen(false);
e.stopPropagation(); onEdit?.();
setMenuOpen(false); }}
onPublish?.(); >
}} {t("chat_window.edit")}
> </button>
{t("chat_window.publish")} <button
</button> type="button"
</div> className="border-none px-3 py-1.5 text-xs text-white light:text-slate-900 hover:bg-zinc-700 light:hover:bg-slate-100 cursor-pointer text-left"
)} onClick={(e) => {
e.stopPropagation();
setMenuOpen(false);
onPublish?.();
}}
>
{t("chat_window.publish")}
</button>
</div>,
document.body
)}
</div> </div>
)} )}
</div> </div>