Skip to content

Commit 3e946ec

Browse files
feat: add translator for w:pPrChange
1 parent de2e37e commit 3e946ec

7 files changed

Lines changed: 554 additions & 75 deletions

File tree

packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ import { translator as w_personalCompose_translator } from './w/personalCompose/
118118
import { translator as w_personalReply_translator } from './w/personalReply/personalReply-translator.js';
119119
import { translator as w_position_translator } from './w/position/position-translator.js';
120120
import { translator as w_pPr_translator } from './w/pPr/pPr-translator.js';
121+
import { translator as w_pPrChange_translator } from './w/pPrChange/pPrChange-translator.js';
121122
import { translator as w_pStyle_translator } from './w/pStyle/pStyle-translator.js';
122123
import { translator as w_permEnd_translator } from './w/perm-end/perm-end-translator.js';
123124
import { translator as w_permStart_translator } from './w/perm-start/perm-start-translator.js';
@@ -324,6 +325,7 @@ const translatorList = Array.from(
324325
w_personalReply_translator,
325326
w_position_translator,
326327
w_pPr_translator,
328+
w_pPrChange_translator,
327329
w_pStyle_translator,
328330
w_permStart_translator,
329331
w_permEnd_translator,

packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/utils.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,8 @@ export function decodePropertiesByKey(xmlName, sdName, translator, params, attrs
500500
* @param {import('@translator').NodeTranslator[]} propertyTranslators An array of property translators to handle nested properties.
501501
* @param {object} [defaultEncodedAttrs={}] Optional default attributes to include during encoding.
502502
* @param {import('@translator').AttrConfig[]} [attributeHandlers=[]] Optional additional attribute handlers for the nested element.
503+
* @param {object} [options={}] Optional configuration.
504+
* @param {boolean} [options.emitWhenAttributesOnly=false] When true, the decode path emits the XML element if it has attributes even when there are no child elements. Useful for tracked-change wrappers (e.g. w:pPrChange) where the attributes carry independent semantic value.
503505
* @returns {import('@translator').NodeTranslatorConfig} The nested property handler config with xmlName, sdName, encode, and decode functions.
504506
*/
505507
export function createNestedPropertiesTranslator(
@@ -508,6 +510,7 @@ export function createNestedPropertiesTranslator(
508510
propertyTranslators,
509511
defaultEncodedAttrs = {},
510512
attributeHandlers = [],
513+
{ emitWhenAttributesOnly = false } = {},
511514
) {
512515
const propertyTranslatorsByXmlName = {};
513516
const propertyTranslatorsBySdName = {};
@@ -542,7 +545,9 @@ export function createNestedPropertiesTranslator(
542545
// Process property translators
543546
const elements = decodeProperties(params, propertyTranslatorsBySdName, currentValue);
544547

545-
if (elements.length === 0) {
548+
const hasAttributes = emitWhenAttributesOnly && Object.keys(decodedAttrs).length > 0;
549+
550+
if (elements.length === 0 && !hasAttributes) {
546551
return undefined;
547552
}
548553

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// @ts-check
2+
import { translator as mcAlternateContentTranslator } from '../../mc/altermateContent';
3+
import { translator as wAdjustRightIndTranslator } from '../adjustRightInd';
4+
import { translator as wAutoSpaceDETranslator } from '../autoSpaceDE';
5+
import { translator as wAutoSpaceDNTranslator } from '../autoSpaceDN';
6+
import { translator as wBidiTranslator } from '../bidi';
7+
import { translator as wCnfStyleTranslator } from '../cnfStyle';
8+
import { translator as wContextualSpacingTranslator } from '../contextualSpacing';
9+
import { translator as wDivIdTranslator } from '../divId';
10+
import { translator as wFramePrTranslator } from '../framePr';
11+
import { translator as wIndTranslator } from '../ind';
12+
import { translator as wJcTranslatorTranslator } from '../jc';
13+
import { translator as wKeepLinesTranslator } from '../keepLines';
14+
import { translator as wKeepNextTranslator } from '../keepNext';
15+
import { translator as wKinsokuTranslator } from '../kinsoku';
16+
import { translator as wMirrorIndentsTranslator } from '../mirrorIndents';
17+
import { translator as wNumPrTranslator } from '../numPr';
18+
import { translator as wOutlineLvlTranslator } from '../outlineLvl';
19+
import { translator as wOverflowPunctTranslator } from '../overflowPunct';
20+
import { translator as wPBdrTranslator } from '../pBdr';
21+
import { translator as wPStyleTranslator } from '../pStyle';
22+
import { translator as wPageBreakBeforeTranslator } from '../pageBreakBefore';
23+
import { translator as wShdTranslator } from '../shd';
24+
import { translator as wSnapToGridTranslator } from '../snapToGrid';
25+
import { translator as wSpacingTranslator } from '../spacing';
26+
import { translator as wSuppressAutoHyphensTranslator } from '../suppressAutoHyphens';
27+
import { translator as wSuppressLineNumbersTranslator } from '../suppressLineNumbers';
28+
import { translator as wSuppressOverlapTranslator } from '../suppressOverlap';
29+
import { translator as wTabsTranslator } from '../tabs';
30+
import { translator as wTextAlignmentTranslator } from '../textAlignment';
31+
import { translator as wTextDirectionTranslator } from '../textDirection';
32+
import { translator as wTextboxTightWrapTranslator } from '../textboxTightWrap';
33+
import { translator as wTopLinePunctTranslator } from '../topLinePunct';
34+
import { translator as wWidowControlTranslator } from '../widowControl';
35+
import { translator as wWordWrapTranslator } from '../wordWrap';
36+
import { translator as wRPrTranslator } from '../rpr';
37+
38+
/** @type {import('@translator').NodeTranslator[]} */
39+
export const basePropertyTranslators = [
40+
mcAlternateContentTranslator,
41+
wAdjustRightIndTranslator,
42+
wAutoSpaceDETranslator,
43+
wAutoSpaceDNTranslator,
44+
wBidiTranslator,
45+
wCnfStyleTranslator,
46+
wContextualSpacingTranslator,
47+
wDivIdTranslator,
48+
wFramePrTranslator,
49+
wIndTranslator,
50+
wJcTranslatorTranslator,
51+
wKeepLinesTranslator,
52+
wKeepNextTranslator,
53+
wKinsokuTranslator,
54+
wMirrorIndentsTranslator,
55+
wNumPrTranslator,
56+
wOutlineLvlTranslator,
57+
wOverflowPunctTranslator,
58+
wPBdrTranslator,
59+
wPStyleTranslator,
60+
wPageBreakBeforeTranslator,
61+
wShdTranslator,
62+
wSnapToGridTranslator,
63+
wSpacingTranslator,
64+
wSuppressAutoHyphensTranslator,
65+
wSuppressLineNumbersTranslator,
66+
wSuppressOverlapTranslator,
67+
wTabsTranslator,
68+
wTextAlignmentTranslator,
69+
wTextDirectionTranslator,
70+
wTextboxTightWrapTranslator,
71+
wTopLinePunctTranslator,
72+
wWidowControlTranslator,
73+
wWordWrapTranslator,
74+
wRPrTranslator,
75+
];

packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/pPr/pPr-translator.js

Lines changed: 3 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,11 @@
11
// @ts-check
22
import { NodeTranslator } from '@translator';
33
import { createNestedPropertiesTranslator } from '@converter/v3/handlers/utils.js';
4-
import { translator as mcAlternateContentTranslator } from '../../mc/altermateContent';
5-
import { translator as wAdjustRightIndTranslator } from '../adjustRightInd';
6-
import { translator as wAutoSpaceDETranslator } from '../autoSpaceDE';
7-
import { translator as wAutoSpaceDNTranslator } from '../autoSpaceDN';
8-
import { translator as wBidiTranslator } from '../bidi';
9-
import { translator as wCnfStyleTranslator } from '../cnfStyle';
10-
import { translator as wContextualSpacingTranslator } from '../contextualSpacing';
11-
import { translator as wDivIdTranslator } from '../divId';
12-
import { translator as wFramePrTranslator } from '../framePr';
13-
import { translator as wIndTranslator } from '../ind';
14-
import { translator as wJcTranslatorTranslator } from '../jc';
15-
import { translator as wKeepLinesTranslator } from '../keepLines';
16-
import { translator as wKeepNextTranslator } from '../keepNext';
17-
import { translator as wKinsokuTranslator } from '../kinsoku';
18-
import { translator as wMirrorIndentsTranslator } from '../mirrorIndents';
19-
import { translator as wNumPrTranslator } from '../numPr';
20-
import { translator as wOutlineLvlTranslator } from '../outlineLvl';
21-
import { translator as wOverflowPunctTranslator } from '../overflowPunct';
22-
import { translator as wPBdrTranslator } from '../pBdr';
23-
import { translator as wPStyleTranslator } from '../pStyle';
24-
import { translator as wPageBreakBeforeTranslator } from '../pageBreakBefore';
25-
import { translator as wShdTranslator } from '../shd';
26-
import { translator as wSnapToGridTranslator } from '../snapToGrid';
27-
import { translator as wSpacingTranslator } from '../spacing';
28-
import { translator as wSuppressAutoHyphensTranslator } from '../suppressAutoHyphens';
29-
import { translator as wSuppressLineNumbersTranslator } from '../suppressLineNumbers';
30-
import { translator as wSuppressOverlapTranslator } from '../suppressOverlap';
31-
import { translator as wTabsTranslator } from '../tabs';
32-
import { translator as wTextAlignmentTranslator } from '../textAlignment';
33-
import { translator as wTextDirectionTranslator } from '../textDirection';
34-
import { translator as wTextboxTightWrapTranslator } from '../textboxTightWrap';
35-
import { translator as wTopLinePunctTranslator } from '../topLinePunct';
36-
import { translator as wWidowControlTranslator } from '../widowControl';
37-
import { translator as wWordWrapTranslator } from '../wordWrap';
38-
import { translator as wRPrTranslator } from '../rpr';
4+
import { basePropertyTranslators } from './pPr-base-translators.js';
5+
import { translator as wPPrChangeTranslator } from '../pPrChange';
396

40-
// Property translators for w:pPr child elements
41-
// Each translator handles a specific property of the paragraph properties
427
/** @type {import('@translator').NodeTranslator[]} */
43-
const propertyTranslators = [
44-
mcAlternateContentTranslator,
45-
wAdjustRightIndTranslator,
46-
wAutoSpaceDETranslator,
47-
wAutoSpaceDNTranslator,
48-
wBidiTranslator,
49-
wCnfStyleTranslator,
50-
wContextualSpacingTranslator,
51-
wDivIdTranslator,
52-
wFramePrTranslator,
53-
wIndTranslator,
54-
wJcTranslatorTranslator,
55-
wKeepLinesTranslator,
56-
wKeepNextTranslator,
57-
wKinsokuTranslator,
58-
wMirrorIndentsTranslator,
59-
wNumPrTranslator,
60-
wOutlineLvlTranslator,
61-
wOverflowPunctTranslator,
62-
wPBdrTranslator,
63-
wPStyleTranslator,
64-
wPageBreakBeforeTranslator,
65-
wShdTranslator,
66-
wSnapToGridTranslator,
67-
wSpacingTranslator,
68-
wSuppressAutoHyphensTranslator,
69-
wSuppressLineNumbersTranslator,
70-
wSuppressOverlapTranslator,
71-
wTabsTranslator,
72-
wTextAlignmentTranslator,
73-
wTextDirectionTranslator,
74-
wTextboxTightWrapTranslator,
75-
wTopLinePunctTranslator,
76-
wWidowControlTranslator,
77-
wWordWrapTranslator,
78-
wRPrTranslator,
79-
];
8+
const propertyTranslators = [...basePropertyTranslators, wPPrChangeTranslator];
809

8110
/**
8211
* The NodeTranslator instance for the w:pPr element.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './pPrChange-translator.js';
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { NodeTranslator } from '@translator';
2+
import { carbonCopy } from '@core/utilities/carbonCopy.js';
3+
import { createNestedPropertiesTranslator, createAttributeHandler } from '@converter/v3/handlers/utils.js';
4+
import { basePropertyTranslators } from '../pPr/pPr-base-translators.js';
5+
6+
const pPrTranslator = NodeTranslator.from(
7+
createNestedPropertiesTranslator('w:pPr', 'paragraphProperties', basePropertyTranslators),
8+
);
9+
10+
const ATTRIBUTE_HANDLERS = [
11+
createAttributeHandler('w:id'),
12+
createAttributeHandler('w:author'),
13+
createAttributeHandler('w:date'),
14+
];
15+
16+
function getSectPr(pPrNode) {
17+
const sectPr = pPrNode?.elements?.find((el) => el.name === 'w:sectPr');
18+
return sectPr ? carbonCopy(sectPr) : undefined;
19+
}
20+
21+
/**
22+
* The NodeTranslator instance for the w:pPrChange element.
23+
* @type {import('@translator').NodeTranslator}
24+
*/
25+
export const translator = NodeTranslator.from({
26+
xmlName: 'w:pPrChange',
27+
sdNodeOrKeyName: 'change',
28+
type: NodeTranslator.translatorTypes.NODE,
29+
attributes: ATTRIBUTE_HANDLERS,
30+
encode: (params, encodedAttrs = {}) => {
31+
const changeNode = params.nodes[0];
32+
const pPrNode = changeNode?.elements?.find((el) => el.name === 'w:pPr');
33+
34+
let paragraphProperties = pPrNode ? pPrTranslator.encode({ ...params, nodes: [pPrNode] }) : undefined;
35+
const sectPr = getSectPr(pPrNode);
36+
if (sectPr) {
37+
paragraphProperties = {
38+
...(paragraphProperties || {}),
39+
sectPr,
40+
};
41+
}
42+
43+
const result = {
44+
...encodedAttrs,
45+
...(paragraphProperties ? { paragraphProperties } : {}),
46+
};
47+
48+
return Object.keys(result).length ? result : undefined;
49+
},
50+
decode: function (params) {
51+
const change = params.node?.attrs?.change;
52+
if (!change || typeof change !== 'object') return undefined;
53+
54+
const decodedAttrs = this.decodeAttributes({
55+
node: { ...params.node, attrs: change },
56+
});
57+
const hasParagraphProperties = Object.prototype.hasOwnProperty.call(change, 'paragraphProperties');
58+
const paragraphProperties = hasParagraphProperties ? change.paragraphProperties : undefined;
59+
60+
let pPrNode =
61+
paragraphProperties && typeof paragraphProperties === 'object'
62+
? pPrTranslator.decode({
63+
...params,
64+
node: { ...params.node, attrs: { paragraphProperties } },
65+
})
66+
: undefined;
67+
68+
const sectPr = paragraphProperties?.sectPr ? carbonCopy(paragraphProperties.sectPr) : undefined;
69+
if (sectPr) {
70+
if (!pPrNode) {
71+
pPrNode = {
72+
name: 'w:pPr',
73+
type: 'element',
74+
attributes: {},
75+
elements: [],
76+
};
77+
}
78+
pPrNode.elements = [...(pPrNode.elements || []), sectPr];
79+
}
80+
81+
if (!pPrNode && hasParagraphProperties) {
82+
pPrNode = {
83+
name: 'w:pPr',
84+
type: 'element',
85+
attributes: {},
86+
elements: [],
87+
};
88+
}
89+
90+
if (!pPrNode && !Object.keys(decodedAttrs).length) return undefined;
91+
92+
return {
93+
name: 'w:pPrChange',
94+
type: 'element',
95+
attributes: decodedAttrs,
96+
elements: pPrNode ? [pPrNode] : [],
97+
};
98+
},
99+
});

0 commit comments

Comments
 (0)