@sofereditor/react 0.4.0 → 0.6.0
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/index.cjs +811 -157
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -2
- package/dist/index.d.ts +40 -2
- package/dist/index.js +701 -48
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -38,6 +38,7 @@ __export(index_exports, {
|
|
|
38
38
|
getBlockElement: () => getBlockElement,
|
|
39
39
|
getCellElement: () => getCellElement,
|
|
40
40
|
getFragmentForOffset: () => getFragmentForOffset,
|
|
41
|
+
htmlToSlice: () => htmlToSlice,
|
|
41
42
|
isTableRectSelection: () => isTableRectSelection,
|
|
42
43
|
locatePoint: () => locatePoint,
|
|
43
44
|
readDomSelection: () => readDomSelection,
|
|
@@ -54,7 +55,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
54
55
|
|
|
55
56
|
// src/Editor.tsx
|
|
56
57
|
var import_react14 = require("react");
|
|
57
|
-
var
|
|
58
|
+
var import_core8 = require("@sofereditor/core");
|
|
58
59
|
|
|
59
60
|
// src/dom-bridge.ts
|
|
60
61
|
function findContainingBlockElement(node, root) {
|
|
@@ -329,6 +330,615 @@ function applyDomSelection(root, modelSel) {
|
|
|
329
330
|
}
|
|
330
331
|
}
|
|
331
332
|
|
|
333
|
+
// src/pastePlan.ts
|
|
334
|
+
var import_core2 = require("@sofereditor/core");
|
|
335
|
+
|
|
336
|
+
// src/htmlToSlice.ts
|
|
337
|
+
var import_core = require("@sofereditor/core");
|
|
338
|
+
function htmlToSlice(html) {
|
|
339
|
+
if (!html || html.trim().length === 0) return null;
|
|
340
|
+
let doc;
|
|
341
|
+
try {
|
|
342
|
+
doc = new DOMParser().parseFromString(html, "text/html");
|
|
343
|
+
} catch {
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
const body = doc.body;
|
|
347
|
+
if (!body) return null;
|
|
348
|
+
const blocks = [];
|
|
349
|
+
try {
|
|
350
|
+
walkBlockLevel(body, {}, blocks);
|
|
351
|
+
} catch {
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
if (blocks.length === 0) return null;
|
|
355
|
+
normalizeListLevels(blocks);
|
|
356
|
+
const temTexto = blocks.some((b) => b.text.trim().length > 0);
|
|
357
|
+
if (!temTexto) return null;
|
|
358
|
+
const blockLevel = blocks.length > 1 || blocks[0].type !== "paragraph";
|
|
359
|
+
return { blocks, openStart: false, openEnd: false, ...blockLevel ? { blockLevel: true } : {} };
|
|
360
|
+
}
|
|
361
|
+
var IGNORED_TAGS = /* @__PURE__ */ new Set(["script", "style", "meta", "link", "head", "title", "xml"]);
|
|
362
|
+
var BLOCK_LEVEL_TAGS = /* @__PURE__ */ new Set([
|
|
363
|
+
"p",
|
|
364
|
+
"div",
|
|
365
|
+
"section",
|
|
366
|
+
"article",
|
|
367
|
+
"header",
|
|
368
|
+
"footer",
|
|
369
|
+
"main",
|
|
370
|
+
"figure",
|
|
371
|
+
"figcaption",
|
|
372
|
+
"pre",
|
|
373
|
+
"address"
|
|
374
|
+
]);
|
|
375
|
+
var BLOCK_TRIGGER_TAGS = /* @__PURE__ */ new Set([
|
|
376
|
+
"p",
|
|
377
|
+
"div",
|
|
378
|
+
"ul",
|
|
379
|
+
"ol",
|
|
380
|
+
"li",
|
|
381
|
+
"h1",
|
|
382
|
+
"h2",
|
|
383
|
+
"h3",
|
|
384
|
+
"h4",
|
|
385
|
+
"h5",
|
|
386
|
+
"h6",
|
|
387
|
+
"blockquote",
|
|
388
|
+
"table",
|
|
389
|
+
"tr",
|
|
390
|
+
"td",
|
|
391
|
+
"th"
|
|
392
|
+
]);
|
|
393
|
+
function isNamespacedTag(tag) {
|
|
394
|
+
return tag.includes(":");
|
|
395
|
+
}
|
|
396
|
+
function hasBlockDescendant(el) {
|
|
397
|
+
const all = el.querySelectorAll("*");
|
|
398
|
+
for (const e of Array.from(all)) {
|
|
399
|
+
if (BLOCK_TRIGGER_TAGS.has(e.tagName.toLowerCase())) return true;
|
|
400
|
+
}
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
function walkBlockLevel(container, marks, blocks) {
|
|
404
|
+
let loose = [];
|
|
405
|
+
const flushLoose = () => {
|
|
406
|
+
if (loose.length === 0) return;
|
|
407
|
+
const nodes = loose;
|
|
408
|
+
loose = [];
|
|
409
|
+
const delta = inlineDeltaFromNodes(nodes, marks);
|
|
410
|
+
if (delta.length > 0) blocks.push(makeBlock("paragraph", delta, {}));
|
|
411
|
+
};
|
|
412
|
+
for (const node of Array.from(container.childNodes)) {
|
|
413
|
+
if (node.nodeType === Node.COMMENT_NODE) continue;
|
|
414
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
415
|
+
loose.push(node);
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
if (node.nodeType !== Node.ELEMENT_NODE) continue;
|
|
419
|
+
const el = node;
|
|
420
|
+
const tag = el.tagName.toLowerCase();
|
|
421
|
+
if (IGNORED_TAGS.has(tag) || isNamespacedTag(tag)) continue;
|
|
422
|
+
switch (tag) {
|
|
423
|
+
case "ul":
|
|
424
|
+
case "ol": {
|
|
425
|
+
flushLoose();
|
|
426
|
+
handleList(el, tag === "ol" ? "ordered" : "bullet", 0, marks, blocks);
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
case "h1":
|
|
430
|
+
case "h2":
|
|
431
|
+
case "h3":
|
|
432
|
+
case "h4":
|
|
433
|
+
case "h5":
|
|
434
|
+
case "h6": {
|
|
435
|
+
flushLoose();
|
|
436
|
+
const level = Number(tag[1]);
|
|
437
|
+
const attrs = { level };
|
|
438
|
+
const align = readAlign(el);
|
|
439
|
+
if (align) attrs.align = align;
|
|
440
|
+
const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);
|
|
441
|
+
blocks.push(makeBlock("heading", delta, attrs));
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
case "blockquote": {
|
|
445
|
+
flushLoose();
|
|
446
|
+
handleBlockquote(el, marks, blocks);
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
case "table": {
|
|
450
|
+
flushLoose();
|
|
451
|
+
handleTable(el, marks, blocks);
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
case "li": {
|
|
455
|
+
flushLoose();
|
|
456
|
+
const nestedLists = topLevelListDescendants(el);
|
|
457
|
+
const clone = el.cloneNode(true);
|
|
458
|
+
for (const l of topLevelListDescendants(clone)) l.remove();
|
|
459
|
+
const delta = inlineDeltaFromNodes(Array.from(clone.childNodes), marks);
|
|
460
|
+
blocks.push(makeBlock("listItem", delta, { listKind: "bullet", listLevel: 0 }));
|
|
461
|
+
for (const nested of nestedLists) {
|
|
462
|
+
const nestedKind = nested.tagName.toLowerCase() === "ol" ? "ordered" : "bullet";
|
|
463
|
+
handleList(nested, nestedKind, 1, marks, blocks);
|
|
464
|
+
}
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
case "img":
|
|
468
|
+
break;
|
|
469
|
+
case "br":
|
|
470
|
+
loose.push(node);
|
|
471
|
+
break;
|
|
472
|
+
default: {
|
|
473
|
+
const styleAttr = el.getAttribute("style") ?? "";
|
|
474
|
+
const wordListLevel = parseWordListLevel(styleAttr);
|
|
475
|
+
if (wordListLevel != null && !hasBlockDescendant(el)) {
|
|
476
|
+
flushLoose();
|
|
477
|
+
const marker = findWordListMarker(el);
|
|
478
|
+
const listKind = marker ? detectWordListKind(marker) : "bullet";
|
|
479
|
+
const attrs = { listKind, listLevel: wordListLevel };
|
|
480
|
+
const align = readAlign(el);
|
|
481
|
+
if (align) attrs.align = align;
|
|
482
|
+
const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);
|
|
483
|
+
blocks.push(makeBlock("listItem", delta, attrs));
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
if (hasBlockDescendant(el)) {
|
|
487
|
+
flushLoose();
|
|
488
|
+
let nextMarks = applyTagMarks(tag, marks);
|
|
489
|
+
nextMarks = applyStyleMarks(el.getAttribute("style"), nextMarks);
|
|
490
|
+
walkBlockLevel(el, nextMarks, blocks);
|
|
491
|
+
break;
|
|
492
|
+
}
|
|
493
|
+
if (BLOCK_LEVEL_TAGS.has(tag)) {
|
|
494
|
+
flushLoose();
|
|
495
|
+
const attrs = {};
|
|
496
|
+
const align = readAlign(el);
|
|
497
|
+
if (align) attrs.align = align;
|
|
498
|
+
const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);
|
|
499
|
+
blocks.push(makeBlock("paragraph", delta, attrs));
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
loose.push(node);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
flushLoose();
|
|
507
|
+
}
|
|
508
|
+
function handleList(listEl, kind, level, marks, blocks) {
|
|
509
|
+
for (const child of Array.from(listEl.children)) {
|
|
510
|
+
const tag = child.tagName.toLowerCase();
|
|
511
|
+
if (tag === "ul" || tag === "ol") {
|
|
512
|
+
const siblingKind = tag === "ol" ? "ordered" : "bullet";
|
|
513
|
+
handleList(child, siblingKind, level + 1, marks, blocks);
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
if (tag !== "li") continue;
|
|
517
|
+
const nestedLists = topLevelListDescendants(child);
|
|
518
|
+
const clone = child.cloneNode(true);
|
|
519
|
+
for (const l of topLevelListDescendants(clone)) l.remove();
|
|
520
|
+
const delta = inlineDeltaFromNodes(Array.from(clone.childNodes), marks);
|
|
521
|
+
const attrs = { listKind: kind, listLevel: clampLevel(level) };
|
|
522
|
+
const align = readAlign(child);
|
|
523
|
+
if (align) attrs.align = align;
|
|
524
|
+
blocks.push(makeBlock("listItem", delta, attrs));
|
|
525
|
+
for (const nested of nestedLists) {
|
|
526
|
+
const nestedKind = nested.tagName.toLowerCase() === "ol" ? "ordered" : "bullet";
|
|
527
|
+
handleList(nested, nestedKind, level + 1, marks, blocks);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
function topLevelListDescendants(root) {
|
|
532
|
+
const all = Array.from(root.querySelectorAll("ul, ol"));
|
|
533
|
+
return all.filter((el) => {
|
|
534
|
+
let p = el.parentElement;
|
|
535
|
+
while (p && p !== root) {
|
|
536
|
+
const t = p.tagName.toLowerCase();
|
|
537
|
+
if (t === "ul" || t === "ol" || t === "table" || t === "td" || t === "th") return false;
|
|
538
|
+
p = p.parentElement;
|
|
539
|
+
}
|
|
540
|
+
return true;
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
function handleBlockquote(el, marks, blocks) {
|
|
544
|
+
if (!hasBlockDescendant(el)) {
|
|
545
|
+
const attrs = {};
|
|
546
|
+
const align = readAlign(el);
|
|
547
|
+
if (align) attrs.align = align;
|
|
548
|
+
const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);
|
|
549
|
+
blocks.push(makeBlock("blockquote", delta, attrs));
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
const nested = [];
|
|
553
|
+
walkBlockLevel(el, marks, nested);
|
|
554
|
+
for (const b of nested) {
|
|
555
|
+
blocks.push(b.type === "paragraph" ? { ...b, type: "blockquote" } : b);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
function handleTable(table, marks, blocks) {
|
|
559
|
+
for (const row of directRows(table)) {
|
|
560
|
+
const cells = Array.from(row.children).filter(
|
|
561
|
+
(c) => ["td", "th"].includes(c.tagName.toLowerCase())
|
|
562
|
+
);
|
|
563
|
+
for (const cell of cells) {
|
|
564
|
+
const cellBlocks = [];
|
|
565
|
+
walkBlockLevel(cell, marks, cellBlocks);
|
|
566
|
+
for (const b of cellBlocks) {
|
|
567
|
+
const attrs = {};
|
|
568
|
+
if (b.attrs.align) attrs.align = b.attrs.align;
|
|
569
|
+
blocks.push(makeBlock("paragraph", b.delta, attrs));
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
function directRows(table) {
|
|
575
|
+
const rows = [];
|
|
576
|
+
for (const child of Array.from(table.children)) {
|
|
577
|
+
const tag = child.tagName.toLowerCase();
|
|
578
|
+
if (tag === "tr") {
|
|
579
|
+
rows.push(child);
|
|
580
|
+
} else if (tag === "tbody" || tag === "thead" || tag === "tfoot") {
|
|
581
|
+
for (const r of Array.from(child.children)) {
|
|
582
|
+
if (r.tagName.toLowerCase() === "tr") rows.push(r);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return rows;
|
|
587
|
+
}
|
|
588
|
+
function parseWordListLevel(styleAttr) {
|
|
589
|
+
const m = /mso-list\s*:\s*l\d+\s+level(\d+)/i.exec(styleAttr);
|
|
590
|
+
if (!m) return null;
|
|
591
|
+
const n = Number.parseInt(m[1], 10);
|
|
592
|
+
const fromLevelN = Number.isFinite(n) ? Math.max(0, n - 1) : 0;
|
|
593
|
+
const fromMargin = wordListLevelFromMarginLeft(marginLeftPt(styleAttr));
|
|
594
|
+
return clampLevel(Math.max(fromLevelN, fromMargin));
|
|
595
|
+
}
|
|
596
|
+
function marginLeftPt(styleAttr) {
|
|
597
|
+
const raw = parseStyle(styleAttr)["margin-left"];
|
|
598
|
+
if (!raw) return 0;
|
|
599
|
+
const m = /^(-?[\d.]+)\s*(pt|cm|in|px)$/i.exec(raw.trim());
|
|
600
|
+
if (!m) return 0;
|
|
601
|
+
const val = Number.parseFloat(m[1]);
|
|
602
|
+
if (!Number.isFinite(val) || val < 0) return 0;
|
|
603
|
+
switch (m[2].toLowerCase()) {
|
|
604
|
+
case "pt":
|
|
605
|
+
return val;
|
|
606
|
+
case "cm":
|
|
607
|
+
return val * 28.3465;
|
|
608
|
+
case "in":
|
|
609
|
+
return val * 72;
|
|
610
|
+
case "px":
|
|
611
|
+
return val * 0.75;
|
|
612
|
+
default:
|
|
613
|
+
return 0;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
function wordListLevelFromMarginLeft(pt) {
|
|
617
|
+
if (pt <= 0) return 0;
|
|
618
|
+
return Math.max(0, Math.round(pt / 36) - 1);
|
|
619
|
+
}
|
|
620
|
+
function findWordListMarker(el) {
|
|
621
|
+
const all = el.querySelectorAll("*");
|
|
622
|
+
for (const e of Array.from(all)) {
|
|
623
|
+
const style = (e.getAttribute("style") ?? "").toLowerCase().replace(/\s+/g, "");
|
|
624
|
+
if (style.includes("mso-list:ignore")) return e;
|
|
625
|
+
}
|
|
626
|
+
return null;
|
|
627
|
+
}
|
|
628
|
+
function detectWordListKind(markerEl) {
|
|
629
|
+
const all = [markerEl, ...Array.from(markerEl.querySelectorAll("*"))];
|
|
630
|
+
for (const e of all) {
|
|
631
|
+
const style = (e.getAttribute("style") ?? "").toLowerCase();
|
|
632
|
+
if (style.includes("symbol") || style.includes("wingdings")) return "bullet";
|
|
633
|
+
}
|
|
634
|
+
const text = (markerEl.textContent ?? "").replace(/ /g, " ").trim();
|
|
635
|
+
if (/^[0-9]+[.)]/.test(text)) return "ordered";
|
|
636
|
+
if (/^[a-zA-Z][.)]/.test(text)) return "ordered";
|
|
637
|
+
return "bullet";
|
|
638
|
+
}
|
|
639
|
+
function inlineDeltaFromNodes(nodes, marks) {
|
|
640
|
+
const ops = [];
|
|
641
|
+
for (const n of nodes) walkInlineNode(n, marks, ops);
|
|
642
|
+
return trimEdgeWhitespace(collapseRepeatedSpaces(mergeAdjacentOps(ops)));
|
|
643
|
+
}
|
|
644
|
+
function collapseRepeatedSpaces(ops) {
|
|
645
|
+
return ops.map(
|
|
646
|
+
(op) => typeof op.insert === "string" ? { ...op, insert: op.insert.replace(/ {2,}/g, " ") } : op
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
function walkInlineNode(node, marks, ops) {
|
|
650
|
+
if (node.nodeType === Node.COMMENT_NODE) return;
|
|
651
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
652
|
+
const text = normalizeWhitespace(node.textContent ?? "");
|
|
653
|
+
if (text.length > 0) pushOp(ops, text, marks);
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
657
|
+
const el = node;
|
|
658
|
+
const tag = el.tagName.toLowerCase();
|
|
659
|
+
if (IGNORED_TAGS.has(tag) || isNamespacedTag(tag)) return;
|
|
660
|
+
if (tag === "img") return;
|
|
661
|
+
if (tag === "br") {
|
|
662
|
+
ops.push({ insert: " " });
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
const styleAttr = (el.getAttribute("style") ?? "").toLowerCase().replace(/\s+/g, "");
|
|
666
|
+
if (styleAttr.includes("mso-list:ignore")) return;
|
|
667
|
+
let nextMarks = applyTagMarks(tag, marks);
|
|
668
|
+
nextMarks = applyStyleMarks(el.getAttribute("style"), nextMarks);
|
|
669
|
+
if (tag === "a") {
|
|
670
|
+
const href = el.getAttribute("href");
|
|
671
|
+
if (href) nextMarks = { ...nextMarks, link: { href } };
|
|
672
|
+
}
|
|
673
|
+
for (const child of Array.from(el.childNodes)) walkInlineNode(child, nextMarks, ops);
|
|
674
|
+
}
|
|
675
|
+
function applyTagMarks(tag, marks) {
|
|
676
|
+
switch (tag) {
|
|
677
|
+
case "b":
|
|
678
|
+
case "strong":
|
|
679
|
+
return { ...marks, bold: true };
|
|
680
|
+
case "i":
|
|
681
|
+
case "em":
|
|
682
|
+
return { ...marks, italic: true };
|
|
683
|
+
case "u":
|
|
684
|
+
return { ...marks, underline: true };
|
|
685
|
+
case "s":
|
|
686
|
+
case "strike":
|
|
687
|
+
case "del":
|
|
688
|
+
return { ...marks, strike: true };
|
|
689
|
+
default:
|
|
690
|
+
return marks;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
function parseStyle(styleAttr) {
|
|
694
|
+
const out = {};
|
|
695
|
+
if (!styleAttr) return out;
|
|
696
|
+
for (const decl of styleAttr.split(";")) {
|
|
697
|
+
const idx = decl.indexOf(":");
|
|
698
|
+
if (idx === -1) continue;
|
|
699
|
+
const prop = decl.slice(0, idx).trim().toLowerCase();
|
|
700
|
+
const val = decl.slice(idx + 1).trim();
|
|
701
|
+
if (prop) out[prop] = val;
|
|
702
|
+
}
|
|
703
|
+
return out;
|
|
704
|
+
}
|
|
705
|
+
function applyStyleMarks(styleAttr, marks) {
|
|
706
|
+
const decls = parseStyle(styleAttr);
|
|
707
|
+
if (Object.keys(decls).length === 0) return marks;
|
|
708
|
+
const next = { ...marks };
|
|
709
|
+
if ("font-weight" in decls) {
|
|
710
|
+
const v = decls["font-weight"].toLowerCase();
|
|
711
|
+
if (v === "bold" || /^[5-9]00$/.test(v)) next.bold = true;
|
|
712
|
+
else if (v === "normal" || /^[1-4]00$/.test(v)) delete next.bold;
|
|
713
|
+
}
|
|
714
|
+
if ("font-style" in decls) {
|
|
715
|
+
const v = decls["font-style"].toLowerCase();
|
|
716
|
+
if (v === "italic" || v === "oblique") next.italic = true;
|
|
717
|
+
else if (v === "normal") delete next.italic;
|
|
718
|
+
}
|
|
719
|
+
const deco = decls["text-decoration-line"] ?? decls["text-decoration"];
|
|
720
|
+
if (deco !== void 0) {
|
|
721
|
+
const v = deco.toLowerCase();
|
|
722
|
+
delete next.underline;
|
|
723
|
+
delete next.strike;
|
|
724
|
+
if (v.includes("underline")) next.underline = true;
|
|
725
|
+
if (v.includes("line-through")) next.strike = true;
|
|
726
|
+
}
|
|
727
|
+
if ("color" in decls) {
|
|
728
|
+
const css = normalizeCssColor(decls.color);
|
|
729
|
+
if (css && !isBlack(css)) next.color = css;
|
|
730
|
+
else delete next.color;
|
|
731
|
+
}
|
|
732
|
+
const bg = decls["background-color"] ?? decls.background;
|
|
733
|
+
if (bg !== void 0) {
|
|
734
|
+
const css = normalizeCssColor(bg);
|
|
735
|
+
if (css && css !== "#ffffff") next.highlight = css;
|
|
736
|
+
else delete next.highlight;
|
|
737
|
+
}
|
|
738
|
+
if ("font-size" in decls) {
|
|
739
|
+
const pt = parseFontSizePt(decls["font-size"]);
|
|
740
|
+
if (pt != null) next.fontSize = formatFontSizePt(pt);
|
|
741
|
+
}
|
|
742
|
+
return next;
|
|
743
|
+
}
|
|
744
|
+
function parseFontSizePt(raw) {
|
|
745
|
+
const v = raw.trim().toLowerCase();
|
|
746
|
+
const m = /^(-?[\d.]+)\s*(pt|px|in)$/.exec(v);
|
|
747
|
+
if (!m) return void 0;
|
|
748
|
+
const num = Number.parseFloat(m[1]);
|
|
749
|
+
if (!Number.isFinite(num)) return void 0;
|
|
750
|
+
let pt;
|
|
751
|
+
switch (m[2]) {
|
|
752
|
+
case "pt":
|
|
753
|
+
pt = num;
|
|
754
|
+
break;
|
|
755
|
+
case "px":
|
|
756
|
+
pt = num * 0.75;
|
|
757
|
+
break;
|
|
758
|
+
case "in":
|
|
759
|
+
pt = num * 72;
|
|
760
|
+
break;
|
|
761
|
+
default:
|
|
762
|
+
return void 0;
|
|
763
|
+
}
|
|
764
|
+
const rounded = Math.round(pt * 10) / 10;
|
|
765
|
+
if (rounded <= 0 || rounded > 200) return void 0;
|
|
766
|
+
return rounded;
|
|
767
|
+
}
|
|
768
|
+
function formatFontSizePt(pt) {
|
|
769
|
+
const rounded = Math.round(pt * 10) / 10;
|
|
770
|
+
const str = Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1);
|
|
771
|
+
return `${str}pt`;
|
|
772
|
+
}
|
|
773
|
+
var NAMED_COLORS = {
|
|
774
|
+
black: "#000000",
|
|
775
|
+
white: "#ffffff",
|
|
776
|
+
red: "#ff0000",
|
|
777
|
+
green: "#008000",
|
|
778
|
+
blue: "#0000ff",
|
|
779
|
+
yellow: "#ffff00",
|
|
780
|
+
cyan: "#00ffff",
|
|
781
|
+
magenta: "#ff00ff",
|
|
782
|
+
gray: "#808080",
|
|
783
|
+
grey: "#808080",
|
|
784
|
+
orange: "#ffa500",
|
|
785
|
+
purple: "#800080"
|
|
786
|
+
};
|
|
787
|
+
function normalizeCssColor(raw) {
|
|
788
|
+
if (!raw) return void 0;
|
|
789
|
+
const v = raw.trim().toLowerCase();
|
|
790
|
+
if (!v || v === "transparent" || v === "none" || v === "inherit" || v === "initial") return void 0;
|
|
791
|
+
if (v.startsWith("#")) {
|
|
792
|
+
let hex = v.slice(1);
|
|
793
|
+
if (hex.length === 3) hex = hex.split("").map((c) => c + c).join("");
|
|
794
|
+
return /^[0-9a-f]{6}$/.test(hex) ? `#${hex}` : void 0;
|
|
795
|
+
}
|
|
796
|
+
const rgbMatch = /^rgba?\(([^)]+)\)$/.exec(v);
|
|
797
|
+
if (rgbMatch) {
|
|
798
|
+
const parts = rgbMatch[1].split(",").map((s) => s.trim());
|
|
799
|
+
if (parts.length < 3) return void 0;
|
|
800
|
+
const alpha = parts[3] !== void 0 ? Number.parseFloat(parts[3]) : 1;
|
|
801
|
+
if (alpha === 0) return void 0;
|
|
802
|
+
const toHex = (n) => {
|
|
803
|
+
const num = Math.max(0, Math.min(255, Number.parseInt(n, 10) || 0));
|
|
804
|
+
return num.toString(16).padStart(2, "0");
|
|
805
|
+
};
|
|
806
|
+
return `#${toHex(parts[0])}${toHex(parts[1])}${toHex(parts[2])}`;
|
|
807
|
+
}
|
|
808
|
+
return NAMED_COLORS[v];
|
|
809
|
+
}
|
|
810
|
+
function isBlack(hex) {
|
|
811
|
+
return hex.toLowerCase() === "#000000";
|
|
812
|
+
}
|
|
813
|
+
function readAlign(el) {
|
|
814
|
+
const style = parseStyle(el.getAttribute("style"));
|
|
815
|
+
const v = style["text-align"];
|
|
816
|
+
if (!v) return void 0;
|
|
817
|
+
switch (v.toLowerCase()) {
|
|
818
|
+
case "left":
|
|
819
|
+
case "start":
|
|
820
|
+
return "left";
|
|
821
|
+
case "center":
|
|
822
|
+
return "center";
|
|
823
|
+
case "right":
|
|
824
|
+
case "end":
|
|
825
|
+
return "right";
|
|
826
|
+
case "justify":
|
|
827
|
+
return "justify";
|
|
828
|
+
default:
|
|
829
|
+
return void 0;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
function clampLevel(n) {
|
|
833
|
+
if (!Number.isFinite(n)) return 0;
|
|
834
|
+
return Math.max(0, Math.min(import_core.MAX_LIST_LEVEL, Math.trunc(n)));
|
|
835
|
+
}
|
|
836
|
+
function normalizeListLevels(blocks) {
|
|
837
|
+
let i = 0;
|
|
838
|
+
while (i < blocks.length) {
|
|
839
|
+
if (blocks[i].type !== "listItem") {
|
|
840
|
+
i++;
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
let j = i + 1;
|
|
844
|
+
while (j < blocks.length && blocks[j].type === "listItem") j++;
|
|
845
|
+
if (j - i > 1) {
|
|
846
|
+
let min = Number.POSITIVE_INFINITY;
|
|
847
|
+
for (let k = i; k < j; k++) {
|
|
848
|
+
const lvl = blocks[k].attrs.listLevel ?? 0;
|
|
849
|
+
if (lvl < min) min = lvl;
|
|
850
|
+
}
|
|
851
|
+
if (min > 0) {
|
|
852
|
+
for (let k = i; k < j; k++) {
|
|
853
|
+
const lvl = blocks[k].attrs.listLevel ?? 0;
|
|
854
|
+
blocks[k].attrs.listLevel = clampLevel(lvl - min);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
i = j;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
function pushOp(ops, text, marks) {
|
|
862
|
+
const hasMarks = Object.keys(marks).length > 0;
|
|
863
|
+
ops.push(hasMarks ? { insert: text, attributes: { ...marks } } : { insert: text });
|
|
864
|
+
}
|
|
865
|
+
function normalizeWhitespace(raw) {
|
|
866
|
+
return raw.replace(/[ \t\r\n]+/g, " ");
|
|
867
|
+
}
|
|
868
|
+
function trimEdgeWhitespace(ops) {
|
|
869
|
+
let out = ops;
|
|
870
|
+
if (out.length > 0 && typeof out[0].insert === "string") {
|
|
871
|
+
const trimmed = out[0].insert.replace(/^ +/, "");
|
|
872
|
+
if (trimmed.length === 0) out = out.slice(1);
|
|
873
|
+
else if (trimmed !== out[0].insert) out = [{ ...out[0], insert: trimmed }, ...out.slice(1)];
|
|
874
|
+
}
|
|
875
|
+
if (out.length > 0 && typeof out[out.length - 1].insert === "string") {
|
|
876
|
+
const last = out[out.length - 1];
|
|
877
|
+
const trimmed = last.insert.replace(/ +$/, "");
|
|
878
|
+
if (trimmed.length === 0) out = out.slice(0, -1);
|
|
879
|
+
else if (trimmed !== last.insert) out = [...out.slice(0, -1), { ...last, insert: trimmed }];
|
|
880
|
+
}
|
|
881
|
+
return out;
|
|
882
|
+
}
|
|
883
|
+
function mergeAdjacentOps(ops) {
|
|
884
|
+
const out = [];
|
|
885
|
+
for (const op of ops) {
|
|
886
|
+
const prev = out[out.length - 1];
|
|
887
|
+
if (prev && typeof prev.insert === "string" && typeof op.insert === "string" && sameMarks(prev.attributes, op.attributes)) {
|
|
888
|
+
prev.insert = prev.insert + op.insert;
|
|
889
|
+
continue;
|
|
890
|
+
}
|
|
891
|
+
out.push({ ...op });
|
|
892
|
+
}
|
|
893
|
+
return out;
|
|
894
|
+
}
|
|
895
|
+
function sameMarks(a, b) {
|
|
896
|
+
const ak = a ? Object.keys(a).sort() : [];
|
|
897
|
+
const bk = b ? Object.keys(b).sort() : [];
|
|
898
|
+
if (ak.length !== bk.length) return false;
|
|
899
|
+
for (let i = 0; i < ak.length; i++) {
|
|
900
|
+
if (ak[i] !== bk[i]) return false;
|
|
901
|
+
const av = a[ak[i]];
|
|
902
|
+
const bv = b[bk[i]];
|
|
903
|
+
if (typeof av === "object" && av !== null && typeof bv === "object" && bv !== null) {
|
|
904
|
+
if (JSON.stringify(av) !== JSON.stringify(bv)) return false;
|
|
905
|
+
} else if (av !== bv) {
|
|
906
|
+
return false;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
return true;
|
|
910
|
+
}
|
|
911
|
+
function deltaToText(delta) {
|
|
912
|
+
let s = "";
|
|
913
|
+
for (const op of delta) if (typeof op.insert === "string") s += op.insert;
|
|
914
|
+
return s;
|
|
915
|
+
}
|
|
916
|
+
function makeBlock(type, delta, attrs) {
|
|
917
|
+
return { type, text: deltaToText(delta), delta, attrs };
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// src/pastePlan.ts
|
|
921
|
+
function planPaste(cd) {
|
|
922
|
+
const raw = cd.getData(import_core2.SOFER_MIME);
|
|
923
|
+
if (raw) {
|
|
924
|
+
try {
|
|
925
|
+
const slice = JSON.parse(raw);
|
|
926
|
+
if (slice && Array.isArray(slice.blocks)) return { kind: "sofer", slice };
|
|
927
|
+
} catch {
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
const html = cd.getData("text/html");
|
|
931
|
+
if (html) {
|
|
932
|
+
const slice = htmlToSlice(html);
|
|
933
|
+
if (slice) return { kind: "html", slice };
|
|
934
|
+
}
|
|
935
|
+
const files = Array.from(cd.files ?? []).filter((f) => f.type.startsWith("image/"));
|
|
936
|
+
if (files.length > 0) return { kind: "images", files };
|
|
937
|
+
const text = cd.getData("text/plain");
|
|
938
|
+
if (text.length > 0) return { kind: "text", text };
|
|
939
|
+
return { kind: "none" };
|
|
940
|
+
}
|
|
941
|
+
|
|
332
942
|
// src/BehindImageSelectAffordance.tsx
|
|
333
943
|
var import_react = require("react");
|
|
334
944
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -645,7 +1255,7 @@ function LinkDialog() {
|
|
|
645
1255
|
|
|
646
1256
|
// src/PageConfigDialog.tsx
|
|
647
1257
|
var import_react3 = require("react");
|
|
648
|
-
var
|
|
1258
|
+
var import_core3 = require("@sofereditor/core");
|
|
649
1259
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
650
1260
|
function PageConfigDialog() {
|
|
651
1261
|
const { pageConfigRequest, resolvePageConfigRequest, setPageSettings } = useEditorContext();
|
|
@@ -679,14 +1289,14 @@ function PageConfigDialog() {
|
|
|
679
1289
|
}, [pageConfigRequest]);
|
|
680
1290
|
(0, import_react3.useEffect)(() => {
|
|
681
1291
|
if (sizePreset === "custom") return;
|
|
682
|
-
const p =
|
|
683
|
-
setWidthMm(roundMm((0,
|
|
684
|
-
setHeightMm(roundMm((0,
|
|
1292
|
+
const p = import_core3.PAGE_PRESETS[sizePreset];
|
|
1293
|
+
setWidthMm(roundMm((0, import_core3.pxToMm)(p.width)));
|
|
1294
|
+
setHeightMm(roundMm((0, import_core3.pxToMm)(p.height)));
|
|
685
1295
|
}, [sizePreset]);
|
|
686
1296
|
(0, import_react3.useEffect)(() => {
|
|
687
1297
|
if (marginPreset === "custom") return;
|
|
688
|
-
const px =
|
|
689
|
-
const mm = roundMm((0,
|
|
1298
|
+
const px = import_core3.MARGIN_PRESETS[marginPreset];
|
|
1299
|
+
const mm = roundMm((0, import_core3.pxToMm)(px));
|
|
690
1300
|
setMarginTopMm(mm);
|
|
691
1301
|
setMarginBottomMm(mm);
|
|
692
1302
|
setMarginLeftMm(mm);
|
|
@@ -695,8 +1305,8 @@ function PageConfigDialog() {
|
|
|
695
1305
|
const customWidth = sizePreset === "custom";
|
|
696
1306
|
const previewPx = (0, import_react3.useMemo)(
|
|
697
1307
|
() => ({
|
|
698
|
-
width: Math.round((0,
|
|
699
|
-
height: Math.round((0,
|
|
1308
|
+
width: Math.round((0, import_core3.mmToPx)(widthMm)),
|
|
1309
|
+
height: Math.round((0, import_core3.mmToPx)(heightMm))
|
|
700
1310
|
}),
|
|
701
1311
|
[widthMm, heightMm]
|
|
702
1312
|
);
|
|
@@ -704,12 +1314,12 @@ function PageConfigDialog() {
|
|
|
704
1314
|
const onApply = (e) => {
|
|
705
1315
|
e.preventDefault();
|
|
706
1316
|
const next = {
|
|
707
|
-
width: (0,
|
|
708
|
-
height: (0,
|
|
709
|
-
marginTop: (0,
|
|
710
|
-
marginBottom: (0,
|
|
711
|
-
marginLeft: (0,
|
|
712
|
-
marginRight: (0,
|
|
1317
|
+
width: (0, import_core3.mmToPx)(widthMm),
|
|
1318
|
+
height: (0, import_core3.mmToPx)(heightMm),
|
|
1319
|
+
marginTop: (0, import_core3.mmToPx)(marginTopMm),
|
|
1320
|
+
marginBottom: (0, import_core3.mmToPx)(marginBottomMm),
|
|
1321
|
+
marginLeft: (0, import_core3.mmToPx)(marginLeftMm),
|
|
1322
|
+
marginRight: (0, import_core3.mmToPx)(marginRightMm)
|
|
713
1323
|
};
|
|
714
1324
|
setPageSettings(next);
|
|
715
1325
|
resolvePageConfigRequest(true);
|
|
@@ -968,14 +1578,14 @@ function roundMm(mm) {
|
|
|
968
1578
|
return Math.round(mm * 10) / 10;
|
|
969
1579
|
}
|
|
970
1580
|
function hydrateFromSettings(s, setters) {
|
|
971
|
-
const preset = (0,
|
|
1581
|
+
const preset = (0, import_core3.detectPreset)(s.width, s.height);
|
|
972
1582
|
setters.setSizePreset(preset);
|
|
973
|
-
setters.setWidthMm(roundMm((0,
|
|
974
|
-
setters.setHeightMm(roundMm((0,
|
|
975
|
-
const top = roundMm((0,
|
|
976
|
-
const bottom = roundMm((0,
|
|
977
|
-
const left = roundMm((0,
|
|
978
|
-
const right = roundMm((0,
|
|
1583
|
+
setters.setWidthMm(roundMm((0, import_core3.pxToMm)(s.width)));
|
|
1584
|
+
setters.setHeightMm(roundMm((0, import_core3.pxToMm)(s.height)));
|
|
1585
|
+
const top = roundMm((0, import_core3.pxToMm)(s.marginTop));
|
|
1586
|
+
const bottom = roundMm((0, import_core3.pxToMm)(s.marginBottom));
|
|
1587
|
+
const left = roundMm((0, import_core3.pxToMm)(s.marginLeft));
|
|
1588
|
+
const right = roundMm((0, import_core3.pxToMm)(s.marginRight));
|
|
979
1589
|
setters.setMarginTopMm(top);
|
|
980
1590
|
setters.setMarginBottomMm(bottom);
|
|
981
1591
|
setters.setMarginLeftMm(left);
|
|
@@ -986,7 +1596,7 @@ function detectMarginPreset(s) {
|
|
|
986
1596
|
const TOL = 1;
|
|
987
1597
|
const allEqual = Math.abs(s.marginTop - s.marginBottom) <= TOL && Math.abs(s.marginTop - s.marginLeft) <= TOL && Math.abs(s.marginTop - s.marginRight) <= TOL;
|
|
988
1598
|
if (!allEqual) return "custom";
|
|
989
|
-
for (const [name, px] of Object.entries(
|
|
1599
|
+
for (const [name, px] of Object.entries(import_core3.MARGIN_PRESETS)) {
|
|
990
1600
|
if (Math.abs(s.marginTop - px) <= TOL) return name;
|
|
991
1601
|
}
|
|
992
1602
|
return "custom";
|
|
@@ -1546,7 +2156,7 @@ function Handle({
|
|
|
1546
2156
|
|
|
1547
2157
|
// src/RemoteCursorsOverlay.tsx
|
|
1548
2158
|
var import_react7 = require("react");
|
|
1549
|
-
var
|
|
2159
|
+
var import_core4 = require("@sofereditor/core");
|
|
1550
2160
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1551
2161
|
var DEFAULT_COLOR = "#3b82f6";
|
|
1552
2162
|
function RemoteCursorsOverlay({
|
|
@@ -1569,7 +2179,7 @@ function RemoteCursorsOverlay({
|
|
|
1569
2179
|
if (clientId === awareness.clientID) return;
|
|
1570
2180
|
const s = state;
|
|
1571
2181
|
if (!s.cursor) return;
|
|
1572
|
-
const sel = (0,
|
|
2182
|
+
const sel = (0, import_core4.decodeSelection)(doc, s.cursor);
|
|
1573
2183
|
if (!sel) return;
|
|
1574
2184
|
const color = s.user?.color || DEFAULT_COLOR;
|
|
1575
2185
|
const name = s.user?.name || "An\xF4nimo";
|
|
@@ -1716,11 +2326,11 @@ function RemoteCursorsOverlay({
|
|
|
1716
2326
|
|
|
1717
2327
|
// src/NodeView.tsx
|
|
1718
2328
|
var import_react10 = require("react");
|
|
1719
|
-
var
|
|
2329
|
+
var import_core6 = require("@sofereditor/core");
|
|
1720
2330
|
|
|
1721
2331
|
// src/renderInline.tsx
|
|
1722
2332
|
var import_react8 = require("react");
|
|
1723
|
-
var
|
|
2333
|
+
var import_core5 = require("@sofereditor/core");
|
|
1724
2334
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1725
2335
|
function renderInline(delta, keyPrefix) {
|
|
1726
2336
|
if (delta.length === 0 || delta.length === 1 && typeof delta[0].insert === "string" && delta[0].insert === "") {
|
|
@@ -1736,7 +2346,7 @@ function renderInline(delta, keyPrefix) {
|
|
|
1736
2346
|
const key = `${keyPrefix}-${offset}`;
|
|
1737
2347
|
body.push(/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react8.Fragment, { children: wrap(text, op.attributes) }, key));
|
|
1738
2348
|
offset += text.length;
|
|
1739
|
-
} else if ((0,
|
|
2349
|
+
} else if ((0, import_core5.isImageEmbed)(op.insert)) {
|
|
1740
2350
|
const key = `${keyPrefix}-${offset}`;
|
|
1741
2351
|
const layout = op.insert.layout ?? "inline";
|
|
1742
2352
|
const commentMarkId = op.attributes?.comment?.markId;
|
|
@@ -1900,10 +2510,10 @@ function ImageEmbedViewImpl({
|
|
|
1900
2510
|
);
|
|
1901
2511
|
}
|
|
1902
2512
|
function decorateBlanks(text) {
|
|
1903
|
-
const segs = (0,
|
|
2513
|
+
const segs = (0, import_core5.splitUnderscoreRuns)(text);
|
|
1904
2514
|
if (segs.length === 1 && !segs[0].blank) return text;
|
|
1905
2515
|
return segs.map(
|
|
1906
|
-
(s, i) => s.blank ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "data-blank": "true", style:
|
|
2516
|
+
(s, i) => s.blank ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "data-blank": "true", style: import_core5.BLANK_STYLE, children: s.text }, i) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react8.Fragment, { children: s.text }, i)
|
|
1907
2517
|
);
|
|
1908
2518
|
}
|
|
1909
2519
|
function wrap(text, attrs) {
|
|
@@ -2101,7 +2711,7 @@ function NodeView({ block, index, children, fragment, tableFragment }) {
|
|
|
2101
2711
|
const common = commonBlockProps(block.attrs, index, block.type, fragment);
|
|
2102
2712
|
switch (block.type) {
|
|
2103
2713
|
case "heading": {
|
|
2104
|
-
const level =
|
|
2714
|
+
const level = clampLevel2(block.attrs.level);
|
|
2105
2715
|
const Tag = `h${level}`;
|
|
2106
2716
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2107
2717
|
Tag,
|
|
@@ -2162,7 +2772,7 @@ function NodeView({ block, index, children, fragment, tableFragment }) {
|
|
|
2162
2772
|
function commonBlockProps(attrs, index, type, fragment) {
|
|
2163
2773
|
const style = {};
|
|
2164
2774
|
if (attrs.align) style.textAlign = attrs.align;
|
|
2165
|
-
Object.assign(style, (0,
|
|
2775
|
+
Object.assign(style, (0, import_core6.answerLineStyle)(attrs) ?? {});
|
|
2166
2776
|
const base = {
|
|
2167
2777
|
"data-block-index": index,
|
|
2168
2778
|
"data-block-type": type,
|
|
@@ -2188,7 +2798,7 @@ function cellStyle(attrs, border) {
|
|
|
2188
2798
|
if (attrs?.bgColor) style.backgroundColor = attrs.bgColor;
|
|
2189
2799
|
return Object.keys(style).length > 0 ? style : void 0;
|
|
2190
2800
|
}
|
|
2191
|
-
function
|
|
2801
|
+
function clampLevel2(l) {
|
|
2192
2802
|
if (l === 2 || l === 3 || l === 4 || l === 5 || l === 6) return l;
|
|
2193
2803
|
return 1;
|
|
2194
2804
|
}
|
|
@@ -2209,7 +2819,7 @@ function TableView({ block, index, tableFragment }) {
|
|
|
2209
2819
|
const rect = (0, import_react10.useMemo)(() => {
|
|
2210
2820
|
if (editor.selection.anchor.blockIndex !== index) return null;
|
|
2211
2821
|
if (editor.selection.focus.blockIndex !== index) return null;
|
|
2212
|
-
return (0,
|
|
2822
|
+
return (0, import_core6.tableRectSelection)(editor.doc, editor.selection);
|
|
2213
2823
|
}, [editor.doc, editor.selection, index]);
|
|
2214
2824
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ed-table-wrap", children: [
|
|
2215
2825
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
@@ -2245,10 +2855,11 @@ function TableView({ block, index, tableFragment }) {
|
|
|
2245
2855
|
cell?.attrs,
|
|
2246
2856
|
// rowStart/rowEnd são os limites do FRAGMENTO: numa tabela
|
|
2247
2857
|
// quebrada entre páginas, cada página fecha a própria caixa.
|
|
2248
|
-
(0,
|
|
2858
|
+
(0, import_core6.cellBorderStyle)(
|
|
2249
2859
|
block.attrs.borderPreset,
|
|
2250
2860
|
{ row: r, col: c, rowspan, colspan, cols, rowStart, rowEnd },
|
|
2251
|
-
"screen"
|
|
2861
|
+
"screen",
|
|
2862
|
+
block.attrs.borderColor
|
|
2252
2863
|
)
|
|
2253
2864
|
),
|
|
2254
2865
|
className: inRect ? "ed-cell ed-cell--selected" : "ed-cell",
|
|
@@ -2820,14 +3431,14 @@ function shallowEqual(a, b) {
|
|
|
2820
3431
|
}
|
|
2821
3432
|
|
|
2822
3433
|
// src/useEditor.ts
|
|
2823
|
-
var
|
|
3434
|
+
var import_core7 = require("@sofereditor/core");
|
|
2824
3435
|
var MAX_INSERT_WIDTH = 600;
|
|
2825
3436
|
function useEditor(opts = {}) {
|
|
2826
|
-
const doc = (0, import_react13.useMemo)(() => opts.document ?? new
|
|
2827
|
-
const history = (0, import_react13.useMemo)(() => new
|
|
3437
|
+
const doc = (0, import_react13.useMemo)(() => opts.document ?? new import_core7.EditorDocument(), [opts.document]);
|
|
3438
|
+
const history = (0, import_react13.useMemo)(() => new import_core7.EditorHistory(doc), [doc]);
|
|
2828
3439
|
const [snapshot, setSnapshot] = (0, import_react13.useState)(() => doc.toJSON());
|
|
2829
3440
|
const [selection, setSelectionState] = (0, import_react13.useState)(
|
|
2830
|
-
() => (0,
|
|
3441
|
+
() => (0, import_core7.collapsedSelection)({ blockIndex: 0, offset: 0 })
|
|
2831
3442
|
);
|
|
2832
3443
|
const selectionRef = (0, import_react13.useRef)(selection);
|
|
2833
3444
|
selectionRef.current = selection;
|
|
@@ -2892,7 +3503,7 @@ function useEditor(opts = {}) {
|
|
|
2892
3503
|
const isMarkActive = (0, import_react13.useCallback)(
|
|
2893
3504
|
(name, value) => {
|
|
2894
3505
|
const sel = selectionRef.current;
|
|
2895
|
-
const { start, end } = (0,
|
|
3506
|
+
const { start, end } = (0, import_core7.orderedRange)(sel);
|
|
2896
3507
|
if (start.blockIndex === end.blockIndex && start.offset === end.offset) {
|
|
2897
3508
|
const pending = pendingMarksRef.current[name];
|
|
2898
3509
|
if (pending !== void 0) {
|
|
@@ -2901,7 +3512,7 @@ function useEditor(opts = {}) {
|
|
|
2901
3512
|
}
|
|
2902
3513
|
const yText2 = doc.getBlockText(start.blockIndex);
|
|
2903
3514
|
if (!yText2 || start.offset === 0) return false;
|
|
2904
|
-
return (0,
|
|
3515
|
+
return (0, import_core7.isMarkUniformInRange)(yText2, start.offset - 1, start.offset, name, value);
|
|
2905
3516
|
}
|
|
2906
3517
|
if (start.blockIndex !== end.blockIndex) {
|
|
2907
3518
|
for (let i = start.blockIndex; i <= end.blockIndex; i++) {
|
|
@@ -2910,19 +3521,19 @@ function useEditor(opts = {}) {
|
|
|
2910
3521
|
const s = i === start.blockIndex ? start.offset : 0;
|
|
2911
3522
|
const e = i === end.blockIndex ? end.offset : yText2.length;
|
|
2912
3523
|
if (s >= e) continue;
|
|
2913
|
-
if (!(0,
|
|
3524
|
+
if (!(0, import_core7.isMarkUniformInRange)(yText2, s, e, name, value)) return false;
|
|
2914
3525
|
}
|
|
2915
3526
|
return true;
|
|
2916
3527
|
}
|
|
2917
3528
|
const yText = doc.getBlockText(start.blockIndex);
|
|
2918
3529
|
if (!yText) return false;
|
|
2919
|
-
return (0,
|
|
3530
|
+
return (0, import_core7.isMarkUniformInRange)(yText, start.offset, end.offset, name, value);
|
|
2920
3531
|
},
|
|
2921
3532
|
[doc]
|
|
2922
3533
|
);
|
|
2923
3534
|
const getActiveMarks = (0, import_react13.useCallback)(() => {
|
|
2924
3535
|
const sel = selectionRef.current;
|
|
2925
|
-
const { start, end } = (0,
|
|
3536
|
+
const { start, end } = (0, import_core7.orderedRange)(sel);
|
|
2926
3537
|
if (start.blockIndex !== end.blockIndex) {
|
|
2927
3538
|
return {};
|
|
2928
3539
|
}
|
|
@@ -2930,14 +3541,14 @@ function useEditor(opts = {}) {
|
|
|
2930
3541
|
if (!yText) return {};
|
|
2931
3542
|
if (start.offset === end.offset) {
|
|
2932
3543
|
if (start.offset === 0) return {};
|
|
2933
|
-
return (0,
|
|
3544
|
+
return (0, import_core7.getMarksInRange)(yText, start.offset - 1, start.offset);
|
|
2934
3545
|
}
|
|
2935
|
-
return (0,
|
|
3546
|
+
return (0, import_core7.getMarksInRange)(yText, start.offset, end.offset);
|
|
2936
3547
|
}, [doc]);
|
|
2937
3548
|
const toggleMark = (0, import_react13.useCallback)(
|
|
2938
3549
|
(name, value) => {
|
|
2939
3550
|
const sel = selectionRef.current;
|
|
2940
|
-
const { start, end } = (0,
|
|
3551
|
+
const { start, end } = (0, import_core7.orderedRange)(sel);
|
|
2941
3552
|
const isCollapsed = start.blockIndex === end.blockIndex && start.offset === end.offset;
|
|
2942
3553
|
if (isCollapsed) {
|
|
2943
3554
|
const next = { ...pendingMarksRef.current };
|
|
@@ -2950,39 +3561,39 @@ function useEditor(opts = {}) {
|
|
|
2950
3561
|
setPendingMarks(next);
|
|
2951
3562
|
return;
|
|
2952
3563
|
}
|
|
2953
|
-
(0,
|
|
3564
|
+
(0, import_core7.toggleMark)(ctxRef.current, name, value);
|
|
2954
3565
|
},
|
|
2955
3566
|
[isMarkActive, setPendingMarks]
|
|
2956
3567
|
);
|
|
2957
3568
|
const setMark = (0, import_react13.useCallback)(
|
|
2958
3569
|
(name, value) => {
|
|
2959
3570
|
const sel = selectionRef.current;
|
|
2960
|
-
const { start, end } = (0,
|
|
3571
|
+
const { start, end } = (0, import_core7.orderedRange)(sel);
|
|
2961
3572
|
const isCollapsed = start.blockIndex === end.blockIndex && start.offset === end.offset;
|
|
2962
3573
|
if (isCollapsed) {
|
|
2963
3574
|
setPendingMarks({ ...pendingMarksRef.current, [name]: value });
|
|
2964
3575
|
return;
|
|
2965
3576
|
}
|
|
2966
|
-
(0,
|
|
3577
|
+
(0, import_core7.setMark)(ctxRef.current, name, value);
|
|
2967
3578
|
},
|
|
2968
3579
|
[setPendingMarks]
|
|
2969
3580
|
);
|
|
2970
3581
|
const removeMark = (0, import_react13.useCallback)(
|
|
2971
3582
|
(name) => {
|
|
2972
3583
|
const sel = selectionRef.current;
|
|
2973
|
-
const { start, end } = (0,
|
|
3584
|
+
const { start, end } = (0, import_core7.orderedRange)(sel);
|
|
2974
3585
|
const isCollapsed = start.blockIndex === end.blockIndex && start.offset === end.offset;
|
|
2975
3586
|
if (isCollapsed) {
|
|
2976
3587
|
setPendingMarks({ ...pendingMarksRef.current, [name]: null });
|
|
2977
3588
|
return;
|
|
2978
3589
|
}
|
|
2979
|
-
(0,
|
|
3590
|
+
(0, import_core7.removeMark)(ctxRef.current, name);
|
|
2980
3591
|
},
|
|
2981
3592
|
[setPendingMarks]
|
|
2982
3593
|
);
|
|
2983
3594
|
const getBlockType = (0, import_react13.useCallback)(() => {
|
|
2984
3595
|
const sel = selectionRef.current;
|
|
2985
|
-
const { start, end } = (0,
|
|
3596
|
+
const { start, end } = (0, import_core7.orderedRange)(sel);
|
|
2986
3597
|
const first = doc.getBlockType(start.blockIndex);
|
|
2987
3598
|
if (first === void 0) return "paragraph";
|
|
2988
3599
|
for (let i = start.blockIndex + 1; i <= end.blockIndex; i++) {
|
|
@@ -2993,7 +3604,7 @@ function useEditor(opts = {}) {
|
|
|
2993
3604
|
const getBlockAttr = (0, import_react13.useCallback)(
|
|
2994
3605
|
(key) => {
|
|
2995
3606
|
const sel = selectionRef.current;
|
|
2996
|
-
const { start, end } = (0,
|
|
3607
|
+
const { start, end } = (0, import_core7.orderedRange)(sel);
|
|
2997
3608
|
const first = doc.getBlockAttrs(start.blockIndex)[key];
|
|
2998
3609
|
for (let i = start.blockIndex + 1; i <= end.blockIndex; i++) {
|
|
2999
3610
|
const next = doc.getBlockAttrs(i)[key];
|
|
@@ -3004,19 +3615,19 @@ function useEditor(opts = {}) {
|
|
|
3004
3615
|
[doc]
|
|
3005
3616
|
);
|
|
3006
3617
|
const setBlockType = (0, import_react13.useCallback)((type, attrs) => {
|
|
3007
|
-
(0,
|
|
3618
|
+
(0, import_core7.setBlockType)(ctxRef.current, type, attrs);
|
|
3008
3619
|
}, []);
|
|
3009
3620
|
const setBlockAttr = (0, import_react13.useCallback)(
|
|
3010
3621
|
(key, value) => {
|
|
3011
|
-
(0,
|
|
3622
|
+
(0, import_core7.setBlockAttr)(ctxRef.current, key, value);
|
|
3012
3623
|
},
|
|
3013
3624
|
[]
|
|
3014
3625
|
);
|
|
3015
3626
|
const setAlign = (0, import_react13.useCallback)((value) => {
|
|
3016
3627
|
const sel = selectionRef.current;
|
|
3017
3628
|
const inCell = sel.focus.cellIndex != null;
|
|
3018
|
-
if (inCell) (0,
|
|
3019
|
-
else (0,
|
|
3629
|
+
if (inCell) (0, import_core7.setCellAttr)(ctxRef.current, "align", value);
|
|
3630
|
+
else (0, import_core7.setBlockAttr)(ctxRef.current, "align", value);
|
|
3020
3631
|
}, []);
|
|
3021
3632
|
const getAlign = (0, import_react13.useCallback)(() => {
|
|
3022
3633
|
const sel = selectionRef.current;
|
|
@@ -3026,7 +3637,7 @@ function useEditor(opts = {}) {
|
|
|
3026
3637
|
return getBlockAttr("align");
|
|
3027
3638
|
}, [doc, getBlockAttr]);
|
|
3028
3639
|
const setCellBackground = (0, import_react13.useCallback)((color) => {
|
|
3029
|
-
(0,
|
|
3640
|
+
(0, import_core7.setCellAttr)(ctxRef.current, "bgColor", color);
|
|
3030
3641
|
}, []);
|
|
3031
3642
|
const getCellBackground = (0, import_react13.useCallback)(() => {
|
|
3032
3643
|
const sel = selectionRef.current;
|
|
@@ -3036,17 +3647,27 @@ function useEditor(opts = {}) {
|
|
|
3036
3647
|
const setTableBorderPreset = (0, import_react13.useCallback)((preset) => {
|
|
3037
3648
|
const sel = selectionRef.current;
|
|
3038
3649
|
if (!doc.isTable(sel.focus.blockIndex)) return;
|
|
3039
|
-
(0,
|
|
3650
|
+
(0, import_core7.setBlockAttrAtIndex)(ctxRef.current, sel.focus.blockIndex, "borderPreset", preset);
|
|
3040
3651
|
}, [doc]);
|
|
3041
3652
|
const getTableBorderPreset = (0, import_react13.useCallback)(() => {
|
|
3042
3653
|
const sel = selectionRef.current;
|
|
3043
3654
|
if (!doc.isTable(sel.focus.blockIndex)) return "all";
|
|
3044
3655
|
return doc.getBlockAttrs(sel.focus.blockIndex).borderPreset ?? "all";
|
|
3045
3656
|
}, [doc]);
|
|
3657
|
+
const setTableBorderColor = (0, import_react13.useCallback)((color) => {
|
|
3658
|
+
const sel = selectionRef.current;
|
|
3659
|
+
if (!doc.isTable(sel.focus.blockIndex)) return;
|
|
3660
|
+
(0, import_core7.setBlockAttrAtIndex)(ctxRef.current, sel.focus.blockIndex, "borderColor", color);
|
|
3661
|
+
}, [doc]);
|
|
3662
|
+
const getTableBorderColor = (0, import_react13.useCallback)(() => {
|
|
3663
|
+
const sel = selectionRef.current;
|
|
3664
|
+
if (!doc.isTable(sel.focus.blockIndex)) return void 0;
|
|
3665
|
+
return doc.getBlockAttrs(sel.focus.blockIndex).borderColor;
|
|
3666
|
+
}, [doc]);
|
|
3046
3667
|
const isListActive = (0, import_react13.useCallback)(
|
|
3047
3668
|
(kind) => {
|
|
3048
3669
|
const sel = selectionRef.current;
|
|
3049
|
-
const { start, end } = (0,
|
|
3670
|
+
const { start, end } = (0, import_core7.orderedRange)(sel);
|
|
3050
3671
|
for (let i = start.blockIndex; i <= end.blockIndex; i++) {
|
|
3051
3672
|
if (doc.getBlockType(i) !== "listItem") return false;
|
|
3052
3673
|
if (doc.getBlockAttrs(i).listKind !== kind) return false;
|
|
@@ -3056,112 +3677,112 @@ function useEditor(opts = {}) {
|
|
|
3056
3677
|
[doc]
|
|
3057
3678
|
);
|
|
3058
3679
|
const toggleList = (0, import_react13.useCallback)((kind) => {
|
|
3059
|
-
(0,
|
|
3680
|
+
(0, import_core7.toggleList)(ctxRef.current, kind);
|
|
3060
3681
|
}, []);
|
|
3061
3682
|
const indentList = (0, import_react13.useCallback)(() => {
|
|
3062
|
-
(0,
|
|
3683
|
+
(0, import_core7.indentList)(ctxRef.current);
|
|
3063
3684
|
}, []);
|
|
3064
3685
|
const dedentList = (0, import_react13.useCallback)(() => {
|
|
3065
|
-
(0,
|
|
3686
|
+
(0, import_core7.dedentList)(ctxRef.current);
|
|
3066
3687
|
}, []);
|
|
3067
3688
|
const splitListItem = (0, import_react13.useCallback)(() => {
|
|
3068
|
-
return (0,
|
|
3689
|
+
return (0, import_core7.splitListItem)(ctxRef.current);
|
|
3069
3690
|
}, []);
|
|
3070
3691
|
const isInTable = (0, import_react13.useCallback)(() => {
|
|
3071
3692
|
return selectionRef.current.focus.cellIndex != null;
|
|
3072
3693
|
}, []);
|
|
3073
3694
|
const getTableLocation = (0, import_react13.useCallback)(() => {
|
|
3074
|
-
return (0,
|
|
3695
|
+
return (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
|
|
3075
3696
|
}, [doc]);
|
|
3076
3697
|
const insertTable = (0, import_react13.useCallback)((rows, cols) => {
|
|
3077
|
-
(0,
|
|
3698
|
+
(0, import_core7.insertTable)(ctxRef.current, rows, cols);
|
|
3078
3699
|
}, []);
|
|
3079
3700
|
const insertAnswerLines = (0, import_react13.useCallback)((count, spacing) => {
|
|
3080
|
-
(0,
|
|
3701
|
+
(0, import_core7.insertAnswerLines)(ctxRef.current, count, spacing);
|
|
3081
3702
|
}, []);
|
|
3082
3703
|
const deleteTable = (0, import_react13.useCallback)((blockIndex) => {
|
|
3083
3704
|
const idx = blockIndex ?? selectionRef.current.focus.blockIndex;
|
|
3084
|
-
(0,
|
|
3705
|
+
(0, import_core7.deleteTable)(ctxRef.current, idx);
|
|
3085
3706
|
}, []);
|
|
3086
3707
|
const insertRowAbove = (0, import_react13.useCallback)(() => {
|
|
3087
|
-
const loc = (0,
|
|
3708
|
+
const loc = (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
|
|
3088
3709
|
if (!loc) return;
|
|
3089
|
-
(0,
|
|
3710
|
+
(0, import_core7.insertTableRow)(ctxRef.current, loc.blockIndex, loc.row, "before");
|
|
3090
3711
|
}, [doc]);
|
|
3091
3712
|
const insertRowBelow = (0, import_react13.useCallback)(() => {
|
|
3092
|
-
const loc = (0,
|
|
3713
|
+
const loc = (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
|
|
3093
3714
|
if (!loc) return;
|
|
3094
|
-
(0,
|
|
3715
|
+
(0, import_core7.insertTableRow)(ctxRef.current, loc.blockIndex, loc.row, "after");
|
|
3095
3716
|
}, [doc]);
|
|
3096
3717
|
const deleteCurrentRow = (0, import_react13.useCallback)(() => {
|
|
3097
|
-
const loc = (0,
|
|
3718
|
+
const loc = (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
|
|
3098
3719
|
if (!loc) return;
|
|
3099
|
-
(0,
|
|
3720
|
+
(0, import_core7.deleteTableRow)(ctxRef.current, loc.blockIndex, loc.row);
|
|
3100
3721
|
}, [doc]);
|
|
3101
3722
|
const insertColumnLeft = (0, import_react13.useCallback)(() => {
|
|
3102
|
-
const loc = (0,
|
|
3723
|
+
const loc = (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
|
|
3103
3724
|
if (!loc) return;
|
|
3104
|
-
(0,
|
|
3725
|
+
(0, import_core7.insertTableColumn)(ctxRef.current, loc.blockIndex, loc.col, "before");
|
|
3105
3726
|
}, [doc]);
|
|
3106
3727
|
const insertColumnRight = (0, import_react13.useCallback)(() => {
|
|
3107
|
-
const loc = (0,
|
|
3728
|
+
const loc = (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
|
|
3108
3729
|
if (!loc) return;
|
|
3109
|
-
(0,
|
|
3730
|
+
(0, import_core7.insertTableColumn)(ctxRef.current, loc.blockIndex, loc.col, "after");
|
|
3110
3731
|
}, [doc]);
|
|
3111
3732
|
const deleteCurrentColumn = (0, import_react13.useCallback)(() => {
|
|
3112
|
-
const loc = (0,
|
|
3733
|
+
const loc = (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
|
|
3113
3734
|
if (!loc) return;
|
|
3114
|
-
(0,
|
|
3735
|
+
(0, import_core7.deleteTableColumn)(ctxRef.current, loc.blockIndex, loc.col);
|
|
3115
3736
|
}, [doc]);
|
|
3116
3737
|
const moveToNextCell = (0, import_react13.useCallback)(() => {
|
|
3117
|
-
(0,
|
|
3738
|
+
(0, import_core7.moveToNextCell)(ctxRef.current);
|
|
3118
3739
|
}, []);
|
|
3119
3740
|
const moveToPrevCell = (0, import_react13.useCallback)(() => {
|
|
3120
|
-
(0,
|
|
3741
|
+
(0, import_core7.moveToPrevCell)(ctxRef.current);
|
|
3121
3742
|
}, []);
|
|
3122
3743
|
const mergeRight = (0, import_react13.useCallback)(() => {
|
|
3123
|
-
const loc = (0,
|
|
3744
|
+
const loc = (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
|
|
3124
3745
|
if (!loc) return false;
|
|
3125
|
-
return (0,
|
|
3746
|
+
return (0, import_core7.mergeRight)(ctxRef.current, loc.blockIndex, loc.row, loc.col);
|
|
3126
3747
|
}, [doc]);
|
|
3127
3748
|
const mergeDown = (0, import_react13.useCallback)(() => {
|
|
3128
|
-
const loc = (0,
|
|
3749
|
+
const loc = (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
|
|
3129
3750
|
if (!loc) return false;
|
|
3130
|
-
return (0,
|
|
3751
|
+
return (0, import_core7.mergeDown)(ctxRef.current, loc.blockIndex, loc.row, loc.col);
|
|
3131
3752
|
}, [doc]);
|
|
3132
3753
|
const splitCell = (0, import_react13.useCallback)(() => {
|
|
3133
|
-
const loc = (0,
|
|
3754
|
+
const loc = (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
|
|
3134
3755
|
if (!loc) return false;
|
|
3135
|
-
return (0,
|
|
3756
|
+
return (0, import_core7.splitCell)(ctxRef.current, loc.blockIndex, loc.row, loc.col);
|
|
3136
3757
|
}, [doc]);
|
|
3137
3758
|
const setColumnWidth = (0, import_react13.useCallback)(
|
|
3138
3759
|
(blockIndex, col, widthPx) => {
|
|
3139
|
-
(0,
|
|
3760
|
+
(0, import_core7.setColumnWidth)(ctxRef.current, blockIndex, col, widthPx);
|
|
3140
3761
|
},
|
|
3141
3762
|
[]
|
|
3142
3763
|
);
|
|
3143
3764
|
const getTableRect = (0, import_react13.useCallback)(
|
|
3144
|
-
() => (0,
|
|
3765
|
+
() => (0, import_core7.tableRectSelection)(doc, selectionRef.current),
|
|
3145
3766
|
[doc]
|
|
3146
3767
|
);
|
|
3147
3768
|
const hasTableRectSelection = (0, import_react13.useCallback)(() => {
|
|
3148
|
-
return (0,
|
|
3769
|
+
return (0, import_core7.tableRectSelection)(doc, selectionRef.current) !== null;
|
|
3149
3770
|
}, [doc]);
|
|
3150
3771
|
const mergeSelection = (0, import_react13.useCallback)(() => {
|
|
3151
|
-
return (0,
|
|
3772
|
+
return (0, import_core7.mergeSelection)(ctxRef.current);
|
|
3152
3773
|
}, []);
|
|
3153
3774
|
const insertImage = (0, import_react13.useCallback)((embed) => {
|
|
3154
|
-
(0,
|
|
3775
|
+
(0, import_core7.insertImage)(ctxRef.current, embed);
|
|
3155
3776
|
}, []);
|
|
3156
3777
|
const setImageAttrs = (0, import_react13.useCallback)(
|
|
3157
3778
|
(blockIndex, offset, partial, cellIndex, newOffset) => {
|
|
3158
|
-
(0,
|
|
3779
|
+
(0, import_core7.setImageAttrs)(ctxRef.current, blockIndex, offset, partial, cellIndex, newOffset);
|
|
3159
3780
|
},
|
|
3160
3781
|
[]
|
|
3161
3782
|
);
|
|
3162
3783
|
const moveEmbedAnchor = (0, import_react13.useCallback)(
|
|
3163
3784
|
(from, to, newOffsetX, newOffsetY) => {
|
|
3164
|
-
(0,
|
|
3785
|
+
(0, import_core7.moveEmbedAnchor)(ctxRef.current, from, to, newOffsetX, newOffsetY);
|
|
3165
3786
|
},
|
|
3166
3787
|
[]
|
|
3167
3788
|
);
|
|
@@ -3179,7 +3800,7 @@ function useEditor(opts = {}) {
|
|
|
3179
3800
|
}
|
|
3180
3801
|
const upload = uploadImageRef.current;
|
|
3181
3802
|
const src = upload ? await upload(file) : dataUrl;
|
|
3182
|
-
(0,
|
|
3803
|
+
(0, import_core7.insertImage)(ctxRef.current, { type: "image", src, width: w, height: h });
|
|
3183
3804
|
}, []);
|
|
3184
3805
|
const getSelectedEmbed = (0, import_react13.useCallback)(() => {
|
|
3185
3806
|
const sel = selectionRef.current;
|
|
@@ -3198,7 +3819,7 @@ function useEditor(opts = {}) {
|
|
|
3198
3819
|
cursor += op.insert.length;
|
|
3199
3820
|
continue;
|
|
3200
3821
|
}
|
|
3201
|
-
if (cursor === start && (0,
|
|
3822
|
+
if (cursor === start && (0, import_core7.isImageEmbed)(op.insert)) {
|
|
3202
3823
|
return {
|
|
3203
3824
|
blockIndex: sel.anchor.blockIndex,
|
|
3204
3825
|
cellIndex: sel.anchor.cellIndex,
|
|
@@ -3289,6 +3910,8 @@ function useEditor(opts = {}) {
|
|
|
3289
3910
|
getCellBackground,
|
|
3290
3911
|
setTableBorderPreset,
|
|
3291
3912
|
getTableBorderPreset,
|
|
3913
|
+
setTableBorderColor,
|
|
3914
|
+
getTableBorderColor,
|
|
3292
3915
|
isListActive,
|
|
3293
3916
|
toggleList,
|
|
3294
3917
|
indentList,
|
|
@@ -3391,7 +4014,7 @@ function Editor({
|
|
|
3391
4014
|
const sel = readDomSelection(root);
|
|
3392
4015
|
awareness.setLocalStateField(
|
|
3393
4016
|
"cursor",
|
|
3394
|
-
sel ? (0,
|
|
4017
|
+
sel ? (0, import_core8.encodeSelection)(doc, sel) ?? null : null
|
|
3395
4018
|
);
|
|
3396
4019
|
};
|
|
3397
4020
|
const onSelChange = () => {
|
|
@@ -3692,14 +4315,14 @@ function Editor({
|
|
|
3692
4315
|
const data = e.data ?? "";
|
|
3693
4316
|
if (data.length > 0) {
|
|
3694
4317
|
e.preventDefault();
|
|
3695
|
-
(0,
|
|
4318
|
+
(0, import_core8.insertText)(ctx, data, ed.consumePendingMarks());
|
|
3696
4319
|
}
|
|
3697
4320
|
return;
|
|
3698
4321
|
}
|
|
3699
4322
|
case "insertLineBreak": {
|
|
3700
4323
|
e.preventDefault();
|
|
3701
4324
|
if (ed.getBlockType() === "listItem") {
|
|
3702
|
-
(0,
|
|
4325
|
+
(0, import_core8.insertText)(ctx, "\n", ed.consumePendingMarks());
|
|
3703
4326
|
return;
|
|
3704
4327
|
}
|
|
3705
4328
|
const selEmbed = ed.getSelectedEmbed();
|
|
@@ -3719,7 +4342,7 @@ function Editor({
|
|
|
3719
4342
|
});
|
|
3720
4343
|
}
|
|
3721
4344
|
if (ed.splitListItem()) return;
|
|
3722
|
-
(0,
|
|
4345
|
+
(0, import_core8.insertParagraph)(ctx);
|
|
3723
4346
|
return;
|
|
3724
4347
|
}
|
|
3725
4348
|
case "insertParagraph": {
|
|
@@ -3741,14 +4364,14 @@ function Editor({
|
|
|
3741
4364
|
});
|
|
3742
4365
|
}
|
|
3743
4366
|
if (ed.splitListItem()) return;
|
|
3744
|
-
(0,
|
|
4367
|
+
(0, import_core8.insertParagraph)(ctx);
|
|
3745
4368
|
return;
|
|
3746
4369
|
}
|
|
3747
4370
|
case "insertReplacementText": {
|
|
3748
4371
|
const text = e.data ?? e.dataTransfer?.getData("text/plain") ?? "";
|
|
3749
4372
|
if (text.length > 0) {
|
|
3750
4373
|
e.preventDefault();
|
|
3751
|
-
(0,
|
|
4374
|
+
(0, import_core8.insertText)(ctx, text, ed.consumePendingMarks());
|
|
3752
4375
|
}
|
|
3753
4376
|
return;
|
|
3754
4377
|
}
|
|
@@ -3761,7 +4384,7 @@ function Editor({
|
|
|
3761
4384
|
case "deleteSoftLineBackward":
|
|
3762
4385
|
case "deleteHardLineBackward": {
|
|
3763
4386
|
e.preventDefault();
|
|
3764
|
-
(0,
|
|
4387
|
+
(0, import_core8.deleteBackward)(ctx);
|
|
3765
4388
|
return;
|
|
3766
4389
|
}
|
|
3767
4390
|
case "deleteContentForward":
|
|
@@ -3769,7 +4392,7 @@ function Editor({
|
|
|
3769
4392
|
case "deleteSoftLineForward":
|
|
3770
4393
|
case "deleteHardLineForward": {
|
|
3771
4394
|
e.preventDefault();
|
|
3772
|
-
(0,
|
|
4395
|
+
(0, import_core8.deleteForward)(ctx);
|
|
3773
4396
|
return;
|
|
3774
4397
|
}
|
|
3775
4398
|
case "historyUndo": {
|
|
@@ -3796,10 +4419,10 @@ function Editor({
|
|
|
3796
4419
|
if (!root) return;
|
|
3797
4420
|
const writeSlice = (e) => {
|
|
3798
4421
|
const ctx = ctxRef.current;
|
|
3799
|
-
const slice = (0,
|
|
4422
|
+
const slice = (0, import_core8.serializeSelection)(ctx.doc, ctx.getSelection());
|
|
3800
4423
|
if (!slice || !e.clipboardData) return false;
|
|
3801
|
-
e.clipboardData.setData(
|
|
3802
|
-
e.clipboardData.setData("text/plain", (0,
|
|
4424
|
+
e.clipboardData.setData(import_core8.SOFER_MIME, JSON.stringify(slice));
|
|
4425
|
+
e.clipboardData.setData("text/plain", (0, import_core8.sliceToText)(slice));
|
|
3803
4426
|
return true;
|
|
3804
4427
|
};
|
|
3805
4428
|
const onCopy = (e) => {
|
|
@@ -3807,40 +4430,34 @@ function Editor({
|
|
|
3807
4430
|
};
|
|
3808
4431
|
const onCut = (e) => {
|
|
3809
4432
|
e.preventDefault();
|
|
3810
|
-
if (writeSlice(e)) (0,
|
|
4433
|
+
if (writeSlice(e)) (0, import_core8.deleteSelection)(ctxRef.current);
|
|
3811
4434
|
};
|
|
3812
4435
|
const onPaste = (e) => {
|
|
3813
4436
|
const ctx = ctxRef.current;
|
|
3814
4437
|
const cd = e.clipboardData;
|
|
3815
4438
|
if (!cd) return;
|
|
3816
|
-
const
|
|
3817
|
-
if (
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
4439
|
+
const plan = planPaste(cd);
|
|
4440
|
+
if (plan.kind === "none") return;
|
|
4441
|
+
e.preventDefault();
|
|
4442
|
+
switch (plan.kind) {
|
|
4443
|
+
case "sofer":
|
|
4444
|
+
case "html":
|
|
4445
|
+
(0, import_core8.insertSlice)(ctx, plan.slice);
|
|
4446
|
+
return;
|
|
4447
|
+
case "images":
|
|
4448
|
+
void (async () => {
|
|
4449
|
+
for (const f of plan.files) await editorRef.current.insertImageFromFile(f);
|
|
4450
|
+
})();
|
|
4451
|
+
return;
|
|
4452
|
+
case "text": {
|
|
4453
|
+
const lines = plan.text.split(/\r\n|\r|\n/);
|
|
4454
|
+
lines.forEach((line, i) => {
|
|
4455
|
+
if (i > 0) (0, import_core8.insertParagraph)(ctx);
|
|
4456
|
+
if (line.length > 0) (0, import_core8.insertText)(ctx, line);
|
|
4457
|
+
});
|
|
4458
|
+
return;
|
|
3826
4459
|
}
|
|
3827
4460
|
}
|
|
3828
|
-
const images = Array.from(cd.files ?? []).filter((f) => f.type.startsWith("image/"));
|
|
3829
|
-
if (images.length > 0) {
|
|
3830
|
-
e.preventDefault();
|
|
3831
|
-
void (async () => {
|
|
3832
|
-
for (const f of images) await editorRef.current.insertImageFromFile(f);
|
|
3833
|
-
})();
|
|
3834
|
-
return;
|
|
3835
|
-
}
|
|
3836
|
-
const text = cd.getData("text/plain");
|
|
3837
|
-
if (text.length === 0) return;
|
|
3838
|
-
e.preventDefault();
|
|
3839
|
-
const lines = text.split(/\r\n|\r|\n/);
|
|
3840
|
-
lines.forEach((line, i) => {
|
|
3841
|
-
if (i > 0) (0, import_core6.insertParagraph)(ctx);
|
|
3842
|
-
if (line.length > 0) (0, import_core6.insertText)(ctx, line);
|
|
3843
|
-
});
|
|
3844
4461
|
};
|
|
3845
4462
|
root.addEventListener("copy", onCopy);
|
|
3846
4463
|
root.addEventListener("cut", onCut);
|
|
@@ -3931,7 +4548,7 @@ function Editor({
|
|
|
3931
4548
|
const onCompositionEnd = (0, import_react14.useCallback)((e) => {
|
|
3932
4549
|
isComposingRef.current = false;
|
|
3933
4550
|
if (e.data && e.data.length > 0) {
|
|
3934
|
-
(0,
|
|
4551
|
+
(0, import_core8.insertText)(ctxRef.current, e.data);
|
|
3935
4552
|
}
|
|
3936
4553
|
}, []);
|
|
3937
4554
|
const topLevels = (0, import_react14.useMemo)(() => groupTopLevels(snapshot.blocks), [snapshot]);
|
|
@@ -4095,11 +4712,15 @@ function groupTopLevels(snapshot) {
|
|
|
4095
4712
|
const groupStart = i;
|
|
4096
4713
|
const items = [];
|
|
4097
4714
|
const kind = block.attrs.listKind === "ordered" ? "ordered" : "bullet";
|
|
4715
|
+
const leaderLevel = clampListLevel2(block.attrs.listLevel);
|
|
4098
4716
|
const groupStyle = block.attrs.listStyle;
|
|
4099
4717
|
while (i < snapshot.length) {
|
|
4100
4718
|
const b = snapshot[i];
|
|
4101
4719
|
if (b.type !== "listItem") break;
|
|
4102
|
-
|
|
4720
|
+
const bLevel = clampListLevel2(b.attrs.listLevel);
|
|
4721
|
+
if (bLevel <= leaderLevel && (b.attrs.listKind ?? "bullet") !== kind) {
|
|
4722
|
+
break;
|
|
4723
|
+
}
|
|
4103
4724
|
if (i > groupStart) {
|
|
4104
4725
|
if (typeof b.attrs.listStart === "number") break;
|
|
4105
4726
|
const itemStyle = b.attrs.listStyle;
|
|
@@ -4207,7 +4828,7 @@ function buildListTree(items) {
|
|
|
4207
4828
|
function renderListTree(tree, top, start, key) {
|
|
4208
4829
|
const className = `ed-list ed-list-${top.listKind}`;
|
|
4209
4830
|
const style = top.listStyle ? { listStyleType: top.listStyle } : void 0;
|
|
4210
|
-
const items = tree.map((node) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(NodeView, { block: node.entry.block, index: node.entry.index, children: node.children.length > 0 ? renderNestedListTree(node.children,
|
|
4831
|
+
const items = tree.map((node) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(NodeView, { block: node.entry.block, index: node.entry.index, children: node.children.length > 0 ? renderNestedListTree(node.children, `nested-${node.entry.index}`) : null }, node.entry.index));
|
|
4211
4832
|
if (top.listKind === "ordered") {
|
|
4212
4833
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
4213
4834
|
"ol",
|
|
@@ -4223,9 +4844,10 @@ function renderListTree(tree, top, start, key) {
|
|
|
4223
4844
|
}
|
|
4224
4845
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("ul", { className, "data-list-kind": "bullet", style, children: items }, key);
|
|
4225
4846
|
}
|
|
4226
|
-
function renderNestedListTree(tree,
|
|
4847
|
+
function renderNestedListTree(tree, key) {
|
|
4848
|
+
const kind = tree[0]?.entry.block.attrs.listKind === "ordered" ? "ordered" : "bullet";
|
|
4227
4849
|
const Tag = kind === "ordered" ? "ol" : "ul";
|
|
4228
|
-
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Tag, { className: `ed-list ed-list-${kind}`, "data-list-kind": kind, children: tree.map((node) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(NodeView, { block: node.entry.block, index: node.entry.index, children: node.children.length > 0 ? renderNestedListTree(node.children,
|
|
4850
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Tag, { className: `ed-list ed-list-${kind}`, "data-list-kind": kind, children: tree.map((node) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(NodeView, { block: node.entry.block, index: node.entry.index, children: node.children.length > 0 ? renderNestedListTree(node.children, `nested-${node.entry.index}`) : null }, node.entry.index)) }, key);
|
|
4229
4851
|
}
|
|
4230
4852
|
function clampListLevel2(l) {
|
|
4231
4853
|
if (typeof l !== "number" || !Number.isFinite(l)) return 0;
|
|
@@ -4254,7 +4876,7 @@ function caretRangeFromPoint(x, y) {
|
|
|
4254
4876
|
|
|
4255
4877
|
// src/Toolbar.tsx
|
|
4256
4878
|
var import_react15 = require("react");
|
|
4257
|
-
var
|
|
4879
|
+
var import_core9 = require("@sofereditor/core");
|
|
4258
4880
|
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
4259
4881
|
var FONT_FAMILIES = ["Arial"];
|
|
4260
4882
|
var FONT_SIZES = ["8pt", "10pt", "11pt", "12pt", "14pt", "16pt", "18pt", "24pt", "32pt", "48pt"];
|
|
@@ -4276,6 +4898,15 @@ var ALIGN_OPTIONS = [
|
|
|
4276
4898
|
{ value: "justify", label: "\u2630", title: "Justificar" }
|
|
4277
4899
|
];
|
|
4278
4900
|
var IMAGE_ALIGN_VALUES = /* @__PURE__ */ new Set(["left", "center", "right"]);
|
|
4901
|
+
var NATIVE_CONTROL_TAGS = /* @__PURE__ */ new Set(["SELECT", "INPUT", "OPTION"]);
|
|
4902
|
+
function isNativeControl(t) {
|
|
4903
|
+
const el = t;
|
|
4904
|
+
return !!el && NATIVE_CONTROL_TAGS.has(el.tagName);
|
|
4905
|
+
}
|
|
4906
|
+
function keepsOwnMouseDown(t) {
|
|
4907
|
+
const el = t;
|
|
4908
|
+
return !!el && (NATIVE_CONTROL_TAGS.has(el.tagName) || el.tagName === "BUTTON");
|
|
4909
|
+
}
|
|
4279
4910
|
var IMAGE_LAYOUT_OPTIONS = [
|
|
4280
4911
|
{ value: "inline", label: "\u2194", title: "Inline (no fluxo do texto)" },
|
|
4281
4912
|
{ value: "wrap-left", label: "\u25E7", title: "Texto contorna \xE0 direita" },
|
|
@@ -4308,10 +4939,7 @@ function Toolbar({ className }) {
|
|
|
4308
4939
|
const blockAlign = getAlign();
|
|
4309
4940
|
const blockDir = getBlockAttr("dir");
|
|
4310
4941
|
const stop = (e) => {
|
|
4311
|
-
|
|
4312
|
-
if (t && (t.tagName === "SELECT" || t.tagName === "INPUT" || t.tagName === "OPTION")) {
|
|
4313
|
-
return;
|
|
4314
|
-
}
|
|
4942
|
+
if (isNativeControl(e.target)) return;
|
|
4315
4943
|
e.preventDefault();
|
|
4316
4944
|
};
|
|
4317
4945
|
const onToggle = (name) => (e) => {
|
|
@@ -4720,7 +5348,7 @@ function AnswerLinesMenu() {
|
|
|
4720
5348
|
document.addEventListener("mousedown", onDown);
|
|
4721
5349
|
return () => document.removeEventListener("mousedown", onDown);
|
|
4722
5350
|
}, [open]);
|
|
4723
|
-
const clamped = Math.max(1, Math.min(
|
|
5351
|
+
const clamped = Math.max(1, Math.min(import_core9.ANSWER_LINE_MAX, Math.trunc(count) || 1));
|
|
4724
5352
|
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { ref: rootRef, className: "ed-toolbar-tablemenu", children: [
|
|
4725
5353
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4726
5354
|
"button",
|
|
@@ -4743,10 +5371,7 @@ function AnswerLinesMenu() {
|
|
|
4743
5371
|
className: "ed-table-popover ed-answerlines-popover",
|
|
4744
5372
|
role: "menu",
|
|
4745
5373
|
onMouseDown: (e) => {
|
|
4746
|
-
|
|
4747
|
-
if (!t || t.tagName !== "INPUT" && t.tagName !== "BUTTON" && t.tagName !== "SELECT") {
|
|
4748
|
-
e.preventDefault();
|
|
4749
|
-
}
|
|
5374
|
+
if (!keepsOwnMouseDown(e.target)) e.preventDefault();
|
|
4750
5375
|
},
|
|
4751
5376
|
children: [
|
|
4752
5377
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { className: "ed-toolbar-label", children: [
|
|
@@ -4756,7 +5381,7 @@ function AnswerLinesMenu() {
|
|
|
4756
5381
|
{
|
|
4757
5382
|
type: "number",
|
|
4758
5383
|
min: 1,
|
|
4759
|
-
max:
|
|
5384
|
+
max: import_core9.ANSWER_LINE_MAX,
|
|
4760
5385
|
value: count,
|
|
4761
5386
|
onChange: (e) => setCount(Number(e.target.value)),
|
|
4762
5387
|
"aria-label": "Quantidade de linhas"
|
|
@@ -4837,10 +5462,7 @@ function TableMenu() {
|
|
|
4837
5462
|
className: "ed-table-popover",
|
|
4838
5463
|
role: "menu",
|
|
4839
5464
|
onMouseDown: (e) => {
|
|
4840
|
-
|
|
4841
|
-
if (!t || t.tagName !== "INPUT" && t.tagName !== "BUTTON") {
|
|
4842
|
-
e.preventDefault();
|
|
4843
|
-
}
|
|
5465
|
+
if (!keepsOwnMouseDown(e.target)) e.preventDefault();
|
|
4844
5466
|
},
|
|
4845
5467
|
children: [
|
|
4846
5468
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
@@ -4946,6 +5568,37 @@ function TableMenu() {
|
|
|
4946
5568
|
}
|
|
4947
5569
|
)
|
|
4948
5570
|
] }),
|
|
5571
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { className: "ed-toolbar-label ed-table-bgcolor", children: [
|
|
5572
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
5573
|
+
"span",
|
|
5574
|
+
{
|
|
5575
|
+
className: "ed-toolbar-swatch",
|
|
5576
|
+
"aria-hidden": true,
|
|
5577
|
+
style: { background: editor.getTableBorderColor() ?? import_core9.TABLE_BORDER_COLOR }
|
|
5578
|
+
}
|
|
5579
|
+
),
|
|
5580
|
+
"Cor da borda",
|
|
5581
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
5582
|
+
"input",
|
|
5583
|
+
{
|
|
5584
|
+
type: "color",
|
|
5585
|
+
value: editor.getTableBorderColor() ?? import_core9.TABLE_BORDER_COLOR,
|
|
5586
|
+
onChange: (e) => editor.setTableBorderColor(e.target.value),
|
|
5587
|
+
"aria-label": "Cor da borda"
|
|
5588
|
+
}
|
|
5589
|
+
)
|
|
5590
|
+
] }),
|
|
5591
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
5592
|
+
"button",
|
|
5593
|
+
{
|
|
5594
|
+
type: "button",
|
|
5595
|
+
onClick: () => {
|
|
5596
|
+
setOpen(false);
|
|
5597
|
+
editor.setTableBorderColor(null);
|
|
5598
|
+
},
|
|
5599
|
+
children: "Restaurar cor padr\xE3o"
|
|
5600
|
+
}
|
|
5601
|
+
),
|
|
4949
5602
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("hr", {}),
|
|
4950
5603
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4951
5604
|
"button",
|
|
@@ -5051,6 +5704,7 @@ function MarkButton({ title, pressed, onClick, children }) {
|
|
|
5051
5704
|
getBlockElement,
|
|
5052
5705
|
getCellElement,
|
|
5053
5706
|
getFragmentForOffset,
|
|
5707
|
+
htmlToSlice,
|
|
5054
5708
|
isTableRectSelection,
|
|
5055
5709
|
locatePoint,
|
|
5056
5710
|
readDomSelection,
|