@meenainwal/rich-text-editor 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ReadME.md +30 -46
- package/dist/core/Editor.d.ts +133 -0
- package/dist/core/HistoryManager.d.ts +30 -0
- package/dist/core/ImageManager.d.ts +19 -0
- package/dist/core/SelectionManager.d.ts +31 -0
- package/dist/index.d.ts +11 -0
- package/dist/rich-text-editor.css +1 -1
- package/dist/style.d.ts +4 -0
- package/dist/test-editor.cjs +2 -2
- package/dist/test-editor.mjs +535 -232
- package/dist/ui/Toolbar.d.ts +19 -0
- package/dist/ui/toolbar/EmojiList.d.ts +6 -0
- package/dist/ui/toolbar/EmojiPicker.d.ts +19 -0
- package/dist/ui/toolbar/ToolbarItem.d.ts +16 -0
- package/dist/ui/toolbar/items/AlignCenter.d.ts +2 -0
- package/dist/ui/toolbar/items/AlignJustify.d.ts +2 -0
- package/dist/ui/toolbar/items/AlignLeft.d.ts +2 -0
- package/dist/ui/toolbar/items/AlignRight.d.ts +2 -0
- package/dist/ui/toolbar/items/Bold.d.ts +2 -0
- package/dist/ui/toolbar/items/BulletList.d.ts +2 -0
- package/dist/ui/toolbar/items/ClearFormatting.d.ts +2 -0
- package/dist/ui/toolbar/items/Emoji.d.ts +2 -0
- package/dist/ui/toolbar/items/FontFamily.d.ts +2 -0
- package/dist/ui/toolbar/items/FontSize.d.ts +2 -0
- package/dist/ui/toolbar/items/Heading.d.ts +2 -0
- package/dist/ui/toolbar/items/HighlightColor.d.ts +2 -0
- package/dist/ui/toolbar/items/HorizontalRule.d.ts +2 -0
- package/dist/ui/toolbar/items/Image.d.ts +2 -0
- package/dist/ui/toolbar/items/Indent.d.ts +2 -0
- package/dist/ui/toolbar/items/Italic.d.ts +2 -0
- package/dist/ui/toolbar/items/LineHeight.d.ts +2 -0
- package/dist/ui/toolbar/items/Link.d.ts +2 -0
- package/dist/ui/toolbar/items/OrderedList.d.ts +2 -0
- package/dist/ui/toolbar/items/Outdent.d.ts +2 -0
- package/dist/ui/toolbar/items/Redo.d.ts +2 -0
- package/dist/ui/toolbar/items/Strikethrough.d.ts +2 -0
- package/dist/ui/toolbar/items/Table.d.ts +2 -0
- package/dist/ui/toolbar/items/TableActions.d.ts +5 -0
- package/dist/ui/toolbar/items/TextColor.d.ts +2 -0
- package/dist/ui/toolbar/items/Underline.d.ts +2 -0
- package/dist/ui/toolbar/items/Undo.d.ts +2 -0
- package/dist/ui/toolbar/registry.d.ts +2 -0
- package/package.json +15 -10
- package/images/Screenshot from 2026-03-07 12-13-13.png +0 -0
- package/images/editor-preview.png +0 -0
package/dist/test-editor.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class
|
|
1
|
+
class h {
|
|
2
2
|
/**
|
|
3
3
|
* Returns the current selection object.
|
|
4
4
|
*/
|
|
@@ -42,7 +42,7 @@ class y {
|
|
|
42
42
|
e && e.removeAllRanges();
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
class
|
|
45
|
+
class y {
|
|
46
46
|
editor;
|
|
47
47
|
activeContainer = null;
|
|
48
48
|
isResizing = !1;
|
|
@@ -58,15 +58,15 @@ class h {
|
|
|
58
58
|
setupListeners() {
|
|
59
59
|
const e = this.editor.el;
|
|
60
60
|
e.addEventListener("mousedown", (t) => {
|
|
61
|
-
const
|
|
62
|
-
if (
|
|
61
|
+
const i = t.target;
|
|
62
|
+
if (i.classList.contains("te-image-resizer")) {
|
|
63
63
|
t.preventDefault(), t.stopPropagation();
|
|
64
|
-
const
|
|
65
|
-
|
|
64
|
+
const n = i.closest(".te-image-container");
|
|
65
|
+
n && (this.selectImage(n), this.startResize(t, i));
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
|
-
const
|
|
69
|
-
|
|
68
|
+
const o = i.closest(".te-image-container");
|
|
69
|
+
o ? this.selectImage(o) : this.deselectImage();
|
|
70
70
|
}), window.addEventListener("mousemove", (t) => {
|
|
71
71
|
this.isResizing && this.handleResize(t);
|
|
72
72
|
}), window.addEventListener("mouseup", () => {
|
|
@@ -83,37 +83,118 @@ class h {
|
|
|
83
83
|
}
|
|
84
84
|
startResize(e, t) {
|
|
85
85
|
if (!this.activeContainer) return;
|
|
86
|
-
this.isResizing = !0, this.currentHandle = Array.from(t.classList).find((
|
|
87
|
-
const
|
|
88
|
-
this.startX = e.clientX, this.startY = e.clientY, this.startWidth =
|
|
86
|
+
this.isResizing = !0, this.currentHandle = Array.from(t.classList).find((o) => o.startsWith("te-resizer-"))?.replace("te-resizer-", "") || null;
|
|
87
|
+
const i = this.activeContainer.querySelector("img");
|
|
88
|
+
this.startX = e.clientX, this.startY = e.clientY, this.startWidth = i.clientWidth, this.startHeight = i.clientHeight, this.aspectRatio = this.startWidth / this.startHeight, document.body.style.cursor = window.getComputedStyle(t).cursor;
|
|
89
89
|
}
|
|
90
90
|
handleResize(e) {
|
|
91
91
|
if (!this.activeContainer || !this.isResizing) return;
|
|
92
|
-
const t = this.activeContainer.querySelector("img"),
|
|
93
|
-
let
|
|
94
|
-
this.currentHandle?.includes("right") ?
|
|
92
|
+
const t = this.activeContainer.querySelector("img"), i = e.clientX - this.startX, o = e.clientY - this.startY;
|
|
93
|
+
let n = this.startWidth, a = this.startHeight;
|
|
94
|
+
this.currentHandle?.includes("right") ? n = this.startWidth + i : this.currentHandle?.includes("left") ? n = this.startWidth - i : this.currentHandle?.includes("bottom") ? n = this.startWidth + o * this.aspectRatio : this.currentHandle?.includes("top") && (n = this.startWidth - o * this.aspectRatio), a = n / this.aspectRatio, n > 50 && n < this.editor.el.clientWidth && (t.style.width = `${n}px`, t.style.height = `${a}px`);
|
|
95
95
|
}
|
|
96
96
|
stopResize() {
|
|
97
97
|
this.isResizing = !1, this.currentHandle = null, document.body.style.cursor = "", this.editor.el.dispatchEvent(new Event("input", { bubbles: !0 }));
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
class u {
|
|
101
|
+
stack = [];
|
|
102
|
+
index = -1;
|
|
103
|
+
maxDepth = 50;
|
|
104
|
+
constructor(e) {
|
|
105
|
+
e !== void 0 && this.record(e);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Records a new state in the history stack.
|
|
109
|
+
* Clears any "redo" states if we record a new action.
|
|
110
|
+
*/
|
|
111
|
+
record(e) {
|
|
112
|
+
this.index >= 0 && this.stack[this.index].html === e || (this.index < this.stack.length - 1 && (this.stack = this.stack.slice(0, this.index + 1)), this.stack.push({ html: e }), this.index++, this.stack.length > this.maxDepth && (this.stack.shift(), this.index--));
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Returns the previous state if available.
|
|
116
|
+
*/
|
|
117
|
+
undo() {
|
|
118
|
+
return this.index > 0 ? (this.index--, this.stack[this.index].html) : null;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Returns the next state if available.
|
|
122
|
+
*/
|
|
123
|
+
redo() {
|
|
124
|
+
return this.index < this.stack.length - 1 ? (this.index++, this.stack[this.index].html) : null;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Checks if undo is possible.
|
|
128
|
+
*/
|
|
129
|
+
canUndo() {
|
|
130
|
+
return this.index > 0;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Checks if redo is possible.
|
|
134
|
+
*/
|
|
135
|
+
canRedo() {
|
|
136
|
+
return this.index < this.stack.length - 1;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
class p {
|
|
101
140
|
container;
|
|
102
141
|
editableElement;
|
|
103
142
|
selection;
|
|
104
143
|
imageManager;
|
|
144
|
+
history;
|
|
105
145
|
options;
|
|
146
|
+
saveTimeout = null;
|
|
147
|
+
historyTimeout = null;
|
|
106
148
|
pendingStyles = {};
|
|
107
149
|
constructor(e, t = {}) {
|
|
108
|
-
|
|
150
|
+
if (this.options = t, this.container = e, typeof document > "u" || !e) {
|
|
151
|
+
this.editableElement = {}, this.selection = {}, this.imageManager = {}, this.history = {};
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
this.container.classList.add("te-container"), this.options.dark && this.container.classList.add("te-dark"), this.editableElement = this.createEditableElement(), this.selection = new h(), this.imageManager = new y(this), this.history = new u(this.editableElement.innerHTML), this.setupInputHandlers(), this.setupImageObserver(), this.container.appendChild(this.editableElement), this.options.autofocus && this.focus(), this.options.theme && this.applyTheme(this.options.theme), document.execCommand("defaultParagraphSeparator", !1, "p");
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Applies custom theme variables to the editor container.
|
|
158
|
+
*/
|
|
159
|
+
applyTheme(e) {
|
|
160
|
+
const t = this.container, i = {
|
|
161
|
+
primaryColor: "--te-primary-color",
|
|
162
|
+
primaryHover: "--te-primary-hover",
|
|
163
|
+
bgApp: "--te-bg-app",
|
|
164
|
+
bgEditor: "--te-bg-editor",
|
|
165
|
+
toolbarBg: "--te-toolbar-bg",
|
|
166
|
+
borderColor: "--te-border-color",
|
|
167
|
+
borderFocus: "--te-border-focus",
|
|
168
|
+
textMain: "--te-text-main",
|
|
169
|
+
textMuted: "--te-text-muted",
|
|
170
|
+
placeholder: "--te-placeholder",
|
|
171
|
+
btnHover: "--te-btn-hover",
|
|
172
|
+
btnActive: "--te-btn-active",
|
|
173
|
+
radiusLg: "--te-radius-lg",
|
|
174
|
+
radiusMd: "--te-radius-md",
|
|
175
|
+
radiusSm: "--te-radius-sm",
|
|
176
|
+
shadowSm: "--te-shadow-sm",
|
|
177
|
+
shadowMd: "--te-shadow-md",
|
|
178
|
+
shadowLg: "--te-shadow-lg"
|
|
179
|
+
};
|
|
180
|
+
for (const [o, n] of Object.entries(i)) {
|
|
181
|
+
const a = e[o];
|
|
182
|
+
a && t.style.setProperty(n, a);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Toggles dark mode on the editor.
|
|
187
|
+
*/
|
|
188
|
+
setDarkMode(e) {
|
|
189
|
+
this.options.dark = e, e ? this.container.classList.add("te-dark") : this.container.classList.remove("te-dark");
|
|
109
190
|
}
|
|
110
191
|
setupImageObserver() {
|
|
111
192
|
new MutationObserver((t) => {
|
|
112
|
-
t.forEach((
|
|
113
|
-
|
|
114
|
-
if (
|
|
115
|
-
const
|
|
116
|
-
|
|
193
|
+
t.forEach((i) => {
|
|
194
|
+
i.addedNodes.forEach((o) => {
|
|
195
|
+
if (o.nodeType === Node.ELEMENT_NODE) {
|
|
196
|
+
const n = o;
|
|
197
|
+
n.tagName === "IMG" && !n.closest(".te-image-container") ? this.wrapImage(n) : n.querySelectorAll("img:not(.te-image)").forEach((s) => {
|
|
117
198
|
s.closest(".te-image-container") || this.wrapImage(s);
|
|
118
199
|
});
|
|
119
200
|
}
|
|
@@ -130,17 +211,17 @@ class u {
|
|
|
130
211
|
wrapImage(e) {
|
|
131
212
|
const t = e.parentElement;
|
|
132
213
|
if (!t) return;
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
if (
|
|
214
|
+
const i = document.createElement("figure");
|
|
215
|
+
i.classList.add("te-image-container"), i.setAttribute("contenteditable", "false");
|
|
216
|
+
const o = document.createElement("img");
|
|
217
|
+
o.src = e.src, o.alt = e.alt || "", e.width && (o.style.width = `${e.width}px`), e.height && (o.style.height = `${e.height}px`), o.classList.add("te-image");
|
|
218
|
+
const n = document.createElement("figcaption");
|
|
219
|
+
if (n.classList.add("te-image-caption"), n.setAttribute("contenteditable", "true"), n.setAttribute("data-placeholder", "Type caption..."), ["top-left", "top-right", "bottom-left", "bottom-right"].forEach((s) => {
|
|
139
220
|
const r = document.createElement("div");
|
|
140
|
-
r.classList.add("te-image-resizer", `te-resizer-${s}`),
|
|
141
|
-
}),
|
|
221
|
+
r.classList.add("te-image-resizer", `te-resizer-${s}`), i.appendChild(r);
|
|
222
|
+
}), i.appendChild(o), i.appendChild(n), t.replaceChild(i, e), !i.nextElementSibling) {
|
|
142
223
|
const s = document.createElement("p");
|
|
143
|
-
s.innerHTML = "<br>",
|
|
224
|
+
s.innerHTML = "<br>", i.after(s);
|
|
144
225
|
}
|
|
145
226
|
}
|
|
146
227
|
setupInputHandlers() {
|
|
@@ -149,15 +230,15 @@ class u {
|
|
|
149
230
|
const t = e.data;
|
|
150
231
|
if (!t) return;
|
|
151
232
|
e.preventDefault();
|
|
152
|
-
const
|
|
153
|
-
for (const [
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const
|
|
157
|
-
if (
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
233
|
+
const i = document.createElement("span");
|
|
234
|
+
for (const [n, a] of Object.entries(this.pendingStyles))
|
|
235
|
+
i.style.setProperty(n, a);
|
|
236
|
+
i.textContent = t;
|
|
237
|
+
const o = this.selection.getRange();
|
|
238
|
+
if (o) {
|
|
239
|
+
o.deleteContents(), o.insertNode(i);
|
|
240
|
+
const n = document.createRange();
|
|
241
|
+
n.setStart(i.firstChild, t.length), n.setEnd(i.firstChild, t.length), this.selection.restoreSelection(n), this.pendingStyles = {}, this.editableElement.dispatchEvent(new Event("input", { bubbles: !0 }));
|
|
161
242
|
}
|
|
162
243
|
}
|
|
163
244
|
}), document.addEventListener("selectionchange", () => {
|
|
@@ -171,24 +252,24 @@ class u {
|
|
|
171
252
|
e.preventDefault(), this.editableElement.classList.remove("dragover");
|
|
172
253
|
const t = e.dataTransfer?.files;
|
|
173
254
|
if (t && t.length > 0)
|
|
174
|
-
for (let
|
|
175
|
-
const
|
|
176
|
-
if (
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
const s =
|
|
255
|
+
for (let i = 0; i < t.length; i++) {
|
|
256
|
+
const o = t[i];
|
|
257
|
+
if (o.type.startsWith("image/")) {
|
|
258
|
+
const n = new FileReader();
|
|
259
|
+
n.onload = (a) => {
|
|
260
|
+
const s = a.target?.result;
|
|
180
261
|
this.insertImage(s);
|
|
181
|
-
},
|
|
262
|
+
}, n.readAsDataURL(o);
|
|
182
263
|
}
|
|
183
264
|
}
|
|
184
265
|
}), this.editableElement.addEventListener("paste", (e) => {
|
|
185
266
|
const t = e.clipboardData;
|
|
186
267
|
if (!t) return;
|
|
187
268
|
if (t.items)
|
|
188
|
-
for (let
|
|
189
|
-
const
|
|
190
|
-
if (
|
|
191
|
-
const s =
|
|
269
|
+
for (let n = 0; n < t.items.length; n++) {
|
|
270
|
+
const a = t.items[n];
|
|
271
|
+
if (a.type.startsWith("image/")) {
|
|
272
|
+
const s = a.getAsFile();
|
|
192
273
|
if (s) {
|
|
193
274
|
e.preventDefault();
|
|
194
275
|
const r = new FileReader();
|
|
@@ -200,14 +281,47 @@ class u {
|
|
|
200
281
|
}
|
|
201
282
|
}
|
|
202
283
|
}
|
|
203
|
-
const
|
|
204
|
-
if (
|
|
205
|
-
const
|
|
206
|
-
e.preventDefault(), this.execute("insertHTML",
|
|
284
|
+
const i = t.getData("text/plain"), o = /<([a-z1-6]+)\b[^>]*>[\s\S]*<\/\1>/i.test(i) || /^\s*<[a-z1-6]+\b[^>]*>/i.test(i);
|
|
285
|
+
if (i && o) {
|
|
286
|
+
const n = i.replace(/(\r\n|\n|\r)/gm, " ").replace(/>\s+</g, "><").trim();
|
|
287
|
+
e.preventDefault(), this.execute("insertHTML", n);
|
|
207
288
|
return;
|
|
208
289
|
}
|
|
290
|
+
}), this.editableElement.addEventListener("input", () => {
|
|
291
|
+
this.handleInput();
|
|
292
|
+
}), this.editableElement.addEventListener("keydown", (e) => {
|
|
293
|
+
(e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "z" ? (e.preventDefault(), e.shiftKey ? this.redo() : this.undo()) : (e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "y" && (e.preventDefault(), this.redo());
|
|
209
294
|
});
|
|
210
295
|
}
|
|
296
|
+
handleInput() {
|
|
297
|
+
this.options.onSaving && this.options.onSaving(), this.scheduleHistoryRecord(), this.scheduleAutoSave(), this.options.onChange && this.options.onChange(this.editableElement.innerHTML);
|
|
298
|
+
}
|
|
299
|
+
scheduleHistoryRecord() {
|
|
300
|
+
this.historyTimeout && clearTimeout(this.historyTimeout), this.historyTimeout = setTimeout(() => {
|
|
301
|
+
this.history.record(this.editableElement.innerHTML);
|
|
302
|
+
}, 500);
|
|
303
|
+
}
|
|
304
|
+
scheduleAutoSave() {
|
|
305
|
+
this.saveTimeout && clearTimeout(this.saveTimeout);
|
|
306
|
+
const e = this.options.autoSaveInterval || 1e3;
|
|
307
|
+
this.saveTimeout = setTimeout(() => {
|
|
308
|
+
this.save();
|
|
309
|
+
}, e);
|
|
310
|
+
}
|
|
311
|
+
save() {
|
|
312
|
+
this.options.onSave && this.options.onSave(this.editableElement.innerHTML);
|
|
313
|
+
}
|
|
314
|
+
undo() {
|
|
315
|
+
const e = this.history.undo();
|
|
316
|
+
e !== null && (this.editableElement.innerHTML = e, this.triggerChange());
|
|
317
|
+
}
|
|
318
|
+
redo() {
|
|
319
|
+
const e = this.history.redo();
|
|
320
|
+
e !== null && (this.editableElement.innerHTML = e, this.triggerChange());
|
|
321
|
+
}
|
|
322
|
+
triggerChange() {
|
|
323
|
+
this.editableElement.dispatchEvent(new Event("input", { bubbles: !0 }));
|
|
324
|
+
}
|
|
211
325
|
createEditableElement() {
|
|
212
326
|
const e = document.createElement("div");
|
|
213
327
|
return e.setAttribute("contenteditable", "true"), e.setAttribute("role", "textbox"), e.setAttribute("spellcheck", "true"), e.classList.add("te-content"), this.options.placeholder && e.setAttribute("data-placeholder", this.options.placeholder), e.style.minHeight = "150px", e.style.outline = "none", e.style.padding = "1rem", e.innerHTML === "" && (e.innerHTML = "<p><br></p>"), e;
|
|
@@ -224,48 +338,141 @@ class u {
|
|
|
224
338
|
execute(e, t = null) {
|
|
225
339
|
this.focus(), document.execCommand(e, !1, t ?? void 0), this.editableElement.dispatchEvent(new Event("input", { bubbles: !0 }));
|
|
226
340
|
}
|
|
341
|
+
/**
|
|
342
|
+
* Inserts a table at the current selection.
|
|
343
|
+
*/
|
|
344
|
+
insertTable(e = 3, t = 3) {
|
|
345
|
+
this.focus();
|
|
346
|
+
const i = this.selection.getRange();
|
|
347
|
+
if (!i) return;
|
|
348
|
+
const o = document.createElement("table");
|
|
349
|
+
o.classList.add("te-table");
|
|
350
|
+
for (let a = 0; a < e; a++) {
|
|
351
|
+
const s = document.createElement("tr");
|
|
352
|
+
for (let r = 0; r < t; r++) {
|
|
353
|
+
const l = document.createElement("td");
|
|
354
|
+
l.innerHTML = "<br>", s.appendChild(l);
|
|
355
|
+
}
|
|
356
|
+
o.appendChild(s);
|
|
357
|
+
}
|
|
358
|
+
i.deleteContents(), i.insertNode(o);
|
|
359
|
+
const n = o.nextElementSibling;
|
|
360
|
+
if (!n || n.tagName !== "P") {
|
|
361
|
+
const a = document.createElement("p");
|
|
362
|
+
a.innerHTML = "<br>", o.after(a), n && n.tagName === "BR" && n.remove();
|
|
363
|
+
}
|
|
364
|
+
this.editableElement.dispatchEvent(new Event("input", { bubbles: !0 }));
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Adds a row to the currently selected table.
|
|
368
|
+
*/
|
|
369
|
+
addRow() {
|
|
370
|
+
const e = this.getSelectedTable();
|
|
371
|
+
if (!e) return;
|
|
372
|
+
const t = document.createElement("tr");
|
|
373
|
+
t.style.borderBottom = "1px solid var(--te-border-color)";
|
|
374
|
+
const i = e.rows[0].cells.length;
|
|
375
|
+
for (let n = 0; n < i; n++) {
|
|
376
|
+
const a = document.createElement("td");
|
|
377
|
+
a.innerHTML = "<br>", t.appendChild(a);
|
|
378
|
+
}
|
|
379
|
+
const o = this.getSelectedTd();
|
|
380
|
+
o ? o.parentElement?.after(t) : e.appendChild(t), this.editableElement.dispatchEvent(new Event("input", { bubbles: !0 }));
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Deletes the currently selected row.
|
|
384
|
+
*/
|
|
385
|
+
deleteRow() {
|
|
386
|
+
const e = this.getSelectedTd();
|
|
387
|
+
if (e && e.parentElement) {
|
|
388
|
+
const t = e.parentElement;
|
|
389
|
+
t.parentElement.rows.length > 1 && (t.remove(), this.editableElement.dispatchEvent(new Event("input", { bubbles: !0 })));
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Adds a column to the currently selected table.
|
|
394
|
+
*/
|
|
395
|
+
addColumn() {
|
|
396
|
+
const e = this.getSelectedTable();
|
|
397
|
+
if (!e) return;
|
|
398
|
+
const t = this.getSelectedTd(), i = t ? t.cellIndex : -1;
|
|
399
|
+
for (let o = 0; o < e.rows.length; o++) {
|
|
400
|
+
const n = e.rows[o], a = document.createElement("td");
|
|
401
|
+
a.innerHTML = "<br>", i !== -1 ? n.cells[i].after(a) : n.appendChild(a);
|
|
402
|
+
}
|
|
403
|
+
this.editableElement.dispatchEvent(new Event("input", { bubbles: !0 }));
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Deletes the currently selected column.
|
|
407
|
+
*/
|
|
408
|
+
deleteColumn() {
|
|
409
|
+
const e = this.getSelectedTd();
|
|
410
|
+
if (!e) return;
|
|
411
|
+
const t = this.getSelectedTable();
|
|
412
|
+
if (!t) return;
|
|
413
|
+
const i = e.cellIndex;
|
|
414
|
+
if (t.rows[0].cells.length > 1) {
|
|
415
|
+
for (let o = 0; o < t.rows.length; o++)
|
|
416
|
+
t.rows[o].cells[i].remove();
|
|
417
|
+
this.editableElement.dispatchEvent(new Event("input", { bubbles: !0 }));
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
getSelectedTd() {
|
|
421
|
+
const e = window.getSelection();
|
|
422
|
+
if (!e || e.rangeCount === 0) return null;
|
|
423
|
+
let t = e.anchorNode;
|
|
424
|
+
for (; t && t !== this.editableElement; ) {
|
|
425
|
+
if (t.nodeName === "TD") return t;
|
|
426
|
+
t = t.parentNode;
|
|
427
|
+
}
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
getSelectedTable() {
|
|
431
|
+
const e = this.getSelectedTd();
|
|
432
|
+
return e ? e.closest("table") : null;
|
|
433
|
+
}
|
|
227
434
|
/**
|
|
228
435
|
* Recursively removes a style property from all elements in a fragment.
|
|
229
436
|
*/
|
|
230
437
|
clearStyleRecursive(e, t) {
|
|
231
|
-
const
|
|
232
|
-
let
|
|
233
|
-
for (;
|
|
234
|
-
|
|
438
|
+
const i = document.createTreeWalker(e, NodeFilter.SHOW_ELEMENT);
|
|
439
|
+
let o = i.nextNode();
|
|
440
|
+
for (; o; )
|
|
441
|
+
o.style.getPropertyValue(t) && o.style.removeProperty(t), o = i.nextNode();
|
|
235
442
|
}
|
|
236
443
|
/**
|
|
237
444
|
* Applies an inline style to the selection.
|
|
238
445
|
* This is used for properties like font-size (px) and font-family
|
|
239
446
|
* where execCommand is outdated or limited.
|
|
240
447
|
*/
|
|
241
|
-
setStyle(e, t,
|
|
242
|
-
if (e === "font-size" && t.endsWith("px"), !
|
|
243
|
-
const
|
|
244
|
-
if (!
|
|
245
|
-
|
|
448
|
+
setStyle(e, t, i) {
|
|
449
|
+
if (e === "font-size" && t.endsWith("px"), !i) {
|
|
450
|
+
const a = window.getSelection();
|
|
451
|
+
if (!a || a.rangeCount === 0) return null;
|
|
452
|
+
i = a.getRangeAt(0);
|
|
246
453
|
}
|
|
247
|
-
if (
|
|
248
|
-
return this.pendingStyles[e] = t,
|
|
249
|
-
let
|
|
250
|
-
|
|
251
|
-
let
|
|
252
|
-
if (
|
|
253
|
-
|
|
454
|
+
if (i.collapsed)
|
|
455
|
+
return this.pendingStyles[e] = t, i;
|
|
456
|
+
let o = i.commonAncestorContainer;
|
|
457
|
+
o.nodeType === Node.TEXT_NODE && (o = o.parentElement);
|
|
458
|
+
let n = null;
|
|
459
|
+
if (o.tagName === "SPAN" && o.children.length === 0 && o.textContent === i.toString())
|
|
460
|
+
o.style.setProperty(e, t), n = i.cloneRange();
|
|
254
461
|
else {
|
|
255
|
-
const
|
|
256
|
-
|
|
462
|
+
const a = document.createElement("span");
|
|
463
|
+
a.style.setProperty(e, t);
|
|
257
464
|
try {
|
|
258
|
-
const s =
|
|
259
|
-
this.clearStyleRecursive(r, e),
|
|
465
|
+
const s = o.tagName === "SPAN" ? o : null, r = i.extractContents();
|
|
466
|
+
this.clearStyleRecursive(r, e), a.appendChild(r), i.insertNode(a), s && s.innerHTML === "" && s.remove();
|
|
260
467
|
const l = document.createRange();
|
|
261
|
-
l.selectNodeContents(
|
|
468
|
+
l.selectNodeContents(a), n = l;
|
|
262
469
|
const c = window.getSelection();
|
|
263
470
|
c && c.rangeCount > 0 && (c.removeAllRanges(), c.addRange(l));
|
|
264
471
|
} catch (s) {
|
|
265
472
|
console.warn("Failed to apply style:", s);
|
|
266
473
|
}
|
|
267
474
|
}
|
|
268
|
-
return this.editableElement.dispatchEvent(new Event("input", { bubbles: !0 })),
|
|
475
|
+
return this.editableElement.dispatchEvent(new Event("input", { bubbles: !0 })), n;
|
|
269
476
|
}
|
|
270
477
|
/**
|
|
271
478
|
* Creates a link at the current selection.
|
|
@@ -275,10 +482,10 @@ class u {
|
|
|
275
482
|
this.focus(), !/^https?:\/\//i.test(e) && !/^mailto:/i.test(e) && !e.startsWith("#") && (e = "https://" + e), document.execCommand("createLink", !1, e);
|
|
276
483
|
const t = window.getSelection();
|
|
277
484
|
if (t && t.rangeCount > 0) {
|
|
278
|
-
let
|
|
279
|
-
|
|
280
|
-
let
|
|
281
|
-
|
|
485
|
+
let o = t.getRangeAt(0).commonAncestorContainer;
|
|
486
|
+
o.nodeType === Node.TEXT_NODE && (o = o.parentElement);
|
|
487
|
+
let n = null;
|
|
488
|
+
o.tagName === "A" ? n = o : n = o.querySelector("a"), n && (n.setAttribute("target", "_blank"), n.setAttribute("rel", "noopener noreferrer"));
|
|
282
489
|
}
|
|
283
490
|
this.editableElement.dispatchEvent(new Event("input", { bubbles: !0 }));
|
|
284
491
|
}
|
|
@@ -289,17 +496,17 @@ class u {
|
|
|
289
496
|
this.focus();
|
|
290
497
|
const t = this.selection.getRange();
|
|
291
498
|
if (!t) return;
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
const
|
|
297
|
-
|
|
499
|
+
const i = document.createElement("figure");
|
|
500
|
+
i.classList.add("te-image-container"), i.setAttribute("contenteditable", "false");
|
|
501
|
+
const o = document.createElement("img");
|
|
502
|
+
o.src = e, o.classList.add("te-image");
|
|
503
|
+
const n = document.createElement("figcaption");
|
|
504
|
+
n.classList.add("te-image-caption"), n.setAttribute("contenteditable", "true"), n.setAttribute("data-placeholder", "Type caption..."), ["top-left", "top-right", "bottom-left", "bottom-right"].forEach((l) => {
|
|
298
505
|
const c = document.createElement("div");
|
|
299
|
-
c.classList.add("te-image-resizer", `te-resizer-${l}`),
|
|
300
|
-
}),
|
|
506
|
+
c.classList.add("te-image-resizer", `te-resizer-${l}`), i.appendChild(c);
|
|
507
|
+
}), i.appendChild(o), i.appendChild(n), t.deleteContents(), t.insertNode(i);
|
|
301
508
|
const s = document.createElement("p");
|
|
302
|
-
s.innerHTML = "<br>",
|
|
509
|
+
s.innerHTML = "<br>", i.after(s);
|
|
303
510
|
const r = document.createRange();
|
|
304
511
|
r.setStart(s, 0), r.setEnd(s, 0), this.selection.restoreSelection(r), this.editableElement.dispatchEvent(new Event("input", { bubbles: !0 }));
|
|
305
512
|
}
|
|
@@ -321,18 +528,24 @@ class u {
|
|
|
321
528
|
get el() {
|
|
322
529
|
return this.editableElement;
|
|
323
530
|
}
|
|
531
|
+
/**
|
|
532
|
+
* Returns the editor options.
|
|
533
|
+
*/
|
|
534
|
+
getOptions() {
|
|
535
|
+
return this.options;
|
|
536
|
+
}
|
|
324
537
|
}
|
|
325
|
-
const
|
|
538
|
+
const v = {
|
|
326
539
|
type: "button",
|
|
327
540
|
title: "Undo",
|
|
328
541
|
command: "undo",
|
|
329
542
|
icon: '<svg viewBox="0 0 24 24"><path d="M9 14L4 9l5-5"></path><path d="M20 20v-7a4 4 0 0 0-4-4H4"></path></svg>'
|
|
330
|
-
},
|
|
543
|
+
}, b = {
|
|
331
544
|
type: "button",
|
|
332
545
|
title: "Redo",
|
|
333
546
|
command: "redo",
|
|
334
547
|
icon: '<svg viewBox="0 0 24 24"><path d="M15 14l5-5-5-5"></path><path d="M4 20v-7a4 4 0 0 1 4-4h12"></path></svg>'
|
|
335
|
-
},
|
|
548
|
+
}, j = {
|
|
336
549
|
type: "select",
|
|
337
550
|
title: "Heading",
|
|
338
551
|
command: "formatBlock",
|
|
@@ -345,7 +558,7 @@ const p = {
|
|
|
345
558
|
{ label: "Heading 5", value: "H5" },
|
|
346
559
|
{ label: "Heading 6", value: "H6" }
|
|
347
560
|
]
|
|
348
|
-
},
|
|
561
|
+
}, f = {
|
|
349
562
|
type: "select",
|
|
350
563
|
title: "Font",
|
|
351
564
|
command: "fontFamily",
|
|
@@ -366,13 +579,13 @@ const p = {
|
|
|
366
579
|
{ label: "Playfair Display", value: "'Playfair Display', serif" },
|
|
367
580
|
{ label: "Merriweather", value: "'Merriweather', serif" }
|
|
368
581
|
]
|
|
369
|
-
},
|
|
582
|
+
}, w = {
|
|
370
583
|
type: "input",
|
|
371
584
|
title: "Size (px)",
|
|
372
585
|
command: "fontSize",
|
|
373
586
|
placeholder: "Size",
|
|
374
587
|
value: "16"
|
|
375
|
-
},
|
|
588
|
+
}, E = {
|
|
376
589
|
type: "select",
|
|
377
590
|
title: "Line Height",
|
|
378
591
|
command: "lineHeight",
|
|
@@ -411,84 +624,84 @@ const p = {
|
|
|
411
624
|
title: "Italic",
|
|
412
625
|
command: "italic",
|
|
413
626
|
icon: '<svg viewBox="0 0 24 24"><line x1="19" y1="4" x2="10" y2="4"></line><line x1="14" y1="20" x2="5" y2="20"></line><line x1="15" y1="4" x2="9" y2="20"></line></svg>'
|
|
414
|
-
},
|
|
627
|
+
}, k = {
|
|
415
628
|
type: "button",
|
|
416
629
|
title: "Underline",
|
|
417
630
|
command: "underline",
|
|
418
631
|
icon: '<svg viewBox="0 0 24 24"><path d="M6 3v7a6 6 0 0 0 6 6 6 6 0 0 0 6-6V3"></path><line x1="4" y1="21" x2="20" y2="21"></line></svg>'
|
|
419
|
-
},
|
|
632
|
+
}, L = {
|
|
420
633
|
type: "button",
|
|
421
634
|
title: "Strikethrough",
|
|
422
635
|
command: "strikeThrough",
|
|
423
636
|
icon: '<svg viewBox="0 0 24 24"><line x1="5" y1="12" x2="19" y2="12"></line><path d="M16 4.9C15.1 4.3 13.9 4 12.6 4c-3.1 0-5.6 1.8-5.6 4s1.8 3.3 4.4 3.8"></path><path d="M7 19.1c.9.6 2.1.9 3.4.9 3.1 0 5.6-1.8 5.6-4s-1.8-3.3-4.4-3.8"></path></svg>'
|
|
424
|
-
},
|
|
637
|
+
}, A = {
|
|
425
638
|
type: "color-picker",
|
|
426
639
|
title: "Text Color",
|
|
427
640
|
command: "foreColor",
|
|
428
641
|
icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 20h16"/><path d="m6 16 6-12 6 12"/><path d="M8 12h8"/></svg>',
|
|
429
642
|
value: "#1e293b"
|
|
430
|
-
},
|
|
643
|
+
}, C = {
|
|
431
644
|
type: "color-picker",
|
|
432
645
|
title: "Highlight Color",
|
|
433
646
|
command: "backColor",
|
|
434
647
|
icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 11-6 6v3h9l3-3"/><path d="m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4"/></svg>',
|
|
435
648
|
value: "#ffffff"
|
|
436
|
-
},
|
|
649
|
+
}, T = {
|
|
437
650
|
type: "button",
|
|
438
651
|
title: "Align Left",
|
|
439
652
|
command: "justifyLeft",
|
|
440
653
|
icon: '<svg viewBox="0 0 24 24"><line x1="17" y1="10" x2="3" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="17" y1="18" x2="3" y2="18"></line></svg>'
|
|
441
|
-
},
|
|
654
|
+
}, H = {
|
|
442
655
|
type: "button",
|
|
443
656
|
title: "Align Center",
|
|
444
657
|
command: "justifyCenter",
|
|
445
658
|
icon: '<svg viewBox="0 0 24 24"><line x1="18" y1="10" x2="6" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="18" y1="18" x2="6" y2="18"></line></svg>'
|
|
446
|
-
},
|
|
659
|
+
}, F = {
|
|
447
660
|
type: "button",
|
|
448
661
|
title: "Align Right",
|
|
449
662
|
command: "justifyRight",
|
|
450
663
|
icon: '<svg viewBox="0 0 24 24"><line x1="21" y1="10" x2="7" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="21" y1="18" x2="7" y2="18"></line></svg>'
|
|
451
|
-
},
|
|
664
|
+
}, R = {
|
|
452
665
|
type: "button",
|
|
453
666
|
title: "Justify",
|
|
454
667
|
command: "justifyFull",
|
|
455
668
|
icon: '<svg viewBox="0 0 24 24"><line x1="21" y1="10" x2="3" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="21" y1="18" x2="3" y2="18"></line></svg>'
|
|
456
|
-
},
|
|
669
|
+
}, M = {
|
|
457
670
|
type: "button",
|
|
458
671
|
title: "Bulleted List",
|
|
459
672
|
command: "insertUnorderedList",
|
|
460
673
|
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="8" y1="6" x2="21" y2="6"></line><line x1="8" y1="12" x2="21" y2="12"></line><line x1="8" y1="18" x2="21" y2="18"></line><circle cx="3" cy="6" r="1" fill="currentColor"></circle><circle cx="3" cy="12" r="1" fill="currentColor"></circle><circle cx="3" cy="18" r="1" fill="currentColor"></circle></svg>'
|
|
461
|
-
},
|
|
674
|
+
}, I = {
|
|
462
675
|
type: "button",
|
|
463
676
|
title: "Numbered List",
|
|
464
677
|
command: "insertOrderedList",
|
|
465
678
|
icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="10" y1="6" x2="21" y2="6"></line><line x1="10" y1="12" x2="21" y2="12"></line><line x1="10" y1="18" x2="21" y2="18"></line><path d="M4 6h1v4"></path><path d="M4 10h2"></path><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"></path></svg>'
|
|
466
|
-
},
|
|
679
|
+
}, O = {
|
|
467
680
|
type: "button",
|
|
468
681
|
title: "Outdent",
|
|
469
682
|
command: "outdent",
|
|
470
683
|
icon: '<svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"></polyline><line x1="21" y1="12" x2="9" y2="12"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="18" x2="3" y2="18"></line></svg>'
|
|
471
|
-
},
|
|
684
|
+
}, z = {
|
|
472
685
|
type: "button",
|
|
473
686
|
title: "Indent",
|
|
474
687
|
command: "indent",
|
|
475
688
|
icon: '<svg viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"></polyline><line x1="3" y1="12" x2="15" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>'
|
|
476
|
-
},
|
|
689
|
+
}, B = {
|
|
477
690
|
type: "button",
|
|
478
691
|
title: "Horizontal Rule",
|
|
479
692
|
command: "insertHorizontalRule",
|
|
480
693
|
icon: '<svg viewBox="0 0 24 24"><line x1="5" y1="12" x2="19" y2="12"></line></svg>'
|
|
481
|
-
},
|
|
694
|
+
}, N = {
|
|
482
695
|
type: "button",
|
|
483
696
|
title: "Clear Formatting",
|
|
484
697
|
command: "removeFormat",
|
|
485
698
|
icon: '<svg viewBox="0 0 24 24"><path d="M17.41 15.41L12 10l-5.41 5.41L5.17 14l5.41-5.41L5.17 3.17 6.59 1.76 12 7.17l5.41-5.41 1.41 1.41-5.41 5.41 5.41 5.41-1.41 1.42z"></path></svg>'
|
|
486
|
-
},
|
|
699
|
+
}, P = {
|
|
487
700
|
type: "button",
|
|
488
701
|
title: "Insert Emoji",
|
|
489
702
|
command: "insertEmoji",
|
|
490
703
|
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><path d="M8 14s1.5 2 4 2 4-2 4-2"></path><line x1="9" y1="9" x2="9.01" y2="9"></line><line x1="15" y1="9" x2="15.01" y2="9"></line></svg>'
|
|
491
|
-
},
|
|
704
|
+
}, D = {
|
|
492
705
|
type: "button",
|
|
493
706
|
title: "Insert Link",
|
|
494
707
|
command: "createLink",
|
|
@@ -498,38 +711,44 @@ const p = {
|
|
|
498
711
|
title: "Insert Image",
|
|
499
712
|
command: "insertImage",
|
|
500
713
|
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><polyline points="21 15 16 10 5 21"></polyline></svg>'
|
|
501
|
-
},
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
714
|
+
}, G = {
|
|
715
|
+
type: "button",
|
|
716
|
+
command: "insertTable",
|
|
717
|
+
title: "Insert Table",
|
|
718
|
+
icon: '<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M3 3h18v18H3V3m2 2v4h4V5H5m6 0v4h4V5h-4m6 0v4h2V5h-2M5 11v4h4v-4H5m6 0v4h4v-4h-4m6 0v4h2v-4h-2M5 17v2h4v-2H5m6 0v2h4v-2h-4m6 0v2h2v-2h-2Z"/></svg>'
|
|
719
|
+
}, d = { type: "divider", title: "" }, $ = [
|
|
720
|
+
{ ...v, id: "undo" },
|
|
721
|
+
{ ...b, id: "redo" },
|
|
722
|
+
d,
|
|
723
|
+
{ ...j, id: "heading" },
|
|
724
|
+
{ ...f, id: "font-family" },
|
|
725
|
+
{ ...w, id: "font-size" },
|
|
726
|
+
{ ...E, id: "line-height" },
|
|
727
|
+
d,
|
|
728
|
+
{ ...S, id: "bold" },
|
|
729
|
+
{ ...x, id: "italic" },
|
|
730
|
+
{ ...k, id: "underline" },
|
|
731
|
+
{ ...L, id: "strikethrough" },
|
|
732
|
+
d,
|
|
733
|
+
{ ...A, id: "text-color" },
|
|
734
|
+
{ ...C, id: "highlight-color" },
|
|
735
|
+
d,
|
|
736
|
+
{ ...T, id: "align-left" },
|
|
737
|
+
{ ...H, id: "align-center" },
|
|
738
|
+
{ ...F, id: "align-right" },
|
|
739
|
+
{ ...R, id: "align-justify" },
|
|
740
|
+
d,
|
|
741
|
+
{ ...M, id: "bullet-list" },
|
|
742
|
+
{ ...I, id: "ordered-list" },
|
|
743
|
+
{ ...O, id: "outdent" },
|
|
744
|
+
{ ...z, id: "indent" },
|
|
745
|
+
d,
|
|
746
|
+
{ ...B, id: "horizontal-rule" },
|
|
747
|
+
{ ...P, id: "emoji" },
|
|
748
|
+
{ ...D, id: "link" },
|
|
749
|
+
{ ...W, id: "image" },
|
|
750
|
+
{ ...G, id: "table" },
|
|
751
|
+
{ ...N, id: "clear-formatting" }
|
|
533
752
|
], g = [
|
|
534
753
|
// Smileys & Emotion
|
|
535
754
|
{ emoji: "😀", name: "grinning", category: "Smileys" },
|
|
@@ -860,18 +1079,20 @@ const p = {
|
|
|
860
1079
|
{ emoji: "🎤", name: "microphone", category: "Activities" },
|
|
861
1080
|
{ emoji: "🎧", name: "headphone", category: "Activities" }
|
|
862
1081
|
];
|
|
863
|
-
class
|
|
1082
|
+
class q {
|
|
864
1083
|
container;
|
|
865
1084
|
searchInput;
|
|
866
1085
|
emojiGrid;
|
|
867
1086
|
onSelect;
|
|
868
1087
|
onClose;
|
|
869
|
-
|
|
870
|
-
|
|
1088
|
+
theme;
|
|
1089
|
+
dark;
|
|
1090
|
+
constructor(e, t, i, o) {
|
|
1091
|
+
this.onSelect = e, this.onClose = t, this.theme = i, this.dark = o, this.container = this.createPickerElement(), this.searchInput = this.container.querySelector(".te-emoji-search"), this.emojiGrid = this.container.querySelector(".te-emoji-grid"), this.setupEvents(), this.renderEmojis(g);
|
|
871
1092
|
}
|
|
872
1093
|
createPickerElement() {
|
|
873
1094
|
const e = document.createElement("div");
|
|
874
|
-
return e.classList.add("te-emoji-picker"), e.innerHTML = `
|
|
1095
|
+
return e.classList.add("te-emoji-picker"), this.theme && this.applyTheme(e, this.theme), this.dark && e.classList.add("te-dark"), e.innerHTML = `
|
|
875
1096
|
<div class="te-emoji-header">
|
|
876
1097
|
<input type="text" class="te-emoji-search" placeholder="Search emoji...">
|
|
877
1098
|
</div>
|
|
@@ -882,10 +1103,10 @@ class G {
|
|
|
882
1103
|
}
|
|
883
1104
|
setupEvents() {
|
|
884
1105
|
this.searchInput.addEventListener("mousedown", (t) => t.stopPropagation()), this.searchInput.addEventListener("click", (t) => t.stopPropagation()), this.searchInput.addEventListener("input", () => {
|
|
885
|
-
const t = this.searchInput.value.toLowerCase(),
|
|
886
|
-
(
|
|
1106
|
+
const t = this.searchInput.value.toLowerCase(), i = g.filter(
|
|
1107
|
+
(o) => o.name.toLowerCase().includes(t) || o.category.toLowerCase().includes(t)
|
|
887
1108
|
);
|
|
888
|
-
this.renderEmojis(
|
|
1109
|
+
this.renderEmojis(i);
|
|
889
1110
|
});
|
|
890
1111
|
const e = (t) => {
|
|
891
1112
|
this.container.contains(t.target) || (this.close(), document.removeEventListener("mousedown", e));
|
|
@@ -897,27 +1118,53 @@ class G {
|
|
|
897
1118
|
this.emojiGrid.innerHTML = '<div class="te-emoji-empty">No emoji found</div>';
|
|
898
1119
|
return;
|
|
899
1120
|
}
|
|
900
|
-
this.searchInput.value.length > 0 ? this.renderGridItems(e) : ["Smileys", "Symbols", "Hands", "Animals", "Food", "Travel", "Objects", "Activities"].forEach((
|
|
901
|
-
const
|
|
902
|
-
if (
|
|
903
|
-
const
|
|
904
|
-
|
|
1121
|
+
this.searchInput.value.length > 0 ? this.renderGridItems(e) : ["Smileys", "Symbols", "Hands", "Animals", "Food", "Travel", "Objects", "Activities"].forEach((o) => {
|
|
1122
|
+
const n = e.filter((a) => a.category === o);
|
|
1123
|
+
if (n.length > 0) {
|
|
1124
|
+
const a = document.createElement("div");
|
|
1125
|
+
a.classList.add("te-emoji-category-title"), a.textContent = o, this.emojiGrid.appendChild(a), this.renderGridItems(n);
|
|
905
1126
|
}
|
|
906
1127
|
});
|
|
907
1128
|
}
|
|
908
1129
|
renderGridItems(e) {
|
|
909
1130
|
e.forEach((t) => {
|
|
910
|
-
const
|
|
911
|
-
|
|
1131
|
+
const i = document.createElement("button");
|
|
1132
|
+
i.type = "button", i.classList.add("te-emoji-item"), i.textContent = t.emoji, i.title = t.name, i.addEventListener("click", () => {
|
|
912
1133
|
this.onSelect(t.emoji), this.close();
|
|
913
|
-
}), this.emojiGrid.appendChild(
|
|
1134
|
+
}), this.emojiGrid.appendChild(i);
|
|
914
1135
|
});
|
|
915
1136
|
}
|
|
1137
|
+
applyTheme(e, t) {
|
|
1138
|
+
const i = {
|
|
1139
|
+
primaryColor: "--te-primary-color",
|
|
1140
|
+
primaryHover: "--te-primary-hover",
|
|
1141
|
+
bgApp: "--te-bg-app",
|
|
1142
|
+
bgEditor: "--te-bg-editor",
|
|
1143
|
+
toolbarBg: "--te-toolbar-bg",
|
|
1144
|
+
borderColor: "--te-border-color",
|
|
1145
|
+
borderFocus: "--te-border-focus",
|
|
1146
|
+
textMain: "--te-text-main",
|
|
1147
|
+
textMuted: "--te-text-muted",
|
|
1148
|
+
placeholder: "--te-placeholder",
|
|
1149
|
+
btnHover: "--te-btn-hover",
|
|
1150
|
+
btnActive: "--te-btn-active",
|
|
1151
|
+
radiusLg: "--te-radius-lg",
|
|
1152
|
+
radiusMd: "--te-radius-md",
|
|
1153
|
+
radiusSm: "--te-radius-sm",
|
|
1154
|
+
shadowSm: "--te-shadow-sm",
|
|
1155
|
+
shadowMd: "--te-shadow-md",
|
|
1156
|
+
shadowLg: "--te-shadow-lg"
|
|
1157
|
+
};
|
|
1158
|
+
for (const [o, n] of Object.entries(i)) {
|
|
1159
|
+
const a = t[o];
|
|
1160
|
+
a && e.style.setProperty(n, a);
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
916
1163
|
show(e) {
|
|
917
1164
|
document.body.appendChild(this.container);
|
|
918
|
-
const t = e.getBoundingClientRect(),
|
|
919
|
-
let
|
|
920
|
-
|
|
1165
|
+
const t = e.getBoundingClientRect(), i = 280;
|
|
1166
|
+
let o = t.bottom + window.scrollY + 5, n = t.left + window.scrollX;
|
|
1167
|
+
n + i > window.innerWidth && (n = window.innerWidth - i - 10), this.container.style.top = `${o}px`, this.container.style.left = `${n}px`, this.searchInput.focus();
|
|
921
1168
|
}
|
|
922
1169
|
close() {
|
|
923
1170
|
this.container.parentElement && (this.container.remove(), this.onClose());
|
|
@@ -926,58 +1173,72 @@ class G {
|
|
|
926
1173
|
return this.container;
|
|
927
1174
|
}
|
|
928
1175
|
}
|
|
929
|
-
class
|
|
1176
|
+
class U {
|
|
930
1177
|
editor;
|
|
931
1178
|
container;
|
|
932
1179
|
savedRange = null;
|
|
933
|
-
items =
|
|
1180
|
+
items = $;
|
|
934
1181
|
activePicker = null;
|
|
1182
|
+
statusEl = null;
|
|
935
1183
|
constructor(e) {
|
|
936
1184
|
this.editor = e, this.container = this.createToolbarElement(), this.render();
|
|
937
1185
|
}
|
|
938
1186
|
createToolbarElement() {
|
|
939
1187
|
const e = document.createElement("div");
|
|
940
|
-
return e.classList.add("te-toolbar"), e;
|
|
1188
|
+
return e.classList.add("te-toolbar"), this.statusEl = document.createElement("div"), this.statusEl.classList.add("te-toolbar-status"), this.statusEl.style.marginLeft = "auto", this.statusEl.style.display = "flex", this.statusEl.style.alignItems = "center", this.statusEl.style.gap = "6px", this.statusEl.style.fontSize = "12px", this.statusEl.style.color = "var(--te-text-muted)", this.statusEl.style.paddingRight = "12px", e;
|
|
941
1189
|
}
|
|
942
1190
|
render() {
|
|
943
|
-
this.
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
else
|
|
953
|
-
|
|
954
|
-
|
|
1191
|
+
const e = this.editor.getOptions().toolbarItems, t = [];
|
|
1192
|
+
this.items.forEach((n) => {
|
|
1193
|
+
(n.type === "divider" || n.id && (!e || e.includes(n.id))) && t.push(n);
|
|
1194
|
+
});
|
|
1195
|
+
const i = [];
|
|
1196
|
+
t.forEach((n, a) => {
|
|
1197
|
+
if (n.type === "divider") {
|
|
1198
|
+
if (i.length === 0 || i[i.length - 1].type === "divider" || !t.slice(a + 1).some((r) => r.type !== "divider")) return;
|
|
1199
|
+
i.push(n);
|
|
1200
|
+
} else
|
|
1201
|
+
i.push(n);
|
|
1202
|
+
}), i.forEach((n) => {
|
|
1203
|
+
if (n.type === "button")
|
|
1204
|
+
this.renderButton(n);
|
|
1205
|
+
else if (n.type === "select")
|
|
1206
|
+
this.renderSelect(n);
|
|
1207
|
+
else if (n.type === "input")
|
|
1208
|
+
this.renderInput(n);
|
|
1209
|
+
else if (n.type === "color-picker")
|
|
1210
|
+
this.renderColorPicker(n);
|
|
1211
|
+
else if (n.type === "divider") {
|
|
1212
|
+
const a = document.createElement("div");
|
|
1213
|
+
a.classList.add("te-divider"), this.container.appendChild(a);
|
|
955
1214
|
}
|
|
956
|
-
}), this.editor.el.addEventListener("keyup", () => this.updateActiveStates()), this.editor.el.addEventListener("mouseup", () => this.updateActiveStates());
|
|
1215
|
+
}), this.editor.getOptions().showStatus !== !1 && this.container.appendChild(this.statusEl), this.editor.el.addEventListener("keyup", () => this.updateActiveStates()), this.editor.el.addEventListener("mouseup", () => this.updateActiveStates());
|
|
957
1216
|
}
|
|
958
1217
|
renderButton(e) {
|
|
959
1218
|
const t = document.createElement("button");
|
|
960
|
-
t.classList.add("te-button"), t.innerHTML = e.icon || "", t.title = e.title, t.addEventListener("mousedown", (
|
|
961
|
-
if (
|
|
962
|
-
this.activePicker ? this.activePicker.close() : (this.activePicker = new
|
|
963
|
-
(
|
|
964
|
-
this.editor.execute("insertText",
|
|
1219
|
+
t.classList.add("te-button"), t.innerHTML = e.icon || "", t.title = e.title, t.addEventListener("mousedown", (i) => {
|
|
1220
|
+
if (i.preventDefault(), e.command === "insertEmoji") {
|
|
1221
|
+
this.activePicker ? this.activePicker.close() : (this.activePicker = new q(
|
|
1222
|
+
(o) => {
|
|
1223
|
+
this.editor.execute("insertText", o);
|
|
965
1224
|
},
|
|
966
1225
|
() => {
|
|
967
1226
|
this.activePicker = null;
|
|
968
|
-
}
|
|
1227
|
+
},
|
|
1228
|
+
this.editor.getOptions().theme,
|
|
1229
|
+
this.editor.getOptions().dark
|
|
969
1230
|
), this.activePicker.show(t));
|
|
970
1231
|
return;
|
|
971
1232
|
}
|
|
972
1233
|
if (e.command === "createLink") {
|
|
973
|
-
const
|
|
974
|
-
|
|
1234
|
+
const o = window.prompt("Enter the URL");
|
|
1235
|
+
o && this.editor.createLink(o);
|
|
975
1236
|
return;
|
|
976
1237
|
}
|
|
977
1238
|
if (e.command === "insertImage") {
|
|
978
|
-
const
|
|
979
|
-
|
|
980
|
-
const s =
|
|
1239
|
+
const o = document.createElement("input");
|
|
1240
|
+
o.type = "file", o.accept = "image/*", o.style.display = "none", o.addEventListener("change", (n) => {
|
|
1241
|
+
const s = n.target.files?.[0];
|
|
981
1242
|
if (s) {
|
|
982
1243
|
const r = new FileReader();
|
|
983
1244
|
r.onload = (l) => {
|
|
@@ -985,8 +1246,18 @@ class q {
|
|
|
985
1246
|
this.editor.insertImage(c);
|
|
986
1247
|
}, r.readAsDataURL(s);
|
|
987
1248
|
}
|
|
988
|
-
document.body.removeChild(
|
|
989
|
-
}), document.body.appendChild(
|
|
1249
|
+
document.body.removeChild(o);
|
|
1250
|
+
}), document.body.appendChild(o), o.click();
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
if (e.command === "insertTable") {
|
|
1254
|
+
const o = window.prompt("Enter number of rows", "3"), n = window.prompt("Enter number of columns", "3");
|
|
1255
|
+
o && n && this.editor.insertTable(parseInt(o, 10), parseInt(n, 10));
|
|
1256
|
+
return;
|
|
1257
|
+
}
|
|
1258
|
+
if (["addRow", "deleteRow", "addColumn", "deleteColumn"].includes(e.command || "")) {
|
|
1259
|
+
const o = e.command;
|
|
1260
|
+
this.editor[o]();
|
|
990
1261
|
return;
|
|
991
1262
|
}
|
|
992
1263
|
e.command && this.editor.execute(e.command, e.value || null), this.updateActiveStates();
|
|
@@ -995,37 +1266,37 @@ class q {
|
|
|
995
1266
|
renderInput(e) {
|
|
996
1267
|
const t = document.createElement("input");
|
|
997
1268
|
t.type = "number", t.classList.add("te-input"), t.title = e.title, t.value = e.value || "", t.min = "1", t.max = "100";
|
|
998
|
-
const
|
|
999
|
-
const
|
|
1000
|
-
if (
|
|
1001
|
-
const
|
|
1002
|
-
this.editor.el.contains(
|
|
1269
|
+
const i = () => {
|
|
1270
|
+
const n = window.getSelection();
|
|
1271
|
+
if (n && n.rangeCount > 0) {
|
|
1272
|
+
const a = n.getRangeAt(0);
|
|
1273
|
+
this.editor.el.contains(a.commonAncestorContainer) && (this.savedRange = a.cloneRange());
|
|
1003
1274
|
}
|
|
1004
1275
|
};
|
|
1005
|
-
t.addEventListener("mousedown",
|
|
1006
|
-
const
|
|
1007
|
-
let
|
|
1008
|
-
if (!isNaN(
|
|
1276
|
+
t.addEventListener("mousedown", i), t.addEventListener("focus", i);
|
|
1277
|
+
const o = () => {
|
|
1278
|
+
let n = parseInt(t.value, 10);
|
|
1279
|
+
if (!isNaN(n) && (n = Math.max(1, Math.min(100, n)), t.value = n.toString(), e.command === "fontSize")) {
|
|
1009
1280
|
if (this.savedRange) {
|
|
1010
|
-
const
|
|
1011
|
-
|
|
1281
|
+
const a = this.editor.setStyle("font-size", `${n}px`, this.savedRange);
|
|
1282
|
+
a && (this.savedRange = a);
|
|
1012
1283
|
} else
|
|
1013
|
-
this.editor.setStyle("font-size", `${
|
|
1284
|
+
this.editor.setStyle("font-size", `${n}px`);
|
|
1014
1285
|
t.focus();
|
|
1015
1286
|
}
|
|
1016
1287
|
};
|
|
1017
|
-
t.addEventListener("input",
|
|
1018
|
-
|
|
1288
|
+
t.addEventListener("input", o), t.addEventListener("keydown", (n) => {
|
|
1289
|
+
n.key === "Enter" && (o(), this.editor.focus());
|
|
1019
1290
|
}), this.container.appendChild(t);
|
|
1020
1291
|
}
|
|
1021
1292
|
renderSelect(e) {
|
|
1022
1293
|
const t = document.createElement("select");
|
|
1023
|
-
t.classList.add("te-select"), t.title = e.title, e.options && e.options.forEach((
|
|
1024
|
-
const
|
|
1025
|
-
|
|
1294
|
+
t.classList.add("te-select"), t.title = e.title, e.options && e.options.forEach((i) => {
|
|
1295
|
+
const o = document.createElement("option");
|
|
1296
|
+
o.value = i.value, o.textContent = i.label, t.appendChild(o);
|
|
1026
1297
|
}), t.addEventListener("change", () => {
|
|
1027
|
-
const
|
|
1028
|
-
this.savedRange && this.editor.selection.restoreSelection(this.savedRange), e.command === "formatBlock" ? this.editor.execute(e.command,
|
|
1298
|
+
const i = t.value;
|
|
1299
|
+
this.savedRange && this.editor.selection.restoreSelection(this.savedRange), e.command === "formatBlock" ? this.editor.execute(e.command, i) : e.command === "fontFamily" ? this.editor.setStyle("font-family", i) : e.command === "lineHeight" && this.editor.setStyle("line-height", i), this.editor.focus();
|
|
1029
1300
|
}), t.addEventListener("mousedown", () => {
|
|
1030
1301
|
this.savedRange = this.editor.selection.saveSelection();
|
|
1031
1302
|
}), this.container.appendChild(t);
|
|
@@ -1033,22 +1304,22 @@ class q {
|
|
|
1033
1304
|
renderColorPicker(e) {
|
|
1034
1305
|
const t = document.createElement("div");
|
|
1035
1306
|
if (t.classList.add("te-color-picker-wrapper"), t.title = e.title, e.icon) {
|
|
1036
|
-
const
|
|
1037
|
-
|
|
1038
|
-
const
|
|
1039
|
-
|
|
1307
|
+
const o = document.createElement("div");
|
|
1308
|
+
o.classList.add("te-button", "te-color-icon"), o.innerHTML = e.icon;
|
|
1309
|
+
const n = document.createElement("div");
|
|
1310
|
+
n.classList.add("te-color-indicator"), n.style.backgroundColor = e.value || "#000000", o.appendChild(n), t.appendChild(o);
|
|
1040
1311
|
}
|
|
1041
|
-
const
|
|
1042
|
-
|
|
1312
|
+
const i = document.createElement("input");
|
|
1313
|
+
i.type = "color", i.classList.add("te-color-picker-input"), e.icon || (i.classList.add("te-color-picker"), i.title = e.title), i.value = e.value || "#000000", i.addEventListener("mousedown", () => {
|
|
1043
1314
|
this.savedRange = this.editor.selection.saveSelection();
|
|
1044
|
-
}),
|
|
1315
|
+
}), i.addEventListener("input", () => {
|
|
1045
1316
|
if (e.icon) {
|
|
1046
|
-
const
|
|
1047
|
-
|
|
1317
|
+
const o = t.querySelector(".te-color-indicator");
|
|
1318
|
+
o && (o.style.backgroundColor = i.value);
|
|
1048
1319
|
}
|
|
1049
|
-
}),
|
|
1050
|
-
this.savedRange && this.editor.selection.restoreSelection(this.savedRange), e.command && this.editor.execute(e.command,
|
|
1051
|
-
}), t.appendChild(
|
|
1320
|
+
}), i.addEventListener("change", () => {
|
|
1321
|
+
this.savedRange && this.editor.selection.restoreSelection(this.savedRange), e.command && this.editor.execute(e.command, i.value), this.editor.focus();
|
|
1322
|
+
}), t.appendChild(i), this.container.appendChild(e.icon ? t : i);
|
|
1052
1323
|
}
|
|
1053
1324
|
get el() {
|
|
1054
1325
|
return this.container;
|
|
@@ -1056,25 +1327,57 @@ class q {
|
|
|
1056
1327
|
updateActiveStates() {
|
|
1057
1328
|
const e = this.container.querySelectorAll(".te-button");
|
|
1058
1329
|
let t = 0;
|
|
1059
|
-
this.items.forEach((
|
|
1060
|
-
if (
|
|
1061
|
-
const
|
|
1062
|
-
|
|
1330
|
+
this.items.forEach((i) => {
|
|
1331
|
+
if (i.type === "button") {
|
|
1332
|
+
const o = e[t++];
|
|
1333
|
+
i.command && document.queryCommandState(i.command) ? o.classList.add("active") : o.classList.remove("active");
|
|
1063
1334
|
}
|
|
1064
1335
|
});
|
|
1065
1336
|
}
|
|
1337
|
+
updateStatus(e, t = !1) {
|
|
1338
|
+
if (!this.statusEl || this.editor.getOptions().showStatus === !1) return;
|
|
1339
|
+
if (this.statusEl.innerHTML = "", t) {
|
|
1340
|
+
const o = document.createElement("div");
|
|
1341
|
+
o.classList.add("te-toolbar-loader"), this.statusEl.appendChild(o);
|
|
1342
|
+
}
|
|
1343
|
+
const i = document.createElement("span");
|
|
1344
|
+
i.textContent = e, this.statusEl.appendChild(i);
|
|
1345
|
+
}
|
|
1066
1346
|
}
|
|
1067
|
-
class
|
|
1347
|
+
class V extends p {
|
|
1068
1348
|
toolbar;
|
|
1069
1349
|
constructor(e, t = {}) {
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1350
|
+
const i = {
|
|
1351
|
+
...t,
|
|
1352
|
+
onSaving: () => {
|
|
1353
|
+
this.toolbar?.updateStatus("Auto saving...", !0), t.onSaving && t.onSaving();
|
|
1354
|
+
},
|
|
1355
|
+
onSave: (o) => {
|
|
1356
|
+
const n = (/* @__PURE__ */ new Date()).toLocaleString([], {
|
|
1357
|
+
year: "numeric",
|
|
1358
|
+
month: "short",
|
|
1359
|
+
day: "numeric",
|
|
1360
|
+
hour: "2-digit",
|
|
1361
|
+
minute: "2-digit",
|
|
1362
|
+
hour12: !0
|
|
1363
|
+
});
|
|
1364
|
+
this.toolbar?.updateStatus(`Saved at ${n}`, !1), t.onSave && t.onSave(o);
|
|
1365
|
+
}
|
|
1366
|
+
};
|
|
1367
|
+
if (super(e, i), typeof document > "u" || !e) {
|
|
1368
|
+
this.toolbar = {};
|
|
1369
|
+
return;
|
|
1370
|
+
}
|
|
1371
|
+
this.toolbar = new U(this), this.container.insertBefore(this.toolbar.el, this.editableElement), i.showStatus !== !1 && this.toolbar.updateStatus("All changes saved", !1);
|
|
1372
|
+
}
|
|
1373
|
+
getToolbar() {
|
|
1374
|
+
return this.toolbar;
|
|
1073
1375
|
}
|
|
1074
1376
|
}
|
|
1075
1377
|
export {
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1378
|
+
p as CoreEditor,
|
|
1379
|
+
u as HistoryManager,
|
|
1380
|
+
h as SelectionManager,
|
|
1381
|
+
V as TestEditor,
|
|
1382
|
+
U as Toolbar
|
|
1080
1383
|
};
|