fix(web): portal remaining overlay menus
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { createPortal } from "react-dom";
|
||||
import { useState, useCallback, useEffect, useMemo, useRef } from "react";
|
||||
import { clsx } from "clsx";
|
||||
import type { StaffingRequirement } from "@capakraken/shared";
|
||||
@@ -13,6 +14,7 @@ import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
|
||||
import { formatCents } from "~/lib/format.js";
|
||||
import { ConfettiBurst } from "~/components/ui/ConfettiBurst.js";
|
||||
import { SuccessToast } from "~/components/ui/SuccessToast.js";
|
||||
import { useAnchoredOverlay } from "~/hooks/useAnchoredOverlay.js";
|
||||
|
||||
// ─── Constants ────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -307,7 +309,7 @@ function ResourcePersonPicker({ value, onChange }: { value: string; onChange: (v
|
||||
const [query, setQuery] = useState(value);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [debouncedSearch, setDebouncedSearch] = useState("");
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
// Debounce search query to avoid excessive API calls
|
||||
useEffect(() => {
|
||||
@@ -331,21 +333,18 @@ function ResourcePersonPicker({ value, onChange }: { value: string; onChange: (v
|
||||
setQuery(value);
|
||||
}, [value]);
|
||||
|
||||
// Close on outside click
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
function handleClick(e: MouseEvent) {
|
||||
if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
|
||||
setOpen(false);
|
||||
}
|
||||
}
|
||||
document.addEventListener("mousedown", handleClick);
|
||||
return () => document.removeEventListener("mousedown", handleClick);
|
||||
}, [open]);
|
||||
const { panelRef, position } = useAnchoredOverlay<HTMLInputElement>({
|
||||
open,
|
||||
onClose: () => setOpen(false),
|
||||
align: "start",
|
||||
matchTriggerWidth: true,
|
||||
triggerRef: inputRef,
|
||||
});
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="relative">
|
||||
<div className="relative">
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
value={query}
|
||||
onChange={(e) => {
|
||||
@@ -357,29 +356,40 @@ function ResourcePersonPicker({ value, onChange }: { value: string; onChange: (v
|
||||
placeholder="Search by name or EID…"
|
||||
className={INPUT_CLS}
|
||||
/>
|
||||
{open && filtered.length > 0 && (
|
||||
<ul
|
||||
className="absolute z-50 left-0 right-0 mt-1 bg-white border border-gray-200 rounded-lg shadow-lg overflow-hidden max-h-48 overflow-y-auto"
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
>
|
||||
{filtered.map((r) => (
|
||||
<li key={r.id}>
|
||||
<button
|
||||
type="button"
|
||||
onMouseDown={() => {
|
||||
onChange(r.displayName);
|
||||
setQuery(r.displayName);
|
||||
setOpen(false);
|
||||
}}
|
||||
className="w-full text-left px-3 py-2 text-sm flex items-baseline gap-2 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<span className="truncate">{r.displayName}</span>
|
||||
<span className="text-xs text-gray-400 font-mono shrink-0">{r.eid}</span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
{open && filtered.length > 0 && typeof document !== "undefined"
|
||||
? createPortal(
|
||||
<div
|
||||
ref={panelRef}
|
||||
className="fixed z-[9998] max-h-48 overflow-y-auto rounded-lg border border-gray-200 bg-white shadow-lg"
|
||||
style={{
|
||||
top: position.top,
|
||||
left: position.left,
|
||||
width: position.minWidth,
|
||||
}}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
>
|
||||
<ul>
|
||||
{filtered.map((r) => (
|
||||
<li key={r.id}>
|
||||
<button
|
||||
type="button"
|
||||
onMouseDown={() => {
|
||||
onChange(r.displayName);
|
||||
setQuery(r.displayName);
|
||||
setOpen(false);
|
||||
}}
|
||||
className="flex w-full items-baseline gap-2 px-3 py-2 text-left text-sm transition-colors hover:bg-gray-50"
|
||||
>
|
||||
<span className="truncate">{r.displayName}</span>
|
||||
<span className="shrink-0 font-mono text-xs text-gray-400">{r.eid}</span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>,
|
||||
document.body,
|
||||
)
|
||||
: null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user