Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 29 additions & 13 deletions packages/react/src/components/Popovers/GenericPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,20 @@ import {
autoUpdate,
useHover,
} from "@floating-ui/react";
import { HTMLAttributes, ReactNode, useEffect, useRef } from "react";
import {
createContext,
HTMLAttributes,
ReactNode,
useEffect,
useRef,
} from "react";

import { FloatingUIOptions } from "./FloatingUIOptions.js";

export const GenericPopoverUpdateContext = createContext<
(() => void) | undefined
>(undefined);

export type GenericPopoverReference =
| {
// A DOM element to use as the reference element for the popover.
Expand Down Expand Up @@ -81,10 +91,12 @@ export const GenericPopover = (
children: ReactNode;
},
) => {
const { refs, floatingStyles, context } = useFloating<HTMLDivElement>({
whileElementsMounted: autoUpdate,
...props.useFloatingOptions,
});
const { refs, floatingStyles, context, update } = useFloating<HTMLDivElement>(
{
whileElementsMounted: autoUpdate,
...props.useFloatingOptions,
},
);

const { isMounted, styles } = useTransitionStyles(
context,
Expand Down Expand Up @@ -167,17 +179,21 @@ export const GenericPopover = (
// should be open. So without this fix, the popover just won't transition
// out and will instead appear to hide instantly.
return (
<div
ref={mergedRefs}
{...mergedProps}
dangerouslySetInnerHTML={{ __html: innerHTML.current }}
/>
<GenericPopoverUpdateContext value={update}>
<div
ref={mergedRefs}
{...mergedProps}
dangerouslySetInnerHTML={{ __html: innerHTML.current }}
/>
</GenericPopoverUpdateContext>
);
}

return (
<div ref={mergedRefs} {...mergedProps}>
{props.children}
</div>
<GenericPopoverUpdateContext value={update}>
<div ref={mergedRefs} {...mergedProps}>
{props.children}
</div>
</GenericPopoverUpdateContext>
);
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BlockSchema, InlineContentSchema, StyleSchema } from "@blocknote/core";
import { SuggestionMenu } from "@blocknote/core/extensions";
import { autoPlacement, offset, shift, size } from "@floating-ui/react";
import { flip, offset, shift, size } from "@floating-ui/react";
import { FC, useEffect, useMemo } from "react";

import { useBlockNoteEditor } from "../../../hooks/useBlockNoteEditor.js";
Expand Down Expand Up @@ -119,14 +119,14 @@ export function GridSuggestionMenuController<
offset(10),
// Flips the menu placement to maximize the space available, and prevents
// the menu from being cut off by the confines of the screen.
autoPlacement({
allowedPlacements: ["bottom-start", "top-start"],
flip({
crossAxis: false,
padding: 10,
}),
shift(),
size({
apply({ elements, availableHeight }) {
elements.floating.style.maxHeight = `${Math.max(0, availableHeight)}px`;
elements.floating.style.maxHeight = `${Math.min(600, availableHeight)}px`;
},
padding: 10,
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { BlockSchema, InlineContentSchema, StyleSchema } from "@blocknote/core";
import { FC, useCallback, useEffect } from "react";
import { FC, useCallback, useContext, useEffect } from "react";

import { useBlockNoteContext } from "../../../editor/BlockNoteContext.js";
import { useBlockNoteEditor } from "../../../hooks/useBlockNoteEditor.js";
import { GenericPopoverUpdateContext } from "../../Popovers/GenericPopover.js";
import { useCloseSuggestionMenuNoItems } from "../hooks/useCloseSuggestionMenuNoItems.js";
import { useLoadSuggestionMenuItems } from "../hooks/useLoadSuggestionMenuItems.js";
import { useGridSuggestionMenuKeyboardNavigation } from "./hooks/useGridSuggestionMenuKeyboardNavigation.js";
Expand Down Expand Up @@ -49,6 +50,17 @@ export function GridSuggestionMenuWrapper<Item>(props: {
getItems,
);

// If this component is used inside a `GenericPopover`, the position of the
// popover should be recalculated once all the items are fetched. This is
// because it may need to resize/shift/flip if the height of the suggestion
// menu with all items is too tall and overflows.
const update = useContext(GenericPopoverUpdateContext);
useEffect(() => {
update?.();
}, [loadingState, update]);

useCloseSuggestionMenuNoItems(items, usedQuery, closeMenu);

useCloseSuggestionMenuNoItems(items, usedQuery, closeMenu);

const { selectedIndex } = useGridSuggestionMenuKeyboardNavigation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
SuggestionMenu as SuggestionMenuExtension,
filterSuggestionItems,
} from "@blocknote/core/extensions";
import { autoPlacement, offset, shift, size } from "@floating-ui/react";
import { flip, offset, shift, size } from "@floating-ui/react";
import { FC, useEffect, useMemo } from "react";

import { useBlockNoteEditor } from "../../hooks/useBlockNoteEditor.js";
Expand Down Expand Up @@ -114,14 +114,14 @@ export function SuggestionMenuController<
offset(10),
// Flips the menu placement to maximize the space available, and prevents
// the menu from being cut off by the confines of the screen.
autoPlacement({
allowedPlacements: ["bottom-start", "top-start"],
flip({
crossAxis: false,
padding: 10,
}),
shift(),
size({
apply({ elements, availableHeight }) {
elements.floating.style.maxHeight = `${Math.max(0, availableHeight)}px`;
elements.floating.style.maxHeight = `${Math.min(600, availableHeight)}px`;
},
padding: 10,
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { BlockSchema, InlineContentSchema, StyleSchema } from "@blocknote/core";
import { FC, useCallback, useEffect } from "react";
import { FC, useCallback, useContext, useEffect } from "react";

import { useBlockNoteContext } from "../../editor/BlockNoteContext.js";
import { useBlockNoteEditor } from "../../hooks/useBlockNoteEditor.js";
import { GenericPopoverUpdateContext } from "../Popovers/GenericPopover.js";
import { useCloseSuggestionMenuNoItems } from "./hooks/useCloseSuggestionMenuNoItems.js";
import { useLoadSuggestionMenuItems } from "./hooks/useLoadSuggestionMenuItems.js";
import { useSuggestionMenuKeyboardNavigation } from "./hooks/useSuggestionMenuKeyboardNavigation.js";
Expand Down Expand Up @@ -47,6 +48,15 @@ export function SuggestionMenuWrapper<Item>(props: {
getItems,
);

// If this component is used inside a `GenericPopover`, the position of the
// popover should be recalculated once all the items are fetched. This is
// because it may need to resize/shift/flip if the height of the suggestion
// menu with all items is too tall and overflows.
const update = useContext(GenericPopoverUpdateContext);
useEffect(() => {
update?.();
}, [loadingState, update]);

useCloseSuggestionMenuNoItems(items, usedQuery, closeMenu);

const { selectedIndex } = useSuggestionMenuKeyboardNavigation(
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading