fix: resolve the issue of text editing not being effective before the table (#5365)

#### What type of PR is this?

/kind bug
/area editor
/area console

#### What this PR does / why we need it:

在 #5191 中对表格新增了如果可以左右滚动时,则显示阴影的特性。而根据 `prosemirror` 的数据流处理策略,在 `decorations` 中,state 是 new EditorState,但通过 `this.editor.view` 获取到的 view 为 old EditorView,进而导致使用了最新的坐标,并且使用其坐标通过旧版本的 view 中获取 DOM 而产生的错误。

本 PR 使用 `NodeView` 而不是 `decorations` 来重新处理表格阴影。

#### How to test it?

在默认富文本编辑器中新建一个表格。在表格前使用拼音输入文本,查看此文本能否被输入。
同时查看表格阴影是否存在

#### Which issue(s) this PR fixes:

Fixes #5352

#### Does this PR introduce a user-facing change?
```release-note
解决默认富文本编辑器表格前字符无法被编辑的问题。
```
pull/5382/head v2.12.3
Takagi 2024-02-21 10:44:09 +08:00 committed by GitHub
parent 50e954d297
commit ca4f705f2d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 34 additions and 49 deletions

View File

@ -15,9 +15,6 @@ import {
type NodeView,
type EditorState,
type DOMOutputSpec,
Plugin,
DecorationSet,
Decoration,
} from "@/tiptap/pm";
import TableCell from "./table-cell";
import TableRow from "./table-row";
@ -129,14 +126,22 @@ class TableView implements NodeView {
this.containerDOM.addEventListener("wheel", (e) => {
return this.handleHorizontalWheel(this.containerDOM, e);
});
let mouseX = 0;
let mouseY = 0;
document.addEventListener("mousemove", function (event) {
mouseX = event.clientX;
mouseY = event.clientY;
});
this.containerDOM.addEventListener("scroll", () => {
if (!editor) {
return false;
}
const { state, view } = editor;
const { tr } = state;
view.dispatch(tr);
return false;
const { view } = editor;
const coords = { left: mouseX, top: mouseY };
const pos = view.posAtCoords(coords);
editor.commands.setTextSelection(pos?.pos || 0);
});
this.scrollDom = document.createElement("div");
@ -147,6 +152,11 @@ class TableView implements NodeView {
this.colgroup = this.table.appendChild(document.createElement("colgroup"));
updateColumns(node, this.colgroup, this.table, cellMinWidth);
this.contentDOM = this.table.appendChild(document.createElement("tbody"));
// delay execution during initialization, otherwise
// the correct scrollWidth cannot be obtained.
setTimeout(() => {
this.updateTableShadow();
});
}
update(node: ProseMirrorNode) {
@ -156,15 +166,32 @@ class TableView implements NodeView {
this.node = node;
updateColumns(node, this.colgroup, this.table, this.cellMinWidth);
this.updateTableShadow();
return true;
}
updateTableShadow() {
const { scrollWidth, clientWidth, scrollLeft } = this
.containerDOM as HTMLElement;
if (scrollWidth > clientWidth && scrollLeft < scrollWidth - clientWidth) {
this.dom.classList.add("table-right-shadow");
} else {
this.dom.classList.remove("table-right-shadow");
}
if (scrollLeft > 0) {
this.dom.classList.add("table-left-shadow");
} else {
this.dom.classList.remove("table-left-shadow");
}
}
ignoreMutation(
mutation: MutationRecord | { type: "selection"; target: Element }
) {
return (
mutation.type === "attributes" &&
(mutation.target === this.table ||
mutation.target === this.dom ||
this.colgroup.contains(mutation.target))
);
}
@ -487,48 +514,6 @@ const Table = TiptapTable.extend<ExtensionOptions & TableOptions>({
onTransaction() {
editor = this.editor;
},
addProseMirrorPlugins() {
const plugins = this.parent?.() ?? [];
return [
...plugins,
new Plugin({
props: {
decorations: (state) => {
const { doc, tr } = state;
const decorations: Decoration[] = [];
doc.descendants((node, pos) => {
if (node.type.name === Table.name) {
const { view } = this.editor;
const nodeDom = view.nodeDOM(pos) || view.domAtPos(pos)?.node;
if (!nodeDom) {
return true;
}
const { scrollWidth, clientWidth, scrollLeft } =
nodeDom.firstChild as HTMLElement;
let classNames = "";
if (
scrollWidth > clientWidth &&
scrollLeft < scrollWidth - clientWidth
) {
classNames += "table-right-shadow ";
}
if (scrollLeft > 0) {
classNames += "table-left-shadow ";
}
decorations.push(
Decoration.node(pos, pos + node.nodeSize, {
class: classNames,
})
);
}
});
return DecorationSet.create(tr.doc, decorations);
},
},
}),
];
},
}).configure({ resizable: true });
export default Table;