leksy-editor 2.0.0 → 2.1.1
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 +55 -3
- package/index.js +1 -0
- package/package.json +1 -1
- package/tab.js +91 -7
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,36 @@ 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-
|
|
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
|
+
}
|
|
431
|
+
|
|
432
|
+
.tabs-container .tab-items.animate {
|
|
433
|
+
transition: max-height 0.3s ease, opacity 0.2s ease;
|
|
434
|
+
}
|
|
412
435
|
|
|
413
436
|
.tabs-container .tab-items .tab-item {
|
|
414
437
|
padding: 8px;
|
|
@@ -515,6 +538,7 @@ const CSS = {
|
|
|
515
538
|
box-shadow: 0 2px 5px var(--shadow);
|
|
516
539
|
z-index: 10001;
|
|
517
540
|
max-height: 200px;
|
|
541
|
+
overflow-y: auto;
|
|
518
542
|
}
|
|
519
543
|
#leksy-editor-tab-context-menu .tab-submenu .tab-menu-item {
|
|
520
544
|
text-overflow: ellipsis;
|
|
@@ -529,7 +553,35 @@ const CSS = {
|
|
|
529
553
|
height: 14px;
|
|
530
554
|
margin-right: 4px;
|
|
531
555
|
}
|
|
532
|
-
|
|
556
|
+
`,
|
|
557
|
+
IFRAME_MEDIA_QUERY: `
|
|
558
|
+
@media only screen and (max-width: 991px) {
|
|
559
|
+
.tabs-container {
|
|
560
|
+
position: fixed;
|
|
561
|
+
outline: 1px solid grey;
|
|
562
|
+
width: 100%;
|
|
563
|
+
bottom: 0px;
|
|
564
|
+
border-radius: 16px 16px 0 0;
|
|
565
|
+
}
|
|
566
|
+
.tabs-container {
|
|
567
|
+
height: unset;
|
|
568
|
+
}
|
|
569
|
+
.tabs-container .tab-items {
|
|
570
|
+
max-height: 0;
|
|
571
|
+
opacity: 0;
|
|
572
|
+
}
|
|
573
|
+
.tabs-container .tab-header .tab-expand-button {
|
|
574
|
+
display: inline-block;
|
|
575
|
+
}
|
|
576
|
+
.tabs-container .tab-items.show {
|
|
577
|
+
max-height: 30vh;
|
|
578
|
+
opacity: 1;
|
|
579
|
+
}
|
|
580
|
+
.tabs-container .tab-items .tab-item .tab-dropdown {
|
|
581
|
+
visibility: visible;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
`,
|
|
533
585
|
}
|
|
534
586
|
|
|
535
587
|
const RESIZER_PLUGINS = [
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leksy-editor",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
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
|
-
},
|
|
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,35 @@ 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
|
+
requestAnimationFrame(() => {
|
|
243
|
+
tabListContainer.classList.add('animate');
|
|
244
|
+
});
|
|
245
|
+
if (core.state.isTabExpandInResponsive) {
|
|
246
|
+
expandBtn.classList.add('show')
|
|
247
|
+
tabListContainer.classList.add('show')
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
rightDiv.appendChild(addBtn);
|
|
251
|
+
rightDiv.appendChild(expandBtn);
|
|
252
|
+
|
|
227
253
|
tabHeader.appendChild(headerTitle);
|
|
228
|
-
tabHeader.appendChild(
|
|
254
|
+
tabHeader.appendChild(rightDiv);
|
|
229
255
|
core.elements.tabsContainer.prepend(tabHeader);
|
|
230
256
|
}
|
|
231
257
|
|
|
@@ -235,8 +261,10 @@ const showContextMenu = (core, tab, event, level, options) => {
|
|
|
235
261
|
|
|
236
262
|
const menu = document.createElement('div');
|
|
237
263
|
menu.id = 'leksy-editor-tab-context-menu';
|
|
238
|
-
|
|
239
|
-
|
|
264
|
+
|
|
265
|
+
// temporary visibility to measure
|
|
266
|
+
menu.style.visibility = 'hidden';
|
|
267
|
+
menu.style.display = 'block';
|
|
240
268
|
|
|
241
269
|
const closeMenu = (e) => {
|
|
242
270
|
if (!menu.contains(e.target)) {
|
|
@@ -281,17 +309,43 @@ const showContextMenu = (core, tab, event, level, options) => {
|
|
|
281
309
|
item.onmouseenter = () => {
|
|
282
310
|
submenu = document.createElement('div');
|
|
283
311
|
submenu.className = 'tab-submenu';
|
|
312
|
+
submenu.style.position = 'absolute';
|
|
313
|
+
submenu.style.left = '100%';
|
|
314
|
+
submenu.style.top = '0';
|
|
315
|
+
submenu.style.visibility = 'hidden';
|
|
284
316
|
|
|
285
317
|
items.forEach(subItem => {
|
|
286
318
|
const subOption = createOption(subItem.tabTitle, SVG.TABLE_OF_CONTENT, () => {
|
|
287
|
-
moveTabInto(core, tab.tabId, subItem.tabId);
|
|
319
|
+
moveTabInto(core, tab.tabId, subItem.tabId, options);
|
|
288
320
|
});
|
|
321
|
+
|
|
289
322
|
if (subItem.level > 0) {
|
|
290
323
|
subOption.style.paddingLeft = `${16 + subItem.level * 10}px`;
|
|
291
324
|
}
|
|
325
|
+
|
|
292
326
|
submenu.appendChild(subOption);
|
|
293
327
|
});
|
|
328
|
+
|
|
294
329
|
item.appendChild(submenu);
|
|
330
|
+
|
|
331
|
+
const rect = submenu.getBoundingClientRect();
|
|
332
|
+
const itemRect = item.getBoundingClientRect();
|
|
333
|
+
|
|
334
|
+
const viewportWidth = core.elements.iframeWindow.documentElement.clientWidth;
|
|
335
|
+
const viewportHeight = core.elements.iframeWindow.documentElement.clientHeight;
|
|
336
|
+
|
|
337
|
+
// if no space on right → open left
|
|
338
|
+
if (rect.right > viewportWidth) {
|
|
339
|
+
submenu.style.left = 'auto';
|
|
340
|
+
submenu.style.right = '100%';
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// if no space bottom → shift up
|
|
344
|
+
if (rect.bottom > viewportHeight) {
|
|
345
|
+
submenu.style.top = `${viewportHeight - itemRect.top - rect.height}px`;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
submenu.style.visibility = 'visible';
|
|
295
349
|
};
|
|
296
350
|
|
|
297
351
|
item.onmouseleave = () => {
|
|
@@ -341,7 +395,7 @@ const showContextMenu = (core, tab, event, level, options) => {
|
|
|
341
395
|
menu.appendChild(createOption('Move out', SVG.DELETE, () => outdentTab(core, tab.tabId, options)));
|
|
342
396
|
}
|
|
343
397
|
|
|
344
|
-
menu.appendChild(createOption(tab.showOutlines ? 'Hide
|
|
398
|
+
menu.appendChild(createOption(tab.showOutlines ? 'Hide outlines' : 'Show outlines', SVG.LIST_BULLETS, () => {
|
|
345
399
|
tab.showOutlines = !tab.showOutlines;
|
|
346
400
|
renderTabs(core, options);
|
|
347
401
|
if (tab.showOutlines) switchTab(core, tab.tabId, options);
|
|
@@ -349,6 +403,32 @@ const showContextMenu = (core, tab, event, level, options) => {
|
|
|
349
403
|
|
|
350
404
|
core.elements.iframeWindow.body.appendChild(menu);
|
|
351
405
|
core.elements.iframeWindow.addEventListener('click', closeMenu);
|
|
406
|
+
|
|
407
|
+
const rect = menu.getBoundingClientRect();
|
|
408
|
+
const menuWidth = rect.width;
|
|
409
|
+
const menuHeight = rect.height;
|
|
410
|
+
|
|
411
|
+
const viewportWidth = core.elements.iframeWindow.documentElement.clientWidth;
|
|
412
|
+
const viewportHeight = core.elements.iframeWindow.documentElement.clientHeight;
|
|
413
|
+
|
|
414
|
+
let left = event.clientX;
|
|
415
|
+
let top = event.clientY;
|
|
416
|
+
|
|
417
|
+
// if not enough space on right → open to left
|
|
418
|
+
if (left + menuWidth > viewportWidth) {
|
|
419
|
+
left = event.clientX - menuWidth;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// if not enough space on bottom → open above
|
|
423
|
+
if (top + menuHeight > viewportHeight) {
|
|
424
|
+
top = event.clientY - menuHeight;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
menu.style.left = `${left}px`;
|
|
428
|
+
menu.style.top = `${top}px`;
|
|
429
|
+
menu.style.visibility = 'visible';
|
|
430
|
+
|
|
431
|
+
|
|
352
432
|
};
|
|
353
433
|
|
|
354
434
|
const createTab = (core, options) => {
|
|
@@ -368,6 +448,10 @@ const createTab = (core, options) => {
|
|
|
368
448
|
const switchTab = (core, tabId, options) => {
|
|
369
449
|
if (core.state.currentTabId === tabId) return;
|
|
370
450
|
|
|
451
|
+
destroyImageResizer(options, core)
|
|
452
|
+
destroyTableEditPlugin(options, core)
|
|
453
|
+
destroyAnchorPopover(core)
|
|
454
|
+
|
|
371
455
|
const currentTab = findTab(core.state.tabs, core.state.currentTabId);
|
|
372
456
|
if (currentTab) {
|
|
373
457
|
currentTab.content = core.elements.editor.innerHTML;
|