smartrte-react 0.2.6 → 0.2.8
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;
|
|
@@ -2894,7 +3419,24 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2894
3419
|
background: "var(--srte-bg)",
|
|
2895
3420
|
color: "var(--srte-text)",
|
|
2896
3421
|
boxSizing: "border-box"
|
|
2897
|
-
}, children: [_jsxs("div", {
|
|
3422
|
+
}, children: [_jsxs("div", { "aria-disabled": readOnly, onMouseDown: (event) => {
|
|
3423
|
+
if (readOnly) {
|
|
3424
|
+
event.preventDefault();
|
|
3425
|
+
event.stopPropagation();
|
|
3426
|
+
return;
|
|
3427
|
+
}
|
|
3428
|
+
preserveToolbarMouseDown(event);
|
|
3429
|
+
}, onClick: (event) => {
|
|
3430
|
+
if (!readOnly)
|
|
3431
|
+
return;
|
|
3432
|
+
event.preventDefault();
|
|
3433
|
+
event.stopPropagation();
|
|
3434
|
+
}, onKeyDown: (event) => {
|
|
3435
|
+
if (!readOnly)
|
|
3436
|
+
return;
|
|
3437
|
+
event.preventDefault();
|
|
3438
|
+
event.stopPropagation();
|
|
3439
|
+
}, style: {
|
|
2898
3440
|
display: "flex",
|
|
2899
3441
|
flexWrap: "wrap",
|
|
2900
3442
|
maxWidth: "100%",
|
|
@@ -2905,6 +3447,9 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2905
3447
|
position: "sticky",
|
|
2906
3448
|
top: 0,
|
|
2907
3449
|
zIndex: 1,
|
|
3450
|
+
opacity: readOnly ? 0.55 : 1,
|
|
3451
|
+
pointerEvents: readOnly ? "none" : "auto",
|
|
3452
|
+
userSelect: readOnly ? "none" : undefined,
|
|
2908
3453
|
}, children: [media && (_jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", multiple: true, style: { display: "none" }, onChange: (e) => {
|
|
2909
3454
|
const list = e.currentTarget.files;
|
|
2910
3455
|
if (list && list.length) {
|
|
@@ -2943,7 +3488,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2943
3488
|
} }), _jsx("input", { ref: mdInputRef, type: "file", accept: ".md,.markdown,text/markdown,text/plain", style: { display: "none" }, onChange: (e) => {
|
|
2944
3489
|
importTextFile(e.currentTarget.files, "md");
|
|
2945
3490
|
e.currentTarget.value = "";
|
|
2946
|
-
} }), _jsxs("select", {
|
|
3491
|
+
} }), _jsxs("select", { value: currentBlockType, onMouseDown: preserveEditorSelection, onChange: (e) => {
|
|
2947
3492
|
const val = e.target.value;
|
|
2948
3493
|
if (val === "p")
|
|
2949
3494
|
applyFormatBlock("<p>");
|
|
@@ -2960,7 +3505,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2960
3505
|
borderRadius: 6,
|
|
2961
3506
|
background: "var(--srte-input-bg)",
|
|
2962
3507
|
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: () => {
|
|
3508
|
+
}, 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
3509
|
// Save selection before dropdown interaction
|
|
2965
3510
|
const sel = window.getSelection();
|
|
2966
3511
|
if (sel && sel.rangeCount > 0) {
|
|
@@ -2977,7 +3522,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2977
3522
|
borderRadius: 6,
|
|
2978
3523
|
background: "var(--srte-input-bg)",
|
|
2979
3524
|
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: () => {
|
|
3525
|
+
}, 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
3526
|
const sel = window.getSelection();
|
|
2982
3527
|
if (sel && sel.rangeCount > 0) {
|
|
2983
3528
|
const range = sel.getRangeAt(0);
|
|
@@ -3185,7 +3730,12 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3185
3730
|
borderRadius: 6,
|
|
3186
3731
|
background: "var(--srte-input-bg)",
|
|
3187
3732
|
color: "var(--srte-input-text)",
|
|
3188
|
-
}, children: "\u2795 Table" })),
|
|
3733
|
+
}, children: "\u2795 Table" })), _jsxs("div", { style: {
|
|
3734
|
+
display: "inline-flex",
|
|
3735
|
+
gap: 4,
|
|
3736
|
+
alignItems: "center",
|
|
3737
|
+
marginLeft: 6,
|
|
3738
|
+
}, 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
3739
|
height: 32,
|
|
3190
3740
|
padding: "0 10px",
|
|
3191
3741
|
border: "1px solid var(--srte-input-border)",
|
|
@@ -3352,7 +3902,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3352
3902
|
borderRadius: 4,
|
|
3353
3903
|
background: color,
|
|
3354
3904
|
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) => {
|
|
3905
|
+
}, 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
3906
|
if (colorPickerType === 'text') {
|
|
3357
3907
|
applyTextColor(e.target.value);
|
|
3358
3908
|
}
|
|
@@ -3521,7 +4071,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3521
4071
|
borderRadius: 4,
|
|
3522
4072
|
background: "var(--srte-input-bg)",
|
|
3523
4073
|
color: "var(--srte-modal-text)",
|
|
3524
|
-
}, title: sym, children: sym }, i)))] })] }) })), _jsxs("div", { ref: editorScrollRef, style: {
|
|
4074
|
+
}, title: sym, children: sym }, i)))] })] }) })), _jsxs("div", { ref: editorScrollRef, onScroll: () => updateDragHandleForTarget(dragHandle?.target || null), style: {
|
|
3525
4075
|
width: "100%",
|
|
3526
4076
|
maxWidth: "100%",
|
|
3527
4077
|
flex: "1 1 auto",
|
|
@@ -3537,6 +4087,13 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3537
4087
|
}, children: [_jsx("div", { ref: editableRef, contentEditable: !readOnly, suppressContentEditableWarning: true, onInput: handleInput, onKeyUp: updateActiveState, onMouseUp: updateActiveState, onCompositionStart: () => (isComposingRef.current = true), onCompositionEnd: () => {
|
|
3538
4088
|
isComposingRef.current = false;
|
|
3539
4089
|
handleInput();
|
|
4090
|
+
}, onMouseMove: (e) => {
|
|
4091
|
+
if (draggedBlockRef.current)
|
|
4092
|
+
return;
|
|
4093
|
+
updateDragHandleForTarget(getMovableElementFromNode(e.target));
|
|
4094
|
+
}, onMouseLeave: () => {
|
|
4095
|
+
if (!draggedBlockRef.current)
|
|
4096
|
+
scheduleDragHandleHide();
|
|
3540
4097
|
}, onPaste: (e) => {
|
|
3541
4098
|
const items = e.clipboardData?.files;
|
|
3542
4099
|
if (media && items && items.length) {
|
|
@@ -3554,29 +4111,30 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3554
4111
|
}
|
|
3555
4112
|
}, onDragOver: (e) => {
|
|
3556
4113
|
// Allow dragging images within editor and file drops
|
|
3557
|
-
if (
|
|
4114
|
+
if (draggedBlockRef.current ||
|
|
4115
|
+
draggedImageRef.current ||
|
|
3558
4116
|
e.dataTransfer?.types?.includes("Files")) {
|
|
3559
4117
|
e.preventDefault();
|
|
3560
4118
|
}
|
|
3561
4119
|
}, onDrop: (e) => {
|
|
4120
|
+
if (draggedBlockRef.current) {
|
|
4121
|
+
e.preventDefault();
|
|
4122
|
+
const block = draggedBlockRef.current;
|
|
4123
|
+
draggedBlockRef.current = null;
|
|
4124
|
+
pushEditorHistory();
|
|
4125
|
+
if (dropBlockAtPoint(block, e.clientX, e.clientY)) {
|
|
4126
|
+
focusElementEnd(block);
|
|
4127
|
+
updateDragHandleForTarget(block);
|
|
4128
|
+
handleInput();
|
|
4129
|
+
}
|
|
4130
|
+
return;
|
|
4131
|
+
}
|
|
3562
4132
|
// Move existing dragged image inside editor
|
|
3563
4133
|
if (draggedImageRef.current) {
|
|
3564
4134
|
e.preventDefault();
|
|
3565
4135
|
const x = e.clientX;
|
|
3566
4136
|
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
|
-
}
|
|
4137
|
+
let range = getRangeFromPoint(x, y);
|
|
3580
4138
|
const img = draggedImageRef.current;
|
|
3581
4139
|
draggedImageRef.current = null;
|
|
3582
4140
|
if (range &&
|
|
@@ -3585,6 +4143,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3585
4143
|
// Avoid inserting inside the image itself
|
|
3586
4144
|
if (range.startContainer === img || range.endContainer === img)
|
|
3587
4145
|
return;
|
|
4146
|
+
pushEditorHistory();
|
|
3588
4147
|
// If dropping inside a link, insert right after the link element
|
|
3589
4148
|
let container = range.commonAncestorContainer;
|
|
3590
4149
|
let linkAncestor = null;
|
|
@@ -3617,19 +4176,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3617
4176
|
// Try to move caret to drop point
|
|
3618
4177
|
const x = e.clientX;
|
|
3619
4178
|
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
|
-
}
|
|
4179
|
+
let range = getRangeFromPoint(x, y);
|
|
3633
4180
|
if (range) {
|
|
3634
4181
|
const sel = window.getSelection();
|
|
3635
4182
|
sel?.removeAllRanges();
|
|
@@ -3671,6 +4218,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3671
4218
|
}
|
|
3672
4219
|
}, onDragEnd: () => {
|
|
3673
4220
|
draggedImageRef.current = null;
|
|
4221
|
+
draggedBlockRef.current = null;
|
|
3674
4222
|
}, style: {
|
|
3675
4223
|
minHeight: typeof minHeight === "number" ? `${minHeight}px` : minHeight,
|
|
3676
4224
|
maxWidth: "100%",
|
|
@@ -3824,7 +4372,55 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3824
4372
|
setTableMenu(null);
|
|
3825
4373
|
setImageMenu(null);
|
|
3826
4374
|
}
|
|
3827
|
-
} }),
|
|
4375
|
+
} }), dragHandle && !readOnly && (_jsx("button", { type: "button", draggable: true, title: "Drag block", "aria-label": "Drag block", onMouseEnter: () => {
|
|
4376
|
+
if (dragHandleHideTimerRef.current != null) {
|
|
4377
|
+
window.clearTimeout(dragHandleHideTimerRef.current);
|
|
4378
|
+
dragHandleHideTimerRef.current = null;
|
|
4379
|
+
}
|
|
4380
|
+
}, onMouseLeave: () => {
|
|
4381
|
+
if (!draggedBlockRef.current)
|
|
4382
|
+
scheduleDragHandleHide();
|
|
4383
|
+
}, onMouseDown: (e) => {
|
|
4384
|
+
e.stopPropagation();
|
|
4385
|
+
}, onDragStart: (e) => {
|
|
4386
|
+
draggedBlockRef.current = dragHandle.target;
|
|
4387
|
+
e.dataTransfer.effectAllowed = "move";
|
|
4388
|
+
e.dataTransfer.setData("text/plain", "moving-block");
|
|
4389
|
+
try {
|
|
4390
|
+
const ghost = dragHandle.target.cloneNode(true);
|
|
4391
|
+
ghost.style.position = "fixed";
|
|
4392
|
+
ghost.style.left = "-10000px";
|
|
4393
|
+
ghost.style.top = "-10000px";
|
|
4394
|
+
ghost.style.width = `${Math.min(dragHandle.target.getBoundingClientRect().width, 480)}px`;
|
|
4395
|
+
ghost.style.opacity = "0.75";
|
|
4396
|
+
document.body.appendChild(ghost);
|
|
4397
|
+
e.dataTransfer.setDragImage(ghost, 12, 12);
|
|
4398
|
+
window.setTimeout(() => ghost.remove(), 0);
|
|
4399
|
+
}
|
|
4400
|
+
catch { }
|
|
4401
|
+
}, onDragEnd: () => {
|
|
4402
|
+
draggedBlockRef.current = null;
|
|
4403
|
+
updateDragHandleForTarget(dragHandle.target);
|
|
4404
|
+
}, style: {
|
|
4405
|
+
position: "absolute",
|
|
4406
|
+
left: dragHandle.left,
|
|
4407
|
+
top: dragHandle.top + Math.max(0, (dragHandle.height - 24) / 2),
|
|
4408
|
+
width: 24,
|
|
4409
|
+
height: 24,
|
|
4410
|
+
display: "inline-flex",
|
|
4411
|
+
alignItems: "center",
|
|
4412
|
+
justifyContent: "center",
|
|
4413
|
+
border: "1px solid var(--srte-border)",
|
|
4414
|
+
borderRadius: 6,
|
|
4415
|
+
background: "var(--srte-input-bg)",
|
|
4416
|
+
color: "var(--srte-input-text)",
|
|
4417
|
+
boxShadow: "var(--srte-menu-shadow)",
|
|
4418
|
+
cursor: "grab",
|
|
4419
|
+
zIndex: 8,
|
|
4420
|
+
fontSize: 14,
|
|
4421
|
+
lineHeight: 1,
|
|
4422
|
+
padding: 0,
|
|
4423
|
+
}, children: "\u22EE\u22EE" })), selectedImage && imageOverlay && (_jsxs("div", { style: {
|
|
3828
4424
|
position: "absolute",
|
|
3829
4425
|
left: imageOverlay.left,
|
|
3830
4426
|
top: imageOverlay.top,
|
|
@@ -3955,7 +4551,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3955
4551
|
alignItems: "center",
|
|
3956
4552
|
padding: "4px 6px",
|
|
3957
4553
|
fontSize: 12,
|
|
3958
|
-
}, children: [_jsx("span", { children: "Fill:" }), _jsx("input", { type: "color",
|
|
4554
|
+
}, children: [_jsx("span", { children: "Fill:" }), _jsx("input", { type: "color", value: tableMenuFillHex(tableMenu.cell), onChange: (e) => {
|
|
3959
4555
|
runTableCellAction(tableMenu.cell, (cell) => applyBgToSelection(e.target.value, cell));
|
|
3960
4556
|
}, style: {
|
|
3961
4557
|
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.8",
|
|
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",
|