Skip to content

Commit 9eb08d9

Browse files
committed
improve import and export of html annotation
1 parent ca47381 commit 9eb08d9

3 files changed

Lines changed: 102 additions & 9 deletions

File tree

packages/super-editor/src/core/super-converter/exporter.js

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1731,17 +1731,25 @@ function prepareCheckboxAnnotation(params) {
17311731
*/
17321732
function prepareHtmlAnnotation(params) {
17331733
const {
1734-
node: { attrs = {} },
1734+
node: { attrs = {}, marks = [] },
1735+
editorSchema,
17351736
} = params;
17361737

17371738
const parser = new window.DOMParser();
17381739
const paragraphHtml = parser.parseFromString(attrs.rawHtml || attrs.displayLabel, 'text/html');
1740+
const marksFromAttrs = translateFieldAttrsToMarks(attrs);
1741+
const allMarks = [...marks, ...marksFromAttrs]
17391742

1740-
const state = EditorState.create({
1741-
doc: PMDOMParser.fromSchema(params.editorSchema).parse(paragraphHtml),
1743+
let state = EditorState.create({
1744+
doc: PMDOMParser.fromSchema(editorSchema).parse(paragraphHtml),
17421745
});
17431746

1747+
if (allMarks.length) {
1748+
state = applyMarksToHtmlAnnotation(state, allMarks);
1749+
}
1750+
17441751
const htmlAnnotationNode = state.doc.toJSON();
1752+
17451753
return {
17461754
name: 'htmlAnnotation',
17471755
elements: translateChildNodes({
@@ -1896,6 +1904,20 @@ function translateFieldAnnotation(params) {
18961904
'w:val': attrs.multipleImage,
18971905
},
18981906
},
1907+
{
1908+
name: 'w:fieldFontFamily',
1909+
attributes: {
1910+
'xmlns:w': customXmlns,
1911+
'w:val': attrs.fontFamily,
1912+
},
1913+
},
1914+
{
1915+
name: 'w:fieldFontSize',
1916+
attributes: {
1917+
'xmlns:w': customXmlns,
1918+
'w:val': attrs.fontSize,
1919+
},
1920+
},
18991921
],
19001922
},
19011923
{
@@ -2103,3 +2125,46 @@ function resizeKeepAspectRatio(width, height, maxWidth) {
21032125
}
21042126
return { width, height };
21052127
}
2128+
2129+
function applyMarksToHtmlAnnotation(state, marks) {
2130+
const { tr, doc, schema } = state;
2131+
const allowedMarks = ['fontFamily', 'fontSize'];
2132+
2133+
if (
2134+
!marks.some((m) => allowedMarks.includes(m.type))
2135+
) {
2136+
return state;
2137+
}
2138+
2139+
const fontFamily = marks.find((m) => m.type === 'fontFamily');
2140+
const fontSize = marks.find((m) => m.type === 'fontSize');
2141+
2142+
doc.descendants((node, pos) => {
2143+
if (!node.isText) return;
2144+
2145+
const found = node.marks.find((m) => m.type.name === 'textStyle');
2146+
const textStyleType = schema.marks.textStyle;
2147+
2148+
if (!found) {
2149+
tr.addMark(pos, pos + node.nodeSize, textStyleType.create({
2150+
...fontFamily?.attrs,
2151+
...fontSize?.attrs,
2152+
}));
2153+
return;
2154+
}
2155+
2156+
if (!found?.attrs.fontFamily && fontFamily) {
2157+
tr.addMark(pos, pos + node.nodeSize, textStyleType.create({
2158+
...found?.attrs,
2159+
...fontFamily.attrs,
2160+
}));
2161+
} else if (!found?.attrs.fontSize && fontSize) {
2162+
tr.addMark(pos, pos + node.nodeSize, textStyleType.create({
2163+
...found?.attrs,
2164+
...fontSize.attrs,
2165+
}));
2166+
}
2167+
});
2168+
2169+
return state.apply(tr);
2170+
};

packages/super-editor/src/core/super-converter/v2/importer/annotationImporter.js

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ export const handleAnnotationNode = (params) => {
1313
const node = nodes[0];
1414
const sdtPr = node.elements.find((el) => el.name === 'w:sdtPr');
1515
const sdtContent = node.elements.find((el) => el.name === 'w:sdtContent');
16-
const { attrs: marksAsAttrs, marks } = parseAnnotationMarks(sdtContent);
16+
const type = sdtPr?.elements.find((el) => el.name === 'w:fieldTypeShort')?.attributes['w:val'];
17+
const { attrs: marksAsAttrs, marks } = parseAnnotationMarks(sdtContent, type);
1718

1819
const docPartObj = sdtPr?.elements.find((el) => el.name === 'w:docPartObj');
1920
if (docPartObj) {
@@ -23,9 +24,10 @@ export const handleAnnotationNode = (params) => {
2324
const alias = sdtPr?.elements.find((el) => el.name === 'w:alias');
2425
const tag = sdtPr?.elements.find((el) => el.name === 'w:tag');
2526
const fieldType = sdtPr?.elements.find((el) => el.name === 'w:fieldType')?.attributes['w:val'];
26-
const type = sdtPr?.elements.find((el) => el.name === 'w:fieldTypeShort')?.attributes['w:val'];
2727
const fieldColor = sdtPr?.elements.find((el) => el.name === 'w:fieldColor')?.attributes['w:val'];
2828
const isMultipleImage = sdtPr?.elements.find((el) => el.name === 'w:fieldMultipleImage')?.attributes['w:val'];
29+
const fontFamily = sdtPr?.elements.find((el) => el.name === 'w:fieldFontFamily')?.attributes['w:val'];
30+
const fontSize = sdtPr?.elements.find((el) => el.name === 'w:fieldFontSize')?.attributes['w:val'];
2931

3032
const attrs = {
3133
type,
@@ -34,23 +36,27 @@ export const handleAnnotationNode = (params) => {
3436
fieldType,
3537
fieldColor,
3638
multipleImage: isMultipleImage === 'true',
39+
fontFamily: fontFamily !== 'null' ? fontFamily : null,
40+
fontSize: fontSize !== 'null' ? fontSize : null,
3741
};
3842

43+
const allAttrs = { ...attrs, ...marksAsAttrs };
44+
3945
if (!attrs.fieldId || !attrs.displayLabel) {
4046
return { nodes: [], consumed: 0 };
4147
}
4248

4349
let result = {
4450
type: 'text',
4551
text: `{{${attrs.displayLabel}}}`,
46-
attrs: { ...attrs, ...marksAsAttrs },
52+
attrs: allAttrs,
4753
marks,
4854
};
4955

5056
if (params.editor.options.annotations) {
5157
result = {
5258
type: 'fieldAnnotation',
53-
attrs: { ...attrs, ...marksAsAttrs }
59+
attrs: allAttrs,
5460
};
5561
};
5662

@@ -65,8 +71,20 @@ export const handleAnnotationNode = (params) => {
6571
* @param {Object} content The sdtContent node
6672
* @returns {Object} The attributes object
6773
*/
68-
export const parseAnnotationMarks = (content = {}) => {
69-
const run = content.elements?.find((el) => el.name === 'w:r');
74+
export const parseAnnotationMarks = (content = {}, type) => {
75+
let mainContent = content;
76+
77+
if (type === 'html') {
78+
/// Note: html annotation has a different structure and can include
79+
/// several paragraphs with different styles. We could find the first paragraph
80+
/// and take the marks from there, but we take fontFamily and fontSize from the annotation attributes.
81+
82+
/// Example:
83+
/// const firstPar = content.elements?.find((el) => el.name === 'w:p');
84+
/// if (firstPar) mainContent = firstPar;
85+
}
86+
87+
const run = mainContent.elements?.find((el) => el.name === 'w:r');
7088
const rPr = run?.elements?.find((el) => el.name === 'w:rPr');
7189
if (!rPr) return {};
7290

packages/super-editor/src/dev/components/DeveloperPlayground.vue

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ const onCreate = ({ editor }) => {
4545
4646
// Set debugging pagination value from editor plugin state
4747
isDebuggingPagination.value = PaginationPluginKey.getState(editor.state)?.isDebugging;
48+
49+
// editor.commands.addFieldAnnotation(0, {
50+
// type: 'html',
51+
// displayLabel: 'Paragraph',
52+
// fieldId: `123`,
53+
// fieldType: 'TEXTINPUT',
54+
// fieldColor: '#980043',
55+
// rawHtml: '<p><span style="font-family: Courier New; font-size: 8pt;">Par 1</span></p><p>Par 2</p>',
56+
// });
4857
};
4958
5059
const onCommentClicked = ({ conversation }) => {
@@ -68,6 +77,7 @@ const editorOptions = computed(() => {
6877
users: [], // For comment @-mentions, only users that have access to the document
6978
pagination: true,
7079
telemetry: telemetry.value,
80+
annotations: true,
7181
}
7282
});
7383

0 commit comments

Comments
 (0)