Skip to content

Commit

Permalink
Use popper to position the suggestions panel
Browse files Browse the repository at this point in the history
  • Loading branch information
kudlajz committed Oct 30, 2024
1 parent c4866bf commit 19398e9
Show file tree
Hide file tree
Showing 9 changed files with 42 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ export function SearchInput<T = unknown>({
suggestions,
onClose: handleClose,
onSelect,
origin: rootRef.current,
children: suggestions.map((suggestion) =>
renderSuggestion({
suggestion,
Expand Down Expand Up @@ -184,6 +185,7 @@ export namespace SearchInput {
activeElement: HTMLElement | undefined;
activeSuggestion: Suggestion<T> | undefined;
loading: boolean;
origin: HTMLElement | null;
query: string;
suggestions: Suggestion<T>[];
onClose: () => void;
Expand Down Expand Up @@ -227,13 +229,15 @@ function defaultRenderSuggestions<T>({
activeElement,
query,
suggestions,
origin,
children,
}: SearchInput.Props.Suggestions<T>) {
return (
<SearchInput.Suggestions<T>
activeElement={activeElement}
query={query}
suggestions={suggestions}
origin={origin}
>
{children}
</SearchInput.Suggestions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
.Suggestions {
display: flex;
flex-direction: column;
width: 100%;
}
50 changes: 31 additions & 19 deletions packages/slate-editor/src/components/SearchInput/Suggestions.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
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';
import { usePopper } from 'react-popper';

import { useFunction } from '#lib';

Expand All @@ -12,40 +15,54 @@ import type { Suggestion } from './types';

export interface Props<T> extends HTMLAttributes<HTMLDivElement> {
activeElement: HTMLElement | undefined;
minHeight?: number;
footer?: ReactNode;
maxHeight?: number;
minHeight?: number;
origin: HTMLElement | null;
query: string;
suggestions: Suggestion<T>[];
footer?: ReactNode;
}

export function Suggestions<T>({
activeElement,
children,
className,
footer,
minHeight = 200,
maxHeight = 500,
minHeight = 200,
origin,
query,
suggestions,
...attributes
}: Props<T>) {
const [height, setHeight] = useState<number>();
const [calculatedMaxHeight, setMaxHeight] = useState<number>();
const container = useRef<HTMLDivElement | null>(null);
const childrenContainer = useRef<HTMLDivElement | null>(null);
const [scrollarea, setScrollarea] = useState<FancyScrollbars | null>(null);

const popper = usePopper(origin, container.current, {
modifiers: [
{
name: 'flip',
enabled: true,
options: {
fallbackPlacements: ['top'],
},
} satisfies Partial<FlipModifier>,
{
name: 'preventOverflow',
enabled: true,
options: {
altAxis: true,
mainAxis: true,
},
} satisfies Partial<PreventOverflowModifier>
],
placement: 'bottom',
});

const updatePanelSize = 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(() => {
Expand All @@ -71,7 +88,8 @@ export function Suggestions<T>({
return (
<Panel
{...attributes}
style={{ maxHeight: calculatedMaxHeight, ...attributes.style }}
{...popper.attributes.popper}
style={{ maxHeight, ...attributes.style, ...popper.styles.popper }}
ref={container}
className={classNames(className, styles.Suggestions)}
footer={footer}
Expand All @@ -82,9 +100,3 @@ export function Suggestions<T>({
</Panel>
);
}

function clamp(num: number, min: number, max: number) {
if (num < min) return min;
if (num > max) return max;
return num;
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export function ContactPlaceholderElement({
query={props.query}
suggestions={props.suggestions}
footer={renderSuggestionsFooter?.(props)}
origin={props.origin}
>
{props.children}
</SearchInput.Suggestions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export function CoveragePlaceholderElement({
query={props.query}
suggestions={props.suggestions}
footer={renderSuggestionsFooter?.({ ...props, onMode })}
origin={props.origin}
>
{props.children}
</SearchInput.Suggestions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export function GalleryBookmarkPlaceholderElement({
query={props.query}
suggestions={props.suggestions}
footer={renderSuggestionsFooter?.(props)}
origin={props.origin}
>
{props.children}
</SearchInput.Suggestions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export function InlineContactPlaceholderElement({
query={props.query}
suggestions={props.suggestions}
footer={renderSuggestionsFooter?.(props)}
origin={props.origin}
>
{props.children}
</SearchInput.Suggestions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export function StoryBookmarkPlaceholderElement({
query={props.query}
suggestions={props.suggestions}
footer={renderSuggestionsFooter?.(props)}
origin={props.origin}
>
{props.children}
</SearchInput.Suggestions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export function StoryEmbedPlaceholderElement({
query={props.query}
suggestions={props.suggestions}
footer={renderSuggestionsFooter?.(props)}
origin={props.origin}
>
{props.children}
</SearchInput.Suggestions>
Expand Down

0 comments on commit 19398e9

Please sign in to comment.