docxodus 12.0.1 → 12.2.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/README.md +9 -4
- package/dist/editor.bundle.js +197 -25
- package/dist/editor.d.ts +42 -3
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +204 -16
- package/dist/editor.js.map +1 -1
- package/dist/embed.bundle.js +635 -50
- package/dist/embed.d.ts +30 -1
- package/dist/embed.d.ts.map +1 -1
- package/dist/embed.iife.js +635 -50
- package/dist/embed.js +124 -13
- package/dist/embed.js.map +1 -1
- package/dist/export-assets.json +33 -33
- package/dist/history.d.ts +265 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +307 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -2
- package/dist/index.js.map +1 -1
- package/dist/ribbon-chrome.d.ts +2 -2
- package/dist/ribbon-chrome.d.ts.map +1 -1
- package/dist/ribbon-chrome.js +6 -4
- package/dist/ribbon-chrome.js.map +1 -1
- package/dist/ribbon.js +39 -8
- package/dist/ribbon.js.map +1 -1
- package/dist/session.js +1 -1
- package/dist/session.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/wasm/_framework/DocumentFormat.OpenXml.Framework.wasm +0 -0
- package/dist/wasm/_framework/DocumentFormat.OpenXml.Framework.wasm.br +0 -0
- package/dist/wasm/_framework/DocumentFormat.OpenXml.wasm +0 -0
- package/dist/wasm/_framework/DocumentFormat.OpenXml.wasm.br +0 -0
- package/dist/wasm/_framework/Docxodus.wasm +0 -0
- package/dist/wasm/_framework/Docxodus.wasm.br +0 -0
- package/dist/wasm/_framework/DocxodusWasm.wasm +0 -0
- package/dist/wasm/_framework/DocxodusWasm.wasm.br +0 -0
- package/dist/wasm/_framework/System.Collections.Concurrent.wasm +0 -0
- package/dist/wasm/_framework/System.Collections.Concurrent.wasm.br +0 -0
- package/dist/wasm/_framework/System.Collections.wasm +0 -0
- package/dist/wasm/_framework/System.Collections.wasm.br +0 -0
- package/dist/wasm/_framework/System.IO.Compression.wasm +0 -0
- package/dist/wasm/_framework/System.IO.Compression.wasm.br +0 -0
- package/dist/wasm/_framework/System.IO.Packaging.wasm +0 -0
- package/dist/wasm/_framework/System.IO.Packaging.wasm.br +0 -0
- package/dist/wasm/_framework/System.Linq.Expressions.wasm +0 -0
- package/dist/wasm/_framework/System.Linq.Expressions.wasm.br +0 -0
- package/dist/wasm/_framework/System.Memory.wasm +0 -0
- package/dist/wasm/_framework/System.Memory.wasm.br +0 -0
- package/dist/wasm/_framework/System.Net.Http.wasm +0 -0
- package/dist/wasm/_framework/System.Net.Http.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.CoreLib.wasm +0 -0
- package/dist/wasm/_framework/System.Private.CoreLib.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.Xml.Linq.wasm +0 -0
- package/dist/wasm/_framework/System.Private.Xml.Linq.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.Xml.wasm +0 -0
- package/dist/wasm/_framework/System.Private.Xml.wasm.br +0 -0
- package/dist/wasm/_framework/System.Runtime.InteropServices.JavaScript.wasm +0 -0
- package/dist/wasm/_framework/System.Runtime.InteropServices.JavaScript.wasm.br +0 -0
- package/dist/wasm/_framework/System.Runtime.wasm +0 -0
- package/dist/wasm/_framework/System.Runtime.wasm.br +0 -0
- package/dist/wasm/_framework/System.Security.Cryptography.wasm +0 -0
- package/dist/wasm/_framework/System.Security.Cryptography.wasm.br +0 -0
- package/dist/wasm/_framework/System.Text.Json.wasm +0 -0
- package/dist/wasm/_framework/System.Text.Json.wasm.br +0 -0
- package/dist/wasm/_framework/System.Text.RegularExpressions.wasm +0 -0
- package/dist/wasm/_framework/System.Text.RegularExpressions.wasm.br +0 -0
- package/dist/wasm/_framework/dotnet.boot.js +21 -21
- package/dist/wasm/_framework/dotnet.boot.js.br +0 -0
- package/dist/wasm/_framework/dotnet.native.wasm +0 -0
- package/dist/wasm/_framework/dotnet.native.wasm.br +0 -0
- package/package.json +4 -3
package/dist/editor.js
CHANGED
|
@@ -37,6 +37,14 @@ function blockPreviewText(unit) {
|
|
|
37
37
|
const text = (unit.textContent ?? "").trim().replace(/\s+/g, " ");
|
|
38
38
|
return text.length > 48 ? `${text.slice(0, 48)}…` : text;
|
|
39
39
|
}
|
|
40
|
+
/** The floating handle's own box and its gap to the block, as `ensureBlockDragStyles` sizes it. */
|
|
41
|
+
const BLOCK_HANDLE_WIDTH = 26;
|
|
42
|
+
const BLOCK_HANDLE_HEIGHT = 28;
|
|
43
|
+
const BLOCK_HANDLE_GAP = 6;
|
|
44
|
+
/** `value` pulled into [`low`, `high`]; `low` wins when the range is narrower than the box. */
|
|
45
|
+
function clampToRange(value, low, high) {
|
|
46
|
+
return Math.max(low, Math.min(value, high));
|
|
47
|
+
}
|
|
40
48
|
function ensureBlockDragStyles(doc) {
|
|
41
49
|
if (blockDragStyledDocuments.has(doc))
|
|
42
50
|
return;
|
|
@@ -606,16 +614,20 @@ function selectionSpanIn(block) {
|
|
|
606
614
|
const span = trimmedSpan(block, { start: Math.min(start, end), length: Math.abs(end - start) });
|
|
607
615
|
return span.length > 0 ? span : null;
|
|
608
616
|
}
|
|
609
|
-
/**
|
|
610
|
-
|
|
611
|
-
|
|
617
|
+
/**
|
|
618
|
+
* A DOM Range over the content text spanning [start, start+length) within `el` (skips markers),
|
|
619
|
+
* or null when the block is detached or the offsets do not resolve. Building the range is kept
|
|
620
|
+
* separate from selecting it so a caller that only wants to PAINT a span (find preview) does not
|
|
621
|
+
* have to touch the selection — and therefore focus — at all.
|
|
622
|
+
*/
|
|
623
|
+
function contentRangeIn(el, start, length) {
|
|
612
624
|
// The block may have been swapped out of the document by a re-render before this runs
|
|
613
625
|
// (e.g. a focus-stealing toolbar control firing twice). addRange on a detached range
|
|
614
626
|
// throws "the given range isn't in document" — skip rather than warn.
|
|
615
|
-
if (!
|
|
616
|
-
return;
|
|
617
|
-
el.
|
|
618
|
-
const range =
|
|
627
|
+
if (!el.isConnected)
|
|
628
|
+
return null;
|
|
629
|
+
const doc = el.ownerDocument;
|
|
630
|
+
const range = doc.createRange();
|
|
619
631
|
const from = contentPositionIn(el, start);
|
|
620
632
|
const to = contentPositionIn(el, start + length);
|
|
621
633
|
try {
|
|
@@ -623,11 +635,66 @@ function selectRange(el, start, length) {
|
|
|
623
635
|
range.setEnd(to.node, to.offset);
|
|
624
636
|
}
|
|
625
637
|
catch {
|
|
626
|
-
return;
|
|
638
|
+
return null;
|
|
627
639
|
}
|
|
640
|
+
return range;
|
|
641
|
+
}
|
|
642
|
+
/** Restore a content-text selection spanning [start, start+length) within `el` (skips markers). */
|
|
643
|
+
function selectRange(el, start, length) {
|
|
644
|
+
const sel = typeof window !== "undefined" ? window.getSelection() : null;
|
|
645
|
+
if (!sel || !el.isConnected)
|
|
646
|
+
return;
|
|
647
|
+
el.focus();
|
|
648
|
+
const range = contentRangeIn(el, start, length);
|
|
649
|
+
if (!range)
|
|
650
|
+
return;
|
|
628
651
|
sel.removeAllRanges();
|
|
629
652
|
sel.addRange(range);
|
|
630
653
|
}
|
|
654
|
+
// ─── Find painting ──────────────────────────────────────────────────────────
|
|
655
|
+
//
|
|
656
|
+
// A find box has to show WHERE the hits are while the user is still typing into it, so it cannot
|
|
657
|
+
// express "the current match" as a selection: putting a caret in a contenteditable block focuses
|
|
658
|
+
// that block, and the next keystroke lands in the document instead of the search field. These
|
|
659
|
+
// highlights paint the same information with no focus and no DOM mutation — the CSS Custom
|
|
660
|
+
// Highlight API takes plain Ranges over the live text and styles them from the stylesheet.
|
|
661
|
+
/** Every match currently on screen. */
|
|
662
|
+
const FIND_HIGHLIGHT = "docxodus-find";
|
|
663
|
+
/** The one the find bar's counter is pointing at. */
|
|
664
|
+
const FIND_HIGHLIGHT_ACTIVE = "docxodus-find-active";
|
|
665
|
+
/** Ranges are cheap, but a pathological query ("e" in a book) is not worth painting in full. */
|
|
666
|
+
const FIND_PAINT_LIMIT = 500;
|
|
667
|
+
const findHighlightStyledDocuments = new WeakSet();
|
|
668
|
+
/**
|
|
669
|
+
* Which editor last painted into the shared registry. Only that instance may clear it, so a
|
|
670
|
+
* second surface on the page cannot wipe the first one's find painting when its own find bar
|
|
671
|
+
* closes. Two live searches still share one painting — the newer one wins, which is what a
|
|
672
|
+
* single visible "current match" should mean.
|
|
673
|
+
*/
|
|
674
|
+
let findPaintOwner = null;
|
|
675
|
+
function ensureFindHighlightStyles(doc) {
|
|
676
|
+
if (findHighlightStyledDocuments.has(doc))
|
|
677
|
+
return;
|
|
678
|
+
findHighlightStyledDocuments.add(doc);
|
|
679
|
+
const style = doc.createElement("style");
|
|
680
|
+
style.dataset.docxodusFindHighlight = "true";
|
|
681
|
+
style.textContent = `
|
|
682
|
+
::highlight(${FIND_HIGHLIGHT}) { background-color: rgba(15, 118, 110, .16); }
|
|
683
|
+
::highlight(${FIND_HIGHLIGHT_ACTIVE}) { background-color: rgba(15, 118, 110, .38); }
|
|
684
|
+
`;
|
|
685
|
+
(doc.head ?? doc.documentElement).appendChild(style);
|
|
686
|
+
}
|
|
687
|
+
function highlightRegistry() {
|
|
688
|
+
if (typeof CSS === "undefined")
|
|
689
|
+
return null;
|
|
690
|
+
const registry = CSS.highlights;
|
|
691
|
+
const ctor = globalThis.Highlight;
|
|
692
|
+
return registry && typeof ctor === "function" ? registry : null;
|
|
693
|
+
}
|
|
694
|
+
function makeHighlight(ranges) {
|
|
695
|
+
const ctor = globalThis.Highlight;
|
|
696
|
+
return new ctor(...ranges);
|
|
697
|
+
}
|
|
631
698
|
/**
|
|
632
699
|
* True when `el`'s immediate parent is a paragraph-border `<div>` the full render wrapped it in
|
|
633
700
|
* (CreateBorderDivs groups visibly-bordered paragraphs into a div). The body wrapper div has no
|
|
@@ -697,6 +764,10 @@ export class DocxEditor {
|
|
|
697
764
|
this.blockMoveMenu = null;
|
|
698
765
|
this.blockMoveLive = null;
|
|
699
766
|
this.blockDragSource = null;
|
|
767
|
+
/** Whether hover or focus currently wants a handle for `blockDragSource`.
|
|
768
|
+
* Tracked apart from the handle's `display`, which the viewport clip also drives: a handle
|
|
769
|
+
* withdrawn because its block scrolled out of view has to come back when it scrolls in. */
|
|
770
|
+
this.blockHandleWanted = false;
|
|
700
771
|
this.blockDragCleanup = [];
|
|
701
772
|
/** Block boxes measured at drag start — see `BlockDropZone`. Empty when no drag is in flight. */
|
|
702
773
|
this.dropZones = [];
|
|
@@ -1009,6 +1080,7 @@ export class DocxEditor {
|
|
|
1009
1080
|
document.removeEventListener("mouseup", this.onMouseUp, true);
|
|
1010
1081
|
}
|
|
1011
1082
|
this.clearDragSelection();
|
|
1083
|
+
this.clearFindMatches();
|
|
1012
1084
|
this.teardownBlockDrag();
|
|
1013
1085
|
this.gutter?.dispose();
|
|
1014
1086
|
this.gutter = null;
|
|
@@ -1151,12 +1223,49 @@ export class DocxEditor {
|
|
|
1151
1223
|
}
|
|
1152
1224
|
return null;
|
|
1153
1225
|
}
|
|
1226
|
+
/**
|
|
1227
|
+
* The rectangle the floating handle may occupy: the window, narrowed by every ancestor that
|
|
1228
|
+
* clips its overflow.
|
|
1229
|
+
*
|
|
1230
|
+
* The handle is `position: fixed`, so it is placed in viewport coordinates and no ancestor's
|
|
1231
|
+
* overflow clips it for us. But the editor is routinely mounted inside a bounded scroller —
|
|
1232
|
+
* the ribbon puts its surface in one, inside a clipped card — and a block scrolled out of that
|
|
1233
|
+
* scroller has to take its handle with it rather than leave it parked over the host's chrome.
|
|
1234
|
+
*
|
|
1235
|
+
* Gated on the computed `overflow` rather than on whether an element scrolls right now, so the
|
|
1236
|
+
* answer does not depend on how much content happens to be loaded.
|
|
1237
|
+
*/
|
|
1238
|
+
blockHandleClipRect() {
|
|
1239
|
+
const view = this.container.ownerDocument.defaultView;
|
|
1240
|
+
const clip = {
|
|
1241
|
+
top: 0,
|
|
1242
|
+
left: 0,
|
|
1243
|
+
right: view?.innerWidth ?? Number.MAX_SAFE_INTEGER,
|
|
1244
|
+
bottom: view?.innerHeight ?? Number.MAX_SAFE_INTEGER,
|
|
1245
|
+
};
|
|
1246
|
+
for (let el = this.editRoot; el; el = el.parentElement) {
|
|
1247
|
+
const style = view?.getComputedStyle(el);
|
|
1248
|
+
if (!style || (style.overflowX === "visible" && style.overflowY === "visible"))
|
|
1249
|
+
continue;
|
|
1250
|
+
const box = el.getBoundingClientRect();
|
|
1251
|
+
if (style.overflowY !== "visible") {
|
|
1252
|
+
clip.top = Math.max(clip.top, box.top);
|
|
1253
|
+
clip.bottom = Math.min(clip.bottom, box.bottom);
|
|
1254
|
+
}
|
|
1255
|
+
if (style.overflowX !== "visible") {
|
|
1256
|
+
clip.left = Math.max(clip.left, box.left);
|
|
1257
|
+
clip.right = Math.min(clip.right, box.right);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
return clip;
|
|
1261
|
+
}
|
|
1154
1262
|
showBlockHandle(unit) {
|
|
1155
1263
|
const handle = this.blockDragHandle;
|
|
1156
1264
|
if (!handle || !this.isMovableBlockUnit(unit))
|
|
1157
1265
|
return;
|
|
1158
1266
|
const changed = unit !== this.blockDragSource;
|
|
1159
1267
|
this.blockDragSource = unit;
|
|
1268
|
+
this.blockHandleWanted = true;
|
|
1160
1269
|
// A block the engine will not move anywhere — one owning a section break, or already carrying
|
|
1161
1270
|
// revision markup a tracked move would have to re-wrap — gets no handle rather than a handle
|
|
1162
1271
|
// that always fails. Asking costs an engine round trip, so hovering only ever CONSUMES a
|
|
@@ -1170,21 +1279,40 @@ export class DocxEditor {
|
|
|
1170
1279
|
if (changed && known === undefined)
|
|
1171
1280
|
this.prefetchBlockMoveTargets(unit);
|
|
1172
1281
|
const rect = unit.getBoundingClientRect();
|
|
1282
|
+
// The handle belongs to the editor's viewport, not the window's. A block scrolled out of that
|
|
1283
|
+
// viewport gets no handle at all: clamping one back into view would leave a grip sitting next
|
|
1284
|
+
// to a block that is not there — and clamping to the WINDOW, as this used to, parked it on
|
|
1285
|
+
// whatever chrome the host draws above the editor.
|
|
1286
|
+
const clip = this.blockHandleClipRect();
|
|
1287
|
+
if (rect.bottom <= clip.top || rect.top >= clip.bottom
|
|
1288
|
+
|| rect.right <= clip.left || rect.left >= clip.right) {
|
|
1289
|
+
// A gesture in flight owns the handle: it is the drag's own source element, and an open
|
|
1290
|
+
// move menu is anchored to it and hands focus back to it on Escape. Withdrawing it
|
|
1291
|
+
// mid-gesture is the same mistake as leaving it behind — mirror `hideBlockHandle`'s guard
|
|
1292
|
+
// and leave it where the gesture put it.
|
|
1293
|
+
if (!this.blockDragging && this.blockMoveMenu?.style.display !== "block")
|
|
1294
|
+
handle.style.display = "none";
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1173
1297
|
handle.style.display = "flex";
|
|
1174
|
-
|
|
1175
|
-
|
|
1298
|
+
// A block only partly in view keeps its handle, pulled to the edge it is disappearing past.
|
|
1299
|
+
const top = rect.top
|
|
1300
|
+
+ (unit.tagName === "TABLE" ? 6 : Math.max(0, (rect.height - BLOCK_HANDLE_HEIGHT) / 2));
|
|
1301
|
+
handle.style.left = `${clampToRange(rect.left - (BLOCK_HANDLE_WIDTH + BLOCK_HANDLE_GAP), clip.left, clip.right - BLOCK_HANDLE_WIDTH)}px`;
|
|
1302
|
+
handle.style.top = `${clampToRange(top, clip.top, clip.bottom - BLOCK_HANDLE_HEIGHT)}px`;
|
|
1176
1303
|
const preview = blockPreviewText(unit);
|
|
1177
1304
|
handle.setAttribute("aria-label", preview ? `Move block: ${preview}` : "Move block");
|
|
1178
1305
|
}
|
|
1179
1306
|
hideBlockHandle() {
|
|
1180
1307
|
if (this.blockDragging || this.blockMoveMenu?.style.display === "block")
|
|
1181
1308
|
return;
|
|
1309
|
+
this.blockHandleWanted = false;
|
|
1182
1310
|
if (this.blockDragHandle)
|
|
1183
1311
|
this.blockDragHandle.style.display = "none";
|
|
1184
1312
|
}
|
|
1185
1313
|
positionBlockHandle() {
|
|
1186
1314
|
const source = this.currentBlockDragSource();
|
|
1187
|
-
if (source && this.
|
|
1315
|
+
if (source && this.blockHandleWanted)
|
|
1188
1316
|
this.showBlockHandle(source);
|
|
1189
1317
|
}
|
|
1190
1318
|
/** Draw the drop line on `zone`'s requested edge, or take it away when there is no target. */
|
|
@@ -1663,6 +1791,7 @@ export class DocxEditor {
|
|
|
1663
1791
|
this.blockMoveMenu = null;
|
|
1664
1792
|
this.blockMoveLive = null;
|
|
1665
1793
|
this.blockDragSource = null;
|
|
1794
|
+
this.blockHandleWanted = false;
|
|
1666
1795
|
this.blockDragging = false;
|
|
1667
1796
|
this.blockDragPointerDown = false;
|
|
1668
1797
|
}
|
|
@@ -3642,16 +3771,75 @@ export class DocxEditor {
|
|
|
3642
3771
|
}
|
|
3643
3772
|
return out;
|
|
3644
3773
|
}
|
|
3645
|
-
/**
|
|
3774
|
+
/**
|
|
3775
|
+
* Select a match, focus its block and scroll it into view — the caret lands on the hit, so
|
|
3776
|
+
* typing continues in the document. A find box that is still being typed into wants
|
|
3777
|
+
* `showFindMatches` instead; this is the commit step (closing the bar, jumping in to edit).
|
|
3778
|
+
*/
|
|
3646
3779
|
selectMatch(match) {
|
|
3647
3780
|
if (!match.block.isConnected)
|
|
3648
3781
|
return;
|
|
3782
|
+
this.clearFindMatches();
|
|
3649
3783
|
this.activeBlock = match.block;
|
|
3650
3784
|
selectRange(match.block, match.start, match.length);
|
|
3651
3785
|
match.block.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
3652
3786
|
}
|
|
3653
|
-
/**
|
|
3654
|
-
|
|
3787
|
+
/**
|
|
3788
|
+
* Paint `matches` and scroll the one at `activeIndex` into view WITHOUT moving focus or the
|
|
3789
|
+
* caret. This is what a find field calls on every keystroke: the document shows where the hits
|
|
3790
|
+
* are and rides to the current one, while the keyboard stays in the search box.
|
|
3791
|
+
*
|
|
3792
|
+
* Falls back to the document selection where the CSS Custom Highlight API is missing — still
|
|
3793
|
+
* without focusing the block, so the next character typed goes to the search field either way.
|
|
3794
|
+
*/
|
|
3795
|
+
showFindMatches(matches, activeIndex) {
|
|
3796
|
+
const active = matches[activeIndex];
|
|
3797
|
+
this.clearFindMatches();
|
|
3798
|
+
if (!active || !active.block.isConnected)
|
|
3799
|
+
return;
|
|
3800
|
+
this.activeBlock = active.block;
|
|
3801
|
+
const activeRange = contentRangeIn(active.block, active.start, active.length);
|
|
3802
|
+
const registry = highlightRegistry();
|
|
3803
|
+
if (registry && activeRange) {
|
|
3804
|
+
ensureFindHighlightStyles(this.container.ownerDocument);
|
|
3805
|
+
const rest = [];
|
|
3806
|
+
for (let i = 0; i < matches.length && rest.length < FIND_PAINT_LIMIT; i++) {
|
|
3807
|
+
if (i === activeIndex)
|
|
3808
|
+
continue;
|
|
3809
|
+
const range = contentRangeIn(matches[i].block, matches[i].start, matches[i].length);
|
|
3810
|
+
if (range)
|
|
3811
|
+
rest.push(range);
|
|
3812
|
+
}
|
|
3813
|
+
if (rest.length > 0)
|
|
3814
|
+
registry.set(FIND_HIGHLIGHT, makeHighlight(rest));
|
|
3815
|
+
registry.set(FIND_HIGHLIGHT_ACTIVE, makeHighlight([activeRange]));
|
|
3816
|
+
findPaintOwner = this;
|
|
3817
|
+
}
|
|
3818
|
+
else if (activeRange) {
|
|
3819
|
+
// No highlight registry (older Firefox/Safari): the document selection is the only paint
|
|
3820
|
+
// available. It is not registry-owned, so `clearFindMatches` leaves it — the next
|
|
3821
|
+
// `showFindMatches` or the closing `selectMatch` overwrites it.
|
|
3822
|
+
const sel = this.container.ownerDocument.defaultView?.getSelection();
|
|
3823
|
+
sel?.removeAllRanges();
|
|
3824
|
+
sel?.addRange(activeRange);
|
|
3825
|
+
}
|
|
3826
|
+
active.block.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
3827
|
+
}
|
|
3828
|
+
/** Drop the find painting (closing the find bar, or committing a match to the caret). */
|
|
3829
|
+
clearFindMatches() {
|
|
3830
|
+
if (findPaintOwner !== this)
|
|
3831
|
+
return;
|
|
3832
|
+
const registry = highlightRegistry();
|
|
3833
|
+
registry?.delete(FIND_HIGHLIGHT);
|
|
3834
|
+
registry?.delete(FIND_HIGHLIGHT_ACTIVE);
|
|
3835
|
+
findPaintOwner = null;
|
|
3836
|
+
}
|
|
3837
|
+
/**
|
|
3838
|
+
* Replace one match's text (formatting of the surrounding run is inherited). `focus: false`
|
|
3839
|
+
* leaves the keyboard where it is — a Replace button pressed from the find bar must not drag
|
|
3840
|
+
* the caret into the document mid-search.
|
|
3841
|
+
*/
|
|
3842
|
+
replaceMatch(match, replacement, options = {}) {
|
|
3655
3843
|
const block = match.block;
|
|
3656
3844
|
if (this.closed || !block.isConnected)
|
|
3657
3845
|
return false;
|
|
@@ -3669,7 +3857,7 @@ export class DocxEditor {
|
|
|
3669
3857
|
if (!res.success)
|
|
3670
3858
|
return false;
|
|
3671
3859
|
const fresh = this.swapBlock(block, unid, res.modified?.[0]);
|
|
3672
|
-
if (fresh)
|
|
3860
|
+
if (fresh && options.focus !== false)
|
|
3673
3861
|
selectRange(fresh, span.start, replacement.length);
|
|
3674
3862
|
return true;
|
|
3675
3863
|
}
|
|
@@ -3692,7 +3880,7 @@ export class DocxEditor {
|
|
|
3692
3880
|
if (!current.isConnected)
|
|
3693
3881
|
break;
|
|
3694
3882
|
const before = current;
|
|
3695
|
-
if (this.replaceMatch({ block: current, start: m.start, length: m.length }, replacement)) {
|
|
3883
|
+
if (this.replaceMatch({ block: current, start: m.start, length: m.length }, replacement, { focus: options.focus })) {
|
|
3696
3884
|
count++;
|
|
3697
3885
|
// swapBlock replaced the node; keep following it.
|
|
3698
3886
|
current = this.activeBlock && this.activeBlock !== before ? this.activeBlock : current;
|