carhop__plugins__PROD-DEV/plugins/carhop-blocks/src/image-stack/edit.js
2025-12-11 15:31:52 +01:00

244 lines
6.1 KiB
JavaScript
Executable File

import { __ } from "@wordpress/i18n";
import {
useBlockProps,
InspectorControls,
MediaUpload,
MediaUploadCheck,
} from "@wordpress/block-editor";
import {
PanelBody,
Button,
FocalPointPicker,
RangeControl,
ToolbarGroup,
ToolbarButton,
} from "@wordpress/components";
import { BlockControls } from "@wordpress/block-editor";
import { useState } from "@wordpress/element";
import "./editor.scss";
export default function Edit({ attributes, setAttributes }) {
const { images, height } = attributes;
const [selectedImageIndex, setSelectedImageIndex] = useState(null);
const blockProps = useBlockProps();
const onSelectImages = (media) => {
const newImages = media.map((img) => ({
id: img.id,
url: img.url,
alt: img.alt || "",
focalPoint: { x: 0.5, y: 0.5 },
scale: 0.8,
rotation: 0,
}));
setAttributes({ images: [...images, ...newImages] });
};
const updateImageFocalPoint = (index, focalPoint) => {
const newImages = [...images];
newImages[index] = { ...newImages[index], focalPoint };
setAttributes({ images: newImages });
};
const updateImageScale = (index, scale) => {
const newImages = [...images];
newImages[index] = { ...newImages[index], scale };
setAttributes({ images: newImages });
};
const updateImageRotation = (index, rotation) => {
const newImages = [...images];
newImages[index] = { ...newImages[index], rotation };
setAttributes({ images: newImages });
};
const removeImage = (index) => {
const newImages = images.filter((_, i) => i !== index);
setAttributes({ images: newImages });
if (selectedImageIndex === index) {
setSelectedImageIndex(null);
}
};
const moveImage = (index, direction) => {
const newImages = [...images];
const newIndex = index + direction;
if (newIndex >= 0 && newIndex < images.length) {
[newImages[index], newImages[newIndex]] = [
newImages[newIndex],
newImages[index],
];
setAttributes({ images: newImages });
setSelectedImageIndex(newIndex);
}
};
const getImageStyle = (image) => {
const focalPoint = image.focalPoint || { x: 0.5, y: 0.5 };
const scale = image.scale || 0.8;
const rotation = image.rotation || 0;
return {
left: `${focalPoint.x * 100}%`,
top: `${focalPoint.y * 100}%`,
transform: ` rotate(${rotation}deg) scale(${scale})`,
};
};
return (
<>
<BlockControls>
<ToolbarGroup>
<MediaUploadCheck>
<MediaUpload
onSelect={onSelectImages}
allowedTypes={["image"]}
multiple
gallery
value={images.map((img) => img.id)}
render={({ open }) => (
<ToolbarButton onClick={open}>
{__("Add Images", "image-stack")}
</ToolbarButton>
)}
/>
</MediaUploadCheck>
</ToolbarGroup>
</BlockControls>
<InspectorControls>
<PanelBody title={__("Container Settings", "image-stack")}>
<RangeControl
label={__("Container Height", "image-stack")}
value={height}
onChange={(value) => setAttributes({ height: value })}
min={200}
max={800}
step={10}
/>
</PanelBody>
{images.length > 0 && (
<PanelBody title={__("Images", "image-stack")} initialOpen={true}>
{images.map((image, index) => (
<PanelBody
key={image.id}
title={`${__("Image", "image-stack")} ${index + 1}`}
initialOpen={selectedImageIndex === index}
onToggle={() =>
setSelectedImageIndex(
selectedImageIndex === index ? null : index
)
}
>
<div className="image-stack-image-preview">
<img src={image.url} alt={image.alt} />
</div>
<FocalPointPicker
label={__("Focal Point", "image-stack")}
url={image.url}
value={image.focalPoint || { x: 0.5, y: 0.5 }}
onChange={(focalPoint) =>
updateImageFocalPoint(index, focalPoint)
}
/>
<RangeControl
label={__("Scale", "image-stack")}
value={image.scale || 0.8}
onChange={(scale) => updateImageScale(index, scale)}
min={0.1}
max={3}
step={0.05}
/>
<RangeControl
label={__("Rotation (deg)", "image-stack")}
value={image.rotation || 0}
onChange={(rotation) => updateImageRotation(index, rotation)}
min={-180}
max={180}
step={1}
/>
<div className="image-stack-image-controls">
<Button
isSecondary
isSmall
disabled={index === 0}
onClick={() => moveImage(index, -1)}
>
{__("↑ Move Up", "image-stack")}
</Button>
<Button
isSecondary
isSmall
disabled={index === images.length - 1}
onClick={() => moveImage(index, 1)}
>
{__("↓ Move Down", "image-stack")}
</Button>
<Button
isDestructive
isSmall
onClick={() => removeImage(index)}
>
{__("Remove", "image-stack")}
</Button>
</div>
</PanelBody>
))}
</PanelBody>
)}
</InspectorControls>
<div {...blockProps}>
<div
className="image-stack-container"
style={{ height: `${height}px` }}
>
{images.length === 0 && (
<div className="image-stack-placeholder">
<MediaUploadCheck>
<MediaUpload
onSelect={onSelectImages}
allowedTypes={["image"]}
multiple
gallery
render={({ open }) => (
<Button variant="primary" onClick={open}>
{__("Ajouter une Image", "image-stack")}
</Button>
)}
/>
</MediaUploadCheck>
</div>
)}
{images.map((image, index) => (
<div
key={image.id}
className={`image-stack-item ${
selectedImageIndex === index ? "is-selected" : ""
}`}
onClick={() => setSelectedImageIndex(index)}
role="button"
tabIndex={0}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
setSelectedImageIndex(index);
}
}}
style={getImageStyle(image)}
>
<img src={image.url} alt={image.alt} />
</div>
))}
</div>
</div>
</>
);
}