@mdaemon/html-editor 1.0.10 → 1.0.12

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/dist/index.d.ts CHANGED
@@ -471,6 +471,7 @@ export declare class Toolbar {
471
471
  private state;
472
472
  private buttonElements;
473
473
  private dropdowns;
474
+ private bodyMenus;
474
475
  private charMap;
475
476
  private emojiPicker;
476
477
  private imageUpload;
@@ -499,6 +500,11 @@ export declare class Toolbar {
499
500
  private createFontSizeDropdown;
500
501
  private createLineHeightDropdown;
501
502
  private createTemplateDropdown;
503
+ /**
504
+ * Position a fixed-position menu below its trigger button.
505
+ * Uses getBoundingClientRect so the menu escapes any overflow:hidden ancestor.
506
+ */
507
+ private positionMenu;
502
508
  private createDropdown;
503
509
  private createColorPicker;
504
510
  private bindEvents;
@@ -516,6 +522,7 @@ export declare class Toolbar {
516
522
  private toggleFullscreen;
517
523
  rebuild(): void;
518
524
  destroy(): void;
525
+ private removeBodyMenus;
519
526
  }
520
527
 
521
528
  export declare interface ToolbarButtonAPI {
package/dist/index.js CHANGED
@@ -42788,6 +42788,7 @@ class Toolbar {
42788
42788
  state;
42789
42789
  buttonElements = /* @__PURE__ */ new Map();
42790
42790
  dropdowns = /* @__PURE__ */ new Map();
42791
+ bodyMenus = [];
42791
42792
  charMap = null;
42792
42793
  emojiPicker = null;
42793
42794
  imageUpload = null;
@@ -43141,6 +43142,15 @@ class Toolbar {
43141
43142
  }
43142
43143
  });
43143
43144
  }
43145
+ /**
43146
+ * Position a fixed-position menu below its trigger button.
43147
+ * Uses getBoundingClientRect so the menu escapes any overflow:hidden ancestor.
43148
+ */
43149
+ positionMenu(button, menu) {
43150
+ const rect = button.getBoundingClientRect();
43151
+ menu.style.top = `${rect.bottom}px`;
43152
+ menu.style.left = `${rect.left}px`;
43153
+ }
43144
43154
  createDropdown(name, label, options, onSelect, getCurrentValue) {
43145
43155
  const wrapper = document.createElement("div");
43146
43156
  wrapper.className = "md-toolbar-dropdown";
@@ -43151,7 +43161,7 @@ class Toolbar {
43151
43161
  button.title = label;
43152
43162
  button.innerHTML = `
43153
43163
  <span class="md-toolbar-dropdown-label">${label}</span>
43154
- <span class="md-toolbar-dropdown-arrow">▼</span>
43164
+ <span class="md-toolbar-dropdown-arrow"><svg width="8" height="8" viewBox="0 0 8 8" fill="currentColor"><path d="M0 2l4 4 4-4z"/></svg></span>
43155
43165
  `;
43156
43166
  const menu = document.createElement("div");
43157
43167
  menu.className = "md-toolbar-dropdown-menu";
@@ -43183,14 +43193,18 @@ class Toolbar {
43183
43193
  e.stopPropagation();
43184
43194
  this.dropdowns.forEach((dropdown, key) => {
43185
43195
  if (key !== name) {
43186
- dropdown.classList.remove("md-toolbar-dropdown-open");
43187
- const m = dropdown.querySelector(".md-toolbar-dropdown-menu");
43188
- if (m) m.style.display = "none";
43196
+ dropdown.classList.remove("md-toolbar-dropdown-open", "md-toolbar-colorpicker-open");
43189
43197
  }
43190
43198
  });
43199
+ this.bodyMenus.forEach((m) => {
43200
+ if (m !== menu) m.style.display = "none";
43201
+ });
43191
43202
  const isOpen = menu.style.display !== "none";
43192
43203
  menu.style.display = isOpen ? "none" : "block";
43193
43204
  wrapper.classList.toggle("md-toolbar-dropdown-open", !isOpen);
43205
+ if (!isOpen) {
43206
+ this.positionMenu(button, menu);
43207
+ }
43194
43208
  if (!isOpen && getCurrentValue) {
43195
43209
  const currentVal = getCurrentValue();
43196
43210
  menu.querySelectorAll(".md-toolbar-dropdown-item").forEach((item) => {
@@ -43200,7 +43214,8 @@ class Toolbar {
43200
43214
  }
43201
43215
  });
43202
43216
  wrapper.appendChild(button);
43203
- wrapper.appendChild(menu);
43217
+ document.body.appendChild(menu);
43218
+ this.bodyMenus.push(menu);
43204
43219
  this.dropdowns.set(name, wrapper);
43205
43220
  return wrapper;
43206
43221
  }
@@ -43215,7 +43230,7 @@ class Toolbar {
43215
43230
  button.innerHTML = `
43216
43231
  <span class="md-toolbar-colorpicker-icon md-icon-${name}">A</span>
43217
43232
  <span class="md-toolbar-colorpicker-preview" style="background-color: ${name === "forecolor" ? "#000" : "#ff0"}"></span>
43218
- <span class="md-toolbar-dropdown-arrow">▼</span>
43233
+ <span class="md-toolbar-dropdown-arrow"><svg width="8" height="8" viewBox="0 0 8 8" fill="currentColor"><path d="M0 2l4 4 4-4z"/></svg></span>
43219
43234
  `;
43220
43235
  const menu = document.createElement("div");
43221
43236
  menu.className = "md-toolbar-colorpicker-menu";
@@ -43266,12 +43281,24 @@ class Toolbar {
43266
43281
  button.addEventListener("click", (e) => {
43267
43282
  e.preventDefault();
43268
43283
  e.stopPropagation();
43284
+ this.dropdowns.forEach((dropdown, key) => {
43285
+ if (key !== name) {
43286
+ dropdown.classList.remove("md-toolbar-dropdown-open", "md-toolbar-colorpicker-open");
43287
+ }
43288
+ });
43289
+ this.bodyMenus.forEach((m) => {
43290
+ if (m !== menu) m.style.display = "none";
43291
+ });
43269
43292
  const isOpen = menu.style.display !== "none";
43270
43293
  menu.style.display = isOpen ? "none" : "block";
43271
43294
  wrapper.classList.toggle("md-toolbar-colorpicker-open", !isOpen);
43295
+ if (!isOpen) {
43296
+ this.positionMenu(button, menu);
43297
+ }
43272
43298
  });
43273
43299
  wrapper.appendChild(button);
43274
- wrapper.appendChild(menu);
43300
+ document.body.appendChild(menu);
43301
+ this.bodyMenus.push(menu);
43275
43302
  this.dropdowns.set(name, wrapper);
43276
43303
  return wrapper;
43277
43304
  }
@@ -43279,7 +43306,9 @@ class Toolbar {
43279
43306
  this.unbindEvents();
43280
43307
  this.boundClickHandler = (e) => {
43281
43308
  const target = e.target;
43282
- if (!target.closest(".md-toolbar-dropdown, .md-toolbar-colorpicker")) {
43309
+ const inDropdown = target.closest(".md-toolbar-dropdown, .md-toolbar-colorpicker");
43310
+ const inBodyMenu = this.bodyMenus.some((m) => m.contains(target));
43311
+ if (!inDropdown && !inBodyMenu) {
43283
43312
  this.closeAllDropdowns();
43284
43313
  }
43285
43314
  };
@@ -43323,10 +43352,9 @@ class Toolbar {
43323
43352
  closeAllDropdowns() {
43324
43353
  this.dropdowns.forEach((dropdown) => {
43325
43354
  dropdown.classList.remove("md-toolbar-dropdown-open", "md-toolbar-colorpicker-open");
43326
- const menu = dropdown.querySelector(".md-toolbar-dropdown-menu, .md-toolbar-colorpicker-menu");
43327
- if (menu) {
43328
- menu.style.display = "none";
43329
- }
43355
+ });
43356
+ this.bodyMenus.forEach((menu) => {
43357
+ menu.style.display = "none";
43330
43358
  });
43331
43359
  }
43332
43360
  startStateUpdates() {
@@ -43492,6 +43520,7 @@ class Toolbar {
43492
43520
  this.searchReplace = null;
43493
43521
  this.buttonElements.clear();
43494
43522
  this.dropdowns.clear();
43523
+ this.removeBodyMenus();
43495
43524
  this.buttonsEl = null;
43496
43525
  this.toggleBtn = null;
43497
43526
  this.render();
@@ -43512,8 +43541,15 @@ class Toolbar {
43512
43541
  this.searchReplace?.destroy();
43513
43542
  this.buttonElements.clear();
43514
43543
  this.dropdowns.clear();
43544
+ this.removeBodyMenus();
43515
43545
  this.container.innerHTML = "";
43516
43546
  }
43547
+ removeBodyMenus() {
43548
+ this.bodyMenus.forEach((menu) => {
43549
+ menu.remove();
43550
+ });
43551
+ this.bodyMenus = [];
43552
+ }
43517
43553
  }
43518
43554
  const SVG_ALIGN_LEFT = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="15" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>';
43519
43555
  const SVG_ALIGN_CENTER = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="6" y1="12" x2="18" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>';
package/dist/index.mjs CHANGED
@@ -42786,6 +42786,7 @@ class Toolbar {
42786
42786
  state;
42787
42787
  buttonElements = /* @__PURE__ */ new Map();
42788
42788
  dropdowns = /* @__PURE__ */ new Map();
42789
+ bodyMenus = [];
42789
42790
  charMap = null;
42790
42791
  emojiPicker = null;
42791
42792
  imageUpload = null;
@@ -43139,6 +43140,15 @@ class Toolbar {
43139
43140
  }
43140
43141
  });
43141
43142
  }
43143
+ /**
43144
+ * Position a fixed-position menu below its trigger button.
43145
+ * Uses getBoundingClientRect so the menu escapes any overflow:hidden ancestor.
43146
+ */
43147
+ positionMenu(button, menu) {
43148
+ const rect = button.getBoundingClientRect();
43149
+ menu.style.top = `${rect.bottom}px`;
43150
+ menu.style.left = `${rect.left}px`;
43151
+ }
43142
43152
  createDropdown(name, label, options, onSelect, getCurrentValue) {
43143
43153
  const wrapper = document.createElement("div");
43144
43154
  wrapper.className = "md-toolbar-dropdown";
@@ -43149,7 +43159,7 @@ class Toolbar {
43149
43159
  button.title = label;
43150
43160
  button.innerHTML = `
43151
43161
  <span class="md-toolbar-dropdown-label">${label}</span>
43152
- <span class="md-toolbar-dropdown-arrow">▼</span>
43162
+ <span class="md-toolbar-dropdown-arrow"><svg width="8" height="8" viewBox="0 0 8 8" fill="currentColor"><path d="M0 2l4 4 4-4z"/></svg></span>
43153
43163
  `;
43154
43164
  const menu = document.createElement("div");
43155
43165
  menu.className = "md-toolbar-dropdown-menu";
@@ -43181,14 +43191,18 @@ class Toolbar {
43181
43191
  e.stopPropagation();
43182
43192
  this.dropdowns.forEach((dropdown, key) => {
43183
43193
  if (key !== name) {
43184
- dropdown.classList.remove("md-toolbar-dropdown-open");
43185
- const m = dropdown.querySelector(".md-toolbar-dropdown-menu");
43186
- if (m) m.style.display = "none";
43194
+ dropdown.classList.remove("md-toolbar-dropdown-open", "md-toolbar-colorpicker-open");
43187
43195
  }
43188
43196
  });
43197
+ this.bodyMenus.forEach((m) => {
43198
+ if (m !== menu) m.style.display = "none";
43199
+ });
43189
43200
  const isOpen = menu.style.display !== "none";
43190
43201
  menu.style.display = isOpen ? "none" : "block";
43191
43202
  wrapper.classList.toggle("md-toolbar-dropdown-open", !isOpen);
43203
+ if (!isOpen) {
43204
+ this.positionMenu(button, menu);
43205
+ }
43192
43206
  if (!isOpen && getCurrentValue) {
43193
43207
  const currentVal = getCurrentValue();
43194
43208
  menu.querySelectorAll(".md-toolbar-dropdown-item").forEach((item) => {
@@ -43198,7 +43212,8 @@ class Toolbar {
43198
43212
  }
43199
43213
  });
43200
43214
  wrapper.appendChild(button);
43201
- wrapper.appendChild(menu);
43215
+ document.body.appendChild(menu);
43216
+ this.bodyMenus.push(menu);
43202
43217
  this.dropdowns.set(name, wrapper);
43203
43218
  return wrapper;
43204
43219
  }
@@ -43213,7 +43228,7 @@ class Toolbar {
43213
43228
  button.innerHTML = `
43214
43229
  <span class="md-toolbar-colorpicker-icon md-icon-${name}">A</span>
43215
43230
  <span class="md-toolbar-colorpicker-preview" style="background-color: ${name === "forecolor" ? "#000" : "#ff0"}"></span>
43216
- <span class="md-toolbar-dropdown-arrow">▼</span>
43231
+ <span class="md-toolbar-dropdown-arrow"><svg width="8" height="8" viewBox="0 0 8 8" fill="currentColor"><path d="M0 2l4 4 4-4z"/></svg></span>
43217
43232
  `;
43218
43233
  const menu = document.createElement("div");
43219
43234
  menu.className = "md-toolbar-colorpicker-menu";
@@ -43264,12 +43279,24 @@ class Toolbar {
43264
43279
  button.addEventListener("click", (e) => {
43265
43280
  e.preventDefault();
43266
43281
  e.stopPropagation();
43282
+ this.dropdowns.forEach((dropdown, key) => {
43283
+ if (key !== name) {
43284
+ dropdown.classList.remove("md-toolbar-dropdown-open", "md-toolbar-colorpicker-open");
43285
+ }
43286
+ });
43287
+ this.bodyMenus.forEach((m) => {
43288
+ if (m !== menu) m.style.display = "none";
43289
+ });
43267
43290
  const isOpen = menu.style.display !== "none";
43268
43291
  menu.style.display = isOpen ? "none" : "block";
43269
43292
  wrapper.classList.toggle("md-toolbar-colorpicker-open", !isOpen);
43293
+ if (!isOpen) {
43294
+ this.positionMenu(button, menu);
43295
+ }
43270
43296
  });
43271
43297
  wrapper.appendChild(button);
43272
- wrapper.appendChild(menu);
43298
+ document.body.appendChild(menu);
43299
+ this.bodyMenus.push(menu);
43273
43300
  this.dropdowns.set(name, wrapper);
43274
43301
  return wrapper;
43275
43302
  }
@@ -43277,7 +43304,9 @@ class Toolbar {
43277
43304
  this.unbindEvents();
43278
43305
  this.boundClickHandler = (e) => {
43279
43306
  const target = e.target;
43280
- if (!target.closest(".md-toolbar-dropdown, .md-toolbar-colorpicker")) {
43307
+ const inDropdown = target.closest(".md-toolbar-dropdown, .md-toolbar-colorpicker");
43308
+ const inBodyMenu = this.bodyMenus.some((m) => m.contains(target));
43309
+ if (!inDropdown && !inBodyMenu) {
43281
43310
  this.closeAllDropdowns();
43282
43311
  }
43283
43312
  };
@@ -43321,10 +43350,9 @@ class Toolbar {
43321
43350
  closeAllDropdowns() {
43322
43351
  this.dropdowns.forEach((dropdown) => {
43323
43352
  dropdown.classList.remove("md-toolbar-dropdown-open", "md-toolbar-colorpicker-open");
43324
- const menu = dropdown.querySelector(".md-toolbar-dropdown-menu, .md-toolbar-colorpicker-menu");
43325
- if (menu) {
43326
- menu.style.display = "none";
43327
- }
43353
+ });
43354
+ this.bodyMenus.forEach((menu) => {
43355
+ menu.style.display = "none";
43328
43356
  });
43329
43357
  }
43330
43358
  startStateUpdates() {
@@ -43490,6 +43518,7 @@ class Toolbar {
43490
43518
  this.searchReplace = null;
43491
43519
  this.buttonElements.clear();
43492
43520
  this.dropdowns.clear();
43521
+ this.removeBodyMenus();
43493
43522
  this.buttonsEl = null;
43494
43523
  this.toggleBtn = null;
43495
43524
  this.render();
@@ -43510,8 +43539,15 @@ class Toolbar {
43510
43539
  this.searchReplace?.destroy();
43511
43540
  this.buttonElements.clear();
43512
43541
  this.dropdowns.clear();
43542
+ this.removeBodyMenus();
43513
43543
  this.container.innerHTML = "";
43514
43544
  }
43545
+ removeBodyMenus() {
43546
+ this.bodyMenus.forEach((menu) => {
43547
+ menu.remove();
43548
+ });
43549
+ this.bodyMenus = [];
43550
+ }
43515
43551
  }
43516
43552
  const SVG_ALIGN_LEFT = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="15" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>';
43517
43553
  const SVG_ALIGN_CENTER = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="6" y1="12" x2="18" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>';
package/dist/styles.css CHANGED
@@ -175,14 +175,42 @@
175
175
  }
176
176
 
177
177
  .md-toolbar-dropdown-arrow {
178
- font-size: 8px;
178
+ display: inline-flex;
179
+ align-items: center;
179
180
  opacity: 0.6;
180
181
  }
182
+ .md-toolbar-dropdown-arrow svg {
183
+ display: block;
184
+ width: 8px;
185
+ height: 8px;
186
+ }
187
+
188
+ .md-toolbar-dropdown-menu,
189
+ .md-toolbar-colorpicker-menu {
190
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
191
+ font-size: 14px;
192
+ box-sizing: border-box;
193
+ }
194
+ .md-toolbar-dropdown-menu *, .md-toolbar-dropdown-menu *::before, .md-toolbar-dropdown-menu *::after,
195
+ .md-toolbar-colorpicker-menu *,
196
+ .md-toolbar-colorpicker-menu *::before,
197
+ .md-toolbar-colorpicker-menu *::after {
198
+ box-sizing: border-box;
199
+ }
200
+ .md-toolbar-dropdown-menu button:not([class]),
201
+ .md-toolbar-colorpicker-menu button:not([class]) {
202
+ appearance: none;
203
+ font: inherit;
204
+ color: inherit;
205
+ background: transparent;
206
+ border: none;
207
+ padding: 0;
208
+ margin: 0;
209
+ cursor: pointer;
210
+ }
181
211
 
182
212
  .md-toolbar-dropdown-menu {
183
- position: absolute;
184
- top: 100%;
185
- left: 0;
213
+ position: fixed;
186
214
  min-width: 150px;
187
215
  max-height: 300px;
188
216
  overflow-y: auto;
@@ -195,6 +223,8 @@
195
223
 
196
224
  .md-toolbar-dropdown-item {
197
225
  display: block;
226
+ margin-top: 2px;
227
+ margin-bottom: 2px;
198
228
  width: 100%;
199
229
  padding: 8px 12px;
200
230
  border: none;
@@ -245,15 +275,14 @@
245
275
  }
246
276
 
247
277
  .md-toolbar-colorpicker-menu {
248
- position: absolute;
249
- top: 100%;
250
- left: 0;
278
+ position: fixed;
251
279
  padding: 8px;
252
280
  background: #fff;
253
281
  border: 1px solid #ccc;
254
282
  border-radius: 3px;
255
283
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
256
- z-index: 100;
284
+ z-index: 1000;
285
+ padding: 6px;
257
286
  }
258
287
 
259
288
  .md-toolbar-colorpicker-grid {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mdaemon/html-editor",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "A TinyMCE-compatible HTML editor built on TipTap",
5
5
  "homepage": "https://github.com/mdaemon-technologies/MDHTMLEditor",
6
6
  "repository": {