diff --git a/packages/slate-editor/src/components/SearchInput/Suggestions.tsx b/packages/slate-editor/src/components/SearchInput/Suggestions.tsx index aa47ac2de..ddb0c3b23 100644 --- a/packages/slate-editor/src/components/SearchInput/Suggestions.tsx +++ b/packages/slate-editor/src/components/SearchInput/Suggestions.tsx @@ -1,5 +1,4 @@ import type { FlipModifier } from '@popperjs/core/lib/modifiers/flip'; -import type { PreventOverflowModifier } from '@popperjs/core/lib/modifiers/preventOverflow'; import classNames from 'classnames'; import type { HTMLAttributes, ReactNode } from 'react'; import React, { useEffect, useRef, useState } from 'react'; @@ -16,6 +15,7 @@ import type { Suggestion } from './types'; export interface Props extends HTMLAttributes { activeElement: HTMLElement | undefined; footer?: ReactNode; + minHeight?: number; maxHeight?: number; origin: HTMLElement | null; query: string; @@ -27,13 +27,15 @@ export function Suggestions({ children, className, footer, - maxHeight = 500, + minHeight = 200, + maxHeight = 400, origin, query, suggestions, ...attributes }: Props) { const [height, setHeight] = useState(); + const [calculatedMaxHeight, setMaxHeight] = useState(); const container = useRef(null); const childrenContainer = useRef(null); const [scrollarea, setScrollarea] = useState(null); @@ -44,42 +46,51 @@ export function Suggestions({ name: 'flip', enabled: true, options: { - fallbackPlacements: ['top'], + fallbackPlacements: ['top-end'], }, } satisfies Partial, - { - name: 'preventOverflow', - enabled: true, - options: { - altAxis: true, - mainAxis: true, - }, - } satisfies Partial, ], - placement: 'bottom', + placement: 'bottom-end', }); const updatePanelSizeAndPosition = useFunction(() => { setHeight(childrenContainer.current?.getBoundingClientRect().height); + + if (container.current) { + const viewport = document.body.getBoundingClientRect(); + const rect = container.current.getBoundingClientRect(); + setMaxHeight(clamp(viewport.height - rect.top - 4, minHeight, maxHeight)); + } else { + setMaxHeight(undefined); + } }); - useEffect(updatePanelSizeAndPosition, [query, suggestions, maxHeight]); + useEffect(updatePanelSizeAndPosition, [ + query, + suggestions, + calculatedMaxHeight, + minHeight, + maxHeight, + ]); useEffect(() => { - if (activeElement) { - scrollarea?.ensureVisible(activeElement); + async function repositionPopper() { + await popper.update?.(); + updatePanelSizeAndPosition(); + + if (activeElement) { + scrollarea?.ensureVisible(activeElement); + } } - }, [scrollarea, activeElement]); - useEffect(() => { - popper.update?.(); - }, [height]); + repositionPopper(); + }, [activeElement, calculatedMaxHeight]); return ( ({ ); } + +function clamp(num: number, min: number, max: number) { + if (num < min) return min; + if (num > max) return max; + return num; +}