editor: only update node views if attributes change

This commit is contained in:
Abdullah Atta
2023-02-22 18:03:45 +05:00
committed by Abdullah Atta
parent 43034cf3a3
commit dc62291fba
6 changed files with 62 additions and 25 deletions

View File

@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Node, mergeAttributes } from "@tiptap/core";
import { hasSameAttributes } from "../../utils/prosemirror";
import { createSelectionBasedNodeView } from "../react";
import { TextDirections } from "../text-direction";
import { EmbedComponent } from "./component";
@@ -100,7 +101,9 @@ export const EmbedNode = Node.create<EmbedOptions>({
},
addNodeView() {
return createSelectionBasedNodeView(EmbedComponent);
return createSelectionBasedNodeView(EmbedComponent, {
shouldUpdate: (prev, next) => !hasSameAttributes(prev.attrs, next.attrs)
});
},
addCommands() {

View File

@@ -23,6 +23,7 @@ import {
mergeAttributes,
findChildren
} from "@tiptap/core";
import { hasSameAttributes } from "../../utils/prosemirror";
import { Attachment, getDataAttribute } from "../attachment";
import { createSelectionBasedNodeView } from "../react";
import { TextDirections } from "../text-direction";
@@ -133,7 +134,9 @@ export const ImageNode = Node.create<ImageOptions>({
},
addNodeView() {
return createSelectionBasedNodeView(ImageComponent);
return createSelectionBasedNodeView(ImageComponent, {
shouldUpdate: (prev, next) => !hasSameAttributes(prev.attrs, next.attrs)
});
},
addCommands() {

View File

@@ -35,7 +35,10 @@ import {
} from "../../toolbar/tools/table";
import { getToolDefinition } from "../../toolbar/tool-definitions";
import { getPosition } from "../../utils/position";
import { findSelectedDOMNode } from "../../utils/prosemirror";
import {
findSelectedDOMNode,
hasSameAttributes
} from "../../utils/prosemirror";
import { DesktopOnly } from "../../components/responsive";
import { TextDirections } from "../text-direction";
@@ -59,12 +62,12 @@ export function TableComponent(props: SelectionBasedReactNodeViewProps) {
<>
<TableRowToolbar
editor={editor}
table={tableRef.current}
table={tableRef}
textDirection={textDirection}
/>
<TableColumnToolbar
editor={editor}
table={tableRef.current}
table={tableRef}
textDirection={textDirection}
/>
</>
@@ -98,7 +101,10 @@ export function TableNodeView(editor: Editor) {
{
component: TableComponent,
shouldUpdate: (prev, next) => {
return prev.type === next.type;
return (
!hasSameAttributes(prev.attrs, next.attrs) ||
prev.childCount !== next.childCount
);
},
contentDOMFactory: () => {
const dom = document.createElement("tbody");
@@ -119,7 +125,7 @@ export function TableNodeView(editor: Editor) {
type TableToolbarProps = {
editor: Editor;
table?: HTMLTableElement;
table?: React.MutableRefObject<HTMLTableElement | undefined>;
textDirection: TextDirections;
};
@@ -127,12 +133,11 @@ function TableRowToolbar(props: TableToolbarProps) {
const { editor, textDirection } = props;
const rowToolsRef = useRef<HTMLDivElement>(null);
useEffect(
() => {
useEffect(() => {
function onSelectionUpdate() {
if (!rowToolsRef.current) {
return;
}
const currentRow = findSelectedDOMNode(editor, ["tableRow"]);
if (!currentRow) return;
@@ -151,10 +156,13 @@ function TableRowToolbar(props: TableToolbarProps) {
rowToolsRef.current.style.left = `${pos.left}px`;
rowToolsRef.current.style.right = `unset`;
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[editor.state.selection, textDirection]
);
}
editor.current?.on("selectionUpdate", onSelectionUpdate);
return () => {
editor.current?.off("selectionUpdate", onSelectionUpdate);
};
}, [textDirection]);
return (
<Flex
@@ -192,9 +200,10 @@ function TableRowToolbar(props: TableToolbarProps) {
function TableColumnToolbar(props: TableToolbarProps) {
const { editor, table } = props;
const columnToolsRef = useRef<HTMLDivElement>(null);
useEffect(
() => {
if (!columnToolsRef.current || !table) {
useEffect(() => {
function onSelectionUpdate() {
if (!columnToolsRef.current || !table?.current) {
return;
}
@@ -209,16 +218,19 @@ function TableColumnToolbar(props: TableToolbarProps) {
location: "top",
align: "center",
target: currentCell as HTMLElement,
yAnchor: table,
yAnchor: table.current,
yOffset: 2
});
columnToolsRef.current.style.left = `${pos.left}px`;
columnToolsRef.current.style.top = `${pos.top}px`;
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[editor.state.selection, table]
);
}
editor.current?.on("selectionUpdate", onSelectionUpdate);
return () => {
editor.current?.off("selectionUpdate", onSelectionUpdate);
};
}, []);
return (
<Flex

View File

@@ -24,7 +24,7 @@ import { TaskListComponent } from "./component";
import { Plugin, PluginKey, NodeSelection } from "prosemirror-state";
import TaskItem from "@tiptap/extension-task-item";
import { dropPoint } from "prosemirror-transform";
import { findChildrenByType } from "../../utils/prosemirror";
import { findChildrenByType, hasSameAttributes } from "../../utils/prosemirror";
export type TaskListAttributes = {
title: string;
@@ -106,6 +106,12 @@ export const TaskListNode = TaskList.extend({
content.classList.add(`${this.name.toLowerCase()}-content-wrapper`);
content.style.whiteSpace = "inherit";
return { dom: content };
},
shouldUpdate: (prev, next) => {
return (
!hasSameAttributes(prev.attrs, next.attrs) ||
prev.childCount !== next.childCount
);
}
});
},

View File

@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Node, mergeAttributes, findChildren } from "@tiptap/core";
import { hasSameAttributes } from "../../utils/prosemirror";
import { getDataAttribute } from "../attachment";
import { createSelectionBasedNodeView } from "../react";
import { WebClipComponent } from "./component";
@@ -108,7 +109,9 @@ export const WebClipNode = Node.create<WebClipOptions>({
},
addNodeView() {
return createSelectionBasedNodeView(WebClipComponent);
return createSelectionBasedNodeView(WebClipComponent, {
shouldUpdate: (prev, next) => !hasSameAttributes(prev.attrs, next.attrs)
});
},
addCommands() {

View File

@@ -28,10 +28,20 @@ import {
Node as ProsemirrorNode,
Mark,
NodeType,
ResolvedPos
ResolvedPos,
Attrs
} from "prosemirror-model";
import { EditorState, Selection } from "prosemirror-state";
export function hasSameAttributes(prev: Attrs, next: Attrs) {
for (const key in prev) {
const prevValue = prev[key];
const nextValue = next[key];
if (prevValue !== nextValue) return false;
}
return true;
}
export type NodeWithOffset = {
node?: ProsemirrorNode;
from: number;