UNPKG

@progress/kendo-vue-editor

Version:
607 lines (606 loc) 16.8 kB
/** * @license *------------------------------------------------------------------------------------------- * Copyright © 2026 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the package root for more information *------------------------------------------------------------------------------------------- */ import { defineComponent as G, toRaw as j, markRaw as P, createVNode as h, isVNode as K, h as T } from "vue"; import { ButtonGroup as J, Toolbar as q, ToolbarSeparator as Q } from "@progress/kendo-vue-buttons"; import { guid as L, WatermarkOverlay as X, classNames as Y, setRef as M, getRef as R, validatePackage as Z, shouldShowValidationUI as ee, getLicenseMessage as te, templateRendering as ie, getListeners as oe, getTemplate as se } from "@progress/kendo-vue-common"; import { Plugin as $, PluginKey as H, spacesFix as ne, history as re, dropCursor as ae, gapCursor as le, tableEditing as pe, Schema as de, marks as ce, EditorState as me, keymap as _, baseKeymap as ue, EditorView as he, getMark as fe } from "@progress/kendo-editor-common"; import { nodes as ge } from "./config/schema.mjs"; import { defaultStyle as ve, tablesStyles as we, rtlStyles as be } from "./config/defaultStyles.mjs"; import { EditorToolsSettings as t } from "./config/toolsSettings.mjs"; import { InsertLinkDialog as ke } from "./dialogs/insertLink.mjs"; import { EditorUtils as y } from "./utils/main.mjs"; import { editorPropsKey as D } from "./utils/props-key.mjs"; import { updateEditorValue as ye } from "./utils/controlled-value.mjs"; import { cleanupAndDestroyProseMirrorView as Ce, nullifyObjectProperties as Se, cleanupIframe as Ee } from "./utils/cleanup.mjs"; import { packageMetadata as A } from "./package-metadata.mjs"; import { Align as V } from "./tools/align.mjs"; import { Indent as Ve } from "./tools/indent.mjs"; import { List as x } from "./tools/lists.mjs"; import { Outdent as Le } from "./tools/outdent.mjs"; import { InlineFormat as b } from "./tools/inlineFormat.mjs"; import { FontName as I } from "./tools/fontStyle.mjs"; import { FormatBlock as $e } from "./tools/formatBlock.mjs"; import { ProseMirror as c } from "./tools/proseMirrorTool.mjs"; import { LinkTool as U } from "./tools/insertLink.mjs"; import { Unlink as Ae } from "./tools/unlink.mjs"; import { CleanFormatting as Oe } from "./tools/cleanFormatting.mjs"; import { SelectAll as Be } from "./tools/selectAll.mjs"; import { InsertImage as Fe } from "./tools/insertImage.mjs"; import { InsertTable as Pe } from "./tools/insertTable/tool.mjs"; import { ViewHtml as Te } from "./tools/viewHtml.mjs"; import { Pdf as Me } from "./tools/pdf.mjs"; import { Print as Re } from "./tools/print.mjs"; import { FindAndReplace as He } from "./tools/findReplace.mjs"; import { ApplyColor as N } from "./tools/applyColor.mjs"; import { keys as k, messages as O } from "./messages/main.mjs"; import { provideLocalizationService as _e } from "@progress/kendo-vue-intl"; const { link: W, bold: De, italic: xe, underline: Ie } = t; function z(e) { return typeof e == "function" || Object.prototype.toString.call(e) === "[object Object]" && !K(e); } const C = { Bold: { comp: b, props: t.bold }, Italic: { comp: b, props: t.italic }, Underline: { comp: b, props: t.underline }, Strikethrough: { comp: b, props: t.strikethrough }, Subscript: { comp: b, props: t.subscript }, Superscript: { comp: b, props: t.superscript }, AlignLeft: { comp: V, props: t.alignLeft }, AlignCenter: { comp: V, props: t.alignCenter }, AlignRight: { comp: V, props: t.alignRight }, AlignJustify: { comp: V, props: t.alignJustify }, Indent: { comp: Ve, props: t.indent }, Outdent: { comp: Le, props: t.outdent }, OrderedList: { comp: x, props: t.orderedList }, UnorderedList: { comp: x, props: t.bulletList }, FontSize: { comp: I, props: t.fontSize }, FontName: { comp: I, props: t.fontName }, FormatBlock: { comp: $e, props: t.formatBlock }, Undo: { comp: c, props: t.undo }, Redo: { comp: c, props: t.redo }, Link: { comp: U, props: t.link }, Unlink: { comp: Ae, props: t.unlink }, InsertImage: { comp: Fe, props: t.image }, ViewHtml: { comp: Te, props: t.viewHtml }, CleanFormatting: { comp: Oe, props: t.cleanFormatting }, SelectAll: { comp: Be, props: t.selectAll }, InsertTable: { comp: Pe, props: t.insertTable }, MergeCells: { comp: c, props: t.mergeCells }, SplitCell: { comp: c, props: t.splitCell }, AddRowBefore: { comp: c, props: t.addRowBefore }, AddRowAfter: { comp: c, props: t.addRowAfter }, AddColumnBefore: { comp: c, props: t.addColumnBefore }, AddColumnAfter: { comp: c, props: t.addColumnAfter }, DeleteRow: { comp: c, props: t.deleteRow }, DeleteColumn: { comp: c, props: t.deleteColumn }, DeleteTable: { comp: c, props: t.deleteTable }, Print: { comp: Re, props: t.print }, Pdf: { comp: Me, props: t.pdf }, InsertFile: { comp: U, props: t.insertFile }, FindAndReplace: { comp: He, props: t.findAndReplace }, ForeColor: { comp: N, props: t.foreColor }, BackColor: { comp: N, props: t.backColor } }, yt = /* @__PURE__ */ G({ name: "KendoEditor", emits: { focus: null, blur: null, change: null, loaded: null, execute: null }, inject: { kendoLocalizationService: { default: null } }, props: { defaultContent: String, value: [Object, String], defaultEditMode: { type: String, default: "iframe", validator: function(e) { return ["iframe", "div"].includes(e); } }, contentStyle: Object, dir: String, tools: Array, keyboardNavigation: { type: Boolean, default: !0 }, resizable: Boolean, preserveWhitespace: { type: [String, Boolean], default: "full", validator: function(e) { return [!0, !1, "full"].includes(e); } }, pasteHtml: Function, extendView: Function, ariaDescribedBy: String, ariaLabelledBy: String, ariaLabel: String }, data() { return { updateGuid: L(), view: void 0, linkDialog: !1, showLicenseWatermark: !1, licenseMessage: void 0 }; }, created() { this._view = void 0, this.trOnChange = null, this.valueisUpdated = !1, this._prevValue = this.$props.value, this.pluginViews = [], Z(A), this.showLicenseWatermark = ee(A), this.licenseMessage = te(A); }, mounted() { this.iframe = R(this, "iframe"), this.contentElement = R(this, "contentElement"), this.initialize(); }, watch: { value: function(e, i) { this.valueisUpdated = !0, this._prevValue = i; } }, updated() { const { value: e } = this.$props, i = this.getView(); e === void 0 || !this.valueisUpdated || !i || (ye(i, this.computedValue(), this._prevValue, this.trOnChange, this.htmlOnChange), this.valueisUpdated = !1, this.trOnChange = null, this.htmlOnChange = null); }, beforeUnmount() { var a, m; this.iframe && (this.iframe.src = "about:blank"); const e = this.iframe, i = this.contentElement, s = this.getView(); if (s) { (a = this.pluginViews) == null || a.forEach((l) => { var f; (f = l == null ? void 0 : l.destroy) == null || f.call(l); }); const u = Ce(s); (m = u == null ? void 0 : u.parentNode) == null || m.removeChild(u); } this.view && Se(this.view), this.view = void 0, this._view = null, this.trOnChange = null, this.htmlOnChange = null, this.pasteEvent = void 0, this.valueisUpdated = !1, this._prevValue = void 0, this.pluginViews && (this.pluginViews.length = 0, this.pluginViews = null), this.iframe = void 0, this.contentElement = void 0, e && Ee(e, i), this.updateGuid = void 0, this.linkDialog = !1; }, unmounted() { this.view = void 0, this._view = null, this.iframe = void 0, this.contentElement = void 0, this.pluginViews = null; }, render() { let e = 100; const { tools: i = [], defaultEditMode: s = "iframe", preserveWhitespace: a = "full", style: m, value: u } = this.$props, l = this.getView(), f = _e(this), w = this.showLicenseWatermark ? h(X, { message: this.licenseMessage }, null) : null; if (this.view) { const o = D.getState(this.view.state); o.preserveWhitespace = a; } let g = this.$props.contentStyle; g === void 0 && (m || {}).height === void 0 && (g = { height: "300px" }); const S = function() { return this.linkDialog && h(ke, { view: l, settings: W, dir: this.$props.dir, onClose: this.handleClose }, null); }, E = function(o, p) { const d = C[o] || o, F = function() { let v; return o === "ForeColor" ? v = f.toLanguageString(k.foregroundColorAriaLabel, O[k.foregroundColorAriaLabel]) : o === "BackColor" && (v = f.toLanguageString(k.backgroundColor, O[k.backgroundColor])), v; }; if (C[o]) { e++; const v = { view: l, dir: this.$props.dir, key: e, updateGuid: this.updateGuid, settings: C[o].props, ...C[o].props, ariaLabel: F() }; return T(P(C[o].comp), v); } else { if (d === "Separator") return h(Q, { key: e }, null); { const v = ie.call(this, d.render, oe.call(this)); return se.call(this, { h: T, template: v, defaultRendering: null, additionalListeners: {}, additionalProps: { view: l, dir: this.$props.dir, updateGuid: this.updateGuid, key: e, settings: d.props } }); } } }, n = function(o, p) { return o.map(function(d) { return E.call(this, d, p); }, this); }, r = i.map(function(o, p) { let d; return Array.isArray(o) ? h(J, { key: `group-${p}-${o.join("-")}` }, z(d = n.call(this, o, p)) ? d : { default: () => [d] }) : E.call(this, o, p); }, this); return h("div", { class: Y("k-editor", { "k-editor-resizable": this.$props.resizable }), dir: this.$props.dir }, [r.length > 0 && h(q, { overflow: "none", class: "k-editor-toolbar", keyboardNavigation: this.$props.keyboardNavigation }, z(r) ? r : { default: () => [r] }), s === "iframe" ? h("div", { class: "k-editor-content" }, [h("iframe", { ref: M(this, "iframe"), title: f.toLanguageString(k.iframeTitle, O[k.iframeTitle]), style: g, class: "k-iframe" }, null)]) : h("div", { style: g, class: "k-editor-content" }, [h("div", { ref: M(this, "contentElement"), role: "textbox", "aria-labelledby": this.$props.ariaLabelledBy, "aria-describedby": this.$props.ariaDescribedBy, "aria-label": this.$props.ariaLabel }, null)]), S.call(this), w]); }, methods: { getView() { return this.view; }, getHTML() { const e = this.getView(); return e ? y.getHtml(e.state) : ""; }, setHTML(e) { const i = this.getView(); i && y.setHtml(i, e); }, focus() { this.getView() && this.getView().focus(); }, updateTools(e) { this.view = e, this.updateGuid = L(); }, initialize() { const e = this.iframe && this.iframe.contentWindow; if (e) { const n = e.document; [ve, we, this.$props.dir === "rtl" ? be : void 0].forEach((o) => { if (o) { const p = n.createElement("style"); p.appendChild(n.createTextNode(o)), n.head.appendChild(p); } }); const r = n.createElement("meta"); r.setAttribute("charset", "utf-8"), n.head.appendChild(r), this.contentElement = n.createElement("div"), n.body.appendChild(this.contentElement), this.contentElement.classList.add("k-content"), this.contentElement.setAttribute("role", "main"); } const i = this.contentElement; if (!i) return; const { preserveWhitespace: s = "full" } = this.$props, a = this, m = function(n) { a.view = n, a.updateGuid = L(); }; let u = [ // https://prosemirror.net/docs/ref/#state.PluginSpec new $({ view: (n) => { const r = { update: m, destroy: () => { const o = a.pluginViews.indexOf(r); o > -1 && a.pluginViews.splice(o, 1); } }; return a.pluginViews.push(r), r; }, key: new H("toolbar-tools-update-plugin") }), new $({ filterTransaction: this.filterTransaction, key: new H("onExecute-event-plugin") }), new $({ key: D, state: { init: () => ({ preserveWhitespace: s }), apply: (n, r) => r } }), ne(), re(), ae(), le(), pe() ], l = { ...y.getShortcuts({ types: { listItem: "list_item", hardBreak: "hard_break" }, toolsSettings: { bold: De, italic: xe, underline: Ie } }), "Mod-k": () => { const { linkDialog: n } = this.$data, r = this.getView(); if (r) { const o = r.state, p = o.selection.empty, d = fe(o, o.schema.marks[W.mark]); !n && !(p && !d) && (this.linkDialog = !0); } return !n; } }; const { defaultContent: f = "", value: w } = this.$props, B = w && typeof w != "string" ? j(w) : y.createDocument(new de({ nodes: ge, marks: ce }), w || f, { preserveWhitespace: s }), g = { state: me.create({ plugins: [...u, _(l), _(ue)], doc: B }), transformPastedHTML: this.onPasteHtml, dispatchTransaction: this.dispatchTransaction, handleDOMEvents: { focus: this.onFocus, blur: this.onBlur, paste: this.onPaste } }, S = { plugins: u, shortcuts: l, target: a, viewProps: g, dom: i }; this.$emit("loaded", S); const E = this.$props.extendView && this.$props.extendView(S) || new he({ mount: i }, g); this.view = P(E); }, filterTransaction(e, i) { const s = { target: this, transaction: e, state: i }; return this.$emit("execute", s), !0; }, onPasteHtml(e) { if (this.$props.pasteHtml && this.pasteEvent) { const i = { target: this, pastedHtml: e, event: this.pasteEvent }, s = this.$props.pasteHtml(i); if (this.pasteEvent = void 0, typeof s == "string") return s; } return e; }, dispatchTransaction(e) { const i = e.docChanged; if (i) { this.trOnChange = e; const s = e.doc, a = e.doc.type.schema, m = this, u = { target: m, value: s, get html() { return m.htmlOnChange = y.getHtml({ doc: s, schema: a }), m.htmlOnChange; }, transaction: e, schema: a }; this.$emit("change", u); } this.getView() && (this.$props.value === void 0 || !i) && this.getView().updateState(this.getView().state.apply(e)); }, onFocus(e, i) { const s = { target: this, event: i }; return this.$emit("focus", s), !1; }, onBlur(e, i) { const s = { target: this, event: i }; return this.$emit("blur", s), !1; }, onPaste(e, i) { return this.$props.pasteHtml && (this.pasteEvent = i), !1; }, handleClose() { this.linkDialog = !1; }, computedValue() { return this.trOnChange !== null ? this.trOnChange.doc : this.$props.value !== void 0 ? this.$props.value : this.getView() ? this.getView().state.doc : this.$props.defaultContent || ""; } } }); export { yt as Editor, C as allTools };