Skip to content
This repository has been archived by the owner on Nov 29, 2023. It is now read-only.

Commit

Permalink
feat: add iframe extension
Browse files Browse the repository at this point in the history
  • Loading branch information
ruibaby committed Nov 25, 2022
1 parent 9418829 commit 0528be0
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const selectedLanguage = computed({
</script>
<template>
<node-view-wrapper
class="code-block divide-gray-100 bg-gray-100 overflow-hidden rounded-md"
class="code-block divide-gray-100 bg-gray-100 overflow-hidden rounded-md my-3"
>
<div class="px-2 py-1.5">
<select
Expand Down
18 changes: 18 additions & 0 deletions packages/editor/src/extensions/commands-menu/suggestion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import MdiFormatHeader5 from "~icons/mdi/format-header-5";
import MdiFormatHeader6 from "~icons/mdi/format-header-6";
import MdiCodeBracesBox from "~icons/mdi/code-braces-box";
// import MdiMathCompass from "~icons/mdi/math-compass";
import MdiWeb from "~icons/mdi/web";
import { markRaw, type Component } from "vue";
import type { SuggestionOptions } from "@tiptap/suggestion";

Expand Down Expand Up @@ -115,6 +116,23 @@ export const CommandCodeBlock: Item = {
},
};

export const CommandIframe: Item = {
icon: markRaw(MdiWeb),
title: "嵌入网页",
keywords: ["iframe", "qianruwangye"],
command: ({ editor, range }: { editor: Editor; range: Range }) => {
editor
.chain()
.focus()
.deleteRange(range)
.insertContent([
{ type: "iframe", attrs: { src: "" } },
{ type: "paragraph", content: "" },
])
.run();
},
};

export default {
items: ({ query }: { query: string }): Item[] => {
return [
Expand Down
55 changes: 55 additions & 0 deletions packages/editor/src/extensions/iframe/IframeView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<script lang="ts" setup>
import type { Node as ProseMirrorNode } from "prosemirror-model";
import type { Decoration } from "prosemirror-view";
import { Editor, NodeViewWrapper, Node } from "@tiptap/vue-3";
import { computed } from "vue";
const props = defineProps<{
editor: Editor;
node: ProseMirrorNode;
decorations: Decoration[];
selected: boolean;
extension: Node<any, any>;
getPos: () => number;
updateAttributes: (attributes: Record<string, any>) => void;
deleteNode: () => void;
}>();
const src = computed({
get: () => {
return props.node?.attrs.src;
},
set: (src: string) => {
props.updateAttributes({ src: src });
},
});
</script>

<template>
<node-view-wrapper
as="div"
class="divide-gray-100 bg-gray-100 overflow-hidden rounded-md w-full my-3"
>
<div class="block w-full relative">
<div class="px-2 py-1.5">
<input
v-model="src"
class="block px-2 w-full py-1.5 text-sm text-gray-900 border border-gray-300 rounded-md bg-gray-50 focus:ring-blue-500 focus:border-blue-500"
placeholder="输入链接"
/>
</div>
<iframe
v-if="src"
class="rounded-md"
:src="node!.attrs.src"
:width="node.attrs.width"
:height="node.attrs.height"
scrolling="no"
border="0"
frameborder="no"
framespacing="0"
allowfullscreen="true"
></iframe>
</div>
</node-view-wrapper>
</template>
158 changes: 158 additions & 0 deletions packages/editor/src/extensions/iframe/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { mergeAttributes, Node, nodeInputRule } from "@tiptap/core";
import { VueNodeViewRenderer } from "@tiptap/vue-3";
import IframeView from "./IframeView.vue";

declare module "@tiptap/core" {
interface Commands<ReturnType> {
iframe: {
setIframe: (options: { src: string }) => ReturnType;
};
}
}

const Iframe = Node.create({
name: "iframe",

group: "block",

selectable: false,

addAttributes() {
return {
...this.parent?.(),
src: {
default: null,
parseHTML: (element) => {
const src = element.getAttribute("src");
return src;
},
},
width: {
default: "100%",
parseHTML: (element) => {
return element.getAttribute("width");
},
renderHTML(attributes) {
return {
width: attributes.width,
};
},
},
height: {
default: 200,
parseHTML: (element) => {
const height = element.getAttribute("height");
return height ? parseInt(height, 10) : null;
},
renderHTML: (attributes) => {
return {
height: attributes.height,
};
},
},
scrolling: {
default: null,
parseHTML: (element) => {
return element.getAttribute("scrolling");
},
renderHTML: (attributes) => {
return {
scrolling: attributes.scrolling,
};
},
},
border: {
default: 0,
parseHTML: (element) => {
const border = element.getAttribute("border");
return border ? parseInt(border, 10) : null;
},
renderHTML: (attributes) => {
return {
border: attributes.border,
};
},
},
frameborder: {
default: "no",
parseHTML: (element) => {
return element.getAttribute("frameborder");
},
renderHTML: (attributes) => {
return {
frameborder: attributes.frameborder,
};
},
},
allowfullscreen: {
default: true,
parseHTML: (element) => {
return element.getAttribute("allowfullscreen");
},
renderHTML: (attributes) => {
return {
allowfullscreen: attributes.allowfullscreen,
};
},
},
framespacing: {
default: 0,
parseHTML: (element) => {
const framespacing = element.getAttribute("framespacing");
return framespacing ? parseInt(framespacing, 10) : null;
},
renderHTML: (attributes) => {
return {
framespacing: attributes.framespacing,
};
},
},
};
},

parseHTML() {
return [
{
tag: "iframe",
},
];
},

renderHTML({ HTMLAttributes }) {
return [
"p",
["iframe", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)],
];
},

addCommands() {
return {
setIframe:
(options) =>
({ commands }) => {
return commands.insertContent({
type: this.name,
attrs: options,
});
},
};
},

addInputRules() {
return [
nodeInputRule({
find: /^\$iframe\$$/,
type: this.type,
getAttributes: () => {
return { width: "100%" };
},
}),
];
},

addNodeView() {
return VueNodeViewRenderer(IframeView);
},
});

export default Iframe;
3 changes: 3 additions & 0 deletions packages/editor/src/extensions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
ExtensionCommands,
} from "../extensions/commands-menu";
import { ExtensionCodeBlock, lowlight } from "@/extensions/code-block";
import ExtensionIframe from "./iframe";

const allExtensions = [
ExtensionBlockquote,
Expand Down Expand Up @@ -83,6 +84,7 @@ const allExtensions = [
ExtensionCodeBlock.configure({
lowlight,
}),
ExtensionIframe,
];

export {
Expand Down Expand Up @@ -121,4 +123,5 @@ export {
CommandsSuggestion,
ExtensionCodeBlock,
lowlight,
ExtensionIframe,
};

0 comments on commit 0528be0

Please sign in to comment.