diff --git a/src/components/FilterBuilder.tsx b/src/components/FilterBuilder.tsx index ebb2a4af..ff4cd66b 100644 --- a/src/components/FilterBuilder.tsx +++ b/src/components/FilterBuilder.tsx @@ -1,4 +1,5 @@ import { useState, useRef, useMemo, useEffect, useCallback } from 'react' +import { createPortal } from 'react-dom' import { Plus, X, CalendarBlank } from '@phosphor-icons/react' import { format, parseISO } from 'date-fns' import { Button } from '@/components/ui/button' @@ -146,18 +147,30 @@ function useOutsideClick(refs: React.RefObject[], onClose: ( }, [refs, onClose]) } -function WikilinkDropdown({ matches, selectedIndex, onSelect, onHover, menuRef }: { +function WikilinkDropdown({ matches, selectedIndex, onSelect, onHover, menuRef, anchorRef }: { matches: WikilinkMatch[] selectedIndex: number onSelect: (title: string) => void onHover: (index: number) => void menuRef: React.RefObject + anchorRef: React.RefObject }) { - return ( + const [pos, setPos] = useState<{ top: number; left: number; width: number } | null>(null) + + useEffect(() => { + const el = anchorRef.current + if (!el) return + const rect = el.getBoundingClientRect() + setPos({ top: rect.bottom + 2, left: rect.left, width: rect.width }) + }, [anchorRef, matches]) + + if (!pos) return null + + return createPortal(
{matches.map((item, index) => ( @@ -169,17 +182,18 @@ function WikilinkDropdown({ matches, selectedIndex, onSelect, onHover, menuRef } onMouseEnter={() => onHover(index)} > - {item.TypeIcon && } + {item.TypeIcon && } {item.title} {item.noteType && ( - + {item.noteType} )}
))} - + , + document.body, ) } @@ -261,7 +275,7 @@ function WikilinkValueInput({ value, entries, onChange }: { }, [onChange, resetIndex]) return ( -
+
)}
diff --git a/src/components/WikilinkSuggestionMenu.css b/src/components/WikilinkSuggestionMenu.css index 4d91c00d..a1505c73 100644 --- a/src/components/WikilinkSuggestionMenu.css +++ b/src/components/WikilinkSuggestionMenu.css @@ -10,3 +10,49 @@ max-width: 400px; z-index: 9999; } + +.wikilink-menu__item { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 5px 8px; + cursor: pointer; + font-size: 13px; + line-height: 1.4; +} + +.wikilink-menu__item:hover, +.wikilink-menu__item--selected { + background: hsl(var(--accent)); +} + +.wikilink-menu__title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + min-width: 0; +} + +.wikilink-menu__type { + flex-shrink: 0; + font-size: 10px; + line-height: 1.4; + white-space: nowrap; +} + +/* Compact variant for FilterBuilder inside dialogs */ +.wikilink-menu--filter { + font-size: 12px; + max-height: 240px; + overflow-y: auto; +} + +.wikilink-menu--filter .wikilink-menu__item { + padding: 4px 8px; + font-size: 12px; +} + +.wikilink-menu--filter .wikilink-menu__type { + font-size: 10px; +}