leksy-editor 2.4.0 → 2.4.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/README.md +4 -4
- package/constant.js +2 -0
- package/index.js +4 -2
- package/package.json +1 -1
- package/plugin.js +25 -18
- package/utilities.js +272 -6
package/README.md
CHANGED
|
@@ -177,10 +177,10 @@ Similarly, Pexels and Tenor can be integrated using `pexels` and `tenor` plugins
|
|
|
177
177
|
|
|
178
178
|
| Function | Description |
|
|
179
179
|
|----------|-------------|
|
|
180
|
-
| `setContents(html)` | Sets the editor's content. |
|
|
181
|
-
| `getContents()` | Returns the
|
|
182
|
-
| `onChange(
|
|
183
|
-
| `onBlur(
|
|
180
|
+
| `setContents(tabs || html)` | Sets the editor's content. |
|
|
181
|
+
| `getContents()` | Returns the array of tabs containing HTML content of the editor. |
|
|
182
|
+
| `onChange(tabs)` | Triggered when content changes. |
|
|
183
|
+
| `onBlur(tabs)` | Triggered when the editor loses focus. |
|
|
184
184
|
| `onAttachment(files)` | Fires when files are attached. |
|
|
185
185
|
| `manipulateImage()` | Custom function for manipulating images. |
|
|
186
186
|
| `handleFilePicker()` | Custom function for file upload, useful for Android and iOS file upload. |
|
package/constant.js
CHANGED
|
@@ -932,6 +932,7 @@ const UNORDERED_LIST_OPTIONS = {
|
|
|
932
932
|
|
|
933
933
|
const RESIZE_MARGIN = 10;
|
|
934
934
|
|
|
935
|
+
const BLOCK_FORMAT_TAGS = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'P'];
|
|
935
936
|
const LIST_STYLES_BY_LEVEL = ['1', 'a', 'i', 'A'];
|
|
936
937
|
const UNORDERED_LIST_STYLES_BY_LEVEL = ['disc', 'circle', 'square'];
|
|
937
938
|
|
|
@@ -958,6 +959,7 @@ export {
|
|
|
958
959
|
CSS_VARIABLES,
|
|
959
960
|
TAB_CATEGORIES,
|
|
960
961
|
RESIZE_MARGIN,
|
|
962
|
+
BLOCK_FORMAT_TAGS,
|
|
961
963
|
LIST_STYLES_BY_LEVEL,
|
|
962
964
|
UNORDERED_LIST_STYLES_BY_LEVEL,
|
|
963
965
|
UNORDERED_LIST_OPTIONS,
|
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import './style.css'
|
|
2
2
|
import { CLASSES, CSS, CSS_VARIABLES, ERRORS, REGEX, SVG } from "./constant"
|
|
3
3
|
import PLUGINS, { applyTextFormat } from './plugin';
|
|
4
|
-
import { showAnchorPopover, changeAllToolbarState, changeToolbarStateByName, changeToolbarValueByName, cleanHTML, debounce, destroyImageResizer, destroyTableEditPlugin, initImageResizer, initTableEditPlugin, makeToolbarButton, makeToolbarColor, makeToolbarDropdown, makeToolbarSelect, rgbToHex, updateTableResizerPosition, destroyAnchorPopover, changeToolbarHtmlByName, showRemoteCursor, syncRemoteChangesDebounce, applyRemoteChanges, updateCursorPositionDebounce, buildTributeValues, updateHeight, getTableGrid, getCellPosition, makeUnorderedList, makeOrderedList, makeHeading, makeBlockQuote, makeStrikethrough, makeCodeBlock, makeSublist, showTooltip, makeToolbarButtonSelect, navigateToHeading, updateOutlineItems, highlightActiveOutline, findAndReplace, handleBackspaceInList } from './utilities';
|
|
4
|
+
import { showAnchorPopover, changeAllToolbarState, changeToolbarStateByName, changeToolbarValueByName, cleanHTML, debounce, destroyImageResizer, destroyTableEditPlugin, initImageResizer, initTableEditPlugin, makeToolbarButton, makeToolbarColor, makeToolbarDropdown, makeToolbarSelect, rgbToHex, updateTableResizerPosition, destroyAnchorPopover, changeToolbarHtmlByName, showRemoteCursor, syncRemoteChangesDebounce, applyRemoteChanges, updateCursorPositionDebounce, buildTributeValues, updateHeight, getTableGrid, getCellPosition, makeUnorderedList, makeOrderedList, makeHeading, makeBlockQuote, makeStrikethrough, makeCodeBlock, makeSublist, showTooltip, makeToolbarButtonSelect, navigateToHeading, updateOutlineItems, highlightActiveOutline, findAndReplace, handleBackspaceInList, trackListSelectionDeletion, cleanupListSelectionDeletion } from './utilities';
|
|
5
5
|
import { initTabs, findTab, renderTabs, prepareTabs } from './tab';
|
|
6
6
|
import { v4 as uuidv4 } from 'uuid';
|
|
7
7
|
|
|
@@ -675,6 +675,7 @@ class LeksyEditor {
|
|
|
675
675
|
contentEditableDiv.className = 'content-editable';
|
|
676
676
|
|
|
677
677
|
contentEditableDiv.oninput = (e) => {
|
|
678
|
+
cleanupListSelectionDeletion(core);
|
|
678
679
|
core.onChange(e.target.innerHTML)
|
|
679
680
|
}
|
|
680
681
|
contentEditableDiv.onbeforeinput = (e) => {
|
|
@@ -858,6 +859,7 @@ class LeksyEditor {
|
|
|
858
859
|
}
|
|
859
860
|
|
|
860
861
|
handleBackspaceInList(event, core);
|
|
862
|
+
trackListSelectionDeletion(event, core);
|
|
861
863
|
|
|
862
864
|
if (!isLinkCreated) core.elements.lastCreatedLink = null
|
|
863
865
|
|
|
@@ -1015,4 +1017,4 @@ export default LeksyEditor
|
|
|
1015
1017
|
export {
|
|
1016
1018
|
convertHtmlToText,
|
|
1017
1019
|
isHTMLEmpty,
|
|
1018
|
-
}
|
|
1020
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leksy-editor",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.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/plugin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EMOJI_CATEGORIES, FONT_SIZE_OPTIONS, FONTS, FORMAT_OPTIONS, MIMETYPE, REGEX, SPECIAL_CHARACTERS, SVG, UNORDERED_LIST_OPTIONS, ORDERED_LIST_OPTIONS, CLASSES } from "./constant"
|
|
2
2
|
import { giphy, pexels, tenor } from "./gallery";
|
|
3
|
-
import { formatLink, changeAllToolbarState, changeToolbarHtmlByName, changeToolbarStateByName, changeToolbarValueByName, cleanHTML, constructEmbedUrl, extractSocialMediaId, isLinkValid, openModal, transformTextStyle, insertTOC, } from "./utilities";
|
|
3
|
+
import { formatLink, changeAllToolbarState, changeToolbarHtmlByName, changeToolbarStateByName, changeToolbarValueByName, cleanHTML, constructEmbedUrl, extractSocialMediaId, isLinkValid, openModal, transformTextStyle, insertTOC, applyFormatBlockInListItem, normalizeHeadingWrappedLists, } from "./utilities";
|
|
4
4
|
|
|
5
5
|
const applyTextFormat = (core, command) => {
|
|
6
6
|
core.elements.editor.focus();
|
|
@@ -12,27 +12,28 @@ const applyTextFormat = (core, command) => {
|
|
|
12
12
|
else changeToolbarStateByName(core, 'inactive', [command])
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
const
|
|
16
|
-
core.elements.iframeWindow.execCommand(
|
|
15
|
+
const applyList = (core, { command, activePlugin, inactivePlugin }) => {
|
|
16
|
+
core.elements.iframeWindow.execCommand(command);
|
|
17
|
+
normalizeHeadingWrappedLists(core);
|
|
17
18
|
core.elements.editor.focus();
|
|
18
19
|
core.updateCaretPosition()
|
|
19
20
|
|
|
20
|
-
const isActive = core.elements.iframeWindow.queryCommandState(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
changeToolbarStateByName(core, 'inactive', ['unordered_list'])
|
|
21
|
+
const isActive = core.elements.iframeWindow.queryCommandState(command);
|
|
22
|
+
changeToolbarStateByName(core, isActive ? 'active' : 'inactive', [activePlugin])
|
|
23
|
+
changeToolbarStateByName(core, 'inactive', [inactivePlugin])
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
const applyOrderList = (core) => applyList(core, {
|
|
27
|
+
command: 'insertOrderedList',
|
|
28
|
+
activePlugin: 'ordered_list',
|
|
29
|
+
inactivePlugin: 'unordered_list'
|
|
30
|
+
})
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
32
|
+
const applyUnorderedList = (core) => applyList(core, {
|
|
33
|
+
command: 'insertUnorderedList',
|
|
34
|
+
activePlugin: 'unordered_list',
|
|
35
|
+
inactivePlugin: 'ordered_list'
|
|
36
|
+
})
|
|
36
37
|
|
|
37
38
|
const PLUGINS = {
|
|
38
39
|
'undo': {
|
|
@@ -258,7 +259,11 @@ const PLUGINS = {
|
|
|
258
259
|
width: '110px',
|
|
259
260
|
click: (tag, core, options) => {
|
|
260
261
|
core.elements.editor.focus();
|
|
261
|
-
core
|
|
262
|
+
const isFormattedInList = applyFormatBlockInListItem(core, tag);
|
|
263
|
+
if (!isFormattedInList) {
|
|
264
|
+
core.elements.iframeWindow.execCommand('formatBlock', false, tag);
|
|
265
|
+
}
|
|
266
|
+
normalizeHeadingWrappedLists(core);
|
|
262
267
|
|
|
263
268
|
core.updateCaretPosition()
|
|
264
269
|
changeToolbarValueByName(core, 'format-block', tag)
|
|
@@ -276,6 +281,7 @@ const PLUGINS = {
|
|
|
276
281
|
const isActive = core.elements.iframeWindow.queryCommandState('insertOrderedList');
|
|
277
282
|
if (!isActive) {
|
|
278
283
|
core.elements.iframeWindow.execCommand('insertOrderedList');
|
|
284
|
+
normalizeHeadingWrappedLists(core);
|
|
279
285
|
}
|
|
280
286
|
const selection = core.elements.iframeWindow.getSelection();
|
|
281
287
|
if (selection.rangeCount > 0) {
|
|
@@ -308,6 +314,7 @@ const PLUGINS = {
|
|
|
308
314
|
const isActive = core.elements.iframeWindow.queryCommandState('insertUnorderedList');
|
|
309
315
|
if (!isActive) {
|
|
310
316
|
core.elements.iframeWindow.execCommand('insertUnorderedList');
|
|
317
|
+
normalizeHeadingWrappedLists(core);
|
|
311
318
|
}
|
|
312
319
|
const selection = core.elements.iframeWindow.getSelection();
|
|
313
320
|
if (selection.rangeCount > 0) {
|
|
@@ -1345,4 +1352,4 @@ export {
|
|
|
1345
1352
|
applyTextFormat,
|
|
1346
1353
|
applyOrderList,
|
|
1347
1354
|
applyUnorderedList
|
|
1348
|
-
}
|
|
1355
|
+
}
|
package/utilities.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { CLASSES, FONT_SIZES, FONTS, FORMATS, GIPHY_POWERED_IMAGE, REGEX, RESIZER_PLUGINS, SOCIAL_MEDIA_BASEURLS, SOCIAL_MEDIA_PATTERNS, SVG, TABLE_PLUGINS, TAB_CATEGORIES, RESIZE_MARGIN, LIST_STYLES_BY_LEVEL, UNORDERED_LIST_STYLES_BY_LEVEL } from "./constant";
|
|
1
|
+
import { BLOCK_FORMAT_TAGS, CLASSES, FONT_SIZES, FONTS, FORMATS, GIPHY_POWERED_IMAGE, REGEX, RESIZER_PLUGINS, SOCIAL_MEDIA_BASEURLS, SOCIAL_MEDIA_PATTERNS, SVG, TABLE_PLUGINS, TAB_CATEGORIES, RESIZE_MARGIN, LIST_STYLES_BY_LEVEL, UNORDERED_LIST_STYLES_BY_LEVEL } from "./constant";
|
|
2
2
|
import { DiffDOM } from 'diff-dom';
|
|
3
|
-
import { applyUnorderedList, applyOrderList } from "./plugin";
|
|
4
3
|
import { v4 as uuidv4 } from 'uuid';
|
|
5
4
|
import { findTab } from "./tab";
|
|
5
|
+
import { applyOrderList, applyUnorderedList } from "./plugin";
|
|
6
6
|
|
|
7
7
|
const dd = new DiffDOM();
|
|
8
8
|
|
|
@@ -2870,6 +2870,98 @@ const isInsideTableCell = (node) => {
|
|
|
2870
2870
|
return element?.closest('td, th');
|
|
2871
2871
|
};
|
|
2872
2872
|
|
|
2873
|
+
const formatListItemContent = (li, tag) => {
|
|
2874
|
+
const formattedBlock = document.createElement(tag);
|
|
2875
|
+
const childNodes = Array.from(li.childNodes);
|
|
2876
|
+
|
|
2877
|
+
childNodes.forEach(child => {
|
|
2878
|
+
if (child.nodeType === Node.ELEMENT_NODE && ['UL', 'OL'].includes(child.tagName)) return;
|
|
2879
|
+
formattedBlock.appendChild(child);
|
|
2880
|
+
});
|
|
2881
|
+
|
|
2882
|
+
if (!formattedBlock.childNodes.length) formattedBlock.innerHTML = '<br>';
|
|
2883
|
+
|
|
2884
|
+
const firstNestedList = Array.from(li.children).find(child => ['UL', 'OL'].includes(child.tagName));
|
|
2885
|
+
li.insertBefore(formattedBlock, firstNestedList || null);
|
|
2886
|
+
|
|
2887
|
+
return formattedBlock;
|
|
2888
|
+
}
|
|
2889
|
+
|
|
2890
|
+
const applyFormatBlockInListItem = (core, tag) => {
|
|
2891
|
+
const selection = core.elements.iframeWindow.getSelection();
|
|
2892
|
+
const node = selection?.rangeCount ? selection.getRangeAt(0).startContainer : null;
|
|
2893
|
+
const element = node?.nodeType === Node.ELEMENT_NODE ? node : node?.parentElement;
|
|
2894
|
+
const li = element?.closest('li');
|
|
2895
|
+
|
|
2896
|
+
if (!li || !core.elements.editor.contains(li)) return false;
|
|
2897
|
+
|
|
2898
|
+
const currentBlock = element.closest(BLOCK_FORMAT_TAGS.join(','));
|
|
2899
|
+
const isCurrentBlockInLi = currentBlock && currentBlock.closest('li') === li;
|
|
2900
|
+
let formattedBlock;
|
|
2901
|
+
|
|
2902
|
+
const placeCaretAtEnd = (core, element) => {
|
|
2903
|
+
const range = document.createRange();
|
|
2904
|
+
const selection = core.elements.iframeWindow.getSelection();
|
|
2905
|
+
|
|
2906
|
+
range.selectNodeContents(element);
|
|
2907
|
+
range.collapse(false);
|
|
2908
|
+
selection.removeAllRanges();
|
|
2909
|
+
selection.addRange(range);
|
|
2910
|
+
}
|
|
2911
|
+
|
|
2912
|
+
if (tag === 'p') {
|
|
2913
|
+
if (isCurrentBlockInLi) {
|
|
2914
|
+
const fragment = document.createDocumentFragment();
|
|
2915
|
+
|
|
2916
|
+
while (currentBlock.firstChild) fragment.appendChild(currentBlock.firstChild);
|
|
2917
|
+
|
|
2918
|
+
li.insertBefore(fragment, currentBlock);
|
|
2919
|
+
currentBlock.remove();
|
|
2920
|
+
}
|
|
2921
|
+
placeCaretAtEnd(core, li);
|
|
2922
|
+
return true;
|
|
2923
|
+
}
|
|
2924
|
+
|
|
2925
|
+
if (isCurrentBlockInLi) {
|
|
2926
|
+
formattedBlock = document.createElement(tag);
|
|
2927
|
+
formattedBlock.innerHTML = currentBlock.innerHTML || '<br>';
|
|
2928
|
+
currentBlock.replaceWith(formattedBlock);
|
|
2929
|
+
} else {
|
|
2930
|
+
formattedBlock = formatListItemContent(li, tag);
|
|
2931
|
+
}
|
|
2932
|
+
|
|
2933
|
+
placeCaretAtEnd(core, formattedBlock);
|
|
2934
|
+
return true;
|
|
2935
|
+
}
|
|
2936
|
+
|
|
2937
|
+
const normalizeHeadingWrappedLists = (core) => {
|
|
2938
|
+
core.elements.editor.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(heading => {
|
|
2939
|
+
const lists = Array.from(heading.children).filter(child => ['UL', 'OL'].includes(child.tagName));
|
|
2940
|
+
if (!lists.length) return;
|
|
2941
|
+
|
|
2942
|
+
const headingTag = heading.tagName.toLowerCase();
|
|
2943
|
+
const fragment = document.createDocumentFragment();
|
|
2944
|
+
|
|
2945
|
+
lists.forEach(list => {
|
|
2946
|
+
list.querySelectorAll('li').forEach(li => {
|
|
2947
|
+
const hasBlock = Array.from(li.children).some(child => BLOCK_FORMAT_TAGS.includes(child.tagName));
|
|
2948
|
+
if (!hasBlock) formatListItemContent(li, headingTag);
|
|
2949
|
+
});
|
|
2950
|
+
fragment.appendChild(list);
|
|
2951
|
+
});
|
|
2952
|
+
|
|
2953
|
+
heading.parentNode.insertBefore(fragment, heading);
|
|
2954
|
+
|
|
2955
|
+
if (heading.textContent.replace(/\u200B/g, '').trim() || heading.querySelector('img, br')) {
|
|
2956
|
+
const paragraph = document.createElement('p');
|
|
2957
|
+
paragraph.innerHTML = heading.innerHTML;
|
|
2958
|
+
heading.replaceWith(paragraph);
|
|
2959
|
+
} else {
|
|
2960
|
+
heading.remove();
|
|
2961
|
+
}
|
|
2962
|
+
});
|
|
2963
|
+
}
|
|
2964
|
+
|
|
2873
2965
|
const makeUnorderedList = (event, core) => {
|
|
2874
2966
|
if (event.key !== ' ') return;
|
|
2875
2967
|
|
|
@@ -3201,7 +3293,7 @@ const indentListItem = (li, core) => {
|
|
|
3201
3293
|
const selection = core.elements.iframeWindow.getSelection();
|
|
3202
3294
|
const range = document.createRange();
|
|
3203
3295
|
range.selectNodeContents(li);
|
|
3204
|
-
range.collapse(
|
|
3296
|
+
range.collapse(true);
|
|
3205
3297
|
selection.removeAllRanges();
|
|
3206
3298
|
selection.addRange(range);
|
|
3207
3299
|
core.elements.editor.focus();
|
|
@@ -3241,7 +3333,109 @@ const outdentListItem = (li, core) => {
|
|
|
3241
3333
|
const isEmptyLi = (li) => {
|
|
3242
3334
|
const text = li.textContent.replace(/\u200B/g, '').trim();
|
|
3243
3335
|
const hasOnlySublist = li.children.length === 1 && li.querySelector('ul, ol');
|
|
3244
|
-
|
|
3336
|
+
const hasMeaningfulElement = li.querySelector('img, iframe, audio, video, table, hr, figure');
|
|
3337
|
+
return text === '' && !hasOnlySublist && !hasMeaningfulElement;
|
|
3338
|
+
}
|
|
3339
|
+
|
|
3340
|
+
const trackListSelectionDeletion = (event, core) => {
|
|
3341
|
+
if (event.defaultPrevented) {
|
|
3342
|
+
delete core.state.pendingListSelectionDeletion;
|
|
3343
|
+
return;
|
|
3344
|
+
}
|
|
3345
|
+
|
|
3346
|
+
if (!['Backspace', 'Delete'].includes(event.key)) {
|
|
3347
|
+
delete core.state.pendingListSelectionDeletion;
|
|
3348
|
+
return;
|
|
3349
|
+
}
|
|
3350
|
+
|
|
3351
|
+
const selection = core.elements.iframeWindow.getSelection();
|
|
3352
|
+
if (!selection?.rangeCount) return;
|
|
3353
|
+
|
|
3354
|
+
const range = selection.getRangeAt(0);
|
|
3355
|
+
if (range.collapsed) {
|
|
3356
|
+
delete core.state.pendingListSelectionDeletion;
|
|
3357
|
+
return;
|
|
3358
|
+
}
|
|
3359
|
+
|
|
3360
|
+
const getElementFromNode = (node) => {
|
|
3361
|
+
if (!node) return null;
|
|
3362
|
+
return node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
|
|
3363
|
+
}
|
|
3364
|
+
|
|
3365
|
+
const startElement = getElementFromNode(range.startContainer);
|
|
3366
|
+
const endElement = getElementFromNode(range.endContainer);
|
|
3367
|
+
const startLi = startElement?.closest('li');
|
|
3368
|
+
const endLi = endElement?.closest('li');
|
|
3369
|
+
|
|
3370
|
+
if (!startLi || !endLi || startLi === endLi) {
|
|
3371
|
+
delete core.state.pendingListSelectionDeletion;
|
|
3372
|
+
return;
|
|
3373
|
+
}
|
|
3374
|
+
|
|
3375
|
+
const listItems = [];
|
|
3376
|
+
|
|
3377
|
+
core.elements.editor.querySelectorAll('li').forEach(li => {
|
|
3378
|
+
if (range.intersectsNode(li)) listItems.push(li);
|
|
3379
|
+
});
|
|
3380
|
+
|
|
3381
|
+
if (listItems.length < 2) {
|
|
3382
|
+
delete core.state.pendingListSelectionDeletion;
|
|
3383
|
+
return;
|
|
3384
|
+
}
|
|
3385
|
+
|
|
3386
|
+
core.state.pendingListSelectionDeletion = {
|
|
3387
|
+
listItems,
|
|
3388
|
+
lists: Array.from(new Set(listItems.map(li => li.parentElement).filter(Boolean))),
|
|
3389
|
+
};
|
|
3390
|
+
}
|
|
3391
|
+
|
|
3392
|
+
const cleanupListSelectionDeletion = (core) => {
|
|
3393
|
+
const pendingDeletion = core.state.pendingListSelectionDeletion;
|
|
3394
|
+
if (!pendingDeletion) return false;
|
|
3395
|
+
|
|
3396
|
+
delete core.state.pendingListSelectionDeletion;
|
|
3397
|
+
|
|
3398
|
+
let didChange = false;
|
|
3399
|
+
const lists = new Set(pendingDeletion.lists);
|
|
3400
|
+
|
|
3401
|
+
pendingDeletion.listItems.forEach(li => {
|
|
3402
|
+
if (!li.isConnected || !core.elements.editor.contains(li)) return;
|
|
3403
|
+
if (!isEmptyLi(li)) return;
|
|
3404
|
+
|
|
3405
|
+
const parentList = li.parentElement;
|
|
3406
|
+
if (parentList) lists.add(parentList);
|
|
3407
|
+
li.remove();
|
|
3408
|
+
didChange = true;
|
|
3409
|
+
});
|
|
3410
|
+
|
|
3411
|
+
Array.from(lists).reverse().forEach(list => {
|
|
3412
|
+
if (!list?.isConnected || !core.elements.editor.contains(list)) return;
|
|
3413
|
+
if (list.querySelector('li')) return;
|
|
3414
|
+
|
|
3415
|
+
list.remove();
|
|
3416
|
+
didChange = true;
|
|
3417
|
+
});
|
|
3418
|
+
|
|
3419
|
+
if (didChange) {
|
|
3420
|
+
if (!core.elements.editor.textContent.replace(/\u200B/g, '').trim() && !core.elements.editor.querySelector('img, iframe, audio, video, table, hr, figure')) {
|
|
3421
|
+
core.elements.editor.innerHTML = '<div><br></div>';
|
|
3422
|
+
}
|
|
3423
|
+
|
|
3424
|
+
const selection = core.elements.iframeWindow.getSelection();
|
|
3425
|
+
if (!selection?.rangeCount || !core.elements.editor.contains(selection.anchorNode)) {
|
|
3426
|
+
const range = document.createRange();
|
|
3427
|
+
const selection = core.elements.iframeWindow.getSelection();
|
|
3428
|
+
|
|
3429
|
+
range.selectNodeContents(core.elements.editor);
|
|
3430
|
+
range.collapse(false);
|
|
3431
|
+
selection.removeAllRanges();
|
|
3432
|
+
selection.addRange(range);
|
|
3433
|
+
}
|
|
3434
|
+
|
|
3435
|
+
core.updateCaretPosition();
|
|
3436
|
+
}
|
|
3437
|
+
|
|
3438
|
+
return didChange;
|
|
3245
3439
|
}
|
|
3246
3440
|
|
|
3247
3441
|
const makeSublist = (event, core) => {
|
|
@@ -3251,6 +3445,7 @@ const makeSublist = (event, core) => {
|
|
|
3251
3445
|
if (!selection.rangeCount) return;
|
|
3252
3446
|
|
|
3253
3447
|
const range = selection.getRangeAt(0);
|
|
3448
|
+
const isAtStart = range.startOffset === 0;
|
|
3254
3449
|
|
|
3255
3450
|
let container = range.commonAncestorContainer;
|
|
3256
3451
|
|
|
@@ -3274,7 +3469,7 @@ const makeSublist = (event, core) => {
|
|
|
3274
3469
|
|
|
3275
3470
|
if (!event.shiftKey) {
|
|
3276
3471
|
const hasContent = selectedLis.some(li => !isEmptyLi(li));
|
|
3277
|
-
if (hasContent) return;
|
|
3472
|
+
if (hasContent && !isAtStart) return;
|
|
3278
3473
|
}
|
|
3279
3474
|
|
|
3280
3475
|
event.preventDefault();
|
|
@@ -3419,7 +3614,74 @@ const insertTOC = (core) => {
|
|
|
3419
3614
|
});
|
|
3420
3615
|
|
|
3421
3616
|
tocContainer.appendChild(ul);
|
|
3422
|
-
|
|
3617
|
+
|
|
3618
|
+
const selection = core.elements.iframeWindow.getSelection();
|
|
3619
|
+
|
|
3620
|
+
if (!selection.rangeCount) return;
|
|
3621
|
+
|
|
3622
|
+
const range = selection.getRangeAt(0);
|
|
3623
|
+
|
|
3624
|
+
if (!range.collapsed) {
|
|
3625
|
+
// Replace selected content with TOC
|
|
3626
|
+
range.deleteContents();
|
|
3627
|
+
range.insertNode(tocContainer);
|
|
3628
|
+
|
|
3629
|
+
// Remove empty list items
|
|
3630
|
+
editor.querySelectorAll('li').forEach(li => {
|
|
3631
|
+
const text = li.textContent.replace(/\u00A0/g, '').trim();
|
|
3632
|
+
|
|
3633
|
+
if (
|
|
3634
|
+
!text &&
|
|
3635
|
+
!li.querySelector(
|
|
3636
|
+
'img, video, table, ul, ol, iframe, blockquote'
|
|
3637
|
+
)
|
|
3638
|
+
) {
|
|
3639
|
+
li.remove();
|
|
3640
|
+
}
|
|
3641
|
+
});
|
|
3642
|
+
|
|
3643
|
+
// Place cursor after TOC
|
|
3644
|
+
const p = document.createElement('p');
|
|
3645
|
+
p.innerHTML = '<br>';
|
|
3646
|
+
tocContainer.after(p);
|
|
3647
|
+
|
|
3648
|
+
const newRange = document.createRange();
|
|
3649
|
+
newRange.setStart(p, 0);
|
|
3650
|
+
newRange.collapse(true);
|
|
3651
|
+
|
|
3652
|
+
selection.removeAllRanges();
|
|
3653
|
+
selection.addRange(newRange);
|
|
3654
|
+
} else {
|
|
3655
|
+
// No selection -> insert after current block
|
|
3656
|
+
let node = range.startContainer;
|
|
3657
|
+
|
|
3658
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
3659
|
+
node = node.parentElement;
|
|
3660
|
+
}
|
|
3661
|
+
|
|
3662
|
+
const block = node.closest(
|
|
3663
|
+
'p, div, h1, h2, h3, h4, h5, h6, blockquote, pre, li'
|
|
3664
|
+
);
|
|
3665
|
+
|
|
3666
|
+
if (block) {
|
|
3667
|
+
block.insertAdjacentElement('afterend', tocContainer);
|
|
3668
|
+
} else {
|
|
3669
|
+
editor.appendChild(tocContainer);
|
|
3670
|
+
}
|
|
3671
|
+
|
|
3672
|
+
// Place cursor after TOC
|
|
3673
|
+
const p = document.createElement('p');
|
|
3674
|
+
p.innerHTML = '<br>';
|
|
3675
|
+
tocContainer.after(p);
|
|
3676
|
+
|
|
3677
|
+
const newRange = document.createRange();
|
|
3678
|
+
newRange.setStart(p, 0);
|
|
3679
|
+
newRange.collapse(true);
|
|
3680
|
+
|
|
3681
|
+
selection.removeAllRanges();
|
|
3682
|
+
selection.addRange(newRange);
|
|
3683
|
+
}
|
|
3684
|
+
|
|
3423
3685
|
core.updateCaretPosition();
|
|
3424
3686
|
};
|
|
3425
3687
|
|
|
@@ -4069,6 +4331,8 @@ export {
|
|
|
4069
4331
|
updateHeight,
|
|
4070
4332
|
getTableGrid,
|
|
4071
4333
|
getCellPosition,
|
|
4334
|
+
applyFormatBlockInListItem,
|
|
4335
|
+
normalizeHeadingWrappedLists,
|
|
4072
4336
|
makeUnorderedList,
|
|
4073
4337
|
makeOrderedList,
|
|
4074
4338
|
showTooltip,
|
|
@@ -4086,4 +4350,6 @@ export {
|
|
|
4086
4350
|
highlightActiveOutline,
|
|
4087
4351
|
findAndReplace,
|
|
4088
4352
|
handleBackspaceInList,
|
|
4353
|
+
trackListSelectionDeletion,
|
|
4354
|
+
cleanupListSelectionDeletion,
|
|
4089
4355
|
}
|