@weasel-js/text-dom 1.9.4 → 1.10.1
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/README.md +6 -1
- package/dist/index.d.ts +52 -74
- package/dist/index.js +446 -198
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useRef, useState, useCallback, useEffect } from 'react';
|
|
2
2
|
import { useLatest, useVisibleRaf } from '@weasel-js/react';
|
|
3
|
-
import { DEFAULT_TEXT_STYLE, smallCapsScaleFor, numericWeight, resolveTextStyle, styleAtRange, runsCarryStyling, runsToPlainText, unboldPatch, nodeHasFlag, setFlagOverRange, applyStyleToRange, patchRangeStyle,
|
|
3
|
+
import { DEFAULT_TEXT_STYLE, smallCapsScaleFor, numericWeight, resolveTextStyle, styleAtRange, runsCarryStyling, runsToPlainText, unboldPatch, nodeHasFlag, setFlagOverRange, applyStyleToRange, patchRangeStyle, effectiveRangeStyle, SMALL_CAPS_SCALE, isHardLineBreak, fontString, supersededKey, rangeWeight, scriptMetricsFor, transformRunTexts, smallCapsText, resolveAlign, paragraphDirection, layoutTextPose, verticalAlignOffset } from '@weasel-js/text';
|
|
4
4
|
import { cssFontFamily, cssFontFamilyLoading, isCanvasFont } from '@weasel-js/font';
|
|
5
|
+
import { isSolidPaint } from '@weasel-js/paint';
|
|
5
6
|
|
|
6
7
|
// src/useTextEdit.ts
|
|
7
8
|
|
|
@@ -82,7 +83,7 @@ function domPositionToCharOffset(parent, node, offset) {
|
|
|
82
83
|
// src/domRuns.ts
|
|
83
84
|
function solidColor(p) {
|
|
84
85
|
if (!p) return null;
|
|
85
|
-
if (
|
|
86
|
+
if (isSolidPaint(p)) return p.color;
|
|
86
87
|
return null;
|
|
87
88
|
}
|
|
88
89
|
var FILL_ATTR = "data-fill";
|
|
@@ -194,7 +195,7 @@ function toRun(text, style) {
|
|
|
194
195
|
if (style.fontSize != null) run.fontSize = style.fontSize;
|
|
195
196
|
if (style.fontFamily != null) run.fontFamily = style.fontFamily;
|
|
196
197
|
if (style.fillJson !== void 0) run.fill = JSON.parse(style.fillJson);
|
|
197
|
-
else if (style.color != null) run.fill = {
|
|
198
|
+
else if (style.color != null) run.fill = { paint: { kind: "solid", color: style.color } };
|
|
198
199
|
if (style.letterSpacing != null) run.letterSpacing = style.letterSpacing;
|
|
199
200
|
if (style.underline) run.underline = true;
|
|
200
201
|
if (style.strikethrough) run.strikethrough = true;
|
|
@@ -333,7 +334,7 @@ function runsToDom(runs, parent, base = DEFAULT_TEXT_STYLE) {
|
|
|
333
334
|
});
|
|
334
335
|
span.setAttribute("data-font-family", run.fontFamily);
|
|
335
336
|
}
|
|
336
|
-
const color = solidColor(run.fill);
|
|
337
|
+
const color = solidColor(run.fill?.paint);
|
|
337
338
|
if (color != null) span.style.color = color;
|
|
338
339
|
else if (run.fill !== void 0) span.setAttribute(FILL_ATTR, JSON.stringify(run.fill));
|
|
339
340
|
if (run.fill === null) span.style.color = "#000";
|
|
@@ -421,67 +422,282 @@ function overlayAlignEdge(el, style) {
|
|
|
421
422
|
return resolveAlign(style.align, paragraphDirection(el.textContent ?? "", style.direction));
|
|
422
423
|
}
|
|
423
424
|
|
|
424
|
-
// src/
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
];
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
return
|
|
425
|
+
// src/overlayFrame.ts
|
|
426
|
+
function overlayTransform(pose, left, top, anchor) {
|
|
427
|
+
const zoom = pose.zoom ?? 1;
|
|
428
|
+
const parts = [];
|
|
429
|
+
if (pose.frame) {
|
|
430
|
+
const [a, b, c, d] = pose.frame;
|
|
431
|
+
const dx = left - (pose.x + pose.width * zoom / 2);
|
|
432
|
+
const dy = top - (pose.y + pose.height * zoom / 2);
|
|
433
|
+
const m = [a * zoom, b * zoom, c * zoom, d * zoom, (a - 1) * dx + c * dy, b * dx + (d - 1) * dy];
|
|
434
|
+
parts.push(`matrix(${m.join(", ")})`);
|
|
435
|
+
} else if (zoom !== 1) {
|
|
436
|
+
parts.push(`scale(${zoom})`);
|
|
437
|
+
}
|
|
438
|
+
if (anchor !== 0) parts.push(`translateX(${-anchor * 100}%)`);
|
|
439
|
+
return parts.length > 0 ? parts.join(" ") : "none";
|
|
439
440
|
}
|
|
440
|
-
function
|
|
441
|
-
|
|
442
|
-
|
|
441
|
+
function applyOverlayStyle(el, style) {
|
|
442
|
+
el.style.position = "absolute";
|
|
443
|
+
el.style.pointerEvents = "auto";
|
|
444
|
+
el.style.boxSizing = "border-box";
|
|
445
|
+
el.style.margin = "0";
|
|
446
|
+
el.style.padding = "0";
|
|
447
|
+
el.style.border = "0";
|
|
448
|
+
el.style.outline = "none";
|
|
449
|
+
el.style.background = "transparent";
|
|
450
|
+
const paint = style.fill?.paint;
|
|
451
|
+
el.style.color = isSolidPaint(paint) ? paint.color : "#000";
|
|
452
|
+
el.style.caretColor = style.caretColor;
|
|
453
|
+
const fontFamily = cssFontFamily(style.fontFamily, {
|
|
454
|
+
weight: numericWeight(style.fontWeight),
|
|
455
|
+
style: style.fontStyle
|
|
456
|
+
});
|
|
457
|
+
el.style.font = fontString({ ...style, fontFamily });
|
|
458
|
+
el.style.textRendering = isCanvasFont(style.fontFamily) ? "" : "geometricPrecision";
|
|
459
|
+
el.style.lineHeight = String(style.lineHeight);
|
|
460
|
+
applyOverlayDirection(el, style);
|
|
461
|
+
const decorations = [];
|
|
462
|
+
if (style.underline) decorations.push("underline");
|
|
463
|
+
if (style.strikethrough) decorations.push("line-through");
|
|
464
|
+
if (style.overline) decorations.push("overline");
|
|
465
|
+
el.style.textDecoration = decorations.length > 0 ? decorations.join(" ") : "none";
|
|
466
|
+
el.style.textTransform = style.textTransform;
|
|
467
|
+
el.style.fontVariant = style.fontVariantCaps;
|
|
468
|
+
el.style.setProperty(
|
|
469
|
+
SMALL_CAPS_SCALE_PROPERTY,
|
|
470
|
+
style.fontVariantCaps === "small-caps" ? String(smallCapsScaleFor(style.fontFamily, numericWeight(style.fontWeight), style.fontStyle)) : ""
|
|
471
|
+
);
|
|
472
|
+
el.style.whiteSpace = style.wrap ? "pre-wrap" : "pre";
|
|
473
|
+
el.style.overflowWrap = "normal";
|
|
474
|
+
el.style.wordBreak = "normal";
|
|
475
|
+
}
|
|
476
|
+
var endsInBreak = (text) => text.length > 0 && isHardLineBreak(text.charCodeAt(text.length - 1));
|
|
477
|
+
function seedOverlay(overlay, text, runs, base) {
|
|
478
|
+
const styled = runs !== void 0 && runs.length > 0;
|
|
479
|
+
if (styled) runsToDom(runs, overlay, base);
|
|
480
|
+
else if (base.fontVariantCaps === "small-caps") runsToDom([{ text }], overlay, base);
|
|
481
|
+
else appendOverlayText(overlay, text);
|
|
482
|
+
let ownBreak = endsInBreak(styled ? runsToPlainText(runs) : text);
|
|
483
|
+
return {
|
|
484
|
+
edited(el) {
|
|
485
|
+
if (ownBreak && !endsInBreak(runsToPlainText(domToRuns(el)))) ownBreak = false;
|
|
486
|
+
},
|
|
487
|
+
trim: (read) => ownBreak ? read : trimCaretHolder(read)
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
function trimCaretHolder(runs) {
|
|
491
|
+
const last = runs[runs.length - 1];
|
|
492
|
+
if (last === void 0 || !last.text.endsWith("\n")) return runs;
|
|
493
|
+
const trimmed = { ...last, text: last.text.slice(0, -1) };
|
|
494
|
+
const head = runs.slice(0, -1);
|
|
495
|
+
return trimmed.text === "" ? head : [...head, trimmed];
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// src/overlaySelection.ts
|
|
499
|
+
function readSelectionOffsets(overlay) {
|
|
500
|
+
const sel = window.getSelection();
|
|
501
|
+
if (!sel || sel.rangeCount === 0) return null;
|
|
502
|
+
const range = sel.getRangeAt(0);
|
|
503
|
+
if (!overlay.contains(range.startContainer) || !overlay.contains(range.endContainer)) {
|
|
504
|
+
return null;
|
|
443
505
|
}
|
|
444
|
-
const
|
|
445
|
-
|
|
506
|
+
const start = domPositionToCharOffset(overlay, range.startContainer, range.startOffset);
|
|
507
|
+
const end = domPositionToCharOffset(overlay, range.endContainer, range.endOffset);
|
|
508
|
+
return start <= end ? { start, end } : { start: end, end: start };
|
|
446
509
|
}
|
|
447
|
-
function
|
|
448
|
-
|
|
510
|
+
function selectOffsets(overlay, start, end) {
|
|
511
|
+
const a = charOffsetToDomPosition(overlay, start);
|
|
512
|
+
const b = charOffsetToDomPosition(overlay, end);
|
|
513
|
+
const sel = window.getSelection();
|
|
514
|
+
if (!a || !b || !sel) return;
|
|
515
|
+
const range = document.createRange();
|
|
516
|
+
range.setStart(a.node, a.offset);
|
|
517
|
+
range.setEnd(b.node, b.offset);
|
|
518
|
+
sel.removeAllRanges();
|
|
519
|
+
sel.addRange(range);
|
|
520
|
+
}
|
|
521
|
+
function writeRunsPreservingSelection(overlay, runs, start, end, base) {
|
|
522
|
+
const active = document.activeElement;
|
|
523
|
+
const inTextEntry = active !== overlay && !overlay.contains(active) && (active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement || active instanceof HTMLElement && active.isContentEditable);
|
|
524
|
+
runsToDom(runs, overlay, base);
|
|
525
|
+
if (!inTextEntry) selectOffsets(overlay, start, end);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// src/pasteRuns.ts
|
|
529
|
+
var UNSEEN = /* @__PURE__ */ new Set([
|
|
530
|
+
"HEAD",
|
|
531
|
+
"IFRAME",
|
|
532
|
+
"NOSCRIPT",
|
|
533
|
+
"OBJECT",
|
|
534
|
+
"SCRIPT",
|
|
535
|
+
"STYLE",
|
|
536
|
+
"TEMPLATE",
|
|
537
|
+
"TITLE"
|
|
538
|
+
]);
|
|
539
|
+
var BLOCKS = /* @__PURE__ */ new Set([
|
|
540
|
+
"ADDRESS",
|
|
541
|
+
"ARTICLE",
|
|
542
|
+
"ASIDE",
|
|
543
|
+
"BLOCKQUOTE",
|
|
544
|
+
"CAPTION",
|
|
545
|
+
"DD",
|
|
546
|
+
"DETAILS",
|
|
547
|
+
"DIV",
|
|
548
|
+
"DL",
|
|
549
|
+
"DT",
|
|
550
|
+
"FIELDSET",
|
|
551
|
+
"FIGCAPTION",
|
|
552
|
+
"FIGURE",
|
|
553
|
+
"FOOTER",
|
|
554
|
+
"FORM",
|
|
555
|
+
"H1",
|
|
556
|
+
"H2",
|
|
557
|
+
"H3",
|
|
558
|
+
"H4",
|
|
559
|
+
"H5",
|
|
560
|
+
"H6",
|
|
561
|
+
"HEADER",
|
|
562
|
+
"HR",
|
|
563
|
+
"LI",
|
|
564
|
+
"MAIN",
|
|
565
|
+
"NAV",
|
|
566
|
+
"OL",
|
|
567
|
+
"P",
|
|
568
|
+
"PRE",
|
|
569
|
+
"SECTION",
|
|
570
|
+
"SUMMARY",
|
|
571
|
+
"TABLE",
|
|
572
|
+
"TR",
|
|
573
|
+
"UL"
|
|
574
|
+
]);
|
|
575
|
+
var CELLS = /* @__PURE__ */ new Set(["TD", "TH"]);
|
|
576
|
+
function keepsSpace(el, inherited) {
|
|
577
|
+
const collapse = el.style.getPropertyValue("white-space-collapse");
|
|
578
|
+
const ws = collapse || el.style.getPropertyValue("white-space");
|
|
579
|
+
if (ws === "") return el.tagName === "PRE" || inherited;
|
|
580
|
+
return /^(pre|pre-wrap|break-spaces|preserve)\b/.test(ws);
|
|
581
|
+
}
|
|
582
|
+
function emphasis(text, style) {
|
|
583
|
+
const read = toRun(text, style);
|
|
584
|
+
const run = { text };
|
|
585
|
+
if (read.bold || (read.fontWeight ?? 0) >= 600) run.bold = true;
|
|
586
|
+
for (const key of ["italic", "underline", "strikethrough", "overline"]) if (read[key]) run[key] = true;
|
|
587
|
+
if (read.script) run.script = read.script;
|
|
588
|
+
return run;
|
|
589
|
+
}
|
|
590
|
+
function htmlToRuns(html) {
|
|
591
|
+
const body = new DOMParser().parseFromString(html, "text/html").body;
|
|
592
|
+
const runs = [];
|
|
593
|
+
let gap = { line: false, tab: false };
|
|
594
|
+
let onLine = false;
|
|
595
|
+
let spaced = false;
|
|
596
|
+
const write = (text, style) => {
|
|
597
|
+
const lead = (gap.line && runs.length > 0 ? "\n" : "") + (gap.tab ? " " : "");
|
|
598
|
+
runs.push(emphasis(lead + text, style));
|
|
599
|
+
gap = { line: false, tab: false };
|
|
600
|
+
onLine = true;
|
|
601
|
+
spaced = false;
|
|
602
|
+
};
|
|
603
|
+
const trimSpace = () => {
|
|
604
|
+
const last = runs[runs.length - 1];
|
|
605
|
+
if (spaced && last) last.text = last.text.slice(0, -1);
|
|
606
|
+
spaced = false;
|
|
607
|
+
};
|
|
608
|
+
const boundary = () => {
|
|
609
|
+
trimSpace();
|
|
610
|
+
gap = { line: gap.line || onLine, tab: false };
|
|
611
|
+
onLine = false;
|
|
612
|
+
};
|
|
613
|
+
const visit = (node, style, keep) => {
|
|
614
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
615
|
+
const data = node.data.replace(/\r\n?/g, "\n");
|
|
616
|
+
if (keep) {
|
|
617
|
+
if (data.length > 0) write(data, style);
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
data.split(/[ \t\n\f]+/).forEach((word, i) => {
|
|
621
|
+
if (i > 0 && onLine && !spaced) {
|
|
622
|
+
write(" ", style);
|
|
623
|
+
spaced = true;
|
|
624
|
+
}
|
|
625
|
+
if (word.length > 0) write(word, style);
|
|
626
|
+
});
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
if (!(node instanceof HTMLElement) || UNSEEN.has(node.tagName)) return;
|
|
630
|
+
if (node.tagName === "BR") {
|
|
631
|
+
if (node.classList.contains("Apple-interchange-newline")) return;
|
|
632
|
+
trimSpace();
|
|
633
|
+
write("\n", style);
|
|
634
|
+
onLine = false;
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
const block = BLOCKS.has(node.tagName);
|
|
638
|
+
if (block) boundary();
|
|
639
|
+
if (CELLS.has(node.tagName) && node.previousElementSibling !== null) {
|
|
640
|
+
trimSpace();
|
|
641
|
+
gap.tab = true;
|
|
642
|
+
}
|
|
643
|
+
const next = styleStateFromElement(node, style);
|
|
644
|
+
const keeps = keepsSpace(node, keep);
|
|
645
|
+
for (const child of node.childNodes) visit(child, next, keeps);
|
|
646
|
+
if (block) boundary();
|
|
647
|
+
};
|
|
648
|
+
visit(body, EMPTY_STYLE, false);
|
|
649
|
+
trimSpace();
|
|
650
|
+
const kept = (run) => JSON.stringify({ ...run, text: "" });
|
|
651
|
+
const merged = [];
|
|
652
|
+
for (const run of runs) {
|
|
653
|
+
const last = merged[merged.length - 1];
|
|
654
|
+
if (run.text.length === 0) continue;
|
|
655
|
+
if (last && kept(last) === kept(run)) last.text += run.text;
|
|
656
|
+
else merged.push(run);
|
|
657
|
+
}
|
|
658
|
+
return merged;
|
|
659
|
+
}
|
|
660
|
+
function clipboardRuns(data) {
|
|
661
|
+
const html = data.getData("text/html");
|
|
662
|
+
const fromHtml = html ? htmlToRuns(html) : [];
|
|
663
|
+
if (fromHtml.length > 0) return fromHtml;
|
|
664
|
+
const plain = data.getData("text/plain").replace(/\r\n/g, "\n");
|
|
665
|
+
return plain.length > 0 ? [{ text: plain }] : [];
|
|
449
666
|
}
|
|
450
|
-
function
|
|
667
|
+
function replaceRunRange(runs, start, end, insert) {
|
|
451
668
|
const out = [];
|
|
452
669
|
let pos = 0;
|
|
453
670
|
let placed = false;
|
|
671
|
+
const place = () => {
|
|
672
|
+
if (!placed) out.push(...insert.map((r) => ({ ...r })));
|
|
673
|
+
placed = true;
|
|
674
|
+
};
|
|
454
675
|
for (const r of runs) {
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
if (k < r.text.length) out.push({ ...r, text: r.text.slice(k) });
|
|
461
|
-
placed = true;
|
|
462
|
-
} else {
|
|
463
|
-
out.push({ ...r });
|
|
464
|
-
}
|
|
465
|
-
pos = end;
|
|
676
|
+
const from = pos;
|
|
677
|
+
pos += r.text.length;
|
|
678
|
+
if (from < start) out.push({ ...r, text: r.text.slice(0, start - from) });
|
|
679
|
+
if (pos >= start) place();
|
|
680
|
+
if (pos > end) out.push({ ...r, text: r.text.slice(Math.max(0, end - from)) });
|
|
466
681
|
}
|
|
467
|
-
|
|
682
|
+
place();
|
|
468
683
|
return applyStyleToRange(out, 0, 0, {});
|
|
469
684
|
}
|
|
470
|
-
function
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
sel.addRange(r);
|
|
685
|
+
function runStyleAt(runs, at) {
|
|
686
|
+
let pos = 0;
|
|
687
|
+
let found = runs[0];
|
|
688
|
+
for (const r of runs) {
|
|
689
|
+
if (r.text.length === 0) continue;
|
|
690
|
+
if (pos >= at && pos > 0) break;
|
|
691
|
+
found = r;
|
|
692
|
+
pos += r.text.length;
|
|
693
|
+
}
|
|
694
|
+
if (!found) return {};
|
|
695
|
+
const style = { ...found };
|
|
696
|
+
delete style.text;
|
|
697
|
+
return style;
|
|
484
698
|
}
|
|
699
|
+
|
|
700
|
+
// src/overlayTyping.ts
|
|
485
701
|
function placeCaretBetweenRuns(overlay, runs, offset) {
|
|
486
702
|
let acc = 0;
|
|
487
703
|
let index = -1;
|
|
@@ -502,6 +718,141 @@ function placeCaretBetweenRuns(overlay, runs, offset) {
|
|
|
502
718
|
sel.removeAllRanges();
|
|
503
719
|
sel.addRange(range);
|
|
504
720
|
}
|
|
721
|
+
function resplitSmallCaps(overlay, e) {
|
|
722
|
+
if (e instanceof InputEvent && e.isComposing) return;
|
|
723
|
+
const at = readSelectionOffsets(overlay);
|
|
724
|
+
if (normalizeSmallCaps(overlay) && at) selectOffsets(overlay, at.start, at.end);
|
|
725
|
+
}
|
|
726
|
+
function namesItsRange(ie, caret) {
|
|
727
|
+
const target = ie.getTargetRanges?.()[0];
|
|
728
|
+
return target !== void 0 && caret.collapsed && !target.collapsed;
|
|
729
|
+
}
|
|
730
|
+
function replacedOffsets(overlay, ie, host) {
|
|
731
|
+
const target = ie.getTargetRanges?.()[0];
|
|
732
|
+
if (target && overlay.contains(target.startContainer) && overlay.contains(target.endContainer)) {
|
|
733
|
+
const a = domPositionToCharOffset(overlay, target.startContainer, target.startOffset);
|
|
734
|
+
const b = domPositionToCharOffset(overlay, target.endContainer, target.endOffset);
|
|
735
|
+
return { start: Math.min(a, b), end: Math.max(a, b) };
|
|
736
|
+
}
|
|
737
|
+
return readSelectionOffsets(overlay) ?? host.lastRange();
|
|
738
|
+
}
|
|
739
|
+
function styledForRange(pasted, runs, at, pending) {
|
|
740
|
+
const base = { ...runStyleAt(runs, at.start === at.end ? at.start : at.start + 1) };
|
|
741
|
+
for (const [key, value] of Object.entries(pending)) {
|
|
742
|
+
if (value === void 0 || value === false) delete base[key];
|
|
743
|
+
else base[key] = value;
|
|
744
|
+
}
|
|
745
|
+
return pasted.map((run) => {
|
|
746
|
+
const styled = { ...base, ...run };
|
|
747
|
+
const superseded = supersededKey(run);
|
|
748
|
+
if (superseded !== void 0) delete styled[superseded];
|
|
749
|
+
return styled;
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
var PASTES = /* @__PURE__ */ new Set(["insertFromPaste", "insertFromDrop"]);
|
|
753
|
+
function typingHandlers(overlay, host) {
|
|
754
|
+
const edited = (e) => {
|
|
755
|
+
resplitSmallCaps(overlay, e);
|
|
756
|
+
host.onEdited();
|
|
757
|
+
};
|
|
758
|
+
const onPaste = (ie) => {
|
|
759
|
+
const at = ie.dataTransfer ? replacedOffsets(overlay, ie, host) : null;
|
|
760
|
+
if (!ie.dataTransfer || !at) return;
|
|
761
|
+
ie.preventDefault();
|
|
762
|
+
const pasted = clipboardRuns(ie.dataTransfer);
|
|
763
|
+
if (pasted.length === 0) return;
|
|
764
|
+
const runs = domToRuns(overlay);
|
|
765
|
+
const next = replaceRunRange(runs, at.start, at.end, styledForRange(pasted, runs, at, host.pending()));
|
|
766
|
+
const after = at.start + runsToPlainText(pasted).length;
|
|
767
|
+
writeRunsPreservingSelection(overlay, next, after, after, host.base());
|
|
768
|
+
host.onWritten(after);
|
|
769
|
+
host.onEdited();
|
|
770
|
+
};
|
|
771
|
+
const onBeforeInput = (ie) => {
|
|
772
|
+
if (PASTES.has(ie.inputType)) {
|
|
773
|
+
onPaste(ie);
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
if (ie.inputType !== "insertText" || !ie.data) return;
|
|
777
|
+
const sel = window.getSelection();
|
|
778
|
+
const caret = sel && sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
|
|
779
|
+
if (caret && namesItsRange(ie, caret)) return;
|
|
780
|
+
const pending = host.pending();
|
|
781
|
+
if (Object.keys(pending).length > 0) {
|
|
782
|
+
const at = readSelectionOffsets(overlay) ?? host.lastRange();
|
|
783
|
+
if (!at || at.start !== at.end) return;
|
|
784
|
+
ie.preventDefault();
|
|
785
|
+
const typed = { ...pending, text: ie.data };
|
|
786
|
+
const next = replaceRunRange(domToRuns(overlay), at.start, at.start, [typed]);
|
|
787
|
+
const after = at.start + ie.data.length;
|
|
788
|
+
writeRunsPreservingSelection(overlay, next, after, after, host.base());
|
|
789
|
+
placeCaretBetweenRuns(overlay, next, after);
|
|
790
|
+
host.onWritten(after);
|
|
791
|
+
host.onEdited();
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
if (!sel || !caret || !caret.collapsed || caret.startContainer.nodeType === Node.TEXT_NODE) return;
|
|
795
|
+
if (!overlay.contains(caret.startContainer)) return;
|
|
796
|
+
ie.preventDefault();
|
|
797
|
+
const textNode = document.createTextNode(ie.data);
|
|
798
|
+
caret.insertNode(textNode);
|
|
799
|
+
sel.collapse(textNode, ie.data.length);
|
|
800
|
+
edited();
|
|
801
|
+
};
|
|
802
|
+
return { onBeforeInput, onInput: edited };
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// src/styleShortcuts.ts
|
|
806
|
+
var STYLE_SHORTCUTS = [
|
|
807
|
+
{ key: "b", shift: false, toggle: "bold" },
|
|
808
|
+
{ key: "i", shift: false, toggle: "italic" },
|
|
809
|
+
{ key: "u", shift: false, toggle: "underline" },
|
|
810
|
+
{ key: "x", shift: true, toggle: "strikethrough" },
|
|
811
|
+
{ key: "=", shift: true, toggle: "super" },
|
|
812
|
+
{ key: "-", shift: true, toggle: "sub" }
|
|
813
|
+
];
|
|
814
|
+
var UNSHIFT = { "+": "=", _: "-" };
|
|
815
|
+
function toggleForKey(e) {
|
|
816
|
+
if (!e.metaKey && !e.ctrlKey) return null;
|
|
817
|
+
const raw = e.key.toLowerCase();
|
|
818
|
+
const key = UNSHIFT[raw] ?? raw;
|
|
819
|
+
return STYLE_SHORTCUTS.find((s) => s.key === key && s.shift === e.shiftKey)?.toggle ?? null;
|
|
820
|
+
}
|
|
821
|
+
function useEditingId(opts) {
|
|
822
|
+
const [editingId, setState] = useState(null);
|
|
823
|
+
const live = useRef(null);
|
|
824
|
+
const setEditingId = useCallback((id) => {
|
|
825
|
+
const prev = live.current;
|
|
826
|
+
if (prev === id) return;
|
|
827
|
+
live.current = id;
|
|
828
|
+
setState(id);
|
|
829
|
+
if (prev !== null) opts.current.onEnd?.(prev);
|
|
830
|
+
if (id !== null) opts.current.onStart?.(id);
|
|
831
|
+
}, [opts]);
|
|
832
|
+
return [editingId, setEditingId];
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// src/textEditTypes.ts
|
|
836
|
+
var TEXT_EDIT_FONT_HOLD = 100;
|
|
837
|
+
|
|
838
|
+
// src/useTextEdit.ts
|
|
839
|
+
function patchForToggle(current, toggle, nodeStyle) {
|
|
840
|
+
if (toggle === "super" || toggle === "sub") {
|
|
841
|
+
return { script: current.script === toggle ? void 0 : toggle };
|
|
842
|
+
}
|
|
843
|
+
const on = toggle === "bold" ? rangeWeight(current, nodeStyle).bold === true : current[toggle] === true || nodeHasFlag(nodeStyle, toggle);
|
|
844
|
+
return { [toggle]: !on };
|
|
845
|
+
}
|
|
846
|
+
function styleAtCaret(runs, at) {
|
|
847
|
+
return at > 0 ? styleAtRange(runs, at - 1, at) : styleAtRange(runs, 0, 1);
|
|
848
|
+
}
|
|
849
|
+
function restoreOverlayFocus(overlay, range) {
|
|
850
|
+
const active = document.activeElement;
|
|
851
|
+
if (active === overlay || overlay.contains(active)) return;
|
|
852
|
+
if (active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement || active instanceof HTMLElement && active.isContentEditable || active instanceof Element && active.closest('[role="dialog"]') !== null) return;
|
|
853
|
+
overlay.focus();
|
|
854
|
+
selectOffsets(overlay, range.start, range.end);
|
|
855
|
+
}
|
|
505
856
|
var NO_PENDING = Object.freeze({});
|
|
506
857
|
function mergePending(prev, patch) {
|
|
507
858
|
const next = { ...prev };
|
|
@@ -513,47 +864,13 @@ function mergePending(prev, patch) {
|
|
|
513
864
|
}
|
|
514
865
|
return next;
|
|
515
866
|
}
|
|
516
|
-
function readSelectionOffsets(overlay) {
|
|
517
|
-
const sel = window.getSelection();
|
|
518
|
-
if (!sel || sel.rangeCount === 0) return null;
|
|
519
|
-
const range = sel.getRangeAt(0);
|
|
520
|
-
if (!overlay.contains(range.startContainer) || !overlay.contains(range.endContainer)) {
|
|
521
|
-
return null;
|
|
522
|
-
}
|
|
523
|
-
const start = domPositionToCharOffset(overlay, range.startContainer, range.startOffset);
|
|
524
|
-
const end = domPositionToCharOffset(overlay, range.endContainer, range.endOffset);
|
|
525
|
-
return start <= end ? { start, end } : { start: end, end: start };
|
|
526
|
-
}
|
|
527
|
-
function writeRunsPreservingSelection(overlay, runs, start, end, base) {
|
|
528
|
-
const active = document.activeElement;
|
|
529
|
-
const inTextEntry = active !== overlay && !overlay.contains(active) && (active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement || active instanceof HTMLElement && active.isContentEditable);
|
|
530
|
-
runsToDom(runs, overlay, base);
|
|
531
|
-
if (inTextEntry) return;
|
|
532
|
-
const a = charOffsetToDomPosition(overlay, start);
|
|
533
|
-
const b = charOffsetToDomPosition(overlay, end);
|
|
534
|
-
const sel = window.getSelection();
|
|
535
|
-
if (!a || !b || !sel) return;
|
|
536
|
-
const range = document.createRange();
|
|
537
|
-
range.setStart(a.node, a.offset);
|
|
538
|
-
range.setEnd(b.node, b.offset);
|
|
539
|
-
sel.removeAllRanges();
|
|
540
|
-
sel.addRange(range);
|
|
541
|
-
}
|
|
542
|
-
function trimCaretHolder(runs) {
|
|
543
|
-
const last = runs[runs.length - 1];
|
|
544
|
-
if (last === void 0 || !last.text.endsWith("\n")) return runs;
|
|
545
|
-
const trimmed = { ...last, text: last.text.slice(0, -1) };
|
|
546
|
-
const head = runs.slice(0, -1);
|
|
547
|
-
return trimmed.text === "" ? head : [...head, trimmed];
|
|
548
|
-
}
|
|
549
867
|
function sameSelection(a, b) {
|
|
550
868
|
if (a === null || b === null) return a === b;
|
|
551
869
|
return a.start === b.start && a.end === b.end;
|
|
552
870
|
}
|
|
553
|
-
var TEXT_EDIT_FONT_HOLD = 100;
|
|
554
871
|
function useTextEdit(opts) {
|
|
555
872
|
const optsRef = useLatest(opts);
|
|
556
|
-
const [editingId, setEditingId] =
|
|
873
|
+
const [editingId, setEditingId] = useEditingId(optsRef);
|
|
557
874
|
const editingIdRef = useLatest(editingId);
|
|
558
875
|
const overlayRef = useRef(null);
|
|
559
876
|
const baseRef = useRef(resolveTextStyle(void 0));
|
|
@@ -574,6 +891,7 @@ function useTextEdit(opts) {
|
|
|
574
891
|
});
|
|
575
892
|
const initialCaretRef = useRef("all");
|
|
576
893
|
const openedRef = useRef(null);
|
|
894
|
+
const holderRef = useRef(null);
|
|
577
895
|
const [selection, setSelection] = useState(null);
|
|
578
896
|
const [rangeStyle, setRangeStyle] = useState(null);
|
|
579
897
|
const [pendingStyle, setPendingStyle] = useState(NO_PENDING);
|
|
@@ -617,7 +935,7 @@ function useTextEdit(opts) {
|
|
|
617
935
|
}, [dropPending]);
|
|
618
936
|
const cancelEdit = useCallback(() => {
|
|
619
937
|
setEditingId(null);
|
|
620
|
-
}, []);
|
|
938
|
+
}, [setEditingId]);
|
|
621
939
|
const commit = useCallback(() => {
|
|
622
940
|
const id = editingId;
|
|
623
941
|
const overlay = overlayRef.current;
|
|
@@ -633,7 +951,7 @@ function useTextEdit(opts) {
|
|
|
633
951
|
setEditingId(null);
|
|
634
952
|
return;
|
|
635
953
|
}
|
|
636
|
-
const runs =
|
|
954
|
+
const runs = holderRef.current?.trim(read) ?? read;
|
|
637
955
|
const hadRuns = priorRuns != null && priorRuns.length > 0;
|
|
638
956
|
const runsInPlay = hadRuns || runsCarryStyling(runs);
|
|
639
957
|
const text = runsToPlainText(runs);
|
|
@@ -646,13 +964,13 @@ function useTextEdit(opts) {
|
|
|
646
964
|
if (setRuns && runsInPlay) setRuns(id, runs);
|
|
647
965
|
}
|
|
648
966
|
setEditingId(null);
|
|
649
|
-
}, [editingId, optsRef, holdNodeStyle]);
|
|
967
|
+
}, [editingId, optsRef, holdNodeStyle, setEditingId]);
|
|
650
968
|
const startEdit = useCallback((id, opts2) => {
|
|
651
969
|
initialCaretRef.current = opts2?.caret ?? "all";
|
|
652
970
|
setEditingId(id);
|
|
653
|
-
}, []);
|
|
971
|
+
}, [setEditingId]);
|
|
654
972
|
const isEditing = useCallback((id) => editingId === id, [editingId]);
|
|
655
|
-
const applyStyleToSelection = useCallback((patch)
|
|
973
|
+
const applyStyleToSelection = useCallback(function applyStyle(patch) {
|
|
656
974
|
const overlay = overlayRef.current;
|
|
657
975
|
if (!overlay) return;
|
|
658
976
|
if (Object.keys(patch).length === 0) return;
|
|
@@ -679,7 +997,7 @@ function useTextEdit(opts) {
|
|
|
679
997
|
restoreOverlayFocus(overlay, range);
|
|
680
998
|
const rest = { ...patch };
|
|
681
999
|
delete rest[unsetFlag];
|
|
682
|
-
if (Object.keys(rest).length > 0)
|
|
1000
|
+
if (Object.keys(rest).length > 0) applyStyle(rest);
|
|
683
1001
|
return;
|
|
684
1002
|
}
|
|
685
1003
|
if (range.start === range.end) {
|
|
@@ -716,13 +1034,7 @@ function useTextEdit(opts) {
|
|
|
716
1034
|
overlay.spellcheck = false;
|
|
717
1035
|
baseRef.current = style;
|
|
718
1036
|
const initialRuns = optsRef.current.getRuns?.(editingId);
|
|
719
|
-
|
|
720
|
-
runsToDom(initialRuns, overlay, style);
|
|
721
|
-
} else if (style.fontVariantCaps === "small-caps") {
|
|
722
|
-
runsToDom([{ text: getText(editingId) }], overlay, style);
|
|
723
|
-
} else {
|
|
724
|
-
appendOverlayText(overlay, getText(editingId));
|
|
725
|
-
}
|
|
1037
|
+
holderRef.current = seedOverlay(overlay, getText(editingId), initialRuns, style);
|
|
726
1038
|
openedRef.current = JSON.stringify(domToRuns(overlay));
|
|
727
1039
|
applyOverlayStyle(overlay, style);
|
|
728
1040
|
const hold = holdForFaces(style, initialRuns ?? []);
|
|
@@ -780,67 +1092,26 @@ function useTextEdit(opts) {
|
|
|
780
1092
|
};
|
|
781
1093
|
const onBlur = (e) => {
|
|
782
1094
|
const next = e.relatedTarget;
|
|
783
|
-
|
|
784
|
-
if (isChrome && next instanceof Element && isChrome(next)) return;
|
|
1095
|
+
if (isChrome(optsRef.current, next)) return;
|
|
785
1096
|
commit();
|
|
786
1097
|
};
|
|
787
|
-
const onBeforeInput = (
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
if (!at || at.start !== at.end) return;
|
|
793
|
-
ie.preventDefault();
|
|
794
|
-
const next = spliceRunAt(domToRuns(overlay), at.start, {
|
|
795
|
-
...pending,
|
|
796
|
-
text: ie.data
|
|
797
|
-
});
|
|
798
|
-
const after = at.start + ie.data.length;
|
|
799
|
-
writeRunsPreservingSelection(overlay, next, after, after, baseRef.current);
|
|
800
|
-
placeCaretBetweenRuns(overlay, next, after);
|
|
1098
|
+
const { onBeforeInput, onInput } = typingHandlers(overlay, {
|
|
1099
|
+
pending: () => pendingRef.current,
|
|
1100
|
+
base: () => baseRef.current,
|
|
1101
|
+
lastRange: () => selectionRef.current,
|
|
1102
|
+
onWritten: (caret) => {
|
|
801
1103
|
dropPending();
|
|
802
|
-
publishRange(overlay, { start:
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
if (!sel2 || sel2.rangeCount === 0) return;
|
|
808
|
-
const range2 = sel2.getRangeAt(0);
|
|
809
|
-
range2.deleteContents();
|
|
810
|
-
{
|
|
811
|
-
const textNode = document.createTextNode(ie.data);
|
|
812
|
-
range2.insertNode(textNode);
|
|
813
|
-
const after = document.createRange();
|
|
814
|
-
after.setStartAfter(textNode);
|
|
815
|
-
after.collapse(true);
|
|
816
|
-
sel2.removeAllRanges();
|
|
817
|
-
sel2.addRange(after);
|
|
1104
|
+
publishRange(overlay, { start: caret, end: caret });
|
|
1105
|
+
},
|
|
1106
|
+
onEdited: () => {
|
|
1107
|
+
holderRef.current?.edited(overlay);
|
|
1108
|
+
place();
|
|
818
1109
|
}
|
|
819
|
-
|
|
820
|
-
};
|
|
821
|
-
const resplitSmallCaps = (e) => {
|
|
822
|
-
if (e instanceof InputEvent && e.isComposing) return;
|
|
823
|
-
const at = readSelectionOffsets(overlay);
|
|
824
|
-
if (!normalizeSmallCaps(overlay) || !at) return;
|
|
825
|
-
const a = charOffsetToDomPosition(overlay, at.start);
|
|
826
|
-
const b = charOffsetToDomPosition(overlay, at.end);
|
|
827
|
-
const sel2 = window.getSelection();
|
|
828
|
-
if (!a || !b || !sel2) return;
|
|
829
|
-
const r = document.createRange();
|
|
830
|
-
r.setStart(a.node, a.offset);
|
|
831
|
-
r.setEnd(b.node, b.offset);
|
|
832
|
-
sel2.removeAllRanges();
|
|
833
|
-
sel2.addRange(r);
|
|
834
|
-
};
|
|
835
|
-
const onInput = (e) => {
|
|
836
|
-
resplitSmallCaps(e);
|
|
837
|
-
place();
|
|
838
|
-
};
|
|
1110
|
+
});
|
|
839
1111
|
const onPointerDownOutside = (e) => {
|
|
840
1112
|
const target = e.target;
|
|
841
1113
|
if (!(target instanceof Node) || overlay.contains(target)) return;
|
|
842
|
-
|
|
843
|
-
if (isChrome && target instanceof Element && isChrome(target)) return;
|
|
1114
|
+
if (isChrome(optsRef.current, target)) return;
|
|
844
1115
|
commit();
|
|
845
1116
|
};
|
|
846
1117
|
overlay.addEventListener("keydown", onKeyDown);
|
|
@@ -897,9 +1168,21 @@ function useTextEdit(opts) {
|
|
|
897
1168
|
rangeStyle,
|
|
898
1169
|
pendingStyle,
|
|
899
1170
|
applyStyleToSelection,
|
|
900
|
-
toggleStyle
|
|
1171
|
+
toggleStyle,
|
|
1172
|
+
effectiveStyle: shownStyle(opts, editingId, nodeStyle, rangeStyle, pendingStyle)
|
|
901
1173
|
};
|
|
902
1174
|
}
|
|
1175
|
+
function isChrome(opts, el) {
|
|
1176
|
+
return el instanceof Element && (opts.isEditorChrome?.(el) ?? false);
|
|
1177
|
+
}
|
|
1178
|
+
function shownStyle(opts, editingId, nodeStyle, rangeStyle, pendingStyle) {
|
|
1179
|
+
if (editingId === null) return null;
|
|
1180
|
+
return effectiveRangeStyle(
|
|
1181
|
+
patchRangeStyle(rangeStyle ?? {}, pendingStyle),
|
|
1182
|
+
nodeStyle ?? opts.getStyle(editingId),
|
|
1183
|
+
opts.getPaint?.(editingId)
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
903
1186
|
var OVERLAY_SEQ = 0;
|
|
904
1187
|
function holdForFaces(style, runs) {
|
|
905
1188
|
const pending = [
|
|
@@ -940,40 +1223,6 @@ function createClipBox() {
|
|
|
940
1223
|
el.style.pointerEvents = "none";
|
|
941
1224
|
return el;
|
|
942
1225
|
}
|
|
943
|
-
function applyOverlayStyle(el, style) {
|
|
944
|
-
el.style.position = "absolute";
|
|
945
|
-
el.style.pointerEvents = "auto";
|
|
946
|
-
el.style.boxSizing = "border-box";
|
|
947
|
-
el.style.margin = "0";
|
|
948
|
-
el.style.padding = "0";
|
|
949
|
-
el.style.border = "0";
|
|
950
|
-
el.style.outline = "none";
|
|
951
|
-
el.style.background = "transparent";
|
|
952
|
-
el.style.color = style.fill !== null && "color" in style.fill ? style.fill.color : "#000";
|
|
953
|
-
el.style.caretColor = style.caretColor;
|
|
954
|
-
const fontFamily = cssFontFamily(style.fontFamily, {
|
|
955
|
-
weight: numericWeight(style.fontWeight),
|
|
956
|
-
style: style.fontStyle
|
|
957
|
-
});
|
|
958
|
-
el.style.font = fontString({ ...style, fontFamily });
|
|
959
|
-
el.style.textRendering = isCanvasFont(style.fontFamily) ? "" : "geometricPrecision";
|
|
960
|
-
el.style.lineHeight = String(style.lineHeight);
|
|
961
|
-
applyOverlayDirection(el, style);
|
|
962
|
-
const decorations = [];
|
|
963
|
-
if (style.underline) decorations.push("underline");
|
|
964
|
-
if (style.strikethrough) decorations.push("line-through");
|
|
965
|
-
if (style.overline) decorations.push("overline");
|
|
966
|
-
el.style.textDecoration = decorations.length > 0 ? decorations.join(" ") : "none";
|
|
967
|
-
el.style.textTransform = style.textTransform;
|
|
968
|
-
el.style.fontVariant = style.fontVariantCaps;
|
|
969
|
-
el.style.setProperty(
|
|
970
|
-
SMALL_CAPS_SCALE_PROPERTY,
|
|
971
|
-
style.fontVariantCaps === "small-caps" ? String(smallCapsScaleFor(style.fontFamily, numericWeight(style.fontWeight), style.fontStyle)) : ""
|
|
972
|
-
);
|
|
973
|
-
el.style.whiteSpace = style.wrap ? "pre-wrap" : "pre";
|
|
974
|
-
el.style.overflowWrap = "normal";
|
|
975
|
-
el.style.wordBreak = "normal";
|
|
976
|
-
}
|
|
977
1226
|
function installSelectionStyle(overlayClass, style) {
|
|
978
1227
|
if (style.selectionBackground == null && style.selectionColor == null) return null;
|
|
979
1228
|
const el = document.createElement("style");
|
|
@@ -1064,7 +1313,8 @@ function placeOverlay(el, clipBox, pose, clip, style) {
|
|
|
1064
1313
|
clipBox.style.height = `${clip?.height ?? 0}px`;
|
|
1065
1314
|
el.style.display = "";
|
|
1066
1315
|
const anchor = style.wrap ? 0 : ALIGN_ANCHOR[overlayAlignEdge(el, style)];
|
|
1067
|
-
|
|
1316
|
+
const left = pose.x + pose.width * (pose.zoom ?? 1) * anchor;
|
|
1317
|
+
el.style.left = `${left - (clip?.x ?? 0)}px`;
|
|
1068
1318
|
el.style.width = style.wrap ? `${pose.width}px` : "max-content";
|
|
1069
1319
|
el.style.minWidth = style.wrap ? "" : `${pose.width}px`;
|
|
1070
1320
|
const script = style.script ? scriptMetricsFor(style.fontFamily, numericWeight(style.fontWeight), style.fontStyle)[style.script] : void 0;
|
|
@@ -1074,16 +1324,14 @@ function placeOverlay(el, clipBox, pose, clip, style) {
|
|
|
1074
1324
|
const aligned = pose.verticalAlign !== void 0 && pose.verticalAlign !== "top";
|
|
1075
1325
|
el.style.minHeight = aligned ? "" : `${pose.height}px`;
|
|
1076
1326
|
const drop = baselineDrop(el, style, pose, lineHeight) - (script?.shift ?? 0) * pose.fontSize;
|
|
1077
|
-
|
|
1327
|
+
const top = overlayTop(pose, aligned ? el.offsetHeight : pose.height, drop);
|
|
1328
|
+
el.style.top = `${top - (clip?.y ?? 0)}px`;
|
|
1078
1329
|
el.style.transformOrigin = "0 0";
|
|
1079
|
-
|
|
1080
|
-
if (pose.zoom !== void 0 && pose.zoom !== 1) transforms.push(`scale(${pose.zoom})`);
|
|
1081
|
-
if (anchor !== 0) transforms.push(`translateX(${-anchor * 100}%)`);
|
|
1082
|
-
el.style.transform = transforms.length > 0 ? transforms.join(" ") : "none";
|
|
1330
|
+
el.style.transform = overlayTransform(pose, left, top, anchor);
|
|
1083
1331
|
const scale = pose.zoom !== void 0 ? 1 : style.fontSize > 0 ? pose.fontSize / style.fontSize : 1;
|
|
1084
1332
|
el.style.letterSpacing = `${style.letterSpacing * scale}px`;
|
|
1085
1333
|
}
|
|
1086
1334
|
|
|
1087
|
-
export { TEXT_EDIT_FONT_HOLD,
|
|
1335
|
+
export { TEXT_EDIT_FONT_HOLD, charOffsetToDomPosition, domPositionToCharOffset, domToRuns, runsToDom, useTextEdit };
|
|
1088
1336
|
//# sourceMappingURL=index.js.map
|
|
1089
1337
|
//# sourceMappingURL=index.js.map
|