improve notepad features and add drawing canvas
All checks were successful
Deploy Sokko G / deploy (push) Successful in 7s
All checks were successful
Deploy Sokko G / deploy (push) Successful in 7s
This commit is contained in:
parent
e73d754082
commit
de1b8a5638
20 changed files with 1732 additions and 141 deletions
|
|
@ -1,6 +1,7 @@
|
|||
// Rôle : fournit l'outil annotation d'images avec marqueurs éditables.
|
||||
import { useState } from "react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { Icon } from "../../../components/Icon.jsx";
|
||||
import { DrawingOverlay } from "./DrawingOverlay.jsx";
|
||||
|
||||
function markerLabel(index, marker, textContent) {
|
||||
return marker.label || `${textContent.markerPrefix || "Marqueur"} ${index + 1}`;
|
||||
|
|
@ -9,9 +10,36 @@ function markerLabel(index, marker, textContent) {
|
|||
export function ImageAnnotationModule({ toolboxId, moduleId, context, editing }) {
|
||||
const data = context.normalizeImageAnnotationData(context.getModuleData(toolboxId, moduleId, { image: "", markers: [] }));
|
||||
const textContent = context.moduleText?.imageAnnotation || {};
|
||||
const previewMediaRef = useRef(null);
|
||||
const sessionKey = `sokkog:image-annotation-drawings:${toolboxId}:${moduleId}`;
|
||||
const [dragOver, setDragOver] = useState(false);
|
||||
const [drawingMode, setDrawingMode] = useState("permanent");
|
||||
const [temporaryDrawings, setTemporaryDrawings] = useState({ strokes: [] });
|
||||
const markers = data.markers;
|
||||
const drawings = drawingMode === "permanent" ? data.drawings || { strokes: [] } : temporaryDrawings;
|
||||
const pastePlaceholder = textContent.pastePlaceholder || "Coller une image ici";
|
||||
const getPreviewSurfaceSize = useCallback((element) => ({
|
||||
width: element?.clientWidth || 0,
|
||||
height: element?.clientHeight || 0
|
||||
}), []);
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
const stored = JSON.parse(sessionStorage.getItem(sessionKey) || "{\"strokes\":[]}");
|
||||
setTemporaryDrawings(stored?.strokes ? stored : { strokes: [] });
|
||||
} catch {
|
||||
setTemporaryDrawings({ strokes: [] });
|
||||
}
|
||||
}, [sessionKey]);
|
||||
|
||||
useEffect(() => {
|
||||
if (drawingMode !== "temporary") return;
|
||||
try {
|
||||
sessionStorage.setItem(sessionKey, JSON.stringify(temporaryDrawings));
|
||||
} catch {
|
||||
context.notify?.(textContent.sessionStorageError || "Les annotations temporaires n'ont pas pu être sauvegardées.");
|
||||
}
|
||||
}, [context, drawingMode, sessionKey, temporaryDrawings, textContent.sessionStorageError]);
|
||||
|
||||
function save(nextData) {
|
||||
context.setModuleData(toolboxId, moduleId, nextData);
|
||||
|
|
@ -20,12 +48,14 @@ export function ImageAnnotationModule({ toolboxId, moduleId, context, editing })
|
|||
async function setImageFromFiles(files) {
|
||||
const file = [...files].find((item) => item?.type?.startsWith("image/"));
|
||||
if (!file) return;
|
||||
save({ image: await context.compressImageFile(file), markers: [] });
|
||||
save({ image: await context.compressImageFile(file), markers: [], drawings: { strokes: [] } });
|
||||
setTemporaryDrawings({ strokes: [] });
|
||||
setDragOver(false);
|
||||
}
|
||||
|
||||
function removeImage() {
|
||||
save({ image: "", markers: [] });
|
||||
save({ image: "", markers: [], drawings: { strokes: [] } });
|
||||
setTemporaryDrawings({ strokes: [] });
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -79,7 +109,7 @@ export function ImageAnnotationModule({ toolboxId, moduleId, context, editing })
|
|||
{data.image ? (
|
||||
<div className="annotation-workspace">
|
||||
<div className="annotation-stage annotation-stage-preview" aria-label={textContent.stageAriaLabel || "Image annotée"}>
|
||||
<div className="annotation-media">
|
||||
<div className="annotation-media" ref={previewMediaRef}>
|
||||
<img src={data.image} alt={textContent.imageAlt || "Image annotée"} draggable="false" />
|
||||
<div className="annotation-preview-actions">
|
||||
<button
|
||||
|
|
@ -88,14 +118,35 @@ export function ImageAnnotationModule({ toolboxId, moduleId, context, editing })
|
|||
onClick={() => context.setImage({
|
||||
dataUrl: data.image,
|
||||
markers,
|
||||
drawings,
|
||||
alt: textContent.imageAlt || "Image annotée",
|
||||
canAnnotate: true,
|
||||
createMarkerId: () => context.uid("marker"),
|
||||
createStrokeId: () => context.uid("stroke"),
|
||||
drawingMode,
|
||||
markerPrefix: textContent.markerPrefix || "Marqueur",
|
||||
addMarkerAriaLabel: textContent.addMarkerAriaLabel || "Ajouter un marqueur sur l'image",
|
||||
markerPlaceholder: textContent.markerPlaceholder || "Libellé du marqueur",
|
||||
deleteMarkerTitle: textContent.deleteMarkerTitle || "Supprimer",
|
||||
onChangeMarkers: (nextMarkers) => save({ ...data, markers: nextMarkers })
|
||||
drawingLabel: textContent.drawingLabel || "Dessin",
|
||||
drawTitle: textContent.drawTitle || "Dessiner",
|
||||
eraseTitle: textContent.eraseTitle || "Gommer",
|
||||
undoDrawingTitle: textContent.undoDrawingTitle || "Annuler le dernier trait",
|
||||
clearDrawingTitle: textContent.clearDrawingTitle || "Effacer les dessins",
|
||||
drawingColorLabel: textContent.drawingColorLabel || "Couleur du dessin",
|
||||
drawingWidthLabel: textContent.drawingWidthLabel || "Épaisseur du trait",
|
||||
temporaryTitle: textContent.temporaryTitle || "Annotations temporaires",
|
||||
permanentTitle: textContent.permanentTitle || "Annotations permanentes",
|
||||
onChangeMarkers: (nextMarkers) => save({ ...data, markers: nextMarkers }),
|
||||
onChangeDrawings: (nextDrawings, nextMode = drawingMode) => {
|
||||
if (nextMode === "permanent") save({ ...data, drawings: nextDrawings });
|
||||
else setTemporaryDrawings(nextDrawings);
|
||||
},
|
||||
onChangeDrawingMode: (nextMode, currentDrawings) => {
|
||||
setDrawingMode(nextMode);
|
||||
if (nextMode === "permanent") save({ ...data, drawings: currentDrawings });
|
||||
else setTemporaryDrawings(currentDrawings);
|
||||
}
|
||||
})}
|
||||
aria-label={textContent.previewAriaLabel || "Agrandir l'image annotée"}
|
||||
title={textContent.previewTitle || "Agrandir"}
|
||||
|
|
@ -118,6 +169,16 @@ export function ImageAnnotationModule({ toolboxId, moduleId, context, editing })
|
|||
{index + 1}
|
||||
</span>
|
||||
))}
|
||||
<DrawingOverlay
|
||||
active={false}
|
||||
erase={false}
|
||||
strokes={drawings.strokes}
|
||||
color="#f6c453"
|
||||
width={4}
|
||||
onChange={() => {}}
|
||||
coordinateMode="percent"
|
||||
getSurfaceSize={() => getPreviewSurfaceSize(previewMediaRef.current)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue