我们有以下 Tiptap 扩展来处理 video 节点:
import { mergeAttributes, Node, nodeInputRule } from "@tiptap/core";
export interface VideoOptions {
url: string;
width: number;
height: number;
HTMLAttributes: Record<string, any>;
}
declare module "@tiptap/core" {
interface Commands<ReturnType> {
video: {
/**
* Set a video node
* @param options.updateSelection set to true will select the newly inserted content
*/
setVideo: (
id: string,
src: string,
width?: number,
height?: number,
controls?: boolean,
controlslist?: string,
options?: { updateSelection: boolean }
) => ReturnType;
/**
* Toggle a video
*/
toggleVideo: (src: string) => ReturnType;
};
}
}
const VIDEO_INPUT_REGEX = /!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\)/;
export const Video = Node.create({
name: "video",
group: "block",
draggable: true,
selectable: true,
addAttributes() {
return {
src: {
default: null,
parseHTML: (el: any) => (el as HTMLSpanElement).getAttribute("src"),
renderHTML: (attrs: any) => ({ src: attrs.src }),
},
controls: {
default: true,
renderHTML: (attrs: any) => ({ controls: attrs.controls }),
parseHTML: (el: any) => {
if ((el as HTMLSpanElement).getAttribute("controls")) {
return (el as HTMLSpanElement).getAttribute("controls");
} else if ((el as HTMLSpanElement).hasAttribute("controls")) {
return true;
} else {
return false;
}
},
},
controlslist: {
default: "",
renderHTML: (attributes: any) => {
return { controlslist: attributes.controlslist };
},
parseHTML: (element: any) => element.getAttribute("controlslist"),
},
documentId: {
default: "",
renderHTML: (attributes: any) => {
return { "data-document-id": attributes.documentId };
},
parseHTML: (element: any) => element.getAttribute("data-document-id"),
},
videoResolution: {
default: "404x720",
renderHTML: (attributes: any) => {
return { "data-video-resolution": attributes.videoResolution };
},
parseHTML: (element: any) =>
element.getAttribute("data-video-resolution"),
},
width: {
renderHTML: (attributes: any) => {
return {
width: parseInt(attributes.width),
};
},
parseHTML: (element) => element.getAttribute("width"),
},
height: {
renderHTML: (attributes: any) => {
return {
height: parseInt(attributes.height),
};
},
parseHTML: (element) => element.getAttribute("height"),
},
};
},
parseHTML() {
return [
{
tag: "div.video-wrapper>video,video",
getAttrs: (el: any) => ({
src: (el as HTMLVideoElement).getAttribute("src"),
}),
},
];
},
renderHTML({ HTMLAttributes }) {
return [
"div",
{ class: "video-wrapper" },
["video", mergeAttributes(HTMLAttributes)],
];
},
addCommands() {
return {
setVideo:
(
id,
src,
width = 640,
height = 480,
controls = true,
controlslist = "nodownload",
options
) =>
({ commands, state }) => {
return commands.insertContentAt(
state.selection,
`<video
controls="${controls}"
controlslist="${controlslist}"
src="${src}"
width="${width}"
height="${height}"
data-document-id="${id}"
data-video-resolution="${width}x${height}" />`,
options
);
},
toggleVideo:
() =>
({ commands }) =>
commands.toggleNode(this.name, "paragraph"),
};
},
addInputRules() {
return [
nodeInputRule({
find: VIDEO_INPUT_REGEX,
type: this.type,
getAttributes: (match) => {
const [, , src] = match;
return { src };
},
}),
];
},
});
我们想要插入具有给定实体的新视频:
if (asset.mime.includes("video")) {
const { id, src, width, height } = getUpdatedVideo(asset);
if (!forceInsert)
editor.chain().focus().setVideo(String(id), src, width, height);
else editor.commands.setVideo(String(id), src, width, height);
}
setCommand
被执行了,我们通过congole.log
检查了它,但是它的节点从未插入到文档中,因此,文档永远不会更新。
我们添加了一个具有类似结构的音频扩展,并且我们以相同的方式执行它,
为什么会失败?
我们正在调用该函数,因此我们总是设置日志,但我们没有运行它:
editor
.chain()
.focus()
.setVideo(String(id), src, width, height)
.run();
run()
是执行动作的指令。