@dialpad/dialtone
Version:
Dialpad's Dialtone design system monorepo
440 lines (439 loc) • 12.8 kB
JavaScript
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