Skip to content

Commit d8f0b67

Browse files
committed
fix: add relationship and rId
1 parent 2ee08c4 commit d8f0b67

6 files changed

Lines changed: 124 additions & 7 deletions

File tree

packages/super-editor/src/core/super-converter/docx-helpers/document-rels.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ export const insertNewRelationship = (target, type, editor) => {
138138
},
139139
};
140140

141+
if (type === 'hyperlink') {
142+
newRel.attributes.TargetMode = 'External';
143+
}
144+
141145
// Insert the new relationship
142146
relationshipsTag.elements.push(newRel);
143147

packages/super-editor/src/core/super-converter/docx-helpers/document-rels.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ describe('insertNewRelationship', () => {
308308
Id: 'rId43',
309309
Type: RELATIONSHIP_TYPES.hyperlink,
310310
Target: 'bar',
311+
TargetMode: 'External',
311312
},
312313
});
313314
});

packages/super-editor/src/extensions/image/imageHelpers/startImageUpload.js

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { ImagePlaceholderPluginKey, findPlaceholder } from './imagePlaceholderPlugin.js';
22
import { handleImageUpload as handleImageUploadDefault } from './handleImageUpload.js';
33
import { processUploadedImage } from './processUploadedImage.js';
4+
import { insertNewRelationship } from '@core/super-converter/docx-helpers/document-rels.js';
45

56
export const startImageUpload = async ({ editor, view, file }) => {
6-
// Handler from config or default
7-
let imageUploadHandler =
7+
const imageUploadHandler =
88
typeof editor.options.handleImageUpload === 'function'
99
? editor.options.handleImageUpload
1010
: handleImageUploadDefault;
1111

12-
let fileSizeMb = (file.size / (1024 * 1024)).toFixed(4);
12+
let fileSizeMb = Number((file.size / (1024 * 1024)).toFixed(4));
1313

1414
if (fileSizeMb > 5) {
1515
window.alert('Image size must be less than 5MB');
@@ -29,6 +29,16 @@ export const startImageUpload = async ({ editor, view, file }) => {
2929
return;
3030
}
3131

32+
await uploadImage({
33+
editor,
34+
view,
35+
file,
36+
size: { width, height },
37+
uploadHandler: imageUploadHandler,
38+
});
39+
};
40+
41+
export async function uploadImage({ editor, view, file, size, uploadHandler }) {
3242
// A fresh object to act as the ID for this upload
3343
let id = {};
3444

@@ -52,7 +62,7 @@ export const startImageUpload = async ({ editor, view, file }) => {
5262
tr.setMeta(ImagePlaceholderPluginKey, imageMeta);
5363
view.dispatch(tr);
5464

55-
imageUploadHandler(file).then(
65+
await uploadHandler(file).then(
5666
(url) => {
5767
let fileName = file.name.replace(' ', '_');
5868
let placeholderPos = findPlaceholder(view.state, id);
@@ -68,9 +78,18 @@ export const startImageUpload = async ({ editor, view, file }) => {
6878
let removeMeta = { type: 'remove', id };
6979

7080
let mediaPath = `word/media/${fileName}`;
81+
82+
let rId = null;
83+
if (editor.options.mode === 'docx') {
84+
const [, path] = mediaPath.split('word/'); // Path without 'word/' part.
85+
const id = addImageRelationship({ editor, path });
86+
if (id) rId = id;
87+
}
88+
7189
let imageNode = schema.nodes.image.create({
7290
src: mediaPath,
73-
size: { width, height },
91+
size,
92+
rId,
7493
});
7594

7695
editor.storage.image.media = Object.assign(editor.storage.image.media, { [mediaPath]: url });
@@ -93,4 +112,15 @@ export const startImageUpload = async ({ editor, view, file }) => {
93112
view.dispatch(tr.setMeta(ImagePlaceholderPluginKey, removeMeta));
94113
},
95114
);
96-
};
115+
}
116+
117+
function addImageRelationship({ editor, path }) {
118+
const target = path;
119+
const type = 'image';
120+
try {
121+
const id = insertNewRelationship(target, type, editor);
122+
return id;
123+
} catch (err) {
124+
return null;
125+
}
126+
}

packages/super-editor/src/extensions/link/link.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Mark, Attribute } from '@core/index.js';
22
import { getMarkRange } from '@/core/helpers/getMarkRange.js';
3+
import { insertNewRelationship } from '@core/super-converter/docx-helpers/document-rels.js';
34

45
/**
56
* Move `from` forward and `to` backward until the first / last
@@ -146,7 +147,15 @@ export const Link = Mark.create({
146147
if (underlineMarkType) tr = tr.removeMark(from, to, underlineMarkType);
147148

148149
if (underlineMarkType) tr = tr.addMark(from, to, underlineMarkType.create());
149-
tr = tr.addMark(from, to, linkMarkType.create({ href, text: finalText }));
150+
151+
let rId = null;
152+
if (editor.options.mode === 'docx') {
153+
const id = addLinkRelationship({ editor, href });
154+
if (id) rId = id;
155+
}
156+
157+
const newLinkMarkType = linkMarkType.create({ href, text: finalText, rId });
158+
tr = tr.addMark(from, to, newLinkMarkType);
150159

151160
dispatch(tr.scrollIntoView());
152161
return true;
@@ -191,3 +200,14 @@ function isAllowedUri(uri, protocols) {
191200
.match(new RegExp(`^(?:(?:${allowedProtocols.join('|')}):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))`, 'i'))
192201
);
193202
}
203+
204+
function addLinkRelationship({ editor, href }) {
205+
const target = href;
206+
const type = 'hyperlink';
207+
try {
208+
const id = insertNewRelationship(target, type, editor);
209+
return id;
210+
} catch (err) {
211+
return null;
212+
}
213+
}

packages/super-editor/src/tests/editor/data/imageBase64.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { loadTestDataForEditorTests, initTestEditor } from '@tests/helpers/helpers.js';
2+
import { TextSelection } from 'prosemirror-state';
3+
import { expect } from 'vitest';
4+
import { getDocumentRelationshipElements } from '@core/super-converter/docx-helpers/document-rels.js';
5+
import { uploadImage } from '@extensions/image/imageHelpers/startImageUpload.js';
6+
import { handleImageUpload as handleImageUploadDefault } from '@extensions/image/imageHelpers/handleImageUpload.js';
7+
import { imageBase64 } from './data/imageBase64.js';
8+
9+
describe('Relationships tests', () => {
10+
window.URL.createObjectURL = vi.fn().mockImplementation((file) => {
11+
return file.name;
12+
});
13+
14+
const filename = 'blank-doc.docx';
15+
let docx, media, mediaFiles, fonts, editor;
16+
17+
beforeAll(async () => ({ docx, media, mediaFiles, fonts } = await loadTestDataForEditorTests(filename)));
18+
beforeEach(() => ({ editor } = initTestEditor({ content: docx, media, mediaFiles, fonts })));
19+
20+
it('tests that the inserted link has a rId and a relationship', () => {
21+
editor.commands.insertContentAt(0, 'link');
22+
23+
editor.view.dispatch(editor.state.tr.setSelection(TextSelection.create(editor.state.doc, 0, 5)));
24+
editor.commands.setLink({ href: 'https://www.superdoc.dev' });
25+
26+
const linkMark = editor.state.doc.firstChild.firstChild.marks[0];
27+
28+
expect(linkMark.type.name).toBe('link');
29+
expect(linkMark.attrs.rId).toBeTruthy();
30+
31+
const relationships = getDocumentRelationshipElements(editor);
32+
const found = relationships.find((i) => i.attributes.Id === linkMark.attrs.rId);
33+
34+
expect(found).toBeTruthy();
35+
expect(found.attributes.Target).toBe('https://www.superdoc.dev');
36+
});
37+
38+
it('tests that the uploaded image has a rId and a relationship', async () => {
39+
const blob = await fetch(imageBase64).then((res) => res.blob());
40+
const file = new File([blob], 'image.png', { type: 'image/png' });
41+
42+
await uploadImage({
43+
editor,
44+
view: editor.view,
45+
file,
46+
size: { width: 100, height: 100 },
47+
uploadHandler: handleImageUploadDefault,
48+
});
49+
50+
const imageNode = editor.state.doc.firstChild.firstChild;
51+
52+
expect(imageNode.type.name).toBe('image');
53+
expect(imageNode.attrs.rId).toBeTruthy();
54+
55+
const relationships = getDocumentRelationshipElements(editor);
56+
const found = relationships.find((i) => i.attributes.Id === imageNode.attrs.rId);
57+
58+
expect(found).toBeTruthy();
59+
expect(found.attributes.Target).toBe('media/image.png');
60+
});
61+
});

0 commit comments

Comments
 (0)