Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Mention from "src/decidim/editor/extensions/mention";
import MentionResource from "src/decidim/editor/extensions/mention_resource";
import VideoEmbed from "src/decidim/editor/extensions/video_embed";
import Emoji from "src/decidim/editor/extensions/emoji";
import ImageLink from "tiptap-extension-image-link";

export default Extension.create({
name: "decidimKit",
Expand Down Expand Up @@ -47,6 +48,7 @@ export default Extension.create({
}),
CharacterCount.configure(this.options.characterCount),
Link.configure({ openOnClick: false, ...this.options.link }),
ImageLink,
Bold,
Dialog,
Indent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,26 +77,33 @@ export default (self) => {
naturalWidth = tmpImg.naturalWidth;
naturalHeight = tmpImg.naturalHeight;

// Get the current width from the node (it might have been updated)
const currentNode = editor.view.state.doc.nodeAt(getPos());
const currentNodeWidth = currentNode ? currentNode.attrs.width : givenWidth;

// Set currentWidth and currentHeight
if (givenWidth === null) {
if (currentNodeWidth === null) {
currentWidth = naturalWidth;
currentHeight = naturalHeight;
} else {
currentWidth = givenWidth;
currentWidth = currentNodeWidth;
currentHeight = Math.round(naturalHeight * (currentWidth / naturalWidth));
}

// Force node update in order to set the initial dimensions
[{ ...node.attrs, width: 1 }, node.attrs].forEach((newAttrs) => {
// The `setTimeout` below is to push the node updates to the next JS
// event loop so that we are not triggering a change in the element
// before it is created as would happen e.g. during the Jest tests.
setTimeout(() => {
editor.view.dispatch(
editor.view.state.tr.setNodeMarkup(getPos(), self.type, newAttrs)
);
}, 0);
});
// Only do this if the node wasn't already updated with a specific width
if (currentNodeWidth === givenWidth) {
[{ ...node.attrs, width: 1 }, node.attrs].forEach((newAttrs) => {
// The `setTimeout` below is to push the node updates to the next JS
// event loop so that we are not triggering a change in the element
// before it is created as would happen e.g. during the Jest tests.
setTimeout(() => {
editor.view.dispatch(
editor.view.state.tr.setNodeMarkup(getPos(), self.type, newAttrs)
);
}, 0);
});
}
}
tmpImg.src = img.src;

Expand Down Expand Up @@ -187,6 +194,12 @@ export default (self) => {

const { alt, src, title, width } = updatedNode.attrs;

// Update currentWidth and currentHeight if width has changed
if (width !== null && width !== undefined && width !== currentWidth) {
currentWidth = width;
currentHeight = Math.round(naturalHeight * (currentWidth / naturalWidth));
}

// We set the value through an attribute change here because otherwise
// we would trigger a mutation in the DOM which causes the update method
// to be called recursively.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@ export default Link.extend({

addCommands() {
const i18n = getDictionary("editor.extensions.link");
const findNodeByAttribute = (doc, nodeType, attrName, attrValue) => {
let foundNode = null;
let foundPos = null;

doc.descendants((node, pos) => {
if (node.type.name === nodeType && node.attrs[attrName] === attrValue) {
foundNode = node;
foundPos = pos;
return false; // stop searching
}
});

return { node: foundNode, pos: foundPos };
};

return {
...this.parent?.(),
Expand All @@ -55,17 +69,36 @@ export default Link.extend({

linkDialog: () => async ({ dispatch, commands }) => {
if (dispatch) {
// Check if the selection is an image
const isImage = this.editor.isActive("image");

// If the cursor is within the link but the link is not selected, the
// link would not be correctly updated. Also if only a part of the
// link is selected, the link would be split to separate links, only
// the current selection getting the updated link URL.
commands.extendMarkRange("link");
if (!isImage) {
commands.extendMarkRange("link");
}

this.storage.bubbleMenu.hide();

const { allowTargetControl } = this.options;

let { href, target } = this.editor.getAttributes("link");
let src = null;
let originalWidth = null;

// If it's an image, get the src attribute and preserve the width
if (isImage) {
const imageAttrs = this.editor.getAttributes("image");
src = imageAttrs.src;
originalWidth = imageAttrs.width;
// Check if the image is already wrapped in an imageLink
const imageLinkAttrs = this.editor.getAttributes("imageLink");
if (imageLinkAttrs.href) {
href = imageLinkAttrs.href;
}
}

const inputs = { href: { type: "text", label: i18n.hrefLabel } };
if (allowTargetControl) {
Expand Down Expand Up @@ -95,9 +128,45 @@ export default Link.extend({
}

if (!href || href.trim().length < 1) {
if (isImage) {
// For images, we don't unset anything if there's no href
return this.editor.chain().focus(null, { scrollIntoView: false }).run();
}
return this.editor.chain().focus(null, { scrollIntoView: false }).unsetLink().run();
}

// If it's an image, use setImageLink command
if (isImage) {
// First apply the image link
this.editor.chain()
.focus(null, { scrollIntoView: false })
.setImageLink({
href,
src,
HTMLAttributes: {
target: target || "_blank"
}
})
.run();

// After setImageLink, find the image node by its src and update the width
// Small delay to ensure the node is fully created after setImageLink
setTimeout(() => {
const { state, view } = this.editor;
const { node: imageNode, pos: imagePos } = findNodeByAttribute(state.doc, "image", "src", src);

if (imageNode && imagePos !== null) {
const tr = state.tr.setNodeMarkup(imagePos, null, {
...imageNode.attrs,
width: originalWidth
});
view.dispatch(tr);
}
}, 10);

return true;
}

return this.editor.chain().focus(null, { scrollIntoView: false }).setLink({ href, target }).toggleLinkBubble().run();
}

Expand Down
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@tiptap/pm": "2.1.13",
"@tiptap/starter-kit": "2.1.13",
"@tiptap/suggestion": "2.1.13",
"tiptap-extension-image-link": "^1.0.0",
"a11y-accordion-component": "^1.2.6",
"a11y-dialog-component": "^5.5.1",
"a11y-dropdown-component": "^1.2.0",
Expand Down
Loading