@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.
Files changed (45) hide show
  1. package/ReadME.md +30 -46
  2. package/dist/core/Editor.d.ts +133 -0
  3. package/dist/core/HistoryManager.d.ts +30 -0
  4. package/dist/core/ImageManager.d.ts +19 -0
  5. package/dist/core/SelectionManager.d.ts +31 -0
  6. package/dist/index.d.ts +11 -0
  7. package/dist/rich-text-editor.css +1 -1
  8. package/dist/style.d.ts +4 -0
  9. package/dist/test-editor.cjs +2 -2
  10. package/dist/test-editor.mjs +535 -232
  11. package/dist/ui/Toolbar.d.ts +19 -0
  12. package/dist/ui/toolbar/EmojiList.d.ts +6 -0
  13. package/dist/ui/toolbar/EmojiPicker.d.ts +19 -0
  14. package/dist/ui/toolbar/ToolbarItem.d.ts +16 -0
  15. package/dist/ui/toolbar/items/AlignCenter.d.ts +2 -0
  16. package/dist/ui/toolbar/items/AlignJustify.d.ts +2 -0
  17. package/dist/ui/toolbar/items/AlignLeft.d.ts +2 -0
  18. package/dist/ui/toolbar/items/AlignRight.d.ts +2 -0
  19. package/dist/ui/toolbar/items/Bold.d.ts +2 -0
  20. package/dist/ui/toolbar/items/BulletList.d.ts +2 -0
  21. package/dist/ui/toolbar/items/ClearFormatting.d.ts +2 -0
  22. package/dist/ui/toolbar/items/Emoji.d.ts +2 -0
  23. package/dist/ui/toolbar/items/FontFamily.d.ts +2 -0
  24. package/dist/ui/toolbar/items/FontSize.d.ts +2 -0
  25. package/dist/ui/toolbar/items/Heading.d.ts +2 -0
  26. package/dist/ui/toolbar/items/HighlightColor.d.ts +2 -0
  27. package/dist/ui/toolbar/items/HorizontalRule.d.ts +2 -0
  28. package/dist/ui/toolbar/items/Image.d.ts +2 -0
  29. package/dist/ui/toolbar/items/Indent.d.ts +2 -0
  30. package/dist/ui/toolbar/items/Italic.d.ts +2 -0
  31. package/dist/ui/toolbar/items/LineHeight.d.ts +2 -0
  32. package/dist/ui/toolbar/items/Link.d.ts +2 -0
  33. package/dist/ui/toolbar/items/OrderedList.d.ts +2 -0
  34. package/dist/ui/toolbar/items/Outdent.d.ts +2 -0
  35. package/dist/ui/toolbar/items/Redo.d.ts +2 -0
  36. package/dist/ui/toolbar/items/Strikethrough.d.ts +2 -0
  37. package/dist/ui/toolbar/items/Table.d.ts +2 -0
  38. package/dist/ui/toolbar/items/TableActions.d.ts +5 -0
  39. package/dist/ui/toolbar/items/TextColor.d.ts +2 -0
  40. package/dist/ui/toolbar/items/Underline.d.ts +2 -0
  41. package/dist/ui/toolbar/items/Undo.d.ts +2 -0
  42. package/dist/ui/toolbar/registry.d.ts +2 -0
  43. package/package.json +15 -10
  44. package/images/Screenshot from 2026-03-07 12-13-13.png +0 -0
  45. package/images/editor-preview.png +0 -0
@@ -1,4 +1,4 @@
1
- class y {
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 h {
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 o = t.target;
62
- if (o.classList.contains("te-image-resizer")) {
61
+ const i = t.target;
62
+ if (i.classList.contains("te-image-resizer")) {
63
63
  t.preventDefault(), t.stopPropagation();
64
- const a = o.closest(".te-image-container");
65
- a && (this.selectImage(a), this.startResize(t, o));
64
+ const n = i.closest(".te-image-container");
65
+ n && (this.selectImage(n), this.startResize(t, i));
66
66
  return;
67
67
  }
68
- const i = o.closest(".te-image-container");
69
- i ? this.selectImage(i) : this.deselectImage();
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((i) => i.startsWith("te-resizer-"))?.replace("te-resizer-", "") || null;
87
- const o = this.activeContainer.querySelector("img");
88
- this.startX = e.clientX, this.startY = e.clientY, this.startWidth = o.clientWidth, this.startHeight = o.clientHeight, this.aspectRatio = this.startWidth / this.startHeight, document.body.style.cursor = window.getComputedStyle(t).cursor;
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"), o = e.clientX - this.startX, i = e.clientY - this.startY;
93
- let a = this.startWidth, n = this.startHeight;
94
- this.currentHandle?.includes("right") ? a = this.startWidth + o : this.currentHandle?.includes("left") ? a = this.startWidth - o : this.currentHandle?.includes("bottom") ? a = this.startWidth + i * this.aspectRatio : this.currentHandle?.includes("top") && (a = this.startWidth - i * this.aspectRatio), n = a / this.aspectRatio, a > 50 && a < this.editor.el.clientWidth && (t.style.width = `${a}px`, t.style.height = `${n}px`);
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
- this.container = e, this.container.classList.add("te-container"), this.options = t, this.editableElement = this.createEditableElement(), this.selection = new y(), this.imageManager = new h(this), this.setupInputHandlers(), this.setupImageObserver(), this.container.appendChild(this.editableElement), this.options.autofocus && this.focus(), document.execCommand("defaultParagraphSeparator", !1, "p");
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((o) => {
113
- o.addedNodes.forEach((i) => {
114
- if (i.nodeType === Node.ELEMENT_NODE) {
115
- const a = i;
116
- a.tagName === "IMG" && !a.closest(".te-image-container") ? this.wrapImage(a) : a.querySelectorAll("img:not(.te-image)").forEach((s) => {
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 o = document.createElement("figure");
134
- o.classList.add("te-image-container"), o.setAttribute("contenteditable", "false");
135
- const i = document.createElement("img");
136
- i.src = e.src, i.alt = e.alt || "", e.width && (i.style.width = `${e.width}px`), e.height && (i.style.height = `${e.height}px`), i.classList.add("te-image");
137
- const a = document.createElement("figcaption");
138
- if (a.classList.add("te-image-caption"), a.setAttribute("contenteditable", "true"), a.setAttribute("data-placeholder", "Type caption..."), ["top-left", "top-right", "bottom-left", "bottom-right"].forEach((s) => {
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}`), o.appendChild(r);
141
- }), o.appendChild(i), o.appendChild(a), t.replaceChild(o, e), !o.nextElementSibling) {
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>", o.after(s);
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 o = document.createElement("span");
153
- for (const [a, n] of Object.entries(this.pendingStyles))
154
- o.style.setProperty(a, n);
155
- o.textContent = t;
156
- const i = this.selection.getRange();
157
- if (i) {
158
- i.deleteContents(), i.insertNode(o);
159
- const a = document.createRange();
160
- a.setStart(o.firstChild, t.length), a.setEnd(o.firstChild, t.length), this.selection.restoreSelection(a), this.pendingStyles = {}, this.editableElement.dispatchEvent(new Event("input", { bubbles: !0 }));
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 o = 0; o < t.length; o++) {
175
- const i = t[o];
176
- if (i.type.startsWith("image/")) {
177
- const a = new FileReader();
178
- a.onload = (n) => {
179
- const s = n.target?.result;
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
- }, a.readAsDataURL(i);
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 a = 0; a < t.items.length; a++) {
189
- const n = t.items[a];
190
- if (n.type.startsWith("image/")) {
191
- const s = n.getAsFile();
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 o = t.getData("text/plain"), i = /<([a-z1-6]+)\b[^>]*>[\s\S]*<\/\1>/i.test(o) || /^\s*<[a-z1-6]+\b[^>]*>/i.test(o);
204
- if (o && i) {
205
- const a = o.replace(/(\r\n|\n|\r)/gm, " ").replace(/>\s+</g, "><").trim();
206
- e.preventDefault(), this.execute("insertHTML", a);
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 o = document.createTreeWalker(e, NodeFilter.SHOW_ELEMENT);
232
- let i = o.nextNode();
233
- for (; i; )
234
- i.style.getPropertyValue(t) && i.style.removeProperty(t), i = o.nextNode();
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, o) {
242
- if (e === "font-size" && t.endsWith("px"), !o) {
243
- const n = window.getSelection();
244
- if (!n || n.rangeCount === 0) return null;
245
- o = n.getRangeAt(0);
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 (o.collapsed)
248
- return this.pendingStyles[e] = t, o;
249
- let i = o.commonAncestorContainer;
250
- i.nodeType === Node.TEXT_NODE && (i = i.parentElement);
251
- let a = null;
252
- if (i.tagName === "SPAN" && i.children.length === 0 && i.textContent === o.toString())
253
- i.style.setProperty(e, t), a = o.cloneRange();
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 n = document.createElement("span");
256
- n.style.setProperty(e, t);
462
+ const a = document.createElement("span");
463
+ a.style.setProperty(e, t);
257
464
  try {
258
- const s = i.tagName === "SPAN" ? i : null, r = o.extractContents();
259
- this.clearStyleRecursive(r, e), n.appendChild(r), o.insertNode(n), s && s.innerHTML === "" && s.remove();
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(n), a = l;
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 })), a;
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 i = t.getRangeAt(0).commonAncestorContainer;
279
- i.nodeType === Node.TEXT_NODE && (i = i.parentElement);
280
- let a = null;
281
- i.tagName === "A" ? a = i : a = i.querySelector("a"), a && (a.setAttribute("target", "_blank"), a.setAttribute("rel", "noopener noreferrer"));
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 o = document.createElement("figure");
293
- o.classList.add("te-image-container"), o.setAttribute("contenteditable", "false");
294
- const i = document.createElement("img");
295
- i.src = e, i.classList.add("te-image");
296
- const a = document.createElement("figcaption");
297
- a.classList.add("te-image-caption"), a.setAttribute("contenteditable", "true"), a.setAttribute("data-placeholder", "Type caption..."), ["top-left", "top-right", "bottom-left", "bottom-right"].forEach((l) => {
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}`), o.appendChild(c);
300
- }), o.appendChild(i), o.appendChild(a), t.deleteContents(), t.insertNode(o);
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>", o.after(s);
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 p = {
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
- }, j = {
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
- }, v = {
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
- }, b = {
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
- }, f = {
582
+ }, w = {
370
583
  type: "input",
371
584
  title: "Size (px)",
372
585
  command: "fontSize",
373
586
  placeholder: "Size",
374
587
  value: "16"
375
- }, w = {
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
- }, E = {
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
- }, A = {
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
- }, k = {
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
- }, L = {
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
- }, C = {
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
- }, F = {
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
- }, H = {
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
- }, T = {
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
- }, R = {
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
- }, O = {
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
- }, I = {
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
- }, M = {
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
- }, z = {
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
- }, B = {
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
- }, N = {
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
- }, P = {
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
- }, m = { type: "divider", title: "" }, D = [
502
- p,
503
- j,
504
- m,
505
- v,
506
- b,
507
- f,
508
- w,
509
- m,
510
- S,
511
- x,
512
- E,
513
- A,
514
- m,
515
- k,
516
- L,
517
- m,
518
- C,
519
- F,
520
- H,
521
- T,
522
- m,
523
- R,
524
- O,
525
- I,
526
- M,
527
- m,
528
- z,
529
- N,
530
- P,
531
- W,
532
- B
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 G {
1082
+ class q {
864
1083
  container;
865
1084
  searchInput;
866
1085
  emojiGrid;
867
1086
  onSelect;
868
1087
  onClose;
869
- constructor(e, t) {
870
- this.onSelect = e, this.onClose = t, 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);
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(), o = g.filter(
886
- (i) => i.name.toLowerCase().includes(t) || i.category.toLowerCase().includes(t)
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(o);
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((i) => {
901
- const a = e.filter((n) => n.category === i);
902
- if (a.length > 0) {
903
- const n = document.createElement("div");
904
- n.classList.add("te-emoji-category-title"), n.textContent = i, this.emojiGrid.appendChild(n), this.renderGridItems(a);
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 o = document.createElement("button");
911
- o.type = "button", o.classList.add("te-emoji-item"), o.textContent = t.emoji, o.title = t.name, o.addEventListener("click", () => {
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(o);
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(), o = 280;
919
- let i = t.bottom + window.scrollY + 5, a = t.left + window.scrollX;
920
- a + o > window.innerWidth && (a = window.innerWidth - o - 10), this.container.style.top = `${i}px`, this.container.style.left = `${a}px`, this.searchInput.focus();
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 q {
1176
+ class U {
930
1177
  editor;
931
1178
  container;
932
1179
  savedRange = null;
933
- items = D;
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.items.forEach((e) => {
944
- if (e.type === "button")
945
- this.renderButton(e);
946
- else if (e.type === "select")
947
- this.renderSelect(e);
948
- else if (e.type === "input")
949
- this.renderInput(e);
950
- else if (e.type === "color-picker")
951
- this.renderColorPicker(e);
952
- else if (e.type === "divider") {
953
- const t = document.createElement("div");
954
- t.classList.add("te-divider"), this.container.appendChild(t);
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", (o) => {
961
- if (o.preventDefault(), e.command === "insertEmoji") {
962
- this.activePicker ? this.activePicker.close() : (this.activePicker = new G(
963
- (i) => {
964
- this.editor.execute("insertText", i);
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 i = window.prompt("Enter the URL");
974
- i && this.editor.createLink(i);
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 i = document.createElement("input");
979
- i.type = "file", i.accept = "image/*", i.style.display = "none", i.addEventListener("change", (a) => {
980
- const s = a.target.files?.[0];
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(i);
989
- }), document.body.appendChild(i), i.click();
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 o = () => {
999
- const a = window.getSelection();
1000
- if (a && a.rangeCount > 0) {
1001
- const n = a.getRangeAt(0);
1002
- this.editor.el.contains(n.commonAncestorContainer) && (this.savedRange = n.cloneRange());
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", o), t.addEventListener("focus", o);
1006
- const i = () => {
1007
- let a = parseInt(t.value, 10);
1008
- if (!isNaN(a) && (a = Math.max(1, Math.min(100, a)), t.value = a.toString(), e.command === "fontSize")) {
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 n = this.editor.setStyle("font-size", `${a}px`, this.savedRange);
1011
- n && (this.savedRange = n);
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", `${a}px`);
1284
+ this.editor.setStyle("font-size", `${n}px`);
1014
1285
  t.focus();
1015
1286
  }
1016
1287
  };
1017
- t.addEventListener("input", i), t.addEventListener("keydown", (a) => {
1018
- a.key === "Enter" && (i(), this.editor.focus());
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((o) => {
1024
- const i = document.createElement("option");
1025
- i.value = o.value, i.textContent = o.label, t.appendChild(i);
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 o = t.value;
1028
- this.savedRange && this.editor.selection.restoreSelection(this.savedRange), e.command === "formatBlock" ? this.editor.execute(e.command, o) : e.command === "fontFamily" ? this.editor.setStyle("font-family", o) : e.command === "lineHeight" && this.editor.setStyle("line-height", o), this.editor.focus();
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 i = document.createElement("div");
1037
- i.classList.add("te-button", "te-color-icon"), i.innerHTML = e.icon;
1038
- const a = document.createElement("div");
1039
- a.classList.add("te-color-indicator"), a.style.backgroundColor = e.value || "#000000", i.appendChild(a), t.appendChild(i);
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 o = document.createElement("input");
1042
- o.type = "color", o.classList.add("te-color-picker-input"), e.icon || (o.classList.add("te-color-picker"), o.title = e.title), o.value = e.value || "#000000", o.addEventListener("mousedown", () => {
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
- }), o.addEventListener("input", () => {
1315
+ }), i.addEventListener("input", () => {
1045
1316
  if (e.icon) {
1046
- const i = t.querySelector(".te-color-indicator");
1047
- i && (i.style.backgroundColor = o.value);
1317
+ const o = t.querySelector(".te-color-indicator");
1318
+ o && (o.style.backgroundColor = i.value);
1048
1319
  }
1049
- }), o.addEventListener("change", () => {
1050
- this.savedRange && this.editor.selection.restoreSelection(this.savedRange), e.command && this.editor.execute(e.command, o.value), this.editor.focus();
1051
- }), t.appendChild(o), this.container.appendChild(e.icon ? t : o);
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((o) => {
1060
- if (o.type === "button") {
1061
- const i = e[t++];
1062
- o.command && document.queryCommandState(o.command) ? i.classList.add("active") : i.classList.remove("active");
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 $ extends u {
1347
+ class V extends p {
1068
1348
  toolbar;
1069
1349
  constructor(e, t = {}) {
1070
- super(e, t), this.toolbar = new q(this), this.container.insertBefore(this.toolbar.el, this.editableElement), setTimeout(() => {
1071
- console.log("TOOLBAR HTML:", this.toolbar.el.outerHTML), console.log("CONTAINER HTML:", this.container.outerHTML);
1072
- }, 1e3);
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
- u as CoreEditor,
1077
- y as SelectionManager,
1078
- $ as TestEditor,
1079
- q as Toolbar
1378
+ p as CoreEditor,
1379
+ u as HistoryManager,
1380
+ h as SelectionManager,
1381
+ V as TestEditor,
1382
+ U as Toolbar
1080
1383
  };