Skip to content

fix: Scrolling responsiveness & UI element overflow #250

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 4, 2023
Merged
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
4 changes: 2 additions & 2 deletions examples/vanilla/src/ui/blockSideMenuFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ export const blockSideMenuFactory: BlockSideMenuFactory<DefaultBlockSchema> = (
container.style.display = "block";
}

container.style.top = params.referenceRect.y + "px";
container.style.top = staticParams.getReferenceRect()!.y + "px";
container.style.left =
params.referenceRect.x - container.offsetWidth + "px";
staticParams.getReferenceRect()!.x - container.offsetWidth + "px";
},
hide: () => {
container.style.display = "none";
Expand Down
4 changes: 2 additions & 2 deletions examples/vanilla/src/ui/formattingToolbarFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ export const formattingToolbarFactory: FormattingToolbarFactory<
"bold" in staticParams.editor.getActiveStyles()
? "unset bold"
: "set bold";
container.style.top = params.referenceRect.y + "px";
container.style.left = params.referenceRect.x + "px";
container.style.top = staticParams.getReferenceRect()!.y + "px";
container.style.left = staticParams.getReferenceRect()!.x + "px";
},
hide: () => {
container.style.display = "none";
Expand Down
4 changes: 2 additions & 2 deletions examples/vanilla/src/ui/hyperlinkToolbarFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ export const hyperlinkToolbarFactory: HyperlinkToolbarFactory = (
container.style.display = "block";
}

container.style.top = params.referenceRect.y + "px";
container.style.left = params.referenceRect.x + "px";
container.style.top = staticParams.getReferenceRect()!.y + "px";
container.style.left = staticParams.getReferenceRect()!.x + "px";
},
hide: () => {
container.style.display = "none";
Expand Down
4 changes: 2 additions & 2 deletions examples/vanilla/src/ui/slashMenuFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ export const slashMenuFactory: SuggestionsMenuFactory<
container.style.display = "block";
}

container.style.top = params.referenceRect.y + "px";
container.style.left = params.referenceRect.x + "px";
container.style.top = staticParams.getReferenceRect()!.y + "px";
container.style.left = staticParams.getReferenceRect()!.x + "px";
},
hide: () => {
container.style.display = "none";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ export type BlockSideMenuStaticParams<BSchema extends BlockSchema> = {

freezeMenu: () => void;
unfreezeMenu: () => void;

getReferenceRect: () => DOMRect;
};

export type BlockSideMenuDynamicParams<BSchema extends BlockSchema> = {
block: Block<BSchema>;

referenceRect: DOMRect;
};

export type BlockSideMenu<BSchema extends BlockSchema> = EditorElement<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ export class BlockMenuView<BSchema extends BlockSchema> {
menuOpen = false;
menuFrozen = false;

private lastPosition: DOMRect | undefined;

constructor({
tiptapEditor,
editor,
Expand All @@ -272,9 +274,6 @@ export class BlockMenuView<BSchema extends BlockSchema> {
// Shows or updates menu position whenever the cursor moves, if the menu isn't frozen.
document.body.addEventListener("mousemove", this.onMouseMove, true);

// Makes menu scroll with the page.
document.addEventListener("scroll", this.onScroll);

// Hides and unfreezes the menu whenever the user selects the editor with the mouse or presses a key.
// TODO: Better integration with suggestions menu and only editor scope?
document.body.addEventListener("mousedown", this.onMouseDown, true);
Expand Down Expand Up @@ -463,20 +462,6 @@ export class BlockMenuView<BSchema extends BlockSchema> {
}
};

onScroll = () => {
// Editor itself may have padding or other styling which affects size/position, so we get the boundingRect of
// the first child (i.e. the blockGroup that wraps all blocks in the editor) for a more accurate bounding box.
const editorBoundingBox = (
this.ttEditor.view.dom.firstChild! as HTMLElement
).getBoundingClientRect();

this.horizontalPosAnchor = editorBoundingBox.x;

if (this.menuOpen) {
this.blockMenu.render(this.getDynamicParams(), false);
}
};

destroy() {
if (this.menuOpen) {
this.menuOpen = false;
Expand All @@ -487,7 +472,6 @@ export class BlockMenuView<BSchema extends BlockSchema> {
this.ttEditor.view.dom.removeEventListener("dragstart", this.onDragStart);
document.body.removeEventListener("drop", this.onDrop);
document.body.removeEventListener("mousedown", this.onMouseDown);
document.removeEventListener("scroll", this.onScroll);
document.body.removeEventListener("keydown", this.onKeyDown);
}

Expand Down Expand Up @@ -556,23 +540,32 @@ export class BlockMenuView<BSchema extends BlockSchema> {
unfreezeMenu: () => {
this.menuFrozen = false;
},
getReferenceRect: () => {
if (!this.menuOpen) {
if (this.lastPosition === undefined) {
throw new Error(
"Attempted to access block reference rect before rendering block side menu."
);
}

return this.lastPosition;
}

const blockContent = this.hoveredBlock!.firstChild! as HTMLElement;
const blockContentBoundingBox = blockContent.getBoundingClientRect();
if (this.horizontalPosAnchoredAtRoot) {
blockContentBoundingBox.x = this.horizontalPosAnchor;
}
this.lastPosition = blockContentBoundingBox;

return blockContentBoundingBox;
},
};
}

getDynamicParams(): BlockSideMenuDynamicParams<BSchema> {
const blockContent = this.hoveredBlock!.firstChild! as HTMLElement;
const blockContentBoundingBox = blockContent.getBoundingClientRect();

return {
block: this.editor.getBlock(this.hoveredBlock!.getAttribute("data-id")!)!,
referenceRect: new DOMRect(
this.horizontalPosAnchoredAtRoot
? this.horizontalPosAnchor
: blockContentBoundingBox.x,
blockContentBoundingBox.y,
blockContentBoundingBox.width,
blockContentBoundingBox.height
),
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ import { BlockSchema } from "../Blocks/api/blockTypes";

export type FormattingToolbarStaticParams<BSchema extends BlockSchema> = {
editor: BlockNoteEditor<BSchema>;
};

export type FormattingToolbarDynamicParams = {
referenceRect: DOMRect;
getReferenceRect: () => DOMRect;
};

export type FormattingToolbar = EditorElement<
FormattingToolbarDynamicParams
>;
export type FormattingToolbarDynamicParams = {};

export type FormattingToolbar = EditorElement<FormattingToolbarDynamicParams>;
export type FormattingToolbarFactory<BSchema extends BlockSchema> =
ElementFactory<
FormattingToolbarStaticParams<BSchema>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { EditorView } from "prosemirror-view";
import { BlockNoteEditor, BlockSchema } from "../..";
import {
FormattingToolbar,
FormattingToolbarDynamicParams,
FormattingToolbarFactory,
FormattingToolbarStaticParams,
} from "./FormattingToolbarFactoryTypes";
Expand Down Expand Up @@ -44,6 +43,8 @@ export class FormattingToolbarView<BSchema extends BlockSchema> {

public prevWasEditable: boolean | null = null;

private lastPosition: DOMRect | undefined;

public shouldShow: (props: {
view: EditorView;
state: EditorState;
Expand Down Expand Up @@ -80,8 +81,6 @@ export class FormattingToolbarView<BSchema extends BlockSchema> {

this.ttEditor.on("focus", this.focusHandler);
this.ttEditor.on("blur", this.blurHandler);

document.addEventListener("scroll", this.scrollHandler);
}

viewMousedownHandler = () => {
Expand Down Expand Up @@ -129,12 +128,6 @@ export class FormattingToolbarView<BSchema extends BlockSchema> {
}
};

scrollHandler = () => {
if (this.toolbarIsOpen) {
this.formattingToolbar.render(this.getDynamicParams(), false);
}
};

update(view: EditorView, oldState?: EditorState) {
const { state, composing } = view;
const { doc, selection } = state;
Expand Down Expand Up @@ -170,7 +163,7 @@ export class FormattingToolbarView<BSchema extends BlockSchema> {
!this.preventShow &&
(shouldShow || this.preventHide)
) {
this.formattingToolbar.render(this.getDynamicParams(), true);
this.formattingToolbar.render({}, true);
this.toolbarIsOpen = true;

return;
Expand All @@ -182,7 +175,7 @@ export class FormattingToolbarView<BSchema extends BlockSchema> {
!this.preventShow &&
(shouldShow || this.preventHide)
) {
this.formattingToolbar.render(this.getDynamicParams(), false);
this.formattingToolbar.render({}, false);
return;
}

Expand All @@ -206,8 +199,6 @@ export class FormattingToolbarView<BSchema extends BlockSchema> {

this.ttEditor.off("focus", this.focusHandler);
this.ttEditor.off("blur", this.blurHandler);

document.removeEventListener("scroll", this.scrollHandler);
}

getSelectionBoundingBox() {
Expand All @@ -233,12 +224,22 @@ export class FormattingToolbarView<BSchema extends BlockSchema> {
getStaticParams(): FormattingToolbarStaticParams<BSchema> {
return {
editor: this.editor,
};
}

getDynamicParams(): FormattingToolbarDynamicParams {
return {
referenceRect: this.getSelectionBoundingBox(),
getReferenceRect: () => {
if (!this.toolbarIsOpen) {
if (this.lastPosition === undefined) {
throw new Error(
"Attempted to access selection reference rect before rendering formatting toolbar."
);
}

return this.lastPosition;
}

const selectionBoundingBox = this.getSelectionBoundingBox();
this.lastPosition = selectionBoundingBox;

return selectionBoundingBox;
},
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { EditorElement, ElementFactory } from "../../shared/EditorElement";
export type HyperlinkToolbarStaticParams = {
editHyperlink: (url: string, text: string) => void;
deleteHyperlink: () => void;

getReferenceRect: () => DOMRect;
};

export type HyperlinkToolbarDynamicParams = {
url: string;
text: string;

referenceRect: DOMRect;
};

export type HyperlinkToolbar = EditorElement<HyperlinkToolbarDynamicParams>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class HyperlinkToolbarView {
hyperlinkMark: Mark | undefined;
hyperlinkMarkRange: Range | undefined;

private lastPosition: DOMRect | undefined;

constructor({ editor, hyperlinkToolbarFactory }: HyperlinkToolbarViewProps) {
this.editor = editor;

Expand All @@ -58,7 +60,6 @@ class HyperlinkToolbarView {

this.editor.view.dom.addEventListener("mouseover", this.mouseOverHandler);
document.addEventListener("click", this.clickHandler, true);
document.addEventListener("scroll", this.scrollHandler);
}

mouseOverHandler = (event: MouseEvent) => {
Expand Down Expand Up @@ -120,12 +121,6 @@ class HyperlinkToolbarView {
}
};

scrollHandler = () => {
if (this.hyperlinkMark !== undefined) {
this.hyperlinkToolbar.render(this.getDynamicParams(), false);
}
};

update() {
if (!this.editor.view.hasFocus()) {
return;
Expand Down Expand Up @@ -220,7 +215,6 @@ class HyperlinkToolbarView {
"mouseover",
this.mouseOverHandler
);
document.removeEventListener("scroll", this.scrollHandler);
}

getStaticParams(): HyperlinkToolbarStaticParams {
Expand Down Expand Up @@ -255,6 +249,26 @@ class HyperlinkToolbarView {

this.hyperlinkToolbar.hide();
},
getReferenceRect: () => {
if (!this.hyperlinkMark) {
if (this.lastPosition === undefined) {
throw new Error(
"Attempted to access hyperlink reference rect before rendering hyperlink toolbar."
);
}

return this.lastPosition;
}

const hyperlinkBoundingBox = posToDOMRect(
this.editor.view,
this.hyperlinkMarkRange!.from,
this.hyperlinkMarkRange!.to
);
this.lastPosition = hyperlinkBoundingBox;

return hyperlinkBoundingBox;
},
};
}

Expand All @@ -265,11 +279,6 @@ class HyperlinkToolbarView {
this.hyperlinkMarkRange!.from,
this.hyperlinkMarkRange!.to
),
referenceRect: posToDOMRect(
this.editor.view,
this.hyperlinkMarkRange!.from,
this.hyperlinkMarkRange!.to
),
};
}
}
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/shared/EditorElement.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export type RequiredStaticParams = Record<string, any>;
export type RequiredDynamicParams = Record<string, any> & {
referenceRect: DOMRect;
export type RequiredStaticParams = Record<string, any> & {
getReferenceRect: () => DOMRect;
};
export type RequiredDynamicParams = Record<string, any> & {};

export type EditorElement<ElementDynamicParams extends RequiredDynamicParams> =
{
Expand Down
Loading