smartrte-react 0.3.1 → 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 +12 -0
- package/dist/components/ClassicEditor.js +538 -403
- package/dist/components/LinkEditorPopover.js +5 -5
- package/dist/theme.d.ts +1 -1
- package/dist/theme.js +268 -26
- package/package.json +1 -1
|
@@ -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();
|
|
@@ -847,19 +913,28 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
847
913
|
const current = getCurrentBlock();
|
|
848
914
|
return current ? [current] : [];
|
|
849
915
|
};
|
|
850
|
-
const
|
|
851
|
-
const
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
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;
|
|
860
935
|
}
|
|
861
936
|
});
|
|
862
|
-
return
|
|
937
|
+
return sortInDocumentOrder(Array.from(editor.querySelectorAll("li")).filter(ownsSelectedContent));
|
|
863
938
|
};
|
|
864
939
|
const getAlignmentTarget = (block) => {
|
|
865
940
|
const item = block.tagName === "LI" ? block : block.closest("li");
|
|
@@ -1085,6 +1160,49 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1085
1160
|
Array.from(list.attributes).forEach((attr) => clone.setAttribute(attr.name, attr.value));
|
|
1086
1161
|
return clone;
|
|
1087
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
|
+
};
|
|
1088
1206
|
const mergeAdjacentCompatibleLists = (list) => {
|
|
1089
1207
|
const isCompatible = (candidate) => candidate instanceof HTMLElement &&
|
|
1090
1208
|
candidate.tagName === list.tagName &&
|
|
@@ -1118,6 +1236,40 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1118
1236
|
}
|
|
1119
1237
|
});
|
|
1120
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
|
+
};
|
|
1121
1273
|
const decorateChecklist = (list, strikeCompleted) => {
|
|
1122
1274
|
list.dataset.srteChecklist = "true";
|
|
1123
1275
|
list.dataset.srteChecklistStrike = strikeCompleted ? "true" : "false";
|
|
@@ -1126,20 +1278,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1126
1278
|
Array.from(list.children).forEach((item) => {
|
|
1127
1279
|
if (!(item instanceof HTMLElement) || item.tagName !== "LI")
|
|
1128
1280
|
return;
|
|
1129
|
-
|
|
1130
|
-
const checked = item.dataset.checked === "true" || Boolean(legacyCheckbox?.checked);
|
|
1131
|
-
item.querySelectorAll(':scope > [data-srte-check]').forEach((control) => control.remove());
|
|
1132
|
-
item.dataset.checked = checked ? "true" : "false";
|
|
1133
|
-
const control = document.createElement("button");
|
|
1134
|
-
control.type = "button";
|
|
1135
|
-
control.dataset.srteCheck = "true";
|
|
1136
|
-
control.contentEditable = "false";
|
|
1137
|
-
control.tabIndex = -1;
|
|
1138
|
-
control.setAttribute("aria-label", checked ? "Mark incomplete" : "Mark complete");
|
|
1139
|
-
control.textContent = checked ? "☑" : "☐";
|
|
1140
|
-
control.style.cssText = "margin-inline:-1.45em .45em;border:0;padding:0;background:transparent;color:inherit;font:inherit;cursor:pointer";
|
|
1141
|
-
item.prepend(control);
|
|
1142
|
-
item.style.textDecoration = strikeCompleted && checked ? "line-through" : "";
|
|
1281
|
+
syncChecklistItemControl(item, strikeCompleted);
|
|
1143
1282
|
});
|
|
1144
1283
|
return mergeAdjacentCompatibleLists(list);
|
|
1145
1284
|
};
|
|
@@ -1167,9 +1306,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1167
1306
|
const parent = list.parentElement;
|
|
1168
1307
|
if (!parent)
|
|
1169
1308
|
return;
|
|
1170
|
-
const
|
|
1171
|
-
paragraph.innerHTML = li.innerHTML || "<br>";
|
|
1172
|
-
paragraph.style.textAlign = li.style.textAlign;
|
|
1309
|
+
const content = extractListItemContent(li);
|
|
1173
1310
|
const beforeList = cloneListShell(list);
|
|
1174
1311
|
const afterList = cloneListShell(list);
|
|
1175
1312
|
while (list.firstChild && list.firstChild !== li) {
|
|
@@ -1180,13 +1317,14 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1180
1317
|
}
|
|
1181
1318
|
if (beforeList.childNodes.length)
|
|
1182
1319
|
parent.insertBefore(beforeList, list);
|
|
1183
|
-
parent.insertBefore(
|
|
1320
|
+
content.forEach((node) => parent.insertBefore(node, list));
|
|
1184
1321
|
if (afterList.childNodes.length)
|
|
1185
1322
|
parent.insertBefore(afterList, list);
|
|
1186
1323
|
li.remove();
|
|
1187
1324
|
if (!list.querySelector("li"))
|
|
1188
1325
|
list.remove();
|
|
1189
|
-
|
|
1326
|
+
const focusTarget = [...content].reverse().find((node) => node instanceof HTMLElement);
|
|
1327
|
+
focusElementEnd(focusTarget || parent);
|
|
1190
1328
|
};
|
|
1191
1329
|
const convertListTag = (list, listTag) => {
|
|
1192
1330
|
const replacement = cloneListShell(list, listTag);
|
|
@@ -1195,69 +1333,149 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1195
1333
|
const li = replacement.querySelector("li");
|
|
1196
1334
|
focusElementEnd(li || replacement);
|
|
1197
1335
|
};
|
|
1198
|
-
const getOrCreateNestedList = (
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
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);
|
|
1202
1359
|
});
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
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;
|
|
1208
1381
|
}
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
li.appendChild(nested);
|
|
1382
|
+
catch {
|
|
1383
|
+
return false;
|
|
1212
1384
|
}
|
|
1213
|
-
return nested;
|
|
1214
1385
|
};
|
|
1215
|
-
const nestSelectedListItems = (items
|
|
1386
|
+
const nestSelectedListItems = (items) => {
|
|
1216
1387
|
if (items.length === 0)
|
|
1217
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)));
|
|
1218
1392
|
let changed = false;
|
|
1219
|
-
let
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
if (!previous || previous.tagName.toLowerCase() !== "li")
|
|
1393
|
+
let lastMoved = null;
|
|
1394
|
+
sourceLists.forEach((source) => {
|
|
1395
|
+
if (!source || !["UL", "OL"].includes(source.tagName))
|
|
1223
1396
|
return;
|
|
1224
|
-
const
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
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
|
+
});
|
|
1228
1422
|
});
|
|
1229
|
-
if (changed)
|
|
1230
|
-
|
|
1231
|
-
if (lastItem)
|
|
1232
|
-
focusElementEnd(lastItem);
|
|
1233
|
-
}
|
|
1423
|
+
if (changed && !restoreRangeAfterListMove(savedRange) && lastMoved)
|
|
1424
|
+
focusElementEnd(lastMoved);
|
|
1234
1425
|
return changed;
|
|
1235
1426
|
};
|
|
1236
|
-
const
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1427
|
+
const outdentSelectedListItems = (items) => {
|
|
1428
|
+
if (items.length === 0)
|
|
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);
|
|
1433
|
+
let changed = false;
|
|
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))
|
|
1439
|
+
return;
|
|
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);
|
|
1450
|
+
});
|
|
1451
|
+
if (changed && !restoreRangeAfterListMove(savedRange) && lastMoved)
|
|
1452
|
+
focusElementEnd(lastMoved);
|
|
1453
|
+
return changed;
|
|
1454
|
+
};
|
|
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;
|
|
1256
1470
|
pushEditorHistory();
|
|
1257
|
-
|
|
1471
|
+
const changed = direction === "indent"
|
|
1472
|
+
? nestSelectedListItems(items)
|
|
1473
|
+
: outdentSelectedListItems(items);
|
|
1474
|
+
if (changed) {
|
|
1258
1475
|
handleInput();
|
|
1259
1476
|
requestAnimationFrame(updateActiveState);
|
|
1260
1477
|
}
|
|
1478
|
+
return changed;
|
|
1261
1479
|
};
|
|
1262
1480
|
const restyleSelectedListItems = (items, listTag, styleType) => {
|
|
1263
1481
|
const editor = editableRef.current;
|
|
@@ -1303,6 +1521,66 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1303
1521
|
});
|
|
1304
1522
|
return lastSelected;
|
|
1305
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
|
+
};
|
|
1306
1584
|
const applyListStyle = (value) => {
|
|
1307
1585
|
const listTag = value.startsWith("ordered:") ? "ol" : "ul";
|
|
1308
1586
|
const styleType = value.replace(/^(ordered|bullet):/, "");
|
|
@@ -1311,8 +1589,29 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1311
1589
|
const range = getSelectionRangeInEditor();
|
|
1312
1590
|
if (!range)
|
|
1313
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
|
+
}
|
|
1314
1613
|
const selectedBlocks = getSelectedBlocks(range);
|
|
1315
|
-
const selectedItems =
|
|
1614
|
+
const selectedItems = resolveSelectedListItems(range);
|
|
1316
1615
|
const selectedPlainBlocks = selectedBlocks.filter((block) => !block.closest("ul,ol"));
|
|
1317
1616
|
if (selectedItems.length > 0) {
|
|
1318
1617
|
pushEditorHistory();
|
|
@@ -1325,7 +1624,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1325
1624
|
return;
|
|
1326
1625
|
}
|
|
1327
1626
|
const lists = new Set();
|
|
1328
|
-
|
|
1627
|
+
resolveSelectedListItems(range).forEach((item) => {
|
|
1329
1628
|
const list = item.parentElement;
|
|
1330
1629
|
if (list &&
|
|
1331
1630
|
["ul", "ol"].includes(list.tagName.toLowerCase())) {
|
|
@@ -1412,7 +1711,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1412
1711
|
const range = getSelectionRangeInEditor();
|
|
1413
1712
|
if (!range)
|
|
1414
1713
|
return;
|
|
1415
|
-
const selectedItems =
|
|
1714
|
+
const selectedItems = resolveSelectedListItems(range);
|
|
1416
1715
|
const selectedChecklists = selectedItems.filter((item) => item.parentElement?.dataset.srteChecklist === "true");
|
|
1417
1716
|
if (toggleOff && selectedItems.length > 0 && selectedChecklists.length === selectedItems.length) {
|
|
1418
1717
|
pushEditorHistory();
|
|
@@ -1460,7 +1759,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1460
1759
|
const selected = blocks.filter((block) => {
|
|
1461
1760
|
if (!editor.contains(block))
|
|
1462
1761
|
return false;
|
|
1463
|
-
if (block.tagName.toLowerCase()
|
|
1762
|
+
if (["li", "td", "th"].includes(block.tagName.toLowerCase()))
|
|
1464
1763
|
return false;
|
|
1465
1764
|
if (block.closest("ul,ol"))
|
|
1466
1765
|
return false;
|
|
@@ -1488,11 +1787,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1488
1787
|
list.style.listStyleType = styleType;
|
|
1489
1788
|
parent.insertBefore(list, group[0]);
|
|
1490
1789
|
group.forEach((block) => {
|
|
1491
|
-
const li =
|
|
1492
|
-
li.innerHTML = block.innerHTML || "<br>";
|
|
1493
|
-
li.style.textAlign = block.style.textAlign;
|
|
1790
|
+
const li = createListItemFromBlock(block);
|
|
1494
1791
|
list.appendChild(li);
|
|
1495
|
-
block.remove();
|
|
1496
1792
|
lastLi = li;
|
|
1497
1793
|
});
|
|
1498
1794
|
mergeAdjacentCompatibleLists(list);
|
|
@@ -1501,42 +1797,6 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1501
1797
|
focusElementEnd(lastLi);
|
|
1502
1798
|
return true;
|
|
1503
1799
|
};
|
|
1504
|
-
const convertTableCellSelectionToList = (range, listTag) => {
|
|
1505
|
-
if (range.collapsed)
|
|
1506
|
-
return false;
|
|
1507
|
-
const startCell = getClosestCell(range.startContainer);
|
|
1508
|
-
const endCell = getClosestCell(range.endContainer);
|
|
1509
|
-
if (!startCell || startCell !== endCell)
|
|
1510
|
-
return false;
|
|
1511
|
-
const fragment = range.extractContents();
|
|
1512
|
-
const parts = [document.createDocumentFragment()];
|
|
1513
|
-
Array.from(fragment.childNodes).forEach((node) => {
|
|
1514
|
-
if (node instanceof HTMLBRElement) {
|
|
1515
|
-
parts.push(document.createDocumentFragment());
|
|
1516
|
-
}
|
|
1517
|
-
else {
|
|
1518
|
-
parts[parts.length - 1].appendChild(node);
|
|
1519
|
-
}
|
|
1520
|
-
});
|
|
1521
|
-
const nonEmptyParts = parts.filter((part) => part.textContent?.trim() || part.childNodes.length > 0);
|
|
1522
|
-
if (nonEmptyParts.length === 0) {
|
|
1523
|
-
range.insertNode(fragment);
|
|
1524
|
-
return false;
|
|
1525
|
-
}
|
|
1526
|
-
const list = document.createElement(listTag);
|
|
1527
|
-
nonEmptyParts.forEach((part) => {
|
|
1528
|
-
const item = document.createElement("li");
|
|
1529
|
-
item.appendChild(part);
|
|
1530
|
-
if (!item.innerHTML.trim())
|
|
1531
|
-
item.innerHTML = "<br>";
|
|
1532
|
-
list.appendChild(item);
|
|
1533
|
-
});
|
|
1534
|
-
range.insertNode(list);
|
|
1535
|
-
const lastItem = list.lastElementChild;
|
|
1536
|
-
if (lastItem)
|
|
1537
|
-
focusElementEnd(lastItem);
|
|
1538
|
-
return true;
|
|
1539
|
-
};
|
|
1540
1800
|
const convertRootLineSelectionToList = (range, listTag) => {
|
|
1541
1801
|
const editor = editableRef.current;
|
|
1542
1802
|
if (!editor || range.collapsed)
|
|
@@ -1644,11 +1904,9 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1644
1904
|
const isSelected = selected.has(child);
|
|
1645
1905
|
if (isSelected && toggleOff) {
|
|
1646
1906
|
flushPendingList();
|
|
1647
|
-
const
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
parent.insertBefore(paragraph, list);
|
|
1651
|
-
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;
|
|
1652
1910
|
child.remove();
|
|
1653
1911
|
changed = true;
|
|
1654
1912
|
return;
|
|
@@ -1686,8 +1944,17 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1686
1944
|
};
|
|
1687
1945
|
const range = getSelectionRangeInEditor();
|
|
1688
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
|
+
}
|
|
1689
1956
|
const blocks = getSelectedBlocks(range);
|
|
1690
|
-
const selectedListItems =
|
|
1957
|
+
const selectedListItems = resolveSelectedListItems(range);
|
|
1691
1958
|
if (selectedListItems.length > 0) {
|
|
1692
1959
|
pushEditorHistory();
|
|
1693
1960
|
if (transformSelectedListItems(selectedListItems, listTag)) {
|
|
@@ -1714,12 +1981,6 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1714
1981
|
requestAnimationFrame(updateActiveState);
|
|
1715
1982
|
return;
|
|
1716
1983
|
}
|
|
1717
|
-
if (convertTableCellSelectionToList(range, listTag)) {
|
|
1718
|
-
setListActiveState(true);
|
|
1719
|
-
handleInput();
|
|
1720
|
-
requestAnimationFrame(updateActiveState);
|
|
1721
|
-
return;
|
|
1722
|
-
}
|
|
1723
1984
|
}
|
|
1724
1985
|
}
|
|
1725
1986
|
const block = getCurrentBlock();
|
|
@@ -1749,12 +2010,24 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1749
2010
|
requestAnimationFrame(updateActiveState);
|
|
1750
2011
|
return;
|
|
1751
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
|
+
}
|
|
1752
2022
|
pushEditorHistory();
|
|
2023
|
+
const parent = block.parentElement;
|
|
2024
|
+
if (!parent)
|
|
2025
|
+
return;
|
|
2026
|
+
const nextSibling = block.nextSibling;
|
|
1753
2027
|
const list = document.createElement(listTag);
|
|
1754
|
-
const li =
|
|
1755
|
-
li.innerHTML = block.innerHTML || "<br>";
|
|
2028
|
+
const li = createListItemFromBlock(block);
|
|
1756
2029
|
list.appendChild(li);
|
|
1757
|
-
|
|
2030
|
+
parent.insertBefore(list, nextSibling);
|
|
1758
2031
|
focusElementEnd(li);
|
|
1759
2032
|
setListActiveState(true);
|
|
1760
2033
|
handleInput();
|
|
@@ -1831,7 +2104,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1831
2104
|
}
|
|
1832
2105
|
return merged;
|
|
1833
2106
|
};
|
|
1834
|
-
const selectedItems = new Set(
|
|
2107
|
+
const selectedItems = new Set(resolveSelectedListItems(range));
|
|
1835
2108
|
const sourceLists = Array.from(new Set(Array.from(selectedItems, (item) => item.parentElement)));
|
|
1836
2109
|
let lastWrapped = null;
|
|
1837
2110
|
sourceLists.forEach((source) => {
|
|
@@ -1942,7 +2215,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1942
2215
|
: getSelectedBlocks(range)).filter((block) => editor.contains(block));
|
|
1943
2216
|
if (blocks.length === 0)
|
|
1944
2217
|
return;
|
|
1945
|
-
const listItems =
|
|
2218
|
+
const listItems = resolveSelectedListItems(range);
|
|
1946
2219
|
const plainBlocks = blocks.filter((block) => !block.closest("ul,ol") && !block.closest("table"));
|
|
1947
2220
|
const getItemCode = (item) => item.querySelector(":scope > pre[data-srte-list-code]");
|
|
1948
2221
|
const allTargetsActive = listItems.every((item) => Boolean(getItemCode(item))) &&
|
|
@@ -2576,15 +2849,16 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2576
2849
|
const host = editableRef.current;
|
|
2577
2850
|
if (!host)
|
|
2578
2851
|
return;
|
|
2579
|
-
|
|
2852
|
+
restoreSavedSelection();
|
|
2580
2853
|
let sel = window.getSelection();
|
|
2581
|
-
let range = sel && sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
|
|
2854
|
+
let range = sel && sel.rangeCount > 0 ? sel.getRangeAt(0).cloneRange() : null;
|
|
2582
2855
|
if (!range || !host.contains(range.commonAncestorContainer)) {
|
|
2583
2856
|
range = document.createRange();
|
|
2584
2857
|
range.selectNodeContents(host);
|
|
2585
2858
|
range.collapse(false);
|
|
2586
2859
|
safeSelectRange(range);
|
|
2587
2860
|
}
|
|
2861
|
+
pushEditorHistory();
|
|
2588
2862
|
const span = document.createElement("span");
|
|
2589
2863
|
span.setAttribute("data-formula", tex);
|
|
2590
2864
|
try {
|
|
@@ -3840,6 +4114,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3840
4114
|
ensureCaretBoundaryParagraphs(el);
|
|
3841
4115
|
// Add resize handles to tables
|
|
3842
4116
|
addTableResizeHandles();
|
|
4117
|
+
// Native Enter creates new list items; keep checklist controls in sync.
|
|
4118
|
+
syncChecklistControls(el);
|
|
3843
4119
|
if (!onChange)
|
|
3844
4120
|
return;
|
|
3845
4121
|
const html = el.innerHTML;
|
|
@@ -4692,22 +4968,6 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4692
4968
|
}
|
|
4693
4969
|
return getTopLevelMovableElement();
|
|
4694
4970
|
};
|
|
4695
|
-
const outdentListItem = (item) => {
|
|
4696
|
-
const list = item.parentElement;
|
|
4697
|
-
const parentItem = list?.parentElement;
|
|
4698
|
-
const outerList = parentItem?.parentElement;
|
|
4699
|
-
if (!list ||
|
|
4700
|
-
!parentItem ||
|
|
4701
|
-
parentItem.tagName.toLowerCase() !== "li" ||
|
|
4702
|
-
!outerList ||
|
|
4703
|
-
!["ul", "ol"].includes(outerList.tagName.toLowerCase())) {
|
|
4704
|
-
return false;
|
|
4705
|
-
}
|
|
4706
|
-
outerList.insertBefore(item, parentItem.nextSibling);
|
|
4707
|
-
if (!list.querySelector("li"))
|
|
4708
|
-
list.remove();
|
|
4709
|
-
return true;
|
|
4710
|
-
};
|
|
4711
4971
|
const elementSibling = (element, direction) => {
|
|
4712
4972
|
let sibling = direction === "previous" ? element.previousSibling : element.nextSibling;
|
|
4713
4973
|
while (sibling &&
|
|
@@ -4766,14 +5026,23 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4766
5026
|
return true;
|
|
4767
5027
|
};
|
|
4768
5028
|
const moveCurrentElement = (direction) => {
|
|
5029
|
+
restoreSavedSelection();
|
|
4769
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
|
+
}
|
|
4770
5037
|
if (moveSelectedBlocks(direction))
|
|
4771
5038
|
return;
|
|
4772
5039
|
let target = getMoveTarget();
|
|
4773
5040
|
if (!editor || !target)
|
|
4774
5041
|
return;
|
|
4775
|
-
|
|
5042
|
+
let historyPushed = false;
|
|
4776
5043
|
if (target.tagName === "TD" || target.tagName === "TH") {
|
|
5044
|
+
pushEditorHistory();
|
|
5045
|
+
historyPushed = true;
|
|
4777
5046
|
const cell = target;
|
|
4778
5047
|
const paragraph = document.createElement("p");
|
|
4779
5048
|
while (cell.firstChild)
|
|
@@ -4785,30 +5054,58 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4785
5054
|
const list = target.parentElement;
|
|
4786
5055
|
if (!list)
|
|
4787
5056
|
return;
|
|
5057
|
+
let changed = false;
|
|
4788
5058
|
if (direction === "up") {
|
|
4789
5059
|
const previous = elementSibling(target, "previous");
|
|
4790
|
-
if (previous?.nodeName
|
|
4791
|
-
|
|
5060
|
+
if (previous?.nodeName !== "LI")
|
|
5061
|
+
return;
|
|
5062
|
+
pushEditorHistory();
|
|
5063
|
+
list.insertBefore(target, previous);
|
|
5064
|
+
changed = true;
|
|
4792
5065
|
}
|
|
4793
5066
|
else if (direction === "down") {
|
|
4794
5067
|
const next = elementSibling(target, "next");
|
|
4795
|
-
if (next?.nodeName
|
|
4796
|
-
|
|
5068
|
+
if (next?.nodeName !== "LI")
|
|
5069
|
+
return;
|
|
5070
|
+
pushEditorHistory();
|
|
5071
|
+
list.insertBefore(next, target);
|
|
5072
|
+
changed = true;
|
|
4797
5073
|
}
|
|
4798
5074
|
else if (direction === "right") {
|
|
4799
|
-
|
|
5075
|
+
if (!canIndentListItems([target]))
|
|
5076
|
+
return;
|
|
5077
|
+
pushEditorHistory();
|
|
5078
|
+
changed = nestSelectedListItems([target]);
|
|
4800
5079
|
}
|
|
4801
5080
|
else {
|
|
4802
|
-
|
|
5081
|
+
if (!canOutdentListItems([target]))
|
|
5082
|
+
return;
|
|
5083
|
+
pushEditorHistory();
|
|
5084
|
+
changed = outdentSelectedListItems([target]);
|
|
4803
5085
|
}
|
|
5086
|
+
if (!changed)
|
|
5087
|
+
return;
|
|
5088
|
+
if (direction === "up" || direction === "down")
|
|
5089
|
+
focusElementEnd(target);
|
|
5090
|
+
handleInput();
|
|
5091
|
+
requestAnimationFrame(updateActiveState);
|
|
5092
|
+
return;
|
|
4804
5093
|
}
|
|
4805
5094
|
else if (direction === "up") {
|
|
4806
5095
|
const previous = elementSibling(target, "previous");
|
|
5096
|
+
if (!previous)
|
|
5097
|
+
return;
|
|
5098
|
+
if (!historyPushed)
|
|
5099
|
+
pushEditorHistory();
|
|
4807
5100
|
if (previous)
|
|
4808
5101
|
target.parentElement?.insertBefore(target, previous);
|
|
4809
5102
|
}
|
|
4810
5103
|
else if (direction === "down") {
|
|
4811
5104
|
const next = elementSibling(target, "next");
|
|
5105
|
+
if (!next)
|
|
5106
|
+
return;
|
|
5107
|
+
if (!historyPushed)
|
|
5108
|
+
pushEditorHistory();
|
|
4812
5109
|
if (next)
|
|
4813
5110
|
target.parentElement?.insertBefore(next, target);
|
|
4814
5111
|
}
|
|
@@ -4817,6 +5114,10 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4817
5114
|
const nextMargin = direction === "right"
|
|
4818
5115
|
? Math.min(current + 24, 240)
|
|
4819
5116
|
: Math.max(current - 24, 0);
|
|
5117
|
+
if (nextMargin === current)
|
|
5118
|
+
return;
|
|
5119
|
+
if (!historyPushed)
|
|
5120
|
+
pushEditorHistory();
|
|
4820
5121
|
target.style.marginLeft = nextMargin ? `${nextMargin}px` : "";
|
|
4821
5122
|
}
|
|
4822
5123
|
focusElementEnd(target);
|
|
@@ -4851,14 +5152,13 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4851
5152
|
const activeButtonStyle = (active = false, extra = {}) => ({
|
|
4852
5153
|
height: 32,
|
|
4853
5154
|
minWidth: 32,
|
|
4854
|
-
padding: "0
|
|
4855
|
-
border: active
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
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",
|
|
4862
5162
|
...extra,
|
|
4863
5163
|
});
|
|
4864
5164
|
const preserveToolbarMouseDown = (event) => {
|
|
@@ -4872,16 +5172,16 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4872
5172
|
const editorClass = `srte-editor${theme === 'dark' ? ' srte-dark' : ''}${className ? ' ' + className : ''}`;
|
|
4873
5173
|
return (_jsxs("div", { className: editorClass, style: {
|
|
4874
5174
|
border: "1px solid var(--srte-border)",
|
|
4875
|
-
borderRadius:
|
|
5175
|
+
borderRadius: "var(--srte-radius)",
|
|
4876
5176
|
width: "100%",
|
|
4877
5177
|
maxWidth: "100vw",
|
|
4878
5178
|
overflow: "visible",
|
|
4879
5179
|
display: "flex",
|
|
4880
5180
|
flexDirection: "column",
|
|
4881
|
-
background: "var(--srte-
|
|
4882
|
-
color: "var(--srte-
|
|
5181
|
+
background: "var(--srte-background)",
|
|
5182
|
+
color: "var(--srte-foreground)",
|
|
4883
5183
|
boxSizing: "border-box"
|
|
4884
|
-
}, 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) => {
|
|
4885
5185
|
if (readOnly) {
|
|
4886
5186
|
event.preventDefault();
|
|
4887
5187
|
event.stopPropagation();
|
|
@@ -4899,16 +5199,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4899
5199
|
event.preventDefault();
|
|
4900
5200
|
event.stopPropagation();
|
|
4901
5201
|
}, style: {
|
|
4902
|
-
display: "flex",
|
|
4903
|
-
flexWrap: "wrap",
|
|
4904
5202
|
maxWidth: "100%",
|
|
4905
|
-
gap: 8,
|
|
4906
|
-
padding: 8,
|
|
4907
|
-
borderBottom: "1px solid var(--srte-border-light)",
|
|
4908
|
-
background: "var(--srte-toolbar-bg)",
|
|
4909
|
-
position: "sticky",
|
|
4910
|
-
top: 0,
|
|
4911
|
-
zIndex: 1,
|
|
4912
5203
|
opacity: readOnly ? 0.55 : 1,
|
|
4913
5204
|
pointerEvents: readOnly ? "none" : "auto",
|
|
4914
5205
|
userSelect: readOnly ? "none" : undefined,
|
|
@@ -4950,7 +5241,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4950
5241
|
} }), _jsx("input", { ref: mdInputRef, type: "file", accept: ".md,.markdown,text/markdown,text/plain", style: { display: "none" }, onChange: (e) => {
|
|
4951
5242
|
importTextFile(e.currentTarget.files, "md");
|
|
4952
5243
|
e.currentTarget.value = "";
|
|
4953
|
-
} }), _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) => {
|
|
4954
5245
|
const val = e.target.value;
|
|
4955
5246
|
if (val === "p")
|
|
4956
5247
|
applyFormatBlock("<p>");
|
|
@@ -4973,16 +5264,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
4973
5264
|
borderRadius: 6,
|
|
4974
5265
|
background: "var(--srte-input-bg)",
|
|
4975
5266
|
color: "var(--srte-input-text)",
|
|
4976
|
-
}, 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" })] }),
|
|
4977
|
-
["left", "Left", "Align left"],
|
|
4978
|
-
["center", "Center", "Align center"],
|
|
4979
|
-
["right", "Right", "Align right"],
|
|
4980
|
-
["justify", "Justify", "Justify"],
|
|
4981
|
-
].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, {
|
|
4982
|
-
minWidth: 32,
|
|
4983
|
-
padding: "0 6px",
|
|
4984
|
-
fontSize: 10,
|
|
4985
|
-
}), 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: {
|
|
4986
5268
|
height: 32,
|
|
4987
5269
|
padding: "0 8px",
|
|
4988
5270
|
border: "1px solid var(--srte-input-border)",
|
|
@@ -5006,7 +5288,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
5006
5288
|
background: "var(--srte-input-bg)",
|
|
5007
5289
|
color: "var(--srte-input-text)",
|
|
5008
5290
|
maxWidth: 100,
|
|
5009
|
-
}, 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: () => {
|
|
5010
5292
|
setColorPickerType('text');
|
|
5011
5293
|
setShowColorPicker(true);
|
|
5012
5294
|
}, style: {
|
|
@@ -5018,7 +5300,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
5018
5300
|
background: "var(--srte-input-bg)",
|
|
5019
5301
|
color: "var(--srte-input-text)",
|
|
5020
5302
|
position: "relative",
|
|
5021
|
-
}, 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: () => {
|
|
5022
5304
|
setColorPickerType('background');
|
|
5023
5305
|
setShowColorPicker(true);
|
|
5024
5306
|
}, style: {
|
|
@@ -5029,10 +5311,10 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
5029
5311
|
borderRadius: 6,
|
|
5030
5312
|
background: "var(--srte-input-bg)",
|
|
5031
5313
|
color: "var(--srte-input-text)",
|
|
5032
|
-
}, children: _jsx("span", { style: { fontWeight: 700, padding: "1px 4px", background: "var(--srte-accent-bg)", borderRadius: 3 }, children: "A" }) }), _jsxs("button", { title: "Subscript", onMouseDown: preserveEditorSelection, onClick: () => toggleInlineScript("subscript"), "aria-pressed": activeState.subscript, style: activeButtonStyle(activeState.subscript), children: ["X", _jsx("sub", { children: "2" })] }), _jsxs("button", { title: "Superscript", onMouseDown: preserveEditorSelection, 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" })] }), [
|
|
5033
5315
|
{
|
|
5034
5316
|
key: "check",
|
|
5035
|
-
|
|
5317
|
+
icon: "checklist",
|
|
5036
5318
|
title: "Checklist",
|
|
5037
5319
|
active: activeState.checklist,
|
|
5038
5320
|
action: () => applyChecklist(false, true),
|
|
@@ -5040,7 +5322,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
5040
5322
|
},
|
|
5041
5323
|
{
|
|
5042
5324
|
key: "bullet",
|
|
5043
|
-
|
|
5325
|
+
icon: "bullets",
|
|
5044
5326
|
title: "Bulleted list",
|
|
5045
5327
|
active: activeState.unorderedList,
|
|
5046
5328
|
action: () => applyListStyle("bullet:disc"),
|
|
@@ -5048,13 +5330,13 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
5048
5330
|
},
|
|
5049
5331
|
{
|
|
5050
5332
|
key: "ordered",
|
|
5051
|
-
|
|
5333
|
+
icon: "numbers",
|
|
5052
5334
|
title: "Numbered list",
|
|
5053
5335
|
active: activeState.orderedList,
|
|
5054
5336
|
action: () => applyListStyle("ordered:decimal"),
|
|
5055
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."]],
|
|
5056
5338
|
},
|
|
5057
|
-
].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) => {
|
|
5058
5340
|
const selected = event.currentTarget.value;
|
|
5059
5341
|
if (selected === "check:plain")
|
|
5060
5342
|
applyChecklist(false);
|
|
@@ -5063,200 +5345,62 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
5063
5345
|
else if (selected)
|
|
5064
5346
|
applyListStyle(selected);
|
|
5065
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();
|
|
5066
5377
|
}, style: {
|
|
5067
|
-
width: 28,
|
|
5068
|
-
height: 32,
|
|
5069
|
-
padding: 0,
|
|
5070
|
-
border: "1px solid var(--srte-input-border)",
|
|
5071
|
-
borderLeft: 0,
|
|
5072
|
-
borderRadius: "0 6px 6px 0",
|
|
5073
|
-
background: "var(--srte-input-bg)",
|
|
5074
|
-
color: "var(--srte-input-text)",
|
|
5075
|
-
}, 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: {
|
|
5076
|
-
height: 32,
|
|
5077
|
-
minWidth: 32,
|
|
5078
|
-
padding: "0 8px",
|
|
5079
|
-
border: "1px solid var(--srte-input-border)",
|
|
5080
|
-
borderRadius: 6,
|
|
5081
|
-
background: "var(--srte-input-bg)",
|
|
5082
|
-
color: "var(--srte-input-text)",
|
|
5083
|
-
}, children: "\u03A9" }), _jsx("button", { type: "button", title: "Code block", onPointerDown: preserveEditorSelection, onClick: toggleCodeBlock, "aria-pressed": activeState.codeBlock, style: activeButtonStyle(activeState.codeBlock, {
|
|
5084
|
-
minWidth: 36,
|
|
5085
|
-
fontFamily: "ui-monospace, SFMono-Regular, Menlo",
|
|
5086
|
-
}), children: "< />" }), formula && (_jsx("button", { title: "Insert formula", onClick: () => setShowFormulaDialog(true), style: {
|
|
5087
|
-
height: 32,
|
|
5088
|
-
minWidth: 32,
|
|
5089
|
-
padding: "0 8px",
|
|
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: "\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: {
|
|
5095
|
-
height: 32,
|
|
5096
|
-
minWidth: 34,
|
|
5097
|
-
padding: "0 8px",
|
|
5098
|
-
border: "1px solid var(--srte-input-border)",
|
|
5099
|
-
borderRadius: 6,
|
|
5100
|
-
background: "var(--srte-input-bg)",
|
|
5101
|
-
color: "var(--srte-input-text)",
|
|
5102
|
-
cursor: activeState.link ? "pointer" : "not-allowed",
|
|
5103
|
-
opacity: activeState.link ? 1 : 0.45,
|
|
5104
|
-
fontSize: 16,
|
|
5105
|
-
}, children: _jsx("span", { "aria-hidden": "true", children: "\u2197\u0338" }) }), media && (_jsxs(_Fragment, { children: [_jsx("button", { title: "Insert image", onClick: insertImage, style: {
|
|
5106
|
-
height: 32,
|
|
5107
|
-
padding: "0 10px",
|
|
5108
|
-
border: "1px solid var(--srte-input-border)",
|
|
5109
|
-
borderRadius: 6,
|
|
5110
|
-
background: "var(--srte-input-bg)",
|
|
5111
|
-
color: "var(--srte-input-text)",
|
|
5112
|
-
}, children: "\uD83D\uDDBC\uFE0F Image" }), mediaManager && (_jsx("button", { title: "Open media manager", onClick: () => setShowMediaManager(true), style: {
|
|
5113
|
-
height: 32,
|
|
5114
|
-
padding: "0 10px",
|
|
5115
|
-
border: "1px solid var(--srte-input-border)",
|
|
5116
|
-
borderRadius: 6,
|
|
5117
|
-
background: "var(--srte-input-bg)",
|
|
5118
|
-
color: "var(--srte-input-text)",
|
|
5119
|
-
}, children: "\uD83D\uDCC1 Media" })), _jsx("button", { title: "Import PDF", onClick: () => pdfInputRef.current?.click(), disabled: loadingPdf, style: {
|
|
5120
|
-
height: 32,
|
|
5121
|
-
padding: "0 10px",
|
|
5122
|
-
border: "1px solid var(--srte-input-border)",
|
|
5123
|
-
borderRadius: 6,
|
|
5124
|
-
background: "var(--srte-input-bg)",
|
|
5125
|
-
color: "var(--srte-input-text)",
|
|
5126
|
-
opacity: loadingPdf ? 0.5 : 1,
|
|
5127
|
-
}, children: loadingPdf ? '⌛ Importing...' : '📄 PDF' }), _jsx("button", { title: "Import DOCX", onClick: () => docxInputRef.current?.click(), disabled: loadingDocx, style: {
|
|
5128
|
-
height: 32,
|
|
5129
|
-
padding: "0 10px",
|
|
5130
|
-
border: "1px solid var(--srte-input-border)",
|
|
5131
|
-
borderRadius: 6,
|
|
5132
|
-
background: "var(--srte-input-bg)",
|
|
5133
|
-
color: "var(--srte-input-text)",
|
|
5134
|
-
opacity: loadingDocx ? 0.5 : 1,
|
|
5135
|
-
}, children: loadingDocx ? '⌛ Importing...' : '📝 DOCX' }), _jsx("button", { title: "Import HTML", onClick: () => htmlInputRef.current?.click(), style: {
|
|
5136
|
-
height: 32,
|
|
5137
|
-
padding: "0 10px",
|
|
5138
|
-
border: "1px solid var(--srte-input-border)",
|
|
5139
|
-
borderRadius: 6,
|
|
5140
|
-
background: "var(--srte-input-bg)",
|
|
5141
|
-
color: "var(--srte-input-text)",
|
|
5142
|
-
}, children: "HTML" }), _jsx("button", { title: "Import Markdown", onClick: () => mdInputRef.current?.click(), style: {
|
|
5143
|
-
height: 32,
|
|
5144
|
-
padding: "0 10px",
|
|
5145
|
-
border: "1px solid var(--srte-input-border)",
|
|
5146
|
-
borderRadius: 6,
|
|
5147
|
-
background: "var(--srte-input-bg)",
|
|
5148
|
-
color: "var(--srte-input-text)",
|
|
5149
|
-
}, children: "MD" }), _jsx("button", { title: "Export HTML", onClick: exportHtml, style: {
|
|
5150
|
-
height: 32,
|
|
5151
|
-
padding: "0 10px",
|
|
5152
|
-
border: "1px solid var(--srte-input-border)",
|
|
5153
|
-
borderRadius: 6,
|
|
5154
|
-
background: "var(--srte-input-bg)",
|
|
5155
|
-
color: "var(--srte-input-text)",
|
|
5156
|
-
}, children: "Export HTML" }), _jsx("button", { title: "Export Markdown", onClick: exportMarkdown, style: {
|
|
5157
5378
|
height: 32,
|
|
5158
|
-
padding: "0
|
|
5379
|
+
padding: "0 8px",
|
|
5159
5380
|
border: "1px solid var(--srte-input-border)",
|
|
5160
5381
|
borderRadius: 6,
|
|
5161
5382
|
background: "var(--srte-input-bg)",
|
|
5162
5383
|
color: "var(--srte-input-text)",
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
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: {
|
|
5171
5397
|
height: 32,
|
|
5172
|
-
padding: "0
|
|
5398
|
+
padding: "0 8px",
|
|
5173
5399
|
border: "1px solid var(--srte-input-border)",
|
|
5174
5400
|
borderRadius: 6,
|
|
5175
5401
|
background: "var(--srte-input-bg)",
|
|
5176
5402
|
color: "var(--srte-input-text)",
|
|
5177
|
-
}, children: "Export PDF" }), _jsxs("div", {
|
|
5178
|
-
display: "inline-flex",
|
|
5179
|
-
gap: 4,
|
|
5180
|
-
alignItems: "center",
|
|
5181
|
-
marginLeft: 6,
|
|
5182
|
-
}, children: [_jsx("span", { style: { fontSize: 12, opacity: 0.7 }, children: "Image align:" }), _jsx("button", { onClick: () => {
|
|
5183
|
-
const img = selectedImage;
|
|
5184
|
-
if (!img)
|
|
5185
|
-
return;
|
|
5186
|
-
img.style.display = "block";
|
|
5187
|
-
img.style.margin = "0 auto";
|
|
5188
|
-
img.style.float = "none";
|
|
5189
|
-
scheduleImageOverlay();
|
|
5190
|
-
handleInput();
|
|
5191
|
-
}, title: "Center", style: {
|
|
5192
|
-
height: 28,
|
|
5193
|
-
minWidth: 28,
|
|
5194
|
-
padding: "0 6px",
|
|
5195
|
-
border: "1px solid var(--srte-input-border)",
|
|
5196
|
-
borderRadius: 6,
|
|
5197
|
-
background: "var(--srte-input-bg)",
|
|
5198
|
-
color: "var(--srte-input-text)",
|
|
5199
|
-
}, children: "\u2299" }), _jsx("button", { onClick: () => {
|
|
5200
|
-
const img = selectedImage;
|
|
5201
|
-
if (!img)
|
|
5202
|
-
return;
|
|
5203
|
-
img.style.display = "inline";
|
|
5204
|
-
img.style.float = "left";
|
|
5205
|
-
img.style.margin = "0 8px 8px 0";
|
|
5206
|
-
scheduleImageOverlay();
|
|
5207
|
-
handleInput();
|
|
5208
|
-
}, title: "Float left", style: {
|
|
5209
|
-
height: 28,
|
|
5210
|
-
minWidth: 28,
|
|
5211
|
-
padding: "0 6px",
|
|
5212
|
-
border: "1px solid var(--srte-input-border)",
|
|
5213
|
-
borderRadius: 6,
|
|
5214
|
-
background: "var(--srte-input-bg)",
|
|
5215
|
-
color: "var(--srte-input-text)",
|
|
5216
|
-
}, children: "\u27F8" }), _jsx("button", { onClick: () => {
|
|
5217
|
-
const img = selectedImage;
|
|
5218
|
-
if (!img)
|
|
5219
|
-
return;
|
|
5220
|
-
img.style.display = "inline";
|
|
5221
|
-
img.style.float = "right";
|
|
5222
|
-
img.style.margin = "0 0 8px 8px";
|
|
5223
|
-
scheduleImageOverlay();
|
|
5224
|
-
handleInput();
|
|
5225
|
-
}, title: "Float right", style: {
|
|
5226
|
-
height: 28,
|
|
5227
|
-
minWidth: 28,
|
|
5228
|
-
padding: "0 6px",
|
|
5229
|
-
border: "1px solid var(--srte-input-border)",
|
|
5230
|
-
borderRadius: 6,
|
|
5231
|
-
background: "var(--srte-input-bg)",
|
|
5232
|
-
color: "var(--srte-input-text)",
|
|
5233
|
-
}, children: "\u27F9" })] })] })), table && (_jsx("button", { title: "Insert table", onClick: () => setShowTableDialog(true), style: {
|
|
5234
|
-
height: 32,
|
|
5235
|
-
padding: "0 10px",
|
|
5236
|
-
border: "1px solid var(--srte-input-border)",
|
|
5237
|
-
borderRadius: 6,
|
|
5238
|
-
background: "var(--srte-input-bg)",
|
|
5239
|
-
color: "var(--srte-input-text)",
|
|
5240
|
-
}, children: "\u2795 Table" })), _jsxs("div", { style: {
|
|
5241
|
-
display: "inline-flex",
|
|
5242
|
-
gap: 4,
|
|
5243
|
-
alignItems: "center",
|
|
5244
|
-
marginLeft: 6,
|
|
5245
|
-
}, 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: {
|
|
5246
|
-
height: 32,
|
|
5247
|
-
padding: "0 10px",
|
|
5248
|
-
border: "1px solid var(--srte-input-border)",
|
|
5249
|
-
borderRadius: 6,
|
|
5250
|
-
background: "var(--srte-input-bg)",
|
|
5251
|
-
color: "var(--srte-input-text)",
|
|
5252
|
-
}, children: "\u238C Undo" }), _jsx("button", { title: "Redo", onClick: () => exec("redo"), style: {
|
|
5253
|
-
height: 32,
|
|
5254
|
-
padding: "0 10px",
|
|
5255
|
-
border: "1px solid var(--srte-input-border)",
|
|
5256
|
-
borderRadius: 6,
|
|
5257
|
-
background: "var(--srte-input-bg)",
|
|
5258
|
-
color: "var(--srte-input-text)",
|
|
5259
|
-
}, 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) => {
|
|
5260
5404
|
if (item?.url)
|
|
5261
5405
|
insertImageAtSelection(item);
|
|
5262
5406
|
} })), table && showTableDialog && (_jsx("div", { style: {
|
|
@@ -5716,7 +5860,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
5716
5860
|
pushEditorHistory();
|
|
5717
5861
|
const checked = item.dataset.checked !== "true";
|
|
5718
5862
|
item.dataset.checked = checked ? "true" : "false";
|
|
5719
|
-
t.
|
|
5863
|
+
t.dataset.checked = checked ? "true" : "false";
|
|
5864
|
+
t.setAttribute("aria-pressed", checked ? "true" : "false");
|
|
5720
5865
|
t.setAttribute("aria-label", checked ? "Mark incomplete" : "Mark complete");
|
|
5721
5866
|
item.style.textDecoration =
|
|
5722
5867
|
list.dataset.srteChecklistStrike === "true" && checked ? "line-through" : "";
|
|
@@ -5813,26 +5958,16 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
5813
5958
|
}
|
|
5814
5959
|
// Keep Tab for indentation in lists; otherwise insert 2 spaces
|
|
5815
5960
|
if (e.key === "Tab") {
|
|
5816
|
-
e.preventDefault();
|
|
5817
5961
|
const selection = getSelectionRangeInEditor();
|
|
5818
|
-
const selectedListItems = selection
|
|
5819
|
-
? getSelectedListItems(getSelectedBlocks(selection))
|
|
5820
|
-
: [];
|
|
5962
|
+
const selectedListItems = selection ? resolveSelectedListItems(selection) : [];
|
|
5821
5963
|
if (selectedListItems.length > 0) {
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
return;
|
|
5825
|
-
}
|
|
5826
|
-
pushEditorHistory();
|
|
5827
|
-
exec("outdent");
|
|
5964
|
+
e.preventDefault();
|
|
5965
|
+
applyListDepthChange(selectedListItems, e.shiftKey ? "outdent" : "indent");
|
|
5828
5966
|
return;
|
|
5829
5967
|
}
|
|
5968
|
+
e.preventDefault();
|
|
5830
5969
|
const currentBlock = getCurrentBlock();
|
|
5831
|
-
if (currentBlock?.closest("li")) {
|
|
5832
|
-
pushEditorHistory();
|
|
5833
|
-
exec(e.shiftKey ? "outdent" : "indent");
|
|
5834
|
-
}
|
|
5835
|
-
else {
|
|
5970
|
+
if (!currentBlock?.closest("li")) {
|
|
5836
5971
|
captureInputHistory("insertText");
|
|
5837
5972
|
document.execCommand("insertText", false, " ");
|
|
5838
5973
|
}
|