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