smartrte-react 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ClassicEditor.d.ts +2 -2
- package/dist/components/ClassicEditor.js +938 -596
- package/dist/components/MediaManager.js +4 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/standalone/classic-editor-embed.js +1 -1
- package/dist/theme.d.ts +1 -1
- package/dist/theme.js +25 -3
- package/package.json +1 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
|
-
import { MediaManager } from "./MediaManager";
|
|
3
|
+
import { MediaManager } from "./MediaManager.js";
|
|
4
4
|
import * as pdfjsLib from 'pdfjs-dist';
|
|
5
5
|
import mammoth from 'mammoth';
|
|
6
6
|
import JSZip from 'jszip';
|
|
7
|
-
import { ensureStyleSheet } from '../theme';
|
|
7
|
+
import { ensureStyleSheet } from '../theme.js';
|
|
8
8
|
// Initialize PDF.js worker
|
|
9
9
|
if (typeof window !== 'undefined') {
|
|
10
10
|
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url).toString();
|
|
@@ -20,6 +20,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
20
20
|
], defaultFont, preserveFontFamily = false, preserveColors = false, preserveDocxStyles = true, theme = "light", className, }) {
|
|
21
21
|
ensureStyleSheet();
|
|
22
22
|
const editableRef = useRef(null);
|
|
23
|
+
const editorScrollRef = useRef(null);
|
|
23
24
|
const lastEmittedRef = useRef("");
|
|
24
25
|
const isComposingRef = useRef(false);
|
|
25
26
|
const fileInputRef = useRef(null);
|
|
@@ -53,6 +54,18 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
53
54
|
const savedRangeRef = useRef(null);
|
|
54
55
|
const [currentFontSize, setCurrentFontSize] = useState("");
|
|
55
56
|
const [currentFont, setCurrentFont] = useState("");
|
|
57
|
+
const [activeState, setActiveState] = useState({
|
|
58
|
+
bold: false,
|
|
59
|
+
italic: false,
|
|
60
|
+
underline: false,
|
|
61
|
+
strikeThrough: false,
|
|
62
|
+
subscript: false,
|
|
63
|
+
superscript: false,
|
|
64
|
+
unorderedList: false,
|
|
65
|
+
orderedList: false,
|
|
66
|
+
blockquote: false,
|
|
67
|
+
codeBlock: false,
|
|
68
|
+
});
|
|
56
69
|
useEffect(() => {
|
|
57
70
|
const el = editableRef.current;
|
|
58
71
|
if (!el)
|
|
@@ -77,6 +90,35 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
77
90
|
el.removeEventListener("contextmenu", onCtx, { capture: true });
|
|
78
91
|
};
|
|
79
92
|
}, [value]);
|
|
93
|
+
const updateActiveState = () => {
|
|
94
|
+
const editor = editableRef.current;
|
|
95
|
+
if (!editor)
|
|
96
|
+
return;
|
|
97
|
+
try {
|
|
98
|
+
const sel = window.getSelection();
|
|
99
|
+
const range = sel && sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
|
|
100
|
+
const inEditor = range && editor.contains(range.commonAncestorContainer);
|
|
101
|
+
if (!inEditor)
|
|
102
|
+
return;
|
|
103
|
+
let node = range.commonAncestorContainer;
|
|
104
|
+
if (node.nodeType === Node.TEXT_NODE)
|
|
105
|
+
node = node.parentNode;
|
|
106
|
+
const element = node instanceof HTMLElement ? node : null;
|
|
107
|
+
setActiveState({
|
|
108
|
+
bold: document.queryCommandState("bold"),
|
|
109
|
+
italic: document.queryCommandState("italic"),
|
|
110
|
+
underline: document.queryCommandState("underline"),
|
|
111
|
+
strikeThrough: document.queryCommandState("strikeThrough"),
|
|
112
|
+
subscript: document.queryCommandState("subscript"),
|
|
113
|
+
superscript: document.queryCommandState("superscript"),
|
|
114
|
+
unorderedList: Boolean(element?.closest("ul")),
|
|
115
|
+
orderedList: Boolean(element?.closest("ol")),
|
|
116
|
+
blockquote: Boolean(element?.closest("blockquote")),
|
|
117
|
+
codeBlock: Boolean(element?.closest("pre")),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
catch { }
|
|
121
|
+
};
|
|
80
122
|
// Save selection whenever it changes
|
|
81
123
|
useEffect(() => {
|
|
82
124
|
const saveSelection = () => {
|
|
@@ -86,6 +128,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
86
128
|
const editor = editableRef.current;
|
|
87
129
|
if (editor && editor.contains(range.commonAncestorContainer)) {
|
|
88
130
|
savedRangeRef.current = range.cloneRange();
|
|
131
|
+
updateActiveState();
|
|
89
132
|
}
|
|
90
133
|
}
|
|
91
134
|
};
|
|
@@ -96,8 +139,27 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
96
139
|
}, []);
|
|
97
140
|
const exec = (command, valueArg) => {
|
|
98
141
|
try {
|
|
99
|
-
|
|
100
|
-
|
|
142
|
+
if (!restoreSavedSelection()) {
|
|
143
|
+
safeSelectRange(getSelectionRangeInEditor());
|
|
144
|
+
}
|
|
145
|
+
if (command === "insertUnorderedList") {
|
|
146
|
+
toggleList("ul");
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (command === "insertOrderedList") {
|
|
150
|
+
toggleList("ol");
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const beforeHtml = editableRef.current?.innerHTML || "";
|
|
154
|
+
const ok = document.execCommand(command, false, valueArg);
|
|
155
|
+
const afterHtml = editableRef.current?.innerHTML || "";
|
|
156
|
+
const needsFallback = !ok ||
|
|
157
|
+
(command === "formatBlock" && beforeHtml === afterHtml);
|
|
158
|
+
if (needsFallback) {
|
|
159
|
+
if (command === "formatBlock" && valueArg)
|
|
160
|
+
applyFormatBlockFallback(valueArg);
|
|
161
|
+
}
|
|
162
|
+
handleInput();
|
|
101
163
|
}
|
|
102
164
|
catch { }
|
|
103
165
|
};
|
|
@@ -114,6 +176,29 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
114
176
|
onChange(html);
|
|
115
177
|
}
|
|
116
178
|
};
|
|
179
|
+
const restoreSavedSelection = () => {
|
|
180
|
+
const editor = editableRef.current;
|
|
181
|
+
if (!editor)
|
|
182
|
+
return false;
|
|
183
|
+
const saved = savedRangeRef.current;
|
|
184
|
+
if (saved && editor.contains(saved.commonAncestorContainer)) {
|
|
185
|
+
editor.focus({ preventScroll: true });
|
|
186
|
+
safeSelectRange(saved.cloneRange());
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
editor.focus({ preventScroll: true });
|
|
190
|
+
return false;
|
|
191
|
+
};
|
|
192
|
+
const preserveEditorSelection = () => {
|
|
193
|
+
const editor = editableRef.current;
|
|
194
|
+
const sel = window.getSelection();
|
|
195
|
+
if (!editor || !sel || sel.rangeCount === 0)
|
|
196
|
+
return;
|
|
197
|
+
const range = sel.getRangeAt(0);
|
|
198
|
+
if (editor.contains(range.commonAncestorContainer)) {
|
|
199
|
+
savedRangeRef.current = range.cloneRange();
|
|
200
|
+
}
|
|
201
|
+
};
|
|
117
202
|
const insertLink = () => {
|
|
118
203
|
const url = window.prompt("Enter URL", "https://");
|
|
119
204
|
if (!url)
|
|
@@ -124,7 +209,6 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
124
209
|
const editor = editableRef.current;
|
|
125
210
|
if (!editor)
|
|
126
211
|
return null;
|
|
127
|
-
editor.focus();
|
|
128
212
|
const sel = window.getSelection();
|
|
129
213
|
if (sel && sel.rangeCount > 0) {
|
|
130
214
|
const range = sel.getRangeAt(0);
|
|
@@ -139,6 +223,307 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
139
223
|
range.collapse(false);
|
|
140
224
|
return range;
|
|
141
225
|
};
|
|
226
|
+
const getCurrentBlock = () => {
|
|
227
|
+
const editor = editableRef.current;
|
|
228
|
+
const range = getSelectionRangeInEditor();
|
|
229
|
+
if (!editor || !range)
|
|
230
|
+
return null;
|
|
231
|
+
let node = range.commonAncestorContainer;
|
|
232
|
+
if (node.nodeType === Node.TEXT_NODE)
|
|
233
|
+
node = node.parentNode;
|
|
234
|
+
const element = node instanceof HTMLElement ? node : null;
|
|
235
|
+
const block = element?.closest("p,h1,h2,h3,h4,h5,h6,li,blockquote,pre,div");
|
|
236
|
+
if (!block || block === editor || !editor.contains(block))
|
|
237
|
+
return null;
|
|
238
|
+
return block;
|
|
239
|
+
};
|
|
240
|
+
const blockSelector = "p,h1,h2,h3,h4,h5,h6,li,blockquote,pre,div";
|
|
241
|
+
const getSelectedBlocks = (range) => {
|
|
242
|
+
const editor = editableRef.current;
|
|
243
|
+
if (!editor)
|
|
244
|
+
return [];
|
|
245
|
+
const blocks = Array.from(editor.querySelectorAll(blockSelector))
|
|
246
|
+
.filter((block) => {
|
|
247
|
+
if (block === editor)
|
|
248
|
+
return false;
|
|
249
|
+
const parentBlock = block.parentElement?.closest(blockSelector);
|
|
250
|
+
if (parentBlock && parentBlock !== editor && editor.contains(parentBlock))
|
|
251
|
+
return false;
|
|
252
|
+
try {
|
|
253
|
+
return range.intersectsNode(block);
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
if (blocks.length > 0)
|
|
260
|
+
return blocks;
|
|
261
|
+
const current = getCurrentBlock();
|
|
262
|
+
return current ? [current] : [];
|
|
263
|
+
};
|
|
264
|
+
const getSelectedListItems = (blocks) => {
|
|
265
|
+
const seen = new Set();
|
|
266
|
+
const items = [];
|
|
267
|
+
blocks.forEach((block) => {
|
|
268
|
+
const li = block.tagName.toLowerCase() === "li"
|
|
269
|
+
? block
|
|
270
|
+
: block.closest("li");
|
|
271
|
+
if (li && editableRef.current?.contains(li) && !seen.has(li)) {
|
|
272
|
+
seen.add(li);
|
|
273
|
+
items.push(li);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
return items;
|
|
277
|
+
};
|
|
278
|
+
const copyCellOrBlockStyles = (from, to) => {
|
|
279
|
+
to.innerHTML = from.innerHTML || "<br>";
|
|
280
|
+
const style = from.getAttribute("style");
|
|
281
|
+
if (style)
|
|
282
|
+
to.setAttribute("style", style);
|
|
283
|
+
};
|
|
284
|
+
const applyHeaderCellStyle = (cell) => {
|
|
285
|
+
cell.style.fontWeight = "700";
|
|
286
|
+
cell.style.background = "#f3f4f6";
|
|
287
|
+
cell.style.textAlign = cell.style.textAlign || "left";
|
|
288
|
+
};
|
|
289
|
+
const clearHeaderCellStyle = (cell) => {
|
|
290
|
+
if (cell.style.fontWeight === "700" || cell.style.fontWeight === "bold")
|
|
291
|
+
cell.style.fontWeight = "";
|
|
292
|
+
if (cell.style.background === "var(--srte-surface-subtle)" || cell.style.background === "rgb(243, 244, 246)" || cell.style.background === "#f3f4f6")
|
|
293
|
+
cell.style.background = "";
|
|
294
|
+
};
|
|
295
|
+
const replaceTableCellTag = (cell, tag) => {
|
|
296
|
+
const replacement = document.createElement(tag);
|
|
297
|
+
replacement.innerHTML = cell.innerHTML || " ";
|
|
298
|
+
replacement.colSpan = cell.colSpan;
|
|
299
|
+
replacement.rowSpan = cell.rowSpan;
|
|
300
|
+
const style = cell.getAttribute("style");
|
|
301
|
+
if (style)
|
|
302
|
+
replacement.setAttribute("style", style);
|
|
303
|
+
if (cell.__rtePrevBg != null) {
|
|
304
|
+
replacement.style.background = cell.__rtePrevBg || "";
|
|
305
|
+
delete cell.__rtePrevBg;
|
|
306
|
+
}
|
|
307
|
+
if (replacement.style.background === "var(--srte-accent-bg)" ||
|
|
308
|
+
replacement.style.background.includes("59, 130, 246") ||
|
|
309
|
+
replacement.style.background.includes("59, 158, 255")) {
|
|
310
|
+
replacement.style.background = "";
|
|
311
|
+
}
|
|
312
|
+
replacement.style.outline = "";
|
|
313
|
+
replacement.style.outlineOffset = "";
|
|
314
|
+
replacement.style.border = replacement.style.border || "1px solid #d1d5db";
|
|
315
|
+
replacement.style.padding = replacement.style.padding || "6px";
|
|
316
|
+
replacement.style.minWidth = replacement.style.minWidth || "60px";
|
|
317
|
+
if (tag === "th")
|
|
318
|
+
applyHeaderCellStyle(replacement);
|
|
319
|
+
else
|
|
320
|
+
clearHeaderCellStyle(replacement);
|
|
321
|
+
cell.parentElement?.replaceChild(replacement, cell);
|
|
322
|
+
return replacement;
|
|
323
|
+
};
|
|
324
|
+
const applyFormatBlockFallback = (blockName) => {
|
|
325
|
+
const editor = editableRef.current;
|
|
326
|
+
const block = getCurrentBlock();
|
|
327
|
+
const tag = blockName.replace(/[<>]/g, "").toLowerCase() || "p";
|
|
328
|
+
if (!editor || !block || block === editor || !/^(p|h1|h2|h3|h4|h5|h6|pre|blockquote)$/.test(tag))
|
|
329
|
+
return;
|
|
330
|
+
const replacement = document.createElement(tag);
|
|
331
|
+
copyCellOrBlockStyles(block, replacement);
|
|
332
|
+
block.parentElement?.replaceChild(replacement, block);
|
|
333
|
+
const range = document.createRange();
|
|
334
|
+
range.selectNodeContents(replacement);
|
|
335
|
+
range.collapse(false);
|
|
336
|
+
safeSelectRange(range);
|
|
337
|
+
};
|
|
338
|
+
const cloneListShell = (list, tagName) => {
|
|
339
|
+
const clone = document.createElement(tagName || list.tagName.toLowerCase());
|
|
340
|
+
Array.from(list.attributes).forEach((attr) => clone.setAttribute(attr.name, attr.value));
|
|
341
|
+
return clone;
|
|
342
|
+
};
|
|
343
|
+
const focusElementEnd = (element) => {
|
|
344
|
+
const range = document.createRange();
|
|
345
|
+
range.selectNodeContents(element);
|
|
346
|
+
range.collapse(false);
|
|
347
|
+
safeSelectRange(range);
|
|
348
|
+
savedRangeRef.current = range.cloneRange();
|
|
349
|
+
};
|
|
350
|
+
const insertEmptyListAtSelection = (listTag) => {
|
|
351
|
+
const editor = editableRef.current;
|
|
352
|
+
const range = getSelectionRangeInEditor();
|
|
353
|
+
if (!editor || !range)
|
|
354
|
+
return;
|
|
355
|
+
const list = document.createElement(listTag);
|
|
356
|
+
const li = document.createElement("li");
|
|
357
|
+
li.innerHTML = "<br>";
|
|
358
|
+
list.appendChild(li);
|
|
359
|
+
range.deleteContents();
|
|
360
|
+
range.insertNode(list);
|
|
361
|
+
focusElementEnd(li);
|
|
362
|
+
};
|
|
363
|
+
const unwrapListItem = (li, list) => {
|
|
364
|
+
const parent = list.parentElement;
|
|
365
|
+
if (!parent)
|
|
366
|
+
return;
|
|
367
|
+
const paragraph = document.createElement("p");
|
|
368
|
+
paragraph.innerHTML = li.innerHTML || "<br>";
|
|
369
|
+
const beforeList = cloneListShell(list);
|
|
370
|
+
const afterList = cloneListShell(list);
|
|
371
|
+
while (list.firstChild && list.firstChild !== li) {
|
|
372
|
+
beforeList.appendChild(list.firstChild);
|
|
373
|
+
}
|
|
374
|
+
while (li.nextSibling) {
|
|
375
|
+
afterList.appendChild(li.nextSibling);
|
|
376
|
+
}
|
|
377
|
+
if (beforeList.childNodes.length)
|
|
378
|
+
parent.insertBefore(beforeList, list);
|
|
379
|
+
parent.insertBefore(paragraph, list);
|
|
380
|
+
if (afterList.childNodes.length)
|
|
381
|
+
parent.insertBefore(afterList, list);
|
|
382
|
+
li.remove();
|
|
383
|
+
if (!list.querySelector("li"))
|
|
384
|
+
list.remove();
|
|
385
|
+
focusElementEnd(paragraph);
|
|
386
|
+
};
|
|
387
|
+
const convertListTag = (list, listTag) => {
|
|
388
|
+
const replacement = cloneListShell(list, listTag);
|
|
389
|
+
replacement.innerHTML = list.innerHTML;
|
|
390
|
+
list.parentElement?.replaceChild(replacement, list);
|
|
391
|
+
const li = replacement.querySelector("li");
|
|
392
|
+
focusElementEnd(li || replacement);
|
|
393
|
+
};
|
|
394
|
+
const getOrCreateNestedList = (li, listTag) => {
|
|
395
|
+
let nested = Array.from(li.children).find((child) => {
|
|
396
|
+
const tag = child.tagName.toLowerCase();
|
|
397
|
+
return tag === "ul" || tag === "ol";
|
|
398
|
+
});
|
|
399
|
+
if (nested && nested.tagName.toLowerCase() !== listTag) {
|
|
400
|
+
const replacement = cloneListShell(nested, listTag);
|
|
401
|
+
replacement.innerHTML = nested.innerHTML;
|
|
402
|
+
nested.parentElement?.replaceChild(replacement, nested);
|
|
403
|
+
nested = replacement;
|
|
404
|
+
}
|
|
405
|
+
if (!nested) {
|
|
406
|
+
nested = document.createElement(listTag);
|
|
407
|
+
li.appendChild(nested);
|
|
408
|
+
}
|
|
409
|
+
return nested;
|
|
410
|
+
};
|
|
411
|
+
const nestSelectedListItems = (items, listTag) => {
|
|
412
|
+
if (items.length === 0)
|
|
413
|
+
return false;
|
|
414
|
+
let changed = false;
|
|
415
|
+
let lastTargetList = null;
|
|
416
|
+
items.forEach((li) => {
|
|
417
|
+
const previous = li.previousElementSibling;
|
|
418
|
+
if (!previous || previous.tagName.toLowerCase() !== "li")
|
|
419
|
+
return;
|
|
420
|
+
const targetList = getOrCreateNestedList(previous, listTag);
|
|
421
|
+
targetList.appendChild(li);
|
|
422
|
+
lastTargetList = targetList;
|
|
423
|
+
changed = true;
|
|
424
|
+
});
|
|
425
|
+
if (changed) {
|
|
426
|
+
const lastItem = lastTargetList?.lastElementChild;
|
|
427
|
+
if (lastItem)
|
|
428
|
+
focusElementEnd(lastItem);
|
|
429
|
+
}
|
|
430
|
+
return changed;
|
|
431
|
+
};
|
|
432
|
+
const convertSelectedBlocksToList = (blocks, listTag) => {
|
|
433
|
+
const editor = editableRef.current;
|
|
434
|
+
if (!editor || blocks.length === 0)
|
|
435
|
+
return false;
|
|
436
|
+
const selected = blocks.filter((block) => {
|
|
437
|
+
if (!editor.contains(block))
|
|
438
|
+
return false;
|
|
439
|
+
if (block.tagName.toLowerCase() === "li")
|
|
440
|
+
return false;
|
|
441
|
+
if (block.closest("ul,ol"))
|
|
442
|
+
return false;
|
|
443
|
+
return block.parentElement;
|
|
444
|
+
});
|
|
445
|
+
if (selected.length === 0)
|
|
446
|
+
return false;
|
|
447
|
+
const groups = new Map();
|
|
448
|
+
selected.forEach((block) => {
|
|
449
|
+
const parent = block.parentElement;
|
|
450
|
+
if (!parent)
|
|
451
|
+
return;
|
|
452
|
+
const group = groups.get(parent) || [];
|
|
453
|
+
group.push(block);
|
|
454
|
+
groups.set(parent, group);
|
|
455
|
+
});
|
|
456
|
+
let lastLi = null;
|
|
457
|
+
groups.forEach((group, parent) => {
|
|
458
|
+
group.sort((a, b) => {
|
|
459
|
+
const position = a.compareDocumentPosition(b);
|
|
460
|
+
return position & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
|
|
461
|
+
});
|
|
462
|
+
const list = document.createElement(listTag);
|
|
463
|
+
parent.insertBefore(list, group[0]);
|
|
464
|
+
group.forEach((block) => {
|
|
465
|
+
const li = document.createElement("li");
|
|
466
|
+
li.innerHTML = block.innerHTML || "<br>";
|
|
467
|
+
list.appendChild(li);
|
|
468
|
+
block.remove();
|
|
469
|
+
lastLi = li;
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
if (lastLi)
|
|
473
|
+
focusElementEnd(lastLi);
|
|
474
|
+
return true;
|
|
475
|
+
};
|
|
476
|
+
const toggleList = (listTag) => {
|
|
477
|
+
const editor = editableRef.current;
|
|
478
|
+
if (!editor)
|
|
479
|
+
return;
|
|
480
|
+
if (!restoreSavedSelection())
|
|
481
|
+
safeSelectRange(getSelectionRangeInEditor());
|
|
482
|
+
const range = getSelectionRangeInEditor();
|
|
483
|
+
if (range && !range.collapsed) {
|
|
484
|
+
const blocks = getSelectedBlocks(range);
|
|
485
|
+
const selectedListItems = getSelectedListItems(blocks);
|
|
486
|
+
const convertedBlocks = convertSelectedBlocksToList(blocks, listTag);
|
|
487
|
+
const nestedItems = nestSelectedListItems(selectedListItems, listTag);
|
|
488
|
+
if (convertedBlocks || nestedItems) {
|
|
489
|
+
handleInput();
|
|
490
|
+
requestAnimationFrame(updateActiveState);
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
const block = getCurrentBlock();
|
|
495
|
+
if (!block) {
|
|
496
|
+
insertEmptyListAtSelection(listTag);
|
|
497
|
+
handleInput();
|
|
498
|
+
requestAnimationFrame(updateActiveState);
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
const currentList = block.closest("ul,ol");
|
|
502
|
+
if (currentList && editor.contains(currentList)) {
|
|
503
|
+
if (currentList.tagName.toLowerCase() === listTag) {
|
|
504
|
+
const li = block.closest("li");
|
|
505
|
+
if (li)
|
|
506
|
+
unwrapListItem(li, currentList);
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
convertListTag(currentList, listTag);
|
|
510
|
+
}
|
|
511
|
+
handleInput();
|
|
512
|
+
requestAnimationFrame(updateActiveState);
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const list = document.createElement(listTag);
|
|
516
|
+
const li = document.createElement("li");
|
|
517
|
+
li.innerHTML = block.innerHTML || "<br>";
|
|
518
|
+
list.appendChild(li);
|
|
519
|
+
block.parentElement?.replaceChild(list, block);
|
|
520
|
+
focusElementEnd(li);
|
|
521
|
+
handleInput();
|
|
522
|
+
requestAnimationFrame(updateActiveState);
|
|
523
|
+
};
|
|
524
|
+
const toggleListFallback = (listTag) => {
|
|
525
|
+
toggleList(listTag);
|
|
526
|
+
};
|
|
142
527
|
const insertTextAtSelection = (text) => {
|
|
143
528
|
if (!text)
|
|
144
529
|
return;
|
|
@@ -310,15 +695,17 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
310
695
|
};
|
|
311
696
|
const scheduleImageOverlay = () => {
|
|
312
697
|
const img = selectedImage;
|
|
698
|
+
const scroller = editorScrollRef.current;
|
|
313
699
|
if (!img) {
|
|
314
700
|
setImageOverlay(null);
|
|
315
701
|
return;
|
|
316
702
|
}
|
|
317
703
|
try {
|
|
318
704
|
const rect = img.getBoundingClientRect();
|
|
705
|
+
const hostRect = scroller?.getBoundingClientRect();
|
|
319
706
|
setImageOverlay({
|
|
320
|
-
left: rect.left,
|
|
321
|
-
top: rect.top,
|
|
707
|
+
left: hostRect && scroller ? rect.left - hostRect.left + scroller.scrollLeft : rect.left,
|
|
708
|
+
top: hostRect && scroller ? rect.top - hostRect.top + scroller.scrollTop : rect.top,
|
|
322
709
|
width: rect.width,
|
|
323
710
|
height: rect.height,
|
|
324
711
|
});
|
|
@@ -617,6 +1004,16 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
617
1004
|
if (!media)
|
|
618
1005
|
return;
|
|
619
1006
|
const list = Array.from(files).filter((f) => f.type.startsWith("image/"));
|
|
1007
|
+
if (mediaManager) {
|
|
1008
|
+
try {
|
|
1009
|
+
const uploaded = await mediaManager.upload(list);
|
|
1010
|
+
uploaded.forEach((item) => insertImageAtSelection(item));
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
catch (error) {
|
|
1014
|
+
console.error("Image upload failed, inserting local image data instead:", error);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
620
1017
|
for (const f of list) {
|
|
621
1018
|
await new Promise((resolve) => {
|
|
622
1019
|
const reader = new FileReader();
|
|
@@ -866,7 +1263,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
866
1263
|
cellContents[colIdx] += styledTxt;
|
|
867
1264
|
}
|
|
868
1265
|
cellContents.forEach(content => {
|
|
869
|
-
rowHtml += `<td style="border:1px solid
|
|
1266
|
+
rowHtml += `<td style="border:1px solid #d1d5db;padding:8px;vertical-align:top;">${content || ' '}</td>`;
|
|
870
1267
|
});
|
|
871
1268
|
rowHtml += '</tr>';
|
|
872
1269
|
tableHtml += rowHtml;
|
|
@@ -1525,13 +1922,12 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1525
1922
|
};
|
|
1526
1923
|
const exportPdf = () => {
|
|
1527
1924
|
const html = editableRef.current?.innerHTML || "";
|
|
1528
|
-
const printWindow = window.open("", "_blank", "
|
|
1925
|
+
const printWindow = window.open("", "_blank", "width=900,height=700");
|
|
1529
1926
|
if (!printWindow)
|
|
1530
1927
|
return;
|
|
1531
|
-
printWindow.document.
|
|
1928
|
+
printWindow.document.open();
|
|
1929
|
+
printWindow.document.write(`<!doctype html><html><head><title>Export PDF</title><style>@page{margin:18mm}html,body{background:#fff}body{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;line-height:1.6;padding:32px;color:#111}table{border-collapse:collapse;width:100%;margin:12px 0;break-inside:auto}tr,img,blockquote,pre{break-inside:avoid}td,th{border:1px solid #d1d5db;padding:8px;vertical-align:top}img{max-width:100%;height:auto}blockquote{border-left:4px solid #d1d5db;padding-left:12px;color:#374151}pre,code{background:#f3f4f6}pre{padding:12px;white-space:pre-wrap}@media print{body{padding:0}}</style></head><body>${html || "<p></p>"}<script>window.addEventListener("load",function(){setTimeout(function(){window.focus();window.print();},150);});</script></body></html>`);
|
|
1532
1930
|
printWindow.document.close();
|
|
1533
|
-
printWindow.focus();
|
|
1534
|
-
setTimeout(() => printWindow.print(), 250);
|
|
1535
1931
|
};
|
|
1536
1932
|
const fixNegativeMargins = (root) => {
|
|
1537
1933
|
try {
|
|
@@ -1753,7 +2149,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1753
2149
|
html += "<tr>";
|
|
1754
2150
|
for (let c = 0; c < safeCols; c++) {
|
|
1755
2151
|
html +=
|
|
1756
|
-
'<td style="border:1px solid
|
|
2152
|
+
'<td style="border:1px solid #d1d5db;padding:6px;min-width:60px;"> </td>';
|
|
1757
2153
|
}
|
|
1758
2154
|
html += "</tr>";
|
|
1759
2155
|
}
|
|
@@ -1836,16 +2232,56 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1836
2232
|
}
|
|
1837
2233
|
catch { }
|
|
1838
2234
|
};
|
|
2235
|
+
const getTableGrid = (tbody) => {
|
|
2236
|
+
const rows = Array.from(tbody.querySelectorAll("tr"));
|
|
2237
|
+
const grid = [];
|
|
2238
|
+
rows.forEach((row, rIdx) => {
|
|
2239
|
+
grid[rIdx] = grid[rIdx] || [];
|
|
2240
|
+
let cIdx = 0;
|
|
2241
|
+
const cells = cellsOfRow(row);
|
|
2242
|
+
cells.forEach((cell) => {
|
|
2243
|
+
while (grid[rIdx][cIdx])
|
|
2244
|
+
cIdx += 1;
|
|
2245
|
+
const rowSpan = Math.max(1, cell.rowSpan || 1);
|
|
2246
|
+
const colSpan = Math.max(1, cell.colSpan || 1);
|
|
2247
|
+
for (let r = rIdx; r < rIdx + rowSpan; r += 1) {
|
|
2248
|
+
grid[r] = grid[r] || [];
|
|
2249
|
+
for (let c = cIdx; c < cIdx + colSpan; c += 1) {
|
|
2250
|
+
grid[r][c] = cell;
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
cIdx += colSpan;
|
|
2254
|
+
});
|
|
2255
|
+
});
|
|
2256
|
+
return { rows, grid };
|
|
2257
|
+
};
|
|
2258
|
+
const getCellsInGridRect = (tbody, sr, sc, er, ec) => {
|
|
2259
|
+
const { grid } = getTableGrid(tbody);
|
|
2260
|
+
const seen = new Set();
|
|
2261
|
+
const cells = [];
|
|
2262
|
+
for (let r = sr; r <= er; r += 1) {
|
|
2263
|
+
for (let c = sc; c <= ec; c += 1) {
|
|
2264
|
+
const cell = grid[r]?.[c];
|
|
2265
|
+
if (cell && !seen.has(cell)) {
|
|
2266
|
+
seen.add(cell);
|
|
2267
|
+
cells.push(cell);
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
return cells;
|
|
2272
|
+
};
|
|
1839
2273
|
const getCellPosition = (cell) => {
|
|
1840
2274
|
const row = cell.parentElement;
|
|
1841
2275
|
const tbody = row?.parentElement;
|
|
1842
2276
|
const table = tbody?.parentElement;
|
|
1843
2277
|
if (!row || !tbody || !table)
|
|
1844
2278
|
return null;
|
|
1845
|
-
const rows =
|
|
2279
|
+
const { rows, grid } = getTableGrid(tbody);
|
|
1846
2280
|
const rIdx = rows.indexOf(row);
|
|
1847
|
-
|
|
1848
|
-
|
|
2281
|
+
let cIdx = -1;
|
|
2282
|
+
if (rIdx >= 0) {
|
|
2283
|
+
cIdx = (grid[rIdx] || []).findIndex((candidate) => candidate === cell);
|
|
2284
|
+
}
|
|
1849
2285
|
return { row, tbody, table, rIdx, cIdx };
|
|
1850
2286
|
};
|
|
1851
2287
|
const cellsOfRow = (row) => Array.from(row.children).filter((c) => ["TD", "TH"].includes(c.tagName));
|
|
@@ -1854,42 +2290,27 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1854
2290
|
if (!sel)
|
|
1855
2291
|
return;
|
|
1856
2292
|
const { tbody, sr, sc, er, ec } = sel;
|
|
1857
|
-
const
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
const cell = cells[c];
|
|
1863
|
-
if (!cell)
|
|
1864
|
-
continue;
|
|
1865
|
-
if (cell.__rtePrevBg != null) {
|
|
1866
|
-
cell.style.background = cell.__rtePrevBg;
|
|
1867
|
-
delete cell.__rtePrevBg;
|
|
1868
|
-
}
|
|
1869
|
-
cell.style.outline = "";
|
|
1870
|
-
cell.style.outlineOffset = "";
|
|
2293
|
+
const cells = getCellsInGridRect(tbody, sr, sc, er, ec);
|
|
2294
|
+
cells.forEach((cell) => {
|
|
2295
|
+
if (cell.__rtePrevBg != null) {
|
|
2296
|
+
cell.style.background = cell.__rtePrevBg;
|
|
2297
|
+
delete cell.__rtePrevBg;
|
|
1871
2298
|
}
|
|
1872
|
-
|
|
2299
|
+
cell.style.outline = "";
|
|
2300
|
+
cell.style.outlineOffset = "";
|
|
2301
|
+
});
|
|
1873
2302
|
selectionRef.current = null;
|
|
1874
2303
|
};
|
|
1875
2304
|
const updateSelectionDecor = (tbody, sr, sc, er, ec) => {
|
|
1876
2305
|
clearSelectionDecor();
|
|
1877
2306
|
selectionRef.current = { tbody, sr, sc, er, ec };
|
|
1878
|
-
const
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
continue;
|
|
1886
|
-
cell.__rtePrevBg =
|
|
1887
|
-
cell.style.background || "";
|
|
1888
|
-
cell.style.background = "var(--srte-accent-bg)";
|
|
1889
|
-
cell.style.outline = "2px solid var(--srte-accent)";
|
|
1890
|
-
cell.style.outlineOffset = "-2px";
|
|
1891
|
-
}
|
|
1892
|
-
}
|
|
2307
|
+
const cells = getCellsInGridRect(tbody, sr, sc, er, ec);
|
|
2308
|
+
cells.forEach((cell) => {
|
|
2309
|
+
cell.__rtePrevBg = cell.style.background || "";
|
|
2310
|
+
cell.style.background = "var(--srte-accent-bg)";
|
|
2311
|
+
cell.style.outline = "2px solid var(--srte-accent)";
|
|
2312
|
+
cell.style.outlineOffset = "-2px";
|
|
2313
|
+
});
|
|
1893
2314
|
};
|
|
1894
2315
|
const canMergeSelection = () => {
|
|
1895
2316
|
const sel = selectionRef.current;
|
|
@@ -1897,32 +2318,37 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1897
2318
|
return false;
|
|
1898
2319
|
return sel.sr !== sel.er || sel.sc !== sel.ec;
|
|
1899
2320
|
};
|
|
2321
|
+
const canMergeFromCell = (cell) => Boolean(cell && canMergeSelection() && isCellInsideSelection(cell));
|
|
2322
|
+
const canSplitCell = (cell) => Boolean(cell && (Math.max(1, cell.rowSpan || 1) > 1 || Math.max(1, cell.colSpan || 1) > 1));
|
|
2323
|
+
const isCellInsideSelection = (cell) => {
|
|
2324
|
+
const sel = selectionRef.current;
|
|
2325
|
+
if (!sel)
|
|
2326
|
+
return false;
|
|
2327
|
+
const pos = getCellPosition(cell);
|
|
2328
|
+
if (!pos || pos.tbody !== sel.tbody)
|
|
2329
|
+
return false;
|
|
2330
|
+
return pos.rIdx >= sel.sr && pos.rIdx <= sel.er && pos.cIdx >= sel.sc && pos.cIdx <= sel.ec;
|
|
2331
|
+
};
|
|
2332
|
+
const shouldUseTableSelection = (fallbackCell) => Boolean(selectionRef.current && fallbackCell && isCellInsideSelection(fallbackCell));
|
|
1900
2333
|
const mergeSelection = () => {
|
|
1901
2334
|
const sel = selectionRef.current;
|
|
1902
2335
|
if (!sel)
|
|
1903
2336
|
return;
|
|
1904
2337
|
const { tbody, sr, sc, er, ec } = sel;
|
|
1905
|
-
const
|
|
1906
|
-
const
|
|
1907
|
-
const anchor = cellsOfRow(anchorRow)[sc];
|
|
2338
|
+
const { grid } = getTableGrid(tbody);
|
|
2339
|
+
const anchor = grid[sr]?.[sc];
|
|
1908
2340
|
if (!anchor)
|
|
1909
2341
|
return;
|
|
1910
2342
|
// Collect content and remove other cells
|
|
1911
2343
|
const contents = [];
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
continue;
|
|
1921
|
-
const html = cell.innerHTML.trim();
|
|
1922
|
-
if (html)
|
|
1923
|
-
contents.push(html);
|
|
1924
|
-
}
|
|
1925
|
-
}
|
|
2344
|
+
const cellsToMerge = getCellsInGridRect(tbody, sr, sc, er, ec);
|
|
2345
|
+
cellsToMerge.forEach((cell) => {
|
|
2346
|
+
if (cell === anchor)
|
|
2347
|
+
return;
|
|
2348
|
+
const html = cell.innerHTML.trim();
|
|
2349
|
+
if (html)
|
|
2350
|
+
contents.push(html);
|
|
2351
|
+
});
|
|
1926
2352
|
if (contents.length) {
|
|
1927
2353
|
anchor.innerHTML = (anchor.innerHTML || "") + " " + contents.join(" ");
|
|
1928
2354
|
}
|
|
@@ -1930,18 +2356,10 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1930
2356
|
anchor.colSpan = ec - sc + 1;
|
|
1931
2357
|
anchor.rowSpan = er - sr + 1;
|
|
1932
2358
|
// Remove other cells
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
const cells = cellsOfRow(row);
|
|
1936
|
-
for (let c = ec; c >= sc; c--) {
|
|
1937
|
-
const cell = cells[c];
|
|
1938
|
-
if (!cell)
|
|
1939
|
-
continue;
|
|
1940
|
-
if (r === sr && c === sc)
|
|
1941
|
-
continue;
|
|
2359
|
+
cellsToMerge.forEach((cell) => {
|
|
2360
|
+
if (cell !== anchor)
|
|
1942
2361
|
cell.remove();
|
|
1943
|
-
|
|
1944
|
-
}
|
|
2362
|
+
});
|
|
1945
2363
|
moveCaretToCell(anchor, false);
|
|
1946
2364
|
clearSelectionDecor();
|
|
1947
2365
|
handleInput();
|
|
@@ -1955,7 +2373,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1955
2373
|
const numCols = Array.from(row.children).filter((c) => ["TD", "TH"].includes(c.tagName)).length;
|
|
1956
2374
|
for (let i = 0; i < numCols; i++) {
|
|
1957
2375
|
const td = document.createElement("td");
|
|
1958
|
-
td.style.border = "1px solid
|
|
2376
|
+
td.style.border = "1px solid #d1d5db";
|
|
1959
2377
|
td.style.padding = "6px";
|
|
1960
2378
|
td.style.minWidth = "60px";
|
|
1961
2379
|
td.innerHTML = " ";
|
|
@@ -1985,7 +2403,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
1985
2403
|
for (const r of rows) {
|
|
1986
2404
|
const cells = Array.from(r.children).filter((c) => ["TD", "TH"].includes(c.tagName));
|
|
1987
2405
|
const td = document.createElement("td");
|
|
1988
|
-
td.style.border = "1px solid
|
|
2406
|
+
td.style.border = "1px solid #d1d5db";
|
|
1989
2407
|
td.style.padding = "6px";
|
|
1990
2408
|
td.style.minWidth = "60px";
|
|
1991
2409
|
td.innerHTML = " ";
|
|
@@ -2014,14 +2432,10 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2014
2432
|
table.parentElement?.removeChild(table);
|
|
2015
2433
|
};
|
|
2016
2434
|
const toggleHeaderCell = (cell) => {
|
|
2435
|
+
clearSelectionDecor();
|
|
2017
2436
|
const isTh = cell.tagName === "TH";
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
replacement.style.border =
|
|
2021
|
-
cell.style.border || "1px solid var(--srte-border)";
|
|
2022
|
-
replacement.style.padding = cell.style.padding || "6px";
|
|
2023
|
-
replacement.style.minWidth = cell.style.minWidth || "60px";
|
|
2024
|
-
cell.parentElement?.replaceChild(replacement, cell);
|
|
2437
|
+
replaceTableCellTag(cell, isTh ? "td" : "th");
|
|
2438
|
+
handleInput();
|
|
2025
2439
|
};
|
|
2026
2440
|
const deleteTable = (cell) => {
|
|
2027
2441
|
const pos = getCellPosition(cell);
|
|
@@ -2046,7 +2460,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2046
2460
|
const currentRow = Array.from(tbody.querySelectorAll("tr"))[rIdx];
|
|
2047
2461
|
for (let j = 1; j < cs; j++) {
|
|
2048
2462
|
const td = document.createElement("td");
|
|
2049
|
-
td.style.border = "1px solid
|
|
2463
|
+
td.style.border = "1px solid #d1d5db";
|
|
2050
2464
|
td.style.padding = "6px";
|
|
2051
2465
|
td.style.minWidth = "60px";
|
|
2052
2466
|
td.innerHTML = " ";
|
|
@@ -2059,7 +2473,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2059
2473
|
const row = Array.from(tbody.querySelectorAll("tr"))[rIdx + i];
|
|
2060
2474
|
for (let j = 0; j < cs; j++) {
|
|
2061
2475
|
const td = document.createElement("td");
|
|
2062
|
-
td.style.border = "1px solid
|
|
2476
|
+
td.style.border = "1px solid #d1d5db";
|
|
2063
2477
|
td.style.padding = "6px";
|
|
2064
2478
|
td.style.minWidth = "60px";
|
|
2065
2479
|
td.innerHTML = " ";
|
|
@@ -2074,29 +2488,17 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2074
2488
|
const pos = getCellPosition(cell);
|
|
2075
2489
|
if (!pos)
|
|
2076
2490
|
return;
|
|
2077
|
-
|
|
2078
|
-
const
|
|
2079
|
-
|
|
2080
|
-
return;
|
|
2081
|
-
const cells = cellsOfRow(firstRow);
|
|
2491
|
+
clearSelectionDecor();
|
|
2492
|
+
const { row } = pos;
|
|
2493
|
+
const cells = cellsOfRow(row);
|
|
2082
2494
|
const shouldMakeHeader = cells.some((c) => c.tagName !== "TH");
|
|
2083
2495
|
for (const c of cells) {
|
|
2084
2496
|
const isTh = c.tagName === "TH";
|
|
2085
2497
|
if (shouldMakeHeader && !isTh) {
|
|
2086
|
-
|
|
2087
|
-
th.innerHTML = c.innerHTML || " ";
|
|
2088
|
-
th.style.border = c.style.border || "1px solid var(--srte-border)";
|
|
2089
|
-
th.style.padding = c.style.padding || "6px";
|
|
2090
|
-
th.style.minWidth = c.style.minWidth || "60px";
|
|
2091
|
-
firstRow.replaceChild(th, c);
|
|
2498
|
+
replaceTableCellTag(c, "th");
|
|
2092
2499
|
}
|
|
2093
2500
|
else if (!shouldMakeHeader && isTh) {
|
|
2094
|
-
|
|
2095
|
-
td.innerHTML = c.innerHTML || " ";
|
|
2096
|
-
td.style.border = c.style.border || "1px solid var(--srte-border)";
|
|
2097
|
-
td.style.padding = c.style.padding || "6px";
|
|
2098
|
-
td.style.minWidth = c.style.minWidth || "60px";
|
|
2099
|
-
firstRow.replaceChild(td, c);
|
|
2501
|
+
replaceTableCellTag(c, "td");
|
|
2100
2502
|
}
|
|
2101
2503
|
}
|
|
2102
2504
|
handleInput();
|
|
@@ -2105,37 +2507,35 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2105
2507
|
const pos = getCellPosition(cell);
|
|
2106
2508
|
if (!pos)
|
|
2107
2509
|
return;
|
|
2510
|
+
clearSelectionDecor();
|
|
2108
2511
|
const { tbody, cIdx } = pos;
|
|
2109
|
-
const
|
|
2110
|
-
const
|
|
2111
|
-
|
|
2112
|
-
.
|
|
2512
|
+
const { grid } = getTableGrid(tbody);
|
|
2513
|
+
const seen = new Set();
|
|
2514
|
+
const columnCells = grid
|
|
2515
|
+
.map((row) => row?.[cIdx])
|
|
2516
|
+
.filter((candidate) => {
|
|
2517
|
+
if (!candidate || seen.has(candidate))
|
|
2518
|
+
return false;
|
|
2519
|
+
seen.add(candidate);
|
|
2520
|
+
return true;
|
|
2521
|
+
});
|
|
2113
2522
|
const shouldMakeHeader = columnCells.some((c) => c.tagName !== "TH");
|
|
2114
2523
|
for (const c of columnCells) {
|
|
2115
|
-
|
|
2116
|
-
replacement.innerHTML = c.innerHTML || " ";
|
|
2117
|
-
replacement.style.border = c.style.border || "1px solid var(--srte-border)";
|
|
2118
|
-
replacement.style.padding = c.style.padding || "6px";
|
|
2119
|
-
replacement.style.minWidth = c.style.minWidth || "60px";
|
|
2120
|
-
c.parentElement?.replaceChild(replacement, c);
|
|
2524
|
+
replaceTableCellTag(c, shouldMakeHeader ? "th" : "td");
|
|
2121
2525
|
}
|
|
2122
2526
|
handleInput();
|
|
2123
2527
|
};
|
|
2124
2528
|
const applyBgToSelection = (hex, fallbackCell) => {
|
|
2125
|
-
const sel = selectionRef.current;
|
|
2529
|
+
const sel = shouldUseTableSelection(fallbackCell) ? selectionRef.current : null;
|
|
2126
2530
|
if (sel) {
|
|
2127
|
-
const
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
const cell = cells[c];
|
|
2133
|
-
if (cell)
|
|
2134
|
-
cell.style.background = hex;
|
|
2135
|
-
}
|
|
2136
|
-
}
|
|
2531
|
+
const cells = getCellsInGridRect(sel.tbody, sel.sr, sel.sc, sel.er, sel.ec);
|
|
2532
|
+
clearSelectionDecor();
|
|
2533
|
+
cells.forEach((cell) => {
|
|
2534
|
+
cell.style.background = hex;
|
|
2535
|
+
});
|
|
2137
2536
|
}
|
|
2138
2537
|
else if (fallbackCell) {
|
|
2538
|
+
clearSelectionDecor();
|
|
2139
2539
|
fallbackCell.style.background = hex;
|
|
2140
2540
|
}
|
|
2141
2541
|
};
|
|
@@ -2143,25 +2543,21 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2143
2543
|
const applyToggle = (cell) => {
|
|
2144
2544
|
const cur = cell.style.border;
|
|
2145
2545
|
cell.style.border =
|
|
2146
|
-
cur && cur !== "none" ? "none" : "1px solid #
|
|
2546
|
+
cur && cur !== "none" ? "none" : "1px solid #d1d5db";
|
|
2147
2547
|
};
|
|
2148
|
-
const sel = selectionRef.current;
|
|
2548
|
+
const sel = shouldUseTableSelection(fallbackCell) ? selectionRef.current : null;
|
|
2149
2549
|
if (sel) {
|
|
2150
|
-
|
|
2151
|
-
for (let r = sel.sr; r <= sel.er; r++) {
|
|
2152
|
-
const row = rows[r];
|
|
2153
|
-
const cells = cellsOfRow(row);
|
|
2154
|
-
for (let c = sel.sc; c <= sel.ec; c++) {
|
|
2155
|
-
const cell = cells[c];
|
|
2156
|
-
if (cell)
|
|
2157
|
-
applyToggle(cell);
|
|
2158
|
-
}
|
|
2159
|
-
}
|
|
2550
|
+
getCellsInGridRect(sel.tbody, sel.sr, sel.sc, sel.er, sel.ec).forEach(applyToggle);
|
|
2160
2551
|
}
|
|
2161
2552
|
else if (fallbackCell) {
|
|
2162
2553
|
applyToggle(fallbackCell);
|
|
2163
2554
|
}
|
|
2164
2555
|
};
|
|
2556
|
+
const runTableCellAction = (cell, action) => {
|
|
2557
|
+
action(cell);
|
|
2558
|
+
handleInput();
|
|
2559
|
+
setTableMenu(null);
|
|
2560
|
+
};
|
|
2165
2561
|
// Table column and row resizing functions
|
|
2166
2562
|
const getColumnCells = (table, colIndex) => {
|
|
2167
2563
|
const tbody = table.querySelector('tbody');
|
|
@@ -2282,6 +2678,27 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2282
2678
|
});
|
|
2283
2679
|
});
|
|
2284
2680
|
};
|
|
2681
|
+
const activeButtonStyle = (active = false, extra = {}) => ({
|
|
2682
|
+
height: 32,
|
|
2683
|
+
minWidth: 32,
|
|
2684
|
+
padding: "0 8px",
|
|
2685
|
+
border: active
|
|
2686
|
+
? "2px solid var(--srte-accent)"
|
|
2687
|
+
: "1px solid var(--srte-input-border)",
|
|
2688
|
+
borderRadius: 6,
|
|
2689
|
+
background: active ? "var(--srte-accent-bg)" : "var(--srte-input-bg)",
|
|
2690
|
+
color: "var(--srte-input-text)",
|
|
2691
|
+
boxShadow: active ? "inset 0 0 0 1px var(--srte-accent)" : "none",
|
|
2692
|
+
...extra,
|
|
2693
|
+
});
|
|
2694
|
+
const preserveToolbarMouseDown = (event) => {
|
|
2695
|
+
const target = event.target;
|
|
2696
|
+
const button = target?.closest("button");
|
|
2697
|
+
preserveEditorSelection();
|
|
2698
|
+
if (button && !button.hasAttribute("disabled")) {
|
|
2699
|
+
event.preventDefault();
|
|
2700
|
+
}
|
|
2701
|
+
};
|
|
2285
2702
|
const editorClass = `srte-editor${theme === 'dark' ? ' srte-dark' : ''}${className ? ' ' + className : ''}`;
|
|
2286
2703
|
return (_jsxs("div", { className: editorClass, style: {
|
|
2287
2704
|
border: "1px solid var(--srte-border)",
|
|
@@ -2294,7 +2711,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2294
2711
|
background: "var(--srte-bg)",
|
|
2295
2712
|
color: "var(--srte-text)",
|
|
2296
2713
|
boxSizing: "border-box"
|
|
2297
|
-
}, children: [_jsxs("div", { style: {
|
|
2714
|
+
}, children: [_jsxs("div", { onMouseDown: preserveToolbarMouseDown, style: {
|
|
2298
2715
|
display: "flex",
|
|
2299
2716
|
flexWrap: "wrap",
|
|
2300
2717
|
maxWidth: "100%",
|
|
@@ -2343,7 +2760,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2343
2760
|
} }), _jsx("input", { ref: mdInputRef, type: "file", accept: ".md,.markdown,text/markdown,text/plain", style: { display: "none" }, onChange: (e) => {
|
|
2344
2761
|
importTextFile(e.currentTarget.files, "md");
|
|
2345
2762
|
e.currentTarget.value = "";
|
|
2346
|
-
} }), _jsxs("select", { defaultValue: "p", onChange: (e) => {
|
|
2763
|
+
} }), _jsxs("select", { defaultValue: "p", onMouseDown: preserveEditorSelection, onChange: (e) => {
|
|
2347
2764
|
const val = e.target.value;
|
|
2348
2765
|
if (val === "p")
|
|
2349
2766
|
applyFormatBlock("<p>");
|
|
@@ -2360,42 +2777,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2360
2777
|
borderRadius: 6,
|
|
2361
2778
|
background: "var(--srte-input-bg)",
|
|
2362
2779
|
color: "var(--srte-input-text)",
|
|
2363
|
-
}, children: [_jsx("option", { value: "p", children: "Paragraph" }), _jsx("option", { value: "h1", children: "Heading 1" }), _jsx("option", { value: "h2", children: "Heading 2" }), _jsx("option", { value: "h3", children: "Heading 3" })] }), _jsx("button", { title: "Bold", onClick: () => exec("bold"), style: {
|
|
2364
|
-
height: 32,
|
|
2365
|
-
minWidth: 32,
|
|
2366
|
-
padding: "0 8px",
|
|
2367
|
-
border: "1px solid var(--srte-input-border)",
|
|
2368
|
-
borderRadius: 6,
|
|
2369
|
-
background: "var(--srte-input-bg)",
|
|
2370
|
-
color: "var(--srte-input-text)",
|
|
2371
|
-
}, children: _jsx("span", { style: { fontWeight: 700 }, children: "B" }) }), _jsx("button", { title: "Italic", onClick: () => exec("italic"), style: {
|
|
2372
|
-
height: 32,
|
|
2373
|
-
minWidth: 32,
|
|
2374
|
-
padding: "0 8px",
|
|
2375
|
-
border: "1px solid var(--srte-input-border)",
|
|
2376
|
-
borderRadius: 6,
|
|
2377
|
-
background: "var(--srte-input-bg)",
|
|
2378
|
-
fontStyle: "italic",
|
|
2379
|
-
color: "var(--srte-input-text)",
|
|
2380
|
-
}, children: "I" }), _jsx("button", { title: "Underline", onClick: () => exec("underline"), style: {
|
|
2381
|
-
height: 32,
|
|
2382
|
-
minWidth: 32,
|
|
2383
|
-
padding: "0 8px",
|
|
2384
|
-
border: "1px solid var(--srte-input-border)",
|
|
2385
|
-
borderRadius: 6,
|
|
2386
|
-
background: "var(--srte-input-bg)",
|
|
2387
|
-
textDecoration: "underline",
|
|
2388
|
-
color: "var(--srte-input-text)",
|
|
2389
|
-
}, children: "U" }), _jsx("button", { title: "Strikethrough", onClick: () => exec("strikeThrough"), style: {
|
|
2390
|
-
height: 32,
|
|
2391
|
-
minWidth: 32,
|
|
2392
|
-
padding: "0 8px",
|
|
2393
|
-
border: "1px solid var(--srte-input-border)",
|
|
2394
|
-
borderRadius: 6,
|
|
2395
|
-
background: "var(--srte-input-bg)",
|
|
2396
|
-
textDecoration: "line-through",
|
|
2397
|
-
color: "var(--srte-input-text)",
|
|
2398
|
-
}, children: "S" }), _jsxs("select", { value: currentFontSize, onMouseDown: () => {
|
|
2780
|
+
}, children: [_jsx("option", { value: "p", children: "Paragraph" }), _jsx("option", { value: "h1", children: "Heading 1" }), _jsx("option", { value: "h2", children: "Heading 2" }), _jsx("option", { value: "h3", children: "Heading 3" })] }), _jsx("button", { title: "Bold", onClick: () => exec("bold"), "aria-pressed": activeState.bold, style: activeButtonStyle(activeState.bold), children: _jsx("span", { style: { fontWeight: 700 }, children: "B" }) }), _jsx("button", { title: "Italic", onClick: () => exec("italic"), "aria-pressed": activeState.italic, style: activeButtonStyle(activeState.italic, { fontStyle: "italic" }), children: "I" }), _jsx("button", { title: "Underline", onClick: () => exec("underline"), "aria-pressed": activeState.underline, style: activeButtonStyle(activeState.underline, { textDecoration: "underline" }), children: "U" }), _jsx("button", { title: "Strikethrough", onClick: () => exec("strikeThrough"), "aria-pressed": activeState.strikeThrough, style: activeButtonStyle(activeState.strikeThrough, { textDecoration: "line-through" }), children: "S" }), _jsxs("select", { value: currentFontSize, onMouseDown: () => {
|
|
2399
2781
|
// Save selection before dropdown interaction
|
|
2400
2782
|
const sel = window.getSelection();
|
|
2401
2783
|
if (sel && sel.rangeCount > 0) {
|
|
@@ -2452,45 +2834,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2452
2834
|
borderRadius: 6,
|
|
2453
2835
|
background: "var(--srte-input-bg)",
|
|
2454
2836
|
color: "var(--srte-input-text)",
|
|
2455
|
-
}, children: _jsx("span", { style: { fontWeight: 700, padding: "1px 4px", background: "var(--srte-accent-bg)", borderRadius: 3 }, children: "A" }) }), _jsxs("button", { title: "Subscript", onClick: () => exec("subscript"), style: {
|
|
2456
|
-
height: 32,
|
|
2457
|
-
minWidth: 32,
|
|
2458
|
-
padding: "0 8px",
|
|
2459
|
-
border: "1px solid var(--srte-input-border)",
|
|
2460
|
-
borderRadius: 6,
|
|
2461
|
-
background: "var(--srte-input-bg)",
|
|
2462
|
-
color: "var(--srte-input-text)",
|
|
2463
|
-
}, children: ["X", _jsx("sub", { children: "2" })] }), _jsxs("button", { title: "Superscript", onClick: () => exec("superscript"), style: {
|
|
2464
|
-
height: 32,
|
|
2465
|
-
minWidth: 32,
|
|
2466
|
-
padding: "0 8px",
|
|
2467
|
-
border: "1px solid var(--srte-input-border)",
|
|
2468
|
-
borderRadius: 6,
|
|
2469
|
-
background: "var(--srte-input-bg)",
|
|
2470
|
-
color: "var(--srte-input-text)",
|
|
2471
|
-
}, children: ["X", _jsx("sup", { children: "2" })] }), _jsx("button", { title: "Bulleted list", onClick: () => exec("insertUnorderedList"), style: {
|
|
2472
|
-
height: 32,
|
|
2473
|
-
padding: "0 10px",
|
|
2474
|
-
border: "1px solid var(--srte-input-border)",
|
|
2475
|
-
borderRadius: 6,
|
|
2476
|
-
background: "var(--srte-input-bg)",
|
|
2477
|
-
color: "var(--srte-input-text)",
|
|
2478
|
-
}, children: "\u2022 List" }), _jsx("button", { title: "Numbered list", onClick: () => exec("insertOrderedList"), style: {
|
|
2479
|
-
height: 32,
|
|
2480
|
-
padding: "0 10px",
|
|
2481
|
-
border: "1px solid var(--srte-input-border)",
|
|
2482
|
-
borderRadius: 6,
|
|
2483
|
-
background: "var(--srte-input-bg)",
|
|
2484
|
-
color: "var(--srte-input-text)",
|
|
2485
|
-
}, children: "1. List" }), _jsx("button", { title: "Blockquote", onClick: toggleBlockquote, style: {
|
|
2486
|
-
height: 32,
|
|
2487
|
-
minWidth: 32,
|
|
2488
|
-
padding: "0 8px",
|
|
2489
|
-
border: "1px solid var(--srte-input-border)",
|
|
2490
|
-
borderRadius: 6,
|
|
2491
|
-
background: "var(--srte-input-bg)",
|
|
2492
|
-
color: "var(--srte-input-text)",
|
|
2493
|
-
}, children: "\u275D" }), _jsx("button", { title: "Special characters", onClick: () => setShowSpecialChars(true), style: {
|
|
2837
|
+
}, children: _jsx("span", { style: { fontWeight: 700, padding: "1px 4px", background: "var(--srte-accent-bg)", borderRadius: 3 }, children: "A" }) }), _jsxs("button", { title: "Subscript", onClick: () => exec("subscript"), "aria-pressed": activeState.subscript, style: activeButtonStyle(activeState.subscript), children: ["X", _jsx("sub", { children: "2" })] }), _jsxs("button", { title: "Superscript", onClick: () => exec("superscript"), "aria-pressed": activeState.superscript, style: activeButtonStyle(activeState.superscript), children: ["X", _jsx("sup", { children: "2" })] }), _jsx("button", { title: "Bulleted list", onClick: () => exec("insertUnorderedList"), "aria-pressed": activeState.unorderedList, style: activeButtonStyle(activeState.unorderedList, { padding: "0 10px" }), children: "\u2022 List" }), _jsx("button", { title: "Numbered list", onClick: () => exec("insertOrderedList"), "aria-pressed": activeState.orderedList, style: activeButtonStyle(activeState.orderedList, { padding: "0 10px" }), children: "1. List" }), _jsx("button", { title: "Blockquote", onClick: toggleBlockquote, "aria-pressed": activeState.blockquote, style: activeButtonStyle(activeState.blockquote), children: "\u275D" }), _jsx("button", { title: "Special characters", onClick: () => setShowSpecialChars(true), style: {
|
|
2494
2838
|
height: 32,
|
|
2495
2839
|
minWidth: 32,
|
|
2496
2840
|
padding: "0 8px",
|
|
@@ -2498,16 +2842,10 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2498
2842
|
borderRadius: 6,
|
|
2499
2843
|
background: "var(--srte-input-bg)",
|
|
2500
2844
|
color: "var(--srte-input-text)",
|
|
2501
|
-
}, children: "\u03A9" }), _jsx("button", { title: "Code block", onClick: () => exec("formatBlock", "<pre>"), style: {
|
|
2502
|
-
height: 32,
|
|
2845
|
+
}, children: "\u03A9" }), _jsx("button", { title: "Code block", onClick: () => exec("formatBlock", "<pre>"), "aria-pressed": activeState.codeBlock, style: activeButtonStyle(activeState.codeBlock, {
|
|
2503
2846
|
minWidth: 36,
|
|
2504
|
-
padding: "0 8px",
|
|
2505
|
-
border: "1px solid var(--srte-input-border)",
|
|
2506
|
-
borderRadius: 6,
|
|
2507
|
-
background: "var(--srte-input-bg)",
|
|
2508
2847
|
fontFamily: "ui-monospace, SFMono-Regular, Menlo",
|
|
2509
|
-
|
|
2510
|
-
}, children: "< />" }), formula && (_jsx("button", { title: "Insert formula", onClick: () => setShowFormulaDialog(true), style: {
|
|
2848
|
+
}), children: "< />" }), formula && (_jsx("button", { title: "Insert formula", onClick: () => setShowFormulaDialog(true), style: {
|
|
2511
2849
|
height: 32,
|
|
2512
2850
|
minWidth: 32,
|
|
2513
2851
|
padding: "0 8px",
|
|
@@ -2685,17 +3023,19 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2685
3023
|
position: "fixed",
|
|
2686
3024
|
inset: 0,
|
|
2687
3025
|
background: "var(--srte-modal-backdrop)",
|
|
3026
|
+
backdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
3027
|
+
WebkitBackdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
2688
3028
|
display: "flex",
|
|
2689
3029
|
alignItems: "center",
|
|
2690
3030
|
justifyContent: "center",
|
|
2691
|
-
zIndex:
|
|
3031
|
+
zIndex: 90,
|
|
2692
3032
|
}, onClick: () => setShowTableDialog(false), children: _jsxs("div", { style: {
|
|
2693
3033
|
background: "var(--srte-modal-bg)",
|
|
2694
3034
|
color: "var(--srte-modal-text)",
|
|
2695
3035
|
padding: 16,
|
|
2696
3036
|
borderRadius: 8,
|
|
2697
3037
|
minWidth: 280,
|
|
2698
|
-
}, onClick: (e) => e.stopPropagation(), children: [_jsx("div", { style: { fontWeight: 600, marginBottom: 8 }, children: "Insert table" }), _jsxs("div", { style: { display: "flex", gap: 12, alignItems: "center" }, children: [_jsx("div", { style: {
|
|
3038
|
+
}, onMouseDown: (e) => e.stopPropagation(), onClick: (e) => e.stopPropagation(), children: [_jsx("div", { style: { fontWeight: 600, marginBottom: 8 }, children: "Insert table" }), _jsxs("div", { style: { display: "flex", gap: 12, alignItems: "center" }, children: [_jsx("div", { style: {
|
|
2699
3039
|
display: "grid",
|
|
2700
3040
|
gridTemplateColumns: "repeat(10, 18px)",
|
|
2701
3041
|
gap: 2,
|
|
@@ -2729,6 +3069,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2729
3069
|
position: "fixed",
|
|
2730
3070
|
inset: 0,
|
|
2731
3071
|
background: "var(--srte-modal-backdrop)",
|
|
3072
|
+
backdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
3073
|
+
WebkitBackdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
2732
3074
|
display: "flex",
|
|
2733
3075
|
alignItems: "center",
|
|
2734
3076
|
justifyContent: "center",
|
|
@@ -2783,6 +3125,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2783
3125
|
position: "fixed",
|
|
2784
3126
|
inset: 0,
|
|
2785
3127
|
background: "var(--srte-modal-backdrop)",
|
|
3128
|
+
backdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
3129
|
+
WebkitBackdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
2786
3130
|
display: "flex",
|
|
2787
3131
|
alignItems: "center",
|
|
2788
3132
|
justifyContent: "center",
|
|
@@ -2849,6 +3193,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2849
3193
|
position: "fixed",
|
|
2850
3194
|
inset: 0,
|
|
2851
3195
|
background: "var(--srte-modal-backdrop)",
|
|
3196
|
+
backdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
3197
|
+
WebkitBackdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
2852
3198
|
display: "flex",
|
|
2853
3199
|
alignItems: "center",
|
|
2854
3200
|
justifyContent: "center",
|
|
@@ -2893,6 +3239,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2893
3239
|
position: "fixed",
|
|
2894
3240
|
inset: 0,
|
|
2895
3241
|
background: "var(--srte-modal-backdrop)",
|
|
3242
|
+
backdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
3243
|
+
WebkitBackdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
2896
3244
|
display: "flex",
|
|
2897
3245
|
alignItems: "center",
|
|
2898
3246
|
justifyContent: "center",
|
|
@@ -2990,7 +3338,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2990
3338
|
borderRadius: 4,
|
|
2991
3339
|
background: "var(--srte-input-bg)",
|
|
2992
3340
|
color: "var(--srte-modal-text)",
|
|
2993
|
-
}, title: sym, children: sym }, i)))] })] }) })),
|
|
3341
|
+
}, title: sym, children: sym }, i)))] })] }) })), _jsxs("div", { ref: editorScrollRef, style: {
|
|
2994
3342
|
width: "100%",
|
|
2995
3343
|
maxWidth: "100%",
|
|
2996
3344
|
flex: "1 1 auto",
|
|
@@ -3003,374 +3351,376 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3003
3351
|
boxSizing: "border-box",
|
|
3004
3352
|
position: "relative",
|
|
3005
3353
|
scrollPaddingBottom: 24,
|
|
3006
|
-
}, children: _jsx("div", { ref: editableRef, contentEditable: !readOnly, suppressContentEditableWarning: true, onInput: handleInput, onCompositionStart: () => (isComposingRef.current = true), onCompositionEnd: () => {
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3354
|
+
}, children: [_jsx("div", { ref: editableRef, contentEditable: !readOnly, suppressContentEditableWarning: true, onInput: handleInput, onKeyUp: updateActiveState, onMouseUp: updateActiveState, onCompositionStart: () => (isComposingRef.current = true), onCompositionEnd: () => {
|
|
3355
|
+
isComposingRef.current = false;
|
|
3356
|
+
handleInput();
|
|
3357
|
+
}, onPaste: (e) => {
|
|
3358
|
+
const items = e.clipboardData?.files;
|
|
3359
|
+
if (media && items && items.length) {
|
|
3360
|
+
const hasImage = Array.from(items).some((f) => f.type.startsWith("image/"));
|
|
3361
|
+
if (hasImage) {
|
|
3362
|
+
e.preventDefault();
|
|
3363
|
+
handleLocalImageFiles(items);
|
|
3364
|
+
return;
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
const html = e.clipboardData?.getData("text/html");
|
|
3368
|
+
if (html) {
|
|
3014
3369
|
e.preventDefault();
|
|
3015
|
-
|
|
3016
|
-
return;
|
|
3370
|
+
insertCleanHtml(cleanPastedHtml(html));
|
|
3017
3371
|
}
|
|
3018
|
-
}
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
}
|
|
3024
|
-
}, onDragOver: (e) => {
|
|
3025
|
-
// Allow dragging images within editor and file drops
|
|
3026
|
-
if (draggedImageRef.current ||
|
|
3027
|
-
e.dataTransfer?.types?.includes("Files")) {
|
|
3028
|
-
e.preventDefault();
|
|
3029
|
-
}
|
|
3030
|
-
}, onDrop: (e) => {
|
|
3031
|
-
// Move existing dragged image inside editor
|
|
3032
|
-
if (draggedImageRef.current) {
|
|
3033
|
-
e.preventDefault();
|
|
3034
|
-
const x = e.clientX;
|
|
3035
|
-
const y = e.clientY;
|
|
3036
|
-
let range = null;
|
|
3037
|
-
// @ts-ignore
|
|
3038
|
-
if (document.caretRangeFromPoint) {
|
|
3039
|
-
// @ts-ignore
|
|
3040
|
-
range = document.caretRangeFromPoint(x, y);
|
|
3372
|
+
}, onDragOver: (e) => {
|
|
3373
|
+
// Allow dragging images within editor and file drops
|
|
3374
|
+
if (draggedImageRef.current ||
|
|
3375
|
+
e.dataTransfer?.types?.includes("Files")) {
|
|
3376
|
+
e.preventDefault();
|
|
3041
3377
|
}
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3378
|
+
}, onDrop: (e) => {
|
|
3379
|
+
// Move existing dragged image inside editor
|
|
3380
|
+
if (draggedImageRef.current) {
|
|
3381
|
+
e.preventDefault();
|
|
3382
|
+
const x = e.clientX;
|
|
3383
|
+
const y = e.clientY;
|
|
3384
|
+
let range = null;
|
|
3385
|
+
// @ts-ignore
|
|
3386
|
+
if (document.caretRangeFromPoint) {
|
|
3387
|
+
// @ts-ignore
|
|
3388
|
+
range = document.caretRangeFromPoint(x, y);
|
|
3047
3389
|
}
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
editableRef.current?.contains(range.commonAncestorContainer)) {
|
|
3054
|
-
// Avoid inserting inside the image itself
|
|
3055
|
-
if (range.startContainer === img || range.endContainer === img)
|
|
3056
|
-
return;
|
|
3057
|
-
// If dropping inside a link, insert right after the link element
|
|
3058
|
-
let container = range.commonAncestorContainer;
|
|
3059
|
-
let linkAncestor = null;
|
|
3060
|
-
let el = container;
|
|
3061
|
-
while (el && el !== editableRef.current) {
|
|
3062
|
-
if (el.tagName === "A") {
|
|
3063
|
-
linkAncestor = el;
|
|
3064
|
-
break;
|
|
3390
|
+
else if (document.caretPositionFromPoint) {
|
|
3391
|
+
const pos = document.caretPositionFromPoint(x, y);
|
|
3392
|
+
if (pos) {
|
|
3393
|
+
range = document.createRange();
|
|
3394
|
+
range.setStart(pos.offsetNode, pos.offset);
|
|
3065
3395
|
}
|
|
3066
|
-
el = el.parentElement;
|
|
3067
|
-
}
|
|
3068
|
-
if (linkAncestor) {
|
|
3069
|
-
linkAncestor.parentElement?.insertBefore(img, linkAncestor.nextSibling);
|
|
3070
3396
|
}
|
|
3071
|
-
|
|
3072
|
-
|
|
3397
|
+
const img = draggedImageRef.current;
|
|
3398
|
+
draggedImageRef.current = null;
|
|
3399
|
+
if (range &&
|
|
3400
|
+
img &&
|
|
3401
|
+
editableRef.current?.contains(range.commonAncestorContainer)) {
|
|
3402
|
+
// Avoid inserting inside the image itself
|
|
3403
|
+
if (range.startContainer === img || range.endContainer === img)
|
|
3404
|
+
return;
|
|
3405
|
+
// If dropping inside a link, insert right after the link element
|
|
3406
|
+
let container = range.commonAncestorContainer;
|
|
3407
|
+
let linkAncestor = null;
|
|
3408
|
+
let el = container;
|
|
3409
|
+
while (el && el !== editableRef.current) {
|
|
3410
|
+
if (el.tagName === "A") {
|
|
3411
|
+
linkAncestor = el;
|
|
3412
|
+
break;
|
|
3413
|
+
}
|
|
3414
|
+
el = el.parentElement;
|
|
3415
|
+
}
|
|
3416
|
+
if (linkAncestor) {
|
|
3417
|
+
linkAncestor.parentElement?.insertBefore(img, linkAncestor.nextSibling);
|
|
3418
|
+
}
|
|
3419
|
+
else {
|
|
3420
|
+
range.insertNode(img);
|
|
3421
|
+
}
|
|
3422
|
+
const r = document.createRange();
|
|
3423
|
+
r.setStartAfter(img);
|
|
3424
|
+
r.collapse(true);
|
|
3425
|
+
safeSelectRange(r);
|
|
3426
|
+
setSelectedImage(img);
|
|
3427
|
+
scheduleImageOverlay();
|
|
3428
|
+
handleInput();
|
|
3073
3429
|
}
|
|
3074
|
-
|
|
3075
|
-
r.setStartAfter(img);
|
|
3076
|
-
r.collapse(true);
|
|
3077
|
-
safeSelectRange(r);
|
|
3078
|
-
setSelectedImage(img);
|
|
3079
|
-
scheduleImageOverlay();
|
|
3080
|
-
handleInput();
|
|
3430
|
+
return;
|
|
3081
3431
|
}
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
const y = e.clientY;
|
|
3089
|
-
let range = null;
|
|
3090
|
-
// @ts-ignore
|
|
3091
|
-
if (document.caretRangeFromPoint) {
|
|
3432
|
+
if (media && e.dataTransfer?.files?.length) {
|
|
3433
|
+
e.preventDefault();
|
|
3434
|
+
// Try to move caret to drop point
|
|
3435
|
+
const x = e.clientX;
|
|
3436
|
+
const y = e.clientY;
|
|
3437
|
+
let range = null;
|
|
3092
3438
|
// @ts-ignore
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
const pos = document.caretPositionFromPoint(x, y);
|
|
3097
|
-
if (pos) {
|
|
3098
|
-
range = document.createRange();
|
|
3099
|
-
range.setStart(pos.offsetNode, pos.offset);
|
|
3439
|
+
if (document.caretRangeFromPoint) {
|
|
3440
|
+
// @ts-ignore
|
|
3441
|
+
range = document.caretRangeFromPoint(x, y);
|
|
3100
3442
|
}
|
|
3443
|
+
else if (document.caretPositionFromPoint) {
|
|
3444
|
+
const pos = document.caretPositionFromPoint(x, y);
|
|
3445
|
+
if (pos) {
|
|
3446
|
+
range = document.createRange();
|
|
3447
|
+
range.setStart(pos.offsetNode, pos.offset);
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
if (range) {
|
|
3451
|
+
const sel = window.getSelection();
|
|
3452
|
+
sel?.removeAllRanges();
|
|
3453
|
+
sel?.addRange(range);
|
|
3454
|
+
}
|
|
3455
|
+
handleLocalImageFiles(e.dataTransfer.files);
|
|
3101
3456
|
}
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3457
|
+
}, onClick: (e) => {
|
|
3458
|
+
const t = e.target;
|
|
3459
|
+
if (t && t.tagName === "IMG") {
|
|
3460
|
+
setSelectedImage(t);
|
|
3461
|
+
scheduleImageOverlay();
|
|
3106
3462
|
}
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
const dt = e.dataTransfer;
|
|
3128
|
-
if (dt && typeof dt.setDragImage === "function") {
|
|
3129
|
-
const ghost = new Image();
|
|
3130
|
-
ghost.src = t.src;
|
|
3131
|
-
ghost.width = Math.min(120, t.width);
|
|
3132
|
-
ghost.height = Math.min(120, t.height);
|
|
3133
|
-
dt.setDragImage(ghost, 10, 10);
|
|
3463
|
+
else {
|
|
3464
|
+
setSelectedImage(null);
|
|
3465
|
+
setImageOverlay(null);
|
|
3466
|
+
}
|
|
3467
|
+
}, onDragStart: (e) => {
|
|
3468
|
+
const t = e.target;
|
|
3469
|
+
if (t && t.tagName === "IMG") {
|
|
3470
|
+
draggedImageRef.current = t;
|
|
3471
|
+
try {
|
|
3472
|
+
e.dataTransfer?.setData("text/plain", "moving-image");
|
|
3473
|
+
e.dataTransfer.effectAllowed = "move";
|
|
3474
|
+
// Provide a subtle drag image
|
|
3475
|
+
const dt = e.dataTransfer;
|
|
3476
|
+
if (dt && typeof dt.setDragImage === "function") {
|
|
3477
|
+
const ghost = new Image();
|
|
3478
|
+
ghost.src = t.src;
|
|
3479
|
+
ghost.width = Math.min(120, t.width);
|
|
3480
|
+
ghost.height = Math.min(120, t.height);
|
|
3481
|
+
dt.setDragImage(ghost, 10, 10);
|
|
3482
|
+
}
|
|
3134
3483
|
}
|
|
3484
|
+
catch { }
|
|
3135
3485
|
}
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3486
|
+
else {
|
|
3487
|
+
draggedImageRef.current = null;
|
|
3488
|
+
}
|
|
3489
|
+
}, onDragEnd: () => {
|
|
3139
3490
|
draggedImageRef.current = null;
|
|
3140
|
-
}
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
const el = e.currentTarget;
|
|
3156
|
-
if (!el.innerHTML || el.innerHTML === "<br>") {
|
|
3157
|
-
el.innerHTML = "<p><br></p>";
|
|
3158
|
-
}
|
|
3159
|
-
}, onKeyDown: (e) => {
|
|
3160
|
-
if (formula &&
|
|
3161
|
-
(e.metaKey || e.ctrlKey) &&
|
|
3162
|
-
String(e.key).toLowerCase() === "m") {
|
|
3163
|
-
e.preventDefault();
|
|
3164
|
-
setShowFormulaDialog(true);
|
|
3165
|
-
return;
|
|
3166
|
-
}
|
|
3167
|
-
// Keep Tab for indentation in lists; otherwise insert 2 spaces
|
|
3168
|
-
if (e.key === "Tab") {
|
|
3169
|
-
e.preventDefault();
|
|
3170
|
-
if (document.queryCommandState("insertUnorderedList") ||
|
|
3171
|
-
document.queryCommandState("insertOrderedList")) {
|
|
3172
|
-
exec(e.shiftKey ? "outdent" : "indent");
|
|
3491
|
+
}, style: {
|
|
3492
|
+
minHeight: typeof minHeight === "number" ? `${minHeight}px` : minHeight,
|
|
3493
|
+
maxWidth: "100%",
|
|
3494
|
+
overflowX: "visible",
|
|
3495
|
+
padding: "16px",
|
|
3496
|
+
paddingBottom: "32px",
|
|
3497
|
+
outline: "none",
|
|
3498
|
+
lineHeight: 1.6,
|
|
3499
|
+
boxSizing: "border-box",
|
|
3500
|
+
fontFamily: defaultFont || "inherit",
|
|
3501
|
+
}, "data-placeholder": placeholder, onFocus: (e) => {
|
|
3502
|
+
// Ensure the editor has at least one paragraph to type into
|
|
3503
|
+
const el = e.currentTarget;
|
|
3504
|
+
if (!el.innerHTML || el.innerHTML === "<br>") {
|
|
3505
|
+
el.innerHTML = "<p><br></p>";
|
|
3173
3506
|
}
|
|
3174
|
-
|
|
3175
|
-
|
|
3507
|
+
updateActiveState();
|
|
3508
|
+
}, onKeyDown: (e) => {
|
|
3509
|
+
if (formula &&
|
|
3510
|
+
(e.metaKey || e.ctrlKey) &&
|
|
3511
|
+
String(e.key).toLowerCase() === "m") {
|
|
3512
|
+
e.preventDefault();
|
|
3513
|
+
setShowFormulaDialog(true);
|
|
3514
|
+
return;
|
|
3176
3515
|
}
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
cell &&
|
|
3184
|
-
cell.parentElement &&
|
|
3185
|
-
cell.parentElement.parentElement) {
|
|
3186
|
-
const row = cell.parentElement;
|
|
3187
|
-
const tbody = row.parentElement;
|
|
3188
|
-
const cells = Array.from(row.children).filter((c) => c.tagName === "TD" ||
|
|
3189
|
-
c.tagName === "TH");
|
|
3190
|
-
const rows = Array.from(tbody.children);
|
|
3191
|
-
const rIdx = rows.indexOf(row);
|
|
3192
|
-
const cIdx = cells.indexOf(cell);
|
|
3193
|
-
const atStart = (sel?.anchorOffset || 0) === 0;
|
|
3194
|
-
const cellTextLen = (cell.textContent || "").length;
|
|
3195
|
-
const atEnd = (sel?.anchorOffset || 0) >= cellTextLen;
|
|
3196
|
-
let target = null;
|
|
3197
|
-
if (e.key === "ArrowLeft" && atStart && cIdx > 0) {
|
|
3198
|
-
target = row.children[cIdx - 1];
|
|
3199
|
-
}
|
|
3200
|
-
else if (e.key === "ArrowRight" &&
|
|
3201
|
-
atEnd &&
|
|
3202
|
-
cIdx < row.children.length - 1) {
|
|
3203
|
-
target = row.children[cIdx + 1];
|
|
3204
|
-
}
|
|
3205
|
-
else if (e.key === "ArrowUp" && rIdx > 0 && atStart) {
|
|
3206
|
-
target = rows[rIdx - 1].children[cIdx];
|
|
3516
|
+
// Keep Tab for indentation in lists; otherwise insert 2 spaces
|
|
3517
|
+
if (e.key === "Tab") {
|
|
3518
|
+
e.preventDefault();
|
|
3519
|
+
if (document.queryCommandState("insertUnorderedList") ||
|
|
3520
|
+
document.queryCommandState("insertOrderedList")) {
|
|
3521
|
+
exec(e.shiftKey ? "outdent" : "indent");
|
|
3207
3522
|
}
|
|
3208
|
-
else
|
|
3209
|
-
|
|
3210
|
-
atEnd) {
|
|
3211
|
-
target = rows[rIdx + 1].children[cIdx];
|
|
3523
|
+
else {
|
|
3524
|
+
document.execCommand("insertText", false, " ");
|
|
3212
3525
|
}
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3526
|
+
}
|
|
3527
|
+
// Table navigation with arrows inside cells
|
|
3528
|
+
if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key)) {
|
|
3529
|
+
const sel = window.getSelection();
|
|
3530
|
+
const cell = getClosestCell(sel?.anchorNode || null);
|
|
3531
|
+
if (table &&
|
|
3532
|
+
cell &&
|
|
3533
|
+
cell.parentElement &&
|
|
3534
|
+
cell.parentElement.parentElement) {
|
|
3535
|
+
const row = cell.parentElement;
|
|
3536
|
+
const tbody = row.parentElement;
|
|
3537
|
+
const cells = Array.from(row.children).filter((c) => c.tagName === "TD" ||
|
|
3538
|
+
c.tagName === "TH");
|
|
3539
|
+
const rows = Array.from(tbody.children);
|
|
3540
|
+
const rIdx = rows.indexOf(row);
|
|
3541
|
+
const cIdx = cells.indexOf(cell);
|
|
3542
|
+
const atStart = (sel?.anchorOffset || 0) === 0;
|
|
3543
|
+
const cellTextLen = (cell.textContent || "").length;
|
|
3544
|
+
const atEnd = (sel?.anchorOffset || 0) >= cellTextLen;
|
|
3545
|
+
let target = null;
|
|
3546
|
+
if (e.key === "ArrowLeft" && atStart && cIdx > 0) {
|
|
3547
|
+
target = row.children[cIdx - 1];
|
|
3548
|
+
}
|
|
3549
|
+
else if (e.key === "ArrowRight" &&
|
|
3550
|
+
atEnd &&
|
|
3551
|
+
cIdx < row.children.length - 1) {
|
|
3552
|
+
target = row.children[cIdx + 1];
|
|
3553
|
+
}
|
|
3554
|
+
else if (e.key === "ArrowUp" && rIdx > 0 && atStart) {
|
|
3555
|
+
target = rows[rIdx - 1].children[cIdx];
|
|
3556
|
+
}
|
|
3557
|
+
else if (e.key === "ArrowDown" &&
|
|
3558
|
+
rIdx < rows.length - 1 &&
|
|
3559
|
+
atEnd) {
|
|
3560
|
+
target = rows[rIdx + 1].children[cIdx];
|
|
3561
|
+
}
|
|
3562
|
+
if (target) {
|
|
3563
|
+
e.preventDefault();
|
|
3564
|
+
moveCaretToCell(target, e.key === "ArrowRight" || e.key === "ArrowDown");
|
|
3565
|
+
}
|
|
3216
3566
|
}
|
|
3217
3567
|
}
|
|
3218
|
-
}
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
clearSelectionDecor();
|
|
3223
|
-
return;
|
|
3224
|
-
}
|
|
3225
|
-
const pos = getCellPosition(cell);
|
|
3226
|
-
if (!pos)
|
|
3227
|
-
return;
|
|
3228
|
-
selectingRef.current = { tbody: pos.tbody, start: cell };
|
|
3229
|
-
const onMove = (ev) => {
|
|
3230
|
-
const under = document.elementFromPoint(ev.clientX, ev.clientY);
|
|
3231
|
-
const overCell = getClosestCell(under);
|
|
3232
|
-
const startInfo = selectingRef.current;
|
|
3233
|
-
if (!overCell || !startInfo)
|
|
3568
|
+
}, onMouseDown: (e) => {
|
|
3569
|
+
const cell = getClosestCell(e.target);
|
|
3570
|
+
if (!cell) {
|
|
3571
|
+
clearSelectionDecor();
|
|
3234
3572
|
return;
|
|
3235
|
-
|
|
3236
|
-
const
|
|
3237
|
-
if (!
|
|
3238
|
-
return;
|
|
3239
|
-
const sr = Math.min(a.rIdx, b.rIdx);
|
|
3240
|
-
const sc = Math.min(a.cIdx, b.cIdx);
|
|
3241
|
-
const er = Math.max(a.rIdx, b.rIdx);
|
|
3242
|
-
const ec = Math.max(a.cIdx, b.cIdx);
|
|
3243
|
-
updateSelectionDecor(a.tbody, sr, sc, er, ec);
|
|
3244
|
-
};
|
|
3245
|
-
const onUp = () => {
|
|
3246
|
-
window.removeEventListener("mousemove", onMove);
|
|
3247
|
-
window.removeEventListener("mouseup", onUp);
|
|
3248
|
-
selectingRef.current = null;
|
|
3249
|
-
};
|
|
3250
|
-
window.addEventListener("mousemove", onMove);
|
|
3251
|
-
window.addEventListener("mouseup", onUp);
|
|
3252
|
-
}, onContextMenu: (e) => {
|
|
3253
|
-
const target = e.target;
|
|
3254
|
-
if (target && target.tagName === "IMG") {
|
|
3255
|
-
e.preventDefault();
|
|
3256
|
-
const vw = window.innerWidth;
|
|
3257
|
-
const vh = window.innerHeight;
|
|
3258
|
-
const menuW = 220;
|
|
3259
|
-
const menuH = 200;
|
|
3260
|
-
const x = Math.max(8, Math.min(e.clientX, vw - menuW - 8));
|
|
3261
|
-
const y = Math.max(8, Math.min(e.clientY, vh - menuH - 8));
|
|
3262
|
-
setImageMenu({ x, y, img: target });
|
|
3263
|
-
setTableMenu(null);
|
|
3264
|
-
return;
|
|
3265
|
-
}
|
|
3266
|
-
const cell = getClosestCell(e.target);
|
|
3267
|
-
if (cell) {
|
|
3268
|
-
e.preventDefault();
|
|
3269
|
-
const vw = window.innerWidth;
|
|
3270
|
-
const vh = window.innerHeight;
|
|
3271
|
-
const menuW = 220;
|
|
3272
|
-
const menuH = 300;
|
|
3273
|
-
const x = Math.max(8, Math.min(e.clientX, vw - menuW - 8));
|
|
3274
|
-
const y = Math.max(8, Math.min(e.clientY, vh - menuH - 8));
|
|
3275
|
-
setTableMenu({ x, y, cell });
|
|
3276
|
-
}
|
|
3277
|
-
else {
|
|
3278
|
-
setTableMenu(null);
|
|
3279
|
-
setImageMenu(null);
|
|
3280
|
-
}
|
|
3281
|
-
} }) }), selectedImage && imageOverlay && (_jsxs("div", { style: {
|
|
3282
|
-
position: "fixed",
|
|
3283
|
-
left: imageOverlay.left,
|
|
3284
|
-
top: imageOverlay.top,
|
|
3285
|
-
width: imageOverlay.width,
|
|
3286
|
-
height: imageOverlay.height,
|
|
3287
|
-
pointerEvents: "none",
|
|
3288
|
-
zIndex: 49,
|
|
3289
|
-
}, children: [_jsx("div", { style: {
|
|
3290
|
-
position: "absolute",
|
|
3291
|
-
inset: 0,
|
|
3292
|
-
outline: "2px solid var(--srte-accent)",
|
|
3293
|
-
outlineOffset: -2,
|
|
3294
|
-
} }), _jsx("div", { title: "Resize", onMouseDown: (e) => {
|
|
3295
|
-
e.preventDefault();
|
|
3296
|
-
if (!selectedImage)
|
|
3573
|
+
}
|
|
3574
|
+
const pos = getCellPosition(cell);
|
|
3575
|
+
if (!pos)
|
|
3297
3576
|
return;
|
|
3298
|
-
|
|
3299
|
-
side: "left",
|
|
3300
|
-
startX: e.clientX,
|
|
3301
|
-
startWidth: selectedImage.getBoundingClientRect().width,
|
|
3302
|
-
};
|
|
3577
|
+
selectingRef.current = { tbody: pos.tbody, start: cell };
|
|
3303
3578
|
const onMove = (ev) => {
|
|
3304
|
-
const
|
|
3305
|
-
|
|
3579
|
+
const under = document.elementFromPoint(ev.clientX, ev.clientY);
|
|
3580
|
+
const overCell = getClosestCell(under);
|
|
3581
|
+
const startInfo = selectingRef.current;
|
|
3582
|
+
if (!overCell || !startInfo)
|
|
3306
3583
|
return;
|
|
3307
|
-
const
|
|
3308
|
-
const
|
|
3309
|
-
|
|
3310
|
-
selectedImage.style.height = "auto";
|
|
3311
|
-
scheduleImageOverlay();
|
|
3312
|
-
};
|
|
3313
|
-
const onUp = () => {
|
|
3314
|
-
window.removeEventListener("mousemove", onMove);
|
|
3315
|
-
window.removeEventListener("mouseup", onUp);
|
|
3316
|
-
resizingRef.current = null;
|
|
3317
|
-
handleInput();
|
|
3318
|
-
};
|
|
3319
|
-
window.addEventListener("mousemove", onMove);
|
|
3320
|
-
window.addEventListener("mouseup", onUp);
|
|
3321
|
-
}, style: {
|
|
3322
|
-
position: "absolute",
|
|
3323
|
-
left: -6,
|
|
3324
|
-
top: "50%",
|
|
3325
|
-
transform: "translateY(-50%)",
|
|
3326
|
-
width: 8,
|
|
3327
|
-
height: 24,
|
|
3328
|
-
background: "var(--srte-accent)",
|
|
3329
|
-
borderRadius: 2,
|
|
3330
|
-
cursor: "ew-resize",
|
|
3331
|
-
pointerEvents: "auto",
|
|
3332
|
-
} }), _jsx("div", { title: "Resize", onMouseDown: (e) => {
|
|
3333
|
-
e.preventDefault();
|
|
3334
|
-
if (!selectedImage)
|
|
3335
|
-
return;
|
|
3336
|
-
resizingRef.current = {
|
|
3337
|
-
side: "right",
|
|
3338
|
-
startX: e.clientX,
|
|
3339
|
-
startWidth: selectedImage.getBoundingClientRect().width,
|
|
3340
|
-
};
|
|
3341
|
-
const onMove = (ev) => {
|
|
3342
|
-
const info = resizingRef.current;
|
|
3343
|
-
if (!info || !selectedImage)
|
|
3584
|
+
const a = getCellPosition(startInfo.start);
|
|
3585
|
+
const b = getCellPosition(overCell);
|
|
3586
|
+
if (!a || !b || a.tbody !== b.tbody)
|
|
3344
3587
|
return;
|
|
3345
|
-
const
|
|
3346
|
-
const
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3588
|
+
const sr = Math.min(a.rIdx, b.rIdx);
|
|
3589
|
+
const sc = Math.min(a.cIdx, b.cIdx);
|
|
3590
|
+
const er = Math.max(a.rIdx, b.rIdx);
|
|
3591
|
+
const ec = Math.max(a.cIdx, b.cIdx);
|
|
3592
|
+
updateSelectionDecor(a.tbody, sr, sc, er, ec);
|
|
3350
3593
|
};
|
|
3351
3594
|
const onUp = () => {
|
|
3352
3595
|
window.removeEventListener("mousemove", onMove);
|
|
3353
3596
|
window.removeEventListener("mouseup", onUp);
|
|
3354
|
-
|
|
3355
|
-
handleInput();
|
|
3597
|
+
selectingRef.current = null;
|
|
3356
3598
|
};
|
|
3357
3599
|
window.addEventListener("mousemove", onMove);
|
|
3358
3600
|
window.addEventListener("mouseup", onUp);
|
|
3359
|
-
},
|
|
3601
|
+
}, onContextMenu: (e) => {
|
|
3602
|
+
const target = e.target;
|
|
3603
|
+
if (target && target.tagName === "IMG") {
|
|
3604
|
+
e.preventDefault();
|
|
3605
|
+
const vw = window.innerWidth;
|
|
3606
|
+
const vh = window.innerHeight;
|
|
3607
|
+
const menuW = 220;
|
|
3608
|
+
const menuH = 200;
|
|
3609
|
+
const x = Math.max(8, Math.min(e.clientX, vw - menuW - 8));
|
|
3610
|
+
const y = Math.max(8, Math.min(e.clientY, vh - menuH - 8));
|
|
3611
|
+
setImageMenu({ x, y, img: target });
|
|
3612
|
+
setTableMenu(null);
|
|
3613
|
+
return;
|
|
3614
|
+
}
|
|
3615
|
+
const cell = getClosestCell(e.target);
|
|
3616
|
+
if (cell) {
|
|
3617
|
+
e.preventDefault();
|
|
3618
|
+
const vw = window.innerWidth;
|
|
3619
|
+
const vh = window.innerHeight;
|
|
3620
|
+
const menuW = 220;
|
|
3621
|
+
const menuH = 300;
|
|
3622
|
+
const x = Math.max(8, Math.min(e.clientX, vw - menuW - 8));
|
|
3623
|
+
const y = Math.max(8, Math.min(e.clientY, vh - menuH - 8));
|
|
3624
|
+
setTableMenu({ x, y, cell });
|
|
3625
|
+
}
|
|
3626
|
+
else {
|
|
3627
|
+
setTableMenu(null);
|
|
3628
|
+
setImageMenu(null);
|
|
3629
|
+
}
|
|
3630
|
+
} }), selectedImage && imageOverlay && (_jsxs("div", { style: {
|
|
3360
3631
|
position: "absolute",
|
|
3361
|
-
|
|
3362
|
-
top:
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3632
|
+
left: imageOverlay.left,
|
|
3633
|
+
top: imageOverlay.top,
|
|
3634
|
+
width: imageOverlay.width,
|
|
3635
|
+
height: imageOverlay.height,
|
|
3636
|
+
pointerEvents: "none",
|
|
3637
|
+
zIndex: 5,
|
|
3638
|
+
}, children: [_jsx("div", { style: {
|
|
3639
|
+
position: "absolute",
|
|
3640
|
+
inset: 0,
|
|
3641
|
+
outline: "2px solid var(--srte-accent)",
|
|
3642
|
+
outlineOffset: -2,
|
|
3643
|
+
} }), _jsx("div", { title: "Resize", onMouseDown: (e) => {
|
|
3644
|
+
e.preventDefault();
|
|
3645
|
+
if (!selectedImage)
|
|
3646
|
+
return;
|
|
3647
|
+
resizingRef.current = {
|
|
3648
|
+
side: "left",
|
|
3649
|
+
startX: e.clientX,
|
|
3650
|
+
startWidth: selectedImage.getBoundingClientRect().width,
|
|
3651
|
+
};
|
|
3652
|
+
const onMove = (ev) => {
|
|
3653
|
+
const info = resizingRef.current;
|
|
3654
|
+
if (!info || !selectedImage)
|
|
3655
|
+
return;
|
|
3656
|
+
const delta = info.startX - ev.clientX;
|
|
3657
|
+
const next = Math.max(80, Math.round(info.startWidth + delta));
|
|
3658
|
+
selectedImage.style.width = next + "px";
|
|
3659
|
+
selectedImage.style.height = "auto";
|
|
3660
|
+
scheduleImageOverlay();
|
|
3661
|
+
};
|
|
3662
|
+
const onUp = () => {
|
|
3663
|
+
window.removeEventListener("mousemove", onMove);
|
|
3664
|
+
window.removeEventListener("mouseup", onUp);
|
|
3665
|
+
resizingRef.current = null;
|
|
3666
|
+
handleInput();
|
|
3667
|
+
};
|
|
3668
|
+
window.addEventListener("mousemove", onMove);
|
|
3669
|
+
window.addEventListener("mouseup", onUp);
|
|
3670
|
+
}, style: {
|
|
3671
|
+
position: "absolute",
|
|
3672
|
+
left: -4,
|
|
3673
|
+
top: "50%",
|
|
3674
|
+
transform: "translateY(-50%)",
|
|
3675
|
+
width: 8,
|
|
3676
|
+
height: 24,
|
|
3677
|
+
background: "var(--srte-accent)",
|
|
3678
|
+
borderRadius: 2,
|
|
3679
|
+
cursor: "ew-resize",
|
|
3680
|
+
pointerEvents: "auto",
|
|
3681
|
+
} }), _jsx("div", { title: "Resize", onMouseDown: (e) => {
|
|
3682
|
+
e.preventDefault();
|
|
3683
|
+
if (!selectedImage)
|
|
3684
|
+
return;
|
|
3685
|
+
resizingRef.current = {
|
|
3686
|
+
side: "right",
|
|
3687
|
+
startX: e.clientX,
|
|
3688
|
+
startWidth: selectedImage.getBoundingClientRect().width,
|
|
3689
|
+
};
|
|
3690
|
+
const onMove = (ev) => {
|
|
3691
|
+
const info = resizingRef.current;
|
|
3692
|
+
if (!info || !selectedImage)
|
|
3693
|
+
return;
|
|
3694
|
+
const delta = ev.clientX - info.startX;
|
|
3695
|
+
const next = Math.max(80, Math.round(info.startWidth + delta));
|
|
3696
|
+
selectedImage.style.width = next + "px";
|
|
3697
|
+
selectedImage.style.height = "auto";
|
|
3698
|
+
scheduleImageOverlay();
|
|
3699
|
+
};
|
|
3700
|
+
const onUp = () => {
|
|
3701
|
+
window.removeEventListener("mousemove", onMove);
|
|
3702
|
+
window.removeEventListener("mouseup", onUp);
|
|
3703
|
+
resizingRef.current = null;
|
|
3704
|
+
handleInput();
|
|
3705
|
+
};
|
|
3706
|
+
window.addEventListener("mousemove", onMove);
|
|
3707
|
+
window.addEventListener("mouseup", onUp);
|
|
3708
|
+
}, style: {
|
|
3709
|
+
position: "absolute",
|
|
3710
|
+
right: -4,
|
|
3711
|
+
top: "50%",
|
|
3712
|
+
transform: "translateY(-50%)",
|
|
3713
|
+
width: 8,
|
|
3714
|
+
height: 24,
|
|
3715
|
+
background: "var(--srte-accent)",
|
|
3716
|
+
borderRadius: 2,
|
|
3717
|
+
cursor: "ew-resize",
|
|
3718
|
+
pointerEvents: "auto",
|
|
3719
|
+
} })] }))] }), tableMenu && (_jsx("div", { style: {
|
|
3371
3720
|
position: "fixed",
|
|
3372
3721
|
inset: 0,
|
|
3373
3722
|
zIndex: 60,
|
|
3723
|
+
background: "transparent",
|
|
3374
3724
|
}, onClick: () => setTableMenu(null), onContextMenu: (e) => {
|
|
3375
3725
|
// Prevent native menu while overlay is shown and reposition our menu
|
|
3376
3726
|
e.preventDefault();
|
|
@@ -3402,56 +3752,53 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3402
3752
|
maxHeight: 260,
|
|
3403
3753
|
overflowY: "auto",
|
|
3404
3754
|
color: "var(--srte-menu-text)",
|
|
3405
|
-
}, onClick: (e) => e.stopPropagation(), children: [_jsx("div", { style: { fontWeight: 600, fontSize: 11, margin: "2px 6px 6px" }, children: "Table" }), _jsxs("div", { style: { display: "grid", gap: 4 }, children: [_jsxs("
|
|
3406
|
-
display: "flex",
|
|
3407
|
-
alignItems: "center",
|
|
3408
|
-
gap: 8,
|
|
3409
|
-
padding: "6px 8px",
|
|
3410
|
-
fontSize: 12,
|
|
3411
|
-
}, onClick: () => setShowTableDialog(true), children: [_jsx("span", { children: "\u2795" }), _jsx("span", { children: "Insert table\u2026" })] }), _jsx("hr", { style: { margin: "4px 0" } }), _jsxs("div", { style: {
|
|
3755
|
+
}, onClick: (e) => e.stopPropagation(), children: [_jsx("div", { style: { fontWeight: 600, fontSize: 11, margin: "2px 6px 6px" }, children: "Table" }), _jsxs("div", { style: { display: "grid", gap: 4 }, children: [_jsxs("div", { style: {
|
|
3412
3756
|
display: "flex",
|
|
3413
3757
|
gap: 8,
|
|
3414
3758
|
alignItems: "center",
|
|
3415
3759
|
padding: "4px 6px",
|
|
3416
3760
|
fontSize: 12,
|
|
3417
3761
|
}, children: [_jsx("span", { children: "Fill:" }), _jsx("input", { type: "color", defaultValue: "#ffffff", onChange: (e) => {
|
|
3418
|
-
applyBgToSelection(e.target.value,
|
|
3419
|
-
setTableMenu(null);
|
|
3762
|
+
runTableCellAction(tableMenu.cell, (cell) => applyBgToSelection(e.target.value, cell));
|
|
3420
3763
|
}, style: {
|
|
3421
3764
|
width: 28,
|
|
3422
3765
|
height: 18,
|
|
3423
3766
|
padding: 0,
|
|
3424
3767
|
border: "none",
|
|
3425
3768
|
background: "transparent",
|
|
3426
|
-
} })] }), _jsxs("button", { style: {
|
|
3769
|
+
} })] }), _jsxs("button", { title: "Show or hide border for this cell, or for the selected cell range.", style: {
|
|
3427
3770
|
display: "flex",
|
|
3428
3771
|
alignItems: "center",
|
|
3429
3772
|
gap: 8,
|
|
3430
3773
|
padding: "6px 8px",
|
|
3431
3774
|
fontSize: 12,
|
|
3432
3775
|
}, onClick: () => {
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
}, children: [_jsx("span", { children: "\u25A6" }), _jsx("span", { children: "Toggle border" })] }), _jsx("hr", { style: { margin: "4px 0" } }), _jsxs("button", { disabled: !canMergeSelection(), style: {
|
|
3776
|
+
runTableCellAction(tableMenu.cell, toggleBorderSelection);
|
|
3777
|
+
}, children: [_jsx("span", { children: "\u25A6" }), _jsx("span", { children: "Show/hide cell border" })] }), _jsx("hr", { style: { margin: "4px 0" } }), _jsxs("button", { disabled: !canMergeFromCell(tableMenu.cell), style: {
|
|
3436
3778
|
display: "flex",
|
|
3437
3779
|
alignItems: "center",
|
|
3438
3780
|
gap: 8,
|
|
3439
3781
|
padding: "6px 8px",
|
|
3440
3782
|
fontSize: 12,
|
|
3441
|
-
opacity:
|
|
3442
|
-
cursor:
|
|
3783
|
+
opacity: canMergeFromCell(tableMenu.cell) ? 1 : 0.45,
|
|
3784
|
+
cursor: canMergeFromCell(tableMenu.cell) ? "pointer" : "not-allowed",
|
|
3443
3785
|
}, onClick: () => {
|
|
3786
|
+
if (!canMergeFromCell(tableMenu.cell))
|
|
3787
|
+
return;
|
|
3444
3788
|
mergeSelection();
|
|
3445
3789
|
setTableMenu(null);
|
|
3446
|
-
}, children: [_jsx("span", { children: "\u21C4" }), _jsx("span", { children: "Merge cells" })] }), _jsxs("button", { style: {
|
|
3790
|
+
}, children: [_jsx("span", { children: "\u21C4" }), _jsx("span", { children: "Merge cells" })] }), _jsxs("button", { disabled: !canSplitCell(tableMenu.cell), title: canSplitCell(tableMenu.cell) ? "Split this merged cell back into individual cells." : "Only merged cells can be split.", style: {
|
|
3447
3791
|
display: "flex",
|
|
3448
3792
|
alignItems: "center",
|
|
3449
3793
|
gap: 8,
|
|
3450
3794
|
padding: "6px 8px",
|
|
3451
3795
|
fontSize: 12,
|
|
3796
|
+
opacity: canSplitCell(tableMenu.cell) ? 1 : 0.45,
|
|
3797
|
+
cursor: canSplitCell(tableMenu.cell) ? "pointer" : "not-allowed",
|
|
3452
3798
|
}, onClick: () => {
|
|
3453
|
-
|
|
3454
|
-
|
|
3799
|
+
if (!canSplitCell(tableMenu.cell))
|
|
3800
|
+
return;
|
|
3801
|
+
runTableCellAction(tableMenu.cell, splitCell);
|
|
3455
3802
|
}, children: [_jsx("span", { children: "\u2922" }), _jsx("span", { children: "Split cell" })] }), _jsx("hr", { style: { margin: "4px 0" } }), _jsxs("button", { style: {
|
|
3456
3803
|
display: "flex",
|
|
3457
3804
|
alignItems: "center",
|
|
@@ -3459,8 +3806,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3459
3806
|
padding: "6px 8px",
|
|
3460
3807
|
fontSize: 12,
|
|
3461
3808
|
}, onClick: () => {
|
|
3462
|
-
|
|
3463
|
-
setTableMenu(null);
|
|
3809
|
+
runTableCellAction(tableMenu.cell, (cell) => addRow(cell, "above"));
|
|
3464
3810
|
}, children: [_jsx("span", { children: "\u21A5" }), _jsx("span", { children: "Row above" })] }), _jsxs("button", { style: {
|
|
3465
3811
|
display: "flex",
|
|
3466
3812
|
alignItems: "center",
|
|
@@ -3468,8 +3814,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3468
3814
|
padding: "6px 8px",
|
|
3469
3815
|
fontSize: 12,
|
|
3470
3816
|
}, onClick: () => {
|
|
3471
|
-
|
|
3472
|
-
setTableMenu(null);
|
|
3817
|
+
runTableCellAction(tableMenu.cell, (cell) => addRow(cell, "below"));
|
|
3473
3818
|
}, children: [_jsx("span", { children: "\u21A7" }), _jsx("span", { children: "Row below" })] }), _jsxs("button", { style: {
|
|
3474
3819
|
display: "flex",
|
|
3475
3820
|
alignItems: "center",
|
|
@@ -3477,8 +3822,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3477
3822
|
padding: "6px 8px",
|
|
3478
3823
|
fontSize: 12,
|
|
3479
3824
|
}, onClick: () => {
|
|
3480
|
-
|
|
3481
|
-
setTableMenu(null);
|
|
3825
|
+
runTableCellAction(tableMenu.cell, (cell) => addCol(cell, "left"));
|
|
3482
3826
|
}, children: [_jsx("span", { children: "\u2190" }), _jsx("span", { children: "Column left" })] }), _jsxs("button", { style: {
|
|
3483
3827
|
display: "flex",
|
|
3484
3828
|
alignItems: "center",
|
|
@@ -3486,8 +3830,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3486
3830
|
padding: "6px 8px",
|
|
3487
3831
|
fontSize: 12,
|
|
3488
3832
|
}, onClick: () => {
|
|
3489
|
-
|
|
3490
|
-
setTableMenu(null);
|
|
3833
|
+
runTableCellAction(tableMenu.cell, (cell) => addCol(cell, "right"));
|
|
3491
3834
|
}, children: [_jsx("span", { children: "\u2192" }), _jsx("span", { children: "Column right" })] }), _jsxs("button", { style: {
|
|
3492
3835
|
display: "flex",
|
|
3493
3836
|
alignItems: "center",
|
|
@@ -3495,8 +3838,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3495
3838
|
padding: "6px 8px",
|
|
3496
3839
|
fontSize: 12,
|
|
3497
3840
|
}, onClick: () => {
|
|
3498
|
-
|
|
3499
|
-
setTableMenu(null);
|
|
3841
|
+
runTableCellAction(tableMenu.cell, deleteRow);
|
|
3500
3842
|
}, children: [_jsx("span", { children: "\u2716" }), _jsx("span", { children: "Delete row" })] }), _jsxs("button", { style: {
|
|
3501
3843
|
display: "flex",
|
|
3502
3844
|
alignItems: "center",
|
|
@@ -3504,8 +3846,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3504
3846
|
padding: "6px 8px",
|
|
3505
3847
|
fontSize: 12,
|
|
3506
3848
|
}, onClick: () => {
|
|
3507
|
-
|
|
3508
|
-
setTableMenu(null);
|
|
3849
|
+
runTableCellAction(tableMenu.cell, deleteCol);
|
|
3509
3850
|
}, children: [_jsx("span", { children: "\u2716" }), _jsx("span", { children: "Delete column" })] }), _jsxs("button", { style: {
|
|
3510
3851
|
display: "flex",
|
|
3511
3852
|
alignItems: "center",
|
|
@@ -3513,36 +3854,37 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3513
3854
|
padding: "6px 8px",
|
|
3514
3855
|
fontSize: 12,
|
|
3515
3856
|
}, onClick: () => {
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
}, children: [_jsx("span", { children: "H" }), _jsx("span", { children: "Toggle header" })] }), _jsxs("button", { style: {
|
|
3857
|
+
runTableCellAction(tableMenu.cell, toggleHeaderCell);
|
|
3858
|
+
}, children: [_jsx("span", { children: "H" }), _jsx("span", { children: tableMenu.cell.tagName === "TH" ? "Remove cell header" : "Make cell header" })] }), _jsxs("button", { style: {
|
|
3519
3859
|
display: "flex",
|
|
3520
3860
|
alignItems: "center",
|
|
3521
3861
|
gap: 8,
|
|
3522
3862
|
padding: "6px 8px",
|
|
3523
3863
|
fontSize: 12,
|
|
3524
3864
|
}, onClick: () => {
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
}, children: [_jsx("span", { children: "H\u2081" }), _jsx("span", { children: "Toggle header row" })] }), _jsxs("button", { style: {
|
|
3865
|
+
runTableCellAction(tableMenu.cell, toggleHeaderRow);
|
|
3866
|
+
}, children: [_jsx("span", { children: "H\u2081" }), _jsx("span", { children: "Make this row header" })] }), _jsxs("button", { style: {
|
|
3528
3867
|
display: "flex",
|
|
3529
3868
|
alignItems: "center",
|
|
3530
3869
|
gap: 8,
|
|
3531
3870
|
padding: "6px 8px",
|
|
3532
3871
|
fontSize: 12,
|
|
3533
3872
|
}, onClick: () => {
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
}, children: [_jsx("span", { children: "H\u2195" }), _jsx("span", { children: "Toggle header column" })] }), _jsx("hr", { style: { margin: "4px 0" } }), _jsxs("button", { style: {
|
|
3873
|
+
runTableCellAction(tableMenu.cell, toggleHeaderColumn);
|
|
3874
|
+
}, children: [_jsx("span", { children: "H\u2195" }), _jsx("span", { children: "Make this column header" })] }), _jsx("hr", { style: { margin: "4px 0" } }), _jsxs("button", { style: {
|
|
3537
3875
|
display: "flex",
|
|
3538
3876
|
alignItems: "center",
|
|
3539
3877
|
gap: 8,
|
|
3540
3878
|
padding: "6px 8px",
|
|
3541
3879
|
fontSize: 12,
|
|
3542
3880
|
}, onClick: () => {
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3881
|
+
runTableCellAction(tableMenu.cell, deleteTable);
|
|
3882
|
+
}, children: [_jsx("span", { children: "\uD83D\uDDD1" }), _jsx("span", { children: "Delete table" })] })] })] }) })), imageMenu && (_jsx("div", { style: {
|
|
3883
|
+
position: "fixed",
|
|
3884
|
+
inset: 0,
|
|
3885
|
+
zIndex: 60,
|
|
3886
|
+
background: "transparent",
|
|
3887
|
+
}, onClick: () => setImageMenu(null), onContextMenu: (e) => {
|
|
3546
3888
|
e.preventDefault();
|
|
3547
3889
|
const vw = window.innerWidth;
|
|
3548
3890
|
const vh = window.innerHeight;
|