fix(web): portal remaining overlay menus

This commit is contained in:
2026-03-30 14:20:05 +02:00
parent ea2efabd7f
commit 27b0e38b93
3 changed files with 111 additions and 71 deletions
@@ -1,5 +1,6 @@
"use client";
import { createPortal } from "react-dom";
import {
closestCenter,
DndContext,
@@ -21,6 +22,7 @@ import { CSS } from "@dnd-kit/utilities";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
import { trpc } from "~/lib/trpc/client.js";
import { useAnchoredOverlay } from "~/hooks/useAnchoredOverlay.js";
// ---------------------------------------------------------------------------
// Types
@@ -212,6 +214,17 @@ function TagAdder({
.filter((t) => t.toLowerCase().includes(lower) && !existingTags.includes(t))
.slice(0, 8);
}, [value, allKnownTags, existingTags]);
const { panelRef, position } = useAnchoredOverlay<HTMLInputElement>({
open: showSuggestions && suggestions.length > 0,
onClose: () => {
setShowSuggestions(false);
if (!value.trim()) {
onClose();
}
},
align: "start",
triggerRef: inputRef,
});
function submit(tag: string) {
const trimmed = tag.trim();
@@ -250,24 +263,34 @@ function TagAdder({
placeholder="Tag..."
className="w-24 px-2 py-0.5 text-xs rounded-full border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-1 focus:ring-brand-400"
/>
{showSuggestions && suggestions.length > 0 && (
<div className="absolute z-20 top-full left-0 mt-1 w-40 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg py-1 max-h-32 overflow-y-auto">
{suggestions.map((s) => (
<button
key={s}
type="button"
onMouseDown={(e) => e.preventDefault()}
onClick={() => {
submit(s);
setShowSuggestions(false);
{showSuggestions && suggestions.length > 0 && typeof document !== "undefined"
? createPortal(
<div
ref={panelRef}
className="fixed z-[9998] max-h-32 w-40 overflow-y-auto rounded-lg border border-gray-200 bg-white py-1 shadow-lg dark:border-gray-700 dark:bg-gray-800"
style={{
top: position.top,
left: position.left,
}}
className="block w-full text-left px-3 py-1 text-xs text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"
>
<TagPill tag={s} />
</button>
))}
</div>
)}
{suggestions.map((s) => (
<button
key={s}
type="button"
onMouseDown={(e) => e.preventDefault()}
onClick={() => {
submit(s);
setShowSuggestions(false);
}}
className="block w-full text-left px-3 py-1 text-xs text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700"
>
<TagPill tag={s} />
</button>
))}
</div>,
document.body,
)
: null}
</div>
);
}