docxodus 9.3.0 → 9.5.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/editor.bundle.js +588 -114
- package/dist/editor.d.ts +87 -11
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +317 -100
- package/dist/editor.js.map +1 -1
- package/dist/embed.bundle.js +621 -115
- package/dist/embed.iife.js +621 -115
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/page-geometry.d.ts +74 -0
- package/dist/page-geometry.d.ts.map +1 -0
- package/dist/page-geometry.js +105 -0
- package/dist/page-geometry.js.map +1 -0
- package/dist/pagination.bundle.js +8 -6
- package/dist/pagination.d.ts +2 -25
- package/dist/pagination.d.ts.map +1 -1
- package/dist/pagination.js +2 -45
- package/dist/pagination.js.map +1 -1
- package/dist/ribbon-chrome.d.ts +1 -1
- package/dist/ribbon-chrome.d.ts.map +1 -1
- package/dist/ribbon-chrome.js +18 -18
- package/dist/ribbon.js +2 -0
- package/dist/ribbon.js.map +1 -1
- package/dist/session.bundle.js +25 -1
- package/dist/session.d.ts +20 -2
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +23 -1
- package/dist/session.js.map +1 -1
- package/dist/types.d.ts +8 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/viewport.d.ts +80 -0
- package/dist/viewport.d.ts.map +1 -0
- package/dist/viewport.js +139 -0
- package/dist/viewport.js.map +1 -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.Private.CoreLib.wasm +0 -0
- package/dist/wasm/_framework/System.Private.CoreLib.wasm.br +0 -0
- package/dist/wasm/_framework/dotnet.boot.js +5 -5
- 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 +1 -1
package/dist/editor.bundle.js
CHANGED
|
@@ -161,20 +161,18 @@ var DocxodusEditor = (() => {
|
|
|
161
161
|
return (renderer ?? RENDERERS.decimal)(value);
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
-
// src/
|
|
164
|
+
// src/page-geometry.ts
|
|
165
165
|
var DEFAULT_PAGE_WIDTH = 612;
|
|
166
166
|
var DEFAULT_PAGE_HEIGHT = 792;
|
|
167
167
|
var DEFAULT_MARGIN = 72;
|
|
168
|
-
var
|
|
169
|
-
var MIN_BODY_CONTENT_HEIGHT = 72;
|
|
168
|
+
var DEFAULT_HEADER_FOOTER_HEIGHT = 36;
|
|
170
169
|
function pxToPt(px) {
|
|
171
170
|
return px * 0.75;
|
|
172
171
|
}
|
|
173
172
|
function ptToPx(pt) {
|
|
174
173
|
return pt / 0.75;
|
|
175
174
|
}
|
|
176
|
-
|
|
177
|
-
function parseDimensions(section) {
|
|
175
|
+
function parseSectionDimensions(section) {
|
|
178
176
|
const pageWidth = parseFloat(section.dataset.pageWidth || "") || DEFAULT_PAGE_WIDTH;
|
|
179
177
|
const pageHeight = parseFloat(section.dataset.pageHeight || "") || DEFAULT_PAGE_HEIGHT;
|
|
180
178
|
const contentWidth = parseFloat(section.dataset.contentWidth || "") || pageWidth - 2 * DEFAULT_MARGIN;
|
|
@@ -198,6 +196,42 @@ var DocxodusEditor = (() => {
|
|
|
198
196
|
footerHeight
|
|
199
197
|
};
|
|
200
198
|
}
|
|
199
|
+
function sectionWrappers(root) {
|
|
200
|
+
const sections = Array.from(root.querySelectorAll("[data-section-index]"));
|
|
201
|
+
return sections.length > 0 ? sections : [root];
|
|
202
|
+
}
|
|
203
|
+
var MIN_FIT_SCALE = 0.25;
|
|
204
|
+
function fitScale(availablePx, naturalPt, max = 1) {
|
|
205
|
+
if (!(availablePx > 0) || !(naturalPt > 0)) return max;
|
|
206
|
+
const naturalPx = ptToPx(naturalPt);
|
|
207
|
+
if (naturalPx <= availablePx) return max;
|
|
208
|
+
return Math.max(MIN_FIT_SCALE, Math.min(max, availablePx / naturalPx));
|
|
209
|
+
}
|
|
210
|
+
function applyZoom(el, scale, naturalPt) {
|
|
211
|
+
if (scale === 1) {
|
|
212
|
+
el.style.removeProperty("zoom");
|
|
213
|
+
el.style.removeProperty("transform");
|
|
214
|
+
el.style.removeProperty("transform-origin");
|
|
215
|
+
el.style.removeProperty("margin-right");
|
|
216
|
+
el.style.removeProperty("margin-bottom");
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const zoomSupported = typeof CSS !== "undefined" && typeof CSS.supports === "function" && CSS.supports("zoom", "0.5");
|
|
220
|
+
if (zoomSupported) {
|
|
221
|
+
el.style.zoom = String(scale);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
el.style.transform = `scale(${scale})`;
|
|
225
|
+
el.style.transformOrigin = "top left";
|
|
226
|
+
if (naturalPt) {
|
|
227
|
+
el.style.marginRight = `-${ptToPx(naturalPt.width * (1 - scale))}px`;
|
|
228
|
+
el.style.marginBottom = `-${ptToPx(naturalPt.height * (1 - scale))}px`;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// src/pagination.ts
|
|
233
|
+
var MAX_FOOTNOTE_AREA_RATIO = 0.6;
|
|
234
|
+
var MIN_BODY_CONTENT_HEIGHT = 72;
|
|
201
235
|
var PaginationEngine = class {
|
|
202
236
|
/**
|
|
203
237
|
* Creates a new pagination engine.
|
|
@@ -243,7 +277,7 @@ var DocxodusEditor = (() => {
|
|
|
243
277
|
const sectionsToProcess = sections.length > 0 ? Array.from(sections) : [this.stagingElement];
|
|
244
278
|
for (const section of sectionsToProcess) {
|
|
245
279
|
const sectionIndex = parseInt(section.dataset.sectionIndex || "0", 10);
|
|
246
|
-
const dims =
|
|
280
|
+
const dims = parseSectionDimensions(section);
|
|
247
281
|
this.stagingElement.style.visibility = "hidden";
|
|
248
282
|
this.stagingElement.style.position = "absolute";
|
|
249
283
|
this.stagingElement.style.left = "-9999px";
|
|
@@ -1541,6 +1575,114 @@ var DocxodusEditor = (() => {
|
|
|
1541
1575
|
return engine.paginate();
|
|
1542
1576
|
}
|
|
1543
1577
|
|
|
1578
|
+
// src/viewport.ts
|
|
1579
|
+
var DocumentViewport = class {
|
|
1580
|
+
constructor(host, options = {}) {
|
|
1581
|
+
this.root = null;
|
|
1582
|
+
/** Natural page size in points — the widest section's PAGE box, which is what must fit. */
|
|
1583
|
+
this.natural = { width: 0, height: 0 };
|
|
1584
|
+
this.observer = null;
|
|
1585
|
+
this.host = host;
|
|
1586
|
+
this.options = {
|
|
1587
|
+
columnWidth: options.columnWidth ?? "section",
|
|
1588
|
+
fitToWidth: options.fitToWidth ?? true,
|
|
1589
|
+
scale: options.scale ?? 1
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* Adopt a freshly mounted document root (a continuous flow, or the paginated page stack).
|
|
1594
|
+
* Safe to call on every remount; the previous root is released first.
|
|
1595
|
+
*
|
|
1596
|
+
* `applySectionGeometry` is false for the paginated view, which already builds real page
|
|
1597
|
+
* boxes at the section's dimensions — there the viewport contributes only the fit zoom.
|
|
1598
|
+
*/
|
|
1599
|
+
attach(root, applySectionGeometry) {
|
|
1600
|
+
this.release();
|
|
1601
|
+
this.root = root;
|
|
1602
|
+
this.natural = applySectionGeometry ? this.stampSections(root) : this.measurePages(root);
|
|
1603
|
+
this.refresh();
|
|
1604
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
1605
|
+
this.observer = new ResizeObserver(() => this.refresh());
|
|
1606
|
+
this.observer.observe(this.host);
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
/** Recompute the fit zoom against the host's current width. */
|
|
1610
|
+
refresh() {
|
|
1611
|
+
if (!this.root) return;
|
|
1612
|
+
const scale = this.scale;
|
|
1613
|
+
applyZoom(this.root, scale, this.natural);
|
|
1614
|
+
this.host.style.setProperty(
|
|
1615
|
+
"--docx-sheet-width",
|
|
1616
|
+
this.natural.width > 0 ? `${ptToPx(this.natural.width) * scale}px` : "100%"
|
|
1617
|
+
);
|
|
1618
|
+
}
|
|
1619
|
+
/** The zoom currently applied (1 = 100%). Reported by the ribbon's anchor rail. */
|
|
1620
|
+
get scale() {
|
|
1621
|
+
if (!this.root) return this.options.scale;
|
|
1622
|
+
return this.options.fitToWidth ? fitScale(this.availableWidthPx(), this.natural.width, this.options.scale) : this.options.scale;
|
|
1623
|
+
}
|
|
1624
|
+
dispose() {
|
|
1625
|
+
this.release();
|
|
1626
|
+
this.root = null;
|
|
1627
|
+
}
|
|
1628
|
+
release() {
|
|
1629
|
+
this.observer?.disconnect();
|
|
1630
|
+
this.observer = null;
|
|
1631
|
+
if (this.root) applyZoom(this.root, 1);
|
|
1632
|
+
this.host.style.removeProperty("--docx-sheet-width");
|
|
1633
|
+
}
|
|
1634
|
+
/** The host's content box, which is the space a page has to fit into. */
|
|
1635
|
+
availableWidthPx() {
|
|
1636
|
+
const style = typeof getComputedStyle === "function" ? getComputedStyle(this.host) : null;
|
|
1637
|
+
const padding = style ? (parseFloat(style.paddingLeft) || 0) + (parseFloat(style.paddingRight) || 0) : 0;
|
|
1638
|
+
return Math.max(0, this.host.clientWidth - padding);
|
|
1639
|
+
}
|
|
1640
|
+
/**
|
|
1641
|
+
* Give each section wrapper its `w:sectPr` geometry: the authored text column, guttered by
|
|
1642
|
+
* the authored margins. The wrapper then measures exactly one page wide, which is what the
|
|
1643
|
+
* sheet chrome paints and what the fit zoom scales.
|
|
1644
|
+
*/
|
|
1645
|
+
stampSections(root) {
|
|
1646
|
+
const sections = sectionWrappers(root);
|
|
1647
|
+
if (this.options.columnWidth === "fluid") {
|
|
1648
|
+
root.style.removeProperty("width");
|
|
1649
|
+
for (const section of sections) {
|
|
1650
|
+
section.style.removeProperty("width");
|
|
1651
|
+
section.style.removeProperty("padding-left");
|
|
1652
|
+
section.style.removeProperty("padding-right");
|
|
1653
|
+
}
|
|
1654
|
+
return { width: 0, height: 0 };
|
|
1655
|
+
}
|
|
1656
|
+
let widest = 0;
|
|
1657
|
+
for (const section of sections) {
|
|
1658
|
+
const dims = parseSectionDimensions(section);
|
|
1659
|
+
section.style.width = `${dims.contentWidth}pt`;
|
|
1660
|
+
section.style.paddingLeft = `${dims.marginLeft}pt`;
|
|
1661
|
+
section.style.paddingRight = `${dims.marginRight}pt`;
|
|
1662
|
+
section.style.boxSizing = "content-box";
|
|
1663
|
+
section.style.marginLeft = "auto";
|
|
1664
|
+
section.style.marginRight = "auto";
|
|
1665
|
+
widest = Math.max(widest, dims.pageWidth);
|
|
1666
|
+
}
|
|
1667
|
+
if (widest > 0 && sections[0] !== root) root.style.width = `${widest}pt`;
|
|
1668
|
+
return { width: widest, height: 0 };
|
|
1669
|
+
}
|
|
1670
|
+
/**
|
|
1671
|
+
* The paginated view's page boxes are already page-sized — `pagination.ts` writes each
|
|
1672
|
+
* box's `width` in points — so the widest of those is the natural width. Reading the
|
|
1673
|
+
* inline width rather than the laid-out box keeps this independent of the per-box zoom
|
|
1674
|
+
* pagination may itself have applied.
|
|
1675
|
+
*/
|
|
1676
|
+
measurePages(root) {
|
|
1677
|
+
let widest = 0;
|
|
1678
|
+
for (const box of Array.from(root.children)) {
|
|
1679
|
+
const declared = /^([\d.]+)pt$/.exec(box.style.width || "");
|
|
1680
|
+
widest = Math.max(widest, declared ? parseFloat(declared[1]) : pxToPt(box.offsetWidth));
|
|
1681
|
+
}
|
|
1682
|
+
return { width: widest, height: 0 };
|
|
1683
|
+
}
|
|
1684
|
+
};
|
|
1685
|
+
|
|
1544
1686
|
// src/editor-headerfooter.ts
|
|
1545
1687
|
var PAGE_FORMAT_LABELS = [
|
|
1546
1688
|
{ value: "", label: "Format\u2026" },
|
|
@@ -3629,6 +3771,146 @@ var DocxodusEditor = (() => {
|
|
|
3629
3771
|
return once(cleanup);
|
|
3630
3772
|
}
|
|
3631
3773
|
|
|
3774
|
+
// node_modules/@atlaskit/pragmatic-drag-and-drop/dist/esm/public-utils/element/custom-native-drag-preview/set-custom-native-drag-preview.js
|
|
3775
|
+
function ownKeys4(e, r) {
|
|
3776
|
+
var t = Object.keys(e);
|
|
3777
|
+
if (Object.getOwnPropertySymbols) {
|
|
3778
|
+
var o = Object.getOwnPropertySymbols(e);
|
|
3779
|
+
r && (o = o.filter(function(r2) {
|
|
3780
|
+
return Object.getOwnPropertyDescriptor(e, r2).enumerable;
|
|
3781
|
+
})), t.push.apply(t, o);
|
|
3782
|
+
}
|
|
3783
|
+
return t;
|
|
3784
|
+
}
|
|
3785
|
+
function _objectSpread4(e) {
|
|
3786
|
+
for (var r = 1; r < arguments.length; r++) {
|
|
3787
|
+
var t = null != arguments[r] ? arguments[r] : {};
|
|
3788
|
+
r % 2 ? ownKeys4(Object(t), true).forEach(function(r2) {
|
|
3789
|
+
_defineProperty(e, r2, t[r2]);
|
|
3790
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys4(Object(t)).forEach(function(r2) {
|
|
3791
|
+
Object.defineProperty(e, r2, Object.getOwnPropertyDescriptor(t, r2));
|
|
3792
|
+
});
|
|
3793
|
+
}
|
|
3794
|
+
return e;
|
|
3795
|
+
}
|
|
3796
|
+
function defaultOffset() {
|
|
3797
|
+
return {
|
|
3798
|
+
x: 0,
|
|
3799
|
+
y: 0
|
|
3800
|
+
};
|
|
3801
|
+
}
|
|
3802
|
+
function setCustomNativeDragPreview(_ref) {
|
|
3803
|
+
var render = _ref.render, nativeSetDragImage = _ref.nativeSetDragImage, _ref$getOffset = _ref.getOffset, getOffset = _ref$getOffset === void 0 ? defaultOffset : _ref$getOffset;
|
|
3804
|
+
var container = document.createElement("div");
|
|
3805
|
+
if (supportsPopover()) {
|
|
3806
|
+
container.setAttribute("popover", "manual");
|
|
3807
|
+
}
|
|
3808
|
+
Object.assign(container.style, _objectSpread4(_objectSpread4({
|
|
3809
|
+
// Ensuring we don't cause reflow when adding the element to the page
|
|
3810
|
+
// Using `position:fixed` rather than `position:absolute` so we are
|
|
3811
|
+
// positioned on the current viewport.
|
|
3812
|
+
// `position:fixed` also creates a new stacking context, so we don't need to do that here
|
|
3813
|
+
position: "fixed"
|
|
3814
|
+
}, supportsPopover() ? (
|
|
3815
|
+
// needs to come first as it has 'inset: unset' which
|
|
3816
|
+
// needs to be overridden by our top / left values
|
|
3817
|
+
popoverResetUserAgentStyles
|
|
3818
|
+
) : {
|
|
3819
|
+
// Fallback: using maximum possible z-index so that this element
|
|
3820
|
+
// will always be on top of other positioned content.
|
|
3821
|
+
zIndex: maxZIndex
|
|
3822
|
+
}), {}, {
|
|
3823
|
+
// According to `mdn`, the element can be offscreen:
|
|
3824
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/setDragImage#imgelement
|
|
3825
|
+
//
|
|
3826
|
+
// However, that information does not appear in the specs:
|
|
3827
|
+
// https://html.spec.whatwg.org/multipage/dnd.html#dom-datatransfer-setdragimage-dev
|
|
3828
|
+
//
|
|
3829
|
+
// If the element is _completely_ offscreen, Safari@17.1 will cancel the drag
|
|
3830
|
+
top: 0,
|
|
3831
|
+
left: 0,
|
|
3832
|
+
// Avoiding any additional events caused by the new element (being super safe)
|
|
3833
|
+
pointerEvents: "none"
|
|
3834
|
+
}));
|
|
3835
|
+
document.body.append(container);
|
|
3836
|
+
if (supportsPopover()) {
|
|
3837
|
+
container.showPopover();
|
|
3838
|
+
}
|
|
3839
|
+
var unmount = render({
|
|
3840
|
+
container
|
|
3841
|
+
});
|
|
3842
|
+
queueMicrotask(function() {
|
|
3843
|
+
var previewOffset = getOffset({
|
|
3844
|
+
container
|
|
3845
|
+
});
|
|
3846
|
+
if (isSafari()) {
|
|
3847
|
+
var rect = container.getBoundingClientRect();
|
|
3848
|
+
if (rect.width === 0) {
|
|
3849
|
+
return;
|
|
3850
|
+
}
|
|
3851
|
+
container.style.left = "-".concat(rect.width - 1e-4, "px");
|
|
3852
|
+
}
|
|
3853
|
+
nativeSetDragImage === null || nativeSetDragImage === void 0 || nativeSetDragImage(container, previewOffset.x, previewOffset.y);
|
|
3854
|
+
});
|
|
3855
|
+
function cleanup() {
|
|
3856
|
+
unbindMonitor();
|
|
3857
|
+
unmount === null || unmount === void 0 || unmount();
|
|
3858
|
+
document.body.removeChild(container);
|
|
3859
|
+
}
|
|
3860
|
+
var unbindMonitor = monitorForElements({
|
|
3861
|
+
// Remove portal in the dragstart event so that the user will never see it
|
|
3862
|
+
onDragStart: cleanup,
|
|
3863
|
+
// Backup: remove portal when the drop finishes (this would be an error case)
|
|
3864
|
+
onDrop: cleanup
|
|
3865
|
+
});
|
|
3866
|
+
}
|
|
3867
|
+
|
|
3868
|
+
// node_modules/@atlaskit/pragmatic-drag-and-drop/dist/esm/util/is-safari-on-ios.js
|
|
3869
|
+
var isSafariOnIOS = once(function isSafariOnIOS2() {
|
|
3870
|
+
if (false) {
|
|
3871
|
+
return false;
|
|
3872
|
+
}
|
|
3873
|
+
return isSafari() && "ontouchend" in document;
|
|
3874
|
+
});
|
|
3875
|
+
|
|
3876
|
+
// node_modules/@atlaskit/pragmatic-drag-and-drop/dist/esm/public-utils/element/custom-native-drag-preview/center-under-pointer.js
|
|
3877
|
+
var centerUnderPointer = function centerUnderPointer2(_ref) {
|
|
3878
|
+
var container = _ref.container;
|
|
3879
|
+
var rect = container.getBoundingClientRect();
|
|
3880
|
+
return {
|
|
3881
|
+
x: rect.width / 2,
|
|
3882
|
+
y: rect.height / 2
|
|
3883
|
+
};
|
|
3884
|
+
};
|
|
3885
|
+
|
|
3886
|
+
// node_modules/@atlaskit/pragmatic-drag-and-drop/dist/esm/public-utils/element/custom-native-drag-preview/pointer-outside-of-preview.js
|
|
3887
|
+
function pointerOutsideOfPreview(point) {
|
|
3888
|
+
return function getOffset(_ref) {
|
|
3889
|
+
var container = _ref.container;
|
|
3890
|
+
if (isSafariOnIOS() || isAndroid()) {
|
|
3891
|
+
return centerUnderPointer({
|
|
3892
|
+
container
|
|
3893
|
+
});
|
|
3894
|
+
}
|
|
3895
|
+
Object.assign(container.style, {
|
|
3896
|
+
borderInlineStart: "".concat(point.x, " solid transparent"),
|
|
3897
|
+
borderTop: "".concat(point.y, " solid transparent")
|
|
3898
|
+
});
|
|
3899
|
+
var computed = window.getComputedStyle(container);
|
|
3900
|
+
if (computed.direction === "rtl") {
|
|
3901
|
+
var box = container.getBoundingClientRect();
|
|
3902
|
+
return {
|
|
3903
|
+
x: box.width,
|
|
3904
|
+
y: 0
|
|
3905
|
+
};
|
|
3906
|
+
}
|
|
3907
|
+
return {
|
|
3908
|
+
x: 0,
|
|
3909
|
+
y: 0
|
|
3910
|
+
};
|
|
3911
|
+
};
|
|
3912
|
+
}
|
|
3913
|
+
|
|
3632
3914
|
// node_modules/@atlaskit/pragmatic-drag-and-drop-auto-scroll/dist/esm/shared/engagement-history.js
|
|
3633
3915
|
var ledger2 = /* @__PURE__ */ new Map();
|
|
3634
3916
|
var requested = /* @__PURE__ */ new Set();
|
|
@@ -3762,7 +4044,7 @@ var DocxodusEditor = (() => {
|
|
|
3762
4044
|
}
|
|
3763
4045
|
|
|
3764
4046
|
// node_modules/@atlaskit/pragmatic-drag-and-drop-auto-scroll/dist/esm/shared/configuration.js
|
|
3765
|
-
function
|
|
4047
|
+
function ownKeys5(e, r) {
|
|
3766
4048
|
var t = Object.keys(e);
|
|
3767
4049
|
if (Object.getOwnPropertySymbols) {
|
|
3768
4050
|
var o = Object.getOwnPropertySymbols(e);
|
|
@@ -3772,12 +4054,12 @@ var DocxodusEditor = (() => {
|
|
|
3772
4054
|
}
|
|
3773
4055
|
return t;
|
|
3774
4056
|
}
|
|
3775
|
-
function
|
|
4057
|
+
function _objectSpread5(e) {
|
|
3776
4058
|
for (var r = 1; r < arguments.length; r++) {
|
|
3777
4059
|
var t = null != arguments[r] ? arguments[r] : {};
|
|
3778
|
-
r % 2 ?
|
|
4060
|
+
r % 2 ? ownKeys5(Object(t), true).forEach(function(r2) {
|
|
3779
4061
|
_defineProperty(e, r2, t[r2]);
|
|
3780
|
-
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) :
|
|
4062
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys5(Object(t)).forEach(function(r2) {
|
|
3781
4063
|
Object.defineProperty(e, r2, Object.getOwnPropertyDescriptor(t, r2));
|
|
3782
4064
|
});
|
|
3783
4065
|
}
|
|
@@ -3812,7 +4094,7 @@ var DocxodusEditor = (() => {
|
|
|
3812
4094
|
};
|
|
3813
4095
|
function getInternalConfig(provided) {
|
|
3814
4096
|
var _provided$maxScrollSp;
|
|
3815
|
-
return
|
|
4097
|
+
return _objectSpread5(_objectSpread5({}, baseConfig), {}, {
|
|
3816
4098
|
// only allowing limited control over the config at this stage
|
|
3817
4099
|
maxPixelScrollPerSecond: maxPixelScrollPerSecond[(_provided$maxScrollSp = provided === null || provided === void 0 ? void 0 : provided.maxScrollSpeed) !== null && _provided$maxScrollSp !== void 0 ? _provided$maxScrollSp : "standard"]
|
|
3818
4100
|
});
|
|
@@ -4296,7 +4578,7 @@ var DocxodusEditor = (() => {
|
|
|
4296
4578
|
}
|
|
4297
4579
|
|
|
4298
4580
|
// node_modules/@atlaskit/pragmatic-drag-and-drop-auto-scroll/dist/esm/over-element/make-api.js
|
|
4299
|
-
function
|
|
4581
|
+
function ownKeys6(e, r) {
|
|
4300
4582
|
var t = Object.keys(e);
|
|
4301
4583
|
if (Object.getOwnPropertySymbols) {
|
|
4302
4584
|
var o = Object.getOwnPropertySymbols(e);
|
|
@@ -4306,12 +4588,12 @@ var DocxodusEditor = (() => {
|
|
|
4306
4588
|
}
|
|
4307
4589
|
return t;
|
|
4308
4590
|
}
|
|
4309
|
-
function
|
|
4591
|
+
function _objectSpread6(e) {
|
|
4310
4592
|
for (var r = 1; r < arguments.length; r++) {
|
|
4311
4593
|
var t = null != arguments[r] ? arguments[r] : {};
|
|
4312
|
-
r % 2 ?
|
|
4594
|
+
r % 2 ? ownKeys6(Object(t), true).forEach(function(r2) {
|
|
4313
4595
|
_defineProperty(e, r2, t[r2]);
|
|
4314
|
-
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) :
|
|
4596
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys6(Object(t)).forEach(function(r2) {
|
|
4315
4597
|
Object.defineProperty(e, r2, Object.getOwnPropertyDescriptor(t, r2));
|
|
4316
4598
|
});
|
|
4317
4599
|
}
|
|
@@ -4350,7 +4632,7 @@ var DocxodusEditor = (() => {
|
|
|
4350
4632
|
}
|
|
4351
4633
|
function autoScrollWindow() {
|
|
4352
4634
|
var args = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
|
|
4353
|
-
var unique =
|
|
4635
|
+
var unique = _objectSpread6({}, args);
|
|
4354
4636
|
windowRegistry.add(unique);
|
|
4355
4637
|
function cleanup() {
|
|
4356
4638
|
windowRegistry.delete(unique);
|
|
@@ -4503,6 +4785,11 @@ var DocxodusEditor = (() => {
|
|
|
4503
4785
|
var EDITABLE_TAGS = /* @__PURE__ */ new Set(["P", "H1", "H2", "H3", "H4", "H5", "H6"]);
|
|
4504
4786
|
var BLOCK_DRAG_TYPE = "docxodus-block";
|
|
4505
4787
|
var blockDragStyledDocuments = /* @__PURE__ */ new WeakSet();
|
|
4788
|
+
function blockPreviewText(unit) {
|
|
4789
|
+
if (unit.tagName === "TABLE") return "Table";
|
|
4790
|
+
const text = (unit.textContent ?? "").trim().replace(/\s+/g, " ");
|
|
4791
|
+
return text.length > 48 ? `${text.slice(0, 48)}\u2026` : text;
|
|
4792
|
+
}
|
|
4506
4793
|
function ensureBlockDragStyles(doc) {
|
|
4507
4794
|
if (blockDragStyledDocuments.has(doc)) return;
|
|
4508
4795
|
blockDragStyledDocuments.add(doc);
|
|
@@ -4517,10 +4804,28 @@ var DocxodusEditor = (() => {
|
|
|
4517
4804
|
}
|
|
4518
4805
|
.docx-block-handle:hover, .docx-block-handle:focus-visible { color: #344054; border-color: #98a2b3; outline: none; }
|
|
4519
4806
|
.docx-block-handle[aria-pressed="true"] { color: #175cd3; border-color: #84adff; background: #eff8ff; }
|
|
4520
|
-
.docx-block-handle.docx-block-dragging { cursor: grabbing; opacity: .
|
|
4807
|
+
.docx-block-handle.docx-block-dragging { cursor: grabbing; opacity: .35; }
|
|
4808
|
+
/* The block being carried. Dimming it is the "a drag is happening" signal that survives the
|
|
4809
|
+
pointer being anywhere on screen \u2014 the drop line only says where, not what. */
|
|
4810
|
+
.docx-block-drag-source { opacity: .38; transition: opacity 120ms ease-out; }
|
|
4811
|
+
/* Positioned by transform so tracking the pointer costs no layout. Flipping display none\u2192block
|
|
4812
|
+
restarts the fade \u2014 one cheap entry animation per appearance, none while it tracks. */
|
|
4521
4813
|
.docx-block-drop-indicator {
|
|
4522
|
-
position: fixed; z-index: 2147482999; display: none; height:
|
|
4523
|
-
|
|
4814
|
+
position: fixed; top: 0; left: 0; z-index: 2147482999; display: none; height: 0;
|
|
4815
|
+
pointer-events: none; border-top: 2px solid #2e90fa;
|
|
4816
|
+
filter: drop-shadow(0 1px 2px rgba(46,144,250,.5));
|
|
4817
|
+
animation: docx-block-drop-in 110ms ease-out;
|
|
4818
|
+
}
|
|
4819
|
+
.docx-block-drop-indicator::before {
|
|
4820
|
+
content: ""; position: absolute; top: -5px; left: -2px; width: 8px; height: 8px;
|
|
4821
|
+
border-radius: 50%; background: #2e90fa;
|
|
4822
|
+
}
|
|
4823
|
+
@keyframes docx-block-drop-in { from { opacity: 0; } to { opacity: 1; } }
|
|
4824
|
+
.docx-block-drag-preview {
|
|
4825
|
+
max-width: 320px; padding: 6px 10px; border: 1px solid #b2ddff; border-radius: 6px;
|
|
4826
|
+
background: #eff8ff; color: #175cd3; box-shadow: 0 6px 16px rgba(16,24,40,.18);
|
|
4827
|
+
font: 500 13px/1.35 system-ui, sans-serif; white-space: nowrap; overflow: hidden;
|
|
4828
|
+
text-overflow: ellipsis;
|
|
4524
4829
|
}
|
|
4525
4830
|
.docx-block-move-menu {
|
|
4526
4831
|
position: fixed; z-index: 2147483001; display: none; min-width: 150px; padding: 5px;
|
|
@@ -4908,12 +5213,22 @@ var DocxodusEditor = (() => {
|
|
|
4908
5213
|
this.blockMoveLive = null;
|
|
4909
5214
|
this.blockDragSource = null;
|
|
4910
5215
|
this.blockDragCleanup = [];
|
|
4911
|
-
|
|
5216
|
+
/** Block boxes measured at drag start — see `BlockDropZone`. Empty when no drag is in flight. */
|
|
5217
|
+
this.dropZones = [];
|
|
5218
|
+
/** Combined scroll offset when `dropZones` was measured, and the scroller measured against. */
|
|
5219
|
+
this.dropZoneOrigin = 0;
|
|
5220
|
+
this.dropZoneScroller = null;
|
|
4912
5221
|
this.blockDragging = false;
|
|
4913
5222
|
this.blockDragPointerDown = false;
|
|
4914
5223
|
/** Anchors the current drag source may legally move next to, per the engine's own rules.
|
|
4915
5224
|
* Null when the bridge predates ValidMoveTargets — then every block is offered, as before. */
|
|
4916
5225
|
this.blockMoveTargets = null;
|
|
5226
|
+
/** Memoized `ValidMoveTargets` answers, keyed by source anchor and dropped whenever an edit
|
|
5227
|
+
* lands. The legal-target set is a property of the document, so hovering back and forth over
|
|
5228
|
+
* the same blocks between edits must not re-ask the engine. */
|
|
5229
|
+
this.blockMoveTargetCache = /* @__PURE__ */ new Map();
|
|
5230
|
+
/** Cancels the pending idle prefetch of the hovered block's targets — see `showBlockHandle`. */
|
|
5231
|
+
this.blockMoveTargetPrefetch = null;
|
|
4917
5232
|
/** Why the last move was refused, verbatim from the engine — diagnostics, not announcement copy. */
|
|
4918
5233
|
this.lastMoveError = null;
|
|
4919
5234
|
/**
|
|
@@ -5031,6 +5346,11 @@ var DocxodusEditor = (() => {
|
|
|
5031
5346
|
this.handle = handle;
|
|
5032
5347
|
this.options = options;
|
|
5033
5348
|
this.editRoot = container;
|
|
5349
|
+
this.viewport = new DocumentViewport(container, {
|
|
5350
|
+
columnWidth: options.columnWidth,
|
|
5351
|
+
fitToWidth: options.fitToWidth,
|
|
5352
|
+
scale: options.scale
|
|
5353
|
+
});
|
|
5034
5354
|
if (typeof document !== "undefined") {
|
|
5035
5355
|
document.addEventListener("selectionchange", this.onSelectionChange);
|
|
5036
5356
|
document.addEventListener("mousedown", this.onMouseDown, true);
|
|
@@ -5113,6 +5433,8 @@ var DocxodusEditor = (() => {
|
|
|
5113
5433
|
editable: options.editable ?? true,
|
|
5114
5434
|
paginated: options.paginated ?? false,
|
|
5115
5435
|
scale: options.scale ?? 1,
|
|
5436
|
+
columnWidth: options.columnWidth ?? "section",
|
|
5437
|
+
fitToWidth: options.fitToWidth ?? true,
|
|
5116
5438
|
headerFooter: options.headerFooter ?? false,
|
|
5117
5439
|
blockDrag: options.blockDrag ?? false,
|
|
5118
5440
|
trackedChanges: options.trackedChanges ?? 0 /* Accept */,
|
|
@@ -5169,6 +5491,7 @@ var DocxodusEditor = (() => {
|
|
|
5169
5491
|
}
|
|
5170
5492
|
this.clearDragSelection();
|
|
5171
5493
|
this.teardownBlockDrag();
|
|
5494
|
+
this.viewport.dispose();
|
|
5172
5495
|
this.exports.DocxSessionBridge.CloseSession(this.handle);
|
|
5173
5496
|
}
|
|
5174
5497
|
/**
|
|
@@ -5186,6 +5509,14 @@ var DocxodusEditor = (() => {
|
|
|
5186
5509
|
get root() {
|
|
5187
5510
|
return this.container;
|
|
5188
5511
|
}
|
|
5512
|
+
/**
|
|
5513
|
+
* The zoom the viewport is currently applying (1 = 100%). Below 1 the page is wider than the
|
|
5514
|
+
* host and has been scaled to fit rather than reflowed — the honest thing to show a user who
|
|
5515
|
+
* is wondering why a phone shows the whole page.
|
|
5516
|
+
*/
|
|
5517
|
+
get zoom() {
|
|
5518
|
+
return this.viewport.scale;
|
|
5519
|
+
}
|
|
5189
5520
|
/**
|
|
5190
5521
|
* The live `DocxSession` handle backing this editor — the model of record.
|
|
5191
5522
|
*
|
|
@@ -5214,8 +5545,7 @@ var DocxodusEditor = (() => {
|
|
|
5214
5545
|
return true;
|
|
5215
5546
|
}
|
|
5216
5547
|
const destination = res.created?.[0] ?? res.modified?.[0];
|
|
5217
|
-
|
|
5218
|
-
else this.reconcile();
|
|
5548
|
+
this.reconcile();
|
|
5219
5549
|
const moved = destination ? this.bodyUnitNodes().find((el) => el.getAttribute("data-anchor") === destination.unid) : null;
|
|
5220
5550
|
if (moved) {
|
|
5221
5551
|
moved.classList.add("docx-block-move-flash");
|
|
@@ -5240,7 +5570,12 @@ var DocxodusEditor = (() => {
|
|
|
5240
5570
|
const node = target instanceof Node ? target : null;
|
|
5241
5571
|
const el = node?.nodeType === Node.ELEMENT_NODE ? node : node?.parentElement;
|
|
5242
5572
|
if (!el || !this.editRoot.contains(el)) return null;
|
|
5243
|
-
|
|
5573
|
+
let unit = null;
|
|
5574
|
+
for (let candidate = el.closest("[data-anchor]"); candidate && this.editRoot.contains(candidate); candidate = candidate.parentElement?.closest("[data-anchor]") ?? null) {
|
|
5575
|
+
unit = candidate;
|
|
5576
|
+
}
|
|
5577
|
+
if (!unit || unit.closest("section.footnotes, section.endnotes")) return null;
|
|
5578
|
+
return unit;
|
|
5244
5579
|
}
|
|
5245
5580
|
isMovableBlockUnit(unit) {
|
|
5246
5581
|
if (!unit || !this.anchorIdOf(unit)) return false;
|
|
@@ -5262,18 +5597,18 @@ var DocxodusEditor = (() => {
|
|
|
5262
5597
|
if (!handle || !this.isMovableBlockUnit(unit)) return;
|
|
5263
5598
|
const changed = unit !== this.blockDragSource;
|
|
5264
5599
|
this.blockDragSource = unit;
|
|
5265
|
-
|
|
5266
|
-
if (
|
|
5600
|
+
const known = this.blockMoveTargetsFor(unit, { cachedOnly: true });
|
|
5601
|
+
if (known?.size === 0) {
|
|
5267
5602
|
handle.style.display = "none";
|
|
5268
5603
|
return;
|
|
5269
5604
|
}
|
|
5605
|
+
if (changed && known === void 0) this.prefetchBlockMoveTargets(unit);
|
|
5270
5606
|
const rect = unit.getBoundingClientRect();
|
|
5271
5607
|
handle.style.display = "flex";
|
|
5272
5608
|
handle.style.left = `${Math.max(4, rect.left - 32)}px`;
|
|
5273
5609
|
handle.style.top = `${Math.max(4, rect.top + (unit.tagName === "TABLE" ? 6 : Math.max(0, (rect.height - 28) / 2)))}px`;
|
|
5274
|
-
const preview = (unit
|
|
5610
|
+
const preview = blockPreviewText(unit);
|
|
5275
5611
|
handle.setAttribute("aria-label", preview ? `Move block: ${preview}` : "Move block");
|
|
5276
|
-
if (changed) this.refreshBlockDropTargets();
|
|
5277
5612
|
}
|
|
5278
5613
|
hideBlockHandle() {
|
|
5279
5614
|
if (this.blockDragging || this.blockMoveMenu?.style.display === "block") return;
|
|
@@ -5283,14 +5618,33 @@ var DocxodusEditor = (() => {
|
|
|
5283
5618
|
const source = this.currentBlockDragSource();
|
|
5284
5619
|
if (source && this.blockDragHandle?.style.display !== "none") this.showBlockHandle(source);
|
|
5285
5620
|
}
|
|
5286
|
-
|
|
5621
|
+
/** Draw the drop line on `zone`'s requested edge, or take it away when there is no target. */
|
|
5622
|
+
paintDropIndicator(data) {
|
|
5287
5623
|
const indicator = this.blockDropIndicator;
|
|
5624
|
+
const zone = data.zone;
|
|
5288
5625
|
if (!indicator) return;
|
|
5289
|
-
|
|
5626
|
+
if (!zone) {
|
|
5627
|
+
this.hideDropIndicator();
|
|
5628
|
+
return;
|
|
5629
|
+
}
|
|
5630
|
+
const y = Math.round(this.dropEdgeY(zone, data.position === "after" ? "after" : "before") + this.dropZoneShift()) - 1;
|
|
5631
|
+
indicator.style.transform = `translate3d(${Math.round(zone.left)}px, ${y}px, 0)`;
|
|
5632
|
+
indicator.style.width = `${Math.max(24, Math.round(zone.width))}px`;
|
|
5290
5633
|
indicator.style.display = "block";
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5634
|
+
}
|
|
5635
|
+
/**
|
|
5636
|
+
* Where to draw the line for an insertion on `position` of `zone` — the MIDDLE of the gap to
|
|
5637
|
+
* the neighbour on that side, not the zone's own border-box edge. A paragraph's `w:spacing`
|
|
5638
|
+
* becomes a CSS margin, which sits outside the box, so drawing on the edge underlines the
|
|
5639
|
+
* block's last line instead of reading as a gap between two blocks. Falls back to the raw edge
|
|
5640
|
+
* at the ends of the flow, and degrades to the same value when blocks are contiguous.
|
|
5641
|
+
*/
|
|
5642
|
+
dropEdgeY(zone, position) {
|
|
5643
|
+
const neighbour = this.dropZones[zone.index + (position === "after" ? 1 : -1)];
|
|
5644
|
+
if (!neighbour) {
|
|
5645
|
+
return position === "after" ? zone.bottom + zone.marginAfter / 2 : zone.top - zone.marginBefore / 2;
|
|
5646
|
+
}
|
|
5647
|
+
return position === "after" ? (zone.bottom + neighbour.top) / 2 : (neighbour.bottom + zone.top) / 2;
|
|
5294
5648
|
}
|
|
5295
5649
|
hideDropIndicator() {
|
|
5296
5650
|
if (this.blockDropIndicator) this.blockDropIndicator.style.display = "none";
|
|
@@ -5310,19 +5664,52 @@ var DocxodusEditor = (() => {
|
|
|
5310
5664
|
* behaviour this replaces. Null (no bridge support) keeps the previous offer-everything path.
|
|
5311
5665
|
*/
|
|
5312
5666
|
refreshBlockMoveTargets(source) {
|
|
5667
|
+
this.blockMoveTargets = source ? this.blockMoveTargetsFor(source) ?? null : null;
|
|
5668
|
+
}
|
|
5669
|
+
/**
|
|
5670
|
+
* This block's legal destinations, from the memo when it is there. Returns `undefined` — not
|
|
5671
|
+
* `null` — for "not asked yet", so a caller can tell an unknown answer from the engine's
|
|
5672
|
+
* "no bridge support, offer everything" one.
|
|
5673
|
+
*/
|
|
5674
|
+
blockMoveTargetsFor(source, options = {}) {
|
|
5313
5675
|
const bridge = this.exports.DocxSessionBridge;
|
|
5314
|
-
const sourceId =
|
|
5315
|
-
if (!sourceId || typeof bridge.ValidMoveTargets !== "function")
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5676
|
+
const sourceId = this.anchorIdOf(source);
|
|
5677
|
+
if (!sourceId || typeof bridge.ValidMoveTargets !== "function") return null;
|
|
5678
|
+
if (this.blockMoveTargetCache.has(sourceId)) return this.blockMoveTargetCache.get(sourceId);
|
|
5679
|
+
if (options.cachedOnly) return void 0;
|
|
5680
|
+
let targets;
|
|
5319
5681
|
try {
|
|
5320
|
-
const
|
|
5321
|
-
|
|
5322
|
-
targets.map((t) => [t.anchorId, { before: t.before, after: t.after }])
|
|
5323
|
-
);
|
|
5682
|
+
const parsed = JSON.parse(bridge.ValidMoveTargets(this.handle, sourceId));
|
|
5683
|
+
targets = new Map(parsed.map((t) => [t.anchorId, { before: t.before, after: t.after }]));
|
|
5324
5684
|
} catch {
|
|
5325
|
-
|
|
5685
|
+
targets = null;
|
|
5686
|
+
}
|
|
5687
|
+
this.blockMoveTargetCache.set(sourceId, targets);
|
|
5688
|
+
return targets;
|
|
5689
|
+
}
|
|
5690
|
+
/**
|
|
5691
|
+
* Ask for the hovered block's destinations off the interaction path, and hide the handle if
|
|
5692
|
+
* the answer comes back empty and that block is still the one under the pointer. The handle
|
|
5693
|
+
* therefore appears immediately on hover and withdraws a beat later on the rare immovable
|
|
5694
|
+
* block, instead of every hover paying for the query up front.
|
|
5695
|
+
*/
|
|
5696
|
+
prefetchBlockMoveTargets(source) {
|
|
5697
|
+
const view = this.container.ownerDocument.defaultView;
|
|
5698
|
+
if (!view) return;
|
|
5699
|
+
this.blockMoveTargetPrefetch?.();
|
|
5700
|
+
const run = () => {
|
|
5701
|
+
this.blockMoveTargetPrefetch = null;
|
|
5702
|
+
if (this.closed || !source.isConnected || this.blockDragSource !== source) return;
|
|
5703
|
+
if (this.blockMoveTargetsFor(source)?.size === 0 && this.blockDragHandle)
|
|
5704
|
+
this.blockDragHandle.style.display = "none";
|
|
5705
|
+
};
|
|
5706
|
+
const idle = view;
|
|
5707
|
+
if (idle.requestIdleCallback && idle.cancelIdleCallback) {
|
|
5708
|
+
const id = idle.requestIdleCallback(run, { timeout: 500 });
|
|
5709
|
+
this.blockMoveTargetPrefetch = () => idle.cancelIdleCallback(id);
|
|
5710
|
+
} else {
|
|
5711
|
+
const id = view.setTimeout(run, 0);
|
|
5712
|
+
this.blockMoveTargetPrefetch = () => view.clearTimeout(id);
|
|
5326
5713
|
}
|
|
5327
5714
|
}
|
|
5328
5715
|
/**
|
|
@@ -5337,45 +5724,75 @@ var DocxodusEditor = (() => {
|
|
|
5337
5724
|
if (!sides) return false;
|
|
5338
5725
|
return position ? sides[position] : sides.before || sides.after;
|
|
5339
5726
|
}
|
|
5727
|
+
/** Measure every movable block once, at drag start. See `BlockDropZone`. */
|
|
5728
|
+
captureDropZones() {
|
|
5729
|
+
const view = this.container.ownerDocument.defaultView;
|
|
5730
|
+
this.dropZoneScroller = this.scrollContainer();
|
|
5731
|
+
this.dropZoneOrigin = this.scrollOffsetSum();
|
|
5732
|
+
this.dropZones = [];
|
|
5733
|
+
const boxes = [];
|
|
5734
|
+
for (const unit of this.bodyUnitNodes()) {
|
|
5735
|
+
const anchorId = this.isMovableBlockUnit(unit) ? this.anchorIdOf(unit) : null;
|
|
5736
|
+
if (!anchorId) continue;
|
|
5737
|
+
const box = this.unitWrapperOf(unit);
|
|
5738
|
+
const rect = box.getBoundingClientRect();
|
|
5739
|
+
boxes.push(box);
|
|
5740
|
+
this.dropZones.push({
|
|
5741
|
+
unit,
|
|
5742
|
+
anchorId,
|
|
5743
|
+
index: this.dropZones.length,
|
|
5744
|
+
top: rect.top,
|
|
5745
|
+
bottom: rect.bottom,
|
|
5746
|
+
left: rect.left,
|
|
5747
|
+
width: rect.width,
|
|
5748
|
+
marginBefore: 0,
|
|
5749
|
+
marginAfter: 0
|
|
5750
|
+
});
|
|
5751
|
+
}
|
|
5752
|
+
const ends = new Set([0, this.dropZones.length - 1].filter((i) => i >= 0 && i < boxes.length));
|
|
5753
|
+
for (const i of ends) {
|
|
5754
|
+
const style = view?.getComputedStyle(boxes[i]);
|
|
5755
|
+
this.dropZones[i].marginBefore = parseFloat(style?.marginTop ?? "0") || 0;
|
|
5756
|
+
this.dropZones[i].marginAfter = parseFloat(style?.marginBottom ?? "0") || 0;
|
|
5757
|
+
}
|
|
5758
|
+
}
|
|
5759
|
+
scrollOffsetSum() {
|
|
5760
|
+
const view = this.container.ownerDocument.defaultView;
|
|
5761
|
+
return (view?.scrollY ?? 0) + (this.dropZoneScroller?.scrollTop ?? 0);
|
|
5762
|
+
}
|
|
5763
|
+
/** How far the measured boxes have travelled since capture, from scrolling (drag autoscroll). */
|
|
5764
|
+
dropZoneShift() {
|
|
5765
|
+
return this.dropZoneOrigin - this.scrollOffsetSum();
|
|
5766
|
+
}
|
|
5340
5767
|
/**
|
|
5341
|
-
*
|
|
5342
|
-
*
|
|
5343
|
-
*
|
|
5344
|
-
*
|
|
5768
|
+
* Where a drop at `clientY` lands, or null when nothing there is legal.
|
|
5769
|
+
*
|
|
5770
|
+
* Resolution is by VERTICAL GEOMETRY over the measured blocks, not by which element the pointer
|
|
5771
|
+
* is over: the drag handle floats in the page margin, so a drag straight down the gutter — the
|
|
5772
|
+
* natural gesture — never crosses a paragraph box, and element hit testing gave those drags no
|
|
5773
|
+
* indicator and no drop at all. The nearest block by vertical distance is the target; the half
|
|
5774
|
+
* the pointer is in picks the side, snapped to the other side when only that one is legal
|
|
5775
|
+
* (a section break or a cross-block range usually makes exactly one side illegal). When neither
|
|
5776
|
+
* side is legal — the pointer is in a region this block cannot reach — there is no drop, and
|
|
5777
|
+
* nothing is drawn.
|
|
5345
5778
|
*/
|
|
5346
|
-
|
|
5347
|
-
const
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
const
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
this.blockDragTargetCleanup.push(dropTargetForElements({
|
|
5358
|
-
element: unit,
|
|
5359
|
-
// A target the engine would refuse is not a drop target at all, so Pragmatic never
|
|
5360
|
-
// fires onDragEnter for it and no indicator is drawn over it.
|
|
5361
|
-
canDrop: ({ source }) => source.data.type === BLOCK_DRAG_TYPE && source.data.sourceAnchorId !== this.anchorIdOf(unit) && this.isValidMoveTarget(unit),
|
|
5362
|
-
getData: ({ input }) => ({
|
|
5363
|
-
type: BLOCK_DRAG_TYPE,
|
|
5364
|
-
targetAnchorId: this.anchorIdOf(unit),
|
|
5365
|
-
position: this.dropPositionFor(unit, input.clientY),
|
|
5366
|
-
targetElement: unit
|
|
5367
|
-
}),
|
|
5368
|
-
onDragEnter: ({ self }) => {
|
|
5369
|
-
const pos = self.data.position === "after" ? "after" : "before";
|
|
5370
|
-
this.showDropIndicator(unit, pos);
|
|
5371
|
-
},
|
|
5372
|
-
onDrag: ({ self }) => {
|
|
5373
|
-
const pos = self.data.position === "after" ? "after" : "before";
|
|
5374
|
-
this.showDropIndicator(unit, pos);
|
|
5375
|
-
},
|
|
5376
|
-
onDragLeave: () => this.hideDropIndicator()
|
|
5377
|
-
}));
|
|
5779
|
+
resolveDropAt(clientY) {
|
|
5780
|
+
const y = clientY - this.dropZoneShift();
|
|
5781
|
+
let best = null;
|
|
5782
|
+
let bestGap = Infinity;
|
|
5783
|
+
for (const zone of this.dropZones) {
|
|
5784
|
+
const gap = y < zone.top ? zone.top - y : y > zone.bottom ? y - zone.bottom : 0;
|
|
5785
|
+
if (gap < bestGap) {
|
|
5786
|
+
best = zone;
|
|
5787
|
+
bestGap = gap;
|
|
5788
|
+
}
|
|
5789
|
+
if (gap === 0) break;
|
|
5378
5790
|
}
|
|
5791
|
+
if (!best || best.unit === this.blockDragSource) return null;
|
|
5792
|
+
const preferred = y < (best.top + best.bottom) / 2 ? "before" : "after";
|
|
5793
|
+
if (this.isValidMoveTarget(best.unit, preferred)) return { zone: best, position: preferred };
|
|
5794
|
+
const other = preferred === "before" ? "after" : "before";
|
|
5795
|
+
return this.isValidMoveTarget(best.unit, other) ? { zone: best, position: other } : null;
|
|
5379
5796
|
}
|
|
5380
5797
|
closeBlockMoveMenu(restoreFocus = false) {
|
|
5381
5798
|
if (!this.blockMoveMenu || !this.blockDragHandle) return;
|
|
@@ -5412,7 +5829,8 @@ var DocxodusEditor = (() => {
|
|
|
5412
5829
|
const index = units.indexOf(source);
|
|
5413
5830
|
if (index < 0) return null;
|
|
5414
5831
|
const position = action === "up" || action === "top" ? "before" : "after";
|
|
5415
|
-
const
|
|
5832
|
+
const side = position === "before" ? units.slice(0, index) : units.slice(index + 1);
|
|
5833
|
+
const candidates = side.filter((el) => this.isValidMoveTarget(el, position));
|
|
5416
5834
|
if (candidates.length === 0) return null;
|
|
5417
5835
|
if (action === "up") return { target: candidates[candidates.length - 1], position };
|
|
5418
5836
|
if (action === "down") return { target: candidates[0], position };
|
|
@@ -5549,20 +5967,50 @@ var DocxodusEditor = (() => {
|
|
|
5549
5967
|
const source = this.currentBlockDragSource();
|
|
5550
5968
|
return { type: BLOCK_DRAG_TYPE, sourceAnchorId: source ? this.anchorIdOf(source) : void 0 };
|
|
5551
5969
|
},
|
|
5970
|
+
// The browser would otherwise ghost the 26px grip, which says nothing about what is moving.
|
|
5971
|
+
onGenerateDragPreview: ({ nativeSetDragImage }) => {
|
|
5972
|
+
const source = this.currentBlockDragSource();
|
|
5973
|
+
setCustomNativeDragPreview({
|
|
5974
|
+
nativeSetDragImage,
|
|
5975
|
+
getOffset: pointerOutsideOfPreview({ x: "14px", y: "10px" }),
|
|
5976
|
+
render: ({ container }) => {
|
|
5977
|
+
const chip = doc.createElement("div");
|
|
5978
|
+
chip.className = "docx-block-drag-preview";
|
|
5979
|
+
chip.textContent = source && blockPreviewText(source) || "Move block";
|
|
5980
|
+
container.appendChild(chip);
|
|
5981
|
+
return () => chip.remove();
|
|
5982
|
+
}
|
|
5983
|
+
});
|
|
5984
|
+
},
|
|
5552
5985
|
onDragStart: () => {
|
|
5986
|
+
const source = this.currentBlockDragSource();
|
|
5553
5987
|
this.blockDragging = true;
|
|
5554
5988
|
handle.classList.add("docx-block-dragging");
|
|
5989
|
+
source?.classList.add("docx-block-drag-source");
|
|
5555
5990
|
this.closeBlockMoveMenu();
|
|
5556
|
-
this.refreshBlockMoveTargets(
|
|
5557
|
-
this.
|
|
5991
|
+
this.refreshBlockMoveTargets(source);
|
|
5992
|
+
this.captureDropZones();
|
|
5558
5993
|
},
|
|
5559
5994
|
onDrop: () => {
|
|
5560
5995
|
this.blockDragging = false;
|
|
5561
5996
|
this.blockDragPointerDown = false;
|
|
5997
|
+
this.dropZones = [];
|
|
5562
5998
|
handle.classList.remove("docx-block-dragging");
|
|
5999
|
+
doc.querySelectorAll(".docx-block-drag-source").forEach((el) => el.classList.remove("docx-block-drag-source"));
|
|
5563
6000
|
this.hideDropIndicator();
|
|
5564
6001
|
}
|
|
5565
6002
|
}));
|
|
6003
|
+
this.blockDragCleanup.push(dropTargetForElements({
|
|
6004
|
+
element: this.editRoot,
|
|
6005
|
+
canDrop: ({ source }) => source.data.type === BLOCK_DRAG_TYPE,
|
|
6006
|
+
getData: ({ input }) => {
|
|
6007
|
+
const hit = this.resolveDropAt(input.clientY);
|
|
6008
|
+
return hit ? { type: BLOCK_DRAG_TYPE, targetAnchorId: hit.zone.anchorId, position: hit.position, zone: hit.zone } : { type: BLOCK_DRAG_TYPE };
|
|
6009
|
+
},
|
|
6010
|
+
onDragEnter: ({ self }) => this.paintDropIndicator(self.data),
|
|
6011
|
+
onDrag: ({ self }) => this.paintDropIndicator(self.data),
|
|
6012
|
+
onDragLeave: () => this.hideDropIndicator()
|
|
6013
|
+
}));
|
|
5566
6014
|
this.blockDragCleanup.push(monitorForElements({
|
|
5567
6015
|
canMonitor: ({ source }) => source.data.type === BLOCK_DRAG_TYPE,
|
|
5568
6016
|
onDrop: ({ source, location: location2 }) => {
|
|
@@ -5589,10 +6037,13 @@ var DocxodusEditor = (() => {
|
|
|
5589
6037
|
canScroll: ({ source }) => source.data.type === BLOCK_DRAG_TYPE,
|
|
5590
6038
|
getAllowedAxis: () => "vertical"
|
|
5591
6039
|
}));
|
|
5592
|
-
this.refreshBlockDropTargets();
|
|
5593
6040
|
}
|
|
5594
6041
|
teardownBlockDrag() {
|
|
5595
|
-
|
|
6042
|
+
this.blockMoveTargetPrefetch?.();
|
|
6043
|
+
this.blockMoveTargetPrefetch = null;
|
|
6044
|
+
this.blockMoveTargetCache.clear();
|
|
6045
|
+
this.dropZones = [];
|
|
6046
|
+
this.dropZoneScroller = null;
|
|
5596
6047
|
for (const cleanup of this.blockDragCleanup.splice(0)) cleanup();
|
|
5597
6048
|
this.blockDragHandle?.remove();
|
|
5598
6049
|
this.blockDropIndicator?.remove();
|
|
@@ -5681,17 +6132,17 @@ var DocxodusEditor = (() => {
|
|
|
5681
6132
|
bodyRoot.after(this.region.footerBand);
|
|
5682
6133
|
this.region.refreshAll();
|
|
5683
6134
|
}
|
|
5684
|
-
/**
|
|
6135
|
+
/**
|
|
6136
|
+
* Continuous (non-paginated) mount: inject the converter's styles + body, wire blocks.
|
|
6137
|
+
*
|
|
6138
|
+
* The body always gets its own `.docx-body-flow` wrapper — not only when bands are docked.
|
|
6139
|
+
* It is the sheet: the element the viewport gives page geometry to and zooms, and the one
|
|
6140
|
+
* the bands dock around. Without it the container would have to be both the scrolling host
|
|
6141
|
+
* and the scaled page, which are different boxes.
|
|
6142
|
+
*/
|
|
5685
6143
|
mountHtml(fullHtml) {
|
|
5686
6144
|
const parsed = new DOMParser().parseFromString(fullHtml, "text/html");
|
|
5687
6145
|
const styles = Array.from(parsed.querySelectorAll("style")).map((s) => s.outerHTML).join("");
|
|
5688
|
-
if (!this.region) {
|
|
5689
|
-
this.container.innerHTML = styles + parsed.body.innerHTML;
|
|
5690
|
-
this.editRoot = this.container;
|
|
5691
|
-
if (this.options.editable) this.wireBlocks(this.container);
|
|
5692
|
-
this.stampPlanState();
|
|
5693
|
-
return;
|
|
5694
|
-
}
|
|
5695
6146
|
this.container.innerHTML = styles;
|
|
5696
6147
|
const flow = document.createElement("div");
|
|
5697
6148
|
flow.className = "docx-body-flow";
|
|
@@ -5700,7 +6151,8 @@ var DocxodusEditor = (() => {
|
|
|
5700
6151
|
this.editRoot = flow;
|
|
5701
6152
|
if (this.options.editable) this.wireBlocks(flow);
|
|
5702
6153
|
this.stampPlanState();
|
|
5703
|
-
this.dockBands(flow);
|
|
6154
|
+
if (this.region) this.dockBands(flow);
|
|
6155
|
+
this.viewport.attach(flow, true);
|
|
5704
6156
|
}
|
|
5705
6157
|
/** Paginated mount: flow blocks into page boxes via pagination.ts, wire the page clones. */
|
|
5706
6158
|
mountPaginated(fullHtml) {
|
|
@@ -5721,6 +6173,7 @@ var DocxodusEditor = (() => {
|
|
|
5721
6173
|
this.editRoot = pageRoot;
|
|
5722
6174
|
if (this.options.editable) this.wireBlocks(pageRoot);
|
|
5723
6175
|
if (this.region) this.dockBands(target);
|
|
6176
|
+
this.viewport.attach(pageRoot, false);
|
|
5724
6177
|
}
|
|
5725
6178
|
wireBlocks(root) {
|
|
5726
6179
|
root.querySelectorAll("[data-anchor]").forEach((el) => this.wireBlock(el));
|
|
@@ -6109,11 +6562,22 @@ var DocxodusEditor = (() => {
|
|
|
6109
6562
|
}
|
|
6110
6563
|
parseEdit(json) {
|
|
6111
6564
|
try {
|
|
6112
|
-
|
|
6565
|
+
const result = JSON.parse(json);
|
|
6566
|
+
if (result.success) this.invalidateBlockMoveTargets();
|
|
6567
|
+
return result;
|
|
6113
6568
|
} catch {
|
|
6114
6569
|
return { success: false };
|
|
6115
6570
|
}
|
|
6116
6571
|
}
|
|
6572
|
+
/**
|
|
6573
|
+
* Drop the memoized `ValidMoveTargets` answers. Which blocks a block may move next to is a
|
|
6574
|
+
* fact about the DOCUMENT, so it survives hovering but not editing — and the two places a
|
|
6575
|
+
* document changes are `parseEdit` (every mutation that returns an `EditResult`) and
|
|
6576
|
+
* undo/redo, which return a bare boolean and so cannot go through it.
|
|
6577
|
+
*/
|
|
6578
|
+
invalidateBlockMoveTargets() {
|
|
6579
|
+
this.blockMoveTargetCache.clear();
|
|
6580
|
+
}
|
|
6117
6581
|
// ─── M5: formatting commands (ribbon) ────────────────────────────────
|
|
6118
6582
|
// ─── Multi-block selection helpers (format a whole stack of paragraphs at once) ──────
|
|
6119
6583
|
/**
|
|
@@ -6667,12 +7131,16 @@ var DocxodusEditor = (() => {
|
|
|
6667
7131
|
/** Undo the last edit (incremental repaint; falls back to a full re-render). */
|
|
6668
7132
|
undo() {
|
|
6669
7133
|
if (this.closed) return;
|
|
6670
|
-
if (this.exports.DocxSessionBridge.Undo(this.handle))
|
|
7134
|
+
if (!this.exports.DocxSessionBridge.Undo(this.handle)) return;
|
|
7135
|
+
this.invalidateBlockMoveTargets();
|
|
7136
|
+
this.reconcile();
|
|
6671
7137
|
}
|
|
6672
7138
|
/** Redo the last undone edit (incremental repaint; falls back to a full re-render). */
|
|
6673
7139
|
redo() {
|
|
6674
7140
|
if (this.closed) return;
|
|
6675
|
-
if (this.exports.DocxSessionBridge.Redo(this.handle))
|
|
7141
|
+
if (!this.exports.DocxSessionBridge.Redo(this.handle)) return;
|
|
7142
|
+
this.invalidateBlockMoveTargets();
|
|
7143
|
+
this.reconcile();
|
|
6676
7144
|
}
|
|
6677
7145
|
// ─── Header/footer region commands (no-ops unless `headerFooter` is on) ───────────────
|
|
6678
7146
|
/**
|
|
@@ -6901,7 +7369,11 @@ var DocxodusEditor = (() => {
|
|
|
6901
7369
|
const oldTokens = oldNodes.map(_DocxEditor.domTokenOf);
|
|
6902
7370
|
const oldKinds = oldNodes.map(_DocxEditor.domKindOf);
|
|
6903
7371
|
const bodyDiff = diffUnits(oldTokens, plan.body);
|
|
6904
|
-
if (needsRemount(bodyDiff, plan.body, oldKinds))
|
|
7372
|
+
if (needsRemount(bodyDiff, plan.body, oldKinds)) {
|
|
7373
|
+
return this.bail(
|
|
7374
|
+
`needsRemount (li change or churn): +${bodyDiff.added.length} -${bodyDiff.removed.length} ~${bodyDiff.substituted.length} moved=${bodyDiff.moved.length} of ${plan.body.length}`
|
|
7375
|
+
);
|
|
7376
|
+
}
|
|
6905
7377
|
const fnState = this.notesDiff("footnotes", plan.footnotes);
|
|
6906
7378
|
const enState = this.notesDiff("endnotes", plan.endnotes);
|
|
6907
7379
|
if (fnState === null || enState === null) return this.bail("notes container unstampable/missing");
|
|
@@ -7510,14 +7982,7 @@ var DocxodusEditor = (() => {
|
|
|
7510
7982
|
SHOULD win.
|
|
7511
7983
|
(No backticks in this file's comments: the stylesheet is a template literal.) */
|
|
7512
7984
|
.dxr-scroll { flex: 1 1 auto; min-height: 0; overflow: auto; -webkit-overflow-scrolling: touch; }
|
|
7513
|
-
.dxr[data-chrome] .dxr-surface {
|
|
7514
|
-
.dxr-surface[data-view="continuous"] > * { background: var(--dxr-sheet); }
|
|
7515
|
-
.dxr[data-chrome] .dxr-surface[data-view="continuous"] {
|
|
7516
|
-
padding: 56px 72px;
|
|
7517
|
-
border-radius: 3px;
|
|
7518
|
-
background: var(--dxr-sheet);
|
|
7519
|
-
box-shadow: 0 1px 3px rgba(16, 20, 24, .14), 0 8px 24px rgba(16, 20, 24, .05);
|
|
7520
|
-
}
|
|
7985
|
+
.dxr[data-chrome] .dxr-surface { margin: 26px auto; padding: 0 16px 96px; }
|
|
7521
7986
|
.dxr-surface [contenteditable="true"]:focus {
|
|
7522
7987
|
outline: 2px solid var(--dxr-accent);
|
|
7523
7988
|
outline-offset: 2px;
|
|
@@ -7529,7 +7994,10 @@ var DocxodusEditor = (() => {
|
|
|
7529
7994
|
so they dock as their own regions. Styled from the same tokens as the ribbon
|
|
7530
7995
|
so the surface reads as one instrument rather than two apps. */
|
|
7531
7996
|
.dxr-surface .docx-hf-band {
|
|
7532
|
-
|
|
7997
|
+
/* Docked outside the zoomed sheet, so it takes the page's on-screen width from the
|
|
7998
|
+
custom property the viewport publishes rather than stretching to the whole surface. */
|
|
7999
|
+
width: min(100%, var(--docx-sheet-width, 100%));
|
|
8000
|
+
margin: 0 auto 14px;
|
|
7533
8001
|
padding: 9px 72px 13px;
|
|
7534
8002
|
border: 1px solid var(--dxr-rule);
|
|
7535
8003
|
border-left: 2px solid #c2ccd9;
|
|
@@ -7537,7 +8005,7 @@ var DocxodusEditor = (() => {
|
|
|
7537
8005
|
background: var(--dxr-sheet);
|
|
7538
8006
|
}
|
|
7539
8007
|
.dxr-surface .docx-hf-band + .docx-body-flow { margin-top: 0; }
|
|
7540
|
-
.dxr-surface .docx-hf-band[data-hf-band="footer"] { margin: 14px
|
|
8008
|
+
.dxr-surface .docx-hf-band[data-hf-band="footer"] { margin: 14px auto 0; }
|
|
7541
8009
|
.dxr-surface .docx-hf-chrome {
|
|
7542
8010
|
display: flex;
|
|
7543
8011
|
gap: 8px;
|
|
@@ -7592,13 +8060,15 @@ var DocxodusEditor = (() => {
|
|
|
7592
8060
|
text-transform: none;
|
|
7593
8061
|
}
|
|
7594
8062
|
.dxr-surface .docx-hf-band[data-hf-inherited] { border-style: dashed; }
|
|
7595
|
-
.
|
|
7596
|
-
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
8063
|
+
/* The sheet IS the page. The editor's viewport sizes .docx-body-flow to the section's page
|
|
8064
|
+
width and its section wrappers to the authored text column, so the horizontal gutters here
|
|
8065
|
+
are the document's own w:sectPr margins, not a padding this chrome invents; only the
|
|
8066
|
+
vertical breathing room is ours. Centering is left to margin:auto so a page the viewport
|
|
8067
|
+
has zoomed to fit stays centered at its scaled width. */
|
|
7600
8068
|
.dxr[data-chrome] .dxr-surface[data-view="continuous"] .docx-body-flow {
|
|
7601
|
-
|
|
8069
|
+
max-width: 100%;
|
|
8070
|
+
margin: 0 auto;
|
|
8071
|
+
padding: 56px 0;
|
|
7602
8072
|
border-radius: 3px;
|
|
7603
8073
|
background: var(--dxr-sheet);
|
|
7604
8074
|
box-shadow: 0 1px 3px rgba(16, 20, 24, .14), 0 8px 24px rgba(16, 20, 24, .05);
|
|
@@ -7677,9 +8147,11 @@ var DocxodusEditor = (() => {
|
|
|
7677
8147
|
.dxr[data-chrome="compact"] .dxr-note { display: none; }
|
|
7678
8148
|
.dxr[data-chrome="compact"] .dxr-rail { display: none; }
|
|
7679
8149
|
.dxr[data-chrome="compact"] .dxr-hint { display: none; }
|
|
8150
|
+
/* Compact trims the chrome around the page, never the page: the document's own column width
|
|
8151
|
+
is what the viewport's fit-to-width zoom scales, so a phone shows a whole smaller page
|
|
8152
|
+
instead of a narrower one that breaks its lines somewhere Word never would. */
|
|
7680
8153
|
.dxr[data-chrome="compact"] .dxr-surface { margin: 12px auto; padding: 0 10px 64px; }
|
|
7681
|
-
.dxr[data-chrome="compact"] .dxr-surface[data-view="continuous"] { padding: 22px
|
|
7682
|
-
.dxr[data-chrome="compact"] .dxr-surface[data-view="continuous"] .docx-body-flow { padding: 22px 18px; }
|
|
8154
|
+
.dxr[data-chrome="compact"] .dxr-surface[data-view="continuous"] .docx-body-flow { padding: 22px 0; }
|
|
7683
8155
|
.dxr[data-chrome="compact"] .dxr-surface .docx-hf-band { padding: 9px 18px 13px; }
|
|
7684
8156
|
.dxr[data-chrome="compact"] .dxr-surface .docx-hf-chrome,
|
|
7685
8157
|
.dxr[data-chrome="compact"] .dxr-surface .docx-hf-warning { margin-left: 0; }
|
|
@@ -8429,6 +8901,8 @@ var DocxodusEditor = (() => {
|
|
|
8429
8901
|
fabricateClasses: this.options.fabricateClasses,
|
|
8430
8902
|
editable: this.options.editable,
|
|
8431
8903
|
scale: this.options.scale,
|
|
8904
|
+
columnWidth: this.options.columnWidth,
|
|
8905
|
+
fitToWidth: this.options.fitToWidth,
|
|
8432
8906
|
onEdit: this.options.onEdit,
|
|
8433
8907
|
onMove: this.options.onMove,
|
|
8434
8908
|
paginated,
|