+
+
+ {wrappedImg}
+ {!disabled && (
+ onChange?.(patch)}
+ onReplace={() => setReplacing(true)}
+ onRemove={handleRemove}
+ />
+ )}
+
+
+
{!disabled && (
)}
@@ -118,7 +360,17 @@ const Image = {
keywords: ['image', 'photo', 'picture', 'img'],
isText: false,
create(init = {}) {
- return { id: newBlockId(), type: 'image', src: '', alt: '', caption: '', ...init };
+ return {
+ id: newBlockId(),
+ type: 'image',
+ src: '',
+ alt: '',
+ caption: '',
+ align: 'center',
+ href: '',
+ newTab: false,
+ ...init,
+ };
},
Component: ImageBlock,
};
diff --git a/src/shared/components/BlockEditor/inline/clipboard.js b/src/shared/components/BlockEditor/inline/clipboard.js
index bc4161a..0c826a6 100644
--- a/src/shared/components/BlockEditor/inline/clipboard.js
+++ b/src/shared/components/BlockEditor/inline/clipboard.js
@@ -89,10 +89,31 @@ function blockToElement(block) {
}
if (block.type === 'image') {
const fig = document.createElement('figure');
+ const align = block.align || 'center';
+ fig.setAttribute('data-align', align);
+ if (align === 'left' || align === 'right' || align === 'center') {
+ // CSS inline minimal pour les destinations qui ignorent data-align.
+ fig.setAttribute('style',
+ align === 'center' ? 'text-align:center'
+ : align === 'left' ? 'text-align:left'
+ : 'text-align:right');
+ }
const img = document.createElement('img');
img.setAttribute('src', block.src || '');
if (block.alt) img.setAttribute('alt', block.alt);
- fig.appendChild(img);
+ if (align === 'full') img.setAttribute('width', '100%');
+ let imgHost = img;
+ if (block.href) {
+ const a = document.createElement('a');
+ a.setAttribute('href', block.href);
+ if (block.newTab) {
+ a.setAttribute('target', '_blank');
+ a.setAttribute('rel', 'noopener noreferrer');
+ }
+ a.appendChild(img);
+ imgHost = a;
+ }
+ fig.appendChild(imgHost);
if (block.caption) {
const cap = document.createElement('figcaption');
cap.textContent = block.caption;
@@ -270,12 +291,20 @@ function parseChildren(node, out) {
const img = child.querySelector('img');
if (img) {
const cap = child.querySelector('figcaption');
+ const linkEl = img.parentElement?.tagName === 'A' ? img.parentElement : null;
+ const dataAlign = child.getAttribute('data-align');
+ const align = ['left', 'center', 'right', 'full'].includes(dataAlign) ? dataAlign : 'center';
+ const href = linkEl?.getAttribute('href') || '';
+ const newTab = !!href && linkEl?.getAttribute('target') === '_blank';
out.push({
id: newBlockId(),
type: 'image',
src: img.getAttribute('src') || '',
alt: img.getAttribute('alt') || '',
caption: cap?.textContent?.trim() || '',
+ align,
+ href,
+ newTab,
});
}
continue;
@@ -289,6 +318,9 @@ function parseChildren(node, out) {
src: child.getAttribute('src') || '',
alt: child.getAttribute('alt') || '',
caption: '',
+ align: 'center',
+ href: '',
+ newTab: false,
});
continue;
}
diff --git a/src/shared/icons/index.js b/src/shared/icons/index.js
index 135391d..860dab6 100644
--- a/src/shared/icons/index.js
+++ b/src/shared/icons/index.js
@@ -642,4 +642,38 @@ export const RepeatIcon = (props) => (
+);
+
+// Icônes d'alignement pour le bloc image. Le rectangle représente l'image,
+// les barres représentent le texte/contexte autour.
+export const AlignLeftIcon = (props) => (
+
+);
+
+export const AlignCenterIcon = (props) => (
+
+);
+
+export const AlignRightIcon = (props) => (
+
+);
+
+export const AlignFullWidthIcon = (props) => (
+
);
\ No newline at end of file