allaw-ui 5.1.2 → 5.1.4

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.
@@ -14,6 +14,7 @@ export interface TextAreaProps {
14
14
  error?: string;
15
15
  maxHeight?: number;
16
16
  minHeight?: number;
17
+ maxLength?: number;
17
18
  variant?: "bold" | "semiBold" | "medium";
18
19
  color?: "bleu-allaw" | "mid-grey" | "dark-grey" | "noir" | "pure-white" | "grey-venom" | "venom-grey-dark";
19
20
  dataTestId?: string;
@@ -6,7 +6,7 @@ import Paragraph from "../typography/Paragraph";
6
6
  import TinyInfo from "../typography/TinyInfo";
7
7
  import "./TextArea.css";
8
8
  var TextArea = forwardRef(function (_a, ref) {
9
- var title = _a.title, _b = _a.style, style = _b === void 0 ? "default" : _b, placeholder = _a.placeholder, _c = _a.isRequired, isRequired = _c === void 0 ? false : _c, validate = _a.validate, onError = _a.onError, onChange = _a.onChange, propValue = _a.value, propError = _a.error, _d = _a.maxHeight, maxHeight = _d === void 0 ? 400 : _d, minHeight = _a.minHeight, _e = _a.variant, variant = _e === void 0 ? "medium" : _e, _f = _a.color, color = _f === void 0 ? "noir" : _f, dataTestId = _a.dataTestId, _g = _a.isPrefilled, isPrefilled = _g === void 0 ? false : _g;
9
+ var title = _a.title, _b = _a.style, style = _b === void 0 ? "default" : _b, placeholder = _a.placeholder, _c = _a.isRequired, isRequired = _c === void 0 ? false : _c, validate = _a.validate, onError = _a.onError, onChange = _a.onChange, propValue = _a.value, propError = _a.error, _d = _a.maxHeight, maxHeight = _d === void 0 ? 400 : _d, minHeight = _a.minHeight, maxLength = _a.maxLength, _e = _a.variant, variant = _e === void 0 ? "medium" : _e, _f = _a.color, color = _f === void 0 ? "noir" : _f, dataTestId = _a.dataTestId, _g = _a.isPrefilled, isPrefilled = _g === void 0 ? false : _g;
10
10
  var _h = useState(propValue || ""), value = _h[0], setValue = _h[1];
11
11
  var _j = useState(propError || ""), error = _j[0], setError = _j[1];
12
12
  var _k = useState(false), isTouched = _k[0], setIsTouched = _k[1];
@@ -86,7 +86,7 @@ var TextArea = forwardRef(function (_a, ref) {
86
86
  : ""), "data-testid": dataTestId || "text-area" },
87
87
  React.createElement("textarea", { ref: textareaRef, placeholder: placeholder, className: minHeight !== undefined
88
88
  ? "text-area-placeholder no-transition"
89
- : "text-area-placeholder", value: value, onChange: handleChange, onBlur: handleBlur, style: textareaStyle, "data-testid": dataTestId || "text-area" })),
89
+ : "text-area-placeholder", value: value, onChange: handleChange, onBlur: handleBlur, style: textareaStyle, maxLength: maxLength, "data-testid": dataTestId || "text-area" })),
90
90
  error && isTouched && (React.createElement("div", { className: "error-message" },
91
91
  React.createElement(TinyInfo, { variant: "medium12", color: "actions-error", text: error }))))));
92
92
  });
@@ -213,6 +213,7 @@ var FileUploader = function (_a) {
213
213
  };
214
214
  /**
215
215
  * Crop l'image côté client et retourne un Blob
216
+ * Utilise la même logique que ImageCropperModal pour calculer le crop
216
217
  */
217
218
  var cropImageToBlob = function (imageUrl, cropMetadata, outputWidth, outputHeight) { return __awaiter(void 0, void 0, void 0, function () {
218
219
  return __generator(this, function (_a) {
@@ -230,47 +231,64 @@ var FileUploader = function (_a) {
230
231
  reject(new Error("Impossible de créer le contexte canvas"));
231
232
  return;
232
233
  }
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;
234
+ // Dimensions de la zone de crop dans le modal (selon le CSS)
235
+ // Ces dimensions correspondent exactement à celles utilisées dans ImageCropperModal.module.css
236
+ var cropAreaWidth = 280; // Dimensions réelles du cadre de crop pour square/circle
237
+ var cropAreaHeight = 280;
239
238
  if (cropMetadata.shape === "banner") {
240
- cropAreaWidth = 600;
241
- cropAreaHeight = 200;
239
+ cropAreaWidth = 320;
240
+ cropAreaHeight = 60;
242
241
  }
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;
242
+ // Le zoom dans cropMetadata est le zoom total (baseZoom * zoom relatif)
243
+ // baseZoom est calculé dans le modal comme Math.max(cropWidth / imgWidth, cropHeight / imgHeight)
244
+ // puis multiplié par le zoom relatif (1-3) pour obtenir le zoom total
245
+ var totalZoom = cropMetadata.zoom;
246
+ // Dans le modal, la transform CSS est appliquée sur le container de l'image :
247
+ // transform: translate(offsetX, offsetY) scale(totalZoom)
248
+ // transform-origin: center
249
+ //
250
+ // L'ordre d'application est : scale d'abord (depuis le centre), puis translate
251
+ // Géométriquement, cela signifie :
252
+ // 1. L'image est agrandie avec scale(totalZoom) depuis son centre
253
+ // 2. L'image agrandie est déplacée de (offsetX, offsetY)
254
+ //
255
+ // La cropArea est fixe et centrée dans le container
256
+ // Pour trouver quelle partie de l'image originale correspond à la cropArea :
257
+ // - Le centre de la cropArea correspond au point (0, 0) dans le système de coordonnées du container
258
+ // - Ce point (0, 0) dans l'image transformée correspond à (-offsetX, -offsetY) dans l'image agrandie
259
+ // - Ce point dans l'image agrandie correspond à (-offsetX/totalZoom, -offsetY/totalZoom) dans l'image originale
260
+ // Centre de l'image originale
261
+ var imgCenterX = img.width / 2;
262
+ var imgCenterY = img.height / 2;
263
+ // Position du centre de la cropArea dans l'image originale
264
+ // Si l'image transformée est déplacée de offsetX vers la droite,
265
+ // alors le centre de la cropArea (qui reste au centre du container)
266
+ // correspond à un point qui est offsetX pixels à gauche du centre de l'image transformée
267
+ // Converti en coordonnées de l'image originale :
268
+ var cropCenterXInOriginal = imgCenterX - cropMetadata.offsetX / totalZoom;
269
+ var cropCenterYInOriginal = imgCenterY - cropMetadata.offsetY / totalZoom;
270
+ // Dimensions de la zone de crop dans l'image originale
271
+ var cropWidthInOriginal = cropAreaWidth / totalZoom;
272
+ var cropHeightInOriginal = cropAreaHeight / totalZoom;
273
+ // Coordonnées du coin supérieur gauche de la zone à cropper dans l'image originale
274
+ var sourceX = cropCenterXInOriginal - cropWidthInOriginal / 2;
275
+ var sourceY = cropCenterYInOriginal - cropHeightInOriginal / 2;
276
+ var sourceWidth = cropWidthInOriginal;
277
+ var sourceHeight = cropHeightInOriginal;
278
+ // S'assurer que les coordonnées sont valides (pas en dehors de l'image)
279
+ var clampedSourceX = Math.max(0, Math.min(sourceX, img.width - 1));
280
+ var clampedSourceY = Math.max(0, Math.min(sourceY, img.height - 1));
281
+ var clampedSourceWidth = Math.min(sourceWidth, img.width - clampedSourceX);
282
+ var clampedSourceHeight = Math.min(sourceHeight, img.height - clampedSourceY);
261
283
  // 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);
284
+ ctx.drawImage(img, clampedSourceX, clampedSourceY, clampedSourceWidth, clampedSourceHeight, 0, 0, outputWidth, outputHeight);
267
285
  // Si c'est un cercle, appliquer un masque
268
286
  if (cropMetadata.shape === "circle") {
269
287
  var imageData = ctx.getImageData(0, 0, outputWidth, outputHeight);
270
288
  var pixels = imageData.data;
271
289
  var centerX = outputWidth / 2;
272
290
  var centerY = outputHeight / 2;
273
- var radius = outputWidth / 2;
291
+ var radius = Math.min(outputWidth, outputHeight) / 2;
274
292
  for (var y = 0; y < outputHeight; y++) {
275
293
  for (var x = 0; x < outputWidth; x++) {
276
294
  var distance = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2));
@@ -322,8 +340,7 @@ var FileUploader = function (_a) {
322
340
  case 1:
323
341
  _a.trys.push([1, 4, , 5]);
324
342
  dimensions = cropOutputDimensions[cropMetadata.shape] ||
325
- cropOutputDimensions.square ||
326
- { width: 200, height: 200 };
343
+ cropOutputDimensions.square || { width: 200, height: 200 };
327
344
  return [4 /*yield*/, cropImageToBlob(previewUrl, cropMetadata, dimensions.width, dimensions.height)];
328
345
  case 2:
329
346
  croppedBlob = _a.sent();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "allaw-ui",
3
- "version": "5.1.2",
3
+ "version": "5.1.4",
4
4
  "description": "Composants UI pour l'application Allaw",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",