-
Notifications
You must be signed in to change notification settings - Fork 0
/
maxi-editor.min.js
34 lines (34 loc) · 7.3 KB
/
maxi-editor.min.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class MaxiEditor {
constructor(e, t) { this.element = e, this.config = t, this.element.style.height = t.height || "200px", this.commands = {}, this.state = {}, this.includeBootstrapIcons(), this.init() } init() { this.element.contentEditable = !0, this.element.classList.add("maxi-editor"), this.element.setAttribute("data-placeholder", this.config.placeholder || "Start typing something here..."), this.checkContent(), this.element.addEventListener("input", () => this.checkContent()), this.createToolPanel(), this.registerCoreCommands(), this.config.plugins && this.applyPlugins(this.config.plugins), this.trackSelection() } includeBootstrapIcons() { let e = "https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css", t = Array.from(document.styleSheets).some(t => t.href === e); if (!t) { let i = document.createElement("link"); i.rel = "stylesheet", i.href = e, document.head.appendChild(i) } } checkContent() { let e = this.element.innerText.trim(); "" === e ? this.element.classList.add("empty") : this.element.classList.remove("empty") } createToolPanel() {
let e = document.createElement("div"); e.classList.add("maxi-toolbar"), this.element.before(e); let t = { undo: '<i class="bi bi-arrow-counterclockwise"></i>', redo: '<i class="bi bi-arrow-clockwise"></i>', bold: '<i class="bi bi-type-bold"></i>', italic: '<i class="bi bi-type-italic"></i>', underline: '<i class="bi bi-type-underline"></i>', highlight: '<i class="bi bi-brush"></i>', strikethrough: '<i class="bi bi-type-strikethrough"></i>', insertLink: '<i class="bi bi-link"></i>', justifyLeft: '<i class="bi bi-text-left"></i>', justifyCenter: '<i class="bi bi-text-center"></i>', justifyRight: '<i class="bi bi-text-right"></i>', insertUnorderedList: '<i class="bi bi-list-task"></i>', insertOrderedList: '<i class="bi bi-list-ol"></i>', indent: '<i class="bi bi-text-indent-left"></i>', outdent: '<i class="bi bi-text-indent-right"></i>' }, i = { undo: "undo", redo: "redo", bold: "Bold (Ctrl+B)", italic: "Italic (Ctrl+I)", underline: "Underline (Ctrl+U)", highlight: "Highlight Text", insertLink: "Insert Link", strikethrough: "Strikethrough", justifyLeft: "Justify Left", justifyCenter: "Justify Center", justifyRight: "Justify Right", insertUnorderedList: "Unordered List", insertOrderedList: "Ordered List", indent: "Indent", outdent: "Outdent" }; this.config.toolbar.forEach(n => {
if ("headingSelector" === n) {
let s = document.createElement("select"); s.innerHTML = `
<option value="p">Normal</option>
<option value="H1">Heading 1</option>
<option value="H2">Heading 2</option>
<option value="H3">Heading 3</option>
<option value="H4">Heading 4</option>
<option value="H5">Heading 5</option>
<option value="H6">Heading 6</option>
`, s.addEventListener("change", e => this.executeCommand("formatBlock", e.target.value)), e.appendChild(s); return
} if ("fontSelector" === n) {
let o = document.createElement("select"); o.innerHTML = `
<option value="Arial">Arial</option>
<option value="Times New Roman">Times New Roman</option>
<option value="Courier New">Courier New</option>
`, o.addEventListener("change", e => this.executeCommand("fontName", e.target.value)), e.appendChild(o); return
} let r = document.createElement("button"); r.type = "button", r.innerHTML = t[n], r.setAttribute("data-command", n), r.setAttribute("title", i[n] || n), r.addEventListener("click", e => { e.preventDefault(), this.executeCommand(n) }), e.appendChild(r)
})
} registerCoreCommands() { this.registerCommand("formatBlock", e => document.execCommand("formatBlock", !1, e)), this.registerCommand("fontName", e => document.execCommand("fontName", !1, e)), this.registerCommand("bold", () => document.execCommand("bold", !1, null)), this.registerCommand("italic", () => document.execCommand("italic", !1, null)), this.registerCommand("underline", () => document.execCommand("underline", !1, null)), this.registerCommand("justifyLeft", () => document.execCommand("justifyLeft", !1, null)), this.registerCommand("justifyCenter", () => document.execCommand("justifyCenter", !1, null)), this.registerCommand("justifyRight", () => document.execCommand("justifyRight", !1, null)), this.registerCommand("insertUnorderedList", () => document.execCommand("insertUnorderedList", !1, null)), this.registerCommand("insertOrderedList", () => document.execCommand("insertOrderedList", !1, null)), this.registerCommand("indent", () => document.execCommand("indent", !1, null)), this.registerCommand("outdent", () => document.execCommand("outdent", !1, null)), this.registerCommand("undo", () => document.execCommand("undo", !1, null)), this.registerCommand("redo", () => document.execCommand("redo", !1, null)) } registerCommand(e, t) { this.commands[e] = t } executeCommand(e, t = null) { let i = this.commands[e]; i ? i(t) : console.error(`Command ${e} is not registered.`) } applyPlugins(e) { e.forEach(e => e.init(this)) } updateToolbarState() { ["bold", "italic", "underline", "strikethrough", "justifyLeft", "justifyCenter", "justifyRight", "insertUnorderedList", "insertOrderedList", "indent"].forEach(e => { let t = document.queryCommandState(e), i = document.querySelector(`button[data-command=${e}]`); i ? t ? i.classList.add("active") : i.classList.remove("active") : console.warn(`Button with command '${e}' not found.`) }) } trackSelection() { document.addEventListener("selectionchange", () => { this.updateToolbarState() }) } getContent() { return this.element.innerHTML } setContent(e) { this.element.innerHTML = e } setHeight(e) { this.element.style.height = e } setWidth(e) { this.element.style.width = e } static set(e, t) { let i = document.querySelector(e); if (!i) throw Error("Editor element not found"); return t.height && (i.style.height = t.height), t.width && (i.style.width = t.width), new MaxiEditor(i, t) }
} class StrikeThroughPlugin { static init(e) { e.registerCommand("strikethrough", () => { document.execCommand("strikeThrough", !1, null) }) } } class InsertLinkPlugin {
static init(e) { e.registerCommand("insertLink", () => { InsertLinkPlugin.showLinkInputModal(e) }) } static showLinkInputModal(e) {
let t = document.createElement("div"); t.classList.add("link-modal"), t.innerHTML = `
<div class="modal-content">
<label for="link-url">Enter URL:</label>
<input type="text" id="link-url" value="https://" />
<button id="insert-link-btn">Insert Link</button>
<button id="cancel-btn">Cancel</button>
</div>
`, document.body.appendChild(t); let i = window.getSelection(), n = i.rangeCount > 0 ? i.getRangeAt(0) : null, s = t.querySelector("#insert-link-btn"), o = t.querySelector("#cancel-btn"); s.addEventListener("click", () => { let e = t.querySelector("#link-url").value; if (e && n) { let s = document.createElement("a"); s.href = e, s.textContent = n.toString(), n.deleteContents(), n.insertNode(s), i.removeAllRanges(); let o = document.createRange(); o.selectNode(s), i.addRange(o) } t.remove() }), o.addEventListener("click", () => { t.remove() })
}
} class RemoveLinkPlugin { static init(e) { e.registerCommand("removeLink", () => { document.execCommand("unlink", !1, null) }) } }