fix: resolve TypeScript errors in frontend build

This commit is contained in:
Hiro
2026-03-30 23:16:07 +00:00
parent b733306773
commit 24925e1acb
2941 changed files with 418042 additions and 49 deletions

21
node_modules/@tiptap/vue-3/LICENSE.md generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025, Tiptap GmbH
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

18
node_modules/@tiptap/vue-3/README.md generated vendored Normal file
View File

@@ -0,0 +1,18 @@
# @tiptap/vue-3
[![Version](https://img.shields.io/npm/v/@tiptap/vue-3.svg?label=version)](https://www.npmjs.com/package/@tiptap/vue-3)
[![Downloads](https://img.shields.io/npm/dm/@tiptap/vue-3.svg)](https://npmcharts.com/compare/tiptap?minimal=true)
[![License](https://img.shields.io/npm/l/@tiptap/vue-3.svg)](https://www.npmjs.com/package/@tiptap/vue-3)
[![Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub)](https://github.com/sponsors/ueberdosis)
## Introduction
Tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) a toolkit for building rich text WYSIWYG editors, which is already in use at many well-known companies such as _New York Times_, _The Guardian_ or _Atlassian_.
## Official Documentation
Documentation can be found on the [Tiptap website](https://tiptap.dev).
## License
Tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap/blob/main/LICENSE.md).

655
node_modules/@tiptap/vue-3/dist/index.cjs generated vendored Normal file
View File

@@ -0,0 +1,655 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
Editor: () => Editor,
EditorContent: () => EditorContent,
MarkViewContent: () => MarkViewContent,
NodeViewContent: () => NodeViewContent,
NodeViewWrapper: () => NodeViewWrapper,
VueMarkView: () => VueMarkView,
VueMarkViewRenderer: () => VueMarkViewRenderer,
VueNodeViewRenderer: () => VueNodeViewRenderer,
VueRenderer: () => VueRenderer,
markViewProps: () => markViewProps,
nodeViewProps: () => nodeViewProps,
useEditor: () => useEditor
});
module.exports = __toCommonJS(index_exports);
// src/Editor.ts
var import_core = require("@tiptap/core");
var import_vue = require("vue");
function useDebouncedRef(value) {
return (0, import_vue.customRef)((track, trigger) => {
return {
get() {
track();
return value;
},
set(newValue) {
value = newValue;
requestAnimationFrame(() => {
requestAnimationFrame(() => {
trigger();
});
});
}
};
});
}
var Editor = class extends import_core.Editor {
constructor(options = {}) {
super(options);
this.contentComponent = null;
this.appContext = null;
this.reactiveState = useDebouncedRef(this.view.state);
this.reactiveExtensionStorage = useDebouncedRef(this.extensionStorage);
this.on("beforeTransaction", ({ nextState }) => {
this.reactiveState.value = nextState;
this.reactiveExtensionStorage.value = this.extensionStorage;
});
return (0, import_vue.markRaw)(this);
}
get state() {
return this.reactiveState ? this.reactiveState.value : this.view.state;
}
get storage() {
return this.reactiveExtensionStorage ? this.reactiveExtensionStorage.value : super.storage;
}
/**
* Register a ProseMirror plugin.
*/
registerPlugin(plugin, handlePlugins) {
const nextState = super.registerPlugin(plugin, handlePlugins);
if (this.reactiveState) {
this.reactiveState.value = nextState;
}
return nextState;
}
/**
* Unregister a ProseMirror plugin.
*/
unregisterPlugin(nameOrPluginKey) {
const nextState = super.unregisterPlugin(nameOrPluginKey);
if (this.reactiveState && nextState) {
this.reactiveState.value = nextState;
}
return nextState;
}
};
// src/EditorContent.ts
var import_vue2 = require("vue");
var EditorContent = (0, import_vue2.defineComponent)({
name: "EditorContent",
props: {
editor: {
default: null,
type: Object
}
},
setup(props) {
const rootEl = (0, import_vue2.ref)();
const instance = (0, import_vue2.getCurrentInstance)();
(0, import_vue2.watchEffect)(() => {
const editor = props.editor;
if (editor && editor.options.element && rootEl.value) {
(0, import_vue2.nextTick)(() => {
var _a;
if (!rootEl.value || !((_a = editor.view.dom) == null ? void 0 : _a.parentNode)) {
return;
}
const element = (0, import_vue2.unref)(rootEl.value);
rootEl.value.append(...editor.view.dom.parentNode.childNodes);
editor.contentComponent = instance.ctx._;
if (instance) {
editor.appContext = {
...instance.appContext,
// Vue internally uses prototype chain to forward/shadow injects across the entire component chain
// so don't use object spread operator or 'Object.assign' and just set `provides` as is on editor's appContext
// @ts-expect-error forward instance's 'provides' into appContext
provides: instance.provides
};
}
editor.setOptions({
element
});
editor.createNodeViews();
});
}
});
(0, import_vue2.onBeforeUnmount)(() => {
const editor = props.editor;
if (!editor) {
return;
}
editor.contentComponent = null;
editor.appContext = null;
});
return { rootEl };
},
render() {
return (0, import_vue2.h)("div", {
ref: (el) => {
this.rootEl = el;
}
});
}
});
// src/NodeViewContent.ts
var import_vue3 = require("vue");
var NodeViewContent = (0, import_vue3.defineComponent)({
name: "NodeViewContent",
props: {
as: {
type: String,
default: "div"
}
},
render() {
return (0, import_vue3.h)(this.as, {
style: {
whiteSpace: "pre-wrap"
},
"data-node-view-content": ""
});
}
});
// src/NodeViewWrapper.ts
var import_vue4 = require("vue");
var NodeViewWrapper = (0, import_vue4.defineComponent)({
name: "NodeViewWrapper",
props: {
as: {
type: String,
default: "div"
}
},
inject: ["onDragStart", "decorationClasses"],
render() {
var _a, _b;
return (0, import_vue4.h)(
this.as,
{
// @ts-ignore
class: this.decorationClasses,
style: {
whiteSpace: "normal"
},
"data-node-view-wrapper": "",
// @ts-ignore (https://github.com/vuejs/vue-next/issues/3031)
onDragstart: this.onDragStart
},
(_b = (_a = this.$slots).default) == null ? void 0 : _b.call(_a)
);
}
});
// src/useEditor.ts
var import_vue5 = require("vue");
var useEditor = (options = {}) => {
const editor = (0, import_vue5.shallowRef)();
(0, import_vue5.onMounted)(() => {
editor.value = new Editor(options);
});
(0, import_vue5.onBeforeUnmount)(() => {
var _a, _b, _c, _d;
const nodes = (_b = (_a = editor.value) == null ? void 0 : _a.view.dom) == null ? void 0 : _b.parentNode;
const newEl = nodes == null ? void 0 : nodes.cloneNode(true);
(_c = nodes == null ? void 0 : nodes.parentNode) == null ? void 0 : _c.replaceChild(newEl, nodes);
(_d = editor.value) == null ? void 0 : _d.destroy();
});
return editor;
};
// src/VueMarkViewRenderer.ts
var import_core2 = require("@tiptap/core");
var import_vue7 = require("vue");
// src/VueRenderer.ts
var import_vue6 = require("vue");
var VueRenderer = class {
constructor(component, { props = {}, editor }) {
/**
* Flag to track if the renderer has been destroyed, preventing queued or asynchronous renders from executing after teardown.
*/
this.destroyed = false;
this.editor = editor;
this.component = (0, import_vue6.markRaw)(component);
this.el = document.createElement("div");
this.props = (0, import_vue6.reactive)(props);
this.renderedComponent = this.renderComponent();
}
get element() {
return this.renderedComponent.el;
}
get ref() {
var _a, _b, _c, _d;
if ((_b = (_a = this.renderedComponent.vNode) == null ? void 0 : _a.component) == null ? void 0 : _b.exposed) {
return this.renderedComponent.vNode.component.exposed;
}
return (_d = (_c = this.renderedComponent.vNode) == null ? void 0 : _c.component) == null ? void 0 : _d.proxy;
}
renderComponent() {
if (this.destroyed) {
return this.renderedComponent;
}
let vNode = (0, import_vue6.h)(this.component, this.props);
if (this.editor.appContext) {
vNode.appContext = this.editor.appContext;
}
if (typeof document !== "undefined" && this.el) {
(0, import_vue6.render)(vNode, this.el);
}
const destroy = () => {
if (this.el) {
(0, import_vue6.render)(null, this.el);
}
this.el = null;
vNode = null;
};
return { vNode, destroy, el: this.el ? this.el.firstElementChild : null };
}
updateProps(props = {}) {
if (this.destroyed) {
return;
}
Object.entries(props).forEach(([key, value]) => {
this.props[key] = value;
});
this.renderComponent();
}
destroy() {
if (this.destroyed) {
return;
}
this.destroyed = true;
this.renderedComponent.destroy();
}
};
// src/VueMarkViewRenderer.ts
var markViewProps = {
editor: {
type: Object,
required: true
},
mark: {
type: Object,
required: true
},
extension: {
type: Object,
required: true
},
inline: {
type: Boolean,
required: true
},
view: {
type: Object,
required: true
},
updateAttributes: {
type: Function,
required: true
},
HTMLAttributes: {
type: Object,
required: true
}
};
var MarkViewContent = (0, import_vue7.defineComponent)({
name: "MarkViewContent",
props: {
as: {
type: String,
default: "span"
}
},
render() {
return (0, import_vue7.h)(this.as, {
style: {
whiteSpace: "inherit"
},
"data-mark-view-content": ""
});
}
});
var VueMarkView = class extends import_core2.MarkView {
constructor(component, props, options) {
super(component, props, options);
const componentProps = { ...props, updateAttributes: this.updateAttributes.bind(this) };
const extendedComponent = (0, import_vue7.defineComponent)({
extends: { ...component },
props: Object.keys(componentProps),
template: this.component.template,
setup: (reactiveProps) => {
var _a;
return (_a = component.setup) == null ? void 0 : _a.call(component, reactiveProps, {
expose: () => void 0
});
},
// Add support for scoped styles
__scopeId: component.__scopeId,
__cssModules: component.__cssModules,
__name: component.__name,
__file: component.__file
});
this.renderer = new VueRenderer(extendedComponent, {
editor: this.editor,
props: componentProps
});
}
get dom() {
return this.renderer.element;
}
get contentDOM() {
return this.dom.querySelector("[data-mark-view-content]");
}
updateAttributes(attrs) {
const unproxiedMark = (0, import_vue7.toRaw)(this.mark);
super.updateAttributes(attrs, unproxiedMark);
}
destroy() {
this.renderer.destroy();
}
};
function VueMarkViewRenderer(component, options = {}) {
return (props) => {
if (!props.editor.contentComponent) {
return {};
}
return new VueMarkView(component, props, options);
};
}
// src/VueNodeViewRenderer.ts
var import_core3 = require("@tiptap/core");
var import_vue8 = require("vue");
var nodeViewProps = {
editor: {
type: Object,
required: true
},
node: {
type: Object,
required: true
},
decorations: {
type: Object,
required: true
},
selected: {
type: Boolean,
required: true
},
extension: {
type: Object,
required: true
},
getPos: {
type: Function,
required: true
},
updateAttributes: {
type: Function,
required: true
},
deleteNode: {
type: Function,
required: true
},
view: {
type: Object,
required: true
},
innerDecorations: {
type: Object,
required: true
},
HTMLAttributes: {
type: Object,
required: true
}
};
var VueNodeView = class extends import_core3.NodeView {
constructor() {
super(...arguments);
this.cachedExtensionWithSyncedStorage = null;
}
/**
* Returns a proxy of the extension that redirects storage access to the editor's mutable storage.
* This preserves the original prototype chain (instanceof checks, methods like configure/extend work).
* Cached to avoid proxy creation on every update.
*/
get extensionWithSyncedStorage() {
if (!this.cachedExtensionWithSyncedStorage) {
const editor = this.editor;
const extension = this.extension;
this.cachedExtensionWithSyncedStorage = new Proxy(extension, {
get(target, prop, receiver) {
var _a;
if (prop === "storage") {
return (_a = editor.storage[extension.name]) != null ? _a : {};
}
return Reflect.get(target, prop, receiver);
}
});
}
return this.cachedExtensionWithSyncedStorage;
}
mount() {
const props = {
editor: this.editor,
node: this.node,
decorations: this.decorations,
innerDecorations: this.innerDecorations,
view: this.view,
selected: false,
extension: this.extensionWithSyncedStorage,
HTMLAttributes: this.HTMLAttributes,
getPos: () => this.getPos(),
updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
deleteNode: () => this.deleteNode()
};
const onDragStart = this.onDragStart.bind(this);
this.decorationClasses = (0, import_vue8.ref)(this.getDecorationClasses());
const extendedComponent = (0, import_vue8.defineComponent)({
extends: { ...this.component },
props: Object.keys(props),
template: this.component.template,
setup: (reactiveProps) => {
var _a, _b;
(0, import_vue8.provide)("onDragStart", onDragStart);
(0, import_vue8.provide)("decorationClasses", this.decorationClasses);
return (_b = (_a = this.component).setup) == null ? void 0 : _b.call(_a, reactiveProps, {
expose: () => void 0
});
},
// add support for scoped styles
// @ts-ignore
// eslint-disable-next-line
__scopeId: this.component.__scopeId,
// add support for CSS Modules
// @ts-ignore
// eslint-disable-next-line
__cssModules: this.component.__cssModules,
// add support for vue devtools
// @ts-ignore
// eslint-disable-next-line
__name: this.component.__name,
// @ts-ignore
// eslint-disable-next-line
__file: this.component.__file
});
this.handleSelectionUpdate = this.handleSelectionUpdate.bind(this);
this.editor.on("selectionUpdate", this.handleSelectionUpdate);
this.renderer = new VueRenderer(extendedComponent, {
editor: this.editor,
props
});
}
/**
* Return the DOM element.
* This is the element that will be used to display the node view.
*/
get dom() {
if (!this.renderer.element || !this.renderer.element.hasAttribute("data-node-view-wrapper")) {
throw Error("Please use the NodeViewWrapper component for your node view.");
}
return this.renderer.element;
}
/**
* Return the content DOM element.
* This is the element that will be used to display the rich-text content of the node.
*/
get contentDOM() {
if (this.node.isLeaf) {
return null;
}
return this.dom.querySelector("[data-node-view-content]");
}
/**
* On editor selection update, check if the node is selected.
* If it is, call `selectNode`, otherwise call `deselectNode`.
*/
handleSelectionUpdate() {
const { from, to } = this.editor.state.selection;
const pos = this.getPos();
if (typeof pos !== "number") {
return;
}
if (from <= pos && to >= pos + this.node.nodeSize) {
if (this.renderer.props.selected) {
return;
}
this.selectNode();
} else {
if (!this.renderer.props.selected) {
return;
}
this.deselectNode();
}
}
/**
* On update, update the React component.
* To prevent unnecessary updates, the `update` option can be used.
*/
update(node, decorations, innerDecorations) {
const rerenderComponent = (props) => {
this.decorationClasses.value = this.getDecorationClasses();
this.renderer.updateProps(props);
};
if (typeof this.options.update === "function") {
const oldNode = this.node;
const oldDecorations = this.decorations;
const oldInnerDecorations = this.innerDecorations;
this.node = node;
this.decorations = decorations;
this.innerDecorations = innerDecorations;
return this.options.update({
oldNode,
oldDecorations,
newNode: node,
newDecorations: decorations,
oldInnerDecorations,
innerDecorations,
updateProps: () => rerenderComponent({ node, decorations, innerDecorations, extension: this.extensionWithSyncedStorage })
});
}
if (node.type !== this.node.type) {
return false;
}
if (node === this.node && this.decorations === decorations && this.innerDecorations === innerDecorations) {
return true;
}
this.node = node;
this.decorations = decorations;
this.innerDecorations = innerDecorations;
rerenderComponent({ node, decorations, innerDecorations, extension: this.extensionWithSyncedStorage });
return true;
}
/**
* Select the node.
* Add the `selected` prop and the `ProseMirror-selectednode` class.
*/
selectNode() {
this.renderer.updateProps({
selected: true
});
if (this.renderer.element) {
this.renderer.element.classList.add("ProseMirror-selectednode");
}
}
/**
* Deselect the node.
* Remove the `selected` prop and the `ProseMirror-selectednode` class.
*/
deselectNode() {
this.renderer.updateProps({
selected: false
});
if (this.renderer.element) {
this.renderer.element.classList.remove("ProseMirror-selectednode");
}
}
getDecorationClasses() {
return this.decorations.flatMap((item) => item.type.attrs.class).join(" ");
}
destroy() {
this.renderer.destroy();
this.editor.off("selectionUpdate", this.handleSelectionUpdate);
}
};
function VueNodeViewRenderer(component, options) {
return (props) => {
if (!props.editor.contentComponent) {
return {};
}
const normalizedComponent = typeof component === "function" && "__vccOpts" in component ? component.__vccOpts : component;
return new VueNodeView(normalizedComponent, props, options);
};
}
// src/index.ts
__reExport(index_exports, require("@tiptap/core"), module.exports);
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Editor,
EditorContent,
MarkViewContent,
NodeViewContent,
NodeViewWrapper,
VueMarkView,
VueMarkViewRenderer,
VueNodeViewRenderer,
VueRenderer,
markViewProps,
nodeViewProps,
useEditor,
...require("@tiptap/core")
});
//# sourceMappingURL=index.cjs.map

1
node_modules/@tiptap/vue-3/dist/index.cjs.map generated vendored Normal file

File diff suppressed because one or more lines are too long

226
node_modules/@tiptap/vue-3/dist/index.d.cts generated vendored Normal file
View File

@@ -0,0 +1,226 @@
import { Editor as Editor$1, EditorOptions, Storage, MarkViewRendererOptions, MarkViewProps, MarkView, MarkViewRenderer, NodeViewProps, NodeViewRendererOptions, NodeViewRenderer } from '@tiptap/core';
export * from '@tiptap/core';
import { EditorState, Plugin, PluginKey } from '@tiptap/pm/state';
import * as vue from 'vue';
import { ComponentInternalInstance, ComponentPublicInstance, AppContext, PropType, Ref, h, Component } from 'vue';
import { Node } from '@tiptap/pm/model';
import { Decoration, DecorationSource } from '@tiptap/pm/view';
type ContentComponent = ComponentInternalInstance & {
ctx: ComponentPublicInstance;
};
declare class Editor extends Editor$1 {
private reactiveState;
private reactiveExtensionStorage;
contentComponent: ContentComponent | null;
appContext: AppContext | null;
constructor(options?: Partial<EditorOptions>);
get state(): EditorState;
get storage(): Storage;
/**
* Register a ProseMirror plugin.
*/
registerPlugin(plugin: Plugin, handlePlugins?: (newPlugin: Plugin, plugins: Plugin[]) => Plugin[]): EditorState;
/**
* Unregister a ProseMirror plugin.
*/
unregisterPlugin(nameOrPluginKey: string | PluginKey): EditorState | undefined;
}
declare const EditorContent: vue.DefineComponent<vue.ExtractPropTypes<{
editor: {
default: null;
type: PropType<Editor>;
};
}>, {
rootEl: Ref<Element | undefined, Element | undefined>;
}, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
editor: {
default: null;
type: PropType<Editor>;
};
}>> & Readonly<{}>, {
editor: Editor;
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
declare const NodeViewContent: vue.DefineComponent<vue.ExtractPropTypes<{
as: {
type: StringConstructor;
default: string;
};
}>, {}, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
as: {
type: StringConstructor;
default: string;
};
}>> & Readonly<{}>, {
as: string;
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
declare const NodeViewWrapper: vue.DefineComponent<vue.ExtractPropTypes<{
as: {
type: StringConstructor;
default: string;
};
}>, {}, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
as: {
type: StringConstructor;
default: string;
};
}>> & Readonly<{}>, {
as: string;
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
declare const useEditor: (options?: Partial<EditorOptions>) => vue.ShallowRef<Editor | undefined, Editor | undefined>;
interface VueRendererOptions {
editor: Editor$1;
props?: Record<string, any>;
}
type ExtendedVNode = ReturnType<typeof h> | null;
interface RenderedComponent {
vNode: ExtendedVNode;
destroy: () => void;
el: Element | null;
}
/**
* This class is used to render Vue components inside the editor.
*/
declare class VueRenderer {
renderedComponent: RenderedComponent;
editor: Editor;
component: Component;
el: Element | null;
props: Record<string, any>;
/**
* Flag to track if the renderer has been destroyed, preventing queued or asynchronous renders from executing after teardown.
*/
destroyed: boolean;
constructor(component: Component, { props, editor }: VueRendererOptions);
get element(): Element | null;
get ref(): any;
renderComponent(): RenderedComponent;
updateProps(props?: Record<string, any>): void;
destroy(): void;
}
interface VueMarkViewRendererOptions extends MarkViewRendererOptions {
as?: string;
className?: string;
attrs?: {
[key: string]: string;
};
}
declare const markViewProps: {
editor: {
type: PropType<MarkViewProps["editor"]>;
required: true;
};
mark: {
type: PropType<MarkViewProps["mark"]>;
required: true;
};
extension: {
type: PropType<MarkViewProps["extension"]>;
required: true;
};
inline: {
type: PropType<MarkViewProps["inline"]>;
required: true;
};
view: {
type: PropType<MarkViewProps["view"]>;
required: true;
};
updateAttributes: {
type: PropType<MarkViewProps["updateAttributes"]>;
required: true;
};
HTMLAttributes: {
type: PropType<MarkViewProps["HTMLAttributes"]>;
required: true;
};
};
declare const MarkViewContent: vue.DefineComponent<vue.ExtractPropTypes<{
as: {
type: StringConstructor;
default: string;
};
}>, {}, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
as: {
type: StringConstructor;
default: string;
};
}>> & Readonly<{}>, {
as: string;
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
declare class VueMarkView extends MarkView<Component, VueMarkViewRendererOptions> {
renderer: VueRenderer;
constructor(component: Component, props: MarkViewProps, options?: Partial<VueMarkViewRendererOptions>);
get dom(): HTMLElement;
get contentDOM(): HTMLElement | null;
updateAttributes(attrs: Record<string, any>): void;
destroy(): void;
}
declare function VueMarkViewRenderer(component: Component, options?: Partial<VueMarkViewRendererOptions>): MarkViewRenderer;
declare const nodeViewProps: {
editor: {
type: PropType<NodeViewProps["editor"]>;
required: true;
};
node: {
type: PropType<NodeViewProps["node"]>;
required: true;
};
decorations: {
type: PropType<NodeViewProps["decorations"]>;
required: true;
};
selected: {
type: PropType<NodeViewProps["selected"]>;
required: true;
};
extension: {
type: PropType<NodeViewProps["extension"]>;
required: true;
};
getPos: {
type: PropType<NodeViewProps["getPos"]>;
required: true;
};
updateAttributes: {
type: PropType<NodeViewProps["updateAttributes"]>;
required: true;
};
deleteNode: {
type: PropType<NodeViewProps["deleteNode"]>;
required: true;
};
view: {
type: PropType<NodeViewProps["view"]>;
required: true;
};
innerDecorations: {
type: PropType<NodeViewProps["innerDecorations"]>;
required: true;
};
HTMLAttributes: {
type: PropType<NodeViewProps["HTMLAttributes"]>;
required: true;
};
};
interface VueNodeViewRendererOptions extends NodeViewRendererOptions {
update: ((props: {
oldNode: Node;
oldDecorations: readonly Decoration[];
oldInnerDecorations: DecorationSource;
newNode: Node;
newDecorations: readonly Decoration[];
innerDecorations: DecorationSource;
updateProps: () => void;
}) => boolean) | null;
}
declare function VueNodeViewRenderer(component: Component<NodeViewProps>, options?: Partial<VueNodeViewRendererOptions>): NodeViewRenderer;
export { Editor, EditorContent, MarkViewContent, NodeViewContent, NodeViewWrapper, VueMarkView, VueMarkViewRenderer, type VueMarkViewRendererOptions, VueNodeViewRenderer, type VueNodeViewRendererOptions, VueRenderer, type VueRendererOptions, markViewProps, nodeViewProps, useEditor };

226
node_modules/@tiptap/vue-3/dist/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,226 @@
import { Editor as Editor$1, EditorOptions, Storage, MarkViewRendererOptions, MarkViewProps, MarkView, MarkViewRenderer, NodeViewProps, NodeViewRendererOptions, NodeViewRenderer } from '@tiptap/core';
export * from '@tiptap/core';
import { EditorState, Plugin, PluginKey } from '@tiptap/pm/state';
import * as vue from 'vue';
import { ComponentInternalInstance, ComponentPublicInstance, AppContext, PropType, Ref, h, Component } from 'vue';
import { Node } from '@tiptap/pm/model';
import { Decoration, DecorationSource } from '@tiptap/pm/view';
type ContentComponent = ComponentInternalInstance & {
ctx: ComponentPublicInstance;
};
declare class Editor extends Editor$1 {
private reactiveState;
private reactiveExtensionStorage;
contentComponent: ContentComponent | null;
appContext: AppContext | null;
constructor(options?: Partial<EditorOptions>);
get state(): EditorState;
get storage(): Storage;
/**
* Register a ProseMirror plugin.
*/
registerPlugin(plugin: Plugin, handlePlugins?: (newPlugin: Plugin, plugins: Plugin[]) => Plugin[]): EditorState;
/**
* Unregister a ProseMirror plugin.
*/
unregisterPlugin(nameOrPluginKey: string | PluginKey): EditorState | undefined;
}
declare const EditorContent: vue.DefineComponent<vue.ExtractPropTypes<{
editor: {
default: null;
type: PropType<Editor>;
};
}>, {
rootEl: Ref<Element | undefined, Element | undefined>;
}, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
editor: {
default: null;
type: PropType<Editor>;
};
}>> & Readonly<{}>, {
editor: Editor;
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
declare const NodeViewContent: vue.DefineComponent<vue.ExtractPropTypes<{
as: {
type: StringConstructor;
default: string;
};
}>, {}, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
as: {
type: StringConstructor;
default: string;
};
}>> & Readonly<{}>, {
as: string;
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
declare const NodeViewWrapper: vue.DefineComponent<vue.ExtractPropTypes<{
as: {
type: StringConstructor;
default: string;
};
}>, {}, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
as: {
type: StringConstructor;
default: string;
};
}>> & Readonly<{}>, {
as: string;
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
declare const useEditor: (options?: Partial<EditorOptions>) => vue.ShallowRef<Editor | undefined, Editor | undefined>;
interface VueRendererOptions {
editor: Editor$1;
props?: Record<string, any>;
}
type ExtendedVNode = ReturnType<typeof h> | null;
interface RenderedComponent {
vNode: ExtendedVNode;
destroy: () => void;
el: Element | null;
}
/**
* This class is used to render Vue components inside the editor.
*/
declare class VueRenderer {
renderedComponent: RenderedComponent;
editor: Editor;
component: Component;
el: Element | null;
props: Record<string, any>;
/**
* Flag to track if the renderer has been destroyed, preventing queued or asynchronous renders from executing after teardown.
*/
destroyed: boolean;
constructor(component: Component, { props, editor }: VueRendererOptions);
get element(): Element | null;
get ref(): any;
renderComponent(): RenderedComponent;
updateProps(props?: Record<string, any>): void;
destroy(): void;
}
interface VueMarkViewRendererOptions extends MarkViewRendererOptions {
as?: string;
className?: string;
attrs?: {
[key: string]: string;
};
}
declare const markViewProps: {
editor: {
type: PropType<MarkViewProps["editor"]>;
required: true;
};
mark: {
type: PropType<MarkViewProps["mark"]>;
required: true;
};
extension: {
type: PropType<MarkViewProps["extension"]>;
required: true;
};
inline: {
type: PropType<MarkViewProps["inline"]>;
required: true;
};
view: {
type: PropType<MarkViewProps["view"]>;
required: true;
};
updateAttributes: {
type: PropType<MarkViewProps["updateAttributes"]>;
required: true;
};
HTMLAttributes: {
type: PropType<MarkViewProps["HTMLAttributes"]>;
required: true;
};
};
declare const MarkViewContent: vue.DefineComponent<vue.ExtractPropTypes<{
as: {
type: StringConstructor;
default: string;
};
}>, {}, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
as: {
type: StringConstructor;
default: string;
};
}>> & Readonly<{}>, {
as: string;
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
declare class VueMarkView extends MarkView<Component, VueMarkViewRendererOptions> {
renderer: VueRenderer;
constructor(component: Component, props: MarkViewProps, options?: Partial<VueMarkViewRendererOptions>);
get dom(): HTMLElement;
get contentDOM(): HTMLElement | null;
updateAttributes(attrs: Record<string, any>): void;
destroy(): void;
}
declare function VueMarkViewRenderer(component: Component, options?: Partial<VueMarkViewRendererOptions>): MarkViewRenderer;
declare const nodeViewProps: {
editor: {
type: PropType<NodeViewProps["editor"]>;
required: true;
};
node: {
type: PropType<NodeViewProps["node"]>;
required: true;
};
decorations: {
type: PropType<NodeViewProps["decorations"]>;
required: true;
};
selected: {
type: PropType<NodeViewProps["selected"]>;
required: true;
};
extension: {
type: PropType<NodeViewProps["extension"]>;
required: true;
};
getPos: {
type: PropType<NodeViewProps["getPos"]>;
required: true;
};
updateAttributes: {
type: PropType<NodeViewProps["updateAttributes"]>;
required: true;
};
deleteNode: {
type: PropType<NodeViewProps["deleteNode"]>;
required: true;
};
view: {
type: PropType<NodeViewProps["view"]>;
required: true;
};
innerDecorations: {
type: PropType<NodeViewProps["innerDecorations"]>;
required: true;
};
HTMLAttributes: {
type: PropType<NodeViewProps["HTMLAttributes"]>;
required: true;
};
};
interface VueNodeViewRendererOptions extends NodeViewRendererOptions {
update: ((props: {
oldNode: Node;
oldDecorations: readonly Decoration[];
oldInnerDecorations: DecorationSource;
newNode: Node;
newDecorations: readonly Decoration[];
innerDecorations: DecorationSource;
updateProps: () => void;
}) => boolean) | null;
}
declare function VueNodeViewRenderer(component: Component<NodeViewProps>, options?: Partial<VueNodeViewRendererOptions>): NodeViewRenderer;
export { Editor, EditorContent, MarkViewContent, NodeViewContent, NodeViewWrapper, VueMarkView, VueMarkViewRenderer, type VueMarkViewRendererOptions, VueNodeViewRenderer, type VueNodeViewRendererOptions, VueRenderer, type VueRendererOptions, markViewProps, nodeViewProps, useEditor };

615
node_modules/@tiptap/vue-3/dist/index.js generated vendored Normal file
View File

@@ -0,0 +1,615 @@
// src/Editor.ts
import { Editor as CoreEditor } from "@tiptap/core";
import { customRef, markRaw } from "vue";
function useDebouncedRef(value) {
return customRef((track, trigger) => {
return {
get() {
track();
return value;
},
set(newValue) {
value = newValue;
requestAnimationFrame(() => {
requestAnimationFrame(() => {
trigger();
});
});
}
};
});
}
var Editor = class extends CoreEditor {
constructor(options = {}) {
super(options);
this.contentComponent = null;
this.appContext = null;
this.reactiveState = useDebouncedRef(this.view.state);
this.reactiveExtensionStorage = useDebouncedRef(this.extensionStorage);
this.on("beforeTransaction", ({ nextState }) => {
this.reactiveState.value = nextState;
this.reactiveExtensionStorage.value = this.extensionStorage;
});
return markRaw(this);
}
get state() {
return this.reactiveState ? this.reactiveState.value : this.view.state;
}
get storage() {
return this.reactiveExtensionStorage ? this.reactiveExtensionStorage.value : super.storage;
}
/**
* Register a ProseMirror plugin.
*/
registerPlugin(plugin, handlePlugins) {
const nextState = super.registerPlugin(plugin, handlePlugins);
if (this.reactiveState) {
this.reactiveState.value = nextState;
}
return nextState;
}
/**
* Unregister a ProseMirror plugin.
*/
unregisterPlugin(nameOrPluginKey) {
const nextState = super.unregisterPlugin(nameOrPluginKey);
if (this.reactiveState && nextState) {
this.reactiveState.value = nextState;
}
return nextState;
}
};
// src/EditorContent.ts
import { defineComponent, getCurrentInstance, h, nextTick, onBeforeUnmount, ref, unref, watchEffect } from "vue";
var EditorContent = defineComponent({
name: "EditorContent",
props: {
editor: {
default: null,
type: Object
}
},
setup(props) {
const rootEl = ref();
const instance = getCurrentInstance();
watchEffect(() => {
const editor = props.editor;
if (editor && editor.options.element && rootEl.value) {
nextTick(() => {
var _a;
if (!rootEl.value || !((_a = editor.view.dom) == null ? void 0 : _a.parentNode)) {
return;
}
const element = unref(rootEl.value);
rootEl.value.append(...editor.view.dom.parentNode.childNodes);
editor.contentComponent = instance.ctx._;
if (instance) {
editor.appContext = {
...instance.appContext,
// Vue internally uses prototype chain to forward/shadow injects across the entire component chain
// so don't use object spread operator or 'Object.assign' and just set `provides` as is on editor's appContext
// @ts-expect-error forward instance's 'provides' into appContext
provides: instance.provides
};
}
editor.setOptions({
element
});
editor.createNodeViews();
});
}
});
onBeforeUnmount(() => {
const editor = props.editor;
if (!editor) {
return;
}
editor.contentComponent = null;
editor.appContext = null;
});
return { rootEl };
},
render() {
return h("div", {
ref: (el) => {
this.rootEl = el;
}
});
}
});
// src/NodeViewContent.ts
import { defineComponent as defineComponent2, h as h2 } from "vue";
var NodeViewContent = defineComponent2({
name: "NodeViewContent",
props: {
as: {
type: String,
default: "div"
}
},
render() {
return h2(this.as, {
style: {
whiteSpace: "pre-wrap"
},
"data-node-view-content": ""
});
}
});
// src/NodeViewWrapper.ts
import { defineComponent as defineComponent3, h as h3 } from "vue";
var NodeViewWrapper = defineComponent3({
name: "NodeViewWrapper",
props: {
as: {
type: String,
default: "div"
}
},
inject: ["onDragStart", "decorationClasses"],
render() {
var _a, _b;
return h3(
this.as,
{
// @ts-ignore
class: this.decorationClasses,
style: {
whiteSpace: "normal"
},
"data-node-view-wrapper": "",
// @ts-ignore (https://github.com/vuejs/vue-next/issues/3031)
onDragstart: this.onDragStart
},
(_b = (_a = this.$slots).default) == null ? void 0 : _b.call(_a)
);
}
});
// src/useEditor.ts
import { onBeforeUnmount as onBeforeUnmount2, onMounted, shallowRef } from "vue";
var useEditor = (options = {}) => {
const editor = shallowRef();
onMounted(() => {
editor.value = new Editor(options);
});
onBeforeUnmount2(() => {
var _a, _b, _c, _d;
const nodes = (_b = (_a = editor.value) == null ? void 0 : _a.view.dom) == null ? void 0 : _b.parentNode;
const newEl = nodes == null ? void 0 : nodes.cloneNode(true);
(_c = nodes == null ? void 0 : nodes.parentNode) == null ? void 0 : _c.replaceChild(newEl, nodes);
(_d = editor.value) == null ? void 0 : _d.destroy();
});
return editor;
};
// src/VueMarkViewRenderer.ts
import { MarkView } from "@tiptap/core";
import { defineComponent as defineComponent4, h as h5, toRaw } from "vue";
// src/VueRenderer.ts
import { h as h4, markRaw as markRaw2, reactive, render } from "vue";
var VueRenderer = class {
constructor(component, { props = {}, editor }) {
/**
* Flag to track if the renderer has been destroyed, preventing queued or asynchronous renders from executing after teardown.
*/
this.destroyed = false;
this.editor = editor;
this.component = markRaw2(component);
this.el = document.createElement("div");
this.props = reactive(props);
this.renderedComponent = this.renderComponent();
}
get element() {
return this.renderedComponent.el;
}
get ref() {
var _a, _b, _c, _d;
if ((_b = (_a = this.renderedComponent.vNode) == null ? void 0 : _a.component) == null ? void 0 : _b.exposed) {
return this.renderedComponent.vNode.component.exposed;
}
return (_d = (_c = this.renderedComponent.vNode) == null ? void 0 : _c.component) == null ? void 0 : _d.proxy;
}
renderComponent() {
if (this.destroyed) {
return this.renderedComponent;
}
let vNode = h4(this.component, this.props);
if (this.editor.appContext) {
vNode.appContext = this.editor.appContext;
}
if (typeof document !== "undefined" && this.el) {
render(vNode, this.el);
}
const destroy = () => {
if (this.el) {
render(null, this.el);
}
this.el = null;
vNode = null;
};
return { vNode, destroy, el: this.el ? this.el.firstElementChild : null };
}
updateProps(props = {}) {
if (this.destroyed) {
return;
}
Object.entries(props).forEach(([key, value]) => {
this.props[key] = value;
});
this.renderComponent();
}
destroy() {
if (this.destroyed) {
return;
}
this.destroyed = true;
this.renderedComponent.destroy();
}
};
// src/VueMarkViewRenderer.ts
var markViewProps = {
editor: {
type: Object,
required: true
},
mark: {
type: Object,
required: true
},
extension: {
type: Object,
required: true
},
inline: {
type: Boolean,
required: true
},
view: {
type: Object,
required: true
},
updateAttributes: {
type: Function,
required: true
},
HTMLAttributes: {
type: Object,
required: true
}
};
var MarkViewContent = defineComponent4({
name: "MarkViewContent",
props: {
as: {
type: String,
default: "span"
}
},
render() {
return h5(this.as, {
style: {
whiteSpace: "inherit"
},
"data-mark-view-content": ""
});
}
});
var VueMarkView = class extends MarkView {
constructor(component, props, options) {
super(component, props, options);
const componentProps = { ...props, updateAttributes: this.updateAttributes.bind(this) };
const extendedComponent = defineComponent4({
extends: { ...component },
props: Object.keys(componentProps),
template: this.component.template,
setup: (reactiveProps) => {
var _a;
return (_a = component.setup) == null ? void 0 : _a.call(component, reactiveProps, {
expose: () => void 0
});
},
// Add support for scoped styles
__scopeId: component.__scopeId,
__cssModules: component.__cssModules,
__name: component.__name,
__file: component.__file
});
this.renderer = new VueRenderer(extendedComponent, {
editor: this.editor,
props: componentProps
});
}
get dom() {
return this.renderer.element;
}
get contentDOM() {
return this.dom.querySelector("[data-mark-view-content]");
}
updateAttributes(attrs) {
const unproxiedMark = toRaw(this.mark);
super.updateAttributes(attrs, unproxiedMark);
}
destroy() {
this.renderer.destroy();
}
};
function VueMarkViewRenderer(component, options = {}) {
return (props) => {
if (!props.editor.contentComponent) {
return {};
}
return new VueMarkView(component, props, options);
};
}
// src/VueNodeViewRenderer.ts
import { NodeView } from "@tiptap/core";
import { defineComponent as defineComponent5, provide, ref as ref2 } from "vue";
var nodeViewProps = {
editor: {
type: Object,
required: true
},
node: {
type: Object,
required: true
},
decorations: {
type: Object,
required: true
},
selected: {
type: Boolean,
required: true
},
extension: {
type: Object,
required: true
},
getPos: {
type: Function,
required: true
},
updateAttributes: {
type: Function,
required: true
},
deleteNode: {
type: Function,
required: true
},
view: {
type: Object,
required: true
},
innerDecorations: {
type: Object,
required: true
},
HTMLAttributes: {
type: Object,
required: true
}
};
var VueNodeView = class extends NodeView {
constructor() {
super(...arguments);
this.cachedExtensionWithSyncedStorage = null;
}
/**
* Returns a proxy of the extension that redirects storage access to the editor's mutable storage.
* This preserves the original prototype chain (instanceof checks, methods like configure/extend work).
* Cached to avoid proxy creation on every update.
*/
get extensionWithSyncedStorage() {
if (!this.cachedExtensionWithSyncedStorage) {
const editor = this.editor;
const extension = this.extension;
this.cachedExtensionWithSyncedStorage = new Proxy(extension, {
get(target, prop, receiver) {
var _a;
if (prop === "storage") {
return (_a = editor.storage[extension.name]) != null ? _a : {};
}
return Reflect.get(target, prop, receiver);
}
});
}
return this.cachedExtensionWithSyncedStorage;
}
mount() {
const props = {
editor: this.editor,
node: this.node,
decorations: this.decorations,
innerDecorations: this.innerDecorations,
view: this.view,
selected: false,
extension: this.extensionWithSyncedStorage,
HTMLAttributes: this.HTMLAttributes,
getPos: () => this.getPos(),
updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
deleteNode: () => this.deleteNode()
};
const onDragStart = this.onDragStart.bind(this);
this.decorationClasses = ref2(this.getDecorationClasses());
const extendedComponent = defineComponent5({
extends: { ...this.component },
props: Object.keys(props),
template: this.component.template,
setup: (reactiveProps) => {
var _a, _b;
provide("onDragStart", onDragStart);
provide("decorationClasses", this.decorationClasses);
return (_b = (_a = this.component).setup) == null ? void 0 : _b.call(_a, reactiveProps, {
expose: () => void 0
});
},
// add support for scoped styles
// @ts-ignore
// eslint-disable-next-line
__scopeId: this.component.__scopeId,
// add support for CSS Modules
// @ts-ignore
// eslint-disable-next-line
__cssModules: this.component.__cssModules,
// add support for vue devtools
// @ts-ignore
// eslint-disable-next-line
__name: this.component.__name,
// @ts-ignore
// eslint-disable-next-line
__file: this.component.__file
});
this.handleSelectionUpdate = this.handleSelectionUpdate.bind(this);
this.editor.on("selectionUpdate", this.handleSelectionUpdate);
this.renderer = new VueRenderer(extendedComponent, {
editor: this.editor,
props
});
}
/**
* Return the DOM element.
* This is the element that will be used to display the node view.
*/
get dom() {
if (!this.renderer.element || !this.renderer.element.hasAttribute("data-node-view-wrapper")) {
throw Error("Please use the NodeViewWrapper component for your node view.");
}
return this.renderer.element;
}
/**
* Return the content DOM element.
* This is the element that will be used to display the rich-text content of the node.
*/
get contentDOM() {
if (this.node.isLeaf) {
return null;
}
return this.dom.querySelector("[data-node-view-content]");
}
/**
* On editor selection update, check if the node is selected.
* If it is, call `selectNode`, otherwise call `deselectNode`.
*/
handleSelectionUpdate() {
const { from, to } = this.editor.state.selection;
const pos = this.getPos();
if (typeof pos !== "number") {
return;
}
if (from <= pos && to >= pos + this.node.nodeSize) {
if (this.renderer.props.selected) {
return;
}
this.selectNode();
} else {
if (!this.renderer.props.selected) {
return;
}
this.deselectNode();
}
}
/**
* On update, update the React component.
* To prevent unnecessary updates, the `update` option can be used.
*/
update(node, decorations, innerDecorations) {
const rerenderComponent = (props) => {
this.decorationClasses.value = this.getDecorationClasses();
this.renderer.updateProps(props);
};
if (typeof this.options.update === "function") {
const oldNode = this.node;
const oldDecorations = this.decorations;
const oldInnerDecorations = this.innerDecorations;
this.node = node;
this.decorations = decorations;
this.innerDecorations = innerDecorations;
return this.options.update({
oldNode,
oldDecorations,
newNode: node,
newDecorations: decorations,
oldInnerDecorations,
innerDecorations,
updateProps: () => rerenderComponent({ node, decorations, innerDecorations, extension: this.extensionWithSyncedStorage })
});
}
if (node.type !== this.node.type) {
return false;
}
if (node === this.node && this.decorations === decorations && this.innerDecorations === innerDecorations) {
return true;
}
this.node = node;
this.decorations = decorations;
this.innerDecorations = innerDecorations;
rerenderComponent({ node, decorations, innerDecorations, extension: this.extensionWithSyncedStorage });
return true;
}
/**
* Select the node.
* Add the `selected` prop and the `ProseMirror-selectednode` class.
*/
selectNode() {
this.renderer.updateProps({
selected: true
});
if (this.renderer.element) {
this.renderer.element.classList.add("ProseMirror-selectednode");
}
}
/**
* Deselect the node.
* Remove the `selected` prop and the `ProseMirror-selectednode` class.
*/
deselectNode() {
this.renderer.updateProps({
selected: false
});
if (this.renderer.element) {
this.renderer.element.classList.remove("ProseMirror-selectednode");
}
}
getDecorationClasses() {
return this.decorations.flatMap((item) => item.type.attrs.class).join(" ");
}
destroy() {
this.renderer.destroy();
this.editor.off("selectionUpdate", this.handleSelectionUpdate);
}
};
function VueNodeViewRenderer(component, options) {
return (props) => {
if (!props.editor.contentComponent) {
return {};
}
const normalizedComponent = typeof component === "function" && "__vccOpts" in component ? component.__vccOpts : component;
return new VueNodeView(normalizedComponent, props, options);
};
}
// src/index.ts
export * from "@tiptap/core";
export {
Editor,
EditorContent,
MarkViewContent,
NodeViewContent,
NodeViewWrapper,
VueMarkView,
VueMarkViewRenderer,
VueNodeViewRenderer,
VueRenderer,
markViewProps,
nodeViewProps,
useEditor
};
//# sourceMappingURL=index.js.map

1
node_modules/@tiptap/vue-3/dist/index.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

873
node_modules/@tiptap/vue-3/dist/menus/index.cjs generated vendored Normal file
View File

@@ -0,0 +1,873 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/menus/index.ts
var index_exports = {};
__export(index_exports, {
BubbleMenu: () => BubbleMenu,
FloatingMenu: () => FloatingMenu
});
module.exports = __toCommonJS(index_exports);
// ../extension-bubble-menu/src/bubble-menu-plugin.ts
var import_dom = require("@floating-ui/dom");
var import_core = require("@tiptap/core");
var import_state = require("@tiptap/pm/state");
var import_tables = require("@tiptap/pm/tables");
function combineDOMRects(rect1, rect2) {
const top = Math.min(rect1.top, rect2.top);
const bottom = Math.max(rect1.bottom, rect2.bottom);
const left = Math.min(rect1.left, rect2.left);
const right = Math.max(rect1.right, rect2.right);
const width = right - left;
const height = bottom - top;
const x = left;
const y = top;
return new DOMRect(x, y, width, height);
}
var BubbleMenuView = class {
constructor({
editor,
element,
view,
pluginKey = "bubbleMenu",
updateDelay = 250,
resizeDelay = 60,
shouldShow,
appendTo,
getReferencedVirtualElement,
options
}) {
this.preventHide = false;
this.isVisible = false;
this.scrollTarget = window;
this.floatingUIOptions = {
strategy: "absolute",
placement: "top",
offset: 8,
flip: {},
shift: {},
arrow: false,
size: false,
autoPlacement: false,
hide: false,
inline: false,
onShow: void 0,
onHide: void 0,
onUpdate: void 0,
onDestroy: void 0
};
this.shouldShow = ({ view, state, from, to }) => {
const { doc, selection } = state;
const { empty } = selection;
const isEmptyTextBlock = !doc.textBetween(from, to).length && (0, import_core.isTextSelection)(state.selection);
const isChildOfMenu = this.element.contains(document.activeElement);
const hasEditorFocus = view.hasFocus() || isChildOfMenu;
if (!hasEditorFocus || empty || isEmptyTextBlock || !this.editor.isEditable) {
return false;
}
return true;
};
this.mousedownHandler = () => {
this.preventHide = true;
};
this.dragstartHandler = () => {
this.hide();
};
/**
* Handles the window resize event to update the position of the bubble menu.
* It uses a debounce mechanism to prevent excessive updates.
* The delay is defined by the `resizeDelay` property.
*/
this.resizeHandler = () => {
if (this.resizeDebounceTimer) {
clearTimeout(this.resizeDebounceTimer);
}
this.resizeDebounceTimer = window.setTimeout(() => {
this.updatePosition();
}, this.resizeDelay);
};
this.focusHandler = () => {
setTimeout(() => this.update(this.editor.view));
};
this.blurHandler = ({ event }) => {
var _a;
if (this.editor.isDestroyed) {
this.destroy();
return;
}
if (this.preventHide) {
this.preventHide = false;
return;
}
if ((event == null ? void 0 : event.relatedTarget) && ((_a = this.element.parentNode) == null ? void 0 : _a.contains(event.relatedTarget))) {
return;
}
if ((event == null ? void 0 : event.relatedTarget) === this.editor.view.dom) {
return;
}
this.hide();
};
this.handleDebouncedUpdate = (view, oldState) => {
const selectionChanged = !(oldState == null ? void 0 : oldState.selection.eq(view.state.selection));
const docChanged = !(oldState == null ? void 0 : oldState.doc.eq(view.state.doc));
if (!selectionChanged && !docChanged) {
return;
}
if (this.updateDebounceTimer) {
clearTimeout(this.updateDebounceTimer);
}
this.updateDebounceTimer = window.setTimeout(() => {
this.updateHandler(view, selectionChanged, docChanged, oldState);
}, this.updateDelay);
};
this.updateHandler = (view, selectionChanged, docChanged, oldState) => {
const { composing } = view;
const isSame = !selectionChanged && !docChanged;
if (composing || isSame) {
return;
}
const shouldShow = this.getShouldShow(oldState);
if (!shouldShow) {
this.hide();
return;
}
this.updatePosition();
this.show();
};
/**
* Handles the transaction event to update the position of the bubble menu.
* This allows external code to trigger a position update via:
* `editor.view.dispatch(editor.state.tr.setMeta(pluginKey, 'updatePosition'))`
* The `pluginKey` defaults to `bubbleMenu`
*/
this.transactionHandler = ({ transaction: tr }) => {
const meta = tr.getMeta(this.pluginKey);
if (meta === "updatePosition") {
this.updatePosition();
} else if (meta && typeof meta === "object" && meta.type === "updateOptions") {
this.updateOptions(meta.options);
}
};
var _a;
this.editor = editor;
this.element = element;
this.view = view;
this.pluginKey = pluginKey;
this.updateDelay = updateDelay;
this.resizeDelay = resizeDelay;
this.appendTo = appendTo;
this.scrollTarget = (_a = options == null ? void 0 : options.scrollTarget) != null ? _a : window;
this.getReferencedVirtualElement = getReferencedVirtualElement;
this.floatingUIOptions = {
...this.floatingUIOptions,
...options
};
this.element.tabIndex = 0;
if (shouldShow) {
this.shouldShow = shouldShow;
}
this.element.addEventListener("mousedown", this.mousedownHandler, { capture: true });
this.view.dom.addEventListener("dragstart", this.dragstartHandler);
this.editor.on("focus", this.focusHandler);
this.editor.on("blur", this.blurHandler);
this.editor.on("transaction", this.transactionHandler);
window.addEventListener("resize", this.resizeHandler);
this.scrollTarget.addEventListener("scroll", this.resizeHandler);
this.update(view, view.state);
if (this.getShouldShow()) {
this.show();
this.updatePosition();
}
}
get middlewares() {
const middlewares = [];
if (this.floatingUIOptions.flip) {
middlewares.push((0, import_dom.flip)(typeof this.floatingUIOptions.flip !== "boolean" ? this.floatingUIOptions.flip : void 0));
}
if (this.floatingUIOptions.shift) {
middlewares.push(
(0, import_dom.shift)(typeof this.floatingUIOptions.shift !== "boolean" ? this.floatingUIOptions.shift : void 0)
);
}
if (this.floatingUIOptions.offset) {
middlewares.push(
(0, import_dom.offset)(typeof this.floatingUIOptions.offset !== "boolean" ? this.floatingUIOptions.offset : void 0)
);
}
if (this.floatingUIOptions.arrow) {
middlewares.push((0, import_dom.arrow)(this.floatingUIOptions.arrow));
}
if (this.floatingUIOptions.size) {
middlewares.push((0, import_dom.size)(typeof this.floatingUIOptions.size !== "boolean" ? this.floatingUIOptions.size : void 0));
}
if (this.floatingUIOptions.autoPlacement) {
middlewares.push(
(0, import_dom.autoPlacement)(
typeof this.floatingUIOptions.autoPlacement !== "boolean" ? this.floatingUIOptions.autoPlacement : void 0
)
);
}
if (this.floatingUIOptions.hide) {
middlewares.push((0, import_dom.hide)(typeof this.floatingUIOptions.hide !== "boolean" ? this.floatingUIOptions.hide : void 0));
}
if (this.floatingUIOptions.inline) {
middlewares.push(
(0, import_dom.inline)(typeof this.floatingUIOptions.inline !== "boolean" ? this.floatingUIOptions.inline : void 0)
);
}
return middlewares;
}
get virtualElement() {
var _a, _b, _c;
const { selection } = this.editor.state;
const referencedVirtualElement = (_a = this.getReferencedVirtualElement) == null ? void 0 : _a.call(this);
if (referencedVirtualElement) {
return referencedVirtualElement;
}
if (!((_c = (_b = this.view) == null ? void 0 : _b.dom) == null ? void 0 : _c.parentNode)) {
return;
}
const domRect = (0, import_core.posToDOMRect)(this.view, selection.from, selection.to);
let virtualElement = {
getBoundingClientRect: () => domRect,
getClientRects: () => [domRect]
};
if (selection instanceof import_state.NodeSelection) {
let node = this.view.nodeDOM(selection.from);
const nodeViewWrapper = node.dataset.nodeViewWrapper ? node : node.querySelector("[data-node-view-wrapper]");
if (nodeViewWrapper) {
node = nodeViewWrapper;
}
if (node) {
virtualElement = {
getBoundingClientRect: () => node.getBoundingClientRect(),
getClientRects: () => [node.getBoundingClientRect()]
};
}
}
if (selection instanceof import_tables.CellSelection) {
const { $anchorCell, $headCell } = selection;
const from = $anchorCell ? $anchorCell.pos : $headCell.pos;
const to = $headCell ? $headCell.pos : $anchorCell.pos;
const fromDOM = this.view.nodeDOM(from);
const toDOM = this.view.nodeDOM(to);
if (!fromDOM || !toDOM) {
return;
}
const clientRect = fromDOM === toDOM ? fromDOM.getBoundingClientRect() : combineDOMRects(
fromDOM.getBoundingClientRect(),
toDOM.getBoundingClientRect()
);
virtualElement = {
getBoundingClientRect: () => clientRect,
getClientRects: () => [clientRect]
};
}
return virtualElement;
}
updatePosition() {
const virtualElement = this.virtualElement;
if (!virtualElement) {
return;
}
(0, import_dom.computePosition)(virtualElement, this.element, {
placement: this.floatingUIOptions.placement,
strategy: this.floatingUIOptions.strategy,
middleware: this.middlewares
}).then(({ x, y, strategy, middlewareData }) => {
var _a, _b;
if (((_a = middlewareData.hide) == null ? void 0 : _a.referenceHidden) || ((_b = middlewareData.hide) == null ? void 0 : _b.escaped)) {
this.element.style.visibility = "hidden";
return;
}
this.element.style.visibility = "visible";
this.element.style.width = "max-content";
this.element.style.position = strategy;
this.element.style.left = `${x}px`;
this.element.style.top = `${y}px`;
if (this.isVisible && this.floatingUIOptions.onUpdate) {
this.floatingUIOptions.onUpdate();
}
});
}
update(view, oldState) {
const { state } = view;
const hasValidSelection = state.selection.from !== state.selection.to;
if (this.updateDelay > 0 && hasValidSelection) {
this.handleDebouncedUpdate(view, oldState);
return;
}
const selectionChanged = !(oldState == null ? void 0 : oldState.selection.eq(view.state.selection));
const docChanged = !(oldState == null ? void 0 : oldState.doc.eq(view.state.doc));
this.updateHandler(view, selectionChanged, docChanged, oldState);
}
getShouldShow(oldState) {
var _a;
const { state } = this.view;
const { selection } = state;
const { ranges } = selection;
const from = Math.min(...ranges.map((range) => range.$from.pos));
const to = Math.max(...ranges.map((range) => range.$to.pos));
const shouldShow = (_a = this.shouldShow) == null ? void 0 : _a.call(this, {
editor: this.editor,
element: this.element,
view: this.view,
state,
oldState,
from,
to
});
return shouldShow || false;
}
show() {
var _a;
if (this.isVisible) {
return;
}
this.element.style.visibility = "visible";
this.element.style.opacity = "1";
const appendToElement = typeof this.appendTo === "function" ? this.appendTo() : this.appendTo;
(_a = appendToElement != null ? appendToElement : this.view.dom.parentElement) == null ? void 0 : _a.appendChild(this.element);
if (this.floatingUIOptions.onShow) {
this.floatingUIOptions.onShow();
}
this.isVisible = true;
}
hide() {
if (!this.isVisible) {
return;
}
this.element.style.visibility = "hidden";
this.element.style.opacity = "0";
this.element.remove();
if (this.floatingUIOptions.onHide) {
this.floatingUIOptions.onHide();
}
this.isVisible = false;
}
updateOptions(newProps) {
var _a;
if (newProps.updateDelay !== void 0) {
this.updateDelay = newProps.updateDelay;
}
if (newProps.resizeDelay !== void 0) {
this.resizeDelay = newProps.resizeDelay;
}
if (newProps.appendTo !== void 0) {
this.appendTo = newProps.appendTo;
}
if (newProps.getReferencedVirtualElement !== void 0) {
this.getReferencedVirtualElement = newProps.getReferencedVirtualElement;
}
if (newProps.shouldShow !== void 0) {
if (newProps.shouldShow) {
this.shouldShow = newProps.shouldShow;
}
}
if (newProps.options !== void 0) {
const newScrollTarget = (_a = newProps.options.scrollTarget) != null ? _a : window;
if (newScrollTarget !== this.scrollTarget) {
this.scrollTarget.removeEventListener("scroll", this.resizeHandler);
this.scrollTarget = newScrollTarget;
this.scrollTarget.addEventListener("scroll", this.resizeHandler);
}
this.floatingUIOptions = {
...this.floatingUIOptions,
...newProps.options
};
}
}
destroy() {
this.hide();
this.element.removeEventListener("mousedown", this.mousedownHandler, { capture: true });
this.view.dom.removeEventListener("dragstart", this.dragstartHandler);
window.removeEventListener("resize", this.resizeHandler);
this.scrollTarget.removeEventListener("scroll", this.resizeHandler);
this.editor.off("focus", this.focusHandler);
this.editor.off("blur", this.blurHandler);
this.editor.off("transaction", this.transactionHandler);
if (this.floatingUIOptions.onDestroy) {
this.floatingUIOptions.onDestroy();
}
}
};
var BubbleMenuPlugin = (options) => {
return new import_state.Plugin({
key: typeof options.pluginKey === "string" ? new import_state.PluginKey(options.pluginKey) : options.pluginKey,
view: (view) => new BubbleMenuView({ view, ...options })
});
};
// src/menus/BubbleMenu.ts
var import_state2 = require("@tiptap/pm/state");
var import_vue = require("vue");
var BubbleMenu = (0, import_vue.defineComponent)({
name: "BubbleMenu",
inheritAttrs: false,
props: {
pluginKey: {
type: [String, Object],
default: void 0
},
editor: {
type: Object,
required: true
},
updateDelay: {
type: Number,
default: void 0
},
resizeDelay: {
type: Number,
default: void 0
},
options: {
type: Object,
default: () => ({})
},
appendTo: {
type: [Object, Function],
default: void 0
},
shouldShow: {
type: Function,
default: null
},
getReferencedVirtualElement: {
type: Function,
default: void 0
}
},
setup(props, { slots, attrs }) {
var _a;
const root = (0, import_vue.ref)(null);
const resolvedPluginKey = (_a = props.pluginKey) != null ? _a : new import_state2.PluginKey("bubbleMenu");
(0, import_vue.onMounted)(() => {
const { editor, options, resizeDelay, appendTo, shouldShow, getReferencedVirtualElement, updateDelay } = props;
const el = root.value;
if (!el) {
return;
}
el.style.visibility = "hidden";
el.style.position = "absolute";
el.remove();
(0, import_vue.nextTick)(() => {
editor.registerPlugin(
BubbleMenuPlugin({
editor,
element: el,
options,
pluginKey: resolvedPluginKey,
resizeDelay,
appendTo,
shouldShow,
getReferencedVirtualElement,
updateDelay
})
);
});
});
(0, import_vue.onBeforeUnmount)(() => {
const { editor } = props;
editor.unregisterPlugin(resolvedPluginKey);
});
return () => {
var _a2;
return (0, import_vue.h)("div", { ref: root, ...attrs }, (_a2 = slots.default) == null ? void 0 : _a2.call(slots));
};
}
});
// ../extension-floating-menu/src/floating-menu-plugin.ts
var import_dom2 = require("@floating-ui/dom");
var import_core2 = require("@tiptap/core");
var import_state3 = require("@tiptap/pm/state");
var FloatingMenuView = class {
constructor({
editor,
element,
view,
pluginKey = "floatingMenu",
updateDelay = 250,
resizeDelay = 60,
options,
appendTo,
shouldShow
}) {
this.preventHide = false;
this.isVisible = false;
this.scrollTarget = window;
this.shouldShow = ({ view, state }) => {
const { selection } = state;
const { $anchor, empty } = selection;
const isRootDepth = $anchor.depth === 1;
const isEmptyTextBlock = $anchor.parent.isTextblock && !$anchor.parent.type.spec.code && !$anchor.parent.textContent && $anchor.parent.childCount === 0 && !this.getTextContent($anchor.parent);
if (!view.hasFocus() || !empty || !isRootDepth || !isEmptyTextBlock || !this.editor.isEditable) {
return false;
}
return true;
};
this.floatingUIOptions = {
strategy: "absolute",
placement: "right",
offset: 8,
flip: {},
shift: {},
arrow: false,
size: false,
autoPlacement: false,
hide: false,
inline: false
};
this.updateHandler = (view, selectionChanged, docChanged, oldState) => {
const { composing } = view;
const isSame = !selectionChanged && !docChanged;
if (composing || isSame) {
return;
}
const shouldShow = this.getShouldShow(oldState);
if (!shouldShow) {
this.hide();
return;
}
this.updatePosition();
this.show();
};
this.mousedownHandler = () => {
this.preventHide = true;
};
this.focusHandler = () => {
setTimeout(() => this.update(this.editor.view));
};
this.blurHandler = ({ event }) => {
var _a;
if (this.preventHide) {
this.preventHide = false;
return;
}
if ((event == null ? void 0 : event.relatedTarget) && ((_a = this.element.parentNode) == null ? void 0 : _a.contains(event.relatedTarget))) {
return;
}
if ((event == null ? void 0 : event.relatedTarget) === this.editor.view.dom) {
return;
}
this.hide();
};
/**
* Handles the transaction event to update the position of the floating menu.
* This allows external code to trigger a position update via:
* `editor.view.dispatch(editor.state.tr.setMeta(pluginKey, 'updatePosition'))`
* The `pluginKey` defaults to `floatingMenu`
*/
this.transactionHandler = ({ transaction: tr }) => {
const meta = tr.getMeta(this.pluginKey);
if (meta === "updatePosition") {
this.updatePosition();
} else if (meta && typeof meta === "object" && meta.type === "updateOptions") {
this.updateOptions(meta.options);
}
};
/**
* Handles the window resize event to update the position of the floating menu.
* It uses a debounce mechanism to prevent excessive updates.
* The delay is defined by the `resizeDelay` property.
*/
this.resizeHandler = () => {
if (this.resizeDebounceTimer) {
clearTimeout(this.resizeDebounceTimer);
}
this.resizeDebounceTimer = window.setTimeout(() => {
this.updatePosition();
}, this.resizeDelay);
};
var _a;
this.editor = editor;
this.element = element;
this.view = view;
this.pluginKey = pluginKey;
this.updateDelay = updateDelay;
this.resizeDelay = resizeDelay;
this.appendTo = appendTo;
this.scrollTarget = (_a = options == null ? void 0 : options.scrollTarget) != null ? _a : window;
this.floatingUIOptions = {
...this.floatingUIOptions,
...options
};
this.element.tabIndex = 0;
if (shouldShow) {
this.shouldShow = shouldShow;
}
this.element.addEventListener("mousedown", this.mousedownHandler, { capture: true });
this.editor.on("focus", this.focusHandler);
this.editor.on("blur", this.blurHandler);
this.editor.on("transaction", this.transactionHandler);
window.addEventListener("resize", this.resizeHandler);
this.scrollTarget.addEventListener("scroll", this.resizeHandler);
this.update(view, view.state);
if (this.getShouldShow()) {
this.show();
this.updatePosition();
}
}
getTextContent(node) {
return (0, import_core2.getText)(node, { textSerializers: (0, import_core2.getTextSerializersFromSchema)(this.editor.schema) });
}
get middlewares() {
const middlewares = [];
if (this.floatingUIOptions.flip) {
middlewares.push((0, import_dom2.flip)(typeof this.floatingUIOptions.flip !== "boolean" ? this.floatingUIOptions.flip : void 0));
}
if (this.floatingUIOptions.shift) {
middlewares.push(
(0, import_dom2.shift)(typeof this.floatingUIOptions.shift !== "boolean" ? this.floatingUIOptions.shift : void 0)
);
}
if (this.floatingUIOptions.offset) {
middlewares.push(
(0, import_dom2.offset)(typeof this.floatingUIOptions.offset !== "boolean" ? this.floatingUIOptions.offset : void 0)
);
}
if (this.floatingUIOptions.arrow) {
middlewares.push((0, import_dom2.arrow)(this.floatingUIOptions.arrow));
}
if (this.floatingUIOptions.size) {
middlewares.push((0, import_dom2.size)(typeof this.floatingUIOptions.size !== "boolean" ? this.floatingUIOptions.size : void 0));
}
if (this.floatingUIOptions.autoPlacement) {
middlewares.push(
(0, import_dom2.autoPlacement)(
typeof this.floatingUIOptions.autoPlacement !== "boolean" ? this.floatingUIOptions.autoPlacement : void 0
)
);
}
if (this.floatingUIOptions.hide) {
middlewares.push((0, import_dom2.hide)(typeof this.floatingUIOptions.hide !== "boolean" ? this.floatingUIOptions.hide : void 0));
}
if (this.floatingUIOptions.inline) {
middlewares.push(
(0, import_dom2.inline)(typeof this.floatingUIOptions.inline !== "boolean" ? this.floatingUIOptions.inline : void 0)
);
}
return middlewares;
}
getShouldShow(oldState) {
var _a;
const { state } = this.view;
const { selection } = state;
const { ranges } = selection;
const from = Math.min(...ranges.map((range) => range.$from.pos));
const to = Math.max(...ranges.map((range) => range.$to.pos));
const shouldShow = (_a = this.shouldShow) == null ? void 0 : _a.call(this, {
editor: this.editor,
view: this.view,
state,
oldState,
from,
to
});
return shouldShow;
}
updateOptions(newProps) {
var _a;
if (newProps.updateDelay !== void 0) {
this.updateDelay = newProps.updateDelay;
}
if (newProps.resizeDelay !== void 0) {
this.resizeDelay = newProps.resizeDelay;
}
if (newProps.appendTo !== void 0) {
this.appendTo = newProps.appendTo;
}
if (newProps.shouldShow !== void 0) {
if (newProps.shouldShow) {
this.shouldShow = newProps.shouldShow;
}
}
if (newProps.options !== void 0) {
const newScrollTarget = (_a = newProps.options.scrollTarget) != null ? _a : window;
if (newScrollTarget !== this.scrollTarget) {
this.scrollTarget.removeEventListener("scroll", this.resizeHandler);
this.scrollTarget = newScrollTarget;
this.scrollTarget.addEventListener("scroll", this.resizeHandler);
}
this.floatingUIOptions = {
...this.floatingUIOptions,
...newProps.options
};
}
}
updatePosition() {
const { selection } = this.editor.state;
const domRect = (0, import_core2.posToDOMRect)(this.view, selection.from, selection.to);
const virtualElement = {
getBoundingClientRect: () => domRect,
getClientRects: () => [domRect]
};
(0, import_dom2.computePosition)(virtualElement, this.element, {
placement: this.floatingUIOptions.placement,
strategy: this.floatingUIOptions.strategy,
middleware: this.middlewares
}).then(({ x, y, strategy, middlewareData }) => {
var _a, _b;
if (((_a = middlewareData.hide) == null ? void 0 : _a.referenceHidden) || ((_b = middlewareData.hide) == null ? void 0 : _b.escaped)) {
this.element.style.visibility = "hidden";
return;
}
this.element.style.visibility = "visible";
this.element.style.width = "max-content";
this.element.style.position = strategy;
this.element.style.left = `${x}px`;
this.element.style.top = `${y}px`;
if (this.isVisible && this.floatingUIOptions.onUpdate) {
this.floatingUIOptions.onUpdate();
}
});
}
update(view, oldState) {
const selectionChanged = !(oldState == null ? void 0 : oldState.selection.eq(view.state.selection));
const docChanged = !(oldState == null ? void 0 : oldState.doc.eq(view.state.doc));
this.updateHandler(view, selectionChanged, docChanged, oldState);
}
show() {
var _a;
if (this.isVisible) {
return;
}
this.element.style.visibility = "visible";
this.element.style.opacity = "1";
const appendToElement = typeof this.appendTo === "function" ? this.appendTo() : this.appendTo;
(_a = appendToElement != null ? appendToElement : this.view.dom.parentElement) == null ? void 0 : _a.appendChild(this.element);
if (this.floatingUIOptions.onShow) {
this.floatingUIOptions.onShow();
}
this.isVisible = true;
}
hide() {
if (!this.isVisible) {
return;
}
this.element.style.visibility = "hidden";
this.element.style.opacity = "0";
this.element.remove();
if (this.floatingUIOptions.onHide) {
this.floatingUIOptions.onHide();
}
this.isVisible = false;
}
destroy() {
this.hide();
this.element.removeEventListener("mousedown", this.mousedownHandler, { capture: true });
window.removeEventListener("resize", this.resizeHandler);
this.scrollTarget.removeEventListener("scroll", this.resizeHandler);
this.editor.off("focus", this.focusHandler);
this.editor.off("blur", this.blurHandler);
this.editor.off("transaction", this.transactionHandler);
if (this.floatingUIOptions.onDestroy) {
this.floatingUIOptions.onDestroy();
}
}
};
var FloatingMenuPlugin = (options) => {
return new import_state3.Plugin({
key: typeof options.pluginKey === "string" ? new import_state3.PluginKey(options.pluginKey) : options.pluginKey,
view: (view) => new FloatingMenuView({ view, ...options })
});
};
// src/menus/FloatingMenu.ts
var import_state4 = require("@tiptap/pm/state");
var import_vue2 = require("vue");
var FloatingMenu = (0, import_vue2.defineComponent)({
name: "FloatingMenu",
inheritAttrs: false,
props: {
pluginKey: {
// TODO: TypeScript breaks :(
// type: [String, Object as PropType<Exclude<FloatingMenuPluginProps['pluginKey'], string>>],
type: null,
default: void 0
},
editor: {
type: Object,
required: true
},
updateDelay: {
type: Number,
default: void 0
},
resizeDelay: {
type: Number,
default: void 0
},
options: {
type: Object,
default: () => ({})
},
appendTo: {
type: [Object, Function],
default: void 0
},
shouldShow: {
type: Function,
default: null
}
},
setup(props, { slots, attrs }) {
var _a;
const root = (0, import_vue2.ref)(null);
const resolvedPluginKey = (_a = props.pluginKey) != null ? _a : new import_state4.PluginKey("floatingMenu");
(0, import_vue2.onMounted)(() => {
const { editor, updateDelay, resizeDelay, options, appendTo, shouldShow } = props;
const el = root.value;
if (!el) {
return;
}
el.style.visibility = "hidden";
el.style.position = "absolute";
el.remove();
editor.registerPlugin(
FloatingMenuPlugin({
pluginKey: resolvedPluginKey,
editor,
element: el,
updateDelay,
resizeDelay,
options,
appendTo,
shouldShow
})
);
});
(0, import_vue2.onBeforeUnmount)(() => {
const { editor } = props;
editor.unregisterPlugin(resolvedPluginKey);
});
return () => {
var _a2;
return (0, import_vue2.h)("div", { ref: root, ...attrs }, (_a2 = slots.default) == null ? void 0 : _a2.call(slots));
};
}
});
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BubbleMenu,
FloatingMenu
});
//# sourceMappingURL=index.cjs.map

1
node_modules/@tiptap/vue-3/dist/menus/index.cjs.map generated vendored Normal file

File diff suppressed because one or more lines are too long

389
node_modules/@tiptap/vue-3/dist/menus/index.d.cts generated vendored Normal file
View File

@@ -0,0 +1,389 @@
import * as prosemirror_state from 'prosemirror-state';
import * as prosemirror_view from 'prosemirror-view';
import * as _tiptap_core from '@tiptap/core';
import { Editor } from '@tiptap/core';
import * as _floating_ui_dom from '@floating-ui/dom';
import { VirtualElement, offset, flip, shift, arrow, size, autoPlacement, hide, inline } from '@floating-ui/dom';
import * as vue from 'vue';
import { PropType } from 'vue';
import { PluginKey, EditorState } from '@tiptap/pm/state';
import { EditorView } from '@tiptap/pm/view';
interface BubbleMenuPluginProps {
/**
* The plugin key.
* @type {PluginKey | string}
* @default 'bubbleMenu'
*/
pluginKey: PluginKey | string;
/**
* The editor instance.
*/
editor: Editor;
/**
* The DOM element that contains your menu.
* @type {HTMLElement}
* @default null
*/
element: HTMLElement;
/**
* The delay in milliseconds before the menu should be updated.
* This can be useful to prevent performance issues.
* @type {number}
* @default 250
*/
updateDelay?: number;
/**
* The delay in milliseconds before the menu position should be updated on window resize.
* This can be useful to prevent performance issues.
* @type {number}
* @default 60
*/
resizeDelay?: number;
/**
* A function that determines whether the menu should be shown or not.
* If this function returns `false`, the menu will be hidden, otherwise it will be shown.
*/
shouldShow?: ((props: {
editor: Editor;
element: HTMLElement;
view: EditorView;
state: EditorState;
oldState?: EditorState;
from: number;
to: number;
}) => boolean) | null;
/**
* The DOM element to append your menu to. Default is the editor's parent element.
*
* Sometimes the menu needs to be appended to a different DOM context due to accessibility, clipping, or z-index issues.
*
* @type {HTMLElement}
* @default null
*/
appendTo?: HTMLElement | (() => HTMLElement);
/**
* A function that returns the virtual element for the menu.
* This is useful when the menu needs to be positioned relative to a specific DOM element.
* @type {() => VirtualElement | null}
* @default Position based on the selection.
*/
getReferencedVirtualElement?: () => VirtualElement | null;
/**
* The options for the bubble menu. Those are passed to Floating UI and include options for the placement, offset, flip, shift, arrow, size, autoPlacement,
* hide, and inline middlewares.
* @default {}
* @see https://floating-ui.com/docs/computePosition#options
*/
options?: {
strategy?: 'absolute' | 'fixed';
placement?: 'top' | 'right' | 'bottom' | 'left' | 'top-start' | 'top-end' | 'right-start' | 'right-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end';
offset?: Parameters<typeof offset>[0] | boolean;
flip?: Parameters<typeof flip>[0] | boolean;
shift?: Parameters<typeof shift>[0] | boolean;
arrow?: Parameters<typeof arrow>[0] | false;
size?: Parameters<typeof size>[0] | boolean;
autoPlacement?: Parameters<typeof autoPlacement>[0] | boolean;
hide?: Parameters<typeof hide>[0] | boolean;
inline?: Parameters<typeof inline>[0] | boolean;
onShow?: () => void;
onHide?: () => void;
onUpdate?: () => void;
onDestroy?: () => void;
/**
* The scrollable element that should be listened to when updating the position of the bubble menu.
* If not provided, the window will be used.
* @type {HTMLElement | Window}
*/
scrollTarget?: HTMLElement | Window;
};
}
declare const BubbleMenu: vue.DefineComponent<vue.ExtractPropTypes<{
pluginKey: {
type: PropType<BubbleMenuPluginProps["pluginKey"]>;
default: undefined;
};
editor: {
type: PropType<BubbleMenuPluginProps["editor"]>;
required: true;
};
updateDelay: {
type: PropType<BubbleMenuPluginProps["updateDelay"]>;
default: undefined;
};
resizeDelay: {
type: PropType<BubbleMenuPluginProps["resizeDelay"]>;
default: undefined;
};
options: {
type: PropType<BubbleMenuPluginProps["options"]>;
default: () => {};
};
appendTo: {
type: PropType<BubbleMenuPluginProps["appendTo"]>;
default: undefined;
};
shouldShow: {
type: PropType<Exclude<Required<BubbleMenuPluginProps>["shouldShow"], null>>;
default: null;
};
getReferencedVirtualElement: {
type: PropType<Exclude<Required<BubbleMenuPluginProps>["getReferencedVirtualElement"], null>>;
default: undefined;
};
}>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
[key: string]: any;
}>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
pluginKey: {
type: PropType<BubbleMenuPluginProps["pluginKey"]>;
default: undefined;
};
editor: {
type: PropType<BubbleMenuPluginProps["editor"]>;
required: true;
};
updateDelay: {
type: PropType<BubbleMenuPluginProps["updateDelay"]>;
default: undefined;
};
resizeDelay: {
type: PropType<BubbleMenuPluginProps["resizeDelay"]>;
default: undefined;
};
options: {
type: PropType<BubbleMenuPluginProps["options"]>;
default: () => {};
};
appendTo: {
type: PropType<BubbleMenuPluginProps["appendTo"]>;
default: undefined;
};
shouldShow: {
type: PropType<Exclude<Required<BubbleMenuPluginProps>["shouldShow"], null>>;
default: null;
};
getReferencedVirtualElement: {
type: PropType<Exclude<Required<BubbleMenuPluginProps>["getReferencedVirtualElement"], null>>;
default: undefined;
};
}>> & Readonly<{}>, {
options: {
strategy?: "absolute" | "fixed";
placement?: "top" | "right" | "bottom" | "left" | "top-start" | "top-end" | "right-start" | "right-end" | "bottom-start" | "bottom-end" | "left-start" | "left-end";
offset?: Parameters<typeof _floating_ui_dom.offset>[0] | boolean;
flip?: Parameters<typeof _floating_ui_dom.flip>[0] | boolean;
shift?: Parameters<typeof _floating_ui_dom.shift>[0] | boolean;
arrow?: Parameters<typeof _floating_ui_dom.arrow>[0] | false;
size?: Parameters<typeof _floating_ui_dom.size>[0] | boolean;
autoPlacement?: Parameters<typeof _floating_ui_dom.autoPlacement>[0] | boolean;
hide?: Parameters<typeof _floating_ui_dom.hide>[0] | boolean;
inline?: Parameters<typeof _floating_ui_dom.inline>[0] | boolean;
onShow?: () => void;
onHide?: () => void;
onUpdate?: () => void;
onDestroy?: () => void;
scrollTarget?: HTMLElement | Window;
} | undefined;
pluginKey: string | PluginKey<any>;
updateDelay: number | undefined;
resizeDelay: number | undefined;
appendTo: HTMLElement | (() => HTMLElement) | undefined;
getReferencedVirtualElement: () => _floating_ui_dom.VirtualElement | null;
shouldShow: (props: {
editor: _tiptap_core.Editor;
element: HTMLElement;
view: prosemirror_view.EditorView;
state: prosemirror_state.EditorState;
oldState?: prosemirror_state.EditorState;
from: number;
to: number;
}) => boolean;
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
interface FloatingMenuPluginProps {
/**
* The plugin key for the floating menu.
* @default 'floatingMenu'
*/
pluginKey: PluginKey | string;
/**
* The editor instance.
* @default null
*/
editor: Editor;
/**
* The DOM element that contains your menu.
* @default null
*/
element: HTMLElement;
/**
* The delay in milliseconds before the menu should be updated.
* This can be useful to prevent performance issues.
* @type {number}
* @default 250
*/
updateDelay?: number;
/**
* The delay in milliseconds before the menu position should be updated on window resize.
* This can be useful to prevent performance issues.
* @type {number}
* @default 60
*/
resizeDelay?: number;
/**
* The DOM element to append your menu to. Default is the editor's parent element.
*
* Sometimes the menu needs to be appended to a different DOM context due to accessibility, clipping, or z-index issues.
*
* @type {HTMLElement}
* @default null
*/
appendTo?: HTMLElement | (() => HTMLElement);
/**
* A function that determines whether the menu should be shown or not.
* If this function returns `false`, the menu will be hidden, otherwise it will be shown.
*/
shouldShow?: ((props: {
editor: Editor;
view: EditorView;
state: EditorState;
oldState?: EditorState;
from: number;
to: number;
}) => boolean) | null;
/**
* The options for the floating menu. Those are passed to Floating UI and include options for the placement, offset, flip, shift, arrow, size, autoPlacement,
* hide, and inline middlewares.
* @default {}
* @see https://floating-ui.com/docs/computePosition#options
*/
options?: {
strategy?: 'absolute' | 'fixed';
placement?: 'top' | 'right' | 'bottom' | 'left' | 'top-start' | 'top-end' | 'right-start' | 'right-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end';
offset?: Parameters<typeof offset>[0] | boolean;
flip?: Parameters<typeof flip>[0] | boolean;
shift?: Parameters<typeof shift>[0] | boolean;
arrow?: Parameters<typeof arrow>[0] | false;
size?: Parameters<typeof size>[0] | boolean;
autoPlacement?: Parameters<typeof autoPlacement>[0] | boolean;
hide?: Parameters<typeof hide>[0] | boolean;
inline?: Parameters<typeof inline>[0] | boolean;
onShow?: () => void;
onHide?: () => void;
onUpdate?: () => void;
onDestroy?: () => void;
/**
* The scrollable element that should be listened to when updating the position of the floating menu.
* If not provided, the window will be used.
* @type {HTMLElement | Window}
*/
scrollTarget?: HTMLElement | Window;
};
}
declare module '@tiptap/core' {
interface Commands<ReturnType> {
floatingMenu: {
/**
* Update the position of the floating menu.
* @example editor.commands.updateFloatingMenuPosition()
*/
updateFloatingMenuPosition: () => ReturnType;
};
}
}
declare const FloatingMenu: vue.DefineComponent<vue.ExtractPropTypes<{
pluginKey: {
type: null;
default: undefined;
};
editor: {
type: PropType<FloatingMenuPluginProps["editor"]>;
required: true;
};
updateDelay: {
type: PropType<FloatingMenuPluginProps["updateDelay"]>;
default: undefined;
};
resizeDelay: {
type: PropType<FloatingMenuPluginProps["resizeDelay"]>;
default: undefined;
};
options: {
type: PropType<FloatingMenuPluginProps["options"]>;
default: () => {};
};
appendTo: {
type: PropType<FloatingMenuPluginProps["appendTo"]>;
default: undefined;
};
shouldShow: {
type: PropType<Exclude<Required<FloatingMenuPluginProps>["shouldShow"], null>>;
default: null;
};
}>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
[key: string]: any;
}>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
pluginKey: {
type: null;
default: undefined;
};
editor: {
type: PropType<FloatingMenuPluginProps["editor"]>;
required: true;
};
updateDelay: {
type: PropType<FloatingMenuPluginProps["updateDelay"]>;
default: undefined;
};
resizeDelay: {
type: PropType<FloatingMenuPluginProps["resizeDelay"]>;
default: undefined;
};
options: {
type: PropType<FloatingMenuPluginProps["options"]>;
default: () => {};
};
appendTo: {
type: PropType<FloatingMenuPluginProps["appendTo"]>;
default: undefined;
};
shouldShow: {
type: PropType<Exclude<Required<FloatingMenuPluginProps>["shouldShow"], null>>;
default: null;
};
}>> & Readonly<{}>, {
options: {
strategy?: "absolute" | "fixed";
placement?: "top" | "right" | "bottom" | "left" | "top-start" | "top-end" | "right-start" | "right-end" | "bottom-start" | "bottom-end" | "left-start" | "left-end";
offset?: Parameters<typeof _floating_ui_dom.offset>[0] | boolean;
flip?: Parameters<typeof _floating_ui_dom.flip>[0] | boolean;
shift?: Parameters<typeof _floating_ui_dom.shift>[0] | boolean;
arrow?: Parameters<typeof _floating_ui_dom.arrow>[0] | false;
size?: Parameters<typeof _floating_ui_dom.size>[0] | boolean;
autoPlacement?: Parameters<typeof _floating_ui_dom.autoPlacement>[0] | boolean;
hide?: Parameters<typeof _floating_ui_dom.hide>[0] | boolean;
inline?: Parameters<typeof _floating_ui_dom.inline>[0] | boolean;
onShow?: () => void;
onHide?: () => void;
onUpdate?: () => void;
onDestroy?: () => void;
scrollTarget?: HTMLElement | Window;
} | undefined;
pluginKey: any;
updateDelay: number | undefined;
resizeDelay: number | undefined;
appendTo: HTMLElement | (() => HTMLElement) | undefined;
shouldShow: (props: {
editor: _tiptap_core.Editor;
view: prosemirror_view.EditorView;
state: prosemirror_state.EditorState;
oldState?: prosemirror_state.EditorState;
from: number;
to: number;
}) => boolean;
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
export { BubbleMenu, FloatingMenu };

389
node_modules/@tiptap/vue-3/dist/menus/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,389 @@
import * as prosemirror_state from 'prosemirror-state';
import * as prosemirror_view from 'prosemirror-view';
import * as _tiptap_core from '@tiptap/core';
import { Editor } from '@tiptap/core';
import * as _floating_ui_dom from '@floating-ui/dom';
import { VirtualElement, offset, flip, shift, arrow, size, autoPlacement, hide, inline } from '@floating-ui/dom';
import * as vue from 'vue';
import { PropType } from 'vue';
import { PluginKey, EditorState } from '@tiptap/pm/state';
import { EditorView } from '@tiptap/pm/view';
interface BubbleMenuPluginProps {
/**
* The plugin key.
* @type {PluginKey | string}
* @default 'bubbleMenu'
*/
pluginKey: PluginKey | string;
/**
* The editor instance.
*/
editor: Editor;
/**
* The DOM element that contains your menu.
* @type {HTMLElement}
* @default null
*/
element: HTMLElement;
/**
* The delay in milliseconds before the menu should be updated.
* This can be useful to prevent performance issues.
* @type {number}
* @default 250
*/
updateDelay?: number;
/**
* The delay in milliseconds before the menu position should be updated on window resize.
* This can be useful to prevent performance issues.
* @type {number}
* @default 60
*/
resizeDelay?: number;
/**
* A function that determines whether the menu should be shown or not.
* If this function returns `false`, the menu will be hidden, otherwise it will be shown.
*/
shouldShow?: ((props: {
editor: Editor;
element: HTMLElement;
view: EditorView;
state: EditorState;
oldState?: EditorState;
from: number;
to: number;
}) => boolean) | null;
/**
* The DOM element to append your menu to. Default is the editor's parent element.
*
* Sometimes the menu needs to be appended to a different DOM context due to accessibility, clipping, or z-index issues.
*
* @type {HTMLElement}
* @default null
*/
appendTo?: HTMLElement | (() => HTMLElement);
/**
* A function that returns the virtual element for the menu.
* This is useful when the menu needs to be positioned relative to a specific DOM element.
* @type {() => VirtualElement | null}
* @default Position based on the selection.
*/
getReferencedVirtualElement?: () => VirtualElement | null;
/**
* The options for the bubble menu. Those are passed to Floating UI and include options for the placement, offset, flip, shift, arrow, size, autoPlacement,
* hide, and inline middlewares.
* @default {}
* @see https://floating-ui.com/docs/computePosition#options
*/
options?: {
strategy?: 'absolute' | 'fixed';
placement?: 'top' | 'right' | 'bottom' | 'left' | 'top-start' | 'top-end' | 'right-start' | 'right-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end';
offset?: Parameters<typeof offset>[0] | boolean;
flip?: Parameters<typeof flip>[0] | boolean;
shift?: Parameters<typeof shift>[0] | boolean;
arrow?: Parameters<typeof arrow>[0] | false;
size?: Parameters<typeof size>[0] | boolean;
autoPlacement?: Parameters<typeof autoPlacement>[0] | boolean;
hide?: Parameters<typeof hide>[0] | boolean;
inline?: Parameters<typeof inline>[0] | boolean;
onShow?: () => void;
onHide?: () => void;
onUpdate?: () => void;
onDestroy?: () => void;
/**
* The scrollable element that should be listened to when updating the position of the bubble menu.
* If not provided, the window will be used.
* @type {HTMLElement | Window}
*/
scrollTarget?: HTMLElement | Window;
};
}
declare const BubbleMenu: vue.DefineComponent<vue.ExtractPropTypes<{
pluginKey: {
type: PropType<BubbleMenuPluginProps["pluginKey"]>;
default: undefined;
};
editor: {
type: PropType<BubbleMenuPluginProps["editor"]>;
required: true;
};
updateDelay: {
type: PropType<BubbleMenuPluginProps["updateDelay"]>;
default: undefined;
};
resizeDelay: {
type: PropType<BubbleMenuPluginProps["resizeDelay"]>;
default: undefined;
};
options: {
type: PropType<BubbleMenuPluginProps["options"]>;
default: () => {};
};
appendTo: {
type: PropType<BubbleMenuPluginProps["appendTo"]>;
default: undefined;
};
shouldShow: {
type: PropType<Exclude<Required<BubbleMenuPluginProps>["shouldShow"], null>>;
default: null;
};
getReferencedVirtualElement: {
type: PropType<Exclude<Required<BubbleMenuPluginProps>["getReferencedVirtualElement"], null>>;
default: undefined;
};
}>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
[key: string]: any;
}>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
pluginKey: {
type: PropType<BubbleMenuPluginProps["pluginKey"]>;
default: undefined;
};
editor: {
type: PropType<BubbleMenuPluginProps["editor"]>;
required: true;
};
updateDelay: {
type: PropType<BubbleMenuPluginProps["updateDelay"]>;
default: undefined;
};
resizeDelay: {
type: PropType<BubbleMenuPluginProps["resizeDelay"]>;
default: undefined;
};
options: {
type: PropType<BubbleMenuPluginProps["options"]>;
default: () => {};
};
appendTo: {
type: PropType<BubbleMenuPluginProps["appendTo"]>;
default: undefined;
};
shouldShow: {
type: PropType<Exclude<Required<BubbleMenuPluginProps>["shouldShow"], null>>;
default: null;
};
getReferencedVirtualElement: {
type: PropType<Exclude<Required<BubbleMenuPluginProps>["getReferencedVirtualElement"], null>>;
default: undefined;
};
}>> & Readonly<{}>, {
options: {
strategy?: "absolute" | "fixed";
placement?: "top" | "right" | "bottom" | "left" | "top-start" | "top-end" | "right-start" | "right-end" | "bottom-start" | "bottom-end" | "left-start" | "left-end";
offset?: Parameters<typeof _floating_ui_dom.offset>[0] | boolean;
flip?: Parameters<typeof _floating_ui_dom.flip>[0] | boolean;
shift?: Parameters<typeof _floating_ui_dom.shift>[0] | boolean;
arrow?: Parameters<typeof _floating_ui_dom.arrow>[0] | false;
size?: Parameters<typeof _floating_ui_dom.size>[0] | boolean;
autoPlacement?: Parameters<typeof _floating_ui_dom.autoPlacement>[0] | boolean;
hide?: Parameters<typeof _floating_ui_dom.hide>[0] | boolean;
inline?: Parameters<typeof _floating_ui_dom.inline>[0] | boolean;
onShow?: () => void;
onHide?: () => void;
onUpdate?: () => void;
onDestroy?: () => void;
scrollTarget?: HTMLElement | Window;
} | undefined;
pluginKey: string | PluginKey<any>;
updateDelay: number | undefined;
resizeDelay: number | undefined;
appendTo: HTMLElement | (() => HTMLElement) | undefined;
getReferencedVirtualElement: () => _floating_ui_dom.VirtualElement | null;
shouldShow: (props: {
editor: _tiptap_core.Editor;
element: HTMLElement;
view: prosemirror_view.EditorView;
state: prosemirror_state.EditorState;
oldState?: prosemirror_state.EditorState;
from: number;
to: number;
}) => boolean;
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
interface FloatingMenuPluginProps {
/**
* The plugin key for the floating menu.
* @default 'floatingMenu'
*/
pluginKey: PluginKey | string;
/**
* The editor instance.
* @default null
*/
editor: Editor;
/**
* The DOM element that contains your menu.
* @default null
*/
element: HTMLElement;
/**
* The delay in milliseconds before the menu should be updated.
* This can be useful to prevent performance issues.
* @type {number}
* @default 250
*/
updateDelay?: number;
/**
* The delay in milliseconds before the menu position should be updated on window resize.
* This can be useful to prevent performance issues.
* @type {number}
* @default 60
*/
resizeDelay?: number;
/**
* The DOM element to append your menu to. Default is the editor's parent element.
*
* Sometimes the menu needs to be appended to a different DOM context due to accessibility, clipping, or z-index issues.
*
* @type {HTMLElement}
* @default null
*/
appendTo?: HTMLElement | (() => HTMLElement);
/**
* A function that determines whether the menu should be shown or not.
* If this function returns `false`, the menu will be hidden, otherwise it will be shown.
*/
shouldShow?: ((props: {
editor: Editor;
view: EditorView;
state: EditorState;
oldState?: EditorState;
from: number;
to: number;
}) => boolean) | null;
/**
* The options for the floating menu. Those are passed to Floating UI and include options for the placement, offset, flip, shift, arrow, size, autoPlacement,
* hide, and inline middlewares.
* @default {}
* @see https://floating-ui.com/docs/computePosition#options
*/
options?: {
strategy?: 'absolute' | 'fixed';
placement?: 'top' | 'right' | 'bottom' | 'left' | 'top-start' | 'top-end' | 'right-start' | 'right-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end';
offset?: Parameters<typeof offset>[0] | boolean;
flip?: Parameters<typeof flip>[0] | boolean;
shift?: Parameters<typeof shift>[0] | boolean;
arrow?: Parameters<typeof arrow>[0] | false;
size?: Parameters<typeof size>[0] | boolean;
autoPlacement?: Parameters<typeof autoPlacement>[0] | boolean;
hide?: Parameters<typeof hide>[0] | boolean;
inline?: Parameters<typeof inline>[0] | boolean;
onShow?: () => void;
onHide?: () => void;
onUpdate?: () => void;
onDestroy?: () => void;
/**
* The scrollable element that should be listened to when updating the position of the floating menu.
* If not provided, the window will be used.
* @type {HTMLElement | Window}
*/
scrollTarget?: HTMLElement | Window;
};
}
declare module '@tiptap/core' {
interface Commands<ReturnType> {
floatingMenu: {
/**
* Update the position of the floating menu.
* @example editor.commands.updateFloatingMenuPosition()
*/
updateFloatingMenuPosition: () => ReturnType;
};
}
}
declare const FloatingMenu: vue.DefineComponent<vue.ExtractPropTypes<{
pluginKey: {
type: null;
default: undefined;
};
editor: {
type: PropType<FloatingMenuPluginProps["editor"]>;
required: true;
};
updateDelay: {
type: PropType<FloatingMenuPluginProps["updateDelay"]>;
default: undefined;
};
resizeDelay: {
type: PropType<FloatingMenuPluginProps["resizeDelay"]>;
default: undefined;
};
options: {
type: PropType<FloatingMenuPluginProps["options"]>;
default: () => {};
};
appendTo: {
type: PropType<FloatingMenuPluginProps["appendTo"]>;
default: undefined;
};
shouldShow: {
type: PropType<Exclude<Required<FloatingMenuPluginProps>["shouldShow"], null>>;
default: null;
};
}>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
[key: string]: any;
}>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
pluginKey: {
type: null;
default: undefined;
};
editor: {
type: PropType<FloatingMenuPluginProps["editor"]>;
required: true;
};
updateDelay: {
type: PropType<FloatingMenuPluginProps["updateDelay"]>;
default: undefined;
};
resizeDelay: {
type: PropType<FloatingMenuPluginProps["resizeDelay"]>;
default: undefined;
};
options: {
type: PropType<FloatingMenuPluginProps["options"]>;
default: () => {};
};
appendTo: {
type: PropType<FloatingMenuPluginProps["appendTo"]>;
default: undefined;
};
shouldShow: {
type: PropType<Exclude<Required<FloatingMenuPluginProps>["shouldShow"], null>>;
default: null;
};
}>> & Readonly<{}>, {
options: {
strategy?: "absolute" | "fixed";
placement?: "top" | "right" | "bottom" | "left" | "top-start" | "top-end" | "right-start" | "right-end" | "bottom-start" | "bottom-end" | "left-start" | "left-end";
offset?: Parameters<typeof _floating_ui_dom.offset>[0] | boolean;
flip?: Parameters<typeof _floating_ui_dom.flip>[0] | boolean;
shift?: Parameters<typeof _floating_ui_dom.shift>[0] | boolean;
arrow?: Parameters<typeof _floating_ui_dom.arrow>[0] | false;
size?: Parameters<typeof _floating_ui_dom.size>[0] | boolean;
autoPlacement?: Parameters<typeof _floating_ui_dom.autoPlacement>[0] | boolean;
hide?: Parameters<typeof _floating_ui_dom.hide>[0] | boolean;
inline?: Parameters<typeof _floating_ui_dom.inline>[0] | boolean;
onShow?: () => void;
onHide?: () => void;
onUpdate?: () => void;
onDestroy?: () => void;
scrollTarget?: HTMLElement | Window;
} | undefined;
pluginKey: any;
updateDelay: number | undefined;
resizeDelay: number | undefined;
appendTo: HTMLElement | (() => HTMLElement) | undefined;
shouldShow: (props: {
editor: _tiptap_core.Editor;
view: prosemirror_view.EditorView;
state: prosemirror_state.EditorState;
oldState?: prosemirror_state.EditorState;
from: number;
to: number;
}) => boolean;
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
export { BubbleMenu, FloatingMenu };

865
node_modules/@tiptap/vue-3/dist/menus/index.js generated vendored Normal file
View File

@@ -0,0 +1,865 @@
// ../extension-bubble-menu/src/bubble-menu-plugin.ts
import {
arrow,
autoPlacement,
computePosition,
flip,
hide,
inline,
offset,
shift,
size
} from "@floating-ui/dom";
import { isTextSelection, posToDOMRect } from "@tiptap/core";
import { NodeSelection, Plugin, PluginKey } from "@tiptap/pm/state";
import { CellSelection } from "@tiptap/pm/tables";
function combineDOMRects(rect1, rect2) {
const top = Math.min(rect1.top, rect2.top);
const bottom = Math.max(rect1.bottom, rect2.bottom);
const left = Math.min(rect1.left, rect2.left);
const right = Math.max(rect1.right, rect2.right);
const width = right - left;
const height = bottom - top;
const x = left;
const y = top;
return new DOMRect(x, y, width, height);
}
var BubbleMenuView = class {
constructor({
editor,
element,
view,
pluginKey = "bubbleMenu",
updateDelay = 250,
resizeDelay = 60,
shouldShow,
appendTo,
getReferencedVirtualElement,
options
}) {
this.preventHide = false;
this.isVisible = false;
this.scrollTarget = window;
this.floatingUIOptions = {
strategy: "absolute",
placement: "top",
offset: 8,
flip: {},
shift: {},
arrow: false,
size: false,
autoPlacement: false,
hide: false,
inline: false,
onShow: void 0,
onHide: void 0,
onUpdate: void 0,
onDestroy: void 0
};
this.shouldShow = ({ view, state, from, to }) => {
const { doc, selection } = state;
const { empty } = selection;
const isEmptyTextBlock = !doc.textBetween(from, to).length && isTextSelection(state.selection);
const isChildOfMenu = this.element.contains(document.activeElement);
const hasEditorFocus = view.hasFocus() || isChildOfMenu;
if (!hasEditorFocus || empty || isEmptyTextBlock || !this.editor.isEditable) {
return false;
}
return true;
};
this.mousedownHandler = () => {
this.preventHide = true;
};
this.dragstartHandler = () => {
this.hide();
};
/**
* Handles the window resize event to update the position of the bubble menu.
* It uses a debounce mechanism to prevent excessive updates.
* The delay is defined by the `resizeDelay` property.
*/
this.resizeHandler = () => {
if (this.resizeDebounceTimer) {
clearTimeout(this.resizeDebounceTimer);
}
this.resizeDebounceTimer = window.setTimeout(() => {
this.updatePosition();
}, this.resizeDelay);
};
this.focusHandler = () => {
setTimeout(() => this.update(this.editor.view));
};
this.blurHandler = ({ event }) => {
var _a;
if (this.editor.isDestroyed) {
this.destroy();
return;
}
if (this.preventHide) {
this.preventHide = false;
return;
}
if ((event == null ? void 0 : event.relatedTarget) && ((_a = this.element.parentNode) == null ? void 0 : _a.contains(event.relatedTarget))) {
return;
}
if ((event == null ? void 0 : event.relatedTarget) === this.editor.view.dom) {
return;
}
this.hide();
};
this.handleDebouncedUpdate = (view, oldState) => {
const selectionChanged = !(oldState == null ? void 0 : oldState.selection.eq(view.state.selection));
const docChanged = !(oldState == null ? void 0 : oldState.doc.eq(view.state.doc));
if (!selectionChanged && !docChanged) {
return;
}
if (this.updateDebounceTimer) {
clearTimeout(this.updateDebounceTimer);
}
this.updateDebounceTimer = window.setTimeout(() => {
this.updateHandler(view, selectionChanged, docChanged, oldState);
}, this.updateDelay);
};
this.updateHandler = (view, selectionChanged, docChanged, oldState) => {
const { composing } = view;
const isSame = !selectionChanged && !docChanged;
if (composing || isSame) {
return;
}
const shouldShow = this.getShouldShow(oldState);
if (!shouldShow) {
this.hide();
return;
}
this.updatePosition();
this.show();
};
/**
* Handles the transaction event to update the position of the bubble menu.
* This allows external code to trigger a position update via:
* `editor.view.dispatch(editor.state.tr.setMeta(pluginKey, 'updatePosition'))`
* The `pluginKey` defaults to `bubbleMenu`
*/
this.transactionHandler = ({ transaction: tr }) => {
const meta = tr.getMeta(this.pluginKey);
if (meta === "updatePosition") {
this.updatePosition();
} else if (meta && typeof meta === "object" && meta.type === "updateOptions") {
this.updateOptions(meta.options);
}
};
var _a;
this.editor = editor;
this.element = element;
this.view = view;
this.pluginKey = pluginKey;
this.updateDelay = updateDelay;
this.resizeDelay = resizeDelay;
this.appendTo = appendTo;
this.scrollTarget = (_a = options == null ? void 0 : options.scrollTarget) != null ? _a : window;
this.getReferencedVirtualElement = getReferencedVirtualElement;
this.floatingUIOptions = {
...this.floatingUIOptions,
...options
};
this.element.tabIndex = 0;
if (shouldShow) {
this.shouldShow = shouldShow;
}
this.element.addEventListener("mousedown", this.mousedownHandler, { capture: true });
this.view.dom.addEventListener("dragstart", this.dragstartHandler);
this.editor.on("focus", this.focusHandler);
this.editor.on("blur", this.blurHandler);
this.editor.on("transaction", this.transactionHandler);
window.addEventListener("resize", this.resizeHandler);
this.scrollTarget.addEventListener("scroll", this.resizeHandler);
this.update(view, view.state);
if (this.getShouldShow()) {
this.show();
this.updatePosition();
}
}
get middlewares() {
const middlewares = [];
if (this.floatingUIOptions.flip) {
middlewares.push(flip(typeof this.floatingUIOptions.flip !== "boolean" ? this.floatingUIOptions.flip : void 0));
}
if (this.floatingUIOptions.shift) {
middlewares.push(
shift(typeof this.floatingUIOptions.shift !== "boolean" ? this.floatingUIOptions.shift : void 0)
);
}
if (this.floatingUIOptions.offset) {
middlewares.push(
offset(typeof this.floatingUIOptions.offset !== "boolean" ? this.floatingUIOptions.offset : void 0)
);
}
if (this.floatingUIOptions.arrow) {
middlewares.push(arrow(this.floatingUIOptions.arrow));
}
if (this.floatingUIOptions.size) {
middlewares.push(size(typeof this.floatingUIOptions.size !== "boolean" ? this.floatingUIOptions.size : void 0));
}
if (this.floatingUIOptions.autoPlacement) {
middlewares.push(
autoPlacement(
typeof this.floatingUIOptions.autoPlacement !== "boolean" ? this.floatingUIOptions.autoPlacement : void 0
)
);
}
if (this.floatingUIOptions.hide) {
middlewares.push(hide(typeof this.floatingUIOptions.hide !== "boolean" ? this.floatingUIOptions.hide : void 0));
}
if (this.floatingUIOptions.inline) {
middlewares.push(
inline(typeof this.floatingUIOptions.inline !== "boolean" ? this.floatingUIOptions.inline : void 0)
);
}
return middlewares;
}
get virtualElement() {
var _a, _b, _c;
const { selection } = this.editor.state;
const referencedVirtualElement = (_a = this.getReferencedVirtualElement) == null ? void 0 : _a.call(this);
if (referencedVirtualElement) {
return referencedVirtualElement;
}
if (!((_c = (_b = this.view) == null ? void 0 : _b.dom) == null ? void 0 : _c.parentNode)) {
return;
}
const domRect = posToDOMRect(this.view, selection.from, selection.to);
let virtualElement = {
getBoundingClientRect: () => domRect,
getClientRects: () => [domRect]
};
if (selection instanceof NodeSelection) {
let node = this.view.nodeDOM(selection.from);
const nodeViewWrapper = node.dataset.nodeViewWrapper ? node : node.querySelector("[data-node-view-wrapper]");
if (nodeViewWrapper) {
node = nodeViewWrapper;
}
if (node) {
virtualElement = {
getBoundingClientRect: () => node.getBoundingClientRect(),
getClientRects: () => [node.getBoundingClientRect()]
};
}
}
if (selection instanceof CellSelection) {
const { $anchorCell, $headCell } = selection;
const from = $anchorCell ? $anchorCell.pos : $headCell.pos;
const to = $headCell ? $headCell.pos : $anchorCell.pos;
const fromDOM = this.view.nodeDOM(from);
const toDOM = this.view.nodeDOM(to);
if (!fromDOM || !toDOM) {
return;
}
const clientRect = fromDOM === toDOM ? fromDOM.getBoundingClientRect() : combineDOMRects(
fromDOM.getBoundingClientRect(),
toDOM.getBoundingClientRect()
);
virtualElement = {
getBoundingClientRect: () => clientRect,
getClientRects: () => [clientRect]
};
}
return virtualElement;
}
updatePosition() {
const virtualElement = this.virtualElement;
if (!virtualElement) {
return;
}
computePosition(virtualElement, this.element, {
placement: this.floatingUIOptions.placement,
strategy: this.floatingUIOptions.strategy,
middleware: this.middlewares
}).then(({ x, y, strategy, middlewareData }) => {
var _a, _b;
if (((_a = middlewareData.hide) == null ? void 0 : _a.referenceHidden) || ((_b = middlewareData.hide) == null ? void 0 : _b.escaped)) {
this.element.style.visibility = "hidden";
return;
}
this.element.style.visibility = "visible";
this.element.style.width = "max-content";
this.element.style.position = strategy;
this.element.style.left = `${x}px`;
this.element.style.top = `${y}px`;
if (this.isVisible && this.floatingUIOptions.onUpdate) {
this.floatingUIOptions.onUpdate();
}
});
}
update(view, oldState) {
const { state } = view;
const hasValidSelection = state.selection.from !== state.selection.to;
if (this.updateDelay > 0 && hasValidSelection) {
this.handleDebouncedUpdate(view, oldState);
return;
}
const selectionChanged = !(oldState == null ? void 0 : oldState.selection.eq(view.state.selection));
const docChanged = !(oldState == null ? void 0 : oldState.doc.eq(view.state.doc));
this.updateHandler(view, selectionChanged, docChanged, oldState);
}
getShouldShow(oldState) {
var _a;
const { state } = this.view;
const { selection } = state;
const { ranges } = selection;
const from = Math.min(...ranges.map((range) => range.$from.pos));
const to = Math.max(...ranges.map((range) => range.$to.pos));
const shouldShow = (_a = this.shouldShow) == null ? void 0 : _a.call(this, {
editor: this.editor,
element: this.element,
view: this.view,
state,
oldState,
from,
to
});
return shouldShow || false;
}
show() {
var _a;
if (this.isVisible) {
return;
}
this.element.style.visibility = "visible";
this.element.style.opacity = "1";
const appendToElement = typeof this.appendTo === "function" ? this.appendTo() : this.appendTo;
(_a = appendToElement != null ? appendToElement : this.view.dom.parentElement) == null ? void 0 : _a.appendChild(this.element);
if (this.floatingUIOptions.onShow) {
this.floatingUIOptions.onShow();
}
this.isVisible = true;
}
hide() {
if (!this.isVisible) {
return;
}
this.element.style.visibility = "hidden";
this.element.style.opacity = "0";
this.element.remove();
if (this.floatingUIOptions.onHide) {
this.floatingUIOptions.onHide();
}
this.isVisible = false;
}
updateOptions(newProps) {
var _a;
if (newProps.updateDelay !== void 0) {
this.updateDelay = newProps.updateDelay;
}
if (newProps.resizeDelay !== void 0) {
this.resizeDelay = newProps.resizeDelay;
}
if (newProps.appendTo !== void 0) {
this.appendTo = newProps.appendTo;
}
if (newProps.getReferencedVirtualElement !== void 0) {
this.getReferencedVirtualElement = newProps.getReferencedVirtualElement;
}
if (newProps.shouldShow !== void 0) {
if (newProps.shouldShow) {
this.shouldShow = newProps.shouldShow;
}
}
if (newProps.options !== void 0) {
const newScrollTarget = (_a = newProps.options.scrollTarget) != null ? _a : window;
if (newScrollTarget !== this.scrollTarget) {
this.scrollTarget.removeEventListener("scroll", this.resizeHandler);
this.scrollTarget = newScrollTarget;
this.scrollTarget.addEventListener("scroll", this.resizeHandler);
}
this.floatingUIOptions = {
...this.floatingUIOptions,
...newProps.options
};
}
}
destroy() {
this.hide();
this.element.removeEventListener("mousedown", this.mousedownHandler, { capture: true });
this.view.dom.removeEventListener("dragstart", this.dragstartHandler);
window.removeEventListener("resize", this.resizeHandler);
this.scrollTarget.removeEventListener("scroll", this.resizeHandler);
this.editor.off("focus", this.focusHandler);
this.editor.off("blur", this.blurHandler);
this.editor.off("transaction", this.transactionHandler);
if (this.floatingUIOptions.onDestroy) {
this.floatingUIOptions.onDestroy();
}
}
};
var BubbleMenuPlugin = (options) => {
return new Plugin({
key: typeof options.pluginKey === "string" ? new PluginKey(options.pluginKey) : options.pluginKey,
view: (view) => new BubbleMenuView({ view, ...options })
});
};
// src/menus/BubbleMenu.ts
import { PluginKey as PluginKey2 } from "@tiptap/pm/state";
import { defineComponent, h, nextTick, onBeforeUnmount, onMounted, ref } from "vue";
var BubbleMenu = defineComponent({
name: "BubbleMenu",
inheritAttrs: false,
props: {
pluginKey: {
type: [String, Object],
default: void 0
},
editor: {
type: Object,
required: true
},
updateDelay: {
type: Number,
default: void 0
},
resizeDelay: {
type: Number,
default: void 0
},
options: {
type: Object,
default: () => ({})
},
appendTo: {
type: [Object, Function],
default: void 0
},
shouldShow: {
type: Function,
default: null
},
getReferencedVirtualElement: {
type: Function,
default: void 0
}
},
setup(props, { slots, attrs }) {
var _a;
const root = ref(null);
const resolvedPluginKey = (_a = props.pluginKey) != null ? _a : new PluginKey2("bubbleMenu");
onMounted(() => {
const { editor, options, resizeDelay, appendTo, shouldShow, getReferencedVirtualElement, updateDelay } = props;
const el = root.value;
if (!el) {
return;
}
el.style.visibility = "hidden";
el.style.position = "absolute";
el.remove();
nextTick(() => {
editor.registerPlugin(
BubbleMenuPlugin({
editor,
element: el,
options,
pluginKey: resolvedPluginKey,
resizeDelay,
appendTo,
shouldShow,
getReferencedVirtualElement,
updateDelay
})
);
});
});
onBeforeUnmount(() => {
const { editor } = props;
editor.unregisterPlugin(resolvedPluginKey);
});
return () => {
var _a2;
return h("div", { ref: root, ...attrs }, (_a2 = slots.default) == null ? void 0 : _a2.call(slots));
};
}
});
// ../extension-floating-menu/src/floating-menu-plugin.ts
import {
arrow as arrow2,
autoPlacement as autoPlacement2,
computePosition as computePosition2,
flip as flip2,
hide as hide2,
inline as inline2,
offset as offset2,
shift as shift2,
size as size2
} from "@floating-ui/dom";
import { getText, getTextSerializersFromSchema, posToDOMRect as posToDOMRect2 } from "@tiptap/core";
import { Plugin as Plugin2, PluginKey as PluginKey3 } from "@tiptap/pm/state";
var FloatingMenuView = class {
constructor({
editor,
element,
view,
pluginKey = "floatingMenu",
updateDelay = 250,
resizeDelay = 60,
options,
appendTo,
shouldShow
}) {
this.preventHide = false;
this.isVisible = false;
this.scrollTarget = window;
this.shouldShow = ({ view, state }) => {
const { selection } = state;
const { $anchor, empty } = selection;
const isRootDepth = $anchor.depth === 1;
const isEmptyTextBlock = $anchor.parent.isTextblock && !$anchor.parent.type.spec.code && !$anchor.parent.textContent && $anchor.parent.childCount === 0 && !this.getTextContent($anchor.parent);
if (!view.hasFocus() || !empty || !isRootDepth || !isEmptyTextBlock || !this.editor.isEditable) {
return false;
}
return true;
};
this.floatingUIOptions = {
strategy: "absolute",
placement: "right",
offset: 8,
flip: {},
shift: {},
arrow: false,
size: false,
autoPlacement: false,
hide: false,
inline: false
};
this.updateHandler = (view, selectionChanged, docChanged, oldState) => {
const { composing } = view;
const isSame = !selectionChanged && !docChanged;
if (composing || isSame) {
return;
}
const shouldShow = this.getShouldShow(oldState);
if (!shouldShow) {
this.hide();
return;
}
this.updatePosition();
this.show();
};
this.mousedownHandler = () => {
this.preventHide = true;
};
this.focusHandler = () => {
setTimeout(() => this.update(this.editor.view));
};
this.blurHandler = ({ event }) => {
var _a;
if (this.preventHide) {
this.preventHide = false;
return;
}
if ((event == null ? void 0 : event.relatedTarget) && ((_a = this.element.parentNode) == null ? void 0 : _a.contains(event.relatedTarget))) {
return;
}
if ((event == null ? void 0 : event.relatedTarget) === this.editor.view.dom) {
return;
}
this.hide();
};
/**
* Handles the transaction event to update the position of the floating menu.
* This allows external code to trigger a position update via:
* `editor.view.dispatch(editor.state.tr.setMeta(pluginKey, 'updatePosition'))`
* The `pluginKey` defaults to `floatingMenu`
*/
this.transactionHandler = ({ transaction: tr }) => {
const meta = tr.getMeta(this.pluginKey);
if (meta === "updatePosition") {
this.updatePosition();
} else if (meta && typeof meta === "object" && meta.type === "updateOptions") {
this.updateOptions(meta.options);
}
};
/**
* Handles the window resize event to update the position of the floating menu.
* It uses a debounce mechanism to prevent excessive updates.
* The delay is defined by the `resizeDelay` property.
*/
this.resizeHandler = () => {
if (this.resizeDebounceTimer) {
clearTimeout(this.resizeDebounceTimer);
}
this.resizeDebounceTimer = window.setTimeout(() => {
this.updatePosition();
}, this.resizeDelay);
};
var _a;
this.editor = editor;
this.element = element;
this.view = view;
this.pluginKey = pluginKey;
this.updateDelay = updateDelay;
this.resizeDelay = resizeDelay;
this.appendTo = appendTo;
this.scrollTarget = (_a = options == null ? void 0 : options.scrollTarget) != null ? _a : window;
this.floatingUIOptions = {
...this.floatingUIOptions,
...options
};
this.element.tabIndex = 0;
if (shouldShow) {
this.shouldShow = shouldShow;
}
this.element.addEventListener("mousedown", this.mousedownHandler, { capture: true });
this.editor.on("focus", this.focusHandler);
this.editor.on("blur", this.blurHandler);
this.editor.on("transaction", this.transactionHandler);
window.addEventListener("resize", this.resizeHandler);
this.scrollTarget.addEventListener("scroll", this.resizeHandler);
this.update(view, view.state);
if (this.getShouldShow()) {
this.show();
this.updatePosition();
}
}
getTextContent(node) {
return getText(node, { textSerializers: getTextSerializersFromSchema(this.editor.schema) });
}
get middlewares() {
const middlewares = [];
if (this.floatingUIOptions.flip) {
middlewares.push(flip2(typeof this.floatingUIOptions.flip !== "boolean" ? this.floatingUIOptions.flip : void 0));
}
if (this.floatingUIOptions.shift) {
middlewares.push(
shift2(typeof this.floatingUIOptions.shift !== "boolean" ? this.floatingUIOptions.shift : void 0)
);
}
if (this.floatingUIOptions.offset) {
middlewares.push(
offset2(typeof this.floatingUIOptions.offset !== "boolean" ? this.floatingUIOptions.offset : void 0)
);
}
if (this.floatingUIOptions.arrow) {
middlewares.push(arrow2(this.floatingUIOptions.arrow));
}
if (this.floatingUIOptions.size) {
middlewares.push(size2(typeof this.floatingUIOptions.size !== "boolean" ? this.floatingUIOptions.size : void 0));
}
if (this.floatingUIOptions.autoPlacement) {
middlewares.push(
autoPlacement2(
typeof this.floatingUIOptions.autoPlacement !== "boolean" ? this.floatingUIOptions.autoPlacement : void 0
)
);
}
if (this.floatingUIOptions.hide) {
middlewares.push(hide2(typeof this.floatingUIOptions.hide !== "boolean" ? this.floatingUIOptions.hide : void 0));
}
if (this.floatingUIOptions.inline) {
middlewares.push(
inline2(typeof this.floatingUIOptions.inline !== "boolean" ? this.floatingUIOptions.inline : void 0)
);
}
return middlewares;
}
getShouldShow(oldState) {
var _a;
const { state } = this.view;
const { selection } = state;
const { ranges } = selection;
const from = Math.min(...ranges.map((range) => range.$from.pos));
const to = Math.max(...ranges.map((range) => range.$to.pos));
const shouldShow = (_a = this.shouldShow) == null ? void 0 : _a.call(this, {
editor: this.editor,
view: this.view,
state,
oldState,
from,
to
});
return shouldShow;
}
updateOptions(newProps) {
var _a;
if (newProps.updateDelay !== void 0) {
this.updateDelay = newProps.updateDelay;
}
if (newProps.resizeDelay !== void 0) {
this.resizeDelay = newProps.resizeDelay;
}
if (newProps.appendTo !== void 0) {
this.appendTo = newProps.appendTo;
}
if (newProps.shouldShow !== void 0) {
if (newProps.shouldShow) {
this.shouldShow = newProps.shouldShow;
}
}
if (newProps.options !== void 0) {
const newScrollTarget = (_a = newProps.options.scrollTarget) != null ? _a : window;
if (newScrollTarget !== this.scrollTarget) {
this.scrollTarget.removeEventListener("scroll", this.resizeHandler);
this.scrollTarget = newScrollTarget;
this.scrollTarget.addEventListener("scroll", this.resizeHandler);
}
this.floatingUIOptions = {
...this.floatingUIOptions,
...newProps.options
};
}
}
updatePosition() {
const { selection } = this.editor.state;
const domRect = posToDOMRect2(this.view, selection.from, selection.to);
const virtualElement = {
getBoundingClientRect: () => domRect,
getClientRects: () => [domRect]
};
computePosition2(virtualElement, this.element, {
placement: this.floatingUIOptions.placement,
strategy: this.floatingUIOptions.strategy,
middleware: this.middlewares
}).then(({ x, y, strategy, middlewareData }) => {
var _a, _b;
if (((_a = middlewareData.hide) == null ? void 0 : _a.referenceHidden) || ((_b = middlewareData.hide) == null ? void 0 : _b.escaped)) {
this.element.style.visibility = "hidden";
return;
}
this.element.style.visibility = "visible";
this.element.style.width = "max-content";
this.element.style.position = strategy;
this.element.style.left = `${x}px`;
this.element.style.top = `${y}px`;
if (this.isVisible && this.floatingUIOptions.onUpdate) {
this.floatingUIOptions.onUpdate();
}
});
}
update(view, oldState) {
const selectionChanged = !(oldState == null ? void 0 : oldState.selection.eq(view.state.selection));
const docChanged = !(oldState == null ? void 0 : oldState.doc.eq(view.state.doc));
this.updateHandler(view, selectionChanged, docChanged, oldState);
}
show() {
var _a;
if (this.isVisible) {
return;
}
this.element.style.visibility = "visible";
this.element.style.opacity = "1";
const appendToElement = typeof this.appendTo === "function" ? this.appendTo() : this.appendTo;
(_a = appendToElement != null ? appendToElement : this.view.dom.parentElement) == null ? void 0 : _a.appendChild(this.element);
if (this.floatingUIOptions.onShow) {
this.floatingUIOptions.onShow();
}
this.isVisible = true;
}
hide() {
if (!this.isVisible) {
return;
}
this.element.style.visibility = "hidden";
this.element.style.opacity = "0";
this.element.remove();
if (this.floatingUIOptions.onHide) {
this.floatingUIOptions.onHide();
}
this.isVisible = false;
}
destroy() {
this.hide();
this.element.removeEventListener("mousedown", this.mousedownHandler, { capture: true });
window.removeEventListener("resize", this.resizeHandler);
this.scrollTarget.removeEventListener("scroll", this.resizeHandler);
this.editor.off("focus", this.focusHandler);
this.editor.off("blur", this.blurHandler);
this.editor.off("transaction", this.transactionHandler);
if (this.floatingUIOptions.onDestroy) {
this.floatingUIOptions.onDestroy();
}
}
};
var FloatingMenuPlugin = (options) => {
return new Plugin2({
key: typeof options.pluginKey === "string" ? new PluginKey3(options.pluginKey) : options.pluginKey,
view: (view) => new FloatingMenuView({ view, ...options })
});
};
// src/menus/FloatingMenu.ts
import { PluginKey as PluginKey4 } from "@tiptap/pm/state";
import { defineComponent as defineComponent2, h as h2, onBeforeUnmount as onBeforeUnmount2, onMounted as onMounted2, ref as ref2 } from "vue";
var FloatingMenu = defineComponent2({
name: "FloatingMenu",
inheritAttrs: false,
props: {
pluginKey: {
// TODO: TypeScript breaks :(
// type: [String, Object as PropType<Exclude<FloatingMenuPluginProps['pluginKey'], string>>],
type: null,
default: void 0
},
editor: {
type: Object,
required: true
},
updateDelay: {
type: Number,
default: void 0
},
resizeDelay: {
type: Number,
default: void 0
},
options: {
type: Object,
default: () => ({})
},
appendTo: {
type: [Object, Function],
default: void 0
},
shouldShow: {
type: Function,
default: null
}
},
setup(props, { slots, attrs }) {
var _a;
const root = ref2(null);
const resolvedPluginKey = (_a = props.pluginKey) != null ? _a : new PluginKey4("floatingMenu");
onMounted2(() => {
const { editor, updateDelay, resizeDelay, options, appendTo, shouldShow } = props;
const el = root.value;
if (!el) {
return;
}
el.style.visibility = "hidden";
el.style.position = "absolute";
el.remove();
editor.registerPlugin(
FloatingMenuPlugin({
pluginKey: resolvedPluginKey,
editor,
element: el,
updateDelay,
resizeDelay,
options,
appendTo,
shouldShow
})
);
});
onBeforeUnmount2(() => {
const { editor } = props;
editor.unregisterPlugin(resolvedPluginKey);
});
return () => {
var _a2;
return h2("div", { ref: root, ...attrs }, (_a2 = slots.default) == null ? void 0 : _a2.call(slots));
};
}
});
export {
BubbleMenu,
FloatingMenu
};
//# sourceMappingURL=index.js.map

1
node_modules/@tiptap/vue-3/dist/menus/index.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

66
node_modules/@tiptap/vue-3/package.json generated vendored Normal file
View File

@@ -0,0 +1,66 @@
{
"name": "@tiptap/vue-3",
"description": "Vue components for tiptap",
"version": "3.21.0",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
"tiptap vue components"
],
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"exports": {
".": {
"types": {
"import": "./dist/index.d.ts",
"require": "./dist/index.d.cts"
},
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./menus": {
"types": {
"import": "./dist/menus/index.d.ts",
"require": "./dist/menus/index.d.cts"
},
"import": "./dist/menus/index.js",
"require": "./dist/menus/index.cjs"
}
},
"main": "dist/index.cjs",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"type": "module",
"files": [
"src",
"dist"
],
"devDependencies": {
"vue": "^3.5.13",
"@tiptap/core": "^3.21.0",
"@tiptap/pm": "^3.21.0"
},
"optionalDependencies": {
"@tiptap/extension-bubble-menu": "^3.21.0",
"@tiptap/extension-floating-menu": "^3.21.0"
},
"peerDependencies": {
"vue": "^3.0.0",
"@floating-ui/dom": "^1.0.0",
"@tiptap/core": "^3.21.0",
"@tiptap/pm": "^3.21.0"
},
"repository": {
"type": "git",
"url": "https://github.com/ueberdosis/tiptap",
"directory": "packages/vue-3"
},
"sideEffects": false,
"scripts": {
"build": "tsup",
"lint": "prettier ./src/ --check && eslint --cache --quiet --no-error-on-unmatched-pattern ./src/"
}
}

93
node_modules/@tiptap/vue-3/src/Editor.ts generated vendored Normal file
View File

@@ -0,0 +1,93 @@
/* eslint-disable react-hooks/rules-of-hooks */
import type { EditorOptions, Storage } from '@tiptap/core'
import { Editor as CoreEditor } from '@tiptap/core'
import type { EditorState, Plugin, PluginKey } from '@tiptap/pm/state'
import type { AppContext, ComponentInternalInstance, ComponentPublicInstance, Ref } from 'vue'
import { customRef, markRaw } from 'vue'
function useDebouncedRef<T>(value: T) {
return customRef<T>((track, trigger) => {
return {
get() {
track()
return value
},
set(newValue) {
// update state
value = newValue
// update view as soon as possible
requestAnimationFrame(() => {
requestAnimationFrame(() => {
trigger()
})
})
},
}
})
}
export type ContentComponent = ComponentInternalInstance & {
ctx: ComponentPublicInstance
}
export class Editor extends CoreEditor {
private reactiveState: Ref<EditorState>
private reactiveExtensionStorage: Ref<Storage>
public contentComponent: ContentComponent | null = null
public appContext: AppContext | null = null
constructor(options: Partial<EditorOptions> = {}) {
super(options)
this.reactiveState = useDebouncedRef(this.view.state)
this.reactiveExtensionStorage = useDebouncedRef(this.extensionStorage)
this.on('beforeTransaction', ({ nextState }) => {
this.reactiveState.value = nextState
this.reactiveExtensionStorage.value = this.extensionStorage
})
return markRaw(this) // eslint-disable-line
}
get state() {
return this.reactiveState ? this.reactiveState.value : this.view.state
}
get storage() {
return this.reactiveExtensionStorage ? this.reactiveExtensionStorage.value : super.storage
}
/**
* Register a ProseMirror plugin.
*/
public registerPlugin(
plugin: Plugin,
handlePlugins?: (newPlugin: Plugin, plugins: Plugin[]) => Plugin[],
): EditorState {
const nextState = super.registerPlugin(plugin, handlePlugins)
if (this.reactiveState) {
this.reactiveState.value = nextState
}
return nextState
}
/**
* Unregister a ProseMirror plugin.
*/
public unregisterPlugin(nameOrPluginKey: string | PluginKey): EditorState | undefined {
const nextState = super.unregisterPlugin(nameOrPluginKey)
if (this.reactiveState && nextState) {
this.reactiveState.value = nextState
}
return nextState
}
}

77
node_modules/@tiptap/vue-3/src/EditorContent.ts generated vendored Normal file
View File

@@ -0,0 +1,77 @@
import type { PropType, Ref } from 'vue'
import { defineComponent, getCurrentInstance, h, nextTick, onBeforeUnmount, ref, unref, watchEffect } from 'vue'
import type { Editor } from './Editor.js'
export const EditorContent = defineComponent({
name: 'EditorContent',
props: {
editor: {
default: null,
type: Object as PropType<Editor>,
},
},
setup(props) {
const rootEl: Ref<Element | undefined> = ref()
const instance = getCurrentInstance()
watchEffect(() => {
const editor = props.editor
if (editor && editor.options.element && rootEl.value) {
nextTick(() => {
if (!rootEl.value || !editor.view.dom?.parentNode) {
return
}
// TODO using the new editor.mount method might allow us to remove this
const element = unref(rootEl.value)
rootEl.value.append(...editor.view.dom.parentNode.childNodes)
// @ts-ignore
editor.contentComponent = instance.ctx._
if (instance) {
editor.appContext = {
...instance.appContext,
// Vue internally uses prototype chain to forward/shadow injects across the entire component chain
// so don't use object spread operator or 'Object.assign' and just set `provides` as is on editor's appContext
// @ts-expect-error forward instance's 'provides' into appContext
provides: instance.provides,
}
}
editor.setOptions({
element,
})
editor.createNodeViews()
})
}
})
onBeforeUnmount(() => {
const editor = props.editor
if (!editor) {
return
}
editor.contentComponent = null
editor.appContext = null
})
return { rootEl }
},
render() {
return h('div', {
ref: (el: any) => {
this.rootEl = el
},
})
},
})

21
node_modules/@tiptap/vue-3/src/NodeViewContent.ts generated vendored Normal file
View File

@@ -0,0 +1,21 @@
import { defineComponent, h } from 'vue'
export const NodeViewContent = defineComponent({
name: 'NodeViewContent',
props: {
as: {
type: String,
default: 'div',
},
},
render() {
return h(this.as, {
style: {
whiteSpace: 'pre-wrap',
},
'data-node-view-content': '',
})
},
})

31
node_modules/@tiptap/vue-3/src/NodeViewWrapper.ts generated vendored Normal file
View File

@@ -0,0 +1,31 @@
import { defineComponent, h } from 'vue'
export const NodeViewWrapper = defineComponent({
name: 'NodeViewWrapper',
props: {
as: {
type: String,
default: 'div',
},
},
inject: ['onDragStart', 'decorationClasses'],
render() {
return h(
this.as,
{
// @ts-ignore
class: this.decorationClasses,
style: {
whiteSpace: 'normal',
},
'data-node-view-wrapper': '',
// @ts-ignore (https://github.com/vuejs/vue-next/issues/3031)
onDragstart: this.onDragStart,
},
this.$slots.default?.(),
)
},
})

130
node_modules/@tiptap/vue-3/src/VueMarkViewRenderer.ts generated vendored Normal file
View File

@@ -0,0 +1,130 @@
/* eslint-disable no-underscore-dangle */
import type { MarkViewProps, MarkViewRenderer, MarkViewRendererOptions } from '@tiptap/core'
import { MarkView } from '@tiptap/core'
import type { Component, PropType } from 'vue'
import { defineComponent, h, toRaw } from 'vue'
import type { Editor } from './Editor.js'
import { VueRenderer } from './VueRenderer.js'
export interface VueMarkViewRendererOptions extends MarkViewRendererOptions {
as?: string
className?: string
attrs?: { [key: string]: string }
}
export const markViewProps = {
editor: {
type: Object as PropType<MarkViewProps['editor']>,
required: true as const,
},
mark: {
type: Object as PropType<MarkViewProps['mark']>,
required: true as const,
},
extension: {
type: Object as PropType<MarkViewProps['extension']>,
required: true as const,
},
inline: {
type: Boolean as PropType<MarkViewProps['inline']>,
required: true as const,
},
view: {
type: Object as PropType<MarkViewProps['view']>,
required: true as const,
},
updateAttributes: {
type: Function as PropType<MarkViewProps['updateAttributes']>,
required: true as const,
},
HTMLAttributes: {
type: Object as PropType<MarkViewProps['HTMLAttributes']>,
required: true as const,
},
}
export const MarkViewContent = defineComponent({
name: 'MarkViewContent',
props: {
as: {
type: String,
default: 'span',
},
},
render() {
return h(this.as, {
style: {
whiteSpace: 'inherit',
},
'data-mark-view-content': '',
})
},
})
export class VueMarkView extends MarkView<Component, VueMarkViewRendererOptions> {
renderer: VueRenderer
constructor(component: Component, props: MarkViewProps, options?: Partial<VueMarkViewRendererOptions>) {
super(component, props, options)
const componentProps = { ...props, updateAttributes: this.updateAttributes.bind(this) } satisfies MarkViewProps
// Create extended component with provide
const extendedComponent = defineComponent({
extends: { ...component },
props: Object.keys(componentProps),
template: (this.component as any).template,
setup: reactiveProps => {
return (component as any).setup?.(reactiveProps, {
expose: () => undefined,
})
},
// Add support for scoped styles
__scopeId: (component as any).__scopeId,
__cssModules: (component as any).__cssModules,
__name: (component as any).__name,
__file: (component as any).__file,
})
this.renderer = new VueRenderer(extendedComponent, {
editor: this.editor,
props: componentProps,
})
}
get dom() {
return this.renderer.element as HTMLElement
}
get contentDOM() {
return this.dom.querySelector('[data-mark-view-content]') as HTMLElement | null
}
updateAttributes(attrs: Record<string, any>): void {
// since this.mark is now an proxy, we need to get the actual mark from it
const unproxiedMark = toRaw(this.mark)
super.updateAttributes(attrs, unproxiedMark)
}
destroy() {
this.renderer.destroy()
}
}
export function VueMarkViewRenderer(
component: Component,
options: Partial<VueMarkViewRendererOptions> = {},
): MarkViewRenderer {
return props => {
// try to get the parent component
// this is important for vue devtools to show the component hierarchy correctly
// maybe its `undefined` because <editor-content> isnt rendered yet
if (!(props.editor as Editor).contentComponent) {
return {} as unknown as MarkView<any, any>
}
return new VueMarkView(component, props, options)
}
}

317
node_modules/@tiptap/vue-3/src/VueNodeViewRenderer.ts generated vendored Normal file
View File

@@ -0,0 +1,317 @@
/* eslint-disable no-underscore-dangle */
import type { DecorationWithType, NodeViewProps, NodeViewRenderer, NodeViewRendererOptions } from '@tiptap/core'
import { NodeView } from '@tiptap/core'
import type { Node as ProseMirrorNode } from '@tiptap/pm/model'
import type { Decoration, DecorationSource, NodeView as ProseMirrorNodeView } from '@tiptap/pm/view'
import type { Component, PropType, Ref } from 'vue'
import { defineComponent, provide, ref } from 'vue'
import type { Editor } from './Editor.js'
import { VueRenderer } from './VueRenderer.js'
export const nodeViewProps = {
editor: {
type: Object as PropType<NodeViewProps['editor']>,
required: true as const,
},
node: {
type: Object as PropType<NodeViewProps['node']>,
required: true as const,
},
decorations: {
type: Object as PropType<NodeViewProps['decorations']>,
required: true as const,
},
selected: {
type: Boolean as PropType<NodeViewProps['selected']>,
required: true as const,
},
extension: {
type: Object as PropType<NodeViewProps['extension']>,
required: true as const,
},
getPos: {
type: Function as PropType<NodeViewProps['getPos']>,
required: true as const,
},
updateAttributes: {
type: Function as PropType<NodeViewProps['updateAttributes']>,
required: true as const,
},
deleteNode: {
type: Function as PropType<NodeViewProps['deleteNode']>,
required: true as const,
},
view: {
type: Object as PropType<NodeViewProps['view']>,
required: true as const,
},
innerDecorations: {
type: Object as PropType<NodeViewProps['innerDecorations']>,
required: true as const,
},
HTMLAttributes: {
type: Object as PropType<NodeViewProps['HTMLAttributes']>,
required: true as const,
},
}
export interface VueNodeViewRendererOptions extends NodeViewRendererOptions {
update:
| ((props: {
oldNode: ProseMirrorNode
oldDecorations: readonly Decoration[]
oldInnerDecorations: DecorationSource
newNode: ProseMirrorNode
newDecorations: readonly Decoration[]
innerDecorations: DecorationSource
updateProps: () => void
}) => boolean)
| null
}
class VueNodeView extends NodeView<Component, Editor, VueNodeViewRendererOptions> {
renderer!: VueRenderer
decorationClasses!: Ref<string>
private cachedExtensionWithSyncedStorage: NodeViewProps['extension'] | null = null
/**
* Returns a proxy of the extension that redirects storage access to the editor's mutable storage.
* This preserves the original prototype chain (instanceof checks, methods like configure/extend work).
* Cached to avoid proxy creation on every update.
*/
get extensionWithSyncedStorage(): NodeViewProps['extension'] {
if (!this.cachedExtensionWithSyncedStorage) {
const editor = this.editor
const extension = this.extension
this.cachedExtensionWithSyncedStorage = new Proxy(extension, {
get(target, prop, receiver) {
if (prop === 'storage') {
return editor.storage[extension.name as keyof typeof editor.storage] ?? {}
}
return Reflect.get(target, prop, receiver)
},
})
}
return this.cachedExtensionWithSyncedStorage
}
mount() {
const props = {
editor: this.editor,
node: this.node,
decorations: this.decorations as DecorationWithType[],
innerDecorations: this.innerDecorations,
view: this.view,
selected: false,
extension: this.extensionWithSyncedStorage,
HTMLAttributes: this.HTMLAttributes,
getPos: () => this.getPos(),
updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
deleteNode: () => this.deleteNode(),
} satisfies NodeViewProps
const onDragStart = this.onDragStart.bind(this)
this.decorationClasses = ref(this.getDecorationClasses())
const extendedComponent = defineComponent({
extends: { ...this.component },
props: Object.keys(props),
template: (this.component as any).template,
setup: reactiveProps => {
provide('onDragStart', onDragStart)
provide('decorationClasses', this.decorationClasses)
return (this.component as any).setup?.(reactiveProps, {
expose: () => undefined,
})
},
// add support for scoped styles
// @ts-ignore
// eslint-disable-next-line
__scopeId: this.component.__scopeId,
// add support for CSS Modules
// @ts-ignore
// eslint-disable-next-line
__cssModules: this.component.__cssModules,
// add support for vue devtools
// @ts-ignore
// eslint-disable-next-line
__name: this.component.__name,
// @ts-ignore
// eslint-disable-next-line
__file: this.component.__file,
})
this.handleSelectionUpdate = this.handleSelectionUpdate.bind(this)
this.editor.on('selectionUpdate', this.handleSelectionUpdate)
this.renderer = new VueRenderer(extendedComponent, {
editor: this.editor,
props,
})
}
/**
* Return the DOM element.
* This is the element that will be used to display the node view.
*/
get dom() {
if (!this.renderer.element || !this.renderer.element.hasAttribute('data-node-view-wrapper')) {
throw Error('Please use the NodeViewWrapper component for your node view.')
}
return this.renderer.element as HTMLElement
}
/**
* Return the content DOM element.
* This is the element that will be used to display the rich-text content of the node.
*/
get contentDOM() {
if (this.node.isLeaf) {
return null
}
return this.dom.querySelector('[data-node-view-content]') as HTMLElement | null
}
/**
* On editor selection update, check if the node is selected.
* If it is, call `selectNode`, otherwise call `deselectNode`.
*/
handleSelectionUpdate() {
const { from, to } = this.editor.state.selection
const pos = this.getPos()
if (typeof pos !== 'number') {
return
}
if (from <= pos && to >= pos + this.node.nodeSize) {
if (this.renderer.props.selected) {
return
}
this.selectNode()
} else {
if (!this.renderer.props.selected) {
return
}
this.deselectNode()
}
}
/**
* On update, update the React component.
* To prevent unnecessary updates, the `update` option can be used.
*/
update(node: ProseMirrorNode, decorations: readonly Decoration[], innerDecorations: DecorationSource): boolean {
const rerenderComponent = (props?: Record<string, any>) => {
this.decorationClasses.value = this.getDecorationClasses()
this.renderer.updateProps(props)
}
if (typeof this.options.update === 'function') {
const oldNode = this.node
const oldDecorations = this.decorations
const oldInnerDecorations = this.innerDecorations
this.node = node
this.decorations = decorations
this.innerDecorations = innerDecorations
return this.options.update({
oldNode,
oldDecorations,
newNode: node,
newDecorations: decorations,
oldInnerDecorations,
innerDecorations,
updateProps: () =>
rerenderComponent({ node, decorations, innerDecorations, extension: this.extensionWithSyncedStorage }),
})
}
if (node.type !== this.node.type) {
return false
}
if (node === this.node && this.decorations === decorations && this.innerDecorations === innerDecorations) {
return true
}
this.node = node
this.decorations = decorations
this.innerDecorations = innerDecorations
rerenderComponent({ node, decorations, innerDecorations, extension: this.extensionWithSyncedStorage })
return true
}
/**
* Select the node.
* Add the `selected` prop and the `ProseMirror-selectednode` class.
*/
selectNode() {
this.renderer.updateProps({
selected: true,
})
if (this.renderer.element) {
this.renderer.element.classList.add('ProseMirror-selectednode')
}
}
/**
* Deselect the node.
* Remove the `selected` prop and the `ProseMirror-selectednode` class.
*/
deselectNode() {
this.renderer.updateProps({
selected: false,
})
if (this.renderer.element) {
this.renderer.element.classList.remove('ProseMirror-selectednode')
}
}
getDecorationClasses() {
return (
this.decorations
// @ts-ignore
.flatMap(item => item.type.attrs.class)
.join(' ')
)
}
destroy() {
this.renderer.destroy()
this.editor.off('selectionUpdate', this.handleSelectionUpdate)
}
}
export function VueNodeViewRenderer(
component: Component<NodeViewProps>,
options?: Partial<VueNodeViewRendererOptions>,
): NodeViewRenderer {
return props => {
// try to get the parent component
// this is important for vue devtools to show the component hierarchy correctly
// maybe its `undefined` because <editor-content> isnt rendered yet
if (!(props.editor as Editor).contentComponent) {
return {} as unknown as ProseMirrorNodeView
}
// check for class-component and normalize if neccessary
const normalizedComponent =
typeof component === 'function' && '__vccOpts' in component ? (component.__vccOpts as Component) : component
return new VueNodeView(normalizedComponent, props, options)
}
}

104
node_modules/@tiptap/vue-3/src/VueRenderer.ts generated vendored Normal file
View File

@@ -0,0 +1,104 @@
import type { Editor } from '@tiptap/core'
import type { Component, DefineComponent } from 'vue'
import { h, markRaw, reactive, render } from 'vue'
import type { Editor as ExtendedEditor } from './Editor.js'
export interface VueRendererOptions {
editor: Editor
props?: Record<string, any>
}
type ExtendedVNode = ReturnType<typeof h> | null
interface RenderedComponent {
vNode: ExtendedVNode
destroy: () => void
el: Element | null
}
/**
* This class is used to render Vue components inside the editor.
*/
export class VueRenderer {
renderedComponent!: RenderedComponent
editor: ExtendedEditor
component: Component
el: Element | null
props: Record<string, any>
/**
* Flag to track if the renderer has been destroyed, preventing queued or asynchronous renders from executing after teardown.
*/
destroyed = false
constructor(component: Component, { props = {}, editor }: VueRendererOptions) {
this.editor = editor as ExtendedEditor
this.component = markRaw(component)
this.el = document.createElement('div')
this.props = reactive(props)
this.renderedComponent = this.renderComponent()
}
get element(): Element | null {
return this.renderedComponent.el
}
get ref(): any {
// Composition API
if (this.renderedComponent.vNode?.component?.exposed) {
return this.renderedComponent.vNode.component.exposed
}
// Option API
return this.renderedComponent.vNode?.component?.proxy
}
renderComponent() {
if (this.destroyed) {
return this.renderedComponent
}
let vNode: ExtendedVNode = h(this.component as DefineComponent, this.props)
if (this.editor.appContext) {
vNode.appContext = this.editor.appContext
}
if (typeof document !== 'undefined' && this.el) {
render(vNode, this.el)
}
const destroy = () => {
if (this.el) {
render(null, this.el)
}
this.el = null
vNode = null
}
return { vNode, destroy, el: this.el ? this.el.firstElementChild : null }
}
updateProps(props: Record<string, any> = {}): void {
if (this.destroyed) {
return
}
Object.entries(props).forEach(([key, value]) => {
this.props[key] = value
})
this.renderComponent()
}
destroy(): void {
if (this.destroyed) {
return
}
this.destroyed = true
this.renderedComponent.destroy()
}
}

9
node_modules/@tiptap/vue-3/src/index.ts generated vendored Normal file
View File

@@ -0,0 +1,9 @@
export { Editor } from './Editor.js'
export * from './EditorContent.js'
export * from './NodeViewContent.js'
export * from './NodeViewWrapper.js'
export * from './useEditor.js'
export * from './VueMarkViewRenderer.js'
export * from './VueNodeViewRenderer.js'
export * from './VueRenderer.js'
export * from '@tiptap/core'

100
node_modules/@tiptap/vue-3/src/menus/BubbleMenu.ts generated vendored Normal file
View File

@@ -0,0 +1,100 @@
import type { BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu'
import { BubbleMenuPlugin } from '@tiptap/extension-bubble-menu'
import { PluginKey } from '@tiptap/pm/state'
import type { PropType } from 'vue'
import { defineComponent, h, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
export const BubbleMenu = defineComponent({
name: 'BubbleMenu',
inheritAttrs: false,
props: {
pluginKey: {
type: [String, Object] as PropType<BubbleMenuPluginProps['pluginKey']>,
default: undefined,
},
editor: {
type: Object as PropType<BubbleMenuPluginProps['editor']>,
required: true,
},
updateDelay: {
type: Number as PropType<BubbleMenuPluginProps['updateDelay']>,
default: undefined,
},
resizeDelay: {
type: Number as PropType<BubbleMenuPluginProps['resizeDelay']>,
default: undefined,
},
options: {
type: Object as PropType<BubbleMenuPluginProps['options']>,
default: () => ({}),
},
appendTo: {
type: [Object, Function] as PropType<BubbleMenuPluginProps['appendTo']>,
default: undefined,
},
shouldShow: {
type: Function as PropType<Exclude<Required<BubbleMenuPluginProps>['shouldShow'], null>>,
default: null,
},
getReferencedVirtualElement: {
type: Function as PropType<Exclude<Required<BubbleMenuPluginProps>['getReferencedVirtualElement'], null>>,
default: undefined,
},
},
setup(props, { slots, attrs }) {
const root = ref<HTMLElement | null>(null)
const resolvedPluginKey = props.pluginKey ?? new PluginKey('bubbleMenu')
onMounted(() => {
const { editor, options, resizeDelay, appendTo, shouldShow, getReferencedVirtualElement, updateDelay } = props
const el = root.value
if (!el) {
return
}
el.style.visibility = 'hidden'
el.style.position = 'absolute'
// Remove element from DOM; plugin will re-parent it when shown
el.remove()
nextTick(() => {
editor.registerPlugin(
BubbleMenuPlugin({
editor,
element: el,
options,
pluginKey: resolvedPluginKey,
resizeDelay,
appendTo,
shouldShow,
getReferencedVirtualElement,
updateDelay,
}),
)
})
})
onBeforeUnmount(() => {
const { editor } = props
editor.unregisterPlugin(resolvedPluginKey)
})
// Vue owns this element; attrs are applied reactively by Vue
// Plugin re-parents it when showing the menu
return () => h('div', { ref: root, ...attrs }, slots.default?.())
},
})

94
node_modules/@tiptap/vue-3/src/menus/FloatingMenu.ts generated vendored Normal file
View File

@@ -0,0 +1,94 @@
import type { FloatingMenuPluginProps } from '@tiptap/extension-floating-menu'
import { FloatingMenuPlugin } from '@tiptap/extension-floating-menu'
import { PluginKey } from '@tiptap/pm/state'
import type { PropType } from 'vue'
import { defineComponent, h, onBeforeUnmount, onMounted, ref } from 'vue'
export const FloatingMenu = defineComponent({
name: 'FloatingMenu',
inheritAttrs: false,
props: {
pluginKey: {
// TODO: TypeScript breaks :(
// type: [String, Object as PropType<Exclude<FloatingMenuPluginProps['pluginKey'], string>>],
type: null,
default: undefined,
},
editor: {
type: Object as PropType<FloatingMenuPluginProps['editor']>,
required: true,
},
updateDelay: {
type: Number as PropType<FloatingMenuPluginProps['updateDelay']>,
default: undefined,
},
resizeDelay: {
type: Number as PropType<FloatingMenuPluginProps['resizeDelay']>,
default: undefined,
},
options: {
type: Object as PropType<FloatingMenuPluginProps['options']>,
default: () => ({}),
},
appendTo: {
type: [Object, Function] as PropType<FloatingMenuPluginProps['appendTo']>,
default: undefined,
},
shouldShow: {
type: Function as PropType<Exclude<Required<FloatingMenuPluginProps>['shouldShow'], null>>,
default: null,
},
},
setup(props, { slots, attrs }) {
const root = ref<HTMLElement | null>(null)
const resolvedPluginKey = props.pluginKey ?? new PluginKey('floatingMenu')
onMounted(() => {
const { editor, updateDelay, resizeDelay, options, appendTo, shouldShow } = props
const el = root.value
if (!el) {
return
}
el.style.visibility = 'hidden'
el.style.position = 'absolute'
// Remove element from DOM; plugin will re-parent it when shown
el.remove()
editor.registerPlugin(
FloatingMenuPlugin({
pluginKey: resolvedPluginKey,
editor,
element: el,
updateDelay,
resizeDelay,
options,
appendTo,
shouldShow,
}),
)
})
onBeforeUnmount(() => {
const { editor } = props
editor.unregisterPlugin(resolvedPluginKey)
})
// Vue owns this element; attrs are applied reactively by Vue
// Plugin re-parents it when showing the menu
return () => h('div', { ref: root, ...attrs }, slots.default?.())
},
})

2
node_modules/@tiptap/vue-3/src/menus/index.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export * from './BubbleMenu.js'
export * from './FloatingMenu.js'

24
node_modules/@tiptap/vue-3/src/useEditor.ts generated vendored Normal file
View File

@@ -0,0 +1,24 @@
import type { EditorOptions } from '@tiptap/core'
import { onBeforeUnmount, onMounted, shallowRef } from 'vue'
import { Editor } from './Editor.js'
export const useEditor = (options: Partial<EditorOptions> = {}) => {
const editor = shallowRef<Editor>()
onMounted(() => {
editor.value = new Editor(options)
})
onBeforeUnmount(() => {
// Cloning root node (and its children) to avoid content being lost by destroy
const nodes = editor.value?.view.dom?.parentNode
const newEl = nodes?.cloneNode(true) as HTMLElement
nodes?.parentNode?.replaceChild(newEl, nodes)
editor.value?.destroy()
})
return editor
}