@beweco/aurora-ui 0.0.8 → 0.0.10

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.
package/dist/index.cjs.js CHANGED
@@ -294,13 +294,25 @@ var countries = [
294
294
  { code: "+971", name: "UAE", flag: "🇦🇪" },
295
295
  ];
296
296
  var uniqueCountries = Array.from(new Map(countries.map(function (item) { return [item.code + item.name, item]; })).values());
297
+ var defaultTranslations$1 = {
298
+ label: "Teléfono",
299
+ placeholder: "Número de teléfono",
300
+ searchPlaceholder: "Buscar país...",
301
+ selectCountryAriaLabel: "Seleccionar país",
302
+ expandListAriaLabel: "Desplegar lista de países",
303
+ noCountriesFound: "No se encontraron países",
304
+ };
297
305
  var Phone = function (_a) {
298
- var _b = _a.label, label = _b === void 0 ? "Teléfono" : _b, _c = _a.required, required = _c === void 0 ? false : _c, _d = _a.error, error = _d === void 0 ? false : _d, _e = _a.errorText, errorText = _e === void 0 ? "" : _e, _f = _a.value, value = _f === void 0 ? "" : _f, onChange = _a.onChange, _g = _a.disabled, disabled = _g === void 0 ? false : _g, _h = _a.name, name = _h === void 0 ? "phone" : _h;
306
+ var label = _a.label, _b = _a.required, required = _b === void 0 ? false : _b, _c = _a.error, error = _c === void 0 ? false : _c, _d = _a.errorText, errorText = _d === void 0 ? "" : _d, _e = _a.value, value = _e === void 0 ? "" : _e, onChange = _a.onChange, onBlur = _a.onBlur, _f = _a.disabled, disabled = _f === void 0 ? false : _f, _g = _a.name, name = _g === void 0 ? "phone" : _g, _h = _a.translations, translations = _h === void 0 ? {} : _h;
299
307
  var _j = React.useState(false), isDropdownOpen = _j[0], setIsDropdownOpen = _j[1];
300
308
  var _k = React.useState(uniqueCountries[0]), selectedCountry = _k[0], setSelectedCountry = _k[1];
301
309
  var _l = React.useState(""), inputValue = _l[0], setInputValue = _l[1];
302
310
  var dropdownRef = React.useRef(null);
303
311
  var _m = React.useState(uniqueCountries), filteredCountries = _m[0], setFilteredCountries = _m[1];
312
+ // Combinar traducciones por defecto con las proporcionadas
313
+ var t = __assign(__assign({}, defaultTranslations$1), translations);
314
+ // Usar la etiqueta de las traducciones si no se proporciona label explícitamente
315
+ var finalLabel = label || t.label;
304
316
  // Sincroniza valor externo
305
317
  React.useEffect(function () {
306
318
  if (value) {
@@ -351,11 +363,11 @@ var Phone = function (_a) {
351
363
  onChange(selectedCountry.code + val);
352
364
  }
353
365
  };
354
- return (jsxRuntime.jsxs("div", { className: "flex flex-col gap-1 w-full relative", children: [label && (jsxRuntime.jsxs("label", { htmlFor: "phone-input-".concat(name), className: "text-sm font-medium text-gray-700 dark:text-gray-200 mb-1", children: [label, " ", required && jsxRuntime.jsx("span", { className: "text-pink-600", children: "*" })] })), jsxRuntime.jsxs("div", { className: "flex items-center w-full min-h-[56px] bg-white dark:bg-gray-900 transition-colors shadow-sm border border-[#E4E4E7] dark:border-gray-700 rounded-2xl focus-within:border-blue-500 dark:focus-within:border-blue-400 ".concat(error
366
+ return (jsxRuntime.jsxs("div", { className: "flex flex-col gap-1 w-full relative", children: [finalLabel && (jsxRuntime.jsxs("label", { htmlFor: "phone-input-".concat(name), className: "text-sm font-medium text-gray-700 dark:text-gray-200 mb-1", children: [finalLabel, " ", required && jsxRuntime.jsx("span", { className: "text-pink-600", children: "*" })] })), jsxRuntime.jsxs("div", { className: "flex items-center w-full min-h-[56px] bg-white dark:bg-gray-900 transition-colors shadow-sm border border-[#E4E4E7] dark:border-gray-700 rounded-2xl focus-within:border-blue-500 dark:focus-within:border-blue-400 ".concat(error
355
367
  ? "border-pink-500 dark:border-pink-600"
356
368
  : "border-[#E4E4E7] dark:border-gray-700").concat(disabled ? " bg-gray-100 dark:bg-gray-800 opacity-60" : ""), children: [jsxRuntime.jsxs("div", { className: "relative ml-2", ref: dropdownRef, children: [jsxRuntime.jsxs("button", { type: "button",
357
369
  ///bg-gray-100
358
- className: "flex items-center gap-1 px-4 h-10 rounded-xl dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 focus:outline-none transition-colors", onClick: function () { return setIsDropdownOpen(function (v) { return !v; }); }, disabled: disabled, tabIndex: 0, "aria-label": "Seleccionar pa\u00EDs", children: [jsxRuntime.jsx("span", { className: "text-lg", children: selectedCountry.flag }), jsxRuntime.jsx("span", { className: "text-xs text-gray-700 dark:text-gray-200", children: selectedCountry.code }), jsxRuntime.jsxs("svg", { className: "w-4 h-4 text-gray-400 dark:text-gray-300 ml-1", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", role: "img", "aria-label": "Desplegar lista de pa\u00EDses", children: [jsxRuntime.jsx("title", { children: "Desplegar lista de pa\u00EDses" }), jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" })] })] }), isDropdownOpen && (jsxRuntime.jsxs("div", { className: "absolute z-30 mt-2 w-60 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg", children: [jsxRuntime.jsx("div", { className: "p-2", children: jsxRuntime.jsx("input", { type: "text", className: "w-full px-3 py-2 text-sm bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 text-gray-900 dark:text-gray-100 rounded-lg focus:outline-none focus:border-blue-500 dark:focus:border-blue-400", placeholder: "Buscar pa\u00EDs...", onChange: function (e) {
370
+ className: "flex items-center gap-1 px-4 h-10 rounded-xl dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 focus:outline-none transition-colors", onClick: function () { return setIsDropdownOpen(function (v) { return !v; }); }, disabled: disabled, tabIndex: 0, "aria-label": t.selectCountryAriaLabel, children: [jsxRuntime.jsx("span", { className: "text-lg", children: selectedCountry.flag }), jsxRuntime.jsx("span", { className: "text-xs text-gray-700 dark:text-gray-200", children: selectedCountry.code }), jsxRuntime.jsxs("svg", { className: "w-4 h-4 text-gray-400 dark:text-gray-300 ml-1", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", role: "img", "aria-label": t.expandListAriaLabel, children: [jsxRuntime.jsx("title", { children: t.expandListAriaLabel }), jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" })] })] }), isDropdownOpen && (jsxRuntime.jsxs("div", { className: "absolute z-30 mt-2 w-60 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg", children: [jsxRuntime.jsx("div", { className: "p-2", children: jsxRuntime.jsx("input", { type: "text", className: "w-full px-3 py-2 text-sm bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 text-gray-900 dark:text-gray-100 rounded-lg focus:outline-none focus:border-blue-500 dark:focus:border-blue-400", placeholder: t.searchPlaceholder, onChange: function (e) {
359
371
  var searchTerm = e.target.value.toLowerCase();
360
372
  var filtered = uniqueCountries.filter(function (country) {
361
373
  return country.name.toLowerCase().includes(searchTerm) ||
@@ -364,8 +376,505 @@ var Phone = function (_a) {
364
376
  setFilteredCountries(filtered);
365
377
  } }) }), jsxRuntime.jsx("div", { className: "max-h-60 overflow-y-auto", children: filteredCountries.length > 0 ? (filteredCountries.map(function (country) { return (jsxRuntime.jsxs("button", { type: "button", className: "flex items-center w-full px-4 py-2.5 text-sm hover:bg-gray-50 dark:hover:bg-gray-800 ".concat(country.code === selectedCountry.code
366
378
  ? "bg-blue-50 dark:bg-blue-900"
367
- : ""), onClick: function () { return handleCountrySelect(country); }, children: [jsxRuntime.jsx("span", { className: "mr-3 text-lg", children: country.flag }), jsxRuntime.jsx("span", { className: "flex-1 text-left dark:text-gray-100", children: country.name }), jsxRuntime.jsx("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: country.code })] }, country.code)); })) : (jsxRuntime.jsx("div", { className: "px-4 py-2 text-sm text-gray-500 dark:text-gray-400", children: "No se encontraron pa\u00EDses" })) })] }))] }), jsxRuntime.jsx(react.Input, { type: "tel", className: "flex-1 border-none bg-transparent text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 h-10 px-2", placeholder: "N\u00FAmero de tel\u00E9fono", value: inputValue, onChange: handleInputChange, disabled: disabled, name: name, autoComplete: "tel", id: "phone-input-".concat(name) })] }), error && errorText && (jsxRuntime.jsx("span", { className: "text-xs text-pink-600 dark:text-pink-400 mt-1", children: errorText }))] }));
379
+ : ""), onClick: function () { return handleCountrySelect(country); }, children: [jsxRuntime.jsx("span", { className: "mr-3 text-lg", children: country.flag }), jsxRuntime.jsx("span", { className: "flex-1 text-left dark:text-gray-100", children: country.name }), jsxRuntime.jsx("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: country.code })] }, country.code)); })) : (jsxRuntime.jsx("div", { className: "px-4 py-2 text-sm text-gray-500 dark:text-gray-400", children: t.noCountriesFound })) })] }))] }), jsxRuntime.jsx(react.Input, { type: "tel", className: "flex-1 border-none bg-transparent text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 h-10 px-2", placeholder: t.placeholder, value: inputValue, onChange: handleInputChange, onBlur: onBlur, disabled: disabled, name: name, autoComplete: "tel", id: "phone-input-".concat(name) })] }), error && errorText && (jsxRuntime.jsx("span", { className: "text-xs text-pink-600 dark:text-pink-400 mt-1", children: errorText }))] }));
380
+ };
381
+
382
+ // Traducciones por defecto en español
383
+ var defaultTranslations = {
384
+ uploadText: "Da clic y selecciona tus archivos",
385
+ subText: "",
386
+ dragText: "Suelta el archivo aquí",
387
+ multipleFilesError: "Solo puedes subir un archivo",
388
+ maxFilesError: "Puedes subir máximo {maxFiles} archivos",
389
+ invalidFileTypeError: "Solo se aceptan archivos de tipo: {acceptedTypes}",
390
+ maxFileSizeError: "El archivo excede el tamaño máximo permitido de {maxSize}",
391
+ removeFileAriaLabel: "Remover archivo",
392
+ uploadAreaAriaLabel: "Área de carga de archivos",
393
+ cropModalTitle: "Recortar imagen",
394
+ cropSaveButton: "Guardar",
395
+ cropCancelButton: "Cancelar",
396
+ cropZoomLabel: "Zoom",
397
+ cropInstructions: "Arrastra para mover la imagen y usa el zoom para ajustar el tamaño",
398
+ cropBackgroundLabel: "Color de fondo",
399
+ cropBackgroundWhite: "Blanco",
400
+ cropBackgroundBlack: "Negro",
401
+ cropBackgroundTransparent: "Transparente",
402
+ };
403
+ /**
404
+ * Formatea el tipo de archivo para mostrar
405
+ */
406
+ var formatFileType = function (type) {
407
+ var _a;
408
+ if (type.startsWith(".")) {
409
+ return type.toUpperCase();
410
+ }
411
+ if (type.includes("/")) {
412
+ var parts = type.split("/");
413
+ return ((_a = parts[1]) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || type;
414
+ }
415
+ return type.toUpperCase();
416
+ };
417
+ /**
418
+ * Formatea el tamaño del archivo de forma legible
419
+ */
420
+ var formatFileSize = function (bytes) {
421
+ if (bytes === 0) {
422
+ return "0 Bytes";
423
+ }
424
+ var k = 1024;
425
+ var sizes = ["Bytes", "KB", "MB", "GB"];
426
+ var i = Math.floor(Math.log(bytes) / Math.log(k));
427
+ return "".concat(Math.round(bytes / Math.pow(k, i)), " ").concat(sizes[i]);
428
+ };
429
+ /**
430
+ * Componente de recorte de imagen usando Canvas API
431
+ */
432
+ var ImageCropModal = function (_a) {
433
+ var isOpen = _a.isOpen, imageUrl = _a.imageUrl, targetWidth = _a.targetWidth, targetHeight = _a.targetHeight, onSave = _a.onSave, onCancel = _a.onCancel, translations = _a.translations;
434
+ var canvasRef = React.useRef(null);
435
+ var _b = React.useState(null), imageElement = _b[0], setImageElement = _b[1];
436
+ var _c = React.useState(0), zoomValue = _c[0], setZoomValue = _c[1]; // Valor del slider de -1 a 1
437
+ var _d = React.useState({ x: 0, y: 0 }), position = _d[0], setPosition = _d[1];
438
+ var _e = React.useState(false), isDragging = _e[0], setIsDragging = _e[1];
439
+ var _f = React.useState({ x: 0, y: 0 }), dragStart = _f[0], setDragStart = _f[1];
440
+ var _g = React.useState(1), baseScale = _g[0], setBaseScale = _g[1]; // Escala base inicial
441
+ var _h = React.useState({ width: 400, height: 300 }), canvasSize = _h[0], setCanvasSize = _h[1];
442
+ var _j = React.useState("white"), backgroundColor = _j[0], setBackgroundColor = _j[1];
443
+ // Función para convertir el valor del zoom slider a escala real
444
+ var getScaleFromZoom = function (zoomValue) {
445
+ if (zoomValue === 0) {
446
+ return baseScale;
447
+ }
448
+ if (zoomValue > 0) {
449
+ // Zoom in: de baseScale a baseScale * 3
450
+ return baseScale + baseScale * 2 * zoomValue;
451
+ }
452
+ // Zoom out: de baseScale * 0.3 a baseScale
453
+ return baseScale + baseScale * 0.7 * zoomValue;
454
+ };
455
+ var currentScale = getScaleFromZoom(zoomValue);
456
+ // Cargar imagen cuando se abre el modal
457
+ React.useEffect(function () {
458
+ if (isOpen && imageUrl) {
459
+ var img_1 = new Image();
460
+ img_1.onload = function () {
461
+ setImageElement(img_1);
462
+ // Calcular escala inicial para que la imagen quepa en el área de crop
463
+ var scaleX = targetWidth / img_1.width;
464
+ var scaleY = targetHeight / img_1.height;
465
+ var initialScale = Math.max(scaleX, scaleY);
466
+ setBaseScale(initialScale);
467
+ setZoomValue(0); // Empezar en el centro
468
+ setPosition({ x: 0, y: 0 });
469
+ setBackgroundColor("white"); // Reset color de fondo
470
+ };
471
+ img_1.src = imageUrl;
472
+ }
473
+ }, [isOpen, imageUrl, targetWidth, targetHeight]);
474
+ // Dibujar en el canvas
475
+ React.useEffect(function () {
476
+ if (!(imageElement && canvasRef.current)) {
477
+ return;
478
+ }
479
+ var canvas = canvasRef.current;
480
+ var ctx = canvas.getContext("2d");
481
+ if (!ctx) {
482
+ return;
483
+ }
484
+ // Limpiar canvas
485
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
486
+ // Dibujar fondo gris del canvas
487
+ ctx.fillStyle = "#f3f4f6";
488
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
489
+ // Calcular posición y tamaño de la imagen
490
+ var imgWidth = imageElement.width * currentScale;
491
+ var imgHeight = imageElement.height * currentScale;
492
+ var imgX = (canvas.width - imgWidth) / 2 + position.x;
493
+ var imgY = (canvas.height - imgHeight) / 2 + position.y;
494
+ // Dibujar imagen
495
+ ctx.drawImage(imageElement, imgX, imgY, imgWidth, imgHeight);
496
+ // Dibujar overlay para mostrar área de recorte
497
+ ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
498
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
499
+ // Limpiar área de recorte (centro)
500
+ var cropX = (canvas.width - targetWidth) / 2;
501
+ var cropY = (canvas.height - targetHeight) / 2;
502
+ ctx.clearRect(cropX, cropY, targetWidth, targetHeight);
503
+ // Dibujar color de fondo del área de recorte
504
+ if (backgroundColor !== "transparent") {
505
+ ctx.fillStyle = backgroundColor === "white" ? "#ffffff" : "#000000";
506
+ ctx.fillRect(cropX, cropY, targetWidth, targetHeight);
507
+ }
508
+ // Redibujar imagen solo en el área de recorte
509
+ ctx.save();
510
+ ctx.beginPath();
511
+ ctx.rect(cropX, cropY, targetWidth, targetHeight);
512
+ ctx.clip();
513
+ ctx.drawImage(imageElement, imgX, imgY, imgWidth, imgHeight);
514
+ ctx.restore();
515
+ // Dibujar borde del área de recorte
516
+ ctx.strokeStyle = "#3b82f6";
517
+ ctx.lineWidth = 2;
518
+ ctx.strokeRect(cropX, cropY, targetWidth, targetHeight);
519
+ }, [
520
+ imageElement,
521
+ currentScale,
522
+ position,
523
+ targetWidth,
524
+ targetHeight,
525
+ backgroundColor,
526
+ ]);
527
+ var handleMouseDown = function (e) {
528
+ var _a;
529
+ e.preventDefault();
530
+ setIsDragging(true);
531
+ var rect = (_a = canvasRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
532
+ if (rect) {
533
+ setDragStart({
534
+ x: e.clientX - rect.left - position.x,
535
+ y: e.clientY - rect.top - position.y,
536
+ });
537
+ }
538
+ };
539
+ var handleMouseMove = function (e) {
540
+ var _a;
541
+ if (!isDragging) {
542
+ return;
543
+ }
544
+ e.preventDefault();
545
+ var rect = (_a = canvasRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
546
+ if (rect) {
547
+ setPosition({
548
+ x: e.clientX - rect.left - dragStart.x,
549
+ y: e.clientY - rect.top - dragStart.y,
550
+ });
551
+ }
552
+ };
553
+ var handleMouseUp = function () {
554
+ setIsDragging(false);
555
+ };
556
+ // Eventos táctiles para dispositivos móviles
557
+ var handleTouchStart = function (e) {
558
+ var _a;
559
+ e.preventDefault();
560
+ if (e.touches.length === 1) {
561
+ setIsDragging(true);
562
+ var rect = (_a = canvasRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
563
+ if (rect) {
564
+ var touch = e.touches[0];
565
+ setDragStart({
566
+ x: touch.clientX - rect.left - position.x,
567
+ y: touch.clientY - rect.top - position.y,
568
+ });
569
+ }
570
+ }
571
+ };
572
+ var handleTouchMove = function (e) {
573
+ var _a;
574
+ if (!isDragging || e.touches.length !== 1) {
575
+ return;
576
+ }
577
+ e.preventDefault();
578
+ var rect = (_a = canvasRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
579
+ if (rect) {
580
+ var touch = e.touches[0];
581
+ setPosition({
582
+ x: touch.clientX - rect.left - dragStart.x,
583
+ y: touch.clientY - rect.top - dragStart.y,
584
+ });
585
+ }
586
+ };
587
+ var handleTouchEnd = function () {
588
+ setIsDragging(false);
589
+ };
590
+ var handleSave = function () {
591
+ if (!(imageElement && canvasRef.current)) {
592
+ return;
593
+ }
594
+ // Crear canvas temporal para el recorte
595
+ var tempCanvas = document.createElement("canvas");
596
+ tempCanvas.width = targetWidth;
597
+ tempCanvas.height = targetHeight;
598
+ var tempCtx = tempCanvas.getContext("2d");
599
+ if (!tempCtx) {
600
+ return;
601
+ }
602
+ // Dibujar color de fondo si no es transparente
603
+ if (backgroundColor !== "transparent") {
604
+ tempCtx.fillStyle = backgroundColor === "white" ? "#ffffff" : "#000000";
605
+ tempCtx.fillRect(0, 0, targetWidth, targetHeight);
606
+ }
607
+ // Calcular parámetros de recorte
608
+ var canvas = canvasRef.current;
609
+ var imgWidth = imageElement.width * currentScale;
610
+ var imgHeight = imageElement.height * currentScale;
611
+ var imgX = (canvas.width - imgWidth) / 2 + position.x;
612
+ var imgY = (canvas.height - imgHeight) / 2 + position.y;
613
+ var cropX = (canvas.width - targetWidth) / 2;
614
+ var cropY = (canvas.height - targetHeight) / 2;
615
+ // Calcular área fuente en la imagen original
616
+ var sourceX = (cropX - imgX) / currentScale;
617
+ var sourceY = (cropY - imgY) / currentScale;
618
+ var sourceWidth = targetWidth / currentScale;
619
+ var sourceHeight = targetHeight / currentScale;
620
+ // Dibujar recorte sobre el fondo
621
+ tempCtx.drawImage(imageElement, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, targetWidth, targetHeight);
622
+ // Convertir a blob y crear archivo
623
+ var fileType = backgroundColor === "transparent" ? "image/png" : "image/png";
624
+ tempCanvas.toBlob(function (blob) {
625
+ if (blob) {
626
+ var timestamp = new Date().getTime();
627
+ var file = new File([blob], "cropped-image-".concat(timestamp, ".png"), {
628
+ type: fileType,
629
+ });
630
+ onSave(file, imageElement.src);
631
+ }
632
+ }, fileType);
633
+ };
634
+ // Calcular dimensiones responsive del canvas
635
+ var updateCanvasSize = React.useCallback(function () {
636
+ var minCanvasWidth = Math.min(350, window.innerWidth - 40);
637
+ var minCanvasHeight = Math.min(250, window.innerHeight - 300);
638
+ var newCanvasWidth = Math.max(minCanvasWidth, targetWidth + 60);
639
+ var newCanvasHeight = Math.max(minCanvasHeight, targetHeight + 60);
640
+ setCanvasSize({ width: newCanvasWidth, height: newCanvasHeight });
641
+ }, [targetWidth, targetHeight]);
642
+ // Actualizar tamaño del canvas al cambiar el tamaño de la ventana
643
+ React.useEffect(function () {
644
+ if (!isOpen) {
645
+ return;
646
+ }
647
+ updateCanvasSize();
648
+ var handleResize = function () {
649
+ updateCanvasSize();
650
+ };
651
+ window.addEventListener("resize", handleResize);
652
+ return function () {
653
+ window.removeEventListener("resize", handleResize);
654
+ };
655
+ }, [isOpen, updateCanvasSize]);
656
+ return (jsxRuntime.jsx(react.Modal, { isOpen: isOpen, onClose: onCancel, size: "5xl", className: "sm:max-w-4xl", scrollBehavior: "inside", backdrop: "blur", classNames: {
657
+ backdrop: "bg-black/80",
658
+ wrapper: "p-0 sm:p-4",
659
+ base: "m-0 sm:m-4 max-h-screen sm:max-h-[90vh] w-full sm:w-auto",
660
+ }, children: jsxRuntime.jsxs(react.ModalContent, { className: "m-0 sm:m-0 h-screen sm:h-auto sm:max-h-[90vh] sm:rounded-lg", children: [jsxRuntime.jsxs(react.ModalHeader, { className: "flex flex-col gap-1 px-4 sm:px-6", children: [translations.cropModalTitle, jsxRuntime.jsx("p", { className: "text-xs sm:text-sm text-gray-500 font-normal", children: translations.cropInstructions })] }), jsxRuntime.jsx(react.ModalBody, { className: "px-4 sm:px-6 py-4", children: jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [jsxRuntime.jsx("div", { className: "flex justify-center overflow-hidden", children: jsxRuntime.jsx("canvas", { ref: canvasRef, width: canvasSize.width, height: canvasSize.height, className: "border border-gray-300 cursor-move select-none max-w-full max-h-[50vh] sm:max-h-[60vh]", onMouseDown: handleMouseDown, onMouseMove: handleMouseMove, onMouseUp: handleMouseUp, onMouseLeave: handleMouseUp, onTouchStart: handleTouchStart, onTouchMove: handleTouchMove, onTouchEnd: handleTouchEnd, style: { touchAction: "none" } }) }), jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [jsxRuntime.jsx("label", { htmlFor: "zoom-slider", className: "text-sm font-medium", children: translations.cropZoomLabel }), jsxRuntime.jsxs("div", { className: "flex items-center gap-2 sm:gap-3 w-full", children: [jsxRuntime.jsx("span", { className: "text-xs text-gray-500 min-w-[15px] sm:min-w-[20px]", children: "-" }), jsxRuntime.jsx(react.Slider, { id: "zoom-slider", size: "sm", step: 0.1, minValue: -1, maxValue: 1, value: zoomValue, onChange: function (value) { return setZoomValue(value); }, className: "flex-1", marks: [
661
+ { value: -1, label: "Out" },
662
+ { value: 0, label: "0" },
663
+ { value: 1, label: "In" },
664
+ ] }), jsxRuntime.jsx("span", { className: "text-xs text-gray-500 min-w-[15px] sm:min-w-[20px]", children: "+" })] })] }), jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [jsxRuntime.jsx("div", { className: "text-sm font-medium", children: translations.cropBackgroundLabel }), jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-3 sm:gap-4", children: [jsxRuntime.jsxs("label", { className: "flex items-center gap-2 cursor-pointer", children: [jsxRuntime.jsx("input", { type: "radio", name: "background-color", value: "white", checked: backgroundColor === "white", onChange: function (e) {
665
+ return setBackgroundColor(e.target.value);
666
+ }, className: "text-blue-600 focus:ring-blue-500" }), jsxRuntime.jsx("div", { className: "w-4 h-4 bg-white border border-gray-300 rounded" }), jsxRuntime.jsx("span", { className: "text-sm", children: translations.cropBackgroundWhite })] }), jsxRuntime.jsxs("label", { className: "flex items-center gap-2 cursor-pointer", children: [jsxRuntime.jsx("input", { type: "radio", name: "background-color", value: "black", checked: backgroundColor === "black", onChange: function (e) {
667
+ return setBackgroundColor(e.target.value);
668
+ }, className: "text-blue-600 focus:ring-blue-500" }), jsxRuntime.jsx("div", { className: "w-4 h-4 bg-black border border-gray-300 rounded" }), jsxRuntime.jsx("span", { className: "text-sm", children: translations.cropBackgroundBlack })] }), jsxRuntime.jsxs("label", { className: "flex items-center gap-2 cursor-pointer", children: [jsxRuntime.jsx("input", { type: "radio", name: "background-color", value: "transparent", checked: backgroundColor === "transparent", onChange: function (e) {
669
+ return setBackgroundColor(e.target.value);
670
+ }, className: "text-blue-600 focus:ring-blue-500" }), jsxRuntime.jsx("div", { className: "w-4 h-4 bg-gray-200 border border-gray-300 rounded", style: {
671
+ backgroundImage: "linear-gradient(45deg, #ccc 25%, transparent 25%), linear-gradient(-45deg, #ccc 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #ccc 75%), linear-gradient(-45deg, transparent 75%, #ccc 75%)",
672
+ backgroundSize: "8px 8px",
673
+ backgroundPosition: "0 0, 0 4px, 4px -4px, -4px 0px",
674
+ } }), jsxRuntime.jsx("span", { className: "text-sm", children: translations.cropBackgroundTransparent })] })] })] })] }) }), jsxRuntime.jsxs(react.ModalFooter, { className: "flex flex-col-reverse sm:flex-row gap-2 px-4 sm:px-6", children: [jsxRuntime.jsx(react.Button, { color: "danger", variant: "light", onPress: onCancel, className: "w-full sm:w-auto", children: translations.cropCancelButton }), jsxRuntime.jsx(react.Button, { color: "primary", onPress: handleSave, className: "w-full sm:w-auto", children: translations.cropSaveButton })] })] }) }));
675
+ };
676
+ /**
677
+ * Componente de previsualización de archivo
678
+ */
679
+ var FilePreview = function (_a) {
680
+ var file = _a.file, onRemove = _a.onRemove, removeAriaLabel = _a.removeAriaLabel;
681
+ var _b = React.useState(""), previewUrl = _b[0], setPreviewUrl = _b[1];
682
+ React.useEffect(function () {
683
+ if (file === null || file === void 0 ? void 0 : file.type.startsWith("image/")) {
684
+ var url_1 = URL.createObjectURL(file);
685
+ setPreviewUrl(url_1);
686
+ return function () { return URL.revokeObjectURL(url_1); };
687
+ }
688
+ }, [file]);
689
+ if (!(file === null || file === void 0 ? void 0 : file.type.startsWith("image/"))) {
690
+ return null;
691
+ }
692
+ return (jsxRuntime.jsxs("div", { className: "absolute inset-0 overflow-hidden z-10", children: [jsxRuntime.jsx("img", { src: previewUrl, alt: file.name, className: "w-full h-full object-cover" }), jsxRuntime.jsx(react.Button, { isIconOnly: true, size: "sm", color: "danger", variant: "solid", className: "absolute top-2 right-2 sm:top-3 sm:right-3 z-20 bg-red-100 hover:bg-red-200 text-red-600 min-w-8 h-8 sm:min-w-10 sm:h-10", onPress: onRemove, "aria-label": removeAriaLabel, children: jsxRuntime.jsx(IconComponent, { icon: "heroicons:trash", size: "sm" }) })] }));
693
+ };
694
+ /**
695
+ * Componente UploadDocument basado en Hero UI con funcionalidad mejorada
696
+ *
697
+ * @example
698
+ * ```tsx
699
+ * <UploadDocument
700
+ * onUpload={(files) => console.log(files)}
701
+ * acceptedFiles="image/*,.pdf"
702
+ * multiple={false}
703
+ * translations={{
704
+ * uploadText: "Upload your files",
705
+ * dragText: "Drop files here"
706
+ * }}
707
+ * />
708
+ * ```
709
+ */
710
+ var UploadFile = function (_a) {
711
+ var text = _a.text, textColor = _a.textColor, subText = _a.subText,
712
+ // borderColor, // TODO: Implementar colores personalizados
713
+ iconColor = _a.iconColor,
714
+ // backgroundColor, // TODO: Implementar colores personalizados
715
+ _b = _a.width,
716
+ // backgroundColor, // TODO: Implementar colores personalizados
717
+ width = _b === void 0 ? "100%" : _b, _c = _a.height, height = _c === void 0 ? "auto" : _c, _d = _a.multiple, multiple = _d === void 0 ? false : _d, _e = _a.maxFiles, maxFiles = _e === void 0 ? 1 : _e, maxFileSize = _a.maxFileSize, _f = _a.acceptedFiles, acceptedFiles = _f === void 0 ? ".pdf" : _f, _g = _a.icon, icon = _g === void 0 ? "heroicons:arrow-up-on-square" : _g, onUpload = _a.onUpload, onError = _a.onError, _h = _a.error, error = _h === void 0 ? false : _h, _j = _a.success, success = _j === void 0 ? false : _j, _k = _a.disabled, disabled = _k === void 0 ? false : _k, errorText = _a.errorText, cropConfig = _a.cropConfig, image = _a.image, _l = _a.className, className = _l === void 0 ? "" : _l, _m = _a.translations, translations = _m === void 0 ? {} : _m;
718
+ var inputRef = React.useRef(null);
719
+ var uploadImageRef = React.useRef(image || null);
720
+ var _o = React.useState(false), isDragging = _o[0], setIsDragging = _o[1];
721
+ // Estados para el modal de crop
722
+ var _p = React.useState(false), showCropModal = _p[0], setShowCropModal = _p[1];
723
+ var _q = React.useState(""), selectedImageUrl = _q[0], setSelectedImageUrl = _q[1];
724
+ var _r = React.useState(null); _r[0]; var setOriginalFile = _r[1];
725
+ // Combinar traducciones por defecto con las proporcionadas
726
+ var t = React.useMemo(function () { return (__assign(__assign({}, defaultTranslations), translations)); }, [translations]);
727
+ // Actualizar texto si se pasa como prop (retrocompatibilidad)
728
+ var finalText = text || t.uploadText;
729
+ var finalSubText = subText || t.subText;
730
+ var handleClick = function () {
731
+ var _a;
732
+ if (!(disabled || uploadImageRef.current)) {
733
+ (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.click();
734
+ }
735
+ };
736
+ var validateFiles = React.useCallback(function (files) {
737
+ var filesArray = Array.from(files);
738
+ if (!multiple && filesArray.length > 1) {
739
+ onError === null || onError === void 0 ? void 0 : onError(t.multipleFilesError);
740
+ return [];
741
+ }
742
+ if (filesArray.length > maxFiles) {
743
+ onError === null || onError === void 0 ? void 0 : onError(t.maxFilesError.replace("{maxFiles}", maxFiles.toString()));
744
+ return [];
745
+ }
746
+ // Validar tamaño de archivo
747
+ if (maxFileSize) {
748
+ var oversizedFiles = filesArray.filter(function (file) { return file.size > maxFileSize; });
749
+ if (oversizedFiles.length > 0) {
750
+ var maxSizeFormatted = formatFileSize(maxFileSize);
751
+ onError === null || onError === void 0 ? void 0 : onError(t.maxFileSizeError.replace("{maxSize}", maxSizeFormatted));
752
+ return [];
753
+ }
754
+ }
755
+ // Validar tipos de archivo
756
+ var acceptedExtensions = Array.isArray(acceptedFiles)
757
+ ? acceptedFiles.map(function (ext) { return ext.trim().toLowerCase(); })
758
+ : acceptedFiles.split(",").map(function (ext) { return ext.trim().toLowerCase(); });
759
+ var validFiles = filesArray.filter(function (file) {
760
+ var _a;
761
+ if (acceptedFiles.includes("image/*") &&
762
+ file.type.startsWith("image/")) {
763
+ return true;
764
+ }
765
+ if (acceptedExtensions.some(function (ext) { return ext.includes("/"); })) {
766
+ return acceptedExtensions.some(function (type) { return file.type === type; });
767
+ }
768
+ var fileExtension = ".".concat((_a = file.name.split(".").pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase());
769
+ return acceptedExtensions.some(function (ext) { return ext === fileExtension || ext === "*"; });
770
+ });
771
+ if (validFiles.length === 0) {
772
+ var formattedTypes = acceptedExtensions
773
+ .map(formatFileType)
774
+ .join(", ");
775
+ onError === null || onError === void 0 ? void 0 : onError(t.invalidFileTypeError.replace("{acceptedTypes}", formattedTypes));
776
+ return [];
777
+ }
778
+ return validFiles;
779
+ }, [multiple, maxFiles, maxFileSize, acceptedFiles, t, onError]);
780
+ var handleFileUpload = React.useCallback(function (files) {
781
+ var validFiles = validateFiles(files);
782
+ if (validFiles.length > 0) {
783
+ var file_1 = validFiles[0];
784
+ // Si es imagen y hay configuración de crop, abrir modal
785
+ if (file_1.type.startsWith("image/") && cropConfig) {
786
+ var reader_1 = new FileReader();
787
+ reader_1.onload = function () {
788
+ setSelectedImageUrl(reader_1.result);
789
+ setOriginalFile(file_1);
790
+ setShowCropModal(true);
791
+ };
792
+ reader_1.readAsDataURL(file_1);
793
+ }
794
+ else {
795
+ // Procesar directamente si no necesita crop
796
+ uploadImageRef.current = file_1;
797
+ onUpload(validFiles);
798
+ }
799
+ }
800
+ }, [onUpload, validateFiles, cropConfig]);
801
+ // Manejar guardado de imagen recortada
802
+ var handleCropSave = React.useCallback(function (croppedFile) {
803
+ uploadImageRef.current = croppedFile;
804
+ setShowCropModal(false);
805
+ setSelectedImageUrl("");
806
+ setOriginalFile(null);
807
+ onUpload([croppedFile]);
808
+ }, [onUpload]);
809
+ // Manejar cancelación de crop
810
+ var handleCropCancel = React.useCallback(function () {
811
+ setShowCropModal(false);
812
+ setSelectedImageUrl("");
813
+ setOriginalFile(null);
814
+ }, []);
815
+ var handleChange = function (event) {
816
+ var files = event.target.files;
817
+ if (files && files.length > 0) {
818
+ handleFileUpload(files);
819
+ }
820
+ // Limpiar el input
821
+ if (inputRef.current) {
822
+ inputRef.current.value = "";
823
+ }
824
+ };
825
+ var handleDragEnter = React.useCallback(function (e) {
826
+ e.preventDefault();
827
+ e.stopPropagation();
828
+ if (!disabled) {
829
+ setIsDragging(true);
830
+ }
831
+ }, [disabled]);
832
+ var handleDragLeave = React.useCallback(function (e) {
833
+ e.preventDefault();
834
+ e.stopPropagation();
835
+ setIsDragging(false);
836
+ }, []);
837
+ var handleDragOver = React.useCallback(function (e) {
838
+ e.preventDefault();
839
+ e.stopPropagation();
840
+ }, []);
841
+ var handleDrop = React.useCallback(function (e) {
842
+ e.preventDefault();
843
+ e.stopPropagation();
844
+ setIsDragging(false);
845
+ if (disabled) {
846
+ return;
847
+ }
848
+ var droppedFiles = e.dataTransfer.files;
849
+ if (droppedFiles.length > 0) {
850
+ handleFileUpload(droppedFiles);
851
+ }
852
+ }, [disabled, handleFileUpload]);
853
+ var handleRemoveImage = function () {
854
+ uploadImageRef.current = null;
855
+ onUpload([]);
856
+ };
857
+ // Clases CSS dinámicas usando Tailwind
858
+ var containerClasses = "\n\t\trelative w-full border-2 border-dashed rounded-2xl p-2 sm:p-4 lg:p-4 transition-all duration-300\n\t\t".concat(width !== "100%" ? "w-[".concat(width, "]") : "w-full", "\n\t\t").concat(height !== "auto" ? "h-[".concat(height, "]") : "min-h-[150px] sm:min-h-[180px] lg:min-h-[200px]", "\n\t\t").concat(isDragging
859
+ ? "border-blue-500 bg-blue-50 dark:bg-blue-950"
860
+ : error
861
+ ? "border-red-500 bg-red-50 dark:bg-red-950"
862
+ : success
863
+ ? "border-green-500 bg-green-50 dark:bg-green-950"
864
+ : disabled
865
+ ? "border-gray-300 bg-gray-100 dark:bg-gray-800"
866
+ : "border-gray-300 hover:border-gray-400 bg-gray-50 dark:bg-gray-900", "\n\t\t").concat(!(disabled || uploadImageRef.current) ? "cursor-pointer" : "cursor-default", "\n\t\t").concat(className, "\n\t")
867
+ .trim()
868
+ .replace(/\s+/g, " ");
869
+ var textClasses = "\n\t\ttext-center mb-1 sm:mb-2 text-sm sm:text-base lg:text-lg font-medium\n\t\t".concat(disabled ? "text-gray-400" : textColor || "text-gray-700 dark:text-gray-200", "\n\t");
870
+ var subTextClasses = "\n\t\ttext-xs sm:text-sm text-center\n\t\t".concat(disabled ? "text-gray-400" : "text-gray-500 dark:text-gray-400", "\n\t");
871
+ return (jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsx(react.Card, { className: containerClasses, isPressable: !(disabled || uploadImageRef.current), onPress: handleClick, children: jsxRuntime.jsxs(react.CardBody, { className: "flex flex-col items-center justify-center gap-3 sm:gap-4 relative", onDragEnter: handleDragEnter, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, children: [uploadImageRef.current && (jsxRuntime.jsx(FilePreview, { file: uploadImageRef.current, onRemove: handleRemoveImage, removeAriaLabel: t.removeFileAriaLabel })), jsxRuntime.jsx("div", { className: uploadImageRef.current
872
+ ? "opacity-0"
873
+ : "opacity-100 transition-opacity", children: jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-3 sm:gap-4", children: [jsxRuntime.jsx(IconComponent, { icon: icon, size: "xl", className: "sm:text-4xl lg:text-5xl ".concat(disabled ? "text-gray-400" : iconColor || "text-primary") }), jsxRuntime.jsxs("div", { className: "space-y-1 sm:space-y-2 text-center px-2", children: [jsxRuntime.jsx("p", { className: textClasses, children: isDragging ? t.dragText : finalText }), finalSubText && !isDragging && (jsxRuntime.jsx("p", { className: subTextClasses, children: finalSubText }))] })] }) }), jsxRuntime.jsx("input", { ref: inputRef, type: "file", className: "hidden", multiple: multiple, accept: Array.isArray(acceptedFiles)
874
+ ? acceptedFiles.join(",")
875
+ : acceptedFiles, onChange: handleChange, disabled: disabled, "aria-label": t.uploadAreaAriaLabel })] }) }), error && errorText && (jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-2", role: "alert", children: errorText })), showCropModal && selectedImageUrl && cropConfig && (jsxRuntime.jsx(ImageCropModal, { isOpen: showCropModal, imageUrl: selectedImageUrl, targetWidth: cropConfig.targetWidth, targetHeight: cropConfig.targetHeight, onSave: handleCropSave, onCancel: handleCropCancel, translations: t }))] }));
368
876
  };
877
+ UploadFile.displayName = "UploadFile";
369
878
 
370
879
  var themeColors = {
371
880
  "purple-light": {
@@ -571,6 +1080,7 @@ exports.MenuComponent = MenuComponent;
571
1080
  exports.Phone = Phone;
572
1081
  exports.ThemeContext = ThemeContext;
573
1082
  exports.ThemeProvider = ThemeProvider;
1083
+ exports.UploadFile = UploadFile;
574
1084
  exports.sizeMap = sizeMap;
575
1085
  exports.themeColors = themeColors;
576
1086
  exports.useThemeContext = useThemeContext;