@overlap/rte 1.0.15 → 1.0.16
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/hooks/useCheckbox.d.ts.map +1 -1
- package/dist/index.esm.js +160 -107
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +160 -107
- package/dist/index.js.map +1 -1
- package/dist/plugins/blockFormat.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCheckbox.d.ts","sourceRoot":"","sources":["../../src/hooks/useCheckbox.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EACH,mBAAmB,EAInB,qBAAqB,EACxB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"useCheckbox.d.ts","sourceRoot":"","sources":["../../src/hooks/useCheckbox.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EACH,mBAAmB,EAInB,qBAAqB,EACxB,MAAM,mBAAmB,CAAC;AAQ3B,UAAU,kBAAkB;IACxB,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAClD,aAAa,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IACpC,aAAa,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;IAChD,YAAY,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,aAAa,EAAE,MAAM,aAAa,CAAC;CACtC;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EACxB,SAAS,EACT,aAAa,EACb,aAAa,EACb,YAAY,EACZ,aAAa,GAChB,EAAE,kBAAkB;;iCA+RJ,WAAW,KAAG,OAAO;+BAvM1B,aAAa,KAAG,OAAO;6BAkFvB,aAAa,KAAG,OAAO;;EA2PlC"}
|
package/dist/index.esm.js
CHANGED
|
@@ -228,32 +228,6 @@ function findClosestListItem(node) {
|
|
|
228
228
|
: node;
|
|
229
229
|
return element?.closest("li") || null;
|
|
230
230
|
}
|
|
231
|
-
/**
|
|
232
|
-
* Sets cursor position in a text node after an async DOM update.
|
|
233
|
-
*/
|
|
234
|
-
function setCursorInTextNode(textNode, position, editor) {
|
|
235
|
-
requestAnimationFrame(() => {
|
|
236
|
-
requestAnimationFrame(() => {
|
|
237
|
-
try {
|
|
238
|
-
const range = document.createRange();
|
|
239
|
-
const maxPos = textNode.textContent?.length || 0;
|
|
240
|
-
const safePos = Math.min(Math.max(0, position), maxPos);
|
|
241
|
-
range.setStart(textNode, safePos);
|
|
242
|
-
range.collapse(true);
|
|
243
|
-
const selection = window.getSelection();
|
|
244
|
-
if (selection) {
|
|
245
|
-
selection.removeAllRanges();
|
|
246
|
-
selection.addRange(range);
|
|
247
|
-
if (editor)
|
|
248
|
-
editor.focus();
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
catch (_) {
|
|
252
|
-
// Silently fail - cursor positioning is best-effort
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
231
|
|
|
258
232
|
/**
|
|
259
233
|
* Pure checkbox utility functions for checkbox lists.
|
|
@@ -588,21 +562,39 @@ function useCheckbox({ editorRef, isUpdatingRef, pushToHistory, notifyChange, ge
|
|
|
588
562
|
}
|
|
589
563
|
return true;
|
|
590
564
|
}
|
|
591
|
-
// Normal case: create a new checkbox item
|
|
565
|
+
// Normal case: split content at cursor and create a new checkbox item
|
|
566
|
+
const afterRange = document.createRange();
|
|
567
|
+
afterRange.setStart(range.startContainer, range.startOffset);
|
|
568
|
+
if (listItem.lastChild) {
|
|
569
|
+
afterRange.setEndAfter(listItem.lastChild);
|
|
570
|
+
}
|
|
571
|
+
else {
|
|
572
|
+
afterRange.setEnd(listItem, listItem.childNodes.length);
|
|
573
|
+
}
|
|
574
|
+
const afterFragment = afterRange.extractContents();
|
|
592
575
|
const newLi = document.createElement("li");
|
|
593
576
|
updateListItemChecked(newLi, false);
|
|
594
|
-
const
|
|
595
|
-
|
|
577
|
+
const hasContent = afterFragment.textContent?.trim();
|
|
578
|
+
if (hasContent) {
|
|
579
|
+
newLi.appendChild(afterFragment);
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
newLi.appendChild(document.createTextNode(" "));
|
|
583
|
+
}
|
|
596
584
|
if (listItem.nextSibling) {
|
|
597
585
|
checkboxList.insertBefore(newLi, listItem.nextSibling);
|
|
598
586
|
}
|
|
599
587
|
else {
|
|
600
588
|
checkboxList.appendChild(newLi);
|
|
601
589
|
}
|
|
590
|
+
if (!listItem.firstChild) {
|
|
591
|
+
listItem.appendChild(document.createTextNode(" "));
|
|
592
|
+
}
|
|
602
593
|
if (editor)
|
|
603
594
|
ensureAllCheckboxes(editor);
|
|
595
|
+
const cursorNode = newLi.firstChild || newLi;
|
|
604
596
|
const newRange = document.createRange();
|
|
605
|
-
newRange.setStart(
|
|
597
|
+
newRange.setStart(cursorNode, 0);
|
|
606
598
|
newRange.collapse(true);
|
|
607
599
|
selection.removeAllRanges();
|
|
608
600
|
selection.addRange(newRange);
|
|
@@ -634,8 +626,16 @@ function useCheckbox({ editorRef, isUpdatingRef, pushToHistory, notifyChange, ge
|
|
|
634
626
|
isUpdatingRef.current = false;
|
|
635
627
|
return false;
|
|
636
628
|
}
|
|
637
|
-
//
|
|
638
|
-
|
|
629
|
+
// Resolve the element at the start of the selection as well,
|
|
630
|
+
// because when selecting across multiple list items the
|
|
631
|
+
// commonAncestorContainer is the editor root (above the list).
|
|
632
|
+
const startNode = range.startContainer;
|
|
633
|
+
const startElement = startNode.nodeType === Node.TEXT_NODE
|
|
634
|
+
? startNode.parentElement
|
|
635
|
+
: startNode;
|
|
636
|
+
// Already in a checkbox list? Remove it (convert back to bullet list).
|
|
637
|
+
const existingList = findClosestCheckboxList(element) ||
|
|
638
|
+
(startElement ? findClosestCheckboxList(startElement) : null);
|
|
639
639
|
if (existingList) {
|
|
640
640
|
existingList.classList.remove("rte-checkbox-list");
|
|
641
641
|
existingList
|
|
@@ -648,48 +648,65 @@ function useCheckbox({ editorRef, isUpdatingRef, pushToHistory, notifyChange, ge
|
|
|
648
648
|
isUpdatingRef.current = false;
|
|
649
649
|
return true;
|
|
650
650
|
}
|
|
651
|
-
//
|
|
652
|
-
const
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
if (isValidBlockElement) {
|
|
666
|
-
const textContent = blockElement.textContent || "";
|
|
667
|
-
blockElement.parentElement.replaceChild(ul, blockElement);
|
|
668
|
-
const finalTextNode = li.firstChild;
|
|
669
|
-
if (finalTextNode) {
|
|
670
|
-
finalTextNode.textContent = textContent || " ";
|
|
671
|
-
const cursorPos = textContent ? textContent.length : 0;
|
|
672
|
-
setCursorInTextNode(finalTextNode, cursorPos, editor);
|
|
673
|
-
}
|
|
651
|
+
// Already in a <ul> (bullet list)? Convert in-place to checkbox.
|
|
652
|
+
const existingUl = element.closest("ul") ||
|
|
653
|
+
startElement?.closest("ul");
|
|
654
|
+
if (existingUl && editor.contains(existingUl)) {
|
|
655
|
+
existingUl.classList.add("rte-checkbox-list");
|
|
656
|
+
existingUl.querySelectorAll(":scope > li").forEach((li) => {
|
|
657
|
+
updateListItemChecked(li, false);
|
|
658
|
+
});
|
|
659
|
+
ensureAllCheckboxes(editor);
|
|
660
|
+
isUpdatingRef.current = false;
|
|
661
|
+
const content = getDomContent();
|
|
662
|
+
pushToHistory(content);
|
|
663
|
+
notifyChange(content);
|
|
664
|
+
return true;
|
|
674
665
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
666
|
+
// Already in an <ol> (numbered list)? Convert to <ul> first,
|
|
667
|
+
// then make it a checkbox list.
|
|
668
|
+
const existingOl = element.closest("ol") ||
|
|
669
|
+
startElement?.closest("ol");
|
|
670
|
+
if (existingOl && editor.contains(existingOl)) {
|
|
671
|
+
const ul = document.createElement("ul");
|
|
672
|
+
ul.classList.add("rte-checkbox-list");
|
|
673
|
+
while (existingOl.firstChild) {
|
|
674
|
+
ul.appendChild(existingOl.firstChild);
|
|
683
675
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
676
|
+
existingOl.parentNode?.replaceChild(ul, existingOl);
|
|
677
|
+
ul.querySelectorAll(":scope > li").forEach((li) => {
|
|
678
|
+
updateListItemChecked(li, false);
|
|
679
|
+
});
|
|
680
|
+
ensureAllCheckboxes(editor);
|
|
681
|
+
isUpdatingRef.current = false;
|
|
682
|
+
const content = getDomContent();
|
|
683
|
+
pushToHistory(content);
|
|
684
|
+
notifyChange(content);
|
|
685
|
+
return true;
|
|
686
|
+
}
|
|
687
|
+
// Not in any list: use the browser's insertUnorderedList command
|
|
688
|
+
// to properly handle single paragraphs and multi-paragraph selections,
|
|
689
|
+
// then convert the resulting <ul> to a checkbox list.
|
|
690
|
+
if (document.activeElement !== editor) {
|
|
691
|
+
editor.focus();
|
|
692
|
+
}
|
|
693
|
+
document.execCommand("insertUnorderedList", false);
|
|
694
|
+
// Find the <ul> the cursor is now inside
|
|
695
|
+
const sel = window.getSelection();
|
|
696
|
+
if (sel && sel.rangeCount > 0) {
|
|
697
|
+
const r = sel.getRangeAt(0);
|
|
698
|
+
const node = r.commonAncestorContainer.nodeType === Node.TEXT_NODE
|
|
699
|
+
? r.commonAncestorContainer.parentElement
|
|
700
|
+
: r.commonAncestorContainer;
|
|
701
|
+
const newUl = node?.closest("ul");
|
|
702
|
+
if (newUl && editor.contains(newUl)) {
|
|
703
|
+
newUl.classList.add("rte-checkbox-list");
|
|
704
|
+
newUl.querySelectorAll(":scope > li").forEach((li) => {
|
|
705
|
+
updateListItemChecked(li, false);
|
|
706
|
+
});
|
|
690
707
|
}
|
|
691
708
|
}
|
|
692
|
-
//
|
|
709
|
+
// Finalize: ensure attributes and save to history
|
|
693
710
|
setTimeout(() => {
|
|
694
711
|
if (!editor)
|
|
695
712
|
return;
|
|
@@ -2652,18 +2669,25 @@ function createBlockFormatPlugin(headings = defaultHeadings$2, blockOptions = {}
|
|
|
2652
2669
|
: container;
|
|
2653
2670
|
if (!element)
|
|
2654
2671
|
return undefined;
|
|
2672
|
+
// When the selection spans multiple blocks, commonAncestorContainer
|
|
2673
|
+
// may be the editor root. Fall back to startContainer to detect
|
|
2674
|
+
// formats from an element that is actually inside the content.
|
|
2675
|
+
const startNode = range.startContainer;
|
|
2676
|
+
const startEl = startNode.nodeType === Node.TEXT_NODE
|
|
2677
|
+
? startNode.parentElement
|
|
2678
|
+
: startNode;
|
|
2655
2679
|
const tagName = element.tagName.toLowerCase();
|
|
2656
2680
|
if (headings.includes(tagName))
|
|
2657
2681
|
return tagName;
|
|
2658
|
-
if (element.closest("pre"))
|
|
2682
|
+
if (element.closest("pre") || startEl?.closest("pre"))
|
|
2659
2683
|
return "code";
|
|
2660
|
-
if (element.closest("blockquote"))
|
|
2684
|
+
if (element.closest("blockquote") || startEl?.closest("blockquote"))
|
|
2661
2685
|
return "blockquote";
|
|
2662
|
-
if (findClosestCheckboxList(element))
|
|
2686
|
+
if (findClosestCheckboxList(element) || (startEl && findClosestCheckboxList(startEl)))
|
|
2663
2687
|
return "checkbox-list";
|
|
2664
|
-
if (element.closest("ul"))
|
|
2688
|
+
if (element.closest("ul") || startEl?.closest("ul"))
|
|
2665
2689
|
return "ul";
|
|
2666
|
-
if (element.closest("ol"))
|
|
2690
|
+
if (element.closest("ol") || startEl?.closest("ol"))
|
|
2667
2691
|
return "ol";
|
|
2668
2692
|
if (tagName === "p")
|
|
2669
2693
|
return "p";
|
|
@@ -2699,6 +2723,32 @@ function createBlockFormatPlugin(headings = defaultHeadings$2, blockOptions = {}
|
|
|
2699
2723
|
? container.parentElement
|
|
2700
2724
|
: container;
|
|
2701
2725
|
};
|
|
2726
|
+
// When the selection spans multiple blocks, commonAncestorContainer
|
|
2727
|
+
// is the editor root. Use startContainer to reach elements inside
|
|
2728
|
+
// the actual selected content.
|
|
2729
|
+
const getStartElement = () => {
|
|
2730
|
+
const sel = editor.getSelection();
|
|
2731
|
+
if (!sel || sel.rangeCount === 0)
|
|
2732
|
+
return null;
|
|
2733
|
+
const start = sel.getRangeAt(0).startContainer;
|
|
2734
|
+
return start.nodeType === Node.TEXT_NODE
|
|
2735
|
+
? start.parentElement
|
|
2736
|
+
: start;
|
|
2737
|
+
};
|
|
2738
|
+
const stripCheckboxAttributes = (list) => {
|
|
2739
|
+
list.classList.remove("rte-checkbox-list");
|
|
2740
|
+
list.querySelectorAll("li[role='checkbox']").forEach((li) => {
|
|
2741
|
+
li.removeAttribute("role");
|
|
2742
|
+
li.removeAttribute("tabIndex");
|
|
2743
|
+
li.removeAttribute("aria-checked");
|
|
2744
|
+
});
|
|
2745
|
+
};
|
|
2746
|
+
const findCheckboxInSelection = () => {
|
|
2747
|
+
const el = getCursorElement();
|
|
2748
|
+
const startEl = getStartElement();
|
|
2749
|
+
return ((el ? findClosestCheckboxList(el) : null) ||
|
|
2750
|
+
(startEl ? findClosestCheckboxList(startEl) : null));
|
|
2751
|
+
};
|
|
2702
2752
|
// Helper: merge all adjacent <pre> elements in the editor into one
|
|
2703
2753
|
const mergeAdjacentPre = () => {
|
|
2704
2754
|
const root = document.activeElement;
|
|
@@ -2722,20 +2772,12 @@ function createBlockFormatPlugin(headings = defaultHeadings$2, blockOptions = {}
|
|
|
2722
2772
|
// Helper: if cursor is inside a list, remove the list first
|
|
2723
2773
|
const escapeListIfNeeded = () => {
|
|
2724
2774
|
const el = getCursorElement();
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
const
|
|
2728
|
-
const
|
|
2729
|
-
const inOl = el.closest("ol");
|
|
2775
|
+
const startEl = getStartElement();
|
|
2776
|
+
const inCheckbox = findCheckboxInSelection();
|
|
2777
|
+
const inUl = el?.closest("ul") || startEl?.closest("ul");
|
|
2778
|
+
const inOl = el?.closest("ol") || startEl?.closest("ol");
|
|
2730
2779
|
if (inCheckbox) {
|
|
2731
|
-
inCheckbox
|
|
2732
|
-
inCheckbox
|
|
2733
|
-
.querySelectorAll("li[role='checkbox']")
|
|
2734
|
-
.forEach((li) => {
|
|
2735
|
-
li.removeAttribute("role");
|
|
2736
|
-
li.removeAttribute("tabIndex");
|
|
2737
|
-
li.removeAttribute("aria-checked");
|
|
2738
|
-
});
|
|
2780
|
+
stripCheckboxAttributes(inCheckbox);
|
|
2739
2781
|
editor.executeCommand("insertUnorderedList");
|
|
2740
2782
|
}
|
|
2741
2783
|
else if (inUl) {
|
|
@@ -2746,29 +2788,36 @@ function createBlockFormatPlugin(headings = defaultHeadings$2, blockOptions = {}
|
|
|
2746
2788
|
}
|
|
2747
2789
|
};
|
|
2748
2790
|
if (value === "checkbox-list") {
|
|
2749
|
-
const
|
|
2750
|
-
if (!el)
|
|
2751
|
-
return;
|
|
2752
|
-
const checkboxList = findClosestCheckboxList(el);
|
|
2791
|
+
const checkboxList = findCheckboxInSelection();
|
|
2753
2792
|
if (checkboxList) {
|
|
2754
|
-
checkboxList
|
|
2755
|
-
checkboxList
|
|
2756
|
-
.querySelectorAll("li[role='checkbox']")
|
|
2757
|
-
.forEach((li) => {
|
|
2758
|
-
li.removeAttribute("role");
|
|
2759
|
-
li.removeAttribute("tabIndex");
|
|
2760
|
-
li.removeAttribute("aria-checked");
|
|
2761
|
-
});
|
|
2793
|
+
stripCheckboxAttributes(checkboxList);
|
|
2762
2794
|
}
|
|
2763
2795
|
else {
|
|
2764
2796
|
editor.executeCommand("insertCheckboxList");
|
|
2765
2797
|
}
|
|
2766
2798
|
}
|
|
2767
2799
|
else if (value === "ul") {
|
|
2768
|
-
|
|
2800
|
+
const checkboxList = findCheckboxInSelection();
|
|
2801
|
+
if (checkboxList) {
|
|
2802
|
+
stripCheckboxAttributes(checkboxList);
|
|
2803
|
+
}
|
|
2804
|
+
else {
|
|
2805
|
+
editor.executeCommand("insertUnorderedList");
|
|
2806
|
+
}
|
|
2769
2807
|
}
|
|
2770
2808
|
else if (value === "ol") {
|
|
2771
|
-
|
|
2809
|
+
const checkboxList = findCheckboxInSelection();
|
|
2810
|
+
if (checkboxList) {
|
|
2811
|
+
stripCheckboxAttributes(checkboxList);
|
|
2812
|
+
const ol = document.createElement("ol");
|
|
2813
|
+
while (checkboxList.firstChild) {
|
|
2814
|
+
ol.appendChild(checkboxList.firstChild);
|
|
2815
|
+
}
|
|
2816
|
+
checkboxList.parentNode?.replaceChild(ol, checkboxList);
|
|
2817
|
+
}
|
|
2818
|
+
else {
|
|
2819
|
+
editor.executeCommand("insertOrderedList");
|
|
2820
|
+
}
|
|
2772
2821
|
}
|
|
2773
2822
|
else if (value === "blockquote") {
|
|
2774
2823
|
const el = getCursorElement();
|
|
@@ -2808,13 +2857,17 @@ function createBlockFormatPlugin(headings = defaultHeadings$2, blockOptions = {}
|
|
|
2808
2857
|
: container;
|
|
2809
2858
|
if (!element)
|
|
2810
2859
|
return false;
|
|
2860
|
+
const startNode = range.startContainer;
|
|
2861
|
+
const startEl = startNode.nodeType === Node.TEXT_NODE
|
|
2862
|
+
? startNode.parentElement
|
|
2863
|
+
: startNode;
|
|
2811
2864
|
const tagName = element.tagName.toLowerCase();
|
|
2812
2865
|
return (headings.includes(tagName) ||
|
|
2813
|
-
element.closest("pre") !== null ||
|
|
2814
|
-
element.closest("blockquote") !== null ||
|
|
2815
|
-
findClosestCheckboxList(element) !== null ||
|
|
2816
|
-
element.closest("ul") !== null ||
|
|
2817
|
-
element.closest("ol") !== null);
|
|
2866
|
+
element.closest("pre") !== null || startEl?.closest("pre") !== null ||
|
|
2867
|
+
element.closest("blockquote") !== null || startEl?.closest("blockquote") !== null ||
|
|
2868
|
+
findClosestCheckboxList(element) !== null || (startEl != null && findClosestCheckboxList(startEl) !== null) ||
|
|
2869
|
+
element.closest("ul") !== null || startEl?.closest("ul") !== null ||
|
|
2870
|
+
element.closest("ol") !== null || startEl?.closest("ol") !== null);
|
|
2818
2871
|
},
|
|
2819
2872
|
canExecute: () => true,
|
|
2820
2873
|
};
|