feat: add alignment options to ImageBlock component for improved layout control

This commit is contained in:
swve 2025-04-28 21:25:55 +02:00
parent 0b2c4d3ad1
commit 411845d480
2 changed files with 65 additions and 16 deletions

View file

@ -17,6 +17,9 @@ export default Node.create({
size: { size: {
width: 300, width: 300,
}, },
alignment: {
default: 'center',
},
} }
}, },

View file

@ -1,7 +1,7 @@
import { NodeViewWrapper } from '@tiptap/react' import { NodeViewWrapper } from '@tiptap/react'
import React, { useEffect } from 'react' import React, { useEffect } from 'react'
import { Resizable } from 're-resizable' import { Resizable } from 're-resizable'
import { AlertTriangle, Image, Download } from 'lucide-react' import { AlertTriangle, Image, Download, AlignLeft, AlignCenter, AlignRight } from 'lucide-react'
import { uploadNewImageFile } from '../../../../../services/blocks/Image/images' import { uploadNewImageFile } from '../../../../../services/blocks/Image/images'
import { getActivityBlockMediaDirectory } from '@services/media/media' import { getActivityBlockMediaDirectory } from '@services/media/media'
import { useOrg } from '@components/Contexts/OrgContext' import { useOrg } from '@components/Contexts/OrgContext'
@ -29,10 +29,12 @@ function ImageBlockComponent(props: any) {
const [imageSize, setImageSize] = React.useState({ const [imageSize, setImageSize] = React.useState({
width: props.node.attrs.size ? props.node.attrs.size.width : 300, width: props.node.attrs.size ? props.node.attrs.size.width : 300,
}) })
const [alignment, setAlignment] = React.useState(props.node.attrs.alignment || 'center')
const fileId = blockObject const fileId = blockObject
? `${blockObject.content.file_id}.${blockObject.content.file_format}` ? `${blockObject.content.file_id}.${blockObject.content.file_format}`
: null : null
const handleImageChange = (event: React.ChangeEvent<any>) => { const handleImageChange = (event: React.ChangeEvent<any>) => {
setImage(event.target.files[0]) setImage(event.target.files[0])
} }
@ -49,6 +51,7 @@ function ImageBlockComponent(props: any) {
props.updateAttributes({ props.updateAttributes({
blockObject: object, blockObject: object,
size: imageSize, size: imageSize,
alignment: alignment,
}) })
} }
@ -75,8 +78,26 @@ function ImageBlockComponent(props: any) {
document.body.removeChild(link); document.body.removeChild(link);
}; };
const handleAlignmentChange = (newAlignment: string) => {
setAlignment(newAlignment);
props.updateAttributes({
alignment: newAlignment,
});
};
useEffect(() => {}, [course, org]) useEffect(() => {}, [course, org])
const getAlignmentClass = () => {
switch (alignment) {
case 'left':
return 'justify-start';
case 'right':
return 'justify-end';
default:
return 'justify-center';
}
};
return ( return (
<NodeViewWrapper className="block-image w-full"> <NodeViewWrapper className="block-image w-full">
<FileUploadBlock isEditable={isEditable} isLoading={isLoading} isEmpty={!blockObject} Icon={Image}> <FileUploadBlock isEditable={isEditable} isLoading={isLoading} isEmpty={!blockObject} Icon={Image}>
@ -85,7 +106,7 @@ function ImageBlockComponent(props: any) {
</FileUploadBlock> </FileUploadBlock>
{blockObject && isEditable && ( {blockObject && isEditable && (
<div className="w-full flex justify-center"> <div className={`w-full flex ${getAlignmentClass()}`}>
<Resizable <Resizable
defaultSize={{ width: imageSize.width, height: '100%' }} defaultSize={{ width: imageSize.width, height: '100%' }}
handleStyles={{ handleStyles={{
@ -123,25 +144,50 @@ function ImageBlockComponent(props: any) {
}) })
}} }}
> >
<img <div className="relative">
src={`${getActivityBlockMediaDirectory( <img
org?.org_uuid, src={`${getActivityBlockMediaDirectory(
course?.courseStructure.course_uuid, org?.org_uuid,
props.extension.options.activity.activity_uuid, course?.courseStructure.course_uuid,
blockObject.block_uuid, props.extension.options.activity.activity_uuid,
blockObject ? fileId : ' ', blockObject.block_uuid,
'imageBlock' blockObject ? fileId : ' ',
)}`} 'imageBlock'
alt="" )}`}
className="rounded-lg shadow-sm max-w-full h-auto" alt=""
style={{ width: '100%' }} className="rounded-lg shadow-sm max-w-full h-auto"
/> style={{ width: '100%' }}
/>
<div className="absolute top-2 right-2 flex items-center gap-1.5 bg-white bg-opacity-90 backdrop-blur-xs rounded-lg p-1 shadow-xs transition-opacity opacity-70 hover:opacity-100">
<button
onClick={() => handleAlignmentChange('left')}
className={`p-1.5 rounded-md hover:bg-gray-100 text-gray-600 ${alignment === 'left' ? 'bg-gray-100' : ''}`}
title="Align left"
>
<AlignLeft size={16} />
</button>
<button
onClick={() => handleAlignmentChange('center')}
className={`p-1.5 rounded-md hover:bg-gray-100 text-gray-600 ${alignment === 'center' ? 'bg-gray-100' : ''}`}
title="Center align"
>
<AlignCenter size={16} />
</button>
<button
onClick={() => handleAlignmentChange('right')}
className={`p-1.5 rounded-md hover:bg-gray-100 text-gray-600 ${alignment === 'right' ? 'bg-gray-100' : ''}`}
title="Align right"
>
<AlignRight size={16} />
</button>
</div>
</div>
</Resizable> </Resizable>
</div> </div>
)} )}
{blockObject && !isEditable && ( {blockObject && !isEditable && (
<div className="w-full flex justify-center"> <div className={`w-full flex ${getAlignmentClass()}`}>
<div className="relative"> <div className="relative">
<img <img
src={`${getActivityBlockMediaDirectory( src={`${getActivityBlockMediaDirectory(