smartrte-react 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/components/ClassicEditor.js +643 -446
- package/dist/components/LinkEditorPopover.js +5 -5
- package/dist/theme.d.ts +1 -1
- package/dist/theme.js +268 -26
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import { MediaManager } from "./MediaManager.js";
|
|
4
4
|
import { LinkEditorPopover } from "./LinkEditorPopover.js";
|
|
@@ -12,6 +12,72 @@ import { isShadowModeEnabled, runShadowCommand } from '../adapters/shadowMode.js
|
|
|
12
12
|
import { getCoreInlineMarkResult, isCoreInlineMarkEnabled } from '../adapters/inlineMarkCoreExecution.js';
|
|
13
13
|
import { closestFromTarget, isNode } from '../adapters/domTargets.js';
|
|
14
14
|
import { ensureStyleSheet } from '../theme.js';
|
|
15
|
+
const iconPaths = {
|
|
16
|
+
undo: _jsxs(_Fragment, { children: [_jsx("path", { d: "M9 7 5 11l4 4" }), _jsx("path", { d: "M5 11h7a5 5 0 0 1 5 5" })] }),
|
|
17
|
+
redo: _jsxs(_Fragment, { children: [_jsx("path", { d: "m15 7 4 4-4 4" }), _jsx("path", { d: "M19 11h-7a5 5 0 0 0-5 5" })] }),
|
|
18
|
+
"align-left": _jsx(_Fragment, { children: _jsx("path", { d: "M4 6h16M4 10h11M4 14h16M4 18h11" }) }),
|
|
19
|
+
"align-center": _jsx(_Fragment, { children: _jsx("path", { d: "M4 6h16M7 10h10M4 14h16M7 18h10" }) }),
|
|
20
|
+
"align-right": _jsx(_Fragment, { children: _jsx("path", { d: "M4 6h16M9 10h11M4 14h16M9 18h11" }) }),
|
|
21
|
+
justify: _jsx(_Fragment, { children: _jsx("path", { d: "M4 6h16M4 10h16M4 14h16M4 18h16" }) }),
|
|
22
|
+
move: _jsx(_Fragment, { children: _jsx("path", { d: "m8 7 4-4 4 4M12 3v18M8 17l4 4 4-4" }) }),
|
|
23
|
+
up: _jsx(_Fragment, { children: _jsx("path", { d: "m7 10 5-5 5 5M12 5v14" }) }),
|
|
24
|
+
down: _jsx(_Fragment, { children: _jsx("path", { d: "m7 14 5 5 5-5M12 19V5" }) }),
|
|
25
|
+
outdent: _jsx(_Fragment, { children: _jsx("path", { d: "M11 6h9M11 10h7M11 14h9M11 18h7M8 9l-3 3 3 3" }) }),
|
|
26
|
+
indent: _jsx(_Fragment, { children: _jsx("path", { d: "M11 6h9M11 10h7M11 14h9M11 18h7M4 9l3 3-3 3" }) }),
|
|
27
|
+
link: _jsxs(_Fragment, { children: [_jsx("path", { d: "M10 13a5 5 0 0 0 7.54.54l2-2a5 5 0 0 0-7.07-7.07l-1.15 1.15" }), _jsx("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-2 2a5 5 0 0 0 7.07 7.07l1.14-1.14" })] }),
|
|
28
|
+
quote: _jsx(_Fragment, { children: _jsx("path", { d: "M6 15h4V9H5v6a4 4 0 0 1-1 2.7M15 15h4V9h-5v6a4 4 0 0 1-1 2.7" }) }),
|
|
29
|
+
code: _jsx(_Fragment, { children: _jsx("path", { d: "m8 9-3 3 3 3M16 9l3 3-3 3M14 5l-4 14" }) }),
|
|
30
|
+
checklist: _jsx(_Fragment, { children: _jsx("path", { d: "m4 6 1.5 1.5L8 5M11 6h9M4 12l1.5 1.5L8 11M11 12h9M4 18l1.5 1.5L8 17M11 18h9" }) }),
|
|
31
|
+
bullets: _jsxs(_Fragment, { children: [_jsx("circle", { cx: "5", cy: "6", r: "1" }), _jsx("circle", { cx: "5", cy: "12", r: "1" }), _jsx("circle", { cx: "5", cy: "18", r: "1" }), _jsx("path", { d: "M9 6h11M9 12h11M9 18h11" })] }),
|
|
32
|
+
numbers: _jsx(_Fragment, { children: _jsx("path", { d: "M4 6h1V3.8L4 4.5M4 11h2l-2 2h2M4 17h2l-2 2h2M10 6h10M10 12h10M10 18h10" }) }),
|
|
33
|
+
image: _jsxs(_Fragment, { children: [_jsx("rect", { x: "3", y: "4", width: "18", height: "16", rx: "2" }), _jsx("circle", { cx: "8.5", cy: "9", r: "1.5" }), _jsx("path", { d: "m21 15-5-5L5 20" })] }),
|
|
34
|
+
media: _jsxs(_Fragment, { children: [_jsx("path", { d: "M4 7h6l2 2h8v10H4z" }), _jsx("path", { d: "M8 14h8M12 11v6" })] }),
|
|
35
|
+
table: _jsxs(_Fragment, { children: [_jsx("rect", { x: "3", y: "4", width: "18", height: "16", rx: "1" }), _jsx("path", { d: "M3 10h18M9 4v16M15 4v16" })] }),
|
|
36
|
+
formula: _jsx(_Fragment, { children: _jsx("path", { d: "M18 5H8l5 7-5 7h10" }) }),
|
|
37
|
+
insert: _jsxs(_Fragment, { children: [_jsx("circle", { cx: "12", cy: "12", r: "9" }), _jsx("path", { d: "M12 8v8M8 12h8" })] }),
|
|
38
|
+
import: _jsx(_Fragment, { children: _jsx("path", { d: "M12 3v12M8 11l4 4 4-4M5 20h14" }) }),
|
|
39
|
+
export: _jsx(_Fragment, { children: _jsx("path", { d: "M12 16V4M8 8l4-4 4 4M5 20h14" }) }),
|
|
40
|
+
omega: _jsx(_Fragment, { children: _jsx("path", { d: "M7 19h4v-2a7 7 0 1 1 2 0v2h4" }) }),
|
|
41
|
+
more: _jsxs(_Fragment, { children: [_jsx("circle", { cx: "5", cy: "12", r: "1" }), _jsx("circle", { cx: "12", cy: "12", r: "1" }), _jsx("circle", { cx: "19", cy: "12", r: "1" })] }),
|
|
42
|
+
chevron: _jsx("path", { d: "m8 10 4 4 4-4" }),
|
|
43
|
+
};
|
|
44
|
+
function ToolbarIcon({ name, size = 16 }) {
|
|
45
|
+
return _jsx("svg", { "aria-hidden": "true", width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", children: iconPaths[name] });
|
|
46
|
+
}
|
|
47
|
+
function ToolbarGroup({ label, priority, children }) {
|
|
48
|
+
return _jsx("div", { className: "srte-toolbar-group", role: "group", "aria-label": label, "data-srte-priority": priority || 1, children: children });
|
|
49
|
+
}
|
|
50
|
+
function ToolbarMenu({ label, icon, active, priority, children }) {
|
|
51
|
+
const menuRef = useRef(null);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
const closeOnOutsidePointer = (event) => {
|
|
54
|
+
if (menuRef.current?.open && !menuRef.current.contains(event.target))
|
|
55
|
+
menuRef.current.open = false;
|
|
56
|
+
};
|
|
57
|
+
document.addEventListener("pointerdown", closeOnOutsidePointer);
|
|
58
|
+
return () => document.removeEventListener("pointerdown", closeOnOutsidePointer);
|
|
59
|
+
}, []);
|
|
60
|
+
return (_jsxs("details", { ref: menuRef, className: "srte-toolbar-menu", "data-srte-priority": priority, onKeyDown: (event) => {
|
|
61
|
+
if (event.key === "Escape") {
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
menuRef.current?.removeAttribute("open");
|
|
64
|
+
menuRef.current?.querySelector("summary")?.focus();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (event.key !== "ArrowDown" && event.key !== "ArrowUp")
|
|
68
|
+
return;
|
|
69
|
+
const items = Array.from(menuRef.current?.querySelectorAll('[role="menuitem"]:not([disabled])') || []);
|
|
70
|
+
if (!items.length)
|
|
71
|
+
return;
|
|
72
|
+
event.preventDefault();
|
|
73
|
+
const currentIndex = items.indexOf(document.activeElement);
|
|
74
|
+
const direction = event.key === "ArrowDown" ? 1 : -1;
|
|
75
|
+
items[(currentIndex + direction + items.length) % items.length]?.focus();
|
|
76
|
+
}, children: [_jsxs("summary", { className: `srte-tool-button srte-menu-trigger${active ? " srte-active" : ""}`, "aria-label": label, title: label, children: [_jsx(ToolbarIcon, { name: icon }), _jsx(ToolbarIcon, { name: "chevron", size: 12 })] }), _jsx("div", { className: "srte-menu", role: "menu", "aria-label": label, children: children })] }));
|
|
77
|
+
}
|
|
78
|
+
function MenuItem({ icon, label, active, disabled, onClick, title }) {
|
|
79
|
+
return _jsxs("button", { type: "button", role: "menuitem", className: "srte-menu-item", "aria-label": title || label, "aria-pressed": Boolean(active), "aria-checked": active || undefined, disabled: disabled, title: title || label, onClick: (event) => { onClick(); event.currentTarget.closest("details")?.removeAttribute("open"); }, children: [_jsx(ToolbarIcon, { name: icon }), _jsx("span", { children: label }), active && _jsx("span", { className: "srte-menu-check", "aria-hidden": "true", children: "\u2713" })] });
|
|
80
|
+
}
|
|
15
81
|
// Initialize PDF.js worker
|
|
16
82
|
if (typeof window !== 'undefined') {
|
|
17
83
|
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url).toString();
|
|
@@ -254,12 +320,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
254
320
|
italic: queryState("italic"),
|
|
255
321
|
underline: queryState("underline"),
|
|
256
322
|
strikeThrough: queryState("strikeThrough"),
|
|
257
|
-
subscript: overrideApplies
|
|
258
|
-
|
|
259
|
-
: subscriptActive,
|
|
260
|
-
superscript: overrideApplies && scriptOverride?.command === "superscript"
|
|
261
|
-
? false
|
|
262
|
-
: superscriptActive,
|
|
323
|
+
subscript: overrideApplies ? scriptOverride?.command === "subscript" : subscriptActive,
|
|
324
|
+
superscript: overrideApplies ? scriptOverride?.command === "superscript" : superscriptActive,
|
|
263
325
|
checklist: Boolean(element?.closest('[data-srte-checklist="true"]')),
|
|
264
326
|
unorderedList: Boolean(element?.closest("ul:not([data-srte-checklist=\"true\"])")),
|
|
265
327
|
orderedList: Boolean(element?.closest("ol")),
|
|
@@ -398,55 +460,121 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
398
460
|
const range = getSelectionRangeInEditor();
|
|
399
461
|
if (!editor || !range)
|
|
400
462
|
return;
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
? {
|
|
411
|
-
command,
|
|
412
|
-
container: currentRange.startContainer,
|
|
413
|
-
offset: currentRange.startOffset,
|
|
414
|
-
}
|
|
415
|
-
: null;
|
|
416
|
-
setActiveState((current) => ({
|
|
417
|
-
...current,
|
|
418
|
-
subscript: command === "subscript" ? false : current.subscript,
|
|
419
|
-
superscript: command === "superscript" ? false : current.superscript,
|
|
420
|
-
}));
|
|
421
|
-
handleInput();
|
|
422
|
-
requestAnimationFrame(updateActiveState);
|
|
423
|
-
return;
|
|
424
|
-
}
|
|
425
|
-
if (script) {
|
|
426
|
-
const nextRange = document.createRange();
|
|
427
|
-
nextRange.setStartAfter(script);
|
|
428
|
-
nextRange.collapse(true);
|
|
429
|
-
safeSelectRange(nextRange);
|
|
430
|
-
savedRangeRef.current = nextRange.cloneRange();
|
|
463
|
+
if (range.collapsed) {
|
|
464
|
+
const tagName = command === "subscript" ? "sub" : "sup";
|
|
465
|
+
const pending = inlineScriptCaretOverrideRef.current;
|
|
466
|
+
const pendingApplies = pending &&
|
|
467
|
+
pending.container === range.startContainer && pending.offset === range.startOffset;
|
|
468
|
+
const active = pendingApplies
|
|
469
|
+
? pending.command === command
|
|
470
|
+
: Boolean(getScriptAncestor(range.startContainer, tagName));
|
|
471
|
+
const nextCommand = active ? "normal" : command;
|
|
431
472
|
inlineScriptCaretOverrideRef.current = {
|
|
432
|
-
command,
|
|
433
|
-
container:
|
|
434
|
-
offset:
|
|
473
|
+
command: nextCommand,
|
|
474
|
+
container: range.startContainer,
|
|
475
|
+
offset: range.startOffset,
|
|
435
476
|
};
|
|
436
477
|
setActiveState((current) => ({
|
|
437
478
|
...current,
|
|
438
|
-
subscript:
|
|
439
|
-
superscript:
|
|
479
|
+
subscript: nextCommand === "subscript",
|
|
480
|
+
superscript: nextCommand === "superscript",
|
|
440
481
|
}));
|
|
441
|
-
|
|
482
|
+
savedRangeRef.current = range.cloneRange();
|
|
442
483
|
return;
|
|
443
484
|
}
|
|
444
485
|
inlineScriptCaretOverrideRef.current = null;
|
|
445
|
-
|
|
486
|
+
pushEditorHistory();
|
|
487
|
+
const result = getCoreInlineMarkResult(editor, command);
|
|
488
|
+
if (result) {
|
|
489
|
+
editor.innerHTML = result.html;
|
|
490
|
+
ensureTableWrappers(editor);
|
|
491
|
+
addTableResizeHandles();
|
|
492
|
+
restoreSelectionToDom(editor, result.selectionAfter);
|
|
493
|
+
handleInput();
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
exec(command);
|
|
497
|
+
}
|
|
446
498
|
requestAnimationFrame(updateActiveState);
|
|
447
499
|
}
|
|
448
500
|
catch { }
|
|
449
501
|
};
|
|
502
|
+
useEffect(() => {
|
|
503
|
+
const editor = editableRef.current;
|
|
504
|
+
if (!editor)
|
|
505
|
+
return;
|
|
506
|
+
const splitScriptAtRange = (script, range) => {
|
|
507
|
+
const rightRange = document.createRange();
|
|
508
|
+
rightRange.selectNodeContents(script);
|
|
509
|
+
rightRange.setStart(range.startContainer, range.startOffset);
|
|
510
|
+
const rightContent = rightRange.extractContents();
|
|
511
|
+
const rightScript = script.cloneNode(false);
|
|
512
|
+
rightScript.appendChild(rightContent);
|
|
513
|
+
script.parentNode?.insertBefore(rightScript, script.nextSibling);
|
|
514
|
+
const insertion = document.createRange();
|
|
515
|
+
insertion.setStartAfter(script);
|
|
516
|
+
insertion.collapse(true);
|
|
517
|
+
if (!script.textContent)
|
|
518
|
+
script.remove();
|
|
519
|
+
if (!rightScript.textContent)
|
|
520
|
+
rightScript.remove();
|
|
521
|
+
return insertion;
|
|
522
|
+
};
|
|
523
|
+
const applyPendingInlineScript = (event) => {
|
|
524
|
+
const pending = inlineScriptCaretOverrideRef.current;
|
|
525
|
+
if (!pending || event.inputType !== "insertText" || !event.data)
|
|
526
|
+
return;
|
|
527
|
+
const range = getSelectionRangeInEditor();
|
|
528
|
+
if (!range?.collapsed || range.startContainer !== pending.container || range.startOffset !== pending.offset)
|
|
529
|
+
return;
|
|
530
|
+
event.preventDefault();
|
|
531
|
+
event.stopImmediatePropagation();
|
|
532
|
+
pushEditorHistory();
|
|
533
|
+
const currentScript = getScriptAncestor(range.startContainer, "sub") ||
|
|
534
|
+
getScriptAncestor(range.startContainer, "sup");
|
|
535
|
+
const desiredTag = pending.command === "normal"
|
|
536
|
+
? null
|
|
537
|
+
: pending.command === "subscript" ? "sub" : "sup";
|
|
538
|
+
let insertion = range;
|
|
539
|
+
if (currentScript && currentScript.tagName.toLowerCase() !== desiredTag) {
|
|
540
|
+
insertion = splitScriptAtRange(currentScript, range);
|
|
541
|
+
}
|
|
542
|
+
const text = document.createTextNode(event.data);
|
|
543
|
+
let inserted = text;
|
|
544
|
+
if (desiredTag && currentScript?.tagName.toLowerCase() !== desiredTag) {
|
|
545
|
+
const script = document.createElement(desiredTag);
|
|
546
|
+
script.appendChild(text);
|
|
547
|
+
inserted = script;
|
|
548
|
+
}
|
|
549
|
+
const pendingSize = pendingFontSizeRef.current;
|
|
550
|
+
if (pendingSize) {
|
|
551
|
+
const span = document.createElement("span");
|
|
552
|
+
span.style.fontSize = `${pendingSize.valuePx}px`;
|
|
553
|
+
span.appendChild(inserted);
|
|
554
|
+
inserted = span;
|
|
555
|
+
}
|
|
556
|
+
insertion.insertNode(inserted);
|
|
557
|
+
const nextRange = document.createRange();
|
|
558
|
+
nextRange.setStartAfter(text);
|
|
559
|
+
nextRange.collapse(true);
|
|
560
|
+
safeSelectRange(nextRange);
|
|
561
|
+
inlineScriptCaretOverrideRef.current = {
|
|
562
|
+
command: pending.command,
|
|
563
|
+
container: nextRange.startContainer,
|
|
564
|
+
offset: nextRange.startOffset,
|
|
565
|
+
};
|
|
566
|
+
if (pendingSize)
|
|
567
|
+
pendingFontSizeRef.current = {
|
|
568
|
+
valuePx: pendingSize.valuePx,
|
|
569
|
+
container: nextRange.startContainer,
|
|
570
|
+
offset: nextRange.startOffset,
|
|
571
|
+
};
|
|
572
|
+
savedRangeRef.current = nextRange.cloneRange();
|
|
573
|
+
handleInput();
|
|
574
|
+
};
|
|
575
|
+
editor.addEventListener("beforeinput", applyPendingInlineScript);
|
|
576
|
+
return () => editor.removeEventListener("beforeinput", applyPendingInlineScript);
|
|
577
|
+
}, []);
|
|
450
578
|
const applyFormatBlock = (blockName) => {
|
|
451
579
|
try {
|
|
452
580
|
if (!restoreSavedSelection()) {
|
|
@@ -785,19 +913,28 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
785
913
|
const current = getCurrentBlock();
|
|
786
914
|
return current ? [current] : [];
|
|
787
915
|
};
|
|
788
|
-
const
|
|
789
|
-
const
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
916
|
+
const resolveSelectedListItems = (range) => {
|
|
917
|
+
const editor = editableRef.current;
|
|
918
|
+
if (!editor)
|
|
919
|
+
return [];
|
|
920
|
+
if (range.collapsed) {
|
|
921
|
+
const element = range.startContainer instanceof HTMLElement
|
|
922
|
+
? range.startContainer
|
|
923
|
+
: range.startContainer.parentElement;
|
|
924
|
+
const item = element?.closest("li");
|
|
925
|
+
return item && editor.contains(item) ? [item] : [];
|
|
926
|
+
}
|
|
927
|
+
const ownsSelectedContent = (item) => Array.from(item.childNodes).some((node) => {
|
|
928
|
+
if (node instanceof HTMLElement && (node.matches("ul,ol") || node.dataset.srteCheck === "true"))
|
|
929
|
+
return false;
|
|
930
|
+
try {
|
|
931
|
+
return range.intersectsNode(node);
|
|
932
|
+
}
|
|
933
|
+
catch {
|
|
934
|
+
return false;
|
|
798
935
|
}
|
|
799
936
|
});
|
|
800
|
-
return
|
|
937
|
+
return sortInDocumentOrder(Array.from(editor.querySelectorAll("li")).filter(ownsSelectedContent));
|
|
801
938
|
};
|
|
802
939
|
const getAlignmentTarget = (block) => {
|
|
803
940
|
const item = block.tagName === "LI" ? block : block.closest("li");
|
|
@@ -1023,6 +1160,49 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1023
1160
|
Array.from(list.attributes).forEach((attr) => clone.setAttribute(attr.name, attr.value));
|
|
1024
1161
|
return clone;
|
|
1025
1162
|
};
|
|
1163
|
+
const listItemContentSelector = "p,h1,h2,h3,h4,h5,h6,blockquote,pre";
|
|
1164
|
+
const createListItemFromBlock = (block) => {
|
|
1165
|
+
const item = document.createElement("li");
|
|
1166
|
+
item.style.textAlign = block.style.textAlign;
|
|
1167
|
+
item.appendChild(block);
|
|
1168
|
+
return item;
|
|
1169
|
+
};
|
|
1170
|
+
const extractListItemContent = (item) => {
|
|
1171
|
+
item.querySelectorAll(':scope > [data-srte-check]').forEach((control) => control.remove());
|
|
1172
|
+
const output = [];
|
|
1173
|
+
let inlineParagraph = null;
|
|
1174
|
+
const flushInlineParagraph = () => {
|
|
1175
|
+
if (!inlineParagraph)
|
|
1176
|
+
return;
|
|
1177
|
+
if (!inlineParagraph.childNodes.length)
|
|
1178
|
+
inlineParagraph.innerHTML = "<br>";
|
|
1179
|
+
output.push(inlineParagraph);
|
|
1180
|
+
inlineParagraph = null;
|
|
1181
|
+
};
|
|
1182
|
+
Array.from(item.childNodes).forEach((node) => {
|
|
1183
|
+
if (node instanceof HTMLElement && node.matches(listItemContentSelector)) {
|
|
1184
|
+
flushInlineParagraph();
|
|
1185
|
+
output.push(node);
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
if (node instanceof HTMLElement && node.matches("ul,ol")) {
|
|
1189
|
+
flushInlineParagraph();
|
|
1190
|
+
output.push(node);
|
|
1191
|
+
return;
|
|
1192
|
+
}
|
|
1193
|
+
inlineParagraph || (inlineParagraph = document.createElement("p"));
|
|
1194
|
+
inlineParagraph.appendChild(node);
|
|
1195
|
+
});
|
|
1196
|
+
flushInlineParagraph();
|
|
1197
|
+
if (item.style.textAlign) {
|
|
1198
|
+
output.forEach((node) => {
|
|
1199
|
+
if (node instanceof HTMLElement && node.matches(listItemContentSelector) && !node.style.textAlign) {
|
|
1200
|
+
node.style.textAlign = item.style.textAlign;
|
|
1201
|
+
}
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
return output;
|
|
1205
|
+
};
|
|
1026
1206
|
const mergeAdjacentCompatibleLists = (list) => {
|
|
1027
1207
|
const isCompatible = (candidate) => candidate instanceof HTMLElement &&
|
|
1028
1208
|
candidate.tagName === list.tagName &&
|
|
@@ -1056,6 +1236,40 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1056
1236
|
}
|
|
1057
1237
|
});
|
|
1058
1238
|
};
|
|
1239
|
+
const syncChecklistItemControl = (item, strikeCompleted) => {
|
|
1240
|
+
const legacyCheckbox = item.querySelector(':scope > input[data-srte-check]');
|
|
1241
|
+
const checked = item.dataset.checked === "true" || Boolean(legacyCheckbox?.checked);
|
|
1242
|
+
const controls = Array.from(item.querySelectorAll(':scope > [data-srte-check]'));
|
|
1243
|
+
let control = controls.find((candidate) => candidate.tagName === "BUTTON");
|
|
1244
|
+
controls.forEach((candidate) => {
|
|
1245
|
+
if (candidate !== control)
|
|
1246
|
+
candidate.remove();
|
|
1247
|
+
});
|
|
1248
|
+
if (!control) {
|
|
1249
|
+
control = document.createElement("button");
|
|
1250
|
+
control.type = "button";
|
|
1251
|
+
control.dataset.srteCheck = "true";
|
|
1252
|
+
control.contentEditable = "false";
|
|
1253
|
+
control.tabIndex = -1;
|
|
1254
|
+
control.style.cssText = "border:0;padding:0;background:transparent;color:inherit;font:inherit;cursor:pointer";
|
|
1255
|
+
item.prepend(control);
|
|
1256
|
+
}
|
|
1257
|
+
item.dataset.checked = checked ? "true" : "false";
|
|
1258
|
+
control.dataset.checked = checked ? "true" : "false";
|
|
1259
|
+
control.setAttribute("aria-pressed", checked ? "true" : "false");
|
|
1260
|
+
control.setAttribute("aria-label", checked ? "Mark incomplete" : "Mark complete");
|
|
1261
|
+
control.textContent = "";
|
|
1262
|
+
item.style.textDecoration = strikeCompleted && checked ? "line-through" : "";
|
|
1263
|
+
};
|
|
1264
|
+
const syncChecklistControls = (root) => {
|
|
1265
|
+
root.querySelectorAll('ul[data-srte-checklist="true"]').forEach((list) => {
|
|
1266
|
+
const strikeCompleted = list.dataset.srteChecklistStrike === "true";
|
|
1267
|
+
Array.from(list.children).forEach((item) => {
|
|
1268
|
+
if (item instanceof HTMLElement && item.tagName === "LI")
|
|
1269
|
+
syncChecklistItemControl(item, strikeCompleted);
|
|
1270
|
+
});
|
|
1271
|
+
});
|
|
1272
|
+
};
|
|
1059
1273
|
const decorateChecklist = (list, strikeCompleted) => {
|
|
1060
1274
|
list.dataset.srteChecklist = "true";
|
|
1061
1275
|
list.dataset.srteChecklistStrike = strikeCompleted ? "true" : "false";
|
|
@@ -1064,20 +1278,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1064
1278
|
Array.from(list.children).forEach((item) => {
|
|
1065
1279
|
if (!(item instanceof HTMLElement) || item.tagName !== "LI")
|
|
1066
1280
|
return;
|
|
1067
|
-
|
|
1068
|
-
const checked = item.dataset.checked === "true" || Boolean(legacyCheckbox?.checked);
|
|
1069
|
-
item.querySelectorAll(':scope > [data-srte-check]').forEach((control) => control.remove());
|
|
1070
|
-
item.dataset.checked = checked ? "true" : "false";
|
|
1071
|
-
const control = document.createElement("button");
|
|
1072
|
-
control.type = "button";
|
|
1073
|
-
control.dataset.srteCheck = "true";
|
|
1074
|
-
control.contentEditable = "false";
|
|
1075
|
-
control.tabIndex = -1;
|
|
1076
|
-
control.setAttribute("aria-label", checked ? "Mark incomplete" : "Mark complete");
|
|
1077
|
-
control.textContent = checked ? "☑" : "☐";
|
|
1078
|
-
control.style.cssText = "margin-inline:-1.45em .45em;border:0;padding:0;background:transparent;color:inherit;font:inherit;cursor:pointer";
|
|
1079
|
-
item.prepend(control);
|
|
1080
|
-
item.style.textDecoration = strikeCompleted && checked ? "line-through" : "";
|
|
1281
|
+
syncChecklistItemControl(item, strikeCompleted);
|
|
1081
1282
|
});
|
|
1082
1283
|
return mergeAdjacentCompatibleLists(list);
|
|
1083
1284
|
};
|
|
@@ -1105,9 +1306,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1105
1306
|
const parent = list.parentElement;
|
|
1106
1307
|
if (!parent)
|
|
1107
1308
|
return;
|
|
1108
|
-
const
|
|
1109
|
-
paragraph.innerHTML = li.innerHTML || "<br>";
|
|
1110
|
-
paragraph.style.textAlign = li.style.textAlign;
|
|
1309
|
+
const content = extractListItemContent(li);
|
|
1111
1310
|
const beforeList = cloneListShell(list);
|
|
1112
1311
|
const afterList = cloneListShell(list);
|
|
1113
1312
|
while (list.firstChild && list.firstChild !== li) {
|
|
@@ -1118,13 +1317,14 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1118
1317
|
}
|
|
1119
1318
|
if (beforeList.childNodes.length)
|
|
1120
1319
|
parent.insertBefore(beforeList, list);
|
|
1121
|
-
parent.insertBefore(
|
|
1320
|
+
content.forEach((node) => parent.insertBefore(node, list));
|
|
1122
1321
|
if (afterList.childNodes.length)
|
|
1123
1322
|
parent.insertBefore(afterList, list);
|
|
1124
1323
|
li.remove();
|
|
1125
1324
|
if (!list.querySelector("li"))
|
|
1126
1325
|
list.remove();
|
|
1127
|
-
|
|
1326
|
+
const focusTarget = [...content].reverse().find((node) => node instanceof HTMLElement);
|
|
1327
|
+
focusElementEnd(focusTarget || parent);
|
|
1128
1328
|
};
|
|
1129
1329
|
const convertListTag = (list, listTag) => {
|
|
1130
1330
|
const replacement = cloneListShell(list, listTag);
|
|
@@ -1133,69 +1333,149 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1133
1333
|
const li = replacement.querySelector("li");
|
|
1134
1334
|
focusElementEnd(li || replacement);
|
|
1135
1335
|
};
|
|
1136
|
-
const getOrCreateNestedList = (
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1336
|
+
const getOrCreateNestedList = (item, source) => {
|
|
1337
|
+
const compatible = Array.from(item.children).find((child) => child instanceof HTMLElement &&
|
|
1338
|
+
["UL", "OL"].includes(child.tagName) &&
|
|
1339
|
+
child.tagName === source.tagName &&
|
|
1340
|
+
child.style.listStyleType === source.style.listStyleType &&
|
|
1341
|
+
child.dataset.srteChecklist === source.dataset.srteChecklist &&
|
|
1342
|
+
child.dataset.srteChecklistStrike === source.dataset.srteChecklistStrike);
|
|
1343
|
+
if (compatible)
|
|
1344
|
+
return compatible;
|
|
1345
|
+
const nested = cloneListShell(source);
|
|
1346
|
+
item.appendChild(nested);
|
|
1347
|
+
return nested;
|
|
1348
|
+
};
|
|
1349
|
+
const normalizeListItemStructure = (item) => {
|
|
1350
|
+
if (!item || item.tagName !== "LI")
|
|
1351
|
+
return;
|
|
1352
|
+
Array.from(item.children)
|
|
1353
|
+
.filter((child) => child instanceof HTMLElement && ["UL", "OL"].includes(child.tagName))
|
|
1354
|
+
.forEach((list) => {
|
|
1355
|
+
if (!list.querySelector(":scope > li"))
|
|
1356
|
+
list.remove();
|
|
1357
|
+
else
|
|
1358
|
+
item.appendChild(list);
|
|
1140
1359
|
});
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1360
|
+
};
|
|
1361
|
+
const captureRangeForListMove = () => {
|
|
1362
|
+
const range = getSelectionRangeInEditor();
|
|
1363
|
+
return range ? {
|
|
1364
|
+
startContainer: range.startContainer,
|
|
1365
|
+
startOffset: range.startOffset,
|
|
1366
|
+
endContainer: range.endContainer,
|
|
1367
|
+
endOffset: range.endOffset,
|
|
1368
|
+
} : null;
|
|
1369
|
+
};
|
|
1370
|
+
const restoreRangeAfterListMove = (snapshot) => {
|
|
1371
|
+
const editor = editableRef.current;
|
|
1372
|
+
if (!snapshot || !editor || !editor.contains(snapshot.startContainer) || !editor.contains(snapshot.endContainer))
|
|
1373
|
+
return false;
|
|
1374
|
+
try {
|
|
1375
|
+
const range = document.createRange();
|
|
1376
|
+
range.setStart(snapshot.startContainer, snapshot.startOffset);
|
|
1377
|
+
range.setEnd(snapshot.endContainer, snapshot.endOffset);
|
|
1378
|
+
safeSelectRange(range);
|
|
1379
|
+
savedRangeRef.current = range.cloneRange();
|
|
1380
|
+
return true;
|
|
1146
1381
|
}
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
li.appendChild(nested);
|
|
1382
|
+
catch {
|
|
1383
|
+
return false;
|
|
1150
1384
|
}
|
|
1151
|
-
return nested;
|
|
1152
1385
|
};
|
|
1153
|
-
const nestSelectedListItems = (items
|
|
1386
|
+
const nestSelectedListItems = (items) => {
|
|
1387
|
+
if (items.length === 0)
|
|
1388
|
+
return false;
|
|
1389
|
+
const savedRange = captureRangeForListMove();
|
|
1390
|
+
const selected = new Set(items);
|
|
1391
|
+
const sourceLists = Array.from(new Set(items.map((item) => item.parentElement)));
|
|
1392
|
+
let changed = false;
|
|
1393
|
+
let lastMoved = null;
|
|
1394
|
+
sourceLists.forEach((source) => {
|
|
1395
|
+
if (!source || !["UL", "OL"].includes(source.tagName))
|
|
1396
|
+
return;
|
|
1397
|
+
const sourceItems = Array.from(source.children).filter((child) => child instanceof HTMLElement && child.tagName === "LI");
|
|
1398
|
+
const runs = [];
|
|
1399
|
+
sourceItems.forEach((item) => {
|
|
1400
|
+
if (!selected.has(item))
|
|
1401
|
+
return;
|
|
1402
|
+
const currentRun = runs[runs.length - 1];
|
|
1403
|
+
const previousIndex = currentRun ? sourceItems.indexOf(currentRun[currentRun.length - 1]) : -2;
|
|
1404
|
+
const itemIndex = sourceItems.indexOf(item);
|
|
1405
|
+
if (!currentRun || itemIndex !== previousIndex + 1)
|
|
1406
|
+
runs.push([item]);
|
|
1407
|
+
else
|
|
1408
|
+
currentRun.push(item);
|
|
1409
|
+
});
|
|
1410
|
+
runs.forEach((run) => {
|
|
1411
|
+
const previous = run[0].previousElementSibling;
|
|
1412
|
+
if (!previous || previous.tagName !== "LI" || selected.has(previous))
|
|
1413
|
+
return;
|
|
1414
|
+
const target = getOrCreateNestedList(previous, source);
|
|
1415
|
+
run.forEach((item) => {
|
|
1416
|
+
target.appendChild(item);
|
|
1417
|
+
lastMoved = item;
|
|
1418
|
+
changed = true;
|
|
1419
|
+
});
|
|
1420
|
+
normalizeListItemStructure(previous);
|
|
1421
|
+
});
|
|
1422
|
+
});
|
|
1423
|
+
if (changed && !restoreRangeAfterListMove(savedRange) && lastMoved)
|
|
1424
|
+
focusElementEnd(lastMoved);
|
|
1425
|
+
return changed;
|
|
1426
|
+
};
|
|
1427
|
+
const outdentSelectedListItems = (items) => {
|
|
1154
1428
|
if (items.length === 0)
|
|
1155
1429
|
return false;
|
|
1430
|
+
const savedRange = captureRangeForListMove();
|
|
1431
|
+
const sourceLists = Array.from(new Set(items.map((item) => item.parentElement)));
|
|
1432
|
+
const selected = new Set(items);
|
|
1156
1433
|
let changed = false;
|
|
1157
|
-
let
|
|
1158
|
-
|
|
1159
|
-
const
|
|
1160
|
-
|
|
1434
|
+
let lastMoved = null;
|
|
1435
|
+
sourceLists.forEach((source) => {
|
|
1436
|
+
const parentItem = source?.parentElement;
|
|
1437
|
+
const outerList = parentItem?.parentElement;
|
|
1438
|
+
if (!source || parentItem?.tagName !== "LI" || !outerList || !["UL", "OL"].includes(outerList.tagName))
|
|
1161
1439
|
return;
|
|
1162
|
-
const
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1440
|
+
const moving = Array.from(source.children).filter((child) => child instanceof HTMLElement && child.tagName === "LI" && selected.has(child));
|
|
1441
|
+
let insertionPoint = parentItem.nextSibling;
|
|
1442
|
+
moving.forEach((item) => {
|
|
1443
|
+
outerList.insertBefore(item, insertionPoint);
|
|
1444
|
+
lastMoved = item;
|
|
1445
|
+
changed = true;
|
|
1446
|
+
});
|
|
1447
|
+
if (!source.querySelector(":scope > li"))
|
|
1448
|
+
source.remove();
|
|
1449
|
+
normalizeListItemStructure(parentItem);
|
|
1166
1450
|
});
|
|
1167
|
-
if (changed)
|
|
1168
|
-
|
|
1169
|
-
if (lastItem)
|
|
1170
|
-
focusElementEnd(lastItem);
|
|
1171
|
-
}
|
|
1451
|
+
if (changed && !restoreRangeAfterListMove(savedRange) && lastMoved)
|
|
1452
|
+
focusElementEnd(lastMoved);
|
|
1172
1453
|
return changed;
|
|
1173
1454
|
};
|
|
1174
|
-
const
|
|
1175
|
-
const
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
return;
|
|
1189
|
-
}
|
|
1190
|
-
const listTag = firstList.tagName.toLowerCase();
|
|
1191
|
-
const directItems = items.filter((item) => item.parentElement === firstList);
|
|
1192
|
-
if (directItems.length === 0 || !directItems[0].previousElementSibling)
|
|
1193
|
-
return;
|
|
1455
|
+
const canIndentListItems = (items) => {
|
|
1456
|
+
const selected = new Set(items);
|
|
1457
|
+
return items.some((item) => {
|
|
1458
|
+
const previous = item.previousElementSibling;
|
|
1459
|
+
return previous instanceof HTMLElement && previous.tagName === "LI" && !selected.has(previous);
|
|
1460
|
+
});
|
|
1461
|
+
};
|
|
1462
|
+
const canOutdentListItems = (items) => items.some((item) => {
|
|
1463
|
+
const list = item.parentElement;
|
|
1464
|
+
return list?.parentElement?.tagName === "LI" && ["UL", "OL"].includes(list.parentElement.parentElement?.tagName || "");
|
|
1465
|
+
});
|
|
1466
|
+
const applyListDepthChange = (items, direction) => {
|
|
1467
|
+
const enabled = direction === "indent" ? canIndentListItems(items) : canOutdentListItems(items);
|
|
1468
|
+
if (!enabled)
|
|
1469
|
+
return false;
|
|
1194
1470
|
pushEditorHistory();
|
|
1195
|
-
|
|
1471
|
+
const changed = direction === "indent"
|
|
1472
|
+
? nestSelectedListItems(items)
|
|
1473
|
+
: outdentSelectedListItems(items);
|
|
1474
|
+
if (changed) {
|
|
1196
1475
|
handleInput();
|
|
1197
1476
|
requestAnimationFrame(updateActiveState);
|
|
1198
1477
|
}
|
|
1478
|
+
return changed;
|
|
1199
1479
|
};
|
|
1200
1480
|
const restyleSelectedListItems = (items, listTag, styleType) => {
|
|
1201
1481
|
const editor = editableRef.current;
|
|
@@ -1241,6 +1521,66 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1241
1521
|
});
|
|
1242
1522
|
return lastSelected;
|
|
1243
1523
|
};
|
|
1524
|
+
const getDirectSelectionCell = (range) => {
|
|
1525
|
+
const startCell = getClosestCell(range.startContainer);
|
|
1526
|
+
const endCell = getClosestCell(range.endContainer);
|
|
1527
|
+
if (!startCell || startCell !== endCell)
|
|
1528
|
+
return null;
|
|
1529
|
+
const contentBlock = (node) => {
|
|
1530
|
+
const element = node instanceof HTMLElement ? node : node.parentElement;
|
|
1531
|
+
const block = element?.closest(listItemContentSelector);
|
|
1532
|
+
return block && startCell.contains(block) ? block : null;
|
|
1533
|
+
};
|
|
1534
|
+
return contentBlock(range.startContainer) || contentBlock(range.endContainer)
|
|
1535
|
+
? null
|
|
1536
|
+
: startCell;
|
|
1537
|
+
};
|
|
1538
|
+
const convertDirectTableCellSelectionToList = (range, listTag) => {
|
|
1539
|
+
const cell = getDirectSelectionCell(range);
|
|
1540
|
+
if (!cell || range.collapsed)
|
|
1541
|
+
return null;
|
|
1542
|
+
const fragment = range.extractContents();
|
|
1543
|
+
const parts = [document.createDocumentFragment()];
|
|
1544
|
+
Array.from(fragment.childNodes).forEach((node) => {
|
|
1545
|
+
if (node instanceof HTMLBRElement)
|
|
1546
|
+
parts.push(document.createDocumentFragment());
|
|
1547
|
+
else
|
|
1548
|
+
parts[parts.length - 1].appendChild(node);
|
|
1549
|
+
});
|
|
1550
|
+
const nonEmptyParts = parts.filter((part) => part.textContent?.trim() || part.childNodes.length > 0);
|
|
1551
|
+
if (nonEmptyParts.length === 0) {
|
|
1552
|
+
range.insertNode(fragment);
|
|
1553
|
+
return null;
|
|
1554
|
+
}
|
|
1555
|
+
const list = document.createElement(listTag);
|
|
1556
|
+
nonEmptyParts.forEach((part) => {
|
|
1557
|
+
const paragraph = document.createElement("p");
|
|
1558
|
+
paragraph.appendChild(part);
|
|
1559
|
+
if (!paragraph.innerHTML.trim())
|
|
1560
|
+
paragraph.innerHTML = "<br>";
|
|
1561
|
+
list.appendChild(createListItemFromBlock(paragraph));
|
|
1562
|
+
});
|
|
1563
|
+
range.insertNode(list);
|
|
1564
|
+
const lastItem = list.lastElementChild;
|
|
1565
|
+
if (lastItem)
|
|
1566
|
+
focusElementEnd(lastItem);
|
|
1567
|
+
return list;
|
|
1568
|
+
};
|
|
1569
|
+
const convertDirectTableCellContentsToList = (cell, listTag) => {
|
|
1570
|
+
const range = document.createRange();
|
|
1571
|
+
range.selectNodeContents(cell);
|
|
1572
|
+
if (!cell.textContent?.trim() && !cell.querySelector("img,br")) {
|
|
1573
|
+
const list = document.createElement(listTag);
|
|
1574
|
+
const paragraph = document.createElement("p");
|
|
1575
|
+
paragraph.innerHTML = "<br>";
|
|
1576
|
+
const item = createListItemFromBlock(paragraph);
|
|
1577
|
+
list.appendChild(item);
|
|
1578
|
+
cell.replaceChildren(list);
|
|
1579
|
+
focusElementEnd(item);
|
|
1580
|
+
return list;
|
|
1581
|
+
}
|
|
1582
|
+
return convertDirectTableCellSelectionToList(range, listTag);
|
|
1583
|
+
};
|
|
1244
1584
|
const applyListStyle = (value) => {
|
|
1245
1585
|
const listTag = value.startsWith("ordered:") ? "ol" : "ul";
|
|
1246
1586
|
const styleType = value.replace(/^(ordered|bullet):/, "");
|
|
@@ -1249,8 +1589,29 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1249
1589
|
const range = getSelectionRangeInEditor();
|
|
1250
1590
|
if (!range)
|
|
1251
1591
|
return;
|
|
1592
|
+
const currentBlock = range.collapsed ? getCurrentBlock() : null;
|
|
1593
|
+
if (currentBlock && ["TD", "TH"].includes(currentBlock.tagName)) {
|
|
1594
|
+
pushEditorHistory();
|
|
1595
|
+
const list = convertDirectTableCellContentsToList(currentBlock, listTag);
|
|
1596
|
+
if (list) {
|
|
1597
|
+
list.style.listStyleType = styleType;
|
|
1598
|
+
handleInput();
|
|
1599
|
+
requestAnimationFrame(updateActiveState);
|
|
1600
|
+
}
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
if (!range.collapsed && getDirectSelectionCell(range)) {
|
|
1604
|
+
pushEditorHistory();
|
|
1605
|
+
const list = convertDirectTableCellSelectionToList(range, listTag);
|
|
1606
|
+
if (list) {
|
|
1607
|
+
list.style.listStyleType = styleType;
|
|
1608
|
+
handleInput();
|
|
1609
|
+
requestAnimationFrame(updateActiveState);
|
|
1610
|
+
}
|
|
1611
|
+
return;
|
|
1612
|
+
}
|
|
1252
1613
|
const selectedBlocks = getSelectedBlocks(range);
|
|
1253
|
-
const selectedItems =
|
|
1614
|
+
const selectedItems = resolveSelectedListItems(range);
|
|
1254
1615
|
const selectedPlainBlocks = selectedBlocks.filter((block) => !block.closest("ul,ol"));
|
|
1255
1616
|
if (selectedItems.length > 0) {
|
|
1256
1617
|
pushEditorHistory();
|
|
@@ -1263,7 +1624,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1263
1624
|
return;
|
|
1264
1625
|
}
|
|
1265
1626
|
const lists = new Set();
|
|
1266
|
-
|
|
1627
|
+
resolveSelectedListItems(range).forEach((item) => {
|
|
1267
1628
|
const list = item.parentElement;
|
|
1268
1629
|
if (list &&
|
|
1269
1630
|
["ul", "ol"].includes(list.tagName.toLowerCase())) {
|
|
@@ -1350,7 +1711,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1350
1711
|
const range = getSelectionRangeInEditor();
|
|
1351
1712
|
if (!range)
|
|
1352
1713
|
return;
|
|
1353
|
-
const selectedItems =
|
|
1714
|
+
const selectedItems = resolveSelectedListItems(range);
|
|
1354
1715
|
const selectedChecklists = selectedItems.filter((item) => item.parentElement?.dataset.srteChecklist === "true");
|
|
1355
1716
|
if (toggleOff && selectedItems.length > 0 && selectedChecklists.length === selectedItems.length) {
|
|
1356
1717
|
pushEditorHistory();
|
|
@@ -1398,7 +1759,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1398
1759
|
const selected = blocks.filter((block) => {
|
|
1399
1760
|
if (!editor.contains(block))
|
|
1400
1761
|
return false;
|
|
1401
|
-
if (block.tagName.toLowerCase()
|
|
1762
|
+
if (["li", "td", "th"].includes(block.tagName.toLowerCase()))
|
|
1402
1763
|
return false;
|
|
1403
1764
|
if (block.closest("ul,ol"))
|
|
1404
1765
|
return false;
|
|
@@ -1426,11 +1787,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1426
1787
|
list.style.listStyleType = styleType;
|
|
1427
1788
|
parent.insertBefore(list, group[0]);
|
|
1428
1789
|
group.forEach((block) => {
|
|
1429
|
-
const li =
|
|
1430
|
-
li.innerHTML = block.innerHTML || "<br>";
|
|
1431
|
-
li.style.textAlign = block.style.textAlign;
|
|
1790
|
+
const li = createListItemFromBlock(block);
|
|
1432
1791
|
list.appendChild(li);
|
|
1433
|
-
block.remove();
|
|
1434
1792
|
lastLi = li;
|
|
1435
1793
|
});
|
|
1436
1794
|
mergeAdjacentCompatibleLists(list);
|
|
@@ -1439,42 +1797,6 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1439
1797
|
focusElementEnd(lastLi);
|
|
1440
1798
|
return true;
|
|
1441
1799
|
};
|
|
1442
|
-
const convertTableCellSelectionToList = (range, listTag) => {
|
|
1443
|
-
if (range.collapsed)
|
|
1444
|
-
return false;
|
|
1445
|
-
const startCell = getClosestCell(range.startContainer);
|
|
1446
|
-
const endCell = getClosestCell(range.endContainer);
|
|
1447
|
-
if (!startCell || startCell !== endCell)
|
|
1448
|
-
return false;
|
|
1449
|
-
const fragment = range.extractContents();
|
|
1450
|
-
const parts = [document.createDocumentFragment()];
|
|
1451
|
-
Array.from(fragment.childNodes).forEach((node) => {
|
|
1452
|
-
if (node instanceof HTMLBRElement) {
|
|
1453
|
-
parts.push(document.createDocumentFragment());
|
|
1454
|
-
}
|
|
1455
|
-
else {
|
|
1456
|
-
parts[parts.length - 1].appendChild(node);
|
|
1457
|
-
}
|
|
1458
|
-
});
|
|
1459
|
-
const nonEmptyParts = parts.filter((part) => part.textContent?.trim() || part.childNodes.length > 0);
|
|
1460
|
-
if (nonEmptyParts.length === 0) {
|
|
1461
|
-
range.insertNode(fragment);
|
|
1462
|
-
return false;
|
|
1463
|
-
}
|
|
1464
|
-
const list = document.createElement(listTag);
|
|
1465
|
-
nonEmptyParts.forEach((part) => {
|
|
1466
|
-
const item = document.createElement("li");
|
|
1467
|
-
item.appendChild(part);
|
|
1468
|
-
if (!item.innerHTML.trim())
|
|
1469
|
-
item.innerHTML = "<br>";
|
|
1470
|
-
list.appendChild(item);
|
|
1471
|
-
});
|
|
1472
|
-
range.insertNode(list);
|
|
1473
|
-
const lastItem = list.lastElementChild;
|
|
1474
|
-
if (lastItem)
|
|
1475
|
-
focusElementEnd(lastItem);
|
|
1476
|
-
return true;
|
|
1477
|
-
};
|
|
1478
1800
|
const convertRootLineSelectionToList = (range, listTag) => {
|
|
1479
1801
|
const editor = editableRef.current;
|
|
1480
1802
|
if (!editor || range.collapsed)
|
|
@@ -1582,11 +1904,9 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1582
1904
|
const isSelected = selected.has(child);
|
|
1583
1905
|
if (isSelected && toggleOff) {
|
|
1584
1906
|
flushPendingList();
|
|
1585
|
-
const
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
parent.insertBefore(paragraph, list);
|
|
1589
|
-
lastTarget = paragraph;
|
|
1907
|
+
const content = extractListItemContent(child);
|
|
1908
|
+
content.forEach((node) => parent.insertBefore(node, list));
|
|
1909
|
+
lastTarget = [...content].reverse().find((node) => node instanceof HTMLElement) || parent;
|
|
1590
1910
|
child.remove();
|
|
1591
1911
|
changed = true;
|
|
1592
1912
|
return;
|
|
@@ -1624,8 +1944,17 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1624
1944
|
};
|
|
1625
1945
|
const range = getSelectionRangeInEditor();
|
|
1626
1946
|
if (range && !range.collapsed) {
|
|
1947
|
+
if (getDirectSelectionCell(range)) {
|
|
1948
|
+
pushEditorHistory();
|
|
1949
|
+
if (convertDirectTableCellSelectionToList(range, listTag)) {
|
|
1950
|
+
setListActiveState(true);
|
|
1951
|
+
handleInput();
|
|
1952
|
+
requestAnimationFrame(updateActiveState);
|
|
1953
|
+
}
|
|
1954
|
+
return;
|
|
1955
|
+
}
|
|
1627
1956
|
const blocks = getSelectedBlocks(range);
|
|
1628
|
-
const selectedListItems =
|
|
1957
|
+
const selectedListItems = resolveSelectedListItems(range);
|
|
1629
1958
|
if (selectedListItems.length > 0) {
|
|
1630
1959
|
pushEditorHistory();
|
|
1631
1960
|
if (transformSelectedListItems(selectedListItems, listTag)) {
|
|
@@ -1652,12 +1981,6 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1652
1981
|
requestAnimationFrame(updateActiveState);
|
|
1653
1982
|
return;
|
|
1654
1983
|
}
|
|
1655
|
-
if (convertTableCellSelectionToList(range, listTag)) {
|
|
1656
|
-
setListActiveState(true);
|
|
1657
|
-
handleInput();
|
|
1658
|
-
requestAnimationFrame(updateActiveState);
|
|
1659
|
-
return;
|
|
1660
|
-
}
|
|
1661
1984
|
}
|
|
1662
1985
|
}
|
|
1663
1986
|
const block = getCurrentBlock();
|
|
@@ -1687,12 +2010,24 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1687
2010
|
requestAnimationFrame(updateActiveState);
|
|
1688
2011
|
return;
|
|
1689
2012
|
}
|
|
2013
|
+
if (["TD", "TH"].includes(block.tagName)) {
|
|
2014
|
+
pushEditorHistory();
|
|
2015
|
+
if (convertDirectTableCellContentsToList(block, listTag)) {
|
|
2016
|
+
setListActiveState(true);
|
|
2017
|
+
handleInput();
|
|
2018
|
+
requestAnimationFrame(updateActiveState);
|
|
2019
|
+
}
|
|
2020
|
+
return;
|
|
2021
|
+
}
|
|
1690
2022
|
pushEditorHistory();
|
|
2023
|
+
const parent = block.parentElement;
|
|
2024
|
+
if (!parent)
|
|
2025
|
+
return;
|
|
2026
|
+
const nextSibling = block.nextSibling;
|
|
1691
2027
|
const list = document.createElement(listTag);
|
|
1692
|
-
const li =
|
|
1693
|
-
li.innerHTML = block.innerHTML || "<br>";
|
|
2028
|
+
const li = createListItemFromBlock(block);
|
|
1694
2029
|
list.appendChild(li);
|
|
1695
|
-
|
|
2030
|
+
parent.insertBefore(list, nextSibling);
|
|
1696
2031
|
focusElementEnd(li);
|
|
1697
2032
|
setListActiveState(true);
|
|
1698
2033
|
handleInput();
|
|
@@ -1769,7 +2104,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1769
2104
|
}
|
|
1770
2105
|
return merged;
|
|
1771
2106
|
};
|
|
1772
|
-
const selectedItems = new Set(
|
|
2107
|
+
const selectedItems = new Set(resolveSelectedListItems(range));
|
|
1773
2108
|
const sourceLists = Array.from(new Set(Array.from(selectedItems, (item) => item.parentElement)));
|
|
1774
2109
|
let lastWrapped = null;
|
|
1775
2110
|
sourceLists.forEach((source) => {
|
|
@@ -1880,7 +2215,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1880
2215
|
: getSelectedBlocks(range)).filter((block) => editor.contains(block));
|
|
1881
2216
|
if (blocks.length === 0)
|
|
1882
2217
|
return;
|
|
1883
|
-
const listItems =
|
|
2218
|
+
const listItems = resolveSelectedListItems(range);
|
|
1884
2219
|
const plainBlocks = blocks.filter((block) => !block.closest("ul,ol") && !block.closest("table"));
|
|
1885
2220
|
const getItemCode = (item) => item.querySelector(":scope > pre[data-srte-list-code]");
|
|
1886
2221
|
const allTargetsActive = listItems.every((item) => Boolean(getItemCode(item))) &&
|
|
@@ -2514,15 +2849,16 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2514
2849
|
const host = editableRef.current;
|
|
2515
2850
|
if (!host)
|
|
2516
2851
|
return;
|
|
2517
|
-
|
|
2852
|
+
restoreSavedSelection();
|
|
2518
2853
|
let sel = window.getSelection();
|
|
2519
|
-
let range = sel && sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
|
|
2854
|
+
let range = sel && sel.rangeCount > 0 ? sel.getRangeAt(0).cloneRange() : null;
|
|
2520
2855
|
if (!range || !host.contains(range.commonAncestorContainer)) {
|
|
2521
2856
|
range = document.createRange();
|
|
2522
2857
|
range.selectNodeContents(host);
|
|
2523
2858
|
range.collapse(false);
|
|
2524
2859
|
safeSelectRange(range);
|
|
2525
2860
|
}
|
|
2861
|
+
pushEditorHistory();
|
|
2526
2862
|
const span = document.createElement("span");
|
|
2527
2863
|
span.setAttribute("data-formula", tex);
|
|
2528
2864
|
try {
|
|
@@ -3778,6 +4114,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3778
4114
|
ensureCaretBoundaryParagraphs(el);
|
|
3779
4115
|
// Add resize handles to tables
|
|
3780
4116
|
addTableResizeHandles();
|
|
4117
|
+
// Native Enter creates new list items; keep checklist controls in sync.
|
|
4118
|
+
syncChecklistControls(el);
|
|
3781
4119
|
if (!onChange)
|
|
3782
4120
|
return;
|
|
3783
4121
|
const html = el.innerHTML;
|
|
@@ -4630,22 +4968,6 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4630
4968
|
}
|
|
4631
4969
|
return getTopLevelMovableElement();
|
|
4632
4970
|
};
|
|
4633
|
-
const outdentListItem = (item) => {
|
|
4634
|
-
const list = item.parentElement;
|
|
4635
|
-
const parentItem = list?.parentElement;
|
|
4636
|
-
const outerList = parentItem?.parentElement;
|
|
4637
|
-
if (!list ||
|
|
4638
|
-
!parentItem ||
|
|
4639
|
-
parentItem.tagName.toLowerCase() !== "li" ||
|
|
4640
|
-
!outerList ||
|
|
4641
|
-
!["ul", "ol"].includes(outerList.tagName.toLowerCase())) {
|
|
4642
|
-
return false;
|
|
4643
|
-
}
|
|
4644
|
-
outerList.insertBefore(item, parentItem.nextSibling);
|
|
4645
|
-
if (!list.querySelector("li"))
|
|
4646
|
-
list.remove();
|
|
4647
|
-
return true;
|
|
4648
|
-
};
|
|
4649
4971
|
const elementSibling = (element, direction) => {
|
|
4650
4972
|
let sibling = direction === "previous" ? element.previousSibling : element.nextSibling;
|
|
4651
4973
|
while (sibling &&
|
|
@@ -4704,14 +5026,23 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4704
5026
|
return true;
|
|
4705
5027
|
};
|
|
4706
5028
|
const moveCurrentElement = (direction) => {
|
|
5029
|
+
restoreSavedSelection();
|
|
4707
5030
|
const editor = editableRef.current;
|
|
5031
|
+
const range = getSelectionRangeInEditor();
|
|
5032
|
+
const selectedListItems = range ? resolveSelectedListItems(range) : [];
|
|
5033
|
+
if (selectedListItems.length > 0 && (direction === "left" || direction === "right")) {
|
|
5034
|
+
applyListDepthChange(selectedListItems, direction === "right" ? "indent" : "outdent");
|
|
5035
|
+
return;
|
|
5036
|
+
}
|
|
4708
5037
|
if (moveSelectedBlocks(direction))
|
|
4709
5038
|
return;
|
|
4710
5039
|
let target = getMoveTarget();
|
|
4711
5040
|
if (!editor || !target)
|
|
4712
5041
|
return;
|
|
4713
|
-
|
|
5042
|
+
let historyPushed = false;
|
|
4714
5043
|
if (target.tagName === "TD" || target.tagName === "TH") {
|
|
5044
|
+
pushEditorHistory();
|
|
5045
|
+
historyPushed = true;
|
|
4715
5046
|
const cell = target;
|
|
4716
5047
|
const paragraph = document.createElement("p");
|
|
4717
5048
|
while (cell.firstChild)
|
|
@@ -4723,30 +5054,58 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4723
5054
|
const list = target.parentElement;
|
|
4724
5055
|
if (!list)
|
|
4725
5056
|
return;
|
|
5057
|
+
let changed = false;
|
|
4726
5058
|
if (direction === "up") {
|
|
4727
5059
|
const previous = elementSibling(target, "previous");
|
|
4728
|
-
if (previous?.nodeName
|
|
4729
|
-
|
|
5060
|
+
if (previous?.nodeName !== "LI")
|
|
5061
|
+
return;
|
|
5062
|
+
pushEditorHistory();
|
|
5063
|
+
list.insertBefore(target, previous);
|
|
5064
|
+
changed = true;
|
|
4730
5065
|
}
|
|
4731
5066
|
else if (direction === "down") {
|
|
4732
5067
|
const next = elementSibling(target, "next");
|
|
4733
|
-
if (next?.nodeName
|
|
4734
|
-
|
|
5068
|
+
if (next?.nodeName !== "LI")
|
|
5069
|
+
return;
|
|
5070
|
+
pushEditorHistory();
|
|
5071
|
+
list.insertBefore(next, target);
|
|
5072
|
+
changed = true;
|
|
4735
5073
|
}
|
|
4736
5074
|
else if (direction === "right") {
|
|
4737
|
-
|
|
5075
|
+
if (!canIndentListItems([target]))
|
|
5076
|
+
return;
|
|
5077
|
+
pushEditorHistory();
|
|
5078
|
+
changed = nestSelectedListItems([target]);
|
|
4738
5079
|
}
|
|
4739
5080
|
else {
|
|
4740
|
-
|
|
5081
|
+
if (!canOutdentListItems([target]))
|
|
5082
|
+
return;
|
|
5083
|
+
pushEditorHistory();
|
|
5084
|
+
changed = outdentSelectedListItems([target]);
|
|
4741
5085
|
}
|
|
5086
|
+
if (!changed)
|
|
5087
|
+
return;
|
|
5088
|
+
if (direction === "up" || direction === "down")
|
|
5089
|
+
focusElementEnd(target);
|
|
5090
|
+
handleInput();
|
|
5091
|
+
requestAnimationFrame(updateActiveState);
|
|
5092
|
+
return;
|
|
4742
5093
|
}
|
|
4743
5094
|
else if (direction === "up") {
|
|
4744
5095
|
const previous = elementSibling(target, "previous");
|
|
5096
|
+
if (!previous)
|
|
5097
|
+
return;
|
|
5098
|
+
if (!historyPushed)
|
|
5099
|
+
pushEditorHistory();
|
|
4745
5100
|
if (previous)
|
|
4746
5101
|
target.parentElement?.insertBefore(target, previous);
|
|
4747
5102
|
}
|
|
4748
5103
|
else if (direction === "down") {
|
|
4749
5104
|
const next = elementSibling(target, "next");
|
|
5105
|
+
if (!next)
|
|
5106
|
+
return;
|
|
5107
|
+
if (!historyPushed)
|
|
5108
|
+
pushEditorHistory();
|
|
4750
5109
|
if (next)
|
|
4751
5110
|
target.parentElement?.insertBefore(next, target);
|
|
4752
5111
|
}
|
|
@@ -4755,6 +5114,10 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4755
5114
|
const nextMargin = direction === "right"
|
|
4756
5115
|
? Math.min(current + 24, 240)
|
|
4757
5116
|
: Math.max(current - 24, 0);
|
|
5117
|
+
if (nextMargin === current)
|
|
5118
|
+
return;
|
|
5119
|
+
if (!historyPushed)
|
|
5120
|
+
pushEditorHistory();
|
|
4758
5121
|
target.style.marginLeft = nextMargin ? `${nextMargin}px` : "";
|
|
4759
5122
|
}
|
|
4760
5123
|
focusElementEnd(target);
|
|
@@ -4789,14 +5152,13 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4789
5152
|
const activeButtonStyle = (active = false, extra = {}) => ({
|
|
4790
5153
|
height: 32,
|
|
4791
5154
|
minWidth: 32,
|
|
4792
|
-
padding: "0
|
|
4793
|
-
border: active
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
boxShadow: active ? "inset 0 0 0 1px var(--srte-accent)" : "none",
|
|
5155
|
+
padding: "0 7px",
|
|
5156
|
+
border: active ? "1px solid color-mix(in srgb, var(--srte-primary) 35%, transparent)" : "1px solid transparent",
|
|
5157
|
+
borderRadius: 8,
|
|
5158
|
+
background: active ? "var(--srte-accent-bg)" : "transparent",
|
|
5159
|
+
color: active ? "var(--srte-primary)" : "var(--srte-foreground)",
|
|
5160
|
+
boxShadow: "none",
|
|
5161
|
+
cursor: "pointer",
|
|
4800
5162
|
...extra,
|
|
4801
5163
|
});
|
|
4802
5164
|
const preserveToolbarMouseDown = (event) => {
|
|
@@ -4810,16 +5172,16 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4810
5172
|
const editorClass = `srte-editor${theme === 'dark' ? ' srte-dark' : ''}${className ? ' ' + className : ''}`;
|
|
4811
5173
|
return (_jsxs("div", { className: editorClass, style: {
|
|
4812
5174
|
border: "1px solid var(--srte-border)",
|
|
4813
|
-
borderRadius:
|
|
5175
|
+
borderRadius: "var(--srte-radius)",
|
|
4814
5176
|
width: "100%",
|
|
4815
5177
|
maxWidth: "100vw",
|
|
4816
5178
|
overflow: "visible",
|
|
4817
5179
|
display: "flex",
|
|
4818
5180
|
flexDirection: "column",
|
|
4819
|
-
background: "var(--srte-
|
|
4820
|
-
color: "var(--srte-
|
|
5181
|
+
background: "var(--srte-background)",
|
|
5182
|
+
color: "var(--srte-foreground)",
|
|
4821
5183
|
boxSizing: "border-box"
|
|
4822
|
-
}, children: [_jsxs("div", { "aria-disabled": readOnly, onMouseDown: (event) => {
|
|
5184
|
+
}, children: [_jsxs("div", { className: "srte-toolbar", role: "toolbar", "aria-label": "Text formatting", "aria-disabled": readOnly, onMouseDown: (event) => {
|
|
4823
5185
|
if (readOnly) {
|
|
4824
5186
|
event.preventDefault();
|
|
4825
5187
|
event.stopPropagation();
|
|
@@ -4837,16 +5199,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4837
5199
|
event.preventDefault();
|
|
4838
5200
|
event.stopPropagation();
|
|
4839
5201
|
}, style: {
|
|
4840
|
-
display: "flex",
|
|
4841
|
-
flexWrap: "wrap",
|
|
4842
5202
|
maxWidth: "100%",
|
|
4843
|
-
gap: 8,
|
|
4844
|
-
padding: 8,
|
|
4845
|
-
borderBottom: "1px solid var(--srte-border-light)",
|
|
4846
|
-
background: "var(--srte-toolbar-bg)",
|
|
4847
|
-
position: "sticky",
|
|
4848
|
-
top: 0,
|
|
4849
|
-
zIndex: 1,
|
|
4850
5203
|
opacity: readOnly ? 0.55 : 1,
|
|
4851
5204
|
pointerEvents: readOnly ? "none" : "auto",
|
|
4852
5205
|
userSelect: readOnly ? "none" : undefined,
|
|
@@ -4888,7 +5241,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4888
5241
|
} }), _jsx("input", { ref: mdInputRef, type: "file", accept: ".md,.markdown,text/markdown,text/plain", style: { display: "none" }, onChange: (e) => {
|
|
4889
5242
|
importTextFile(e.currentTarget.files, "md");
|
|
4890
5243
|
e.currentTarget.value = "";
|
|
4891
|
-
} }), _jsxs("select", { value: currentBlockType, onPointerDown: preserveEditorSelection, onMouseDown: preserveEditorSelection, onChange: (e) => {
|
|
5244
|
+
} }), _jsxs(ToolbarGroup, { label: "History", children: [_jsx("button", { type: "button", className: "srte-tool-button", "aria-label": "Undo", title: "Undo", onClick: () => exec("undo"), children: _jsx(ToolbarIcon, { name: "undo" }) }), _jsx("button", { type: "button", className: "srte-tool-button", "aria-label": "Redo", title: "Redo", onClick: () => exec("redo"), children: _jsx(ToolbarIcon, { name: "redo" }) })] }), _jsxs("select", { value: currentBlockType, onPointerDown: preserveEditorSelection, onMouseDown: preserveEditorSelection, onChange: (e) => {
|
|
4892
5245
|
const val = e.target.value;
|
|
4893
5246
|
if (val === "p")
|
|
4894
5247
|
applyFormatBlock("<p>");
|
|
@@ -4911,16 +5264,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4911
5264
|
borderRadius: 6,
|
|
4912
5265
|
background: "var(--srte-input-bg)",
|
|
4913
5266
|
color: "var(--srte-input-text)",
|
|
4914
|
-
}, children: [_jsx("option", { value: "mixed", disabled: true, children: "Mixed" }), _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("option", { value: "h4", children: "Heading 4" }), _jsx("option", { value: "h5", children: "Heading 5" }), _jsx("option", { value: "h6", children: "Heading 6" })] }),
|
|
4915
|
-
["left", "Left", "Align left"],
|
|
4916
|
-
["center", "Center", "Align center"],
|
|
4917
|
-
["right", "Right", "Align right"],
|
|
4918
|
-
["justify", "Justify", "Justify"],
|
|
4919
|
-
].map(([alignment, label, title]) => (_jsx("button", { type: "button", title: title, "aria-label": title, "aria-pressed": currentAlignment === alignment, onPointerDown: preserveEditorSelection, onMouseDown: preserveEditorSelection, onClick: () => applyTextAlignment(alignment), style: activeButtonStyle(currentAlignment === alignment, {
|
|
4920
|
-
minWidth: 32,
|
|
4921
|
-
padding: "0 6px",
|
|
4922
|
-
fontSize: 10,
|
|
4923
|
-
}), children: label }, alignment))), _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, onPointerDown: preserveEditorSelection, onMouseDown: preserveEditorSelection, onChange: (e) => applyFontSize(e.target.value), title: "Font Size", style: {
|
|
5267
|
+
}, children: [_jsx("option", { value: "mixed", disabled: true, children: "Mixed" }), _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("option", { value: "h4", children: "Heading 4" }), _jsx("option", { value: "h5", children: "Heading 5" }), _jsx("option", { value: "h6", children: "Heading 6" })] }), _jsx("button", { type: "button", className: `srte-tool-button${activeState.bold ? " srte-active" : ""}`, title: "Bold", onClick: () => exec("bold"), "aria-pressed": activeState.bold, style: activeButtonStyle(activeState.bold), children: _jsx("span", { style: { fontWeight: 700 }, children: "B" }) }), _jsx("button", { type: "button", className: `srte-tool-button${activeState.italic ? " srte-active" : ""}`, title: "Italic", onClick: () => exec("italic"), "aria-pressed": activeState.italic, style: activeButtonStyle(activeState.italic, { fontStyle: "italic" }), children: "I" }), _jsx("button", { type: "button", className: `srte-tool-button${activeState.underline ? " srte-active" : ""}`, title: "Underline", onClick: () => exec("underline"), "aria-pressed": activeState.underline, style: activeButtonStyle(activeState.underline, { textDecoration: "underline" }), children: "U" }), _jsx("button", { type: "button", className: `srte-tool-button${activeState.strikeThrough ? " srte-active" : ""}`, title: "Strikethrough", onClick: () => exec("strikeThrough"), "aria-pressed": activeState.strikeThrough, style: activeButtonStyle(activeState.strikeThrough, { textDecoration: "line-through" }), children: "S" }), showFontSize && (_jsxs("select", { value: currentFontSize, onPointerDown: preserveEditorSelection, onMouseDown: preserveEditorSelection, onChange: (e) => applyFontSize(e.target.value), title: "Font Size", style: {
|
|
4924
5268
|
height: 32,
|
|
4925
5269
|
padding: "0 8px",
|
|
4926
5270
|
border: "1px solid var(--srte-input-border)",
|
|
@@ -4944,7 +5288,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4944
5288
|
background: "var(--srte-input-bg)",
|
|
4945
5289
|
color: "var(--srte-input-text)",
|
|
4946
5290
|
maxWidth: 100,
|
|
4947
|
-
}, children: [_jsx("option", { value: "", disabled: true, children: "Font" }), fonts.map((f) => (_jsx("option", { value: f.value, children: f.name }, f.value)))] })), _jsx("button", { title: "Text Color", onClick: () => {
|
|
5291
|
+
}, children: [_jsx("option", { value: "", disabled: true, children: "Font" }), fonts.map((f) => (_jsx("option", { value: f.value, children: f.name }, f.value)))] })), _jsx("button", { type: "button", className: "srte-tool-button", "aria-label": "Text color", title: "Text Color", onClick: () => {
|
|
4948
5292
|
setColorPickerType('text');
|
|
4949
5293
|
setShowColorPicker(true);
|
|
4950
5294
|
}, style: {
|
|
@@ -4956,7 +5300,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4956
5300
|
background: "var(--srte-input-bg)",
|
|
4957
5301
|
color: "var(--srte-input-text)",
|
|
4958
5302
|
position: "relative",
|
|
4959
|
-
}, children: _jsx("span", { style: { fontWeight: 700, borderBottom: "3px solid currentColor", lineHeight: 1 }, children: "A" }) }), _jsx("button", { title: "Background Color", onClick: () => {
|
|
5303
|
+
}, children: _jsx("span", { style: { fontWeight: 700, borderBottom: "3px solid currentColor", lineHeight: 1 }, children: "A" }) }), _jsx("button", { type: "button", className: "srte-tool-button", "aria-label": "Background color", title: "Background Color", onClick: () => {
|
|
4960
5304
|
setColorPickerType('background');
|
|
4961
5305
|
setShowColorPicker(true);
|
|
4962
5306
|
}, style: {
|
|
@@ -4967,10 +5311,10 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4967
5311
|
borderRadius: 6,
|
|
4968
5312
|
background: "var(--srte-input-bg)",
|
|
4969
5313
|
color: "var(--srte-input-text)",
|
|
4970
|
-
}, children: _jsx("span", { style: { fontWeight: 700, padding: "1px 4px", background: "var(--srte-accent-bg)", borderRadius: 3 }, children: "A" }) }), _jsxs("button", { title: "Subscript", onClick: () => toggleInlineScript("subscript"), "aria-pressed": activeState.subscript, style: activeButtonStyle(activeState.subscript), children: ["X", _jsx("sub", { children: "2" })] }), _jsxs("button", { title: "Superscript", onClick: () => toggleInlineScript("superscript"), "aria-pressed": activeState.superscript, style: activeButtonStyle(activeState.superscript), children: ["X", _jsx("sup", { children: "2" })] }), [
|
|
5314
|
+
}, children: _jsx("span", { style: { fontWeight: 700, padding: "1px 4px", background: "var(--srte-accent-bg)", borderRadius: 3 }, children: "A" }) }), _jsxs("button", { type: "button", className: `srte-tool-button${activeState.subscript ? " srte-active" : ""}`, title: "Subscript", onMouseDown: preserveEditorSelection, onClick: () => toggleInlineScript("subscript"), "aria-pressed": activeState.subscript, style: activeButtonStyle(activeState.subscript), children: ["X", _jsx("sub", { children: "2" })] }), _jsxs("button", { type: "button", className: `srte-tool-button${activeState.superscript ? " srte-active" : ""}`, title: "Superscript", onMouseDown: preserveEditorSelection, onClick: () => toggleInlineScript("superscript"), "aria-pressed": activeState.superscript, style: activeButtonStyle(activeState.superscript), children: ["X", _jsx("sup", { children: "2" })] }), [
|
|
4971
5315
|
{
|
|
4972
5316
|
key: "check",
|
|
4973
|
-
|
|
5317
|
+
icon: "checklist",
|
|
4974
5318
|
title: "Checklist",
|
|
4975
5319
|
active: activeState.checklist,
|
|
4976
5320
|
action: () => applyChecklist(false, true),
|
|
@@ -4978,7 +5322,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4978
5322
|
},
|
|
4979
5323
|
{
|
|
4980
5324
|
key: "bullet",
|
|
4981
|
-
|
|
5325
|
+
icon: "bullets",
|
|
4982
5326
|
title: "Bulleted list",
|
|
4983
5327
|
active: activeState.unorderedList,
|
|
4984
5328
|
action: () => applyListStyle("bullet:disc"),
|
|
@@ -4986,13 +5330,13 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4986
5330
|
},
|
|
4987
5331
|
{
|
|
4988
5332
|
key: "ordered",
|
|
4989
|
-
|
|
5333
|
+
icon: "numbers",
|
|
4990
5334
|
title: "Numbered list",
|
|
4991
5335
|
active: activeState.orderedList,
|
|
4992
5336
|
action: () => applyListStyle("ordered:decimal"),
|
|
4993
5337
|
options: [["ordered:decimal", "1. 2. 3."], ["ordered:lower-alpha", "a. b. c."], ["ordered:upper-alpha", "A. B. C."], ["ordered:lower-roman", "i. ii. iii."], ["ordered:upper-roman", "I. II. III."]],
|
|
4994
5338
|
},
|
|
4995
|
-
].map((control) => (_jsxs("span", {
|
|
5339
|
+
].map((control) => (_jsxs("span", { className: "srte-split-control", children: [_jsx("button", { type: "button", className: `srte-tool-button${control.active ? " srte-active" : ""}`, title: control.title, onPointerDown: preserveEditorSelection, onClick: control.action, "aria-pressed": control.active, children: _jsx(ToolbarIcon, { name: control.icon }) }), _jsxs("select", { defaultValue: "", "aria-label": `${control.title} styles`, title: `${control.title} styles`, onPointerDown: preserveEditorSelection, onMouseDown: preserveEditorSelection, onChange: (event) => {
|
|
4996
5340
|
const selected = event.currentTarget.value;
|
|
4997
5341
|
if (selected === "check:plain")
|
|
4998
5342
|
applyChecklist(false);
|
|
@@ -5001,200 +5345,62 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
5001
5345
|
else if (selected)
|
|
5002
5346
|
applyListStyle(selected);
|
|
5003
5347
|
event.currentTarget.value = "";
|
|
5348
|
+
}, children: [_jsx("option", { value: "", disabled: true, children: "Style" }), control.options.map(([value, label]) => _jsx("option", { value: value, children: label }, value))] })] }, control.key))), _jsx("button", { type: "button", className: `srte-tool-button${activeState.blockquote ? " srte-active" : ""}`, title: "Blockquote", onPointerDown: preserveEditorSelection, onClick: toggleBlockquote, "aria-pressed": activeState.blockquote, children: _jsx(ToolbarIcon, { name: "quote" }) }), _jsx("button", { type: "button", className: "srte-tool-button", "aria-label": "Special characters", title: "Special characters", onClick: () => setShowSpecialChars(true), children: _jsx(ToolbarIcon, { name: "omega" }) }), _jsx("button", { type: "button", className: `srte-tool-button${activeState.codeBlock ? " srte-active" : ""}`, title: "Code block", onPointerDown: preserveEditorSelection, onClick: toggleCodeBlock, "aria-pressed": activeState.codeBlock, children: _jsx(ToolbarIcon, { name: "code" }) }), _jsx("button", { type: "button", className: `srte-tool-button${activeState.link ? " srte-active" : ""}`, title: "Insert link", "aria-label": "Insert or edit link", "aria-pressed": activeState.link, onPointerDown: preserveEditorSelection, onClick: () => openLinkEditor(), children: _jsx(ToolbarIcon, { name: "link" }) }), (table || media || formula) && (_jsxs(ToolbarMenu, { label: "Insert", icon: "insert", priority: 2, children: [table && _jsx(MenuItem, { icon: "table", label: "Table", title: "Insert table", onClick: () => setShowTableDialog(true) }), media && _jsx(MenuItem, { icon: "image", label: "Upload image", title: "Insert image", onClick: insertImage }), media && _jsx(MenuItem, { icon: "media", label: "Media", title: "Open media", onClick: () => mediaManager ? setShowMediaManager(true) : insertImage() }), formula && _jsx(MenuItem, { icon: "formula", label: "Formula", title: "Insert formula", onClick: () => setShowFormulaDialog(true) })] })), selectedImage && (_jsxs(ToolbarMenu, { label: "Image alignment", icon: "image", children: [_jsx(MenuItem, { icon: "align-center", label: "Center image", onClick: () => {
|
|
5349
|
+
selectedImage.style.display = "block";
|
|
5350
|
+
selectedImage.style.margin = "0 auto";
|
|
5351
|
+
selectedImage.style.float = "none";
|
|
5352
|
+
scheduleImageOverlay();
|
|
5353
|
+
handleInput();
|
|
5354
|
+
} }), _jsx(MenuItem, { icon: "align-left", label: "Float left", onClick: () => {
|
|
5355
|
+
selectedImage.style.display = "inline";
|
|
5356
|
+
selectedImage.style.float = "left";
|
|
5357
|
+
selectedImage.style.margin = "0 8px 8px 0";
|
|
5358
|
+
scheduleImageOverlay();
|
|
5359
|
+
handleInput();
|
|
5360
|
+
} }), _jsx(MenuItem, { icon: "align-right", label: "Float right", onClick: () => {
|
|
5361
|
+
selectedImage.style.display = "inline";
|
|
5362
|
+
selectedImage.style.float = "right";
|
|
5363
|
+
selectedImage.style.margin = "0 0 8px 8px";
|
|
5364
|
+
scheduleImageOverlay();
|
|
5365
|
+
handleInput();
|
|
5366
|
+
} })] })), _jsxs(ToolbarGroup, { label: "Document", priority: 3, children: [_jsxs(ToolbarMenu, { label: loadingPdf || loadingDocx ? "Importing document" : "Import document", icon: "import", children: [_jsx(MenuItem, { icon: "import", label: "PDF (.pdf)", disabled: loadingPdf || loadingDocx, onClick: () => pdfInputRef.current?.click() }), _jsx(MenuItem, { icon: "import", label: "Microsoft Word (.docx)", disabled: loadingPdf || loadingDocx, onClick: () => docxInputRef.current?.click() }), _jsx(MenuItem, { icon: "import", label: "HTML (.html)", disabled: loadingPdf || loadingDocx, onClick: () => htmlInputRef.current?.click() }), _jsx(MenuItem, { icon: "import", label: "Markdown (.md)", disabled: loadingPdf || loadingDocx, onClick: () => mdInputRef.current?.click() })] }), _jsxs(ToolbarMenu, { label: "Export document", icon: "export", children: [_jsx(MenuItem, { icon: "export", label: "PDF", onClick: exportPdf }), _jsx(MenuItem, { icon: "export", label: "Microsoft Word (.docx)", onClick: () => void exportDocx() }), _jsx(MenuItem, { icon: "export", label: "HTML", onClick: exportHtml }), _jsx(MenuItem, { icon: "export", label: "Markdown", onClick: exportMarkdown })] }), _jsxs("select", { className: "srte-command-proxy", "aria-hidden": "true", tabIndex: -1, defaultValue: "", "aria-label": "Import document", title: "Import document", disabled: loadingPdf || loadingDocx, onChange: (event) => {
|
|
5367
|
+
const format = event.currentTarget.value;
|
|
5368
|
+
event.currentTarget.value = "";
|
|
5369
|
+
if (format === "pdf")
|
|
5370
|
+
pdfInputRef.current?.click();
|
|
5371
|
+
else if (format === "docx")
|
|
5372
|
+
docxInputRef.current?.click();
|
|
5373
|
+
else if (format === "html")
|
|
5374
|
+
htmlInputRef.current?.click();
|
|
5375
|
+
else if (format === "markdown")
|
|
5376
|
+
mdInputRef.current?.click();
|
|
5004
5377
|
}, style: {
|
|
5005
|
-
width: 28,
|
|
5006
5378
|
height: 32,
|
|
5007
|
-
padding: 0,
|
|
5008
|
-
border: "1px solid var(--srte-input-border)",
|
|
5009
|
-
borderLeft: 0,
|
|
5010
|
-
borderRadius: "0 6px 6px 0",
|
|
5011
|
-
background: "var(--srte-input-bg)",
|
|
5012
|
-
color: "var(--srte-input-text)",
|
|
5013
|
-
}, children: [_jsx("option", { value: "", disabled: true, children: "Style" }), control.options.map(([value, label]) => _jsx("option", { value: value, children: label }, value))] })] }, control.key))), _jsx("button", { type: "button", title: "Blockquote", onPointerDown: preserveEditorSelection, onClick: toggleBlockquote, "aria-pressed": activeState.blockquote, style: activeButtonStyle(activeState.blockquote), children: "\u275D" }), _jsx("button", { title: "Special characters", onClick: () => setShowSpecialChars(true), style: {
|
|
5014
|
-
height: 32,
|
|
5015
|
-
minWidth: 32,
|
|
5016
|
-
padding: "0 8px",
|
|
5017
|
-
border: "1px solid var(--srte-input-border)",
|
|
5018
|
-
borderRadius: 6,
|
|
5019
|
-
background: "var(--srte-input-bg)",
|
|
5020
|
-
color: "var(--srte-input-text)",
|
|
5021
|
-
}, children: "\u03A9" }), _jsx("button", { type: "button", title: "Code block", onPointerDown: preserveEditorSelection, onClick: toggleCodeBlock, "aria-pressed": activeState.codeBlock, style: activeButtonStyle(activeState.codeBlock, {
|
|
5022
|
-
minWidth: 36,
|
|
5023
|
-
fontFamily: "ui-monospace, SFMono-Regular, Menlo",
|
|
5024
|
-
}), children: "< />" }), formula && (_jsx("button", { title: "Insert formula", onClick: () => setShowFormulaDialog(true), style: {
|
|
5025
|
-
height: 32,
|
|
5026
|
-
minWidth: 32,
|
|
5027
|
-
padding: "0 8px",
|
|
5028
|
-
border: "1px solid var(--srte-input-border)",
|
|
5029
|
-
borderRadius: 6,
|
|
5030
|
-
background: "var(--srte-input-bg)",
|
|
5031
|
-
color: "var(--srte-input-text)",
|
|
5032
|
-
}, children: "\u2211" })), _jsx("button", { type: "button", title: "Insert link", "aria-label": "Insert or edit link", "aria-pressed": activeState.link, onPointerDown: preserveEditorSelection, onClick: () => openLinkEditor(), style: activeButtonStyle(activeState.link, { minWidth: 34, fontSize: 17 }), children: _jsx("span", { "aria-hidden": "true", children: "\u2197" }) }), _jsx("button", { type: "button", title: "Remove link", "aria-label": "Remove link", disabled: !activeState.link, onPointerDown: preserveEditorSelection, onClick: () => exec("unlink"), style: {
|
|
5033
|
-
height: 32,
|
|
5034
|
-
minWidth: 34,
|
|
5035
|
-
padding: "0 8px",
|
|
5036
|
-
border: "1px solid var(--srte-input-border)",
|
|
5037
|
-
borderRadius: 6,
|
|
5038
|
-
background: "var(--srte-input-bg)",
|
|
5039
|
-
color: "var(--srte-input-text)",
|
|
5040
|
-
cursor: activeState.link ? "pointer" : "not-allowed",
|
|
5041
|
-
opacity: activeState.link ? 1 : 0.45,
|
|
5042
|
-
fontSize: 16,
|
|
5043
|
-
}, children: _jsx("span", { "aria-hidden": "true", children: "\u2197\u0338" }) }), media && (_jsxs(_Fragment, { children: [_jsx("button", { title: "Insert image", onClick: insertImage, style: {
|
|
5044
|
-
height: 32,
|
|
5045
|
-
padding: "0 10px",
|
|
5046
|
-
border: "1px solid var(--srte-input-border)",
|
|
5047
|
-
borderRadius: 6,
|
|
5048
|
-
background: "var(--srte-input-bg)",
|
|
5049
|
-
color: "var(--srte-input-text)",
|
|
5050
|
-
}, children: "\uD83D\uDDBC\uFE0F Image" }), mediaManager && (_jsx("button", { title: "Open media manager", onClick: () => setShowMediaManager(true), style: {
|
|
5051
|
-
height: 32,
|
|
5052
|
-
padding: "0 10px",
|
|
5053
|
-
border: "1px solid var(--srte-input-border)",
|
|
5054
|
-
borderRadius: 6,
|
|
5055
|
-
background: "var(--srte-input-bg)",
|
|
5056
|
-
color: "var(--srte-input-text)",
|
|
5057
|
-
}, children: "\uD83D\uDCC1 Media" })), _jsx("button", { title: "Import PDF", onClick: () => pdfInputRef.current?.click(), disabled: loadingPdf, style: {
|
|
5058
|
-
height: 32,
|
|
5059
|
-
padding: "0 10px",
|
|
5060
|
-
border: "1px solid var(--srte-input-border)",
|
|
5061
|
-
borderRadius: 6,
|
|
5062
|
-
background: "var(--srte-input-bg)",
|
|
5063
|
-
color: "var(--srte-input-text)",
|
|
5064
|
-
opacity: loadingPdf ? 0.5 : 1,
|
|
5065
|
-
}, children: loadingPdf ? '⌛ Importing...' : '📄 PDF' }), _jsx("button", { title: "Import DOCX", onClick: () => docxInputRef.current?.click(), disabled: loadingDocx, style: {
|
|
5066
|
-
height: 32,
|
|
5067
|
-
padding: "0 10px",
|
|
5068
|
-
border: "1px solid var(--srte-input-border)",
|
|
5069
|
-
borderRadius: 6,
|
|
5070
|
-
background: "var(--srte-input-bg)",
|
|
5071
|
-
color: "var(--srte-input-text)",
|
|
5072
|
-
opacity: loadingDocx ? 0.5 : 1,
|
|
5073
|
-
}, children: loadingDocx ? '⌛ Importing...' : '📝 DOCX' }), _jsx("button", { title: "Import HTML", onClick: () => htmlInputRef.current?.click(), style: {
|
|
5074
|
-
height: 32,
|
|
5075
|
-
padding: "0 10px",
|
|
5076
|
-
border: "1px solid var(--srte-input-border)",
|
|
5077
|
-
borderRadius: 6,
|
|
5078
|
-
background: "var(--srte-input-bg)",
|
|
5079
|
-
color: "var(--srte-input-text)",
|
|
5080
|
-
}, children: "HTML" }), _jsx("button", { title: "Import Markdown", onClick: () => mdInputRef.current?.click(), style: {
|
|
5081
|
-
height: 32,
|
|
5082
|
-
padding: "0 10px",
|
|
5083
|
-
border: "1px solid var(--srte-input-border)",
|
|
5084
|
-
borderRadius: 6,
|
|
5085
|
-
background: "var(--srte-input-bg)",
|
|
5086
|
-
color: "var(--srte-input-text)",
|
|
5087
|
-
}, children: "MD" }), _jsx("button", { title: "Export HTML", onClick: exportHtml, style: {
|
|
5088
|
-
height: 32,
|
|
5089
|
-
padding: "0 10px",
|
|
5090
|
-
border: "1px solid var(--srte-input-border)",
|
|
5091
|
-
borderRadius: 6,
|
|
5092
|
-
background: "var(--srte-input-bg)",
|
|
5093
|
-
color: "var(--srte-input-text)",
|
|
5094
|
-
}, children: "Export HTML" }), _jsx("button", { title: "Export Markdown", onClick: exportMarkdown, style: {
|
|
5095
|
-
height: 32,
|
|
5096
|
-
padding: "0 10px",
|
|
5379
|
+
padding: "0 8px",
|
|
5097
5380
|
border: "1px solid var(--srte-input-border)",
|
|
5098
5381
|
borderRadius: 6,
|
|
5099
5382
|
background: "var(--srte-input-bg)",
|
|
5100
5383
|
color: "var(--srte-input-text)",
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5384
|
+
opacity: loadingPdf || loadingDocx ? 0.6 : 1,
|
|
5385
|
+
}, children: [_jsx("option", { value: "", disabled: true, children: loadingPdf || loadingDocx ? "Importing…" : "Import…" }), _jsx("option", { value: "pdf", children: "PDF (.pdf)" }), _jsx("option", { value: "docx", children: "Word (.docx)" }), _jsx("option", { value: "html", children: "HTML (.html)" }), _jsx("option", { value: "markdown", children: "Markdown (.md)" })] }), _jsxs("select", { className: "srte-command-proxy", "aria-hidden": "true", tabIndex: -1, defaultValue: "", "aria-label": "Export document", title: "Export document", onChange: (event) => {
|
|
5386
|
+
const format = event.currentTarget.value;
|
|
5387
|
+
event.currentTarget.value = "";
|
|
5388
|
+
if (format === "html")
|
|
5389
|
+
exportHtml();
|
|
5390
|
+
else if (format === "markdown")
|
|
5391
|
+
exportMarkdown();
|
|
5392
|
+
else if (format === "docx")
|
|
5393
|
+
void exportDocx();
|
|
5394
|
+
else if (format === "pdf")
|
|
5395
|
+
exportPdf();
|
|
5396
|
+
}, style: {
|
|
5109
5397
|
height: 32,
|
|
5110
|
-
padding: "0
|
|
5398
|
+
padding: "0 8px",
|
|
5111
5399
|
border: "1px solid var(--srte-input-border)",
|
|
5112
5400
|
borderRadius: 6,
|
|
5113
5401
|
background: "var(--srte-input-bg)",
|
|
5114
5402
|
color: "var(--srte-input-text)",
|
|
5115
|
-
}, children: "Export PDF" }), _jsxs("div", {
|
|
5116
|
-
display: "inline-flex",
|
|
5117
|
-
gap: 4,
|
|
5118
|
-
alignItems: "center",
|
|
5119
|
-
marginLeft: 6,
|
|
5120
|
-
}, children: [_jsx("span", { style: { fontSize: 12, opacity: 0.7 }, children: "Image align:" }), _jsx("button", { onClick: () => {
|
|
5121
|
-
const img = selectedImage;
|
|
5122
|
-
if (!img)
|
|
5123
|
-
return;
|
|
5124
|
-
img.style.display = "block";
|
|
5125
|
-
img.style.margin = "0 auto";
|
|
5126
|
-
img.style.float = "none";
|
|
5127
|
-
scheduleImageOverlay();
|
|
5128
|
-
handleInput();
|
|
5129
|
-
}, title: "Center", style: {
|
|
5130
|
-
height: 28,
|
|
5131
|
-
minWidth: 28,
|
|
5132
|
-
padding: "0 6px",
|
|
5133
|
-
border: "1px solid var(--srte-input-border)",
|
|
5134
|
-
borderRadius: 6,
|
|
5135
|
-
background: "var(--srte-input-bg)",
|
|
5136
|
-
color: "var(--srte-input-text)",
|
|
5137
|
-
}, children: "\u2299" }), _jsx("button", { onClick: () => {
|
|
5138
|
-
const img = selectedImage;
|
|
5139
|
-
if (!img)
|
|
5140
|
-
return;
|
|
5141
|
-
img.style.display = "inline";
|
|
5142
|
-
img.style.float = "left";
|
|
5143
|
-
img.style.margin = "0 8px 8px 0";
|
|
5144
|
-
scheduleImageOverlay();
|
|
5145
|
-
handleInput();
|
|
5146
|
-
}, title: "Float left", style: {
|
|
5147
|
-
height: 28,
|
|
5148
|
-
minWidth: 28,
|
|
5149
|
-
padding: "0 6px",
|
|
5150
|
-
border: "1px solid var(--srte-input-border)",
|
|
5151
|
-
borderRadius: 6,
|
|
5152
|
-
background: "var(--srte-input-bg)",
|
|
5153
|
-
color: "var(--srte-input-text)",
|
|
5154
|
-
}, children: "\u27F8" }), _jsx("button", { onClick: () => {
|
|
5155
|
-
const img = selectedImage;
|
|
5156
|
-
if (!img)
|
|
5157
|
-
return;
|
|
5158
|
-
img.style.display = "inline";
|
|
5159
|
-
img.style.float = "right";
|
|
5160
|
-
img.style.margin = "0 0 8px 8px";
|
|
5161
|
-
scheduleImageOverlay();
|
|
5162
|
-
handleInput();
|
|
5163
|
-
}, title: "Float right", style: {
|
|
5164
|
-
height: 28,
|
|
5165
|
-
minWidth: 28,
|
|
5166
|
-
padding: "0 6px",
|
|
5167
|
-
border: "1px solid var(--srte-input-border)",
|
|
5168
|
-
borderRadius: 6,
|
|
5169
|
-
background: "var(--srte-input-bg)",
|
|
5170
|
-
color: "var(--srte-input-text)",
|
|
5171
|
-
}, children: "\u27F9" })] })] })), table && (_jsx("button", { title: "Insert table", onClick: () => setShowTableDialog(true), style: {
|
|
5172
|
-
height: 32,
|
|
5173
|
-
padding: "0 10px",
|
|
5174
|
-
border: "1px solid var(--srte-input-border)",
|
|
5175
|
-
borderRadius: 6,
|
|
5176
|
-
background: "var(--srte-input-bg)",
|
|
5177
|
-
color: "var(--srte-input-text)",
|
|
5178
|
-
}, children: "\u2795 Table" })), _jsxs("div", { style: {
|
|
5179
|
-
display: "inline-flex",
|
|
5180
|
-
gap: 4,
|
|
5181
|
-
alignItems: "center",
|
|
5182
|
-
marginLeft: 6,
|
|
5183
|
-
}, 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: {
|
|
5184
|
-
height: 32,
|
|
5185
|
-
padding: "0 10px",
|
|
5186
|
-
border: "1px solid var(--srte-input-border)",
|
|
5187
|
-
borderRadius: 6,
|
|
5188
|
-
background: "var(--srte-input-bg)",
|
|
5189
|
-
color: "var(--srte-input-text)",
|
|
5190
|
-
}, children: "\u238C Undo" }), _jsx("button", { title: "Redo", onClick: () => exec("redo"), style: {
|
|
5191
|
-
height: 32,
|
|
5192
|
-
padding: "0 10px",
|
|
5193
|
-
border: "1px solid var(--srte-input-border)",
|
|
5194
|
-
borderRadius: 6,
|
|
5195
|
-
background: "var(--srte-input-bg)",
|
|
5196
|
-
color: "var(--srte-input-text)",
|
|
5197
|
-
}, children: "\u293E Redo" })] }), media && mediaManager && (_jsx(MediaManager, { open: showMediaManager, onClose: () => setShowMediaManager(false), adapter: mediaManager, onSelect: (item) => {
|
|
5403
|
+
}, children: [_jsx("option", { value: "", disabled: true, children: "Export\u2026" }), _jsx("option", { value: "html", children: "HTML (.html)" }), _jsx("option", { value: "markdown", children: "Markdown (.md)" }), _jsx("option", { value: "docx", children: "Word (.docx)" }), _jsx("option", { value: "pdf", children: "PDF (.pdf)" })] })] }), _jsx(ToolbarMenu, { label: "Text alignment", icon: currentAlignment === "center" ? "align-center" : currentAlignment === "right" ? "align-right" : currentAlignment === "justify" ? "justify" : "align-left", active: currentAlignment !== "left", children: ["left", "center", "right", "justify"].map((alignment) => (_jsx(MenuItem, { icon: alignment === "left" ? "align-left" : alignment === "center" ? "align-center" : alignment === "right" ? "align-right" : "justify", label: alignment === "justify" ? "Justify" : `Align ${alignment}`, title: alignment === "justify" ? "Justify" : `Align ${alignment}`, active: currentAlignment === alignment, onClick: () => applyTextAlignment(alignment) }, alignment))) }), _jsxs(ToolbarMenu, { label: "Move and indent", icon: "move", priority: 2, children: [_jsx(MenuItem, { icon: "up", label: "Move block up", title: "Move selected block up", onClick: () => moveCurrentElement("up") }), _jsx(MenuItem, { icon: "down", label: "Move block down", title: "Move selected block down", onClick: () => moveCurrentElement("down") }), _jsx("div", { className: "srte-menu-separator", role: "separator" }), _jsx(MenuItem, { icon: "outdent", label: "Decrease indent", title: "Move selected block left", onClick: () => moveCurrentElement("left") }), _jsx(MenuItem, { icon: "indent", label: "Increase indent", title: "Move selected block right", onClick: () => moveCurrentElement("right") })] }), _jsx("div", { className: "srte-mobile-more", children: _jsxs(ToolbarMenu, { label: "More editor actions", icon: "more", children: [table && _jsx(MenuItem, { icon: "table", label: "Insert table", onClick: () => setShowTableDialog(true) }), media && _jsx(MenuItem, { icon: "image", label: "Upload image", onClick: insertImage }), media && _jsx(MenuItem, { icon: "media", label: "Media", onClick: () => mediaManager ? setShowMediaManager(true) : insertImage() }), formula && _jsx(MenuItem, { icon: "formula", label: "Insert formula", onClick: () => setShowFormulaDialog(true) }), _jsx(MenuItem, { icon: "omega", label: "Special characters", onClick: () => setShowSpecialChars(true) }), _jsx(MenuItem, { icon: "up", label: "Move block up", onClick: () => moveCurrentElement("up") }), _jsx(MenuItem, { icon: "down", label: "Move block down", onClick: () => moveCurrentElement("down") }), _jsx(MenuItem, { icon: "outdent", label: "Decrease indent", onClick: () => moveCurrentElement("left") }), _jsx(MenuItem, { icon: "indent", label: "Increase indent", onClick: () => moveCurrentElement("right") }), _jsx("div", { className: "srte-menu-separator", role: "separator" }), _jsx(MenuItem, { icon: "import", label: "Import Word document", onClick: () => docxInputRef.current?.click() }), _jsx(MenuItem, { icon: "import", label: "Import HTML", onClick: () => htmlInputRef.current?.click() }), _jsx(MenuItem, { icon: "import", label: "Import Markdown", onClick: () => mdInputRef.current?.click() }), _jsx("div", { className: "srte-menu-separator", role: "separator" }), _jsx(MenuItem, { icon: "export", label: "Export PDF", onClick: exportPdf }), _jsx(MenuItem, { icon: "export", label: "Export Word document", onClick: () => void exportDocx() }), _jsx(MenuItem, { icon: "export", label: "Export HTML", onClick: exportHtml }), _jsx(MenuItem, { icon: "export", label: "Export Markdown", onClick: exportMarkdown })] }) })] }), media && mediaManager && (_jsx(MediaManager, { open: showMediaManager, onClose: () => setShowMediaManager(false), adapter: mediaManager, onSelect: (item) => {
|
|
5198
5404
|
if (item?.url)
|
|
5199
5405
|
insertImageAtSelection(item);
|
|
5200
5406
|
} })), table && showTableDialog && (_jsx("div", { style: {
|
|
@@ -5654,7 +5860,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
5654
5860
|
pushEditorHistory();
|
|
5655
5861
|
const checked = item.dataset.checked !== "true";
|
|
5656
5862
|
item.dataset.checked = checked ? "true" : "false";
|
|
5657
|
-
t.
|
|
5863
|
+
t.dataset.checked = checked ? "true" : "false";
|
|
5864
|
+
t.setAttribute("aria-pressed", checked ? "true" : "false");
|
|
5658
5865
|
t.setAttribute("aria-label", checked ? "Mark incomplete" : "Mark complete");
|
|
5659
5866
|
item.style.textDecoration =
|
|
5660
5867
|
list.dataset.srteChecklistStrike === "true" && checked ? "line-through" : "";
|
|
@@ -5751,26 +5958,16 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
5751
5958
|
}
|
|
5752
5959
|
// Keep Tab for indentation in lists; otherwise insert 2 spaces
|
|
5753
5960
|
if (e.key === "Tab") {
|
|
5754
|
-
e.preventDefault();
|
|
5755
5961
|
const selection = getSelectionRangeInEditor();
|
|
5756
|
-
const selectedListItems = selection
|
|
5757
|
-
? getSelectedListItems(getSelectedBlocks(selection))
|
|
5758
|
-
: [];
|
|
5962
|
+
const selectedListItems = selection ? resolveSelectedListItems(selection) : [];
|
|
5759
5963
|
if (selectedListItems.length > 0) {
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
return;
|
|
5763
|
-
}
|
|
5764
|
-
pushEditorHistory();
|
|
5765
|
-
exec("outdent");
|
|
5964
|
+
e.preventDefault();
|
|
5965
|
+
applyListDepthChange(selectedListItems, e.shiftKey ? "outdent" : "indent");
|
|
5766
5966
|
return;
|
|
5767
5967
|
}
|
|
5968
|
+
e.preventDefault();
|
|
5768
5969
|
const currentBlock = getCurrentBlock();
|
|
5769
|
-
if (currentBlock?.closest("li")) {
|
|
5770
|
-
pushEditorHistory();
|
|
5771
|
-
exec(e.shiftKey ? "outdent" : "indent");
|
|
5772
|
-
}
|
|
5773
|
-
else {
|
|
5970
|
+
if (!currentBlock?.closest("li")) {
|
|
5774
5971
|
captureInputHistory("insertText");
|
|
5775
5972
|
document.execCommand("insertText", false, " ");
|
|
5776
5973
|
}
|