leksy-editor 2.0.0 → 2.1.0

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/constant.js CHANGED
@@ -390,6 +390,7 @@ const CSS = {
390
390
  overflow-y: auto;
391
391
  z-index: 9999;
392
392
  color: var(--text-color);
393
+ height: 100vh;
393
394
  }
394
395
 
395
396
  .tabs-container .tab-header {
@@ -401,14 +402,33 @@ const CSS = {
401
402
  align-items: center;
402
403
  border-bottom: 1px solid var(--base-white-dark);
403
404
  }
404
- .tabs-container .tab-header .tab-add-button {
405
+ .tabs-container .tab-header .tab-add-button,
406
+ .tabs-container .tab-header .tab-expand-button {
405
407
  cursor: pointer;
406
408
  border: none;
407
409
  background: transparent;
410
+ width: 24px;
411
+ height: 24px;
408
412
  }
409
- .tabs-container .tab-header .tab-add-button svg {
413
+ .tabs-container .tab-header .tab-expand-button {
414
+ display: none;
415
+ }
416
+ .tabs-container .tab-header .tab-add-button svg,
417
+ .tabs-container .tab-header .tab-expand-button svg {
410
418
  fill: var(--text-color);
411
419
  }
420
+ .tabs-container .tab-header .tab-expand-button svg {
421
+ margin-bottom: 4px;
422
+ rotate: 180deg;
423
+ }
424
+ .tabs-container .tab-header .tab-expand-button.show svg {
425
+ rotate: 0deg;
426
+ }
427
+
428
+ .tabs-container .tab-items {
429
+ overflow-y: auto;
430
+ transition: max-height 0.3s ease, opacity 0.2s ease;
431
+ }
412
432
 
413
433
  .tabs-container .tab-items .tab-item {
414
434
  padding: 8px;
@@ -515,6 +535,7 @@ const CSS = {
515
535
  box-shadow: 0 2px 5px var(--shadow);
516
536
  z-index: 10001;
517
537
  max-height: 200px;
538
+ overflow-y: auto;
518
539
  }
519
540
  #leksy-editor-tab-context-menu .tab-submenu .tab-menu-item {
520
541
  text-overflow: ellipsis;
@@ -529,7 +550,35 @@ const CSS = {
529
550
  height: 14px;
530
551
  margin-right: 4px;
531
552
  }
532
- `
553
+ `,
554
+ IFRAME_MEDIA_QUERY: `
555
+ @media only screen and (max-width: 991px) {
556
+ .tabs-container {
557
+ position: fixed;
558
+ outline: 1px solid grey;
559
+ width: 100%;
560
+ bottom: 0px;
561
+ border-radius: 16px 16px 0 0;
562
+ }
563
+ .tabs-container {
564
+ height: unset;
565
+ }
566
+ .tabs-container .tab-items {
567
+ max-height: 0;
568
+ opacity: 0;
569
+ }
570
+ .tabs-container .tab-header .tab-expand-button {
571
+ display: inline-block;
572
+ }
573
+ .tabs-container .tab-items.show {
574
+ max-height: 30vh;
575
+ opacity: 1;
576
+ }
577
+ .tabs-container .tab-items .tab-item .tab-dropdown {
578
+ visibility: visible;
579
+ }
580
+ }
581
+ `,
533
582
  }
534
583
 
535
584
  const RESIZER_PLUGINS = [
package/index.js CHANGED
@@ -641,6 +641,7 @@ class LeksyEditor {
641
641
  ${CSS.IFRAME_EDITOR}
642
642
  ${CSS.IFRAME_RESIZER}
643
643
  ${CSS.IFRAME_TAB}
644
+ ${CSS.IFRAME_MEDIA_QUERY}
644
645
  ${options.iframeStyle || ''}
645
646
  </style>
646
647
  </head>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leksy-editor",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Leksy Editor is an alternative to traditional WYSIWYG editors, designed primarily for creating mail templates, blogs, and documents without any content manipulation.",
5
5
  "main": "index.js",
6
6
  "directories": {
package/tab.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { v4 as uuidv4 } from 'uuid';
2
2
  import { SVG } from "./constant";
3
- import { insertOutlinesItems } from './utilities';
3
+ import { destroyAnchorPopover, destroyImageResizer, destroyTableEditPlugin, insertOutlinesItems } from './utilities';
4
4
 
5
5
  const initTabs = (core, options) => {
6
6
  if (!options.showTabs) return;
@@ -133,7 +133,7 @@ const renderTabs = (core, options) => {
133
133
  renderTabs(core, options);
134
134
  }
135
135
  switchTab(core, tab.tabId, options);
136
- }, 200);
136
+ }, 250);
137
137
  };
138
138
 
139
139
  tabItem.ondblclick = () => {
@@ -210,8 +210,12 @@ const renderTabs = (core, options) => {
210
210
 
211
211
  const tabListContainer = document.createElement('div');
212
212
  tabListContainer.className = 'tab-items'
213
+ tabListContainer.addEventListener('scroll', () => {
214
+ core.state.tabItemsScrollHeight = tabListContainer.scrollTop;
215
+ });
213
216
  renderLevel(core.state.tabs, tabListContainer, 0);
214
217
  core.elements.tabsContainer.appendChild(tabListContainer);
218
+ tabListContainer.scrollTop = core.state.tabItemsScrollHeight || 0;
215
219
 
216
220
  const tabHeader = document.createElement('span');
217
221
  tabHeader.className = 'tab-header';
@@ -219,13 +223,32 @@ const renderTabs = (core, options) => {
219
223
  const headerTitle = document.createElement('span');
220
224
  headerTitle.innerHTML = "Tabs";
221
225
 
226
+ const rightDiv = document.createElement('div')
222
227
  const addBtn = document.createElement('button');
223
228
  addBtn.className = 'tab-add-button'
224
229
  addBtn.type = 'button';
225
230
  addBtn.innerHTML = SVG.PLUS;
226
231
  addBtn.onclick = () => createTab(core, options);
232
+
233
+ const expandBtn = document.createElement('button');
234
+ expandBtn.className = 'tab-expand-button'
235
+ expandBtn.type = 'button';
236
+ expandBtn.innerHTML = SVG.ARROW_DOWN;
237
+ expandBtn.onclick = () => {
238
+ core.state.isTabExpandInResponsive = !core.state.isTabExpandInResponsive
239
+ expandBtn.classList.toggle('show')
240
+ tabListContainer.classList.toggle('show')
241
+ }
242
+ if (core.state.isTabExpandInResponsive) {
243
+ expandBtn.classList.add('show')
244
+ tabListContainer.classList.add('show')
245
+ }
246
+
247
+ rightDiv.appendChild(addBtn);
248
+ rightDiv.appendChild(expandBtn);
249
+
227
250
  tabHeader.appendChild(headerTitle);
228
- tabHeader.appendChild(addBtn);
251
+ tabHeader.appendChild(rightDiv);
229
252
  core.elements.tabsContainer.prepend(tabHeader);
230
253
  }
231
254
 
@@ -235,8 +258,10 @@ const showContextMenu = (core, tab, event, level, options) => {
235
258
 
236
259
  const menu = document.createElement('div');
237
260
  menu.id = 'leksy-editor-tab-context-menu';
238
- menu.style.left = `${event.clientX}px`;
239
- menu.style.top = `${event.clientY}px`;
261
+
262
+ // temporary visibility to measure
263
+ menu.style.visibility = 'hidden';
264
+ menu.style.display = 'block';
240
265
 
241
266
  const closeMenu = (e) => {
242
267
  if (!menu.contains(e.target)) {
@@ -281,17 +306,43 @@ const showContextMenu = (core, tab, event, level, options) => {
281
306
  item.onmouseenter = () => {
282
307
  submenu = document.createElement('div');
283
308
  submenu.className = 'tab-submenu';
309
+ submenu.style.position = 'absolute';
310
+ submenu.style.left = '100%';
311
+ submenu.style.top = '0';
312
+ submenu.style.visibility = 'hidden';
284
313
 
285
314
  items.forEach(subItem => {
286
315
  const subOption = createOption(subItem.tabTitle, SVG.TABLE_OF_CONTENT, () => {
287
- moveTabInto(core, tab.tabId, subItem.tabId);
316
+ moveTabInto(core, tab.tabId, subItem.tabId, options);
288
317
  });
318
+
289
319
  if (subItem.level > 0) {
290
320
  subOption.style.paddingLeft = `${16 + subItem.level * 10}px`;
291
321
  }
322
+
292
323
  submenu.appendChild(subOption);
293
324
  });
325
+
294
326
  item.appendChild(submenu);
327
+
328
+ const rect = submenu.getBoundingClientRect();
329
+ const itemRect = item.getBoundingClientRect();
330
+
331
+ const viewportWidth = core.elements.iframeWindow.documentElement.clientWidth;
332
+ const viewportHeight = core.elements.iframeWindow.documentElement.clientHeight;
333
+
334
+ // if no space on right → open left
335
+ if (rect.right > viewportWidth) {
336
+ submenu.style.left = 'auto';
337
+ submenu.style.right = '100%';
338
+ }
339
+
340
+ // if no space bottom → shift up
341
+ if (rect.bottom > viewportHeight) {
342
+ submenu.style.top = `${viewportHeight - itemRect.top - rect.height}px`;
343
+ }
344
+
345
+ submenu.style.visibility = 'visible';
295
346
  };
296
347
 
297
348
  item.onmouseleave = () => {
@@ -341,7 +392,7 @@ const showContextMenu = (core, tab, event, level, options) => {
341
392
  menu.appendChild(createOption('Move out', SVG.DELETE, () => outdentTab(core, tab.tabId, options)));
342
393
  }
343
394
 
344
- menu.appendChild(createOption(tab.showOutlines ? 'Hide outline' : 'Show outline', SVG.LIST_BULLETS, () => {
395
+ menu.appendChild(createOption(tab.showOutlines ? 'Hide outlines' : 'Show outlines', SVG.LIST_BULLETS, () => {
345
396
  tab.showOutlines = !tab.showOutlines;
346
397
  renderTabs(core, options);
347
398
  if (tab.showOutlines) switchTab(core, tab.tabId, options);
@@ -349,6 +400,32 @@ const showContextMenu = (core, tab, event, level, options) => {
349
400
 
350
401
  core.elements.iframeWindow.body.appendChild(menu);
351
402
  core.elements.iframeWindow.addEventListener('click', closeMenu);
403
+
404
+ const rect = menu.getBoundingClientRect();
405
+ const menuWidth = rect.width;
406
+ const menuHeight = rect.height;
407
+
408
+ const viewportWidth = core.elements.iframeWindow.documentElement.clientWidth;
409
+ const viewportHeight = core.elements.iframeWindow.documentElement.clientHeight;
410
+
411
+ let left = event.clientX;
412
+ let top = event.clientY;
413
+
414
+ // if not enough space on right → open to left
415
+ if (left + menuWidth > viewportWidth) {
416
+ left = event.clientX - menuWidth;
417
+ }
418
+
419
+ // if not enough space on bottom → open above
420
+ if (top + menuHeight > viewportHeight) {
421
+ top = event.clientY - menuHeight;
422
+ }
423
+
424
+ menu.style.left = `${left}px`;
425
+ menu.style.top = `${top}px`;
426
+ menu.style.visibility = 'visible';
427
+
428
+
352
429
  };
353
430
 
354
431
  const createTab = (core, options) => {
@@ -368,6 +445,10 @@ const createTab = (core, options) => {
368
445
  const switchTab = (core, tabId, options) => {
369
446
  if (core.state.currentTabId === tabId) return;
370
447
 
448
+ destroyImageResizer(options, core)
449
+ destroyTableEditPlugin(options, core)
450
+ destroyAnchorPopover(core)
451
+
371
452
  const currentTab = findTab(core.state.tabs, core.state.currentTabId);
372
453
  if (currentTab) {
373
454
  currentTab.content = core.elements.editor.innerHTML;