fix : slash menu overflow causing page scroll (#16303)

Issue : #15794 

## Context
As shown in the linked issue, slash menu dropdown for rich text editor
at bottom of notes editor screen extended beyond the page container,
causing main page to scroll.

## Changes Made
Added floating UI's `flip()` middleware to the component in
`CustomSlashMenu.tsx` so that the menu automatically flips upward when
detecting not enough space for dropdown. This prevents overflow beyond
the page container.

Additionally, added offset to menu when flipping up to prevent
overlapping of the current line / caret.

## Results


https://github.com/user-attachments/assets/47a79468-779c-4fd7-8e22-2b496456ea92

---------

Co-authored-by: Hyeonjae Park <hyeonjaep@Hyeonjaes-MacBook-Pro.local>
Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
This commit is contained in:
kpark1208
2025-12-09 05:52:58 -05:00
committed by GitHub
parent 7e8151355d
commit e5fea85351
@@ -1,5 +1,6 @@
import { useBlockNoteEditor } from '@blocknote/react';
import styled from '@emotion/styled';
import { autoUpdate, useFloating } from '@floating-ui/react';
import { autoUpdate, flip, offset, useFloating } from '@floating-ui/react';
import { motion } from 'framer-motion';
import { useEffect } from 'react';
import { createPortal } from 'react-dom';
@@ -7,9 +8,9 @@ import { createPortal } from 'react-dom';
import { SLASH_MENU_DROPDOWN_CLICK_OUTSIDE_ID } from '@/ui/input/constants/SlashMenuDropdownClickOutsideId';
import { SLASH_MENU_LIST_ID } from '@/ui/input/constants/SlashMenuListId';
import { CustomSlashMenuListItem } from '@/ui/input/editor/components/CustomSlashMenuListItem';
import {
type CustomSlashMenuProps,
type SuggestionItem,
import type {
CustomSlashMenuProps,
SuggestionItem,
} from '@/ui/input/editor/components/types';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@@ -29,9 +30,32 @@ export const CustomSlashMenu = ({
items,
selectedIndex,
}: CustomSlashMenuProps) => {
const editor = useBlockNoteEditor();
const currentBlock = editor?.getTextCursorPosition()?.block;
const blockType = currentBlock?.type;
const headingLevel =
blockType === 'heading' ? (currentBlock?.props?.level as number) : null;
const getOffsetValue = (placement: string) => {
if (!placement.startsWith('top')) return 0;
switch (headingLevel) {
case 1:
return 65;
case 2:
return 50;
case 3:
return 45;
default:
return 40;
}
};
const { refs, floatingStyles } = useFloating({
placement: 'bottom-start',
whileElementsMounted: autoUpdate,
middleware: [flip(), offset(({ placement }) => getOffsetValue(placement))],
});
const { setSelectedItemId } = useSelectableList(SLASH_MENU_LIST_ID);