@archbase/components 4.0.18 → 4.0.20
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/archbase-components-4.0.20.tgz +0 -0
- package/dist/datagrid/ag-grid/ag-grid-locale-ptbr.d.ts +7 -0
- package/dist/datagrid/ag-grid/ag-grid-mantine-theme.d.ts +24 -0
- package/dist/datagrid/ag-grid/archbase-data-grid-ag-formatters.d.ts +88 -0
- package/dist/datagrid/ag-grid/archbase-data-grid-ag-types.d.ts +329 -0
- package/dist/datagrid/ag-grid/archbase-data-grid-ag-utils.d.ts +71 -0
- package/dist/datagrid/ag-grid/archbase-data-grid-ag.d.ts +14 -0
- package/dist/datagrid/ag-grid/index.d.ts +14 -0
- package/dist/datagrid/index.d.ts +9 -1
- package/dist/editors/ArchbaseImageEdit.d.ts +21 -1
- package/dist/image/editor/functions/crop-image.d.ts +32 -0
- package/dist/image/editor/index.d.ts +0 -30
- package/dist/image/editor/models/index.models.d.ts +16 -0
- package/dist/index.css +1 -1
- package/dist/index.js +13078 -12300
- package/package.json +7 -6
- package/src/datagrid/ag-grid/ag-grid-locale-ptbr.ts +432 -0
- package/src/datagrid/ag-grid/ag-grid-mantine-theme.ts +176 -0
- package/src/datagrid/ag-grid/archbase-data-grid-ag-formatters.tsx +470 -0
- package/src/datagrid/ag-grid/archbase-data-grid-ag-types.tsx +413 -0
- package/src/datagrid/ag-grid/archbase-data-grid-ag-utils.ts +394 -0
- package/src/datagrid/ag-grid/archbase-data-grid-ag.tsx +1241 -0
- package/src/datagrid/ag-grid/index.tsx +84 -0
- package/src/datagrid/index.tsx +59 -8
- package/src/datagrid/main/archbase-data-grid-pagination.tsx +1 -2
- package/src/datagrid/main/archbase-data-grid-toolbar.tsx +1 -2
- package/src/editors/ArchbaseAsyncMultiSelect.tsx +2 -1
- package/src/editors/ArchbaseAsyncSelect.tsx +2 -1
- package/src/editors/ArchbaseAvatarEdit.tsx +1 -0
- package/src/editors/ArchbaseCheckbox.tsx +1 -0
- package/src/editors/ArchbaseChip.tsx +1 -0
- package/src/editors/ArchbaseChipGroup.tsx +1 -0
- package/src/editors/ArchbaseColorGradientPicker.tsx +2 -1
- package/src/editors/ArchbaseDatePickerEdit.tsx +1 -0
- package/src/editors/ArchbaseDateTimePickerEdit.tsx +1 -0
- package/src/editors/ArchbaseDualListbox.tsx +1 -0
- package/src/editors/ArchbaseEdit.tsx +1 -0
- package/src/editors/ArchbaseImageEdit.tsx +58 -2
- package/src/editors/ArchbaseJsonEdit.tsx +1 -0
- package/src/editors/ArchbaseLookupEdit.tsx +2 -1
- package/src/editors/ArchbaseLookupNumber.tsx +2 -1
- package/src/editors/ArchbaseLookupSelect.tsx +2 -1
- package/src/editors/ArchbaseMarkdownEdit.tsx +1 -0
- package/src/editors/ArchbaseMaskEdit.tsx +1 -0
- package/src/editors/ArchbaseMentionInput.tsx +1 -0
- package/src/editors/ArchbaseMultiEmail.tsx +1 -0
- package/src/editors/ArchbaseMultiSelect.tsx +2 -1
- package/src/editors/ArchbaseNumberEdit.tsx +1 -0
- package/src/editors/ArchbaseNumberStepper.tsx +2 -1
- package/src/editors/ArchbasePasswordEdit.tsx +1 -0
- package/src/editors/ArchbaseRadioGroup.tsx +1 -0
- package/src/editors/ArchbaseRating.tsx +1 -0
- package/src/editors/ArchbaseRichTextEdit.tsx +1 -0
- package/src/editors/ArchbaseSelect.tsx +2 -1
- package/src/editors/ArchbaseSignaturePad.tsx +1 -0
- package/src/editors/ArchbaseSwitch.tsx +1 -0
- package/src/editors/ArchbaseTagInputEdit.tsx +1 -0
- package/src/editors/ArchbaseTextArea.tsx +1 -0
- package/src/editors/ArchbaseTimeEdit.tsx +1 -0
- package/src/image/editor/functions/crop-image.ts +172 -0
- package/src/image/editor/index.tsx +411 -413
- package/src/image/editor/models/index.models.ts +16 -0
- package/dist/archbase-components-4.0.18.tgz +0 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilidades de crop usadas pelo modal de edição do ArchbaseImagePickerEditor.
|
|
3
|
+
* Trabalham diretamente sobre data URIs, preservando o mime original (PNG/WebP
|
|
4
|
+
* mantêm alpha; JPEG continua JPEG). Não há "fallback silencioso" para JPEG.
|
|
5
|
+
*
|
|
6
|
+
* Algoritmo de crop com rotação adaptado do exemplo oficial do react-easy-crop:
|
|
7
|
+
* https://github.com/ValentinH/react-easy-crop/blob/master/example/utils.ts
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export interface PixelCrop {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Detecta o mime de saída do crop respeitando alpha:
|
|
19
|
+
* - se a origem é PNG ou WebP, ou se `preserveTransparency` está ligado,
|
|
20
|
+
* retornamos um mime com suporte a alpha (PNG ou WebP);
|
|
21
|
+
* - caso contrário, JPEG.
|
|
22
|
+
*/
|
|
23
|
+
function pickOutputMime(sourceDataUri: string, preserveTransparency: boolean): string {
|
|
24
|
+
const match = sourceDataUri.match(/^data:(image\/[a-zA-Z+\-.]+);/);
|
|
25
|
+
const sourceMime = match?.[1] ?? '';
|
|
26
|
+
if (preserveTransparency || sourceMime === 'image/png' || sourceMime === 'image/webp') {
|
|
27
|
+
return sourceMime === 'image/webp' ? 'image/webp' : 'image/png';
|
|
28
|
+
}
|
|
29
|
+
return 'image/jpeg';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function loadImage(src: string): Promise<HTMLImageElement> {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const img = new Image();
|
|
35
|
+
img.crossOrigin = 'anonymous';
|
|
36
|
+
img.onload = () => resolve(img);
|
|
37
|
+
img.onerror = (e) => reject(e);
|
|
38
|
+
img.src = src;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function toRadians(deg: number): number {
|
|
43
|
+
return (deg * Math.PI) / 180;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Bounding box que comporta a imagem após rotação. Necessário para o canvas
|
|
48
|
+
* intermediário não cortar a imagem rotacionada.
|
|
49
|
+
*/
|
|
50
|
+
function rotateSize(width: number, height: number, rotation: number): { width: number; height: number } {
|
|
51
|
+
const rotRad = toRadians(rotation);
|
|
52
|
+
return {
|
|
53
|
+
width: Math.abs(Math.cos(rotRad) * width) + Math.abs(Math.sin(rotRad) * height),
|
|
54
|
+
height: Math.abs(Math.sin(rotRad) * width) + Math.abs(Math.cos(rotRad) * height),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Aplica rotação (em graus) + crop sobre `imageSrc` e retorna o data URI
|
|
60
|
+
* resultante.
|
|
61
|
+
*
|
|
62
|
+
* Estratégia:
|
|
63
|
+
* 1. Desenha a imagem rotacionada centralizada num canvas auxiliar do
|
|
64
|
+
* tamanho do bounding box rotacionado.
|
|
65
|
+
* 2. Faz drawImage do recorte (pixelCrop) desse canvas auxiliar para o
|
|
66
|
+
* canvas final do tamanho do recorte.
|
|
67
|
+
*
|
|
68
|
+
* Output mime respeita `preserveTransparency` e a origem (ver pickOutputMime).
|
|
69
|
+
*/
|
|
70
|
+
export async function getCroppedImage(
|
|
71
|
+
imageSrc: string,
|
|
72
|
+
pixelCrop: PixelCrop,
|
|
73
|
+
rotation: number = 0,
|
|
74
|
+
preserveTransparency: boolean = false,
|
|
75
|
+
quality: number = 92,
|
|
76
|
+
): Promise<string> {
|
|
77
|
+
const image = await loadImage(imageSrc);
|
|
78
|
+
const mime = pickOutputMime(imageSrc, preserveTransparency);
|
|
79
|
+
|
|
80
|
+
// 1) Canvas intermediário: imagem rotacionada centralizada.
|
|
81
|
+
const { width: bBoxW, height: bBoxH } = rotateSize(image.width, image.height, rotation);
|
|
82
|
+
const work = document.createElement('canvas');
|
|
83
|
+
work.width = Math.round(bBoxW);
|
|
84
|
+
work.height = Math.round(bBoxH);
|
|
85
|
+
const workCtx = work.getContext('2d');
|
|
86
|
+
if (!workCtx) throw new Error('Canvas 2D context not available');
|
|
87
|
+
|
|
88
|
+
workCtx.translate(bBoxW / 2, bBoxH / 2);
|
|
89
|
+
workCtx.rotate(toRadians(rotation));
|
|
90
|
+
workCtx.translate(-image.width / 2, -image.height / 2);
|
|
91
|
+
workCtx.drawImage(image, 0, 0);
|
|
92
|
+
|
|
93
|
+
// 2) Canvas final do tamanho do recorte (em pixels reais).
|
|
94
|
+
const out = document.createElement('canvas');
|
|
95
|
+
out.width = Math.round(pixelCrop.width);
|
|
96
|
+
out.height = Math.round(pixelCrop.height);
|
|
97
|
+
const outCtx = out.getContext('2d');
|
|
98
|
+
if (!outCtx) throw new Error('Canvas 2D context not available');
|
|
99
|
+
|
|
100
|
+
outCtx.drawImage(
|
|
101
|
+
work,
|
|
102
|
+
Math.round(pixelCrop.x),
|
|
103
|
+
Math.round(pixelCrop.y),
|
|
104
|
+
Math.round(pixelCrop.width),
|
|
105
|
+
Math.round(pixelCrop.height),
|
|
106
|
+
0,
|
|
107
|
+
0,
|
|
108
|
+
Math.round(pixelCrop.width),
|
|
109
|
+
Math.round(pixelCrop.height),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// PNG ignora o parâmetro de qualidade; passamos mesmo assim para JPEG/WebP.
|
|
113
|
+
return out.toDataURL(mime, quality / 100);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Redimensiona um data URI para respeitar maxWidth/maxHeight, preservando
|
|
118
|
+
* proporção e mime apropriado. Usado fora do fluxo de crop, ao subir um arquivo.
|
|
119
|
+
*/
|
|
120
|
+
export async function resizeDataUri(
|
|
121
|
+
dataUri: string,
|
|
122
|
+
maxWidth?: number,
|
|
123
|
+
maxHeight?: number,
|
|
124
|
+
maxSizeKb?: number,
|
|
125
|
+
quality: number = 92,
|
|
126
|
+
preserveTransparency: boolean = false,
|
|
127
|
+
): Promise<string> {
|
|
128
|
+
if (!maxWidth && !maxHeight && !maxSizeKb) return dataUri;
|
|
129
|
+
|
|
130
|
+
const img = await loadImage(dataUri);
|
|
131
|
+
let targetWidth = img.width;
|
|
132
|
+
let targetHeight = img.height;
|
|
133
|
+
|
|
134
|
+
if (maxWidth && img.width > maxWidth) {
|
|
135
|
+
const ratio = maxWidth / img.width;
|
|
136
|
+
targetWidth = maxWidth;
|
|
137
|
+
targetHeight = Math.round(img.height * ratio);
|
|
138
|
+
}
|
|
139
|
+
if (maxHeight && targetHeight > maxHeight) {
|
|
140
|
+
const ratio = maxHeight / targetHeight;
|
|
141
|
+
targetHeight = maxHeight;
|
|
142
|
+
targetWidth = Math.round(targetWidth * ratio);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (targetWidth === img.width && targetHeight === img.height && !maxSizeKb) {
|
|
146
|
+
return dataUri;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const canvas = document.createElement('canvas');
|
|
150
|
+
canvas.width = targetWidth;
|
|
151
|
+
canvas.height = targetHeight;
|
|
152
|
+
const ctx = canvas.getContext('2d');
|
|
153
|
+
if (!ctx) return dataUri;
|
|
154
|
+
ctx.drawImage(img, 0, 0, targetWidth, targetHeight);
|
|
155
|
+
|
|
156
|
+
const mime = pickOutputMime(dataUri, preserveTransparency);
|
|
157
|
+
|
|
158
|
+
const compressToSize = (currentQuality: number): string => {
|
|
159
|
+
const result = canvas.toDataURL(mime, currentQuality / 100);
|
|
160
|
+
if (maxSizeKb) {
|
|
161
|
+
const base64 = result.split(',')[1] ?? '';
|
|
162
|
+
const sizeKb = Math.ceil(((3 / 4) * base64.length) / 1024);
|
|
163
|
+
if (sizeKb > maxSizeKb && currentQuality > 20 && mime !== 'image/png') {
|
|
164
|
+
// PNG é lossless; reduzir quality não diminui o tamanho.
|
|
165
|
+
return compressToSize(currentQuality - 10);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return result;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
return compressToSize(quality);
|
|
172
|
+
}
|