allaw-ui 5.1.0 → 5.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -6,6 +6,21 @@ export interface FileUploaderProps {
|
|
|
6
6
|
enableDragAndDrop?: boolean;
|
|
7
7
|
enableCropping?: boolean;
|
|
8
8
|
cropShape?: "circle" | "square" | "banner";
|
|
9
|
+
clientSideCrop?: boolean;
|
|
10
|
+
cropOutputDimensions?: {
|
|
11
|
+
square?: {
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
};
|
|
15
|
+
circle?: {
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
};
|
|
19
|
+
banner?: {
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
9
24
|
iconUrl?: string;
|
|
10
25
|
descriptionParts?: {
|
|
11
26
|
beforeLink: string;
|
|
@@ -41,17 +41,21 @@ import ImageCropperModal from "./ImageCropperModal";
|
|
|
41
41
|
import IconButton from "../../atoms/buttons/IconButton";
|
|
42
42
|
import TertiaryButton from "../../atoms/buttons/TertiaryButton";
|
|
43
43
|
var FileUploader = function (_a) {
|
|
44
|
-
var acceptedExtensions = _a.acceptedExtensions, maxFileSizeMB = _a.maxFileSizeMB, _b = _a.enableDragAndDrop, enableDragAndDrop = _b === void 0 ? true : _b, _c = _a.enableCropping, enableCropping = _c === void 0 ? false : _c, _d = _a.cropShape, cropShape = _d === void 0 ? "square" : _d,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
var
|
|
50
|
-
var
|
|
51
|
-
var
|
|
44
|
+
var acceptedExtensions = _a.acceptedExtensions, maxFileSizeMB = _a.maxFileSizeMB, _b = _a.enableDragAndDrop, enableDragAndDrop = _b === void 0 ? true : _b, _c = _a.enableCropping, enableCropping = _c === void 0 ? false : _c, _d = _a.cropShape, cropShape = _d === void 0 ? "square" : _d, _e = _a.clientSideCrop, clientSideCrop = _e === void 0 ? false : _e, _f = _a.cropOutputDimensions, cropOutputDimensions = _f === void 0 ? {
|
|
45
|
+
square: { width: 200, height: 200 },
|
|
46
|
+
circle: { width: 200, height: 200 },
|
|
47
|
+
banner: { width: 1200, height: 400 },
|
|
48
|
+
} : _f, iconUrl = _a.iconUrl, descriptionParts = _a.descriptionParts, onFileRead = _a.onFileRead, onFileRemove = _a.onFileRemove, _g = _a.uploadProgress, uploadProgress = _g === void 0 ? 0 : _g, _h = _a.isLoading, isLoading = _h === void 0 ? false : _h, _j = _a.autoManageProgress, autoManageProgress = _j === void 0 ? false : _j, _k = _a.progressSteps, progressSteps = _k === void 0 ? 10 : _k, _l = _a.progressInterval, progressInterval = _l === void 0 ? 500 : _l, _m = _a.errorMessage, errorMessage = _m === void 0 ? null : _m, _o = _a.buttonLabel, buttonLabel = _o === void 0 ? "Choisir un fichier" : _o, _p = _a.acceptedLabel, acceptedLabel = _p === void 0 ? "Format accepté :" : _p, _q = _a.maxSizeLabel, maxSizeLabel = _q === void 0 ? "Taille maximale :" : _q, fileName = _a.fileName, fileSize = _a.fileSize, _r = _a.filePresentationLabel, filePresentationLabel = _r === void 0 ? "Voici votre fichier." : _r, initialFile = _a.initialFile, initialPreviewUrl = _a.initialPreviewUrl, initialCropMetadata = _a.initialCropMetadata, originalFileName = _a.originalFileName;
|
|
49
|
+
var _s = useState(initialFile || null), selectedFile = _s[0], setSelectedFile = _s[1];
|
|
50
|
+
var _t = useState(null), fileContent = _t[0], setFileContent = _t[1];
|
|
51
|
+
var _u = useState(false), isHovering = _u[0], setIsHovering = _u[1];
|
|
52
|
+
var _v = useState(false), showCropper = _v[0], setShowCropper = _v[1];
|
|
53
|
+
var _w = useState(initialPreviewUrl || null), previewUrl = _w[0], setPreviewUrl = _w[1];
|
|
54
|
+
var _x = useState(initialCropMetadata || null), cropMetadata = _x[0], setCropMetadata = _x[1];
|
|
55
|
+
var _y = useState(0), internalProgress = _y[0], setInternalProgress = _y[1];
|
|
52
56
|
var progressIntervalRef = useRef(null);
|
|
53
57
|
var fileInputRef = useRef(null);
|
|
54
|
-
var
|
|
58
|
+
var _z = useState(originalFileName), currentFileName = _z[0], setCurrentFileName = _z[1];
|
|
55
59
|
useEffect(function () {
|
|
56
60
|
if (initialFile) {
|
|
57
61
|
setSelectedFile(initialFile);
|
|
@@ -207,19 +211,153 @@ var FileUploader = function (_a) {
|
|
|
207
211
|
setShowCropper(true);
|
|
208
212
|
}
|
|
209
213
|
};
|
|
214
|
+
/**
|
|
215
|
+
* Crop l'image côté client et retourne un Blob
|
|
216
|
+
*/
|
|
217
|
+
var cropImageToBlob = function (imageUrl, cropMetadata, outputWidth, outputHeight) { return __awaiter(void 0, void 0, void 0, function () {
|
|
218
|
+
return __generator(this, function (_a) {
|
|
219
|
+
return [2 /*return*/, new Promise(function (resolve, reject) {
|
|
220
|
+
var img = new window.Image();
|
|
221
|
+
img.crossOrigin = "anonymous";
|
|
222
|
+
img.onload = function () {
|
|
223
|
+
try {
|
|
224
|
+
// Créer un canvas aux dimensions de sortie
|
|
225
|
+
var canvas = document.createElement("canvas");
|
|
226
|
+
canvas.width = outputWidth;
|
|
227
|
+
canvas.height = outputHeight;
|
|
228
|
+
var ctx = canvas.getContext("2d");
|
|
229
|
+
if (!ctx) {
|
|
230
|
+
reject(new Error("Impossible de créer le contexte canvas"));
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
// Dimensions de la zone de crop (basées sur le modal)
|
|
234
|
+
// On doit retrouver les dimensions de la cropArea du modal
|
|
235
|
+
// Pour simplifier, on considère que la cropArea fait 400x400 pour square
|
|
236
|
+
// et qu'on scale proportionnellement
|
|
237
|
+
var cropAreaWidth = 400;
|
|
238
|
+
var cropAreaHeight = 400;
|
|
239
|
+
if (cropMetadata.shape === "banner") {
|
|
240
|
+
cropAreaWidth = 600;
|
|
241
|
+
cropAreaHeight = 200;
|
|
242
|
+
}
|
|
243
|
+
else if (cropMetadata.shape === "circle") {
|
|
244
|
+
cropAreaWidth = 400;
|
|
245
|
+
cropAreaHeight = 400;
|
|
246
|
+
}
|
|
247
|
+
// Calculer les dimensions de l'image transformée dans le modal
|
|
248
|
+
var scaledImgWidth = img.width * cropMetadata.zoom;
|
|
249
|
+
var scaledImgHeight = img.height * cropMetadata.zoom;
|
|
250
|
+
// Position du coin supérieur gauche de la cropArea par rapport à l'image transformée
|
|
251
|
+
// Dans le modal, l'image est centrée et on a des offsets
|
|
252
|
+
var imgCenterX = scaledImgWidth / 2;
|
|
253
|
+
var imgCenterY = scaledImgHeight / 2;
|
|
254
|
+
// Le centre de la cropArea est au centre du viewport
|
|
255
|
+
// avec les offsets appliqués
|
|
256
|
+
var cropCenterX = imgCenterX - cropMetadata.offsetX;
|
|
257
|
+
var cropCenterY = imgCenterY - cropMetadata.offsetY;
|
|
258
|
+
// Coordonnées du coin supérieur gauche de la zone à cropper
|
|
259
|
+
var sourceX = cropCenterX - cropAreaWidth / 2;
|
|
260
|
+
var sourceY = cropCenterY - cropAreaHeight / 2;
|
|
261
|
+
// Dessiner l'image croppée sur le canvas
|
|
262
|
+
ctx.drawImage(img, sourceX / cropMetadata.zoom, // source x dans l'image originale
|
|
263
|
+
sourceY / cropMetadata.zoom, // source y dans l'image originale
|
|
264
|
+
cropAreaWidth / cropMetadata.zoom, // largeur source
|
|
265
|
+
cropAreaHeight / cropMetadata.zoom, // hauteur source
|
|
266
|
+
0, 0, outputWidth, outputHeight);
|
|
267
|
+
// Si c'est un cercle, appliquer un masque
|
|
268
|
+
if (cropMetadata.shape === "circle") {
|
|
269
|
+
var imageData = ctx.getImageData(0, 0, outputWidth, outputHeight);
|
|
270
|
+
var pixels = imageData.data;
|
|
271
|
+
var centerX = outputWidth / 2;
|
|
272
|
+
var centerY = outputHeight / 2;
|
|
273
|
+
var radius = outputWidth / 2;
|
|
274
|
+
for (var y = 0; y < outputHeight; y++) {
|
|
275
|
+
for (var x = 0; x < outputWidth; x++) {
|
|
276
|
+
var distance = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2));
|
|
277
|
+
if (distance > radius) {
|
|
278
|
+
var index = (y * outputWidth + x) * 4;
|
|
279
|
+
pixels[index + 3] = 0; // Rendre transparent
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
ctx.putImageData(imageData, 0, 0);
|
|
284
|
+
}
|
|
285
|
+
// Convertir en Blob PNG
|
|
286
|
+
canvas.toBlob(function (blob) {
|
|
287
|
+
if (blob) {
|
|
288
|
+
resolve(blob);
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
reject(new Error("Échec de la conversion canvas vers Blob"));
|
|
292
|
+
}
|
|
293
|
+
}, "image/png", 0.95);
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
reject(error);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
img.onerror = function () {
|
|
300
|
+
reject(new Error("Échec du chargement de l'image"));
|
|
301
|
+
};
|
|
302
|
+
img.src = imageUrl;
|
|
303
|
+
})];
|
|
304
|
+
});
|
|
305
|
+
}); };
|
|
210
306
|
var handleCropCancel = function () {
|
|
211
307
|
setShowCropper(false);
|
|
212
308
|
};
|
|
213
|
-
var handleCropConfirm = function (cropMetadata) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
309
|
+
var handleCropConfirm = function (cropMetadata) { return __awaiter(void 0, void 0, void 0, function () {
|
|
310
|
+
var dimensions, croppedBlob, croppedFile, croppedContent, croppedUrl, error_2;
|
|
311
|
+
return __generator(this, function (_a) {
|
|
312
|
+
switch (_a.label) {
|
|
313
|
+
case 0:
|
|
314
|
+
setShowCropper(false);
|
|
315
|
+
setCropMetadata(cropMetadata);
|
|
316
|
+
if (autoManageProgress) {
|
|
317
|
+
startProgressSimulation();
|
|
318
|
+
}
|
|
319
|
+
if (!(selectedFile && fileContent && previewUrl)) return [3 /*break*/, 7];
|
|
320
|
+
if (!clientSideCrop) return [3 /*break*/, 6];
|
|
321
|
+
_a.label = 1;
|
|
322
|
+
case 1:
|
|
323
|
+
_a.trys.push([1, 4, , 5]);
|
|
324
|
+
dimensions = cropOutputDimensions[cropMetadata.shape] ||
|
|
325
|
+
cropOutputDimensions.square ||
|
|
326
|
+
{ width: 200, height: 200 };
|
|
327
|
+
return [4 /*yield*/, cropImageToBlob(previewUrl, cropMetadata, dimensions.width, dimensions.height)];
|
|
328
|
+
case 2:
|
|
329
|
+
croppedBlob = _a.sent();
|
|
330
|
+
croppedFile = new File([croppedBlob], selectedFile.name.replace(/\.[^/.]+$/, ".png"), // Remplacer l'extension par .png
|
|
331
|
+
{ type: "image/png" });
|
|
332
|
+
return [4 /*yield*/, croppedFile.arrayBuffer()];
|
|
333
|
+
case 3:
|
|
334
|
+
croppedContent = _a.sent();
|
|
335
|
+
croppedUrl = URL.createObjectURL(croppedBlob);
|
|
336
|
+
if (previewUrl && !initialPreviewUrl) {
|
|
337
|
+
URL.revokeObjectURL(previewUrl);
|
|
338
|
+
}
|
|
339
|
+
setPreviewUrl(croppedUrl);
|
|
340
|
+
setSelectedFile(croppedFile);
|
|
341
|
+
setFileContent(croppedContent);
|
|
342
|
+
setCurrentFileName(croppedFile.name);
|
|
343
|
+
// Appeler onFileRead avec le fichier croppé (pas de cropMetadata car déjà croppé)
|
|
344
|
+
onFileRead === null || onFileRead === void 0 ? void 0 : onFileRead(croppedFile, croppedContent);
|
|
345
|
+
return [3 /*break*/, 5];
|
|
346
|
+
case 4:
|
|
347
|
+
error_2 = _a.sent();
|
|
348
|
+
console.error("Erreur lors du crop de l'image:", error_2);
|
|
349
|
+
// En cas d'erreur, fallback sur l'image originale avec métadonnées
|
|
350
|
+
onFileRead === null || onFileRead === void 0 ? void 0 : onFileRead(selectedFile, fileContent, cropMetadata);
|
|
351
|
+
return [3 /*break*/, 5];
|
|
352
|
+
case 5: return [3 /*break*/, 7];
|
|
353
|
+
case 6:
|
|
354
|
+
// Passer l'image originale avec les métadonnées au serveur
|
|
355
|
+
onFileRead === null || onFileRead === void 0 ? void 0 : onFileRead(selectedFile, fileContent, cropMetadata);
|
|
356
|
+
_a.label = 7;
|
|
357
|
+
case 7: return [2 /*return*/];
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
}); };
|
|
223
361
|
var handleFileDelete = function () {
|
|
224
362
|
if (previewUrl && !initialPreviewUrl) {
|
|
225
363
|
URL.revokeObjectURL(previewUrl);
|