Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
8 changes: 0 additions & 8 deletions docs/app/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,6 @@ body {
box-shadow: unset !important;
}

.demo {
overflow: none;
}

.demo .bn-container {
position: relative;
}

.demo .bn-container:not(.bn-comment-editor),
.demo .bn-editor {
height: 100%;
Expand Down
12 changes: 12 additions & 0 deletions packages/core/src/extensions/SideMenu/SideMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -784,5 +784,17 @@ export const SideMenuExtension = createExtension(({ editor }) => {
view!.state!.show = false;
view!.emitUpdate(view!.state!);
},

/**
* Hides the side menu unless it is currently frozen (e.g. the drag
* handle menu is open). Used to dismiss the menu on scroll without
* interfering with open submenus.
*/
hideMenuIfNotFrozen() {
if (!view!.menuFrozen && view!.state!.show) {
view!.state!.show = false;
view!.emitUpdate(view!.state!);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
},
} as const;
});
38 changes: 36 additions & 2 deletions packages/react/src/components/Popovers/GenericPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
FloatingFocusManager,
useDismiss,
useFloating,
UseFloatingOptions,
useHover,
useInteractions,
useMergeRefs,
Expand Down Expand Up @@ -77,15 +78,48 @@ export function getMountedBoundingClientRectCache(
};
}

/**
* Merges two `whileElementsMounted` handlers into one. Both run when elements
* mount, and both cleanup functions are called on unmount.
*/
function mergeWhileElementsMounted(
a: UseFloatingOptions["whileElementsMounted"],
b: UseFloatingOptions["whileElementsMounted"],
): UseFloatingOptions["whileElementsMounted"] {
if (!a) {
return b;
}
if (!b) {
return a;
}

return (reference, floating, update) => {
const cleanupA = a(reference, floating, update);
const cleanupB = b(reference, floating, update);
return () => {
cleanupA?.();
cleanupB?.();
};
};
}

export const GenericPopover = (
props: FloatingUIOptions & {
reference?: GenericPopoverReference;
children: ReactNode;
},
) => {
const {
whileElementsMounted: _whileElementsMounted,
...restFloatingOptions
} = props.useFloatingOptions ?? {};

const { refs, floatingStyles, context } = useFloating<HTMLDivElement>({
whileElementsMounted: autoUpdate,
...props.useFloatingOptions,
whileElementsMounted: mergeWhileElementsMounted(
autoUpdate,
props.useFloatingOptions?.whileElementsMounted,
),
...restFloatingOptions,
});

const { isMounted, styles } = useTransitionStyles(
Expand Down
40 changes: 38 additions & 2 deletions packages/react/src/components/SideMenu/SideMenuController.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { SideMenuExtension } from "@blocknote/core/extensions";
import { FC, useMemo } from "react";
import { autoUpdate, ReferenceElement } from "@floating-ui/react";
import { FC, useCallback, useMemo } from "react";

import { useBlockNoteEditor } from "../../hooks/useBlockNoteEditor.js";
import { useExtensionState } from "../../hooks/useExtension.js";
import { BlockPopover } from "../Popovers/BlockPopover.js";
import { FloatingUIOptions } from "../Popovers/FloatingUIOptions.js";
Expand All @@ -11,6 +13,7 @@ export const SideMenuController = (props: {
sideMenu?: FC<SideMenuProps>;
floatingUIOptions?: Partial<FloatingUIOptions>;
}) => {
const editor = useBlockNoteEditor();
const state = useExtensionState(SideMenuExtension, {
selector: (state) => {
return state !== undefined
Expand All @@ -24,12 +27,45 @@ export const SideMenuController = (props: {

const { show, block } = state || {};

// Hides the side menu on ancestor scroll so it doesn't overflow outside
// the editor's scroll container.
const whileElementsMounted = useCallback(
(
reference: ReferenceElement,
floating: HTMLElement,
_update: () => void,
) => {
let initialized = false;
return autoUpdate(
reference,
floating,
() => {
if (!initialized) {
// autoUpdate calls this function once when the floating element is mounted
// we don't want to hide the menu in that case
initialized = true;
return;
}
editor.getExtension(SideMenuExtension)?.hideMenuIfNotFrozen();
},
{
ancestorScroll: true,
ancestorResize: false,
elementResize: false,
layoutShift: false,
},
);
},
[editor],
);

const floatingUIOptions = useMemo<FloatingUIOptions>(
() => ({
...props.floatingUIOptions,
useFloatingOptions: {
open: show,
placement: "left-start",
whileElementsMounted,
...props.floatingUIOptions?.useFloatingOptions,
},
useDismissProps: {
Expand All @@ -47,7 +83,7 @@ export const SideMenuController = (props: {
...props.floatingUIOptions?.elementProps,
},
}),
[props.floatingUIOptions, show],
[props.floatingUIOptions, show, whileElementsMounted],
);

const Component = props.sideMenu || SideMenu;
Expand Down
Loading