smartrte-react 0.2.6 → 0.2.7
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.
|
@@ -50,6 +50,11 @@ type ClassicEditorProps = {
|
|
|
50
50
|
* - "dark": Uses the built-in dark theme.
|
|
51
51
|
*/
|
|
52
52
|
theme?: SrteTheme;
|
|
53
|
+
/**
|
|
54
|
+
* Show the inline font-size dropdown in the toolbar.
|
|
55
|
+
* Defaults to true. Heading block controls are independent of this option.
|
|
56
|
+
*/
|
|
57
|
+
showFontSize?: boolean;
|
|
53
58
|
/**
|
|
54
59
|
* Additional CSS class name(s) to apply to the editor's root element.
|
|
55
60
|
* Useful for custom theming by overriding CSS custom properties.
|
|
@@ -57,5 +62,5 @@ type ClassicEditorProps = {
|
|
|
57
62
|
*/
|
|
58
63
|
className?: string;
|
|
59
64
|
};
|
|
60
|
-
export declare function ClassicEditor({ value, onChange, placeholder, minHeight, maxHeight, readOnly, table, media, formula, mediaManager, fonts, defaultFont, preserveFontFamily, preserveColors, preserveDocxStyles, theme, className, }: ClassicEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
65
|
+
export declare function ClassicEditor({ value, onChange, placeholder, minHeight, maxHeight, readOnly, table, media, formula, mediaManager, fonts, defaultFont, preserveFontFamily, preserveColors, preserveDocxStyles, theme, showFontSize, className, }: ClassicEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
61
66
|
export {};
|
|
@@ -17,7 +17,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
17
17
|
{ name: "Times New Roman", value: "'Times New Roman', Times, serif" },
|
|
18
18
|
{ name: "Verdana", value: "Verdana, Geneva, sans-serif" },
|
|
19
19
|
{ name: "Courier New", value: "'Courier New', Courier, monospace" },
|
|
20
|
-
], defaultFont, preserveFontFamily = false, preserveColors = false, preserveDocxStyles = true, theme = "light", className, }) {
|
|
20
|
+
], defaultFont, preserveFontFamily = false, preserveColors = false, preserveDocxStyles = true, theme = "light", showFontSize = true, className, }) {
|
|
21
21
|
ensureStyleSheet();
|
|
22
22
|
const editableRef = useRef(null);
|
|
23
23
|
const editorScrollRef = useRef(null);
|
|
@@ -37,6 +37,9 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
37
37
|
const [imageOverlay, setImageOverlay] = useState(null);
|
|
38
38
|
const resizingRef = useRef(null);
|
|
39
39
|
const draggedImageRef = useRef(null);
|
|
40
|
+
const draggedBlockRef = useRef(null);
|
|
41
|
+
const dragHandleHideTimerRef = useRef(null);
|
|
42
|
+
const [dragHandle, setDragHandle] = useState(null);
|
|
40
43
|
const tableResizeRef = useRef(null);
|
|
41
44
|
const [showTableDialog, setShowTableDialog] = useState(false);
|
|
42
45
|
const [tableRows, setTableRows] = useState(3);
|
|
@@ -58,6 +61,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
58
61
|
});
|
|
59
62
|
const [currentFontSize, setCurrentFontSize] = useState("");
|
|
60
63
|
const [currentFont, setCurrentFont] = useState("");
|
|
64
|
+
const [currentBlockType, setCurrentBlockType] = useState("p");
|
|
61
65
|
const [activeState, setActiveState] = useState({
|
|
62
66
|
bold: false,
|
|
63
67
|
italic: false,
|
|
@@ -108,6 +112,9 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
108
112
|
if (node.nodeType === Node.TEXT_NODE)
|
|
109
113
|
node = node.parentNode;
|
|
110
114
|
const element = node instanceof HTMLElement ? node : null;
|
|
115
|
+
const block = element?.closest("p,h1,h2,h3,h4,h5,h6,li,blockquote,pre,div");
|
|
116
|
+
const tag = block?.tagName.toLowerCase();
|
|
117
|
+
setCurrentBlockType(tag === "h1" || tag === "h2" || tag === "h3" ? tag : "p");
|
|
111
118
|
setActiveState({
|
|
112
119
|
bold: document.queryCommandState("bold"),
|
|
113
120
|
italic: document.queryCommandState("italic"),
|
|
@@ -172,7 +179,22 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
172
179
|
catch { }
|
|
173
180
|
};
|
|
174
181
|
const applyFormatBlock = (blockName) => {
|
|
175
|
-
|
|
182
|
+
try {
|
|
183
|
+
if (!restoreSavedSelection()) {
|
|
184
|
+
safeSelectRange(getSelectionRangeInEditor());
|
|
185
|
+
}
|
|
186
|
+
if (applyFormatBlockFallback(blockName)) {
|
|
187
|
+
const tag = normalizeBlockTag(blockName);
|
|
188
|
+
if (tag === "p" || tag === "h1" || tag === "h2" || tag === "h3") {
|
|
189
|
+
setCurrentBlockType(tag);
|
|
190
|
+
}
|
|
191
|
+
handleInput();
|
|
192
|
+
requestAnimationFrame(updateActiveState);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
exec("formatBlock", blockName);
|
|
196
|
+
}
|
|
197
|
+
catch { }
|
|
176
198
|
};
|
|
177
199
|
const emitChange = () => {
|
|
178
200
|
const el = editableRef.current;
|
|
@@ -386,19 +408,71 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
386
408
|
cell.parentElement?.replaceChild(replacement, cell);
|
|
387
409
|
return replacement;
|
|
388
410
|
};
|
|
389
|
-
const
|
|
390
|
-
const editor = editableRef.current;
|
|
391
|
-
const block = getCurrentBlock();
|
|
411
|
+
const normalizeBlockTag = (blockName) => {
|
|
392
412
|
const tag = blockName.replace(/[<>]/g, "").toLowerCase() || "p";
|
|
393
|
-
|
|
394
|
-
|
|
413
|
+
return /^(p|h1|h2|h3|h4|h5|h6|pre|blockquote)$/.test(tag) ? tag : null;
|
|
414
|
+
};
|
|
415
|
+
const sortInDocumentOrder = (elements) => {
|
|
416
|
+
return [...elements].sort((a, b) => {
|
|
417
|
+
if (a === b)
|
|
418
|
+
return 0;
|
|
419
|
+
const position = a.compareDocumentPosition(b);
|
|
420
|
+
return position & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
|
|
421
|
+
});
|
|
422
|
+
};
|
|
423
|
+
const replaceBlockTag = (block, tag) => {
|
|
424
|
+
if (block.tagName.toLowerCase() === tag)
|
|
425
|
+
return block;
|
|
395
426
|
const replacement = document.createElement(tag);
|
|
396
427
|
copyCellOrBlockStyles(block, replacement);
|
|
397
428
|
block.parentElement?.replaceChild(replacement, block);
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
429
|
+
return replacement;
|
|
430
|
+
};
|
|
431
|
+
const applyFormatBlockFallback = (blockName) => {
|
|
432
|
+
const editor = editableRef.current;
|
|
433
|
+
const range = getSelectionRangeInEditor();
|
|
434
|
+
const tag = normalizeBlockTag(blockName);
|
|
435
|
+
if (!editor || !range || !tag)
|
|
436
|
+
return false;
|
|
437
|
+
if (!editor.innerHTML.trim()) {
|
|
438
|
+
const block = document.createElement(tag);
|
|
439
|
+
block.innerHTML = "<br>";
|
|
440
|
+
editor.appendChild(block);
|
|
441
|
+
focusElementEnd(block);
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
if (range.collapsed) {
|
|
445
|
+
const block = getCurrentBlock();
|
|
446
|
+
if (!block || block === editor || block.closest("ul,ol") || !block.parentElement)
|
|
447
|
+
return false;
|
|
448
|
+
const replacement = replaceBlockTag(block, tag);
|
|
449
|
+
focusElementEnd(replacement);
|
|
450
|
+
return true;
|
|
451
|
+
}
|
|
452
|
+
const selectedBlocks = sortInDocumentOrder(getSelectedBlocks(range)).filter((block) => {
|
|
453
|
+
if (!editor.contains(block) || block === editor)
|
|
454
|
+
return false;
|
|
455
|
+
if (block.closest("ul,ol"))
|
|
456
|
+
return false;
|
|
457
|
+
return Boolean(block.parentElement);
|
|
458
|
+
});
|
|
459
|
+
if (selectedBlocks.length > 0) {
|
|
460
|
+
let lastReplacement = null;
|
|
461
|
+
selectedBlocks.forEach((block) => {
|
|
462
|
+
lastReplacement = replaceBlockTag(block, tag);
|
|
463
|
+
});
|
|
464
|
+
if (lastReplacement)
|
|
465
|
+
focusElementEnd(lastReplacement);
|
|
466
|
+
return true;
|
|
467
|
+
}
|
|
468
|
+
const block = document.createElement(tag);
|
|
469
|
+
const contents = range.extractContents();
|
|
470
|
+
block.appendChild(contents);
|
|
471
|
+
if (!block.innerHTML.trim())
|
|
472
|
+
block.innerHTML = "<br>";
|
|
473
|
+
range.insertNode(block);
|
|
474
|
+
focusElementEnd(block);
|
|
475
|
+
return true;
|
|
402
476
|
};
|
|
403
477
|
const cloneListShell = (list, tagName) => {
|
|
404
478
|
const clone = document.createElement(tagName || list.tagName.toLowerCase());
|
|
@@ -609,7 +683,50 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
609
683
|
};
|
|
610
684
|
const toggleBlockquote = () => {
|
|
611
685
|
try {
|
|
686
|
+
if (!restoreSavedSelection())
|
|
687
|
+
safeSelectRange(getSelectionRangeInEditor());
|
|
612
688
|
const range = getSelectionRangeInEditor();
|
|
689
|
+
const editor = editableRef.current;
|
|
690
|
+
if (!editor || !range)
|
|
691
|
+
return;
|
|
692
|
+
const unwrapQuote = (quote) => {
|
|
693
|
+
const parent = quote.parentElement;
|
|
694
|
+
if (!parent)
|
|
695
|
+
return false;
|
|
696
|
+
const moved = [];
|
|
697
|
+
while (quote.firstChild) {
|
|
698
|
+
const child = quote.firstChild;
|
|
699
|
+
parent.insertBefore(child, quote);
|
|
700
|
+
if (child instanceof HTMLElement)
|
|
701
|
+
moved.push(child);
|
|
702
|
+
}
|
|
703
|
+
quote.remove();
|
|
704
|
+
focusElementEnd(moved[moved.length - 1] || parent);
|
|
705
|
+
return true;
|
|
706
|
+
};
|
|
707
|
+
const wrapBlocks = (blocks) => {
|
|
708
|
+
const selected = sortInDocumentOrder(blocks).filter((block) => {
|
|
709
|
+
if (!editor.contains(block) || block === editor)
|
|
710
|
+
return false;
|
|
711
|
+
if (block.closest("ul,ol"))
|
|
712
|
+
return false;
|
|
713
|
+
if (block.tagName.toLowerCase() === "blockquote")
|
|
714
|
+
return false;
|
|
715
|
+
return Boolean(block.parentElement);
|
|
716
|
+
});
|
|
717
|
+
if (selected.length === 0)
|
|
718
|
+
return false;
|
|
719
|
+
let lastWrapped = null;
|
|
720
|
+
selected.forEach((block) => {
|
|
721
|
+
const quote = document.createElement("blockquote");
|
|
722
|
+
block.parentElement?.insertBefore(quote, block);
|
|
723
|
+
quote.appendChild(block);
|
|
724
|
+
lastWrapped = block;
|
|
725
|
+
});
|
|
726
|
+
if (lastWrapped)
|
|
727
|
+
focusElementEnd(lastWrapped);
|
|
728
|
+
return true;
|
|
729
|
+
};
|
|
613
730
|
if (!range)
|
|
614
731
|
return;
|
|
615
732
|
let node = range.commonAncestorContainer;
|
|
@@ -618,16 +735,36 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
618
735
|
const element = node;
|
|
619
736
|
const quote = element?.closest?.('blockquote');
|
|
620
737
|
if (quote && editableRef.current?.contains(quote)) {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
safeSelectRange(nextRange);
|
|
628
|
-
handleInput();
|
|
629
|
-
return;
|
|
738
|
+
pushEditorHistory();
|
|
739
|
+
if (unwrapQuote(quote)) {
|
|
740
|
+
handleInput();
|
|
741
|
+
requestAnimationFrame(updateActiveState);
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
630
744
|
}
|
|
745
|
+
if (range.collapsed) {
|
|
746
|
+
const block = getCurrentBlock();
|
|
747
|
+
if (block) {
|
|
748
|
+
pushEditorHistory();
|
|
749
|
+
if (!wrapBlocks([block]))
|
|
750
|
+
return;
|
|
751
|
+
handleInput();
|
|
752
|
+
requestAnimationFrame(updateActiveState);
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
const blocks = getSelectedBlocks(range);
|
|
758
|
+
if (blocks.length > 0) {
|
|
759
|
+
pushEditorHistory();
|
|
760
|
+
if (!wrapBlocks(blocks))
|
|
761
|
+
return;
|
|
762
|
+
handleInput();
|
|
763
|
+
requestAnimationFrame(updateActiveState);
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
pushEditorHistory();
|
|
631
768
|
exec("formatBlock", "<blockquote>");
|
|
632
769
|
}
|
|
633
770
|
catch { }
|
|
@@ -750,8 +887,158 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
750
887
|
const applyTextColor = (color) => {
|
|
751
888
|
exec("foreColor", color);
|
|
752
889
|
};
|
|
890
|
+
const parseCssColor = (value) => {
|
|
891
|
+
const normalized = value.trim().toLowerCase();
|
|
892
|
+
if (!normalized || normalized === "transparent")
|
|
893
|
+
return null;
|
|
894
|
+
const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(normalized);
|
|
895
|
+
if (hex) {
|
|
896
|
+
const raw = hex[1].length === 3
|
|
897
|
+
? hex[1].split("").map((part) => part + part).join("")
|
|
898
|
+
: hex[1];
|
|
899
|
+
return {
|
|
900
|
+
r: parseInt(raw.slice(0, 2), 16),
|
|
901
|
+
g: parseInt(raw.slice(2, 4), 16),
|
|
902
|
+
b: parseInt(raw.slice(4, 6), 16),
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
const rgb = /^rgba?\(([^)]+)\)$/.exec(normalized);
|
|
906
|
+
if (!rgb)
|
|
907
|
+
return null;
|
|
908
|
+
const parts = rgb[1].split(",").map((part) => Number(part.trim()));
|
|
909
|
+
if (parts.length < 3 || parts.some((part) => !Number.isFinite(part)))
|
|
910
|
+
return null;
|
|
911
|
+
if (parts.length >= 4 && parts[3] === 0)
|
|
912
|
+
return null;
|
|
913
|
+
return {
|
|
914
|
+
r: Math.max(0, Math.min(255, parts[0])),
|
|
915
|
+
g: Math.max(0, Math.min(255, parts[1])),
|
|
916
|
+
b: Math.max(0, Math.min(255, parts[2])),
|
|
917
|
+
};
|
|
918
|
+
};
|
|
919
|
+
const cssColorToHex = (value) => {
|
|
920
|
+
const color = parseCssColor(value);
|
|
921
|
+
if (!color)
|
|
922
|
+
return "";
|
|
923
|
+
const toHex = (part) => Math.round(part).toString(16).padStart(2, "0");
|
|
924
|
+
return `#${toHex(color.r)}${toHex(color.g)}${toHex(color.b)}`;
|
|
925
|
+
};
|
|
926
|
+
const getRangeStartElement = (range) => {
|
|
927
|
+
const editor = editableRef.current;
|
|
928
|
+
if (!editor || !range)
|
|
929
|
+
return null;
|
|
930
|
+
let node = range.startContainer;
|
|
931
|
+
if (node === editor && editor.childNodes[range.startOffset]) {
|
|
932
|
+
node = editor.childNodes[range.startOffset];
|
|
933
|
+
}
|
|
934
|
+
if (node.nodeType === Node.TEXT_NODE)
|
|
935
|
+
node = node.parentElement;
|
|
936
|
+
return node instanceof HTMLElement && editor.contains(node) ? node : null;
|
|
937
|
+
};
|
|
938
|
+
const getInlineBackgroundHex = (element) => {
|
|
939
|
+
const editor = editableRef.current;
|
|
940
|
+
let current = element;
|
|
941
|
+
while (current && current !== editor) {
|
|
942
|
+
const inlineHex = cssColorToHex(current.style.backgroundColor || current.style.background);
|
|
943
|
+
if (inlineHex)
|
|
944
|
+
return inlineHex;
|
|
945
|
+
current = current.parentElement;
|
|
946
|
+
}
|
|
947
|
+
return "";
|
|
948
|
+
};
|
|
949
|
+
const currentPickerColorHex = () => {
|
|
950
|
+
if (typeof window === "undefined")
|
|
951
|
+
return colorPickerType === "text" ? "#000000" : "#ffffff";
|
|
952
|
+
const editor = editableRef.current;
|
|
953
|
+
if (!editor)
|
|
954
|
+
return colorPickerType === "text" ? "#000000" : "#ffffff";
|
|
955
|
+
const element = getRangeStartElement(getSelectionRangeInEditor());
|
|
956
|
+
if (colorPickerType === "background") {
|
|
957
|
+
return getInlineBackgroundHex(element) || "#ffffff";
|
|
958
|
+
}
|
|
959
|
+
const target = element || editor;
|
|
960
|
+
return cssColorToHex(window.getComputedStyle(target).color) || "#000000";
|
|
961
|
+
};
|
|
962
|
+
const relativeLuminance = (color) => {
|
|
963
|
+
const channel = (value) => {
|
|
964
|
+
const next = value / 255;
|
|
965
|
+
return next <= 0.03928 ? next / 12.92 : Math.pow((next + 0.055) / 1.055, 2.4);
|
|
966
|
+
};
|
|
967
|
+
return 0.2126 * channel(color.r) + 0.7152 * channel(color.g) + 0.0722 * channel(color.b);
|
|
968
|
+
};
|
|
969
|
+
const contrastRatio = (fg, bg) => {
|
|
970
|
+
const lighter = Math.max(relativeLuminance(fg), relativeLuminance(bg));
|
|
971
|
+
const darker = Math.min(relativeLuminance(fg), relativeLuminance(bg));
|
|
972
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
973
|
+
};
|
|
974
|
+
const readableTextColorForBackground = (background) => {
|
|
975
|
+
const bg = parseCssColor(background);
|
|
976
|
+
if (!bg)
|
|
977
|
+
return "";
|
|
978
|
+
const candidates = ["#111827", "#f9fafb", "#1f2937", "#ffffff", "#000000", "#374151", "#e5e7eb"];
|
|
979
|
+
return candidates
|
|
980
|
+
.map((candidate) => ({
|
|
981
|
+
color: candidate,
|
|
982
|
+
ratio: contrastRatio(parseCssColor(candidate), bg),
|
|
983
|
+
}))
|
|
984
|
+
.sort((a, b) => b.ratio - a.ratio)[0]?.color || "";
|
|
985
|
+
};
|
|
753
986
|
const applyBackgroundColor = (color) => {
|
|
754
|
-
|
|
987
|
+
try {
|
|
988
|
+
const editor = editableRef.current;
|
|
989
|
+
if (!editor)
|
|
990
|
+
return;
|
|
991
|
+
if (!restoreSavedSelection())
|
|
992
|
+
safeSelectRange(getSelectionRangeInEditor());
|
|
993
|
+
const range = getSelectionRangeInEditor();
|
|
994
|
+
const readableColor = readableTextColorForBackground(color);
|
|
995
|
+
if (!range)
|
|
996
|
+
return;
|
|
997
|
+
const applyToElement = (element) => {
|
|
998
|
+
element.style.backgroundColor = color;
|
|
999
|
+
if (readableColor)
|
|
1000
|
+
element.style.color = readableColor;
|
|
1001
|
+
};
|
|
1002
|
+
if (range.collapsed) {
|
|
1003
|
+
const span = document.createElement("span");
|
|
1004
|
+
applyToElement(span);
|
|
1005
|
+
span.textContent = "\u200B";
|
|
1006
|
+
range.insertNode(span);
|
|
1007
|
+
const nextRange = document.createRange();
|
|
1008
|
+
nextRange.setStart(span.firstChild || span, 1);
|
|
1009
|
+
nextRange.collapse(true);
|
|
1010
|
+
safeSelectRange(nextRange);
|
|
1011
|
+
savedRangeRef.current = nextRange.cloneRange();
|
|
1012
|
+
handleInput();
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
const selectedCells = Array.from(editor.querySelectorAll("td,th"))
|
|
1016
|
+
.filter((cell) => {
|
|
1017
|
+
try {
|
|
1018
|
+
return range.intersectsNode(cell);
|
|
1019
|
+
}
|
|
1020
|
+
catch {
|
|
1021
|
+
return false;
|
|
1022
|
+
}
|
|
1023
|
+
});
|
|
1024
|
+
if (selectedCells.length > 0) {
|
|
1025
|
+
selectedCells.forEach(applyToElement);
|
|
1026
|
+
handleInput();
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
const span = document.createElement("span");
|
|
1030
|
+
applyToElement(span);
|
|
1031
|
+
const fragment = range.extractContents();
|
|
1032
|
+
span.appendChild(fragment);
|
|
1033
|
+
range.insertNode(span);
|
|
1034
|
+
range.selectNodeContents(span);
|
|
1035
|
+
safeSelectRange(range);
|
|
1036
|
+
savedRangeRef.current = range.cloneRange();
|
|
1037
|
+
handleInput();
|
|
1038
|
+
}
|
|
1039
|
+
catch {
|
|
1040
|
+
exec("hiliteColor", color);
|
|
1041
|
+
}
|
|
755
1042
|
};
|
|
756
1043
|
const insertImage = () => {
|
|
757
1044
|
if (!media)
|
|
@@ -2300,6 +2587,35 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2300
2587
|
console.error("Error wrapping tables", e);
|
|
2301
2588
|
}
|
|
2302
2589
|
};
|
|
2590
|
+
const isCaretBoundaryBlock = (node) => {
|
|
2591
|
+
if (!(node instanceof HTMLElement))
|
|
2592
|
+
return false;
|
|
2593
|
+
const tag = node.tagName.toLowerCase();
|
|
2594
|
+
return (tag === "blockquote" ||
|
|
2595
|
+
tag === "table" ||
|
|
2596
|
+
node.getAttribute("data-table-wrapper") === "true");
|
|
2597
|
+
};
|
|
2598
|
+
const isEmptyCaretParagraph = (node) => {
|
|
2599
|
+
if (!(node instanceof HTMLParagraphElement))
|
|
2600
|
+
return false;
|
|
2601
|
+
return !node.textContent?.trim();
|
|
2602
|
+
};
|
|
2603
|
+
const createCaretParagraph = () => {
|
|
2604
|
+
const paragraph = document.createElement("p");
|
|
2605
|
+
paragraph.innerHTML = "<br>";
|
|
2606
|
+
paragraph.setAttribute("data-srte-caret-boundary", "true");
|
|
2607
|
+
return paragraph;
|
|
2608
|
+
};
|
|
2609
|
+
const ensureCaretBoundaryParagraphs = (root) => {
|
|
2610
|
+
const first = root.firstElementChild;
|
|
2611
|
+
if (isCaretBoundaryBlock(first) && !isEmptyCaretParagraph(first.previousSibling)) {
|
|
2612
|
+
root.insertBefore(createCaretParagraph(), first);
|
|
2613
|
+
}
|
|
2614
|
+
const last = root.lastElementChild;
|
|
2615
|
+
if (isCaretBoundaryBlock(last) && !isEmptyCaretParagraph(last.nextSibling)) {
|
|
2616
|
+
root.appendChild(createCaretParagraph());
|
|
2617
|
+
}
|
|
2618
|
+
};
|
|
2303
2619
|
const handleInput = () => {
|
|
2304
2620
|
if (isComposingRef.current)
|
|
2305
2621
|
return;
|
|
@@ -2310,6 +2626,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2310
2626
|
fixNegativeMargins(el);
|
|
2311
2627
|
// Ensure tables are wrapped for horizontal scrolling
|
|
2312
2628
|
ensureTableWrappers(el);
|
|
2629
|
+
// Keep a reachable typing position around isolating blocks at document edges
|
|
2630
|
+
ensureCaretBoundaryParagraphs(el);
|
|
2313
2631
|
// Add resize handles to tables
|
|
2314
2632
|
addTableResizeHandles();
|
|
2315
2633
|
if (!onChange)
|
|
@@ -2340,8 +2658,9 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2340
2658
|
const el = editableRef.current;
|
|
2341
2659
|
if (!el)
|
|
2342
2660
|
return;
|
|
2343
|
-
|
|
2344
|
-
|
|
2661
|
+
if (!restoreSavedSelection()) {
|
|
2662
|
+
el.focus();
|
|
2663
|
+
}
|
|
2345
2664
|
let sel = window.getSelection();
|
|
2346
2665
|
let range = sel && sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
|
|
2347
2666
|
if (!range || !el.contains(range.commonAncestorContainer)) {
|
|
@@ -2360,10 +2679,14 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2360
2679
|
const node = wrapper.firstChild;
|
|
2361
2680
|
if (!node || !range)
|
|
2362
2681
|
return;
|
|
2682
|
+
pushEditorHistory();
|
|
2683
|
+
if (!range.collapsed)
|
|
2684
|
+
range.deleteContents();
|
|
2363
2685
|
range.insertNode(node);
|
|
2364
2686
|
// Add resize handles to the new table
|
|
2365
|
-
|
|
2366
|
-
|
|
2687
|
+
const insertedTable = node instanceof HTMLTableElement ? node : node.querySelector("table");
|
|
2688
|
+
if (insertedTable) {
|
|
2689
|
+
const tbody = insertedTable.querySelector('tbody');
|
|
2367
2690
|
if (tbody) {
|
|
2368
2691
|
const rows = Array.from(tbody.querySelectorAll('tr'));
|
|
2369
2692
|
rows.forEach((row, index) => {
|
|
@@ -2706,19 +3029,43 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2706
3029
|
handleInput();
|
|
2707
3030
|
};
|
|
2708
3031
|
const applyBgToSelection = (hex, fallbackCell) => {
|
|
3032
|
+
const readableColor = readableTextColorForBackground(hex);
|
|
3033
|
+
const applyFill = (cell) => {
|
|
3034
|
+
cell.style.background = hex;
|
|
3035
|
+
if (readableColor)
|
|
3036
|
+
cell.style.color = readableColor;
|
|
3037
|
+
};
|
|
2709
3038
|
const sel = shouldUseTableSelection(fallbackCell) ? selectionRef.current : null;
|
|
2710
3039
|
if (sel) {
|
|
2711
3040
|
const cells = getCellsInGridRect(sel.tbody, sel.sr, sel.sc, sel.er, sel.ec);
|
|
2712
3041
|
clearSelectionDecor();
|
|
2713
|
-
cells.forEach(
|
|
2714
|
-
cell.style.background = hex;
|
|
2715
|
-
});
|
|
3042
|
+
cells.forEach(applyFill);
|
|
2716
3043
|
}
|
|
2717
3044
|
else if (fallbackCell) {
|
|
2718
3045
|
clearSelectionDecor();
|
|
2719
|
-
fallbackCell
|
|
3046
|
+
applyFill(fallbackCell);
|
|
2720
3047
|
}
|
|
2721
3048
|
};
|
|
3049
|
+
const tableCellFillHex = (cell) => {
|
|
3050
|
+
const storedSelectionBackground = cell.__rtePrevBg;
|
|
3051
|
+
const inlineBackground = typeof storedSelectionBackground === "string"
|
|
3052
|
+
? storedSelectionBackground
|
|
3053
|
+
: cell.style.backgroundColor || cell.style.background;
|
|
3054
|
+
const inlineHex = cssColorToHex(inlineBackground || "");
|
|
3055
|
+
if (inlineHex)
|
|
3056
|
+
return inlineHex;
|
|
3057
|
+
const computedHex = cssColorToHex(window.getComputedStyle(cell).backgroundColor);
|
|
3058
|
+
return computedHex || "#ffffff";
|
|
3059
|
+
};
|
|
3060
|
+
const tableMenuFillHex = (cell) => {
|
|
3061
|
+
const sel = shouldUseTableSelection(cell) ? selectionRef.current : null;
|
|
3062
|
+
if (!sel)
|
|
3063
|
+
return tableCellFillHex(cell);
|
|
3064
|
+
const cells = getCellsInGridRect(sel.tbody, sel.sr, sel.sc, sel.er, sel.ec);
|
|
3065
|
+
const first = cells[0] ? tableCellFillHex(cells[0]) : tableCellFillHex(cell);
|
|
3066
|
+
const allSame = cells.every((candidate) => tableCellFillHex(candidate) === first);
|
|
3067
|
+
return allSame ? first : tableCellFillHex(cell);
|
|
3068
|
+
};
|
|
2722
3069
|
const toggleBorderSelection = (fallbackCell) => {
|
|
2723
3070
|
const applyToggle = (cell) => {
|
|
2724
3071
|
const cur = cell.style.border;
|
|
@@ -2837,6 +3184,184 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2837
3184
|
handleInput();
|
|
2838
3185
|
}
|
|
2839
3186
|
};
|
|
3187
|
+
const getRangeFromPoint = (x, y) => {
|
|
3188
|
+
let range = null;
|
|
3189
|
+
// @ts-ignore
|
|
3190
|
+
if (document.caretRangeFromPoint) {
|
|
3191
|
+
// @ts-ignore
|
|
3192
|
+
range = document.caretRangeFromPoint(x, y);
|
|
3193
|
+
}
|
|
3194
|
+
else if (document.caretPositionFromPoint) {
|
|
3195
|
+
const pos = document.caretPositionFromPoint(x, y);
|
|
3196
|
+
if (pos) {
|
|
3197
|
+
range = document.createRange();
|
|
3198
|
+
range.setStart(pos.offsetNode, pos.offset);
|
|
3199
|
+
}
|
|
3200
|
+
}
|
|
3201
|
+
return range;
|
|
3202
|
+
};
|
|
3203
|
+
const getMovableElementFromNode = (node) => {
|
|
3204
|
+
const editor = editableRef.current;
|
|
3205
|
+
if (!editor || !node)
|
|
3206
|
+
return null;
|
|
3207
|
+
let element = node instanceof HTMLElement ? node : node.parentElement;
|
|
3208
|
+
if (!element || !editor.contains(element))
|
|
3209
|
+
return null;
|
|
3210
|
+
const image = element.closest("img");
|
|
3211
|
+
if (image && editor.contains(image)) {
|
|
3212
|
+
return (image.parentElement?.tagName === "A" ? image.parentElement : image);
|
|
3213
|
+
}
|
|
3214
|
+
const tableElement = element.closest("table");
|
|
3215
|
+
if (tableElement && editor.contains(tableElement)) {
|
|
3216
|
+
return (tableElement.closest('[data-table-wrapper="true"]') || tableElement);
|
|
3217
|
+
}
|
|
3218
|
+
const listElement = element.closest("ul,ol");
|
|
3219
|
+
if (listElement && editor.contains(listElement))
|
|
3220
|
+
return listElement;
|
|
3221
|
+
const block = element.closest('[data-table-wrapper="true"],blockquote,pre,p,h1,h2,h3,h4,h5,h6,div');
|
|
3222
|
+
if (!block || block === editor || !editor.contains(block))
|
|
3223
|
+
return null;
|
|
3224
|
+
if (block.getAttribute("data-srte-caret-boundary") === "true")
|
|
3225
|
+
return null;
|
|
3226
|
+
return block;
|
|
3227
|
+
};
|
|
3228
|
+
const updateDragHandleForTarget = (target) => {
|
|
3229
|
+
const scroller = editorScrollRef.current;
|
|
3230
|
+
if (dragHandleHideTimerRef.current != null) {
|
|
3231
|
+
window.clearTimeout(dragHandleHideTimerRef.current);
|
|
3232
|
+
dragHandleHideTimerRef.current = null;
|
|
3233
|
+
}
|
|
3234
|
+
if (!target || !scroller || !editableRef.current?.contains(target)) {
|
|
3235
|
+
setDragHandle(null);
|
|
3236
|
+
return;
|
|
3237
|
+
}
|
|
3238
|
+
const targetRect = target.getBoundingClientRect();
|
|
3239
|
+
const scrollRect = scroller.getBoundingClientRect();
|
|
3240
|
+
const next = {
|
|
3241
|
+
left: Math.max(4, targetRect.left - scrollRect.left + scroller.scrollLeft - 30),
|
|
3242
|
+
top: targetRect.top - scrollRect.top + scroller.scrollTop,
|
|
3243
|
+
height: Math.max(24, targetRect.height),
|
|
3244
|
+
target,
|
|
3245
|
+
};
|
|
3246
|
+
setDragHandle((current) => {
|
|
3247
|
+
if (current?.target === next.target &&
|
|
3248
|
+
Math.abs(current.left - next.left) < 1 &&
|
|
3249
|
+
Math.abs(current.top - next.top) < 1 &&
|
|
3250
|
+
Math.abs(current.height - next.height) < 1) {
|
|
3251
|
+
return current;
|
|
3252
|
+
}
|
|
3253
|
+
return next;
|
|
3254
|
+
});
|
|
3255
|
+
};
|
|
3256
|
+
const scheduleDragHandleHide = () => {
|
|
3257
|
+
if (dragHandleHideTimerRef.current != null) {
|
|
3258
|
+
window.clearTimeout(dragHandleHideTimerRef.current);
|
|
3259
|
+
}
|
|
3260
|
+
dragHandleHideTimerRef.current = window.setTimeout(() => {
|
|
3261
|
+
dragHandleHideTimerRef.current = null;
|
|
3262
|
+
if (!draggedBlockRef.current)
|
|
3263
|
+
setDragHandle(null);
|
|
3264
|
+
}, 120);
|
|
3265
|
+
};
|
|
3266
|
+
const getImageFromMovableBlock = (block) => {
|
|
3267
|
+
if (block.tagName === "IMG")
|
|
3268
|
+
return block;
|
|
3269
|
+
return block.querySelector("img");
|
|
3270
|
+
};
|
|
3271
|
+
const dropBlockAtPoint = (block, x, y) => {
|
|
3272
|
+
const editor = editableRef.current;
|
|
3273
|
+
if (!editor || !editor.contains(block))
|
|
3274
|
+
return false;
|
|
3275
|
+
const under = document.elementFromPoint(x, y);
|
|
3276
|
+
const draggedImage = getImageFromMovableBlock(block);
|
|
3277
|
+
const targetCell = draggedImage ? getClosestCell(under) : null;
|
|
3278
|
+
if (draggedImage && targetCell && !block.contains(targetCell)) {
|
|
3279
|
+
if (targetCell.contains(block))
|
|
3280
|
+
return false;
|
|
3281
|
+
const range = getRangeFromPoint(x, y);
|
|
3282
|
+
if (range && targetCell.contains(range.commonAncestorContainer)) {
|
|
3283
|
+
range.insertNode(block);
|
|
3284
|
+
}
|
|
3285
|
+
else {
|
|
3286
|
+
targetCell.appendChild(block);
|
|
3287
|
+
}
|
|
3288
|
+
return true;
|
|
3289
|
+
}
|
|
3290
|
+
const target = getMovableElementFromNode(under);
|
|
3291
|
+
if (target && target !== block && !block.contains(target) && !target.contains(block)) {
|
|
3292
|
+
const rect = target.getBoundingClientRect();
|
|
3293
|
+
const parent = target.parentElement;
|
|
3294
|
+
if (!parent)
|
|
3295
|
+
return false;
|
|
3296
|
+
if (y < rect.top + rect.height / 2)
|
|
3297
|
+
parent.insertBefore(block, target);
|
|
3298
|
+
else
|
|
3299
|
+
parent.insertBefore(block, target.nextSibling);
|
|
3300
|
+
return true;
|
|
3301
|
+
}
|
|
3302
|
+
const range = getRangeFromPoint(x, y);
|
|
3303
|
+
if (range && editor.contains(range.commonAncestorContainer)) {
|
|
3304
|
+
if (block.contains(range.commonAncestorContainer))
|
|
3305
|
+
return false;
|
|
3306
|
+
range.insertNode(block);
|
|
3307
|
+
return true;
|
|
3308
|
+
}
|
|
3309
|
+
editor.appendChild(block);
|
|
3310
|
+
return true;
|
|
3311
|
+
};
|
|
3312
|
+
const getTopLevelMovableElement = () => {
|
|
3313
|
+
const editor = editableRef.current;
|
|
3314
|
+
if (!editor)
|
|
3315
|
+
return null;
|
|
3316
|
+
const imageTarget = selectedImage?.parentElement?.tagName === "A"
|
|
3317
|
+
? selectedImage.parentElement
|
|
3318
|
+
: selectedImage;
|
|
3319
|
+
if (imageTarget && editor.contains(imageTarget))
|
|
3320
|
+
return imageTarget;
|
|
3321
|
+
const range = getSelectionRangeInEditor();
|
|
3322
|
+
let node = range?.commonAncestorContainer || null;
|
|
3323
|
+
if (node && node.nodeType === Node.TEXT_NODE)
|
|
3324
|
+
node = node.parentNode;
|
|
3325
|
+
let element = node instanceof HTMLElement ? node : null;
|
|
3326
|
+
if (!element)
|
|
3327
|
+
return null;
|
|
3328
|
+
return getMovableElementFromNode(element);
|
|
3329
|
+
};
|
|
3330
|
+
const elementSibling = (element, direction) => {
|
|
3331
|
+
let sibling = direction === "previous" ? element.previousSibling : element.nextSibling;
|
|
3332
|
+
while (sibling && sibling.nodeType === Node.TEXT_NODE && !sibling.textContent?.trim()) {
|
|
3333
|
+
sibling = direction === "previous" ? sibling.previousSibling : sibling.nextSibling;
|
|
3334
|
+
}
|
|
3335
|
+
return sibling;
|
|
3336
|
+
};
|
|
3337
|
+
const moveCurrentElement = (direction) => {
|
|
3338
|
+
const editor = editableRef.current;
|
|
3339
|
+
const target = getTopLevelMovableElement();
|
|
3340
|
+
if (!editor || !target)
|
|
3341
|
+
return;
|
|
3342
|
+
pushEditorHistory();
|
|
3343
|
+
if (direction === "up") {
|
|
3344
|
+
const previous = elementSibling(target, "previous");
|
|
3345
|
+
if (previous)
|
|
3346
|
+
target.parentElement?.insertBefore(target, previous);
|
|
3347
|
+
}
|
|
3348
|
+
else if (direction === "down") {
|
|
3349
|
+
const next = elementSibling(target, "next");
|
|
3350
|
+
if (next)
|
|
3351
|
+
target.parentElement?.insertBefore(next, target);
|
|
3352
|
+
}
|
|
3353
|
+
else {
|
|
3354
|
+
const current = parseInt(target.style.marginLeft || "0", 10) || 0;
|
|
3355
|
+
const nextMargin = direction === "right"
|
|
3356
|
+
? Math.min(current + 24, 240)
|
|
3357
|
+
: Math.max(current - 24, 0);
|
|
3358
|
+
target.style.marginLeft = nextMargin ? `${nextMargin}px` : "";
|
|
3359
|
+
}
|
|
3360
|
+
focusElementEnd(target);
|
|
3361
|
+
setSelectedImage(target.tagName === "IMG" ? target : target.querySelector("img"));
|
|
3362
|
+
scheduleImageOverlay();
|
|
3363
|
+
handleInput();
|
|
3364
|
+
};
|
|
2840
3365
|
const addTableResizeHandles = () => {
|
|
2841
3366
|
if (!table)
|
|
2842
3367
|
return;
|
|
@@ -2943,7 +3468,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2943
3468
|
} }), _jsx("input", { ref: mdInputRef, type: "file", accept: ".md,.markdown,text/markdown,text/plain", style: { display: "none" }, onChange: (e) => {
|
|
2944
3469
|
importTextFile(e.currentTarget.files, "md");
|
|
2945
3470
|
e.currentTarget.value = "";
|
|
2946
|
-
} }), _jsxs("select", {
|
|
3471
|
+
} }), _jsxs("select", { value: currentBlockType, onMouseDown: preserveEditorSelection, onChange: (e) => {
|
|
2947
3472
|
const val = e.target.value;
|
|
2948
3473
|
if (val === "p")
|
|
2949
3474
|
applyFormatBlock("<p>");
|
|
@@ -2960,7 +3485,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2960
3485
|
borderRadius: 6,
|
|
2961
3486
|
background: "var(--srte-input-bg)",
|
|
2962
3487
|
color: "var(--srte-input-text)",
|
|
2963
|
-
}, children: [_jsx("option", { value: "p", children: "Paragraph" }), _jsx("option", { value: "h1", children: "Heading 1" }), _jsx("option", { value: "h2", children: "Heading 2" }), _jsx("option", { value: "h3", children: "Heading 3" })] }), _jsx("button", { title: "Bold", onClick: () => exec("bold"), "aria-pressed": activeState.bold, style: activeButtonStyle(activeState.bold), children: _jsx("span", { style: { fontWeight: 700 }, children: "B" }) }), _jsx("button", { title: "Italic", onClick: () => exec("italic"), "aria-pressed": activeState.italic, style: activeButtonStyle(activeState.italic, { fontStyle: "italic" }), children: "I" }), _jsx("button", { title: "Underline", onClick: () => exec("underline"), "aria-pressed": activeState.underline, style: activeButtonStyle(activeState.underline, { textDecoration: "underline" }), children: "U" }), _jsx("button", { title: "Strikethrough", onClick: () => exec("strikeThrough"), "aria-pressed": activeState.strikeThrough, style: activeButtonStyle(activeState.strikeThrough, { textDecoration: "line-through" }), children: "S" }), _jsxs("select", { value: currentFontSize, onMouseDown: () => {
|
|
3488
|
+
}, children: [_jsx("option", { value: "p", children: "Paragraph" }), _jsx("option", { value: "h1", children: "Heading 1" }), _jsx("option", { value: "h2", children: "Heading 2" }), _jsx("option", { value: "h3", children: "Heading 3" })] }), _jsx("button", { title: "Bold", onClick: () => exec("bold"), "aria-pressed": activeState.bold, style: activeButtonStyle(activeState.bold), children: _jsx("span", { style: { fontWeight: 700 }, children: "B" }) }), _jsx("button", { title: "Italic", onClick: () => exec("italic"), "aria-pressed": activeState.italic, style: activeButtonStyle(activeState.italic, { fontStyle: "italic" }), children: "I" }), _jsx("button", { title: "Underline", onClick: () => exec("underline"), "aria-pressed": activeState.underline, style: activeButtonStyle(activeState.underline, { textDecoration: "underline" }), children: "U" }), _jsx("button", { title: "Strikethrough", onClick: () => exec("strikeThrough"), "aria-pressed": activeState.strikeThrough, style: activeButtonStyle(activeState.strikeThrough, { textDecoration: "line-through" }), children: "S" }), showFontSize && (_jsxs("select", { value: currentFontSize, onMouseDown: () => {
|
|
2964
3489
|
// Save selection before dropdown interaction
|
|
2965
3490
|
const sel = window.getSelection();
|
|
2966
3491
|
if (sel && sel.rangeCount > 0) {
|
|
@@ -2977,7 +3502,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2977
3502
|
borderRadius: 6,
|
|
2978
3503
|
background: "var(--srte-input-bg)",
|
|
2979
3504
|
color: "var(--srte-input-text)",
|
|
2980
|
-
}, children: [_jsx("option", { value: "", disabled: true, children: "Size" }), _jsx("option", { value: "8", children: "8" }), _jsx("option", { value: "9", children: "9" }), _jsx("option", { value: "10", children: "10" }), _jsx("option", { value: "11", children: "11" }), _jsx("option", { value: "12", children: "12" }), _jsx("option", { value: "14", children: "14" }), _jsx("option", { value: "18", children: "18" }), _jsx("option", { value: "24", children: "24" }), _jsx("option", { value: "30", children: "30" }), _jsx("option", { value: "36", children: "36" }), _jsx("option", { value: "48", children: "48" }), _jsx("option", { value: "60", children: "60" }), _jsx("option", { value: "72", children: "72" }), _jsx("option", { value: "96", children: "96" })] }), preserveFontFamily && (_jsxs("select", { value: currentFont, onMouseDown: () => {
|
|
3505
|
+
}, children: [_jsx("option", { value: "", disabled: true, children: "Size" }), _jsx("option", { value: "8", children: "8" }), _jsx("option", { value: "9", children: "9" }), _jsx("option", { value: "10", children: "10" }), _jsx("option", { value: "11", children: "11" }), _jsx("option", { value: "12", children: "12" }), _jsx("option", { value: "14", children: "14" }), _jsx("option", { value: "18", children: "18" }), _jsx("option", { value: "24", children: "24" }), _jsx("option", { value: "30", children: "30" }), _jsx("option", { value: "36", children: "36" }), _jsx("option", { value: "48", children: "48" }), _jsx("option", { value: "60", children: "60" }), _jsx("option", { value: "72", children: "72" }), _jsx("option", { value: "96", children: "96" })] })), preserveFontFamily && (_jsxs("select", { value: currentFont, onMouseDown: () => {
|
|
2981
3506
|
const sel = window.getSelection();
|
|
2982
3507
|
if (sel && sel.rangeCount > 0) {
|
|
2983
3508
|
const range = sel.getRangeAt(0);
|
|
@@ -3185,7 +3710,12 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3185
3710
|
borderRadius: 6,
|
|
3186
3711
|
background: "var(--srte-input-bg)",
|
|
3187
3712
|
color: "var(--srte-input-text)",
|
|
3188
|
-
}, children: "\u2795 Table" })),
|
|
3713
|
+
}, children: "\u2795 Table" })), _jsxs("div", { style: {
|
|
3714
|
+
display: "inline-flex",
|
|
3715
|
+
gap: 4,
|
|
3716
|
+
alignItems: "center",
|
|
3717
|
+
marginLeft: 6,
|
|
3718
|
+
}, children: [_jsx("span", { style: { fontSize: 12, opacity: 0.7 }, children: "Move:" }), _jsx("button", { title: "Move selected block up", onClick: () => moveCurrentElement("up"), style: activeButtonStyle(false, { height: 28, minWidth: 28, padding: "0 6px" }), children: "\u2191" }), _jsx("button", { title: "Move selected block down", onClick: () => moveCurrentElement("down"), style: activeButtonStyle(false, { height: 28, minWidth: 28, padding: "0 6px" }), children: "\u2193" }), _jsx("button", { title: "Move selected block left", onClick: () => moveCurrentElement("left"), style: activeButtonStyle(false, { height: 28, minWidth: 28, padding: "0 6px" }), children: "\u2190" }), _jsx("button", { title: "Move selected block right", onClick: () => moveCurrentElement("right"), style: activeButtonStyle(false, { height: 28, minWidth: 28, padding: "0 6px" }), children: "\u2192" })] }), _jsx("button", { title: "Undo", onClick: () => exec("undo"), style: {
|
|
3189
3719
|
height: 32,
|
|
3190
3720
|
padding: "0 10px",
|
|
3191
3721
|
border: "1px solid var(--srte-input-border)",
|
|
@@ -3352,7 +3882,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3352
3882
|
borderRadius: 4,
|
|
3353
3883
|
background: color,
|
|
3354
3884
|
cursor: 'pointer',
|
|
3355
|
-
}, title: color }, color))) }), _jsxs("div", { style: { marginBottom: 12 }, children: [_jsx("label", { style: { display: 'block', marginBottom: 6, fontSize: 12 }, children: "Custom color:" }), _jsx("input", { type: "color", onChange: (e) => {
|
|
3885
|
+
}, title: color }, color))) }), _jsxs("div", { style: { marginBottom: 12 }, children: [_jsx("label", { style: { display: 'block', marginBottom: 6, fontSize: 12 }, children: "Custom color:" }), _jsx("input", { type: "color", value: currentPickerColorHex(), onChange: (e) => {
|
|
3356
3886
|
if (colorPickerType === 'text') {
|
|
3357
3887
|
applyTextColor(e.target.value);
|
|
3358
3888
|
}
|
|
@@ -3521,7 +4051,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3521
4051
|
borderRadius: 4,
|
|
3522
4052
|
background: "var(--srte-input-bg)",
|
|
3523
4053
|
color: "var(--srte-modal-text)",
|
|
3524
|
-
}, title: sym, children: sym }, i)))] })] }) })), _jsxs("div", { ref: editorScrollRef, style: {
|
|
4054
|
+
}, title: sym, children: sym }, i)))] })] }) })), _jsxs("div", { ref: editorScrollRef, onScroll: () => updateDragHandleForTarget(dragHandle?.target || null), style: {
|
|
3525
4055
|
width: "100%",
|
|
3526
4056
|
maxWidth: "100%",
|
|
3527
4057
|
flex: "1 1 auto",
|
|
@@ -3537,6 +4067,13 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3537
4067
|
}, children: [_jsx("div", { ref: editableRef, contentEditable: !readOnly, suppressContentEditableWarning: true, onInput: handleInput, onKeyUp: updateActiveState, onMouseUp: updateActiveState, onCompositionStart: () => (isComposingRef.current = true), onCompositionEnd: () => {
|
|
3538
4068
|
isComposingRef.current = false;
|
|
3539
4069
|
handleInput();
|
|
4070
|
+
}, onMouseMove: (e) => {
|
|
4071
|
+
if (draggedBlockRef.current)
|
|
4072
|
+
return;
|
|
4073
|
+
updateDragHandleForTarget(getMovableElementFromNode(e.target));
|
|
4074
|
+
}, onMouseLeave: () => {
|
|
4075
|
+
if (!draggedBlockRef.current)
|
|
4076
|
+
scheduleDragHandleHide();
|
|
3540
4077
|
}, onPaste: (e) => {
|
|
3541
4078
|
const items = e.clipboardData?.files;
|
|
3542
4079
|
if (media && items && items.length) {
|
|
@@ -3554,29 +4091,30 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3554
4091
|
}
|
|
3555
4092
|
}, onDragOver: (e) => {
|
|
3556
4093
|
// Allow dragging images within editor and file drops
|
|
3557
|
-
if (
|
|
4094
|
+
if (draggedBlockRef.current ||
|
|
4095
|
+
draggedImageRef.current ||
|
|
3558
4096
|
e.dataTransfer?.types?.includes("Files")) {
|
|
3559
4097
|
e.preventDefault();
|
|
3560
4098
|
}
|
|
3561
4099
|
}, onDrop: (e) => {
|
|
4100
|
+
if (draggedBlockRef.current) {
|
|
4101
|
+
e.preventDefault();
|
|
4102
|
+
const block = draggedBlockRef.current;
|
|
4103
|
+
draggedBlockRef.current = null;
|
|
4104
|
+
pushEditorHistory();
|
|
4105
|
+
if (dropBlockAtPoint(block, e.clientX, e.clientY)) {
|
|
4106
|
+
focusElementEnd(block);
|
|
4107
|
+
updateDragHandleForTarget(block);
|
|
4108
|
+
handleInput();
|
|
4109
|
+
}
|
|
4110
|
+
return;
|
|
4111
|
+
}
|
|
3562
4112
|
// Move existing dragged image inside editor
|
|
3563
4113
|
if (draggedImageRef.current) {
|
|
3564
4114
|
e.preventDefault();
|
|
3565
4115
|
const x = e.clientX;
|
|
3566
4116
|
const y = e.clientY;
|
|
3567
|
-
let range =
|
|
3568
|
-
// @ts-ignore
|
|
3569
|
-
if (document.caretRangeFromPoint) {
|
|
3570
|
-
// @ts-ignore
|
|
3571
|
-
range = document.caretRangeFromPoint(x, y);
|
|
3572
|
-
}
|
|
3573
|
-
else if (document.caretPositionFromPoint) {
|
|
3574
|
-
const pos = document.caretPositionFromPoint(x, y);
|
|
3575
|
-
if (pos) {
|
|
3576
|
-
range = document.createRange();
|
|
3577
|
-
range.setStart(pos.offsetNode, pos.offset);
|
|
3578
|
-
}
|
|
3579
|
-
}
|
|
4117
|
+
let range = getRangeFromPoint(x, y);
|
|
3580
4118
|
const img = draggedImageRef.current;
|
|
3581
4119
|
draggedImageRef.current = null;
|
|
3582
4120
|
if (range &&
|
|
@@ -3585,6 +4123,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3585
4123
|
// Avoid inserting inside the image itself
|
|
3586
4124
|
if (range.startContainer === img || range.endContainer === img)
|
|
3587
4125
|
return;
|
|
4126
|
+
pushEditorHistory();
|
|
3588
4127
|
// If dropping inside a link, insert right after the link element
|
|
3589
4128
|
let container = range.commonAncestorContainer;
|
|
3590
4129
|
let linkAncestor = null;
|
|
@@ -3617,19 +4156,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3617
4156
|
// Try to move caret to drop point
|
|
3618
4157
|
const x = e.clientX;
|
|
3619
4158
|
const y = e.clientY;
|
|
3620
|
-
let range =
|
|
3621
|
-
// @ts-ignore
|
|
3622
|
-
if (document.caretRangeFromPoint) {
|
|
3623
|
-
// @ts-ignore
|
|
3624
|
-
range = document.caretRangeFromPoint(x, y);
|
|
3625
|
-
}
|
|
3626
|
-
else if (document.caretPositionFromPoint) {
|
|
3627
|
-
const pos = document.caretPositionFromPoint(x, y);
|
|
3628
|
-
if (pos) {
|
|
3629
|
-
range = document.createRange();
|
|
3630
|
-
range.setStart(pos.offsetNode, pos.offset);
|
|
3631
|
-
}
|
|
3632
|
-
}
|
|
4159
|
+
let range = getRangeFromPoint(x, y);
|
|
3633
4160
|
if (range) {
|
|
3634
4161
|
const sel = window.getSelection();
|
|
3635
4162
|
sel?.removeAllRanges();
|
|
@@ -3671,6 +4198,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3671
4198
|
}
|
|
3672
4199
|
}, onDragEnd: () => {
|
|
3673
4200
|
draggedImageRef.current = null;
|
|
4201
|
+
draggedBlockRef.current = null;
|
|
3674
4202
|
}, style: {
|
|
3675
4203
|
minHeight: typeof minHeight === "number" ? `${minHeight}px` : minHeight,
|
|
3676
4204
|
maxWidth: "100%",
|
|
@@ -3824,7 +4352,55 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3824
4352
|
setTableMenu(null);
|
|
3825
4353
|
setImageMenu(null);
|
|
3826
4354
|
}
|
|
3827
|
-
} }),
|
|
4355
|
+
} }), dragHandle && !readOnly && (_jsx("button", { type: "button", draggable: true, title: "Drag block", "aria-label": "Drag block", onMouseEnter: () => {
|
|
4356
|
+
if (dragHandleHideTimerRef.current != null) {
|
|
4357
|
+
window.clearTimeout(dragHandleHideTimerRef.current);
|
|
4358
|
+
dragHandleHideTimerRef.current = null;
|
|
4359
|
+
}
|
|
4360
|
+
}, onMouseLeave: () => {
|
|
4361
|
+
if (!draggedBlockRef.current)
|
|
4362
|
+
scheduleDragHandleHide();
|
|
4363
|
+
}, onMouseDown: (e) => {
|
|
4364
|
+
e.stopPropagation();
|
|
4365
|
+
}, onDragStart: (e) => {
|
|
4366
|
+
draggedBlockRef.current = dragHandle.target;
|
|
4367
|
+
e.dataTransfer.effectAllowed = "move";
|
|
4368
|
+
e.dataTransfer.setData("text/plain", "moving-block");
|
|
4369
|
+
try {
|
|
4370
|
+
const ghost = dragHandle.target.cloneNode(true);
|
|
4371
|
+
ghost.style.position = "fixed";
|
|
4372
|
+
ghost.style.left = "-10000px";
|
|
4373
|
+
ghost.style.top = "-10000px";
|
|
4374
|
+
ghost.style.width = `${Math.min(dragHandle.target.getBoundingClientRect().width, 480)}px`;
|
|
4375
|
+
ghost.style.opacity = "0.75";
|
|
4376
|
+
document.body.appendChild(ghost);
|
|
4377
|
+
e.dataTransfer.setDragImage(ghost, 12, 12);
|
|
4378
|
+
window.setTimeout(() => ghost.remove(), 0);
|
|
4379
|
+
}
|
|
4380
|
+
catch { }
|
|
4381
|
+
}, onDragEnd: () => {
|
|
4382
|
+
draggedBlockRef.current = null;
|
|
4383
|
+
updateDragHandleForTarget(dragHandle.target);
|
|
4384
|
+
}, style: {
|
|
4385
|
+
position: "absolute",
|
|
4386
|
+
left: dragHandle.left,
|
|
4387
|
+
top: dragHandle.top + Math.max(0, (dragHandle.height - 24) / 2),
|
|
4388
|
+
width: 24,
|
|
4389
|
+
height: 24,
|
|
4390
|
+
display: "inline-flex",
|
|
4391
|
+
alignItems: "center",
|
|
4392
|
+
justifyContent: "center",
|
|
4393
|
+
border: "1px solid var(--srte-border)",
|
|
4394
|
+
borderRadius: 6,
|
|
4395
|
+
background: "var(--srte-input-bg)",
|
|
4396
|
+
color: "var(--srte-input-text)",
|
|
4397
|
+
boxShadow: "var(--srte-menu-shadow)",
|
|
4398
|
+
cursor: "grab",
|
|
4399
|
+
zIndex: 8,
|
|
4400
|
+
fontSize: 14,
|
|
4401
|
+
lineHeight: 1,
|
|
4402
|
+
padding: 0,
|
|
4403
|
+
}, children: "\u22EE\u22EE" })), selectedImage && imageOverlay && (_jsxs("div", { style: {
|
|
3828
4404
|
position: "absolute",
|
|
3829
4405
|
left: imageOverlay.left,
|
|
3830
4406
|
top: imageOverlay.top,
|
|
@@ -3955,7 +4531,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3955
4531
|
alignItems: "center",
|
|
3956
4532
|
padding: "4px 6px",
|
|
3957
4533
|
fontSize: 12,
|
|
3958
|
-
}, children: [_jsx("span", { children: "Fill:" }), _jsx("input", { type: "color",
|
|
4534
|
+
}, children: [_jsx("span", { children: "Fill:" }), _jsx("input", { type: "color", value: tableMenuFillHex(tableMenu.cell), onChange: (e) => {
|
|
3959
4535
|
runTableCellAction(tableMenu.cell, (cell) => applyBgToSelection(e.target.value, cell));
|
|
3960
4536
|
}, style: {
|
|
3961
4537
|
width: 28,
|
|
@@ -37,7 +37,7 @@ function ClassicEditorHost(props, ref) {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
catch { }
|
|
40
|
-
}, placeholder: props.placeholder, minHeight: props.minHeight, maxHeight: props.maxHeight, readOnly: props.readOnly, table: props.table, media: props.media, formula: props.formula, mediaManager: props.mediaManager, theme: props.theme, className: props.className }) }));
|
|
40
|
+
}, placeholder: props.placeholder, minHeight: props.minHeight, maxHeight: props.maxHeight, readOnly: props.readOnly, table: props.table, media: props.media, formula: props.formula, showFontSize: props.showFontSize, mediaManager: props.mediaManager, theme: props.theme, className: props.className }) }));
|
|
41
41
|
}
|
|
42
42
|
const ClassicEditorHostWithRef = React.forwardRef(ClassicEditorHost);
|
|
43
43
|
function initClassicEditor(opts) {
|
package/dist/theme.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export type SrteTheme = 'light' | 'dark';
|
|
2
|
-
export declare const SRTE_DEFAULT_CSS = "\n.srte-editor {\n --srte-bg: #ffffff;\n --srte-text: #111111;\n --srte-text-muted: #4b5563;\n --srte-border: #dddddd;\n --srte-border-light: #eeeeee;\n --srte-toolbar-bg: #ffffff;\n --srte-input-bg: #ffffff;\n --srte-input-text: #111111;\n --srte-input-border: #e5e7eb;\n --srte-modal-backdrop: rgba(0, 0, 0, 0.35);\n --srte-modal-backdrop-filter: blur(2px);\n --srte-modal-bg: #ffffff;\n --srte-modal-text: #000000;\n --srte-menu-bg: #ffffff;\n --srte-menu-text: #111111;\n --srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);\n --srte-accent: #1e90ff;\n --srte-accent-bg: rgba(30, 144, 255, 0.15);\n --srte-danger: #dc2626;\n --srte-primary: #2563eb;\n --srte-surface-subtle: #f3f4f6;\n --srte-on-primary: #ffffff;\n --srte-cancel-bg: #f3f4f6;\n}\n.srte-editor.srte-dark {\n --srte-bg: #1e1e1e;\n --srte-text: #e0e0e0;\n --srte-text-muted: #9ca3af;\n --srte-border: #3a3a3a;\n --srte-border-light: #2e2e2e;\n --srte-toolbar-bg: #252525;\n --srte-input-bg: #2a2a2a;\n --srte-input-text: #e0e0e0;\n --srte-input-border: #444444;\n --srte-modal-backdrop: rgba(0, 0, 0, 0.22);\n --srte-modal-backdrop-filter: blur(10px) saturate(0.9);\n --srte-modal-bg: #1e293b;\n --srte-modal-text: #e0e0e0;\n --srte-menu-bg: #1e293b;\n --srte-menu-text: #e0e0e0;\n --srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);\n --srte-accent: #3b9eff;\n --srte-accent-bg: rgba(59, 158, 255, 0.2);\n --srte-danger: #ef4444;\n --srte-primary: #3b82f6;\n --srte-surface-subtle: #333333;\n --srte-on-primary: #ffffff;\n --srte-cancel-bg: #333333;\n}\n.srte-editor [contenteditable] blockquote {\n border-left: 4px solid var(--srte-accent);\n margin: 0.75em 0;\n padding: 0.5em 1em;\n background: var(--srte-surface-subtle);\n color: var(--srte-text);\n}\n.srte-editor [contenteditable] ul {\n list-style-type: disc;\n list-style-position: outside;\n margin: 0.75em 0;\n padding-left: 1.75em;\n}\n.srte-editor [contenteditable] ol {\n list-style-type: decimal;\n list-style-position: outside;\n margin: 0.75em 0;\n padding-left: 1.75em;\n}\n.srte-editor [contenteditable] li {\n display: list-item;\n margin: 0.25em 0;\n padding-left: 0.25em;\n}\n.srte-editor [contenteditable] li::marker {\n color: currentColor;\n}\n.srte-editor.srte-dark [contenteditable] [style*=\"color\"]:not(.srte-preserve-colors):not(.srte-preserve-colors *),\n.srte-editor.srte-dark [contenteditable] [style*=\"background\"]:not(.srte-preserve-colors):not(.srte-preserve-colors *) {\n color: var(--srte-text) !important;\n background: transparent !important;\n background-color: transparent !important;\n}\n.srte-editor [contenteditable] sub,\n.srte-editor [contenteditable] sup {\n line-height: 0;\n}\n";
|
|
2
|
+
export declare const SRTE_DEFAULT_CSS = "\n.srte-editor {\n --srte-bg: #ffffff;\n --srte-text: #111111;\n --srte-text-muted: #4b5563;\n --srte-border: #dddddd;\n --srte-border-light: #eeeeee;\n --srte-toolbar-bg: #ffffff;\n --srte-input-bg: #ffffff;\n --srte-input-text: #111111;\n --srte-input-border: #e5e7eb;\n --srte-modal-backdrop: rgba(0, 0, 0, 0.35);\n --srte-modal-backdrop-filter: blur(2px);\n --srte-modal-bg: #ffffff;\n --srte-modal-text: #000000;\n --srte-menu-bg: #ffffff;\n --srte-menu-text: #111111;\n --srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);\n --srte-accent: #1e90ff;\n --srte-accent-bg: rgba(30, 144, 255, 0.15);\n --srte-danger: #dc2626;\n --srte-primary: #2563eb;\n --srte-surface-subtle: #f3f4f6;\n --srte-on-primary: #ffffff;\n --srte-cancel-bg: #f3f4f6;\n}\n.srte-editor.srte-dark {\n --srte-bg: #1e1e1e;\n --srte-text: #e0e0e0;\n --srte-text-muted: #9ca3af;\n --srte-border: #3a3a3a;\n --srte-border-light: #2e2e2e;\n --srte-toolbar-bg: #252525;\n --srte-input-bg: #2a2a2a;\n --srte-input-text: #e0e0e0;\n --srte-input-border: #444444;\n --srte-modal-backdrop: rgba(0, 0, 0, 0.22);\n --srte-modal-backdrop-filter: blur(10px) saturate(0.9);\n --srte-modal-bg: #1e293b;\n --srte-modal-text: #e0e0e0;\n --srte-menu-bg: #1e293b;\n --srte-menu-text: #e0e0e0;\n --srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);\n --srte-accent: #3b9eff;\n --srte-accent-bg: rgba(59, 158, 255, 0.2);\n --srte-danger: #ef4444;\n --srte-primary: #3b82f6;\n --srte-surface-subtle: #333333;\n --srte-on-primary: #ffffff;\n --srte-cancel-bg: #333333;\n}\n.srte-editor [contenteditable] blockquote {\n border-left: 4px solid var(--srte-accent);\n margin: 0.75em 0;\n padding: 0.5em 1em;\n background: var(--srte-surface-subtle);\n color: var(--srte-text);\n}\n.srte-editor [contenteditable] p,\n.srte-editor [contenteditable] h1,\n.srte-editor [contenteditable] h2,\n.srte-editor [contenteditable] h3 {\n color: inherit;\n}\n.srte-editor [contenteditable] p {\n display: block;\n margin: 0 0 0.75em;\n font-size: 1em;\n font-weight: 400;\n line-height: 1.6;\n}\n.srte-editor [contenteditable] h1,\n.srte-editor [contenteditable] h2,\n.srte-editor [contenteditable] h3 {\n display: block;\n margin: 0.75em 0 0.4em;\n font-weight: 700;\n line-height: 1.25;\n}\n.srte-editor [contenteditable] h1 {\n font-size: 2em;\n}\n.srte-editor [contenteditable] h2 {\n font-size: 1.5em;\n}\n.srte-editor [contenteditable] h3 {\n font-size: 1.25em;\n}\n.srte-editor [contenteditable] > :first-child {\n margin-top: 0;\n}\n.srte-editor [contenteditable] ul {\n list-style-type: disc;\n list-style-position: outside;\n margin: 0.75em 0;\n padding-left: 1.75em;\n}\n.srte-editor [contenteditable] ol {\n list-style-type: decimal;\n list-style-position: outside;\n margin: 0.75em 0;\n padding-left: 1.75em;\n}\n.srte-editor [contenteditable] li {\n display: list-item;\n margin: 0.25em 0;\n padding-left: 0.25em;\n}\n.srte-editor [contenteditable] li::marker {\n color: currentColor;\n}\n.srte-editor.srte-dark [contenteditable] [style*=\"color\"]:not(td):not(th):not(.srte-preserve-colors):not(.srte-preserve-colors *),\n.srte-editor.srte-dark [contenteditable] [style*=\"background\"]:not(td):not(th):not(.srte-preserve-colors):not(.srte-preserve-colors *) {\n color: var(--srte-text) !important;\n background: transparent !important;\n background-color: transparent !important;\n}\n.srte-editor [contenteditable] sub,\n.srte-editor [contenteditable] sup {\n line-height: 0;\n}\n";
|
|
3
3
|
export declare function ensureStyleSheet(): void;
|
package/dist/theme.js
CHANGED
|
@@ -56,6 +56,39 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
56
56
|
background: var(--srte-surface-subtle);
|
|
57
57
|
color: var(--srte-text);
|
|
58
58
|
}
|
|
59
|
+
.srte-editor [contenteditable] p,
|
|
60
|
+
.srte-editor [contenteditable] h1,
|
|
61
|
+
.srte-editor [contenteditable] h2,
|
|
62
|
+
.srte-editor [contenteditable] h3 {
|
|
63
|
+
color: inherit;
|
|
64
|
+
}
|
|
65
|
+
.srte-editor [contenteditable] p {
|
|
66
|
+
display: block;
|
|
67
|
+
margin: 0 0 0.75em;
|
|
68
|
+
font-size: 1em;
|
|
69
|
+
font-weight: 400;
|
|
70
|
+
line-height: 1.6;
|
|
71
|
+
}
|
|
72
|
+
.srte-editor [contenteditable] h1,
|
|
73
|
+
.srte-editor [contenteditable] h2,
|
|
74
|
+
.srte-editor [contenteditable] h3 {
|
|
75
|
+
display: block;
|
|
76
|
+
margin: 0.75em 0 0.4em;
|
|
77
|
+
font-weight: 700;
|
|
78
|
+
line-height: 1.25;
|
|
79
|
+
}
|
|
80
|
+
.srte-editor [contenteditable] h1 {
|
|
81
|
+
font-size: 2em;
|
|
82
|
+
}
|
|
83
|
+
.srte-editor [contenteditable] h2 {
|
|
84
|
+
font-size: 1.5em;
|
|
85
|
+
}
|
|
86
|
+
.srte-editor [contenteditable] h3 {
|
|
87
|
+
font-size: 1.25em;
|
|
88
|
+
}
|
|
89
|
+
.srte-editor [contenteditable] > :first-child {
|
|
90
|
+
margin-top: 0;
|
|
91
|
+
}
|
|
59
92
|
.srte-editor [contenteditable] ul {
|
|
60
93
|
list-style-type: disc;
|
|
61
94
|
list-style-position: outside;
|
|
@@ -76,8 +109,8 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
76
109
|
.srte-editor [contenteditable] li::marker {
|
|
77
110
|
color: currentColor;
|
|
78
111
|
}
|
|
79
|
-
.srte-editor.srte-dark [contenteditable] [style*="color"]:not(.srte-preserve-colors):not(.srte-preserve-colors *),
|
|
80
|
-
.srte-editor.srte-dark [contenteditable] [style*="background"]:not(.srte-preserve-colors):not(.srte-preserve-colors *) {
|
|
112
|
+
.srte-editor.srte-dark [contenteditable] [style*="color"]:not(td):not(th):not(.srte-preserve-colors):not(.srte-preserve-colors *),
|
|
113
|
+
.srte-editor.srte-dark [contenteditable] [style*="background"]:not(td):not(th):not(.srte-preserve-colors):not(.srte-preserve-colors *) {
|
|
81
114
|
color: var(--srte-text) !important;
|
|
82
115
|
background: transparent !important;
|
|
83
116
|
background-color: transparent !important;
|
|
@@ -91,8 +124,13 @@ const SRTE_STYLE_ID = 'srte-theme-defaults';
|
|
|
91
124
|
export function ensureStyleSheet() {
|
|
92
125
|
if (typeof document === 'undefined')
|
|
93
126
|
return;
|
|
94
|
-
|
|
127
|
+
const existing = document.getElementById(SRTE_STYLE_ID);
|
|
128
|
+
if (existing) {
|
|
129
|
+
if (existing.textContent !== SRTE_DEFAULT_CSS) {
|
|
130
|
+
existing.textContent = SRTE_DEFAULT_CSS;
|
|
131
|
+
}
|
|
95
132
|
return;
|
|
133
|
+
}
|
|
96
134
|
const style = document.createElement('style');
|
|
97
135
|
style.id = SRTE_STYLE_ID;
|
|
98
136
|
style.textContent = SRTE_DEFAULT_CSS;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smartrte-react",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "A powerful, feature-rich Rich Text Editor for React with support for tables, mathematical formulas (LaTeX/KaTeX), and media management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|