diff --git a/src/food-market.web/src/components/Field.tsx b/src/food-market.web/src/components/Field.tsx index 91aef2d..d788f0d 100644 --- a/src/food-market.web/src/components/Field.tsx +++ b/src/food-market.web/src/components/Field.tsx @@ -1,7 +1,8 @@ import { - Children, isValidElement, useEffect, useMemo, useRef, useState, + Children, isValidElement, useEffect, useLayoutEffect, useMemo, useRef, useState, type InputHTMLAttributes, type SelectHTMLAttributes, type ReactNode, type TextareaHTMLAttributes, } from 'react' +import { createPortal } from 'react-dom' import { ChevronDown } from 'lucide-react' import { cn } from '@/lib/utils' import { useOrgSettings } from '@/lib/useOrgSettings' @@ -88,6 +89,37 @@ export function Select({ const [createError, setCreateError] = useState(null) const wrapRef = useRef(null) const searchRef = useRef(null) + const dropdownRef = useRef(null) + const [pos, setPos] = useState<{ top: number; left: number; width: number; openUp: boolean } | null>(null) + + // Считаем позицию dropdown'а: предпочтительно вниз, но если внизу < 240px, + // а вверху больше — открываем вверх (auto-flip). Через portal в body + // — overflow-hidden родителей не режет. + const recomputePos = () => { + const el = wrapRef.current + if (!el) return + const r = el.getBoundingClientRect() + const spaceBelow = window.innerHeight - r.bottom + const spaceAbove = r.top + const openUp = spaceBelow < 240 && spaceAbove > spaceBelow + setPos({ + top: openUp ? r.top - 4 : r.bottom + 4, + left: r.left, + width: r.width, + openUp, + }) + } + useLayoutEffect(() => { + if (!open) return + recomputePos() + const onScrollOrResize = () => setOpen(false) + window.addEventListener('scroll', onScrollOrResize, true) + window.addEventListener('resize', onScrollOrResize) + return () => { + window.removeEventListener('scroll', onScrollOrResize, true) + window.removeEventListener('resize', onScrollOrResize) + } + }, [open]) const current = String(value ?? '') const selected = options.find((o) => o.value === current) @@ -102,9 +134,10 @@ export function Select({ useEffect(() => { if (!open) return const onDoc = (e: MouseEvent) => { - if (wrapRef.current && !wrapRef.current.contains(e.target as Node)) { - setOpen(false); setQuery('') - } + const t = e.target as Node + const inWrap = wrapRef.current?.contains(t) + const inDropdown = dropdownRef.current?.contains(t) + if (!inWrap && !inDropdown) { setOpen(false); setQuery('') } } document.addEventListener('mousedown', onDoc) return () => document.removeEventListener('mousedown', onDoc) @@ -156,8 +189,17 @@ export function Select({ {triggerLabel} - {open && ( -
+ {open && pos && createPortal( +
)} -
+
, + document.body, )}
)