smartrte-react 0.2.3 → 0.2.4
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 +935 -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,35 +2507,30 @@ 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
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
const cells = cellsOfRow(row);
|
|
2131
|
-
for (let c = sel.sc; c <= sel.ec; c++) {
|
|
2132
|
-
const cell = cells[c];
|
|
2133
|
-
if (cell)
|
|
2134
|
-
cell.style.background = hex;
|
|
2135
|
-
}
|
|
2136
|
-
}
|
|
2531
|
+
getCellsInGridRect(sel.tbody, sel.sr, sel.sc, sel.er, sel.ec).forEach((cell) => {
|
|
2532
|
+
cell.style.background = hex;
|
|
2533
|
+
});
|
|
2137
2534
|
}
|
|
2138
2535
|
else if (fallbackCell) {
|
|
2139
2536
|
fallbackCell.style.background = hex;
|
|
@@ -2143,25 +2540,21 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2143
2540
|
const applyToggle = (cell) => {
|
|
2144
2541
|
const cur = cell.style.border;
|
|
2145
2542
|
cell.style.border =
|
|
2146
|
-
cur && cur !== "none" ? "none" : "1px solid #
|
|
2543
|
+
cur && cur !== "none" ? "none" : "1px solid #d1d5db";
|
|
2147
2544
|
};
|
|
2148
|
-
const sel = selectionRef.current;
|
|
2545
|
+
const sel = shouldUseTableSelection(fallbackCell) ? selectionRef.current : null;
|
|
2149
2546
|
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
|
-
}
|
|
2547
|
+
getCellsInGridRect(sel.tbody, sel.sr, sel.sc, sel.er, sel.ec).forEach(applyToggle);
|
|
2160
2548
|
}
|
|
2161
2549
|
else if (fallbackCell) {
|
|
2162
2550
|
applyToggle(fallbackCell);
|
|
2163
2551
|
}
|
|
2164
2552
|
};
|
|
2553
|
+
const runTableCellAction = (cell, action) => {
|
|
2554
|
+
action(cell);
|
|
2555
|
+
handleInput();
|
|
2556
|
+
setTableMenu(null);
|
|
2557
|
+
};
|
|
2165
2558
|
// Table column and row resizing functions
|
|
2166
2559
|
const getColumnCells = (table, colIndex) => {
|
|
2167
2560
|
const tbody = table.querySelector('tbody');
|
|
@@ -2282,6 +2675,27 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2282
2675
|
});
|
|
2283
2676
|
});
|
|
2284
2677
|
};
|
|
2678
|
+
const activeButtonStyle = (active = false, extra = {}) => ({
|
|
2679
|
+
height: 32,
|
|
2680
|
+
minWidth: 32,
|
|
2681
|
+
padding: "0 8px",
|
|
2682
|
+
border: active
|
|
2683
|
+
? "2px solid var(--srte-accent)"
|
|
2684
|
+
: "1px solid var(--srte-input-border)",
|
|
2685
|
+
borderRadius: 6,
|
|
2686
|
+
background: active ? "var(--srte-accent-bg)" : "var(--srte-input-bg)",
|
|
2687
|
+
color: "var(--srte-input-text)",
|
|
2688
|
+
boxShadow: active ? "inset 0 0 0 1px var(--srte-accent)" : "none",
|
|
2689
|
+
...extra,
|
|
2690
|
+
});
|
|
2691
|
+
const preserveToolbarMouseDown = (event) => {
|
|
2692
|
+
const target = event.target;
|
|
2693
|
+
const button = target?.closest("button");
|
|
2694
|
+
preserveEditorSelection();
|
|
2695
|
+
if (button && !button.hasAttribute("disabled")) {
|
|
2696
|
+
event.preventDefault();
|
|
2697
|
+
}
|
|
2698
|
+
};
|
|
2285
2699
|
const editorClass = `srte-editor${theme === 'dark' ? ' srte-dark' : ''}${className ? ' ' + className : ''}`;
|
|
2286
2700
|
return (_jsxs("div", { className: editorClass, style: {
|
|
2287
2701
|
border: "1px solid var(--srte-border)",
|
|
@@ -2294,7 +2708,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2294
2708
|
background: "var(--srte-bg)",
|
|
2295
2709
|
color: "var(--srte-text)",
|
|
2296
2710
|
boxSizing: "border-box"
|
|
2297
|
-
}, children: [_jsxs("div", { style: {
|
|
2711
|
+
}, children: [_jsxs("div", { onMouseDown: preserveToolbarMouseDown, style: {
|
|
2298
2712
|
display: "flex",
|
|
2299
2713
|
flexWrap: "wrap",
|
|
2300
2714
|
maxWidth: "100%",
|
|
@@ -2343,7 +2757,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2343
2757
|
} }), _jsx("input", { ref: mdInputRef, type: "file", accept: ".md,.markdown,text/markdown,text/plain", style: { display: "none" }, onChange: (e) => {
|
|
2344
2758
|
importTextFile(e.currentTarget.files, "md");
|
|
2345
2759
|
e.currentTarget.value = "";
|
|
2346
|
-
} }), _jsxs("select", { defaultValue: "p", onChange: (e) => {
|
|
2760
|
+
} }), _jsxs("select", { defaultValue: "p", onMouseDown: preserveEditorSelection, onChange: (e) => {
|
|
2347
2761
|
const val = e.target.value;
|
|
2348
2762
|
if (val === "p")
|
|
2349
2763
|
applyFormatBlock("<p>");
|
|
@@ -2360,42 +2774,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2360
2774
|
borderRadius: 6,
|
|
2361
2775
|
background: "var(--srte-input-bg)",
|
|
2362
2776
|
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: () => {
|
|
2777
|
+
}, 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
2778
|
// Save selection before dropdown interaction
|
|
2400
2779
|
const sel = window.getSelection();
|
|
2401
2780
|
if (sel && sel.rangeCount > 0) {
|
|
@@ -2452,45 +2831,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2452
2831
|
borderRadius: 6,
|
|
2453
2832
|
background: "var(--srte-input-bg)",
|
|
2454
2833
|
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: {
|
|
2834
|
+
}, 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
2835
|
height: 32,
|
|
2495
2836
|
minWidth: 32,
|
|
2496
2837
|
padding: "0 8px",
|
|
@@ -2498,16 +2839,10 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2498
2839
|
borderRadius: 6,
|
|
2499
2840
|
background: "var(--srte-input-bg)",
|
|
2500
2841
|
color: "var(--srte-input-text)",
|
|
2501
|
-
}, children: "\u03A9" }), _jsx("button", { title: "Code block", onClick: () => exec("formatBlock", "<pre>"), style: {
|
|
2502
|
-
height: 32,
|
|
2842
|
+
}, children: "\u03A9" }), _jsx("button", { title: "Code block", onClick: () => exec("formatBlock", "<pre>"), "aria-pressed": activeState.codeBlock, style: activeButtonStyle(activeState.codeBlock, {
|
|
2503
2843
|
minWidth: 36,
|
|
2504
|
-
padding: "0 8px",
|
|
2505
|
-
border: "1px solid var(--srte-input-border)",
|
|
2506
|
-
borderRadius: 6,
|
|
2507
|
-
background: "var(--srte-input-bg)",
|
|
2508
2844
|
fontFamily: "ui-monospace, SFMono-Regular, Menlo",
|
|
2509
|
-
|
|
2510
|
-
}, children: "< />" }), formula && (_jsx("button", { title: "Insert formula", onClick: () => setShowFormulaDialog(true), style: {
|
|
2845
|
+
}), children: "< />" }), formula && (_jsx("button", { title: "Insert formula", onClick: () => setShowFormulaDialog(true), style: {
|
|
2511
2846
|
height: 32,
|
|
2512
2847
|
minWidth: 32,
|
|
2513
2848
|
padding: "0 8px",
|
|
@@ -2685,17 +3020,19 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2685
3020
|
position: "fixed",
|
|
2686
3021
|
inset: 0,
|
|
2687
3022
|
background: "var(--srte-modal-backdrop)",
|
|
3023
|
+
backdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
3024
|
+
WebkitBackdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
2688
3025
|
display: "flex",
|
|
2689
3026
|
alignItems: "center",
|
|
2690
3027
|
justifyContent: "center",
|
|
2691
|
-
zIndex:
|
|
3028
|
+
zIndex: 90,
|
|
2692
3029
|
}, onClick: () => setShowTableDialog(false), children: _jsxs("div", { style: {
|
|
2693
3030
|
background: "var(--srte-modal-bg)",
|
|
2694
3031
|
color: "var(--srte-modal-text)",
|
|
2695
3032
|
padding: 16,
|
|
2696
3033
|
borderRadius: 8,
|
|
2697
3034
|
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: {
|
|
3035
|
+
}, 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
3036
|
display: "grid",
|
|
2700
3037
|
gridTemplateColumns: "repeat(10, 18px)",
|
|
2701
3038
|
gap: 2,
|
|
@@ -2729,6 +3066,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2729
3066
|
position: "fixed",
|
|
2730
3067
|
inset: 0,
|
|
2731
3068
|
background: "var(--srte-modal-backdrop)",
|
|
3069
|
+
backdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
3070
|
+
WebkitBackdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
2732
3071
|
display: "flex",
|
|
2733
3072
|
alignItems: "center",
|
|
2734
3073
|
justifyContent: "center",
|
|
@@ -2783,6 +3122,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2783
3122
|
position: "fixed",
|
|
2784
3123
|
inset: 0,
|
|
2785
3124
|
background: "var(--srte-modal-backdrop)",
|
|
3125
|
+
backdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
3126
|
+
WebkitBackdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
2786
3127
|
display: "flex",
|
|
2787
3128
|
alignItems: "center",
|
|
2788
3129
|
justifyContent: "center",
|
|
@@ -2849,6 +3190,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2849
3190
|
position: "fixed",
|
|
2850
3191
|
inset: 0,
|
|
2851
3192
|
background: "var(--srte-modal-backdrop)",
|
|
3193
|
+
backdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
3194
|
+
WebkitBackdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
2852
3195
|
display: "flex",
|
|
2853
3196
|
alignItems: "center",
|
|
2854
3197
|
justifyContent: "center",
|
|
@@ -2893,6 +3236,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2893
3236
|
position: "fixed",
|
|
2894
3237
|
inset: 0,
|
|
2895
3238
|
background: "var(--srte-modal-backdrop)",
|
|
3239
|
+
backdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
3240
|
+
WebkitBackdropFilter: "var(--srte-modal-backdrop-filter)",
|
|
2896
3241
|
display: "flex",
|
|
2897
3242
|
alignItems: "center",
|
|
2898
3243
|
justifyContent: "center",
|
|
@@ -2990,7 +3335,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
2990
3335
|
borderRadius: 4,
|
|
2991
3336
|
background: "var(--srte-input-bg)",
|
|
2992
3337
|
color: "var(--srte-modal-text)",
|
|
2993
|
-
}, title: sym, children: sym }, i)))] })] }) })),
|
|
3338
|
+
}, title: sym, children: sym }, i)))] })] }) })), _jsxs("div", { ref: editorScrollRef, style: {
|
|
2994
3339
|
width: "100%",
|
|
2995
3340
|
maxWidth: "100%",
|
|
2996
3341
|
flex: "1 1 auto",
|
|
@@ -3003,374 +3348,376 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3003
3348
|
boxSizing: "border-box",
|
|
3004
3349
|
position: "relative",
|
|
3005
3350
|
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
|
-
|
|
3351
|
+
}, children: [_jsx("div", { ref: editableRef, contentEditable: !readOnly, suppressContentEditableWarning: true, onInput: handleInput, onKeyUp: updateActiveState, onMouseUp: updateActiveState, onCompositionStart: () => (isComposingRef.current = true), onCompositionEnd: () => {
|
|
3352
|
+
isComposingRef.current = false;
|
|
3353
|
+
handleInput();
|
|
3354
|
+
}, onPaste: (e) => {
|
|
3355
|
+
const items = e.clipboardData?.files;
|
|
3356
|
+
if (media && items && items.length) {
|
|
3357
|
+
const hasImage = Array.from(items).some((f) => f.type.startsWith("image/"));
|
|
3358
|
+
if (hasImage) {
|
|
3359
|
+
e.preventDefault();
|
|
3360
|
+
handleLocalImageFiles(items);
|
|
3361
|
+
return;
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
const html = e.clipboardData?.getData("text/html");
|
|
3365
|
+
if (html) {
|
|
3014
3366
|
e.preventDefault();
|
|
3015
|
-
|
|
3016
|
-
return;
|
|
3367
|
+
insertCleanHtml(cleanPastedHtml(html));
|
|
3017
3368
|
}
|
|
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);
|
|
3369
|
+
}, onDragOver: (e) => {
|
|
3370
|
+
// Allow dragging images within editor and file drops
|
|
3371
|
+
if (draggedImageRef.current ||
|
|
3372
|
+
e.dataTransfer?.types?.includes("Files")) {
|
|
3373
|
+
e.preventDefault();
|
|
3041
3374
|
}
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3375
|
+
}, onDrop: (e) => {
|
|
3376
|
+
// Move existing dragged image inside editor
|
|
3377
|
+
if (draggedImageRef.current) {
|
|
3378
|
+
e.preventDefault();
|
|
3379
|
+
const x = e.clientX;
|
|
3380
|
+
const y = e.clientY;
|
|
3381
|
+
let range = null;
|
|
3382
|
+
// @ts-ignore
|
|
3383
|
+
if (document.caretRangeFromPoint) {
|
|
3384
|
+
// @ts-ignore
|
|
3385
|
+
range = document.caretRangeFromPoint(x, y);
|
|
3047
3386
|
}
|
|
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;
|
|
3387
|
+
else if (document.caretPositionFromPoint) {
|
|
3388
|
+
const pos = document.caretPositionFromPoint(x, y);
|
|
3389
|
+
if (pos) {
|
|
3390
|
+
range = document.createRange();
|
|
3391
|
+
range.setStart(pos.offsetNode, pos.offset);
|
|
3065
3392
|
}
|
|
3066
|
-
el = el.parentElement;
|
|
3067
|
-
}
|
|
3068
|
-
if (linkAncestor) {
|
|
3069
|
-
linkAncestor.parentElement?.insertBefore(img, linkAncestor.nextSibling);
|
|
3070
3393
|
}
|
|
3071
|
-
|
|
3072
|
-
|
|
3394
|
+
const img = draggedImageRef.current;
|
|
3395
|
+
draggedImageRef.current = null;
|
|
3396
|
+
if (range &&
|
|
3397
|
+
img &&
|
|
3398
|
+
editableRef.current?.contains(range.commonAncestorContainer)) {
|
|
3399
|
+
// Avoid inserting inside the image itself
|
|
3400
|
+
if (range.startContainer === img || range.endContainer === img)
|
|
3401
|
+
return;
|
|
3402
|
+
// If dropping inside a link, insert right after the link element
|
|
3403
|
+
let container = range.commonAncestorContainer;
|
|
3404
|
+
let linkAncestor = null;
|
|
3405
|
+
let el = container;
|
|
3406
|
+
while (el && el !== editableRef.current) {
|
|
3407
|
+
if (el.tagName === "A") {
|
|
3408
|
+
linkAncestor = el;
|
|
3409
|
+
break;
|
|
3410
|
+
}
|
|
3411
|
+
el = el.parentElement;
|
|
3412
|
+
}
|
|
3413
|
+
if (linkAncestor) {
|
|
3414
|
+
linkAncestor.parentElement?.insertBefore(img, linkAncestor.nextSibling);
|
|
3415
|
+
}
|
|
3416
|
+
else {
|
|
3417
|
+
range.insertNode(img);
|
|
3418
|
+
}
|
|
3419
|
+
const r = document.createRange();
|
|
3420
|
+
r.setStartAfter(img);
|
|
3421
|
+
r.collapse(true);
|
|
3422
|
+
safeSelectRange(r);
|
|
3423
|
+
setSelectedImage(img);
|
|
3424
|
+
scheduleImageOverlay();
|
|
3425
|
+
handleInput();
|
|
3073
3426
|
}
|
|
3074
|
-
|
|
3075
|
-
r.setStartAfter(img);
|
|
3076
|
-
r.collapse(true);
|
|
3077
|
-
safeSelectRange(r);
|
|
3078
|
-
setSelectedImage(img);
|
|
3079
|
-
scheduleImageOverlay();
|
|
3080
|
-
handleInput();
|
|
3427
|
+
return;
|
|
3081
3428
|
}
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
const y = e.clientY;
|
|
3089
|
-
let range = null;
|
|
3090
|
-
// @ts-ignore
|
|
3091
|
-
if (document.caretRangeFromPoint) {
|
|
3429
|
+
if (media && e.dataTransfer?.files?.length) {
|
|
3430
|
+
e.preventDefault();
|
|
3431
|
+
// Try to move caret to drop point
|
|
3432
|
+
const x = e.clientX;
|
|
3433
|
+
const y = e.clientY;
|
|
3434
|
+
let range = null;
|
|
3092
3435
|
// @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);
|
|
3436
|
+
if (document.caretRangeFromPoint) {
|
|
3437
|
+
// @ts-ignore
|
|
3438
|
+
range = document.caretRangeFromPoint(x, y);
|
|
3100
3439
|
}
|
|
3440
|
+
else if (document.caretPositionFromPoint) {
|
|
3441
|
+
const pos = document.caretPositionFromPoint(x, y);
|
|
3442
|
+
if (pos) {
|
|
3443
|
+
range = document.createRange();
|
|
3444
|
+
range.setStart(pos.offsetNode, pos.offset);
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
if (range) {
|
|
3448
|
+
const sel = window.getSelection();
|
|
3449
|
+
sel?.removeAllRanges();
|
|
3450
|
+
sel?.addRange(range);
|
|
3451
|
+
}
|
|
3452
|
+
handleLocalImageFiles(e.dataTransfer.files);
|
|
3101
3453
|
}
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3454
|
+
}, onClick: (e) => {
|
|
3455
|
+
const t = e.target;
|
|
3456
|
+
if (t && t.tagName === "IMG") {
|
|
3457
|
+
setSelectedImage(t);
|
|
3458
|
+
scheduleImageOverlay();
|
|
3106
3459
|
}
|
|
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);
|
|
3460
|
+
else {
|
|
3461
|
+
setSelectedImage(null);
|
|
3462
|
+
setImageOverlay(null);
|
|
3463
|
+
}
|
|
3464
|
+
}, onDragStart: (e) => {
|
|
3465
|
+
const t = e.target;
|
|
3466
|
+
if (t && t.tagName === "IMG") {
|
|
3467
|
+
draggedImageRef.current = t;
|
|
3468
|
+
try {
|
|
3469
|
+
e.dataTransfer?.setData("text/plain", "moving-image");
|
|
3470
|
+
e.dataTransfer.effectAllowed = "move";
|
|
3471
|
+
// Provide a subtle drag image
|
|
3472
|
+
const dt = e.dataTransfer;
|
|
3473
|
+
if (dt && typeof dt.setDragImage === "function") {
|
|
3474
|
+
const ghost = new Image();
|
|
3475
|
+
ghost.src = t.src;
|
|
3476
|
+
ghost.width = Math.min(120, t.width);
|
|
3477
|
+
ghost.height = Math.min(120, t.height);
|
|
3478
|
+
dt.setDragImage(ghost, 10, 10);
|
|
3479
|
+
}
|
|
3134
3480
|
}
|
|
3481
|
+
catch { }
|
|
3135
3482
|
}
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3483
|
+
else {
|
|
3484
|
+
draggedImageRef.current = null;
|
|
3485
|
+
}
|
|
3486
|
+
}, onDragEnd: () => {
|
|
3139
3487
|
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");
|
|
3488
|
+
}, style: {
|
|
3489
|
+
minHeight: typeof minHeight === "number" ? `${minHeight}px` : minHeight,
|
|
3490
|
+
maxWidth: "100%",
|
|
3491
|
+
overflowX: "visible",
|
|
3492
|
+
padding: "16px",
|
|
3493
|
+
paddingBottom: "32px",
|
|
3494
|
+
outline: "none",
|
|
3495
|
+
lineHeight: 1.6,
|
|
3496
|
+
boxSizing: "border-box",
|
|
3497
|
+
fontFamily: defaultFont || "inherit",
|
|
3498
|
+
}, "data-placeholder": placeholder, onFocus: (e) => {
|
|
3499
|
+
// Ensure the editor has at least one paragraph to type into
|
|
3500
|
+
const el = e.currentTarget;
|
|
3501
|
+
if (!el.innerHTML || el.innerHTML === "<br>") {
|
|
3502
|
+
el.innerHTML = "<p><br></p>";
|
|
3173
3503
|
}
|
|
3174
|
-
|
|
3175
|
-
|
|
3504
|
+
updateActiveState();
|
|
3505
|
+
}, onKeyDown: (e) => {
|
|
3506
|
+
if (formula &&
|
|
3507
|
+
(e.metaKey || e.ctrlKey) &&
|
|
3508
|
+
String(e.key).toLowerCase() === "m") {
|
|
3509
|
+
e.preventDefault();
|
|
3510
|
+
setShowFormulaDialog(true);
|
|
3511
|
+
return;
|
|
3176
3512
|
}
|
|
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];
|
|
3513
|
+
// Keep Tab for indentation in lists; otherwise insert 2 spaces
|
|
3514
|
+
if (e.key === "Tab") {
|
|
3515
|
+
e.preventDefault();
|
|
3516
|
+
if (document.queryCommandState("insertUnorderedList") ||
|
|
3517
|
+
document.queryCommandState("insertOrderedList")) {
|
|
3518
|
+
exec(e.shiftKey ? "outdent" : "indent");
|
|
3207
3519
|
}
|
|
3208
|
-
else
|
|
3209
|
-
|
|
3210
|
-
atEnd) {
|
|
3211
|
-
target = rows[rIdx + 1].children[cIdx];
|
|
3520
|
+
else {
|
|
3521
|
+
document.execCommand("insertText", false, " ");
|
|
3212
3522
|
}
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3523
|
+
}
|
|
3524
|
+
// Table navigation with arrows inside cells
|
|
3525
|
+
if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key)) {
|
|
3526
|
+
const sel = window.getSelection();
|
|
3527
|
+
const cell = getClosestCell(sel?.anchorNode || null);
|
|
3528
|
+
if (table &&
|
|
3529
|
+
cell &&
|
|
3530
|
+
cell.parentElement &&
|
|
3531
|
+
cell.parentElement.parentElement) {
|
|
3532
|
+
const row = cell.parentElement;
|
|
3533
|
+
const tbody = row.parentElement;
|
|
3534
|
+
const cells = Array.from(row.children).filter((c) => c.tagName === "TD" ||
|
|
3535
|
+
c.tagName === "TH");
|
|
3536
|
+
const rows = Array.from(tbody.children);
|
|
3537
|
+
const rIdx = rows.indexOf(row);
|
|
3538
|
+
const cIdx = cells.indexOf(cell);
|
|
3539
|
+
const atStart = (sel?.anchorOffset || 0) === 0;
|
|
3540
|
+
const cellTextLen = (cell.textContent || "").length;
|
|
3541
|
+
const atEnd = (sel?.anchorOffset || 0) >= cellTextLen;
|
|
3542
|
+
let target = null;
|
|
3543
|
+
if (e.key === "ArrowLeft" && atStart && cIdx > 0) {
|
|
3544
|
+
target = row.children[cIdx - 1];
|
|
3545
|
+
}
|
|
3546
|
+
else if (e.key === "ArrowRight" &&
|
|
3547
|
+
atEnd &&
|
|
3548
|
+
cIdx < row.children.length - 1) {
|
|
3549
|
+
target = row.children[cIdx + 1];
|
|
3550
|
+
}
|
|
3551
|
+
else if (e.key === "ArrowUp" && rIdx > 0 && atStart) {
|
|
3552
|
+
target = rows[rIdx - 1].children[cIdx];
|
|
3553
|
+
}
|
|
3554
|
+
else if (e.key === "ArrowDown" &&
|
|
3555
|
+
rIdx < rows.length - 1 &&
|
|
3556
|
+
atEnd) {
|
|
3557
|
+
target = rows[rIdx + 1].children[cIdx];
|
|
3558
|
+
}
|
|
3559
|
+
if (target) {
|
|
3560
|
+
e.preventDefault();
|
|
3561
|
+
moveCaretToCell(target, e.key === "ArrowRight" || e.key === "ArrowDown");
|
|
3562
|
+
}
|
|
3216
3563
|
}
|
|
3217
3564
|
}
|
|
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)
|
|
3565
|
+
}, onMouseDown: (e) => {
|
|
3566
|
+
const cell = getClosestCell(e.target);
|
|
3567
|
+
if (!cell) {
|
|
3568
|
+
clearSelectionDecor();
|
|
3234
3569
|
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)
|
|
3570
|
+
}
|
|
3571
|
+
const pos = getCellPosition(cell);
|
|
3572
|
+
if (!pos)
|
|
3297
3573
|
return;
|
|
3298
|
-
|
|
3299
|
-
side: "left",
|
|
3300
|
-
startX: e.clientX,
|
|
3301
|
-
startWidth: selectedImage.getBoundingClientRect().width,
|
|
3302
|
-
};
|
|
3574
|
+
selectingRef.current = { tbody: pos.tbody, start: cell };
|
|
3303
3575
|
const onMove = (ev) => {
|
|
3304
|
-
const
|
|
3305
|
-
|
|
3576
|
+
const under = document.elementFromPoint(ev.clientX, ev.clientY);
|
|
3577
|
+
const overCell = getClosestCell(under);
|
|
3578
|
+
const startInfo = selectingRef.current;
|
|
3579
|
+
if (!overCell || !startInfo)
|
|
3306
3580
|
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)
|
|
3581
|
+
const a = getCellPosition(startInfo.start);
|
|
3582
|
+
const b = getCellPosition(overCell);
|
|
3583
|
+
if (!a || !b || a.tbody !== b.tbody)
|
|
3344
3584
|
return;
|
|
3345
|
-
const
|
|
3346
|
-
const
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3585
|
+
const sr = Math.min(a.rIdx, b.rIdx);
|
|
3586
|
+
const sc = Math.min(a.cIdx, b.cIdx);
|
|
3587
|
+
const er = Math.max(a.rIdx, b.rIdx);
|
|
3588
|
+
const ec = Math.max(a.cIdx, b.cIdx);
|
|
3589
|
+
updateSelectionDecor(a.tbody, sr, sc, er, ec);
|
|
3350
3590
|
};
|
|
3351
3591
|
const onUp = () => {
|
|
3352
3592
|
window.removeEventListener("mousemove", onMove);
|
|
3353
3593
|
window.removeEventListener("mouseup", onUp);
|
|
3354
|
-
|
|
3355
|
-
handleInput();
|
|
3594
|
+
selectingRef.current = null;
|
|
3356
3595
|
};
|
|
3357
3596
|
window.addEventListener("mousemove", onMove);
|
|
3358
3597
|
window.addEventListener("mouseup", onUp);
|
|
3359
|
-
},
|
|
3598
|
+
}, onContextMenu: (e) => {
|
|
3599
|
+
const target = e.target;
|
|
3600
|
+
if (target && target.tagName === "IMG") {
|
|
3601
|
+
e.preventDefault();
|
|
3602
|
+
const vw = window.innerWidth;
|
|
3603
|
+
const vh = window.innerHeight;
|
|
3604
|
+
const menuW = 220;
|
|
3605
|
+
const menuH = 200;
|
|
3606
|
+
const x = Math.max(8, Math.min(e.clientX, vw - menuW - 8));
|
|
3607
|
+
const y = Math.max(8, Math.min(e.clientY, vh - menuH - 8));
|
|
3608
|
+
setImageMenu({ x, y, img: target });
|
|
3609
|
+
setTableMenu(null);
|
|
3610
|
+
return;
|
|
3611
|
+
}
|
|
3612
|
+
const cell = getClosestCell(e.target);
|
|
3613
|
+
if (cell) {
|
|
3614
|
+
e.preventDefault();
|
|
3615
|
+
const vw = window.innerWidth;
|
|
3616
|
+
const vh = window.innerHeight;
|
|
3617
|
+
const menuW = 220;
|
|
3618
|
+
const menuH = 300;
|
|
3619
|
+
const x = Math.max(8, Math.min(e.clientX, vw - menuW - 8));
|
|
3620
|
+
const y = Math.max(8, Math.min(e.clientY, vh - menuH - 8));
|
|
3621
|
+
setTableMenu({ x, y, cell });
|
|
3622
|
+
}
|
|
3623
|
+
else {
|
|
3624
|
+
setTableMenu(null);
|
|
3625
|
+
setImageMenu(null);
|
|
3626
|
+
}
|
|
3627
|
+
} }), selectedImage && imageOverlay && (_jsxs("div", { style: {
|
|
3360
3628
|
position: "absolute",
|
|
3361
|
-
|
|
3362
|
-
top:
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3629
|
+
left: imageOverlay.left,
|
|
3630
|
+
top: imageOverlay.top,
|
|
3631
|
+
width: imageOverlay.width,
|
|
3632
|
+
height: imageOverlay.height,
|
|
3633
|
+
pointerEvents: "none",
|
|
3634
|
+
zIndex: 5,
|
|
3635
|
+
}, children: [_jsx("div", { style: {
|
|
3636
|
+
position: "absolute",
|
|
3637
|
+
inset: 0,
|
|
3638
|
+
outline: "2px solid var(--srte-accent)",
|
|
3639
|
+
outlineOffset: -2,
|
|
3640
|
+
} }), _jsx("div", { title: "Resize", onMouseDown: (e) => {
|
|
3641
|
+
e.preventDefault();
|
|
3642
|
+
if (!selectedImage)
|
|
3643
|
+
return;
|
|
3644
|
+
resizingRef.current = {
|
|
3645
|
+
side: "left",
|
|
3646
|
+
startX: e.clientX,
|
|
3647
|
+
startWidth: selectedImage.getBoundingClientRect().width,
|
|
3648
|
+
};
|
|
3649
|
+
const onMove = (ev) => {
|
|
3650
|
+
const info = resizingRef.current;
|
|
3651
|
+
if (!info || !selectedImage)
|
|
3652
|
+
return;
|
|
3653
|
+
const delta = info.startX - ev.clientX;
|
|
3654
|
+
const next = Math.max(80, Math.round(info.startWidth + delta));
|
|
3655
|
+
selectedImage.style.width = next + "px";
|
|
3656
|
+
selectedImage.style.height = "auto";
|
|
3657
|
+
scheduleImageOverlay();
|
|
3658
|
+
};
|
|
3659
|
+
const onUp = () => {
|
|
3660
|
+
window.removeEventListener("mousemove", onMove);
|
|
3661
|
+
window.removeEventListener("mouseup", onUp);
|
|
3662
|
+
resizingRef.current = null;
|
|
3663
|
+
handleInput();
|
|
3664
|
+
};
|
|
3665
|
+
window.addEventListener("mousemove", onMove);
|
|
3666
|
+
window.addEventListener("mouseup", onUp);
|
|
3667
|
+
}, style: {
|
|
3668
|
+
position: "absolute",
|
|
3669
|
+
left: -4,
|
|
3670
|
+
top: "50%",
|
|
3671
|
+
transform: "translateY(-50%)",
|
|
3672
|
+
width: 8,
|
|
3673
|
+
height: 24,
|
|
3674
|
+
background: "var(--srte-accent)",
|
|
3675
|
+
borderRadius: 2,
|
|
3676
|
+
cursor: "ew-resize",
|
|
3677
|
+
pointerEvents: "auto",
|
|
3678
|
+
} }), _jsx("div", { title: "Resize", onMouseDown: (e) => {
|
|
3679
|
+
e.preventDefault();
|
|
3680
|
+
if (!selectedImage)
|
|
3681
|
+
return;
|
|
3682
|
+
resizingRef.current = {
|
|
3683
|
+
side: "right",
|
|
3684
|
+
startX: e.clientX,
|
|
3685
|
+
startWidth: selectedImage.getBoundingClientRect().width,
|
|
3686
|
+
};
|
|
3687
|
+
const onMove = (ev) => {
|
|
3688
|
+
const info = resizingRef.current;
|
|
3689
|
+
if (!info || !selectedImage)
|
|
3690
|
+
return;
|
|
3691
|
+
const delta = ev.clientX - info.startX;
|
|
3692
|
+
const next = Math.max(80, Math.round(info.startWidth + delta));
|
|
3693
|
+
selectedImage.style.width = next + "px";
|
|
3694
|
+
selectedImage.style.height = "auto";
|
|
3695
|
+
scheduleImageOverlay();
|
|
3696
|
+
};
|
|
3697
|
+
const onUp = () => {
|
|
3698
|
+
window.removeEventListener("mousemove", onMove);
|
|
3699
|
+
window.removeEventListener("mouseup", onUp);
|
|
3700
|
+
resizingRef.current = null;
|
|
3701
|
+
handleInput();
|
|
3702
|
+
};
|
|
3703
|
+
window.addEventListener("mousemove", onMove);
|
|
3704
|
+
window.addEventListener("mouseup", onUp);
|
|
3705
|
+
}, style: {
|
|
3706
|
+
position: "absolute",
|
|
3707
|
+
right: -4,
|
|
3708
|
+
top: "50%",
|
|
3709
|
+
transform: "translateY(-50%)",
|
|
3710
|
+
width: 8,
|
|
3711
|
+
height: 24,
|
|
3712
|
+
background: "var(--srte-accent)",
|
|
3713
|
+
borderRadius: 2,
|
|
3714
|
+
cursor: "ew-resize",
|
|
3715
|
+
pointerEvents: "auto",
|
|
3716
|
+
} })] }))] }), tableMenu && (_jsx("div", { style: {
|
|
3371
3717
|
position: "fixed",
|
|
3372
3718
|
inset: 0,
|
|
3373
3719
|
zIndex: 60,
|
|
3720
|
+
background: "transparent",
|
|
3374
3721
|
}, onClick: () => setTableMenu(null), onContextMenu: (e) => {
|
|
3375
3722
|
// Prevent native menu while overlay is shown and reposition our menu
|
|
3376
3723
|
e.preventDefault();
|
|
@@ -3402,56 +3749,53 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3402
3749
|
maxHeight: 260,
|
|
3403
3750
|
overflowY: "auto",
|
|
3404
3751
|
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: {
|
|
3752
|
+
}, 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
3753
|
display: "flex",
|
|
3413
3754
|
gap: 8,
|
|
3414
3755
|
alignItems: "center",
|
|
3415
3756
|
padding: "4px 6px",
|
|
3416
3757
|
fontSize: 12,
|
|
3417
3758
|
}, children: [_jsx("span", { children: "Fill:" }), _jsx("input", { type: "color", defaultValue: "#ffffff", onChange: (e) => {
|
|
3418
|
-
applyBgToSelection(e.target.value,
|
|
3419
|
-
setTableMenu(null);
|
|
3759
|
+
runTableCellAction(tableMenu.cell, (cell) => applyBgToSelection(e.target.value, cell));
|
|
3420
3760
|
}, style: {
|
|
3421
3761
|
width: 28,
|
|
3422
3762
|
height: 18,
|
|
3423
3763
|
padding: 0,
|
|
3424
3764
|
border: "none",
|
|
3425
3765
|
background: "transparent",
|
|
3426
|
-
} })] }), _jsxs("button", { style: {
|
|
3766
|
+
} })] }), _jsxs("button", { title: "Show or hide border for this cell, or for the selected cell range.", style: {
|
|
3427
3767
|
display: "flex",
|
|
3428
3768
|
alignItems: "center",
|
|
3429
3769
|
gap: 8,
|
|
3430
3770
|
padding: "6px 8px",
|
|
3431
3771
|
fontSize: 12,
|
|
3432
3772
|
}, 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: {
|
|
3773
|
+
runTableCellAction(tableMenu.cell, toggleBorderSelection);
|
|
3774
|
+
}, 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
3775
|
display: "flex",
|
|
3437
3776
|
alignItems: "center",
|
|
3438
3777
|
gap: 8,
|
|
3439
3778
|
padding: "6px 8px",
|
|
3440
3779
|
fontSize: 12,
|
|
3441
|
-
opacity:
|
|
3442
|
-
cursor:
|
|
3780
|
+
opacity: canMergeFromCell(tableMenu.cell) ? 1 : 0.45,
|
|
3781
|
+
cursor: canMergeFromCell(tableMenu.cell) ? "pointer" : "not-allowed",
|
|
3443
3782
|
}, onClick: () => {
|
|
3783
|
+
if (!canMergeFromCell(tableMenu.cell))
|
|
3784
|
+
return;
|
|
3444
3785
|
mergeSelection();
|
|
3445
3786
|
setTableMenu(null);
|
|
3446
|
-
}, children: [_jsx("span", { children: "\u21C4" }), _jsx("span", { children: "Merge cells" })] }), _jsxs("button", { style: {
|
|
3787
|
+
}, 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
3788
|
display: "flex",
|
|
3448
3789
|
alignItems: "center",
|
|
3449
3790
|
gap: 8,
|
|
3450
3791
|
padding: "6px 8px",
|
|
3451
3792
|
fontSize: 12,
|
|
3793
|
+
opacity: canSplitCell(tableMenu.cell) ? 1 : 0.45,
|
|
3794
|
+
cursor: canSplitCell(tableMenu.cell) ? "pointer" : "not-allowed",
|
|
3452
3795
|
}, onClick: () => {
|
|
3453
|
-
|
|
3454
|
-
|
|
3796
|
+
if (!canSplitCell(tableMenu.cell))
|
|
3797
|
+
return;
|
|
3798
|
+
runTableCellAction(tableMenu.cell, splitCell);
|
|
3455
3799
|
}, children: [_jsx("span", { children: "\u2922" }), _jsx("span", { children: "Split cell" })] }), _jsx("hr", { style: { margin: "4px 0" } }), _jsxs("button", { style: {
|
|
3456
3800
|
display: "flex",
|
|
3457
3801
|
alignItems: "center",
|
|
@@ -3459,8 +3803,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3459
3803
|
padding: "6px 8px",
|
|
3460
3804
|
fontSize: 12,
|
|
3461
3805
|
}, onClick: () => {
|
|
3462
|
-
|
|
3463
|
-
setTableMenu(null);
|
|
3806
|
+
runTableCellAction(tableMenu.cell, (cell) => addRow(cell, "above"));
|
|
3464
3807
|
}, children: [_jsx("span", { children: "\u21A5" }), _jsx("span", { children: "Row above" })] }), _jsxs("button", { style: {
|
|
3465
3808
|
display: "flex",
|
|
3466
3809
|
alignItems: "center",
|
|
@@ -3468,8 +3811,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3468
3811
|
padding: "6px 8px",
|
|
3469
3812
|
fontSize: 12,
|
|
3470
3813
|
}, onClick: () => {
|
|
3471
|
-
|
|
3472
|
-
setTableMenu(null);
|
|
3814
|
+
runTableCellAction(tableMenu.cell, (cell) => addRow(cell, "below"));
|
|
3473
3815
|
}, children: [_jsx("span", { children: "\u21A7" }), _jsx("span", { children: "Row below" })] }), _jsxs("button", { style: {
|
|
3474
3816
|
display: "flex",
|
|
3475
3817
|
alignItems: "center",
|
|
@@ -3477,8 +3819,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3477
3819
|
padding: "6px 8px",
|
|
3478
3820
|
fontSize: 12,
|
|
3479
3821
|
}, onClick: () => {
|
|
3480
|
-
|
|
3481
|
-
setTableMenu(null);
|
|
3822
|
+
runTableCellAction(tableMenu.cell, (cell) => addCol(cell, "left"));
|
|
3482
3823
|
}, children: [_jsx("span", { children: "\u2190" }), _jsx("span", { children: "Column left" })] }), _jsxs("button", { style: {
|
|
3483
3824
|
display: "flex",
|
|
3484
3825
|
alignItems: "center",
|
|
@@ -3486,8 +3827,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3486
3827
|
padding: "6px 8px",
|
|
3487
3828
|
fontSize: 12,
|
|
3488
3829
|
}, onClick: () => {
|
|
3489
|
-
|
|
3490
|
-
setTableMenu(null);
|
|
3830
|
+
runTableCellAction(tableMenu.cell, (cell) => addCol(cell, "right"));
|
|
3491
3831
|
}, children: [_jsx("span", { children: "\u2192" }), _jsx("span", { children: "Column right" })] }), _jsxs("button", { style: {
|
|
3492
3832
|
display: "flex",
|
|
3493
3833
|
alignItems: "center",
|
|
@@ -3495,8 +3835,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3495
3835
|
padding: "6px 8px",
|
|
3496
3836
|
fontSize: 12,
|
|
3497
3837
|
}, onClick: () => {
|
|
3498
|
-
|
|
3499
|
-
setTableMenu(null);
|
|
3838
|
+
runTableCellAction(tableMenu.cell, deleteRow);
|
|
3500
3839
|
}, children: [_jsx("span", { children: "\u2716" }), _jsx("span", { children: "Delete row" })] }), _jsxs("button", { style: {
|
|
3501
3840
|
display: "flex",
|
|
3502
3841
|
alignItems: "center",
|
|
@@ -3504,8 +3843,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3504
3843
|
padding: "6px 8px",
|
|
3505
3844
|
fontSize: 12,
|
|
3506
3845
|
}, onClick: () => {
|
|
3507
|
-
|
|
3508
|
-
setTableMenu(null);
|
|
3846
|
+
runTableCellAction(tableMenu.cell, deleteCol);
|
|
3509
3847
|
}, children: [_jsx("span", { children: "\u2716" }), _jsx("span", { children: "Delete column" })] }), _jsxs("button", { style: {
|
|
3510
3848
|
display: "flex",
|
|
3511
3849
|
alignItems: "center",
|
|
@@ -3513,36 +3851,37 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
|
|
|
3513
3851
|
padding: "6px 8px",
|
|
3514
3852
|
fontSize: 12,
|
|
3515
3853
|
}, onClick: () => {
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
}, children: [_jsx("span", { children: "H" }), _jsx("span", { children: "Toggle header" })] }), _jsxs("button", { style: {
|
|
3854
|
+
runTableCellAction(tableMenu.cell, toggleHeaderCell);
|
|
3855
|
+
}, children: [_jsx("span", { children: "H" }), _jsx("span", { children: tableMenu.cell.tagName === "TH" ? "Remove cell header" : "Make cell header" })] }), _jsxs("button", { style: {
|
|
3519
3856
|
display: "flex",
|
|
3520
3857
|
alignItems: "center",
|
|
3521
3858
|
gap: 8,
|
|
3522
3859
|
padding: "6px 8px",
|
|
3523
3860
|
fontSize: 12,
|
|
3524
3861
|
}, onClick: () => {
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
}, children: [_jsx("span", { children: "H\u2081" }), _jsx("span", { children: "Toggle header row" })] }), _jsxs("button", { style: {
|
|
3862
|
+
runTableCellAction(tableMenu.cell, toggleHeaderRow);
|
|
3863
|
+
}, children: [_jsx("span", { children: "H\u2081" }), _jsx("span", { children: "Make this row header" })] }), _jsxs("button", { style: {
|
|
3528
3864
|
display: "flex",
|
|
3529
3865
|
alignItems: "center",
|
|
3530
3866
|
gap: 8,
|
|
3531
3867
|
padding: "6px 8px",
|
|
3532
3868
|
fontSize: 12,
|
|
3533
3869
|
}, 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: {
|
|
3870
|
+
runTableCellAction(tableMenu.cell, toggleHeaderColumn);
|
|
3871
|
+
}, children: [_jsx("span", { children: "H\u2195" }), _jsx("span", { children: "Make this column header" })] }), _jsx("hr", { style: { margin: "4px 0" } }), _jsxs("button", { style: {
|
|
3537
3872
|
display: "flex",
|
|
3538
3873
|
alignItems: "center",
|
|
3539
3874
|
gap: 8,
|
|
3540
3875
|
padding: "6px 8px",
|
|
3541
3876
|
fontSize: 12,
|
|
3542
3877
|
}, onClick: () => {
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3878
|
+
runTableCellAction(tableMenu.cell, deleteTable);
|
|
3879
|
+
}, children: [_jsx("span", { children: "\uD83D\uDDD1" }), _jsx("span", { children: "Delete table" })] })] })] }) })), imageMenu && (_jsx("div", { style: {
|
|
3880
|
+
position: "fixed",
|
|
3881
|
+
inset: 0,
|
|
3882
|
+
zIndex: 60,
|
|
3883
|
+
background: "transparent",
|
|
3884
|
+
}, onClick: () => setImageMenu(null), onContextMenu: (e) => {
|
|
3546
3885
|
e.preventDefault();
|
|
3547
3886
|
const vw = window.innerWidth;
|
|
3548
3887
|
const vh = window.innerHeight;
|