UNPKG

@dialpad/dialtone

Version:

Dialpad's Dialtone design system monorepo

440 lines (439 loc) 12.8 kB
import { BubbleMenuPlugin as g } from "@tiptap/extension-bubble-menu"; import { defineComponent as d, ref as l, onMounted as c, onBeforeUnmount as a, h as u, getCurrentInstance as y, watchEffect as b, nextTick as C, unref as w, markRaw as f, reactive as S, render as h, shallowRef as _, customRef as x, provide as v } from "vue"; import { Editor as N, NodeView as O } from "@tiptap/core"; export * from "@tiptap/core"; import { FloatingMenuPlugin as P } from "@tiptap/extension-floating-menu"; const A = d({ name: "BubbleMenu", props: { pluginKey: { type: [String, Object], default: "bubbleMenu" }, editor: { type: Object, required: !0 }, updateDelay: { type: Number, default: void 0 }, tippyOptions: { type: Object, default: () => ({}) }, shouldShow: { type: Function, default: null } }, setup(r, { slots: t }) { const n = l(null); return c(() => { const { updateDelay: e, editor: o, pluginKey: i, shouldShow: s, tippyOptions: p } = r; o.registerPlugin(g({ updateDelay: e, editor: o, element: n.value, pluginKey: i, shouldShow: s, tippyOptions: p })); }), a(() => { const { pluginKey: e, editor: o } = r; o.unregisterPlugin(e); }), () => { var e; return u("div", { ref: n }, (e = t.default) === null || e === void 0 ? void 0 : e.call(t)); }; } }); function m(r) { return x((t, n) => ({ get() { return t(), r; }, set(e) { r = e, requestAnimationFrame(() => { requestAnimationFrame(() => { n(); }); }); } })); } class D extends N { constructor(t = {}) { return super(t), this.contentComponent = null, this.appContext = null, this.reactiveState = m(this.view.state), this.reactiveExtensionStorage = m(this.extensionStorage), this.on("beforeTransaction", ({ nextState: n }) => { this.reactiveState.value = n, this.reactiveExtensionStorage.value = this.extensionStorage; }), f(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(t, n) { const e = super.registerPlugin(t, n); return this.reactiveState && (this.reactiveState.value = e), e; } /** * Unregister a ProseMirror plugin. */ unregisterPlugin(t) { const n = super.unregisterPlugin(t); return this.reactiveState && n && (this.reactiveState.value = n), n; } } const U = d({ name: "EditorContent", props: { editor: { default: null, type: Object } }, setup(r) { const t = l(), n = y(); return b(() => { const e = r.editor; e && e.options.element && t.value && C(() => { if (!t.value || !e.options.element.firstChild) return; const o = w(t.value); t.value.append(...e.options.element.childNodes), e.contentComponent = n.ctx._, n && (e.appContext = { ...n.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: n.provides }), e.setOptions({ element: o }), e.createNodeViews(); }); }), a(() => { const e = r.editor; e && (e.contentComponent = null, e.appContext = null); }), { rootEl: t }; }, render() { return u("div", { ref: (r) => { this.rootEl = r; } }); } }), K = d({ name: "FloatingMenu", props: { pluginKey: { // TODO: TypeScript breaks :( // type: [String, Object as PropType<Exclude<FloatingMenuPluginProps['pluginKey'], string>>], type: null, default: "floatingMenu" }, editor: { type: Object, required: !0 }, tippyOptions: { type: Object, default: () => ({}) }, shouldShow: { type: Function, default: null } }, setup(r, { slots: t }) { const n = l(null); return c(() => { const { pluginKey: e, editor: o, tippyOptions: i, shouldShow: s } = r; o.registerPlugin(P({ pluginKey: e, editor: o, element: n.value, tippyOptions: i, shouldShow: s })); }), a(() => { const { pluginKey: e, editor: o } = r; o.unregisterPlugin(e); }), () => { var e; return u("div", { ref: n }, (e = t.default) === null || e === void 0 ? void 0 : e.call(t)); }; } }), L = d({ name: "NodeViewContent", props: { as: { type: String, default: "div" } }, render() { return u(this.as, { style: { whiteSpace: "pre-wrap" }, "data-node-view-content": "" }); } }), R = d({ name: "NodeViewWrapper", props: { as: { type: String, default: "div" } }, inject: ["onDragStart", "decorationClasses"], render() { var r, t; return u(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 }, (t = (r = this.$slots).default) === null || t === void 0 ? void 0 : t.call(r)); } }), B = (r = {}) => { const t = _(); return c(() => { t.value = new D(r); }), a(() => { var n, e, o; const i = (n = t.value) === null || n === void 0 ? void 0 : n.options.element, s = i == null ? void 0 : i.cloneNode(!0); (e = i == null ? void 0 : i.parentNode) === null || e === void 0 || e.replaceChild(s, i), (o = t.value) === null || o === void 0 || o.destroy(); }), t; }; class E { constructor(t, { props: n = {}, editor: e }) { this.editor = e, this.component = f(t), this.el = document.createElement("div"), this.props = S(n), this.renderedComponent = this.renderComponent(); } get element() { return this.renderedComponent.el; } get ref() { var t, n, e, o; return !((n = (t = this.renderedComponent.vNode) === null || t === void 0 ? void 0 : t.component) === null || n === void 0) && n.exposed ? this.renderedComponent.vNode.component.exposed : (o = (e = this.renderedComponent.vNode) === null || e === void 0 ? void 0 : e.component) === null || o === void 0 ? void 0 : o.proxy; } renderComponent() { let t = u(this.component, this.props); return this.editor.appContext && (t.appContext = this.editor.appContext), typeof document < "u" && this.el && h(t, this.el), { vNode: t, destroy: () => { this.el && h(null, this.el), this.el = null, t = null; }, el: this.el ? this.el.firstElementChild : null }; } updateProps(t = {}) { Object.entries(t).forEach(([n, e]) => { this.props[n] = e; }), this.renderComponent(); } destroy() { this.renderedComponent.destroy(); } } const T = { editor: { type: Object, required: !0 }, node: { type: Object, required: !0 }, decorations: { type: Object, required: !0 }, selected: { type: Boolean, required: !0 }, extension: { type: Object, required: !0 }, getPos: { type: Function, required: !0 }, updateAttributes: { type: Function, required: !0 }, deleteNode: { type: Function, required: !0 }, view: { type: Object, required: !0 }, innerDecorations: { type: Object, required: !0 }, HTMLAttributes: { type: Object, required: !0 } }; class j extends O { mount() { const t = { editor: this.editor, node: this.node, decorations: this.decorations, innerDecorations: this.innerDecorations, view: this.view, selected: !1, extension: this.extension, HTMLAttributes: this.HTMLAttributes, getPos: () => this.getPos(), updateAttributes: (o = {}) => this.updateAttributes(o), deleteNode: () => this.deleteNode() }, n = this.onDragStart.bind(this); this.decorationClasses = l(this.getDecorationClasses()); const e = d({ extends: { ...this.component }, props: Object.keys(t), template: this.component.template, setup: (o) => { var i, s; return v("onDragStart", n), v("decorationClasses", this.decorationClasses), (s = (i = this.component).setup) === null || s === void 0 ? void 0 : s.call(i, o, { expose: () => { } }); }, // 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 E(e, { editor: this.editor, props: t }); } /** * 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() { return this.node.isLeaf ? null : 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: t, to: n } = this.editor.state.selection, e = this.getPos(); if (typeof e == "number") if (t <= e && n >= e + 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(t, n, e) { const o = (i) => { this.decorationClasses.value = this.getDecorationClasses(), this.renderer.updateProps(i); }; if (typeof this.options.update == "function") { const i = this.node, s = this.decorations, p = this.innerDecorations; return this.node = t, this.decorations = n, this.innerDecorations = e, this.options.update({ oldNode: i, oldDecorations: s, newNode: t, newDecorations: n, oldInnerDecorations: p, innerDecorations: e, updateProps: () => o({ node: t, decorations: n, innerDecorations: e }) }); } return t.type !== this.node.type ? !1 : (t === this.node && this.decorations === n && this.innerDecorations === e || (this.node = t, this.decorations = n, this.innerDecorations = e, o({ node: t, decorations: n, innerDecorations: e })), !0); } /** * Select the node. * Add the `selected` prop and the `ProseMirror-selectednode` class. */ selectNode() { this.renderer.updateProps({ selected: !0 }), 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: !1 }), this.renderer.element && this.renderer.element.classList.remove("ProseMirror-selectednode"); } getDecorationClasses() { return this.decorations.map((t) => t.type.attrs.class).flat().join(" "); } destroy() { this.renderer.destroy(), this.editor.off("selectionUpdate", this.handleSelectionUpdate); } } function I(r, t) { return (n) => { if (!n.editor.contentComponent) return {}; const e = typeof r == "function" && "__vccOpts" in r ? r.__vccOpts : r; return new j(e, n, t); }; } export { A as BubbleMenu, D as Editor, U as EditorContent, K as FloatingMenu, L as NodeViewContent, R as NodeViewWrapper, I as VueNodeViewRenderer, E as VueRenderer, T as nodeViewProps, B as useEditor }; //# sourceMappingURL=vue-3.js.map