fix: replace Portal with inline block to avoid Radix pointer-event capture
This commit is contained in:
parent
68667fea59
commit
d04f03b6b1
|
|
@ -8,7 +8,6 @@ import {
|
||||||
import { api } from '@/api';
|
import { api } from '@/api';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import * as Portal from '@radix-ui/react-portal';
|
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
|
|
||||||
// Debounce helper
|
// Debounce helper
|
||||||
|
|
@ -256,44 +255,33 @@ export default function BillMerchantRules({ billId, onRulesChanged }) {
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Suggestions — rendered in a Portal so the list escapes the BillModal's
|
{/* Suggestions — inline block, no absolute positioning.
|
||||||
overflow-y-auto container and is fully clickable regardless of scroll */}
|
Avoids overflow-y-auto clipping AND Radix Dialog pointer-event capture. */}
|
||||||
{showSuggestions && filteredSuggestions.length > 0 && inputRef.current && (
|
{showSuggestions && filteredSuggestions.length > 0 && (
|
||||||
<Portal.Root>
|
<div className="overflow-hidden rounded-lg border border-border/80 bg-card shadow-sm">
|
||||||
<div
|
<p className="border-b border-border/50 px-3 py-1.5 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground">
|
||||||
style={{
|
Recent unmatched transactions
|
||||||
position: 'fixed',
|
</p>
|
||||||
top: inputRef.current.getBoundingClientRect().bottom + 4,
|
<div className="max-h-40 overflow-y-auto">
|
||||||
left: inputRef.current.getBoundingClientRect().left,
|
{filteredSuggestions.map(s => {
|
||||||
width: inputRef.current.getBoundingClientRect().width,
|
const amountVal = Math.abs(Number(s.amount || 0)) / 100;
|
||||||
zIndex: 9999,
|
return (
|
||||||
}}
|
<button
|
||||||
className="overflow-hidden rounded-lg border border-border/80 bg-card shadow-md"
|
key={s.id}
|
||||||
>
|
type="button"
|
||||||
<p className="border-b border-border/50 px-3 py-1.5 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground">
|
className="flex w-full items-center gap-3 px-3 py-2 text-left text-xs hover:bg-muted/50 focus:bg-muted/50 focus:outline-none"
|
||||||
Recent unmatched transactions
|
onMouseDown={e => { e.preventDefault(); pickSuggestion(s); }}
|
||||||
</p>
|
>
|
||||||
<div className="max-h-48 overflow-y-auto">
|
<Building2 className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
||||||
{filteredSuggestions.map(s => {
|
<span className="min-w-0 flex-1 truncate font-medium">{s.label}</span>
|
||||||
const amountVal = Math.abs(Number(s.amount || 0)) / 100;
|
<span className="shrink-0 font-mono text-muted-foreground tabular-nums">
|
||||||
return (
|
${amountVal.toFixed(2)}
|
||||||
<button
|
</span>
|
||||||
key={s.id}
|
</button>
|
||||||
type="button"
|
);
|
||||||
className="flex w-full items-center gap-3 px-3 py-2 text-left text-xs hover:bg-muted/50 focus:bg-muted/50 focus:outline-none"
|
})}
|
||||||
onMouseDown={e => { e.preventDefault(); pickSuggestion(s); }}
|
|
||||||
>
|
|
||||||
<Building2 className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
|
||||||
<span className="min-w-0 flex-1 truncate font-medium">{s.label}</span>
|
|
||||||
<span className="shrink-0 font-mono text-muted-foreground tabular-nums">
|
|
||||||
${amountVal.toFixed(2)}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Portal.Root>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Conflict warning */}
|
{/* Conflict warning */}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue