Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
15 changes: 15 additions & 0 deletions packages/react/src/components/Popovers/FloatingUIReturns.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {
ElementProps,
UseFloatingReturn,
UseInteractionsReturn,
} from "@floating-ui/react";

export type FloatingUIReturns = {
useFloatingReturn: UseFloatingReturn;
isMounted: boolean;
styles: React.CSSProperties;
status: "unmounted" | "initial" | "open" | "close";
dismiss?: ElementProps;
hover?: ElementProps;
useInteractionsReturn: UseInteractionsReturn;
};
59 changes: 48 additions & 11 deletions packages/react/src/components/Popovers/GenericPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +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";
import { FloatingUIReturns } from "./FloatingUIReturns.js";

export const GenericPopoverContext = createContext<
FloatingUIReturns | undefined
>(undefined);

export type GenericPopoverReference =
| {
Expand Down Expand Up @@ -81,10 +92,11 @@ export const GenericPopover = (
children: ReactNode;
},
) => {
const { refs, floatingStyles, context } = useFloating<HTMLDivElement>({
const useFloatingReturn = useFloating<HTMLDivElement>({
whileElementsMounted: autoUpdate,
...props.useFloatingOptions,
});
const { refs, floatingStyles, context } = useFloatingReturn;

const { isMounted, styles } = useTransitionStyles(
context,
Expand All @@ -101,7 +113,8 @@ export const GenericPopover = (
// not even be managed by React, so we may be unable to set them. Seems like
// `refs.setReferences` attaches most of the same listeners anyway, but
// possible both are needed.
const { getFloatingProps } = useInteractions([dismiss, hover]);
const useInteractionsReturn = useInteractions([dismiss, hover]);
const { getFloatingProps } = useInteractionsReturn;

const innerHTML = useRef<string>("");
const ref = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -167,17 +180,41 @@ 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 }}
/>
<GenericPopoverContext
value={{
useFloatingReturn,
isMounted,
styles,
status,
dismiss,
hover,
useInteractionsReturn,
}}
>
<div
ref={mergedRefs}
{...mergedProps}
dangerouslySetInnerHTML={{ __html: innerHTML.current }}
/>
</GenericPopoverContext>
);
}

return (
<div ref={mergedRefs} {...mergedProps}>
{props.children}
</div>
<GenericPopoverContext
value={{
useFloatingReturn,
isMounted,
styles,
status,
dismiss,
hover,
useInteractionsReturn,
}}
>
<div ref={mergedRefs} {...mergedProps}>
{props.children}
</div>
</GenericPopoverContext>
);
};
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,8 +114,8 @@ 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(),
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 { GenericPopoverContext } 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(GenericPopoverContext)?.useFloatingReturn.update;
useEffect(() => {
update?.();
}, [loadingState, update]);

useCloseSuggestionMenuNoItems(items, usedQuery, closeMenu);

const { selectedIndex } = useSuggestionMenuKeyboardNavigation(
Expand Down
Loading