@greatapps/common 1.1.462 → 1.1.463
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.
|
@@ -12,6 +12,22 @@ function useImageUpload(options) {
|
|
|
12
12
|
const [isProcessing, setIsProcessing] = useState(false);
|
|
13
13
|
const fileInputRef = useRef(null);
|
|
14
14
|
const currentFileName = useRef("");
|
|
15
|
+
const checkMinimumDimensions = useCallback(
|
|
16
|
+
(naturalWidth, naturalHeight) => {
|
|
17
|
+
const { width: minW, height: minH, aspectRatio } = config;
|
|
18
|
+
const imgAspect = naturalWidth / naturalHeight;
|
|
19
|
+
if (imgAspect >= aspectRatio) {
|
|
20
|
+
const cropHeight = naturalHeight;
|
|
21
|
+
const cropWidth = cropHeight * aspectRatio;
|
|
22
|
+
return cropWidth >= minW && cropHeight >= minH;
|
|
23
|
+
} else {
|
|
24
|
+
const cropWidth = naturalWidth;
|
|
25
|
+
const cropHeight = cropWidth / aspectRatio;
|
|
26
|
+
return cropWidth >= minW && cropHeight >= minH;
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
[config]
|
|
30
|
+
);
|
|
15
31
|
const handleFileSelect = useCallback(
|
|
16
32
|
(file) => {
|
|
17
33
|
const validation = validateImage(file);
|
|
@@ -22,38 +38,32 @@ function useImageUpload(options) {
|
|
|
22
38
|
currentFileName.current = file.name;
|
|
23
39
|
const reader = new FileReader();
|
|
24
40
|
reader.onload = (e) => {
|
|
25
|
-
|
|
26
|
-
|
|
41
|
+
const base64 = e.target?.result;
|
|
42
|
+
setOriginalImage(base64);
|
|
43
|
+
const img = new Image();
|
|
44
|
+
img.onload = () => {
|
|
45
|
+
const { naturalWidth, naturalHeight } = img;
|
|
46
|
+
if (checkMinimumDimensions(naturalWidth, naturalHeight)) {
|
|
47
|
+
setShowCropModal(true);
|
|
48
|
+
} else {
|
|
49
|
+
setTooSmallError({
|
|
50
|
+
fileName: currentFileName.current,
|
|
51
|
+
minWidth: config.width,
|
|
52
|
+
minHeight: config.height
|
|
53
|
+
});
|
|
54
|
+
setShowTooSmallModal(true);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
img.src = base64;
|
|
27
58
|
};
|
|
28
59
|
reader.readAsDataURL(file);
|
|
29
60
|
},
|
|
30
|
-
[onError]
|
|
61
|
+
[onError, config, checkMinimumDimensions]
|
|
31
62
|
);
|
|
32
63
|
const handleImageLoadInModal = useCallback(
|
|
33
|
-
(
|
|
34
|
-
const { width: minW, height: minH, aspectRatio } = config;
|
|
35
|
-
const imgAspect = naturalWidth / naturalHeight;
|
|
36
|
-
let canFitMinCrop = false;
|
|
37
|
-
if (imgAspect >= aspectRatio) {
|
|
38
|
-
const cropHeight = naturalHeight;
|
|
39
|
-
const cropWidth = cropHeight * aspectRatio;
|
|
40
|
-
canFitMinCrop = cropWidth >= minW && cropHeight >= minH;
|
|
41
|
-
} else {
|
|
42
|
-
const cropWidth = naturalWidth;
|
|
43
|
-
const cropHeight = cropWidth / aspectRatio;
|
|
44
|
-
canFitMinCrop = cropWidth >= minW && cropHeight >= minH;
|
|
45
|
-
}
|
|
46
|
-
if (!canFitMinCrop) {
|
|
47
|
-
setShowCropModal(false);
|
|
48
|
-
setTooSmallError({
|
|
49
|
-
fileName: currentFileName.current,
|
|
50
|
-
minWidth: minW,
|
|
51
|
-
minHeight: minH
|
|
52
|
-
});
|
|
53
|
-
setShowTooSmallModal(true);
|
|
54
|
-
}
|
|
64
|
+
(_naturalWidth, _naturalHeight) => {
|
|
55
65
|
},
|
|
56
|
-
[
|
|
66
|
+
[]
|
|
57
67
|
);
|
|
58
68
|
const handleSelectAnotherFile = useCallback(() => {
|
|
59
69
|
setShowTooSmallModal(false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/images/hooks/use-image-upload.hook.ts"],"sourcesContent":["'use client'\n\nimport { useState, useCallback, useRef } from 'react'\nimport type { Crop } from 'react-image-crop'\nimport { cropImageToCanvas } from '../utils/crop-image'\nimport { validateImage } from '../utils/validate-image'\nimport type { ImageConfig } from '../types/image.type'\n\nexport interface ImageTooSmallError {\n fileName: string\n minWidth: number\n minHeight: number\n}\n\nexport interface UseImageUploadOptions {\n config: ImageConfig\n onSuccess?: (base64Image: string) => void\n onError?: (error: string) => void\n}\n\nexport function useImageUpload(options: UseImageUploadOptions) {\n const { config, onSuccess, onError } = options\n\n const [originalImage, setOriginalImage] = useState<string | null>(null)\n const [croppedPreview, setCroppedPreview] = useState<string | null>(null)\n const [showCropModal, setShowCropModal] = useState(false)\n const [showTooSmallModal, setShowTooSmallModal] = useState(false)\n const [tooSmallError, setTooSmallError] = useState<ImageTooSmallError | null>(null)\n const [isProcessing, setIsProcessing] = useState(false)\n const fileInputRef = useRef<HTMLInputElement>(null)\n const currentFileName = useRef<string>('')\n\n //
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/images/hooks/use-image-upload.hook.ts"],"sourcesContent":["'use client'\n\nimport { useState, useCallback, useRef } from 'react'\nimport type { Crop } from 'react-image-crop'\nimport { cropImageToCanvas } from '../utils/crop-image'\nimport { validateImage } from '../utils/validate-image'\nimport type { ImageConfig } from '../types/image.type'\n\nexport interface ImageTooSmallError {\n fileName: string\n minWidth: number\n minHeight: number\n}\n\nexport interface UseImageUploadOptions {\n config: ImageConfig\n onSuccess?: (base64Image: string) => void\n onError?: (error: string) => void\n}\n\nexport function useImageUpload(options: UseImageUploadOptions) {\n const { config, onSuccess, onError } = options\n\n const [originalImage, setOriginalImage] = useState<string | null>(null)\n const [croppedPreview, setCroppedPreview] = useState<string | null>(null)\n const [showCropModal, setShowCropModal] = useState(false)\n const [showTooSmallModal, setShowTooSmallModal] = useState(false)\n const [tooSmallError, setTooSmallError] = useState<ImageTooSmallError | null>(null)\n const [isProcessing, setIsProcessing] = useState(false)\n const fileInputRef = useRef<HTMLInputElement>(null)\n const currentFileName = useRef<string>('')\n\n // Verifica se a imagem cabe o crop mínimo\n const checkMinimumDimensions = useCallback(\n (naturalWidth: number, naturalHeight: number): boolean => {\n const { width: minW, height: minH, aspectRatio } = config\n const imgAspect = naturalWidth / naturalHeight\n\n if (imgAspect >= aspectRatio) {\n const cropHeight = naturalHeight\n const cropWidth = cropHeight * aspectRatio\n return cropWidth >= minW && cropHeight >= minH\n } else {\n const cropWidth = naturalWidth\n const cropHeight = cropWidth / aspectRatio\n return cropWidth >= minW && cropHeight >= minH\n }\n },\n [config]\n )\n\n // Valida dimensões ANTES de abrir qualquer modal\n const handleFileSelect = useCallback(\n (file: File) => {\n const validation = validateImage(file)\n if (!validation.valid) {\n onError?.(validation.error || 'Arquivo inválido')\n return\n }\n\n currentFileName.current = file.name\n\n // Converte para base64\n const reader = new FileReader()\n reader.onload = (e) => {\n const base64 = e.target?.result as string\n setOriginalImage(base64)\n\n // Carrega imagem em memória para verificar dimensões ANTES de abrir modal\n const img = new Image()\n img.onload = () => {\n const { naturalWidth, naturalHeight } = img\n\n if (checkMinimumDimensions(naturalWidth, naturalHeight)) {\n // Dimensões OK - abre modal de crop\n setShowCropModal(true)\n } else {\n // Imagem muito pequena - abre modal de erro diretamente\n setTooSmallError({\n fileName: currentFileName.current,\n minWidth: config.width,\n minHeight: config.height,\n })\n setShowTooSmallModal(true)\n }\n }\n img.src = base64\n }\n reader.readAsDataURL(file)\n },\n [onError, config, checkMinimumDimensions]\n )\n\n // Mantido para compatibilidade, mas não mais necessário para validação\n const handleImageLoadInModal = useCallback(\n (_naturalWidth: number, _naturalHeight: number) => {\n // Validação agora é feita antes de abrir a modal\n // Este callback pode ser usado para outros propósitos se necessário\n },\n []\n )\n\n const handleSelectAnotherFile = useCallback(() => {\n setShowTooSmallModal(false)\n setTooSmallError(null)\n fileInputRef.current?.click()\n }, [])\n\n const handleCancelTooSmall = useCallback(() => {\n setShowTooSmallModal(false)\n setShowCropModal(false)\n setTooSmallError(null)\n setOriginalImage(null)\n }, [])\n\n const handleCropConfirm = useCallback(\n async (imageElement: HTMLImageElement, cropArea: Crop) => {\n setIsProcessing(true)\n try {\n // Crop é feito inteiramente no cliente via Canvas\n // Já produz imagem no tamanho final (config.width x config.height)\n const croppedBase64 = await cropImageToCanvas(\n imageElement,\n cropArea,\n config.width,\n config.height\n )\n\n setCroppedPreview(croppedBase64)\n setShowCropModal(false)\n onSuccess?.(croppedBase64)\n } catch {\n onError?.('Erro ao aplicar crop')\n } finally {\n setIsProcessing(false)\n }\n },\n [config, onSuccess, onError]\n )\n\n const clear = useCallback(() => {\n setOriginalImage(null)\n setCroppedPreview(null)\n }, [])\n\n return {\n originalImage,\n croppedPreview,\n showCropModal,\n showTooSmallModal,\n tooSmallError,\n config,\n isProcessing,\n fileInputRef,\n handleFileSelect,\n handleCropConfirm,\n handleImageLoadInModal,\n handleSelectAnotherFile,\n handleCancelTooSmall,\n setShowCropModal,\n setShowTooSmallModal,\n clear,\n }\n}\n"],"mappings":";AAEA,SAAS,UAAU,aAAa,cAAc;AAE9C,SAAS,yBAAyB;AAClC,SAAS,qBAAqB;AAevB,SAAS,eAAe,SAAgC;AAC7D,QAAM,EAAE,QAAQ,WAAW,QAAQ,IAAI;AAEvC,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAwB,IAAI;AACtE,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAwB,IAAI;AACxE,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,KAAK;AACxD,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,SAAS,KAAK;AAChE,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAoC,IAAI;AAClF,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,KAAK;AACtD,QAAM,eAAe,OAAyB,IAAI;AAClD,QAAM,kBAAkB,OAAe,EAAE;AAGzC,QAAM,yBAAyB;AAAA,IAC7B,CAAC,cAAsB,kBAAmC;AACxD,YAAM,EAAE,OAAO,MAAM,QAAQ,MAAM,YAAY,IAAI;AACnD,YAAM,YAAY,eAAe;AAEjC,UAAI,aAAa,aAAa;AAC5B,cAAM,aAAa;AACnB,cAAM,YAAY,aAAa;AAC/B,eAAO,aAAa,QAAQ,cAAc;AAAA,MAC5C,OAAO;AACL,cAAM,YAAY;AAClB,cAAM,aAAa,YAAY;AAC/B,eAAO,aAAa,QAAQ,cAAc;AAAA,MAC5C;AAAA,IACF;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAGA,QAAM,mBAAmB;AAAA,IACvB,CAAC,SAAe;AACd,YAAM,aAAa,cAAc,IAAI;AACrC,UAAI,CAAC,WAAW,OAAO;AACrB,kBAAU,WAAW,SAAS,qBAAkB;AAChD;AAAA,MACF;AAEA,sBAAgB,UAAU,KAAK;AAG/B,YAAM,SAAS,IAAI,WAAW;AAC9B,aAAO,SAAS,CAAC,MAAM;AACrB,cAAM,SAAS,EAAE,QAAQ;AACzB,yBAAiB,MAAM;AAGvB,cAAM,MAAM,IAAI,MAAM;AACtB,YAAI,SAAS,MAAM;AACjB,gBAAM,EAAE,cAAc,cAAc,IAAI;AAExC,cAAI,uBAAuB,cAAc,aAAa,GAAG;AAEvD,6BAAiB,IAAI;AAAA,UACvB,OAAO;AAEL,6BAAiB;AAAA,cACf,UAAU,gBAAgB;AAAA,cAC1B,UAAU,OAAO;AAAA,cACjB,WAAW,OAAO;AAAA,YACpB,CAAC;AACD,iCAAqB,IAAI;AAAA,UAC3B;AAAA,QACF;AACA,YAAI,MAAM;AAAA,MACZ;AACA,aAAO,cAAc,IAAI;AAAA,IAC3B;AAAA,IACA,CAAC,SAAS,QAAQ,sBAAsB;AAAA,EAC1C;AAGA,QAAM,yBAAyB;AAAA,IAC7B,CAAC,eAAuB,mBAA2B;AAAA,IAGnD;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,0BAA0B,YAAY,MAAM;AAChD,yBAAqB,KAAK;AAC1B,qBAAiB,IAAI;AACrB,iBAAa,SAAS,MAAM;AAAA,EAC9B,GAAG,CAAC,CAAC;AAEL,QAAM,uBAAuB,YAAY,MAAM;AAC7C,yBAAqB,KAAK;AAC1B,qBAAiB,KAAK;AACtB,qBAAiB,IAAI;AACrB,qBAAiB,IAAI;AAAA,EACvB,GAAG,CAAC,CAAC;AAEL,QAAM,oBAAoB;AAAA,IACxB,OAAO,cAAgC,aAAmB;AACxD,sBAAgB,IAAI;AACpB,UAAI;AAGF,cAAM,gBAAgB,MAAM;AAAA,UAC1B;AAAA,UACA;AAAA,UACA,OAAO;AAAA,UACP,OAAO;AAAA,QACT;AAEA,0BAAkB,aAAa;AAC/B,yBAAiB,KAAK;AACtB,oBAAY,aAAa;AAAA,MAC3B,QAAQ;AACN,kBAAU,sBAAsB;AAAA,MAClC,UAAE;AACA,wBAAgB,KAAK;AAAA,MACvB;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,WAAW,OAAO;AAAA,EAC7B;AAEA,QAAM,QAAQ,YAAY,MAAM;AAC9B,qBAAiB,IAAI;AACrB,sBAAkB,IAAI;AAAA,EACxB,GAAG,CAAC,CAAC;AAEL,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -30,7 +30,26 @@ export function useImageUpload(options: UseImageUploadOptions) {
|
|
|
30
30
|
const fileInputRef = useRef<HTMLInputElement>(null)
|
|
31
31
|
const currentFileName = useRef<string>('')
|
|
32
32
|
|
|
33
|
-
//
|
|
33
|
+
// Verifica se a imagem cabe o crop mínimo
|
|
34
|
+
const checkMinimumDimensions = useCallback(
|
|
35
|
+
(naturalWidth: number, naturalHeight: number): boolean => {
|
|
36
|
+
const { width: minW, height: minH, aspectRatio } = config
|
|
37
|
+
const imgAspect = naturalWidth / naturalHeight
|
|
38
|
+
|
|
39
|
+
if (imgAspect >= aspectRatio) {
|
|
40
|
+
const cropHeight = naturalHeight
|
|
41
|
+
const cropWidth = cropHeight * aspectRatio
|
|
42
|
+
return cropWidth >= minW && cropHeight >= minH
|
|
43
|
+
} else {
|
|
44
|
+
const cropWidth = naturalWidth
|
|
45
|
+
const cropHeight = cropWidth / aspectRatio
|
|
46
|
+
return cropWidth >= minW && cropHeight >= minH
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
[config]
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
// Valida dimensões ANTES de abrir qualquer modal
|
|
34
53
|
const handleFileSelect = useCallback(
|
|
35
54
|
(file: File) => {
|
|
36
55
|
const validation = validateImage(file)
|
|
@@ -41,46 +60,44 @@ export function useImageUpload(options: UseImageUploadOptions) {
|
|
|
41
60
|
|
|
42
61
|
currentFileName.current = file.name
|
|
43
62
|
|
|
44
|
-
// Converte para base64
|
|
63
|
+
// Converte para base64
|
|
45
64
|
const reader = new FileReader()
|
|
46
65
|
reader.onload = (e) => {
|
|
47
|
-
|
|
48
|
-
|
|
66
|
+
const base64 = e.target?.result as string
|
|
67
|
+
setOriginalImage(base64)
|
|
68
|
+
|
|
69
|
+
// Carrega imagem em memória para verificar dimensões ANTES de abrir modal
|
|
70
|
+
const img = new Image()
|
|
71
|
+
img.onload = () => {
|
|
72
|
+
const { naturalWidth, naturalHeight } = img
|
|
73
|
+
|
|
74
|
+
if (checkMinimumDimensions(naturalWidth, naturalHeight)) {
|
|
75
|
+
// Dimensões OK - abre modal de crop
|
|
76
|
+
setShowCropModal(true)
|
|
77
|
+
} else {
|
|
78
|
+
// Imagem muito pequena - abre modal de erro diretamente
|
|
79
|
+
setTooSmallError({
|
|
80
|
+
fileName: currentFileName.current,
|
|
81
|
+
minWidth: config.width,
|
|
82
|
+
minHeight: config.height,
|
|
83
|
+
})
|
|
84
|
+
setShowTooSmallModal(true)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
img.src = base64
|
|
49
88
|
}
|
|
50
89
|
reader.readAsDataURL(file)
|
|
51
90
|
},
|
|
52
|
-
[onError]
|
|
91
|
+
[onError, config, checkMinimumDimensions]
|
|
53
92
|
)
|
|
54
93
|
|
|
55
|
-
//
|
|
94
|
+
// Mantido para compatibilidade, mas não mais necessário para validação
|
|
56
95
|
const handleImageLoadInModal = useCallback(
|
|
57
|
-
(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
let canFitMinCrop = false
|
|
62
|
-
if (imgAspect >= aspectRatio) {
|
|
63
|
-
const cropHeight = naturalHeight
|
|
64
|
-
const cropWidth = cropHeight * aspectRatio
|
|
65
|
-
canFitMinCrop = cropWidth >= minW && cropHeight >= minH
|
|
66
|
-
} else {
|
|
67
|
-
const cropWidth = naturalWidth
|
|
68
|
-
const cropHeight = cropWidth / aspectRatio
|
|
69
|
-
canFitMinCrop = cropWidth >= minW && cropHeight >= minH
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (!canFitMinCrop) {
|
|
73
|
-
// Fecha modal de crop e abre modal de erro
|
|
74
|
-
setShowCropModal(false)
|
|
75
|
-
setTooSmallError({
|
|
76
|
-
fileName: currentFileName.current,
|
|
77
|
-
minWidth: minW,
|
|
78
|
-
minHeight: minH,
|
|
79
|
-
})
|
|
80
|
-
setShowTooSmallModal(true)
|
|
81
|
-
}
|
|
96
|
+
(_naturalWidth: number, _naturalHeight: number) => {
|
|
97
|
+
// Validação agora é feita antes de abrir a modal
|
|
98
|
+
// Este callback pode ser usado para outros propósitos se necessário
|
|
82
99
|
},
|
|
83
|
-
[
|
|
100
|
+
[]
|
|
84
101
|
)
|
|
85
102
|
|
|
86
103
|
const handleSelectAnotherFile = useCallback(() => {
|