// Rôle : fournit l'outil images avec import, collage, libellés et annotation rapide. import { useState } from "react"; import { Icon } from "../../../components/Icon.jsx"; import { usePointerReorder } from "../../../hooks/usePointerReorder.js"; export function ImagesModule({ toolboxId, moduleId, context, editing }) { const data = context.getModuleData(toolboxId, moduleId, { images: [] }); const textContent = context.moduleText?.images || {}; const [dragOver, setDragOver] = useState(false); const pastePlaceholder = textContent.pastePlaceholder || "Coller une image ici"; const { draggingId: draggingImageId, dropTarget, startDrag } = usePointerReorder({ targetSelector: "[data-image-id]", getTargetId: (target) => target.dataset.imageId, canDropOn: (target) => target.dataset.imageModuleId === moduleId, getPlacement: (event, target) => { const rect = target.getBoundingClientRect(); return event.clientX > rect.left + rect.width / 2 ? "after" : "before"; }, onMove: (draggingId, targetId, placement) => { const nextImages = data.images.filter((image) => image.id !== draggingId); const targetIndex = nextImages.findIndex((image) => image.id === targetId); if (targetIndex === -1) return; nextImages.splice(placement === "after" ? targetIndex + 1 : targetIndex, 0, data.images.find((image) => image.id === draggingId)); context.setModuleData(toolboxId, moduleId, { images: nextImages.filter(Boolean) }); } }); async function addFiles(files) { if (await context.addImageFiles(toolboxId, moduleId, files)) { setDragOver(false); } } function updateImage(imageId, patch) { context.setModuleData(toolboxId, moduleId, { images: data.images.map((image) => image.id === imageId ? { ...image, ...patch } : image) }); } return ( <> {editing && (
{ if (event.currentTarget.textContent.trim() === pastePlaceholder) event.currentTarget.textContent = ""; }} onBlur={(event) => { if (!event.currentTarget.textContent.trim()) event.currentTarget.textContent = pastePlaceholder; }} onPaste={(event) => { const files = [...(event.clipboardData?.items || [])] .filter((item) => item.type.startsWith("image/")) .map((item) => item.getAsFile()) .filter(Boolean); if (!files.length) return; event.preventDefault(); event.currentTarget.textContent = pastePlaceholder; addFiles(files); }} > {pastePlaceholder}
)}
{data.images.map((image) => { const isDragging = draggingImageId === image.id; const isDropTarget = dropTarget.id === image.id; const className = [ isDragging ? "is-dragging" : "", isDropTarget ? "is-drop-target" : "", isDropTarget && dropTarget.placement === "after" ? "drop-after" : "" ].filter(Boolean).join(" "); return (
{editing ? ( updateImage(image.id, { label: event.target.value })} aria-label={textContent.labelAriaLabel || "Libellé de l'image"} /> ) : image.label ? (
{image.label}
) : null}
); })}
); }