Skip to content

Commit 3cb501f

Browse files
author
Artem Nistuley
committed
fix: rebuild fragment on geometry change
1 parent 9adf23e commit 3cb501f

2 files changed

Lines changed: 21 additions & 3 deletions

File tree

packages/layout-engine/painters/dom/src/index.test.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3029,7 +3029,7 @@ describe('DomPainter', () => {
30293029
expect(appliedWordSpacing).toBeCloseTo(expectedWordSpacing, 5);
30303030
});
30313031

3032-
it('reuses fragment DOM nodes when layout geometry changes', () => {
3032+
it('rebuilds fragment DOM nodes when layout geometry changes to keep line epochs in sync', () => {
30333033
const painter = createTestPainter({ blocks: [block], measures: [measure] });
30343034
painter.paint(layout, mount);
30353035

@@ -3051,9 +3051,12 @@ describe('DomPainter', () => {
30513051

30523052
painter.paint(movedLayout, mount);
30533053
const fragmentAfter = mount.querySelector('.superdoc-fragment') as HTMLElement;
3054+
const lineAfter = fragmentAfter.querySelector('.superdoc-line') as HTMLElement;
30543055

3055-
expect(fragmentAfter).toBe(fragmentBefore);
3056+
expect(fragmentAfter).not.toBe(fragmentBefore);
30563057
expect(fragmentAfter.style.left).toBe('60px');
3058+
expect(fragmentAfter.dataset.layoutEpoch).toBeTruthy();
3059+
expect(lineAfter.dataset.layoutEpoch).toBe(fragmentAfter.dataset.layoutEpoch);
30573060
});
30583061

30593062
it('rebuilds fragment DOM when block content changes via setData', () => {
@@ -5016,10 +5019,13 @@ describe('DomPainter', () => {
50165019
painter.paint(updatedLayout, mount);
50175020

50185021
const updatedWrapper = mount.querySelector('.superdoc-fragment-list-item') as HTMLElement;
5019-
expect(updatedWrapper).toBe(initialWrapper);
5022+
const updatedLine = updatedWrapper.querySelector('.superdoc-line') as HTMLElement;
5023+
expect(updatedWrapper).not.toBe(initialWrapper);
50205024
expect(updatedWrapper.style.left).toBe('90px');
50215025
expect(updatedWrapper.style.top).toBe('55px');
50225026
expect(updatedWrapper.style.width).toBe('310px');
5027+
expect(updatedWrapper.dataset.layoutEpoch).toBeTruthy();
5028+
expect(updatedLine.dataset.layoutEpoch).toBe(updatedWrapper.dataset.layoutEpoch);
50235029
});
50245030

50255031
it('applies resolved zIndex only to anchored media fragments', () => {

packages/layout-engine/painters/dom/src/renderer.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2748,6 +2748,7 @@ export class DomPainter {
27482748

27492749
if (current) {
27502750
existing.delete(key);
2751+
const geometryChanged = hasFragmentGeometryChanged(current.fragment, fragment);
27512752
const sdtBoundaryMismatch = shouldRebuildForSdtBoundary(current.element, sdtBoundary);
27522753
// Detect mismatch in any between-border property
27532754
const betweenBorderMismatch =
@@ -2764,6 +2765,7 @@ export class DomPainter {
27642765
current.element.dataset.pmStart != null &&
27652766
this.currentMapping.map(Number(current.element.dataset.pmStart)) !== newPmStart;
27662767
const needsRebuild =
2768+
geometryChanged ||
27672769
this.changedBlocks.has(fragment.blockId) ||
27682770
current.signature !== fragmentSignature(fragment, this.blockLookup) ||
27692771
sdtBoundaryMismatch ||
@@ -7322,6 +7324,16 @@ const fragmentSignature = (fragment: Fragment, lookup: BlockLookup): string => {
73227324
return base;
73237325
};
73247326

7327+
const hasFragmentGeometryChanged = (previous: Fragment, next: Fragment): boolean =>
7328+
previous.x !== next.x ||
7329+
previous.y !== next.y ||
7330+
previous.width !== next.width ||
7331+
('height' in previous &&
7332+
'height' in next &&
7333+
typeof previous.height === 'number' &&
7334+
typeof next.height === 'number' &&
7335+
previous.height !== next.height);
7336+
73257337
const getSdtMetadataId = (metadata: SdtMetadata | null | undefined): string => {
73267338
if (!metadata) return '';
73277339
if ('id' in metadata && metadata.id != null) {

0 commit comments

Comments
 (0)