@archbase/components 3.0.25 → 4.0.1
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/buttons/ArchbaseSpeedDial.d.ts +59 -0
- package/dist/buttons/index.d.ts +2 -0
- package/dist/charts/ArchbaseArcGauge.d.ts +55 -0
- package/dist/charts/ArchbaseGantt.d.ts +70 -0
- package/dist/charts/ArchbaseLinearGauge.d.ts +55 -0
- package/dist/charts/ArchbaseResourceTimeline.d.ts +61 -0
- package/dist/charts/ArchbaseSparkline.d.ts +45 -0
- package/dist/charts/index.d.ts +10 -0
- package/dist/containers/ArchbaseExpansionPanel.d.ts +60 -0
- package/dist/containers/ArchbaseFloatingPanel.d.ts +91 -0
- package/dist/containers/index.d.ts +4 -0
- package/dist/datagrid/export/ArchbaseDataGridExcelExport.d.ts +67 -0
- package/dist/datagrid/export/index.d.ts +2 -0
- package/dist/datagrid/index.d.ts +1 -0
- package/dist/display/ArchbaseAnimationWrapper.d.ts +73 -0
- package/dist/display/ArchbaseBarcodeGenerator.d.ts +50 -0
- package/dist/display/ArchbaseCarousel.d.ts +100 -0
- package/dist/display/ArchbaseCodeViewer.d.ts +63 -0
- package/dist/display/ArchbaseDiffViewer.d.ts +41 -0
- package/dist/display/ArchbaseJsonTree.d.ts +34 -0
- package/dist/display/ArchbaseLightbox.d.ts +89 -0
- package/dist/display/ArchbaseMarquee.d.ts +46 -0
- package/dist/display/ArchbasePhotoAlbum.d.ts +58 -0
- package/dist/display/ArchbaseQRCode.d.ts +44 -0
- package/dist/display/ArchbaseTooltipRich.d.ts +82 -0
- package/dist/display/ArchbaseVirtualList.d.ts +116 -0
- package/dist/display/index.d.ts +24 -0
- package/dist/editors/ArchbaseAIPromptInput.d.ts +69 -0
- package/dist/editors/ArchbaseAsyncMultiSelect.d.ts +1 -1
- package/dist/editors/ArchbaseAsyncSelect.d.ts +1 -1
- package/dist/editors/ArchbaseBarcodeScanner.d.ts +63 -0
- package/dist/editors/ArchbaseCepInput.d.ts +26 -0
- package/dist/editors/ArchbaseColorGradientPicker.d.ts +80 -0
- package/dist/editors/ArchbaseCpfCnpjInput.d.ts +21 -0
- package/dist/editors/ArchbaseCurrencyInput.d.ts +25 -0
- package/dist/editors/ArchbaseDualListbox.d.ts +61 -0
- package/dist/editors/ArchbaseInPlaceEditor.d.ts +67 -0
- package/dist/editors/ArchbaseLightGrid.d.ts +56 -0
- package/dist/editors/ArchbaseMentionInput.d.ts +75 -0
- package/dist/editors/ArchbaseMultiEmail.d.ts +53 -0
- package/dist/editors/ArchbaseMultiViewCalendar.d.ts +47 -0
- package/dist/editors/ArchbaseNumberStepper.d.ts +66 -0
- package/dist/editors/ArchbaseOTPInput.d.ts +27 -0
- package/dist/editors/ArchbasePhoneInput.d.ts +52 -0
- package/dist/editors/ArchbaseRangeSlider.d.ts +22 -0
- package/dist/editors/ArchbaseSignaturePad.d.ts +60 -0
- package/dist/editors/ArchbaseSpeechToTextInput.d.ts +106 -0
- package/dist/editors/ArchbaseSpreadsheet.d.ts +56 -0
- package/dist/editors/ArchbaseTagInput.d.ts +55 -0
- package/dist/editors/index.d.ts +40 -0
- package/dist/feedback/ArchbaseChunkProgressBar.d.ts +35 -0
- package/dist/feedback/ArchbaseRipple.d.ts +43 -0
- package/dist/feedback/ArchbaseSkeleton.d.ts +115 -0
- package/dist/feedback/index.d.ts +6 -0
- package/dist/filters/ArchbaseColumnSelector.d.ts +38 -0
- package/dist/filters/index.d.ts +2 -0
- package/dist/forms/ArchbaseFieldArray.d.ts +80 -0
- package/dist/forms/ArchbaseFormWizard.d.ts +73 -0
- package/dist/forms/ArchbaseStepper.d.ts +51 -0
- package/dist/forms/index.d.ts +8 -0
- package/dist/forms/useArchbaseFormWizard.d.ts +74 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/useArchbaseConfirm.d.ts +59 -0
- package/dist/image/editor/index.d.ts +43 -4
- package/dist/image/index.d.ts +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +22510 -13820
- package/dist/list/ArchbaseTreeList.d.ts +76 -0
- package/dist/list/index.d.ts +2 -0
- package/dist/navigation/ArchbaseActionSheet.d.ts +70 -0
- package/dist/navigation/ArchbaseAppBar.d.ts +63 -0
- package/dist/navigation/ArchbaseBottomNavigation.d.ts +61 -0
- package/dist/navigation/ArchbaseContextMenu.d.ts +76 -0
- package/dist/navigation/ArchbaseKeyboardShortcuts.d.ts +85 -0
- package/dist/navigation/ArchbaseSpotlight.d.ts +69 -0
- package/dist/navigation/index.d.ts +12 -0
- package/dist/notification/ArchbaseNotificationCenter.d.ts +56 -0
- package/dist/notification/index.d.ts +2 -0
- package/dist/onboarding/ArchbaseOnboardingTour.d.ts +27 -76
- package/dist/viewers/ArchbaseDocViewer.d.ts +40 -0
- package/dist/viewers/ArchbasePdfBuilder.d.ts +53 -0
- package/dist/viewers/ArchbaseXmlViewer.d.ts +36 -0
- package/dist/viewers/index.d.ts +6 -0
- package/package.json +41 -18
- package/src/buttons/ArchbaseSpeedDial.tsx +282 -0
- package/src/buttons/index.ts +3 -0
- package/src/charts/ArchbaseArcGauge.tsx +325 -0
- package/src/charts/ArchbaseGantt.tsx +494 -0
- package/src/charts/ArchbaseLinearGauge.tsx +288 -0
- package/src/charts/ArchbaseResourceTimeline.tsx +433 -0
- package/src/charts/ArchbaseSparkline.tsx +193 -0
- package/src/charts/index.ts +20 -1
- package/src/containers/ArchbaseExpansionPanel.tsx +208 -0
- package/src/containers/ArchbaseFloatingPanel.tsx +448 -0
- package/src/containers/index.ts +17 -0
- package/src/datagrid/components/pagination/grid-pagination.tsx +2 -2
- package/src/datagrid/export/ArchbaseDataGridExcelExport.tsx +293 -0
- package/src/datagrid/export/index.ts +8 -0
- package/src/datagrid/index.tsx +2 -0
- package/src/display/ArchbaseAnimationWrapper.tsx +272 -0
- package/src/display/ArchbaseBarcodeGenerator.tsx +303 -0
- package/src/display/ArchbaseCarousel.tsx +679 -0
- package/src/display/ArchbaseCodeViewer.tsx +271 -0
- package/src/display/ArchbaseDiffViewer.tsx +108 -0
- package/src/display/ArchbaseJsonTree.tsx +150 -0
- package/src/display/ArchbaseLightbox.tsx +279 -0
- package/src/display/ArchbaseMarquee.tsx +268 -0
- package/src/display/ArchbasePhotoAlbum.tsx +415 -0
- package/src/display/ArchbaseQRCode.tsx +137 -0
- package/src/display/ArchbaseTooltipRich.tsx +317 -0
- package/src/display/ArchbaseVirtualList.tsx +618 -0
- package/src/display/index.ts +94 -1
- package/src/editors/ArchbaseAIPromptInput.tsx +432 -0
- package/src/editors/ArchbaseBarcodeScanner.tsx +570 -0
- package/src/editors/ArchbaseCepInput.tsx +173 -0
- package/src/editors/ArchbaseColorGradientPicker.tsx +460 -0
- package/src/editors/ArchbaseCpfCnpjInput.tsx +193 -0
- package/src/editors/ArchbaseCurrencyInput.tsx +100 -0
- package/src/editors/ArchbaseDualListbox.tsx +671 -0
- package/src/editors/ArchbaseInPlaceEditor.tsx +406 -0
- package/src/editors/ArchbaseLightGrid.tsx +538 -0
- package/src/editors/ArchbaseMentionInput.tsx +384 -0
- package/src/editors/ArchbaseMultiEmail.tsx +367 -0
- package/src/editors/ArchbaseMultiViewCalendar.tsx +439 -0
- package/src/editors/ArchbaseNumberStepper.tsx +219 -0
- package/src/editors/ArchbaseOTPInput.tsx +133 -0
- package/src/editors/ArchbaseOperationHoursEditor.tsx +1 -1
- package/src/editors/ArchbasePhoneInput.tsx +355 -0
- package/src/editors/ArchbaseRangeSlider.tsx +109 -0
- package/src/editors/ArchbaseSignaturePad.tsx +425 -0
- package/src/editors/ArchbaseSpeechToTextInput.tsx +444 -0
- package/src/editors/ArchbaseSpreadsheet.tsx +388 -0
- package/src/editors/ArchbaseTagInput.tsx +326 -0
- package/src/editors/index.tsx +92 -0
- package/src/feedback/ArchbaseChunkProgressBar.tsx +156 -0
- package/src/feedback/ArchbaseRipple.tsx +278 -0
- package/src/feedback/ArchbaseSkeleton.tsx +419 -0
- package/src/feedback/index.ts +22 -1
- package/src/filters/ArchbaseColumnSelector.tsx +268 -0
- package/src/filters/index.ts +2 -0
- package/src/forms/ArchbaseFieldArray.tsx +305 -0
- package/src/forms/ArchbaseFormWizard.tsx +284 -0
- package/src/forms/ArchbaseStepper.tsx +137 -0
- package/src/forms/index.ts +24 -2
- package/src/forms/useArchbaseFormWizard.ts +283 -0
- package/src/hooks/index.ts +12 -0
- package/src/hooks/useArchbaseConfirm.ts +293 -0
- package/src/image/editor/functions/image-processing.ts +5 -0
- package/src/image/editor/index.tsx +305 -391
- package/src/image/index.ts +1 -0
- package/src/index.ts +3 -0
- package/src/list/ArchbaseTreeList.tsx +422 -0
- package/src/list/index.ts +3 -0
- package/src/navigation/ArchbaseActionSheet.tsx +301 -0
- package/src/navigation/ArchbaseAppBar.tsx +233 -0
- package/src/navigation/ArchbaseBottomNavigation.tsx +243 -0
- package/src/navigation/ArchbaseContextMenu.tsx +298 -0
- package/src/navigation/ArchbaseKeyboardShortcuts.tsx +472 -0
- package/src/navigation/ArchbaseSpotlight.tsx +483 -0
- package/src/navigation/index.ts +48 -1
- package/src/notification/ArchbaseNotificationCenter.tsx +502 -0
- package/src/notification/index.ts +2 -0
- package/src/onboarding/ArchbaseOnboardingTour.tsx +229 -102
- package/src/viewers/ArchbaseDocViewer.tsx +584 -0
- package/src/viewers/ArchbasePdfBuilder.tsx +291 -0
- package/src/viewers/ArchbaseXmlViewer.tsx +142 -0
- package/src/viewers/index.ts +12 -0
|
@@ -1,437 +1,351 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} from '@
|
|
1
|
+
/**
|
|
2
|
+
* ArchbaseImagePickerEditor - Wrapper para react-image-picker-editor
|
|
3
|
+
* Componente para seleção, edição e compressão de imagens em png, jpeg e webp
|
|
4
|
+
*
|
|
5
|
+
* Funcionalidades:
|
|
6
|
+
* - Suporta diferentes tipos de entrada: URL, data URI (base64), blob URL
|
|
7
|
+
* - Debounce para evitar múltiplas chamadas do callback
|
|
8
|
+
* - Mantém compatibilidade total com a API anterior
|
|
9
|
+
*/
|
|
10
|
+
import { ActionIconVariant, Text, useMantineColorScheme } from '@mantine/core';
|
|
11
|
+
import { useDebouncedCallback } from '@mantine/hooks';
|
|
12
12
|
import { useArchbaseTranslation } from '@archbase/core';
|
|
13
|
-
import React, {
|
|
13
|
+
import React, { memo, useEffect, useMemo, useRef, useState, useCallback } from 'react';
|
|
14
14
|
import { useArchbaseTheme } from '@archbase/core';
|
|
15
|
-
import
|
|
16
|
-
import
|
|
15
|
+
import ReactImagePickerEditor, { ImagePickerConf } from 'react-image-picker-editor';
|
|
16
|
+
import 'react-image-picker-editor/dist/index.css';
|
|
17
17
|
import './image_editor_styles.scss';
|
|
18
|
-
import { ArchbaseImagePickerConf
|
|
18
|
+
import { ArchbaseImagePickerConf } from './models/index.models';
|
|
19
19
|
|
|
20
20
|
export * from './models/index.models';
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
// Mapeamento de idiomas para o react-image-picker-editor
|
|
23
|
+
const LANGUAGE_MAP: Record<string, string> = {
|
|
24
|
+
'pt-BR': 'pt',
|
|
25
|
+
'pt': 'pt',
|
|
26
|
+
'en': 'en',
|
|
27
|
+
'en-US': 'en',
|
|
28
|
+
'es': 'es',
|
|
29
|
+
'es-ES': 'es',
|
|
30
|
+
'fr': 'fr',
|
|
31
|
+
'fr-FR': 'fr',
|
|
32
|
+
'de': 'de',
|
|
33
|
+
'de-DE': 'de',
|
|
34
|
+
'fa': 'fa',
|
|
29
35
|
};
|
|
30
36
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Verifica se uma string é um data URI válido (com prefixo data:image/...)
|
|
39
|
+
*/
|
|
40
|
+
function isDataUri(str: string | null | undefined): boolean {
|
|
41
|
+
if (!str) return false;
|
|
42
|
+
return str.startsWith('data:');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Verifica se uma string é um blob URL
|
|
47
|
+
*/
|
|
48
|
+
function isBlobUrl(str: string | null | undefined): boolean {
|
|
49
|
+
if (!str) return false;
|
|
50
|
+
return str.startsWith('blob:');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Verifica se uma string é uma URL externa (http/https)
|
|
55
|
+
*/
|
|
56
|
+
function isExternalUrl(str: string | null | undefined): boolean {
|
|
57
|
+
if (!str) return false;
|
|
58
|
+
return str.startsWith('http://') || str.startsWith('https://');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Verifica se uma string parece ser base64 puro (sem prefixo data:)
|
|
63
|
+
* Base64 válido contém apenas A-Z, a-z, 0-9, +, /, = e tem comprimento múltiplo de 4
|
|
64
|
+
*/
|
|
65
|
+
function isRawBase64(str: string | null | undefined): boolean {
|
|
66
|
+
if (!str || str.length < 100) return false; // Imagens base64 são geralmente grandes
|
|
67
|
+
// Verificar se não começa com prefixos conhecidos
|
|
68
|
+
if (isDataUri(str) || isBlobUrl(str) || isExternalUrl(str)) return false;
|
|
69
|
+
// Verificar padrão de base64
|
|
70
|
+
const base64Regex = /^[A-Za-z0-9+/]+=*$/;
|
|
71
|
+
// Verificar apenas os primeiros 100 caracteres para performance
|
|
72
|
+
return base64Regex.test(str.substring(0, 100));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Normaliza a imagem de entrada para um formato que o componente pode exibir
|
|
77
|
+
* Converte base64 puro para data URI se necessário
|
|
78
|
+
*/
|
|
79
|
+
function normalizeImageSrc(src: string | null | undefined): string | null {
|
|
80
|
+
if (!src) return null;
|
|
81
|
+
|
|
82
|
+
// Se já é um formato válido, retornar como está
|
|
83
|
+
if (isDataUri(src) || isBlobUrl(src) || isExternalUrl(src)) {
|
|
84
|
+
return src;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Se parece ser base64 puro, converter para data URI
|
|
88
|
+
if (isRawBase64(src)) {
|
|
89
|
+
// Tentar detectar o tipo da imagem pelo header base64
|
|
90
|
+
// JPEG começa com /9j/
|
|
91
|
+
// PNG começa com iVBOR
|
|
92
|
+
// GIF começa com R0lGOD
|
|
93
|
+
// WebP começa com UklGR
|
|
94
|
+
let mimeType = 'image/jpeg'; // Padrão
|
|
95
|
+
if (src.startsWith('/9j/')) {
|
|
96
|
+
mimeType = 'image/jpeg';
|
|
97
|
+
} else if (src.startsWith('iVBOR')) {
|
|
98
|
+
mimeType = 'image/png';
|
|
99
|
+
} else if (src.startsWith('R0lGOD')) {
|
|
100
|
+
mimeType = 'image/gif';
|
|
101
|
+
} else if (src.startsWith('UklGR')) {
|
|
102
|
+
mimeType = 'image/webp';
|
|
103
|
+
}
|
|
104
|
+
return `data:${mimeType};base64,${src}`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Se não reconhecemos o formato, retornar como está (pode ser uma URL relativa)
|
|
108
|
+
return src;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Converte a configuração do Archbase para o formato do react-image-picker-editor
|
|
113
|
+
*/
|
|
114
|
+
function toImagePickerConf(
|
|
115
|
+
config: ArchbaseImagePickerConf,
|
|
116
|
+
colorScheme: 'light' | 'dark',
|
|
117
|
+
language: string
|
|
118
|
+
): ImagePickerConf {
|
|
119
|
+
return {
|
|
120
|
+
width: typeof config.width === 'number' ? `${config.width}px` : (config.width as string),
|
|
121
|
+
height: typeof config.height === 'number' ? `${config.height}px` : (config.height as string),
|
|
122
|
+
borderRadius: typeof config.borderRadius === 'number' ? `${config.borderRadius}px` : (config.borderRadius as string),
|
|
123
|
+
aspectRatio: config.aspectRatio ?? undefined,
|
|
124
|
+
objectFit: config.objectFit,
|
|
125
|
+
compressInitial: config.compressInitial ?? undefined,
|
|
126
|
+
hideDeleteBtn: config.hideDeleteBtn,
|
|
127
|
+
hideDownloadBtn: config.hideDownloadBtn,
|
|
128
|
+
hideEditBtn: config.hideEditBtn,
|
|
129
|
+
hideAddBtn: config.hideAddBtn,
|
|
130
|
+
darkMode: colorScheme === 'dark',
|
|
131
|
+
language: LANGUAGE_MAP[language] || 'en',
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Calcula o tamanho da imagem em KB a partir de uma string base64
|
|
137
|
+
*/
|
|
138
|
+
function calculateImageSize(dataUri: string | null | undefined): number | null {
|
|
139
|
+
if (!dataUri || !dataUri.length || !isDataUri(dataUri)) return null;
|
|
140
|
+
// Remove o header do data URI para calcular apenas o conteúdo base64
|
|
141
|
+
const base64 = dataUri.split(',')[1];
|
|
142
|
+
if (!base64) return null;
|
|
143
|
+
// Fórmula: (3/4) * length / 1024 para obter KB aproximado
|
|
144
|
+
return Math.ceil(((3 / 4) * base64.length) / 1024);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Extrai o formato da imagem do data URI
|
|
149
|
+
*/
|
|
150
|
+
function extractFormat(dataUri: string | null | undefined): string {
|
|
151
|
+
if (!dataUri || !isDataUri(dataUri)) return '';
|
|
152
|
+
const match = dataUri.match(/data:image\/([a-zA-Z]+);base64,/);
|
|
153
|
+
if (match && match[1]) {
|
|
154
|
+
return match[1] === 'jpeg' ? 'jpeg' : match[1];
|
|
155
|
+
}
|
|
156
|
+
return 'png';
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Props do ArchbaseImagePickerEditor
|
|
161
|
+
* Mantém compatibilidade total com a versão anterior
|
|
162
|
+
*/
|
|
163
|
+
export interface ArchbaseImagePickerEditorProps {
|
|
164
|
+
/** Configuração do editor (interface ArchbaseImagePickerConf) */
|
|
165
|
+
config?: ArchbaseImagePickerConf;
|
|
166
|
+
/** Imagem inicial (data URI, URL externa ou blob URL) */
|
|
167
|
+
imageSrcProp?: string;
|
|
168
|
+
/** Cor de destaque */
|
|
169
|
+
color?: string;
|
|
170
|
+
/**
|
|
171
|
+
* Callback quando a imagem é alterada
|
|
172
|
+
* @deprecated Use config.onChangeImage ao invés
|
|
173
|
+
*/
|
|
174
|
+
imageChanged?: (newDataUri: string | undefined) => void;
|
|
175
|
+
/** Variante dos botões de ação */
|
|
176
|
+
variant?: ActionIconVariant;
|
|
177
|
+
}
|
|
43
178
|
|
|
179
|
+
/**
|
|
180
|
+
* ArchbaseImagePickerEditor - Componente de seleção e edição de imagens
|
|
181
|
+
*
|
|
182
|
+
* Funcionalidades:
|
|
183
|
+
* - Seleção de imagens (upload)
|
|
184
|
+
* - Edição com crop, redimensionamento
|
|
185
|
+
* - Compressão de qualidade (JPEG/WebP)
|
|
186
|
+
* - Conversão de formatos (PNG, JPEG, WebP)
|
|
187
|
+
* - Filtros (contraste, brilho, saturação, sépia, blur)
|
|
188
|
+
* - Suporte a dark mode
|
|
189
|
+
* - Suporte a URL externa, data URI e blob URL
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* ```tsx
|
|
193
|
+
* <ArchbaseImagePickerEditor
|
|
194
|
+
* imageSrcProp={imageValue}
|
|
195
|
+
* config={{
|
|
196
|
+
* width: 300,
|
|
197
|
+
* height: 200,
|
|
198
|
+
* compressInitial: 80,
|
|
199
|
+
* objectFit: 'contain',
|
|
200
|
+
* onChangeImage: (newImage) => setImageValue(newImage),
|
|
201
|
+
* }}
|
|
202
|
+
* />
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
44
205
|
export const ArchbaseImagePickerEditor = memo(
|
|
45
206
|
({
|
|
46
207
|
config = {},
|
|
47
208
|
imageSrcProp = '',
|
|
48
209
|
color = '#1e88e5',
|
|
49
|
-
imageChanged
|
|
210
|
+
imageChanged,
|
|
50
211
|
variant = 'transparent',
|
|
51
|
-
}: {
|
|
52
|
-
config: ArchbaseImagePickerConf;
|
|
53
|
-
imageSrcProp?: string;
|
|
54
|
-
color?: string;
|
|
55
|
-
imageChanged?: Function;
|
|
56
|
-
variant?: ActionIconVariant
|
|
57
|
-
}) => {
|
|
212
|
+
}: ArchbaseImagePickerEditorProps) => {
|
|
58
213
|
const theme = useArchbaseTheme();
|
|
59
214
|
const { colorScheme } = useMantineColorScheme();
|
|
60
|
-
const { t } = useArchbaseTranslation();
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const [
|
|
66
|
-
const [
|
|
67
|
-
const [configuration, setConfiguration] = useState<ArchbaseImagePickerConf>(initialConfig);
|
|
68
|
-
const imagePicker = useRef<any>(null);
|
|
69
|
-
const fileType = useRef('');
|
|
70
|
-
const urlImage = useRef('');
|
|
71
|
-
const uuidFilePicker = Date.now().toString(20);
|
|
215
|
+
const { t, i18n } = useArchbaseTranslation();
|
|
216
|
+
|
|
217
|
+
// Normalizar a imagem de entrada para formato suportado
|
|
218
|
+
const normalizedInitialSrc = useMemo(() => normalizeImageSrc(imageSrcProp), [imageSrcProp]);
|
|
219
|
+
|
|
220
|
+
const [imageSrc, setImageSrc] = useState<string | null>(normalizedInitialSrc);
|
|
221
|
+
const [loadImage, setLoadImage] = useState<boolean>(!!normalizedInitialSrc);
|
|
72
222
|
const imageName = useRef('download');
|
|
73
|
-
const mounted = useRef(false);
|
|
74
223
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}, [config]);
|
|
224
|
+
// Ref para rastrear a última imagem reportada (evitar callbacks duplicados)
|
|
225
|
+
const lastReportedImageRef = useRef<string | null | undefined>(undefined);
|
|
78
226
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}, [imageSrcProp]);
|
|
227
|
+
// Ref para a imagem original (antes de normalização) para comparação
|
|
228
|
+
const originalImagePropRef = useRef<string | undefined>(imageSrcProp);
|
|
82
229
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const result = await parseToBase64(imageSrcProp);
|
|
86
|
-
const newState: IState = result.state;
|
|
87
|
-
newState.originImageSrc = imageSrcProp;
|
|
88
|
-
newState.arrayCopiedImages = [
|
|
89
|
-
{
|
|
90
|
-
lastImage: result.imageUri,
|
|
91
|
-
width: newState.maxWidth,
|
|
92
|
-
height: newState.maxHeight,
|
|
93
|
-
quality: newState.quality,
|
|
94
|
-
format: newState.format,
|
|
95
|
-
originImageSrc: imageSrcProp,
|
|
96
|
-
},
|
|
97
|
-
];
|
|
98
|
-
setImageSrc(result.imageUri);
|
|
99
|
-
setState(newState);
|
|
100
|
-
setLoadImage(true);
|
|
101
|
-
} else {
|
|
102
|
-
const newState = { ...state };
|
|
103
|
-
newState.originImageSrc = null;
|
|
104
|
-
newState.arrayCopiedImages = [];
|
|
105
|
-
setLoadImage(false);
|
|
106
|
-
setImageSrc(null);
|
|
107
|
-
setState(newState);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
230
|
+
// Ref para saber se é a primeira renderização
|
|
231
|
+
const isFirstRender = useRef(true);
|
|
110
232
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
233
|
+
// Configuração padrão
|
|
234
|
+
const defaultConfig: ArchbaseImagePickerConf = {
|
|
235
|
+
objectFit: 'cover',
|
|
236
|
+
hideDeleteBtn: false,
|
|
237
|
+
hideDownloadBtn: false,
|
|
238
|
+
hideEditBtn: false,
|
|
239
|
+
hideAddBtn: false,
|
|
240
|
+
compressInitial: null,
|
|
241
|
+
showImageSize: true,
|
|
242
|
+
width: 330,
|
|
243
|
+
height: 250,
|
|
244
|
+
borderRadius: '8px',
|
|
245
|
+
};
|
|
114
246
|
|
|
115
|
-
|
|
116
|
-
const dataConf = { ...configuration, ...config };
|
|
117
|
-
setConfiguration(dataConf);
|
|
118
|
-
}
|
|
247
|
+
const mergedConfig = useMemo(() => ({ ...defaultConfig, ...config }), [config]);
|
|
119
248
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
249
|
+
// Converter para configuração do react-image-picker-editor
|
|
250
|
+
const pickerConfig = useMemo(
|
|
251
|
+
() => toImagePickerConf(mergedConfig, colorScheme as 'light' | 'dark', i18n.language),
|
|
252
|
+
[mergedConfig, colorScheme, i18n.language]
|
|
253
|
+
);
|
|
124
254
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
fileType.current = file.type;
|
|
131
|
-
if (!fileType.current.includes('image')) return;
|
|
132
|
-
urlImage.current = `data:${file.type};base64,`;
|
|
133
|
-
if (file) {
|
|
134
|
-
const reader = new FileReader();
|
|
135
|
-
reader.onload = handleReaderLoaded.bind(this);
|
|
136
|
-
reader.readAsBinaryString(file);
|
|
137
|
-
}
|
|
255
|
+
// Callback com debounce para evitar múltiplas chamadas rápidas
|
|
256
|
+
const debouncedOnChange = useDebouncedCallback((newDataUri: string | undefined) => {
|
|
257
|
+
// Verificar se a imagem realmente mudou
|
|
258
|
+
if (lastReportedImageRef.current === newDataUri) {
|
|
259
|
+
return;
|
|
138
260
|
}
|
|
139
|
-
}
|
|
140
261
|
|
|
141
|
-
|
|
142
|
-
const binaryString = readerEvt.target.result;
|
|
143
|
-
const base64textString = btoa(binaryString);
|
|
144
|
-
let newState = { ...state };
|
|
145
|
-
const newImageSrc = urlImage.current + base64textString;
|
|
146
|
-
newState.originImageSrc = urlImage.current + base64textString;
|
|
147
|
-
newState.format = fileType.current.split('image/')[1];
|
|
148
|
-
if (configuration.compressInitial) {
|
|
149
|
-
newState = {
|
|
150
|
-
...newState,
|
|
151
|
-
quality: Math.min(configuration.compressInitial || 92, 100),
|
|
152
|
-
maintainAspectRatio: true,
|
|
153
|
-
format: newState.format ? newState.format : 'png',
|
|
154
|
-
};
|
|
155
|
-
const result = await convertImageUsingCanvas(newState.originImageSrc as string, false, newState, {
|
|
156
|
-
getDimFromImage: true,
|
|
157
|
-
});
|
|
158
|
-
setState(result.state);
|
|
159
|
-
setImageSrc(result.imageUri);
|
|
160
|
-
setLoadImage(true);
|
|
161
|
-
if (configuration.onChangeImage) {
|
|
162
|
-
configuration.onChangeImage(result.imageUri);
|
|
163
|
-
}
|
|
164
|
-
} else {
|
|
165
|
-
const img = document.createElement('img');
|
|
166
|
-
img.src = newImageSrc;
|
|
167
|
-
img.onload = () => {
|
|
168
|
-
newState.arrayCopiedImages = [];
|
|
169
|
-
newState.maxHeight = img.height;
|
|
170
|
-
newState.maxWidth = img.width;
|
|
171
|
-
newState.format = fileType.current.split('image/')[1];
|
|
172
|
-
newState.arrayCopiedImages.push({
|
|
173
|
-
lastImage: newImageSrc,
|
|
174
|
-
width: img.width,
|
|
175
|
-
height: img.height,
|
|
176
|
-
quality: newState.quality,
|
|
177
|
-
format: fileType.current.split('image/')[1],
|
|
178
|
-
originImageSrc: newState.originImageSrc as string,
|
|
179
|
-
});
|
|
180
|
-
setState(newState);
|
|
181
|
-
setImageSrc(newImageSrc);
|
|
182
|
-
setLoadImage(true);
|
|
183
|
-
if (configuration.onChangeImage) {
|
|
184
|
-
configuration.onChangeImage(newImageSrc);
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
}
|
|
262
|
+
lastReportedImageRef.current = newDataUri;
|
|
189
263
|
|
|
190
|
-
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
} else {
|
|
194
|
-
return '';
|
|
195
|
-
}
|
|
196
|
-
}, [imageSrc]);
|
|
197
|
-
|
|
198
|
-
function parseToBase64(imageUrl: string): Promise<{ imageUri: string; state: IState }> {
|
|
199
|
-
let newState = { ...state };
|
|
200
|
-
let type: string;
|
|
201
|
-
|
|
202
|
-
// Verifica se a URL é base64
|
|
203
|
-
if (imageUrl.startsWith('data:image/')) {
|
|
204
|
-
// Extrai o tipo do formato 'data:image/png;base64,'
|
|
205
|
-
const matches = imageUrl.match(/data:image\/([a-zA-Z]+);base64,/);
|
|
206
|
-
if (matches && matches[1]) {
|
|
207
|
-
type = matches[1];
|
|
208
|
-
} else {
|
|
209
|
-
// Se não houver correspondência, define um tipo padrão
|
|
210
|
-
type = 'png'; // ou qualquer tipo padrão que você preferir
|
|
211
|
-
}
|
|
212
|
-
} else {
|
|
213
|
-
// Trata a URL como um caminho de arquivo
|
|
214
|
-
const types = imageUrl.split('.');
|
|
215
|
-
type = types[types.length - 1];
|
|
216
|
-
if (!['png', 'jpeg', 'webp'].includes(type)) {
|
|
217
|
-
type = 'png'; // ou qualquer tipo padrão que você preferir
|
|
218
|
-
}
|
|
264
|
+
// Chamar callback do config (API principal)
|
|
265
|
+
if (mergedConfig.onChangeImage) {
|
|
266
|
+
mergedConfig.onChangeImage(newDataUri);
|
|
219
267
|
}
|
|
220
268
|
|
|
221
|
-
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
if (config.compressInitial >= 0 && config.compressInitial <= 100) {
|
|
225
|
-
quality = config.compressInitial;
|
|
226
|
-
}
|
|
227
|
-
newState.quality = quality;
|
|
269
|
+
// Chamar callback legado (para compatibilidade)
|
|
270
|
+
if (imageChanged) {
|
|
271
|
+
imageChanged(newDataUri);
|
|
228
272
|
}
|
|
273
|
+
}, 300); // 300ms de debounce
|
|
229
274
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
img.onload = () => {
|
|
237
|
-
const canvas = document.createElement('canvas');
|
|
238
|
-
const ctx: any = canvas.getContext('2d');
|
|
239
|
-
const ratio = 1.0;
|
|
240
|
-
canvas.width = img.width * ratio;
|
|
241
|
-
canvas.height = img.height * ratio;
|
|
242
|
-
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
243
|
-
const dataURI = canvas.toDataURL(`image/${type}`, newState.quality / 100);
|
|
244
|
-
return resolve({
|
|
245
|
-
dataUri: dataURI,
|
|
246
|
-
width: canvas.width,
|
|
247
|
-
height: canvas.height,
|
|
248
|
-
});
|
|
249
|
-
};
|
|
250
|
-
img.onerror = (e: any) => {
|
|
251
|
-
return reject(e.message || `Error loading the src = ${imageUrl}`);
|
|
252
|
-
};
|
|
253
|
-
img.src = imageUrl;
|
|
254
|
-
}).then((data: any) => {
|
|
255
|
-
newState = {
|
|
256
|
-
...newState,
|
|
257
|
-
maxHeight: data.height,
|
|
258
|
-
maxWidth: data.width,
|
|
259
|
-
};
|
|
260
|
-
return { imageUri: data.dataUri, state: newState };
|
|
261
|
-
});
|
|
262
|
-
}
|
|
275
|
+
// Sincronizar com prop externa
|
|
276
|
+
useEffect(() => {
|
|
277
|
+
// Só atualizar se a prop original realmente mudou
|
|
278
|
+
if (imageSrcProp !== originalImagePropRef.current) {
|
|
279
|
+
originalImagePropRef.current = imageSrcProp;
|
|
263
280
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
281
|
+
const normalizedSrc = normalizeImageSrc(imageSrcProp);
|
|
282
|
+
setImageSrc(normalizedSrc);
|
|
283
|
+
setLoadImage(!!normalizedSrc);
|
|
267
284
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
setImageSrc(data.imageSrc);
|
|
273
|
-
if (configuration.onChangeImage) {
|
|
274
|
-
configuration.onChangeImage(data.imageSrc);
|
|
285
|
+
// Atualizar a referência sem disparar callback na sincronização inicial
|
|
286
|
+
if (isFirstRender.current) {
|
|
287
|
+
lastReportedImageRef.current = normalizedSrc;
|
|
288
|
+
isFirstRender.current = false;
|
|
275
289
|
}
|
|
276
290
|
}
|
|
277
|
-
}
|
|
291
|
+
}, [imageSrcProp]);
|
|
278
292
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
setState(newState);
|
|
287
|
-
setShowEditPanel(false);
|
|
288
|
-
if (configuration.onChangeImage) {
|
|
289
|
-
configuration.onChangeImage(undefined);
|
|
293
|
+
// Handler quando a imagem muda no editor
|
|
294
|
+
const handleImageChanged = useCallback((newDataUri: string) => {
|
|
295
|
+
const newValue = newDataUri || undefined;
|
|
296
|
+
|
|
297
|
+
// Só processa se for diferente do valor atual
|
|
298
|
+
if (imageSrc === newDataUri) {
|
|
299
|
+
return;
|
|
290
300
|
}
|
|
291
|
-
|
|
301
|
+
|
|
302
|
+
setImageSrc(newDataUri || null);
|
|
303
|
+
setLoadImage(!!newDataUri);
|
|
304
|
+
|
|
305
|
+
// Usar callback com debounce
|
|
306
|
+
debouncedOnChange(newValue);
|
|
307
|
+
}, [imageSrc, debouncedOnChange]);
|
|
308
|
+
|
|
309
|
+
// Calcular tamanho e formato da imagem (apenas para data URIs)
|
|
310
|
+
const sizeImage = useMemo(() => calculateImageSize(imageSrc), [imageSrc]);
|
|
311
|
+
const format = useMemo(() => extractFormat(imageSrc), [imageSrc]);
|
|
312
|
+
|
|
313
|
+
// Verificar se deve mostrar informações de tamanho
|
|
314
|
+
const showSizeInfo = loadImage && sizeImage !== null && mergedConfig.showImageSize && isDataUri(imageSrc);
|
|
315
|
+
|
|
316
|
+
// Estilo do container
|
|
317
|
+
const containerStyle = useMemo(() => ({
|
|
318
|
+
width: typeof mergedConfig.width === 'number' ? `${mergedConfig.width}px` : mergedConfig.width,
|
|
319
|
+
backgroundColor: mergedConfig.imageBackgroundColor ?? (colorScheme === 'dark' ? theme.colors.dark[7] : theme.white),
|
|
320
|
+
}), [mergedConfig.width, mergedConfig.imageBackgroundColor, colorScheme, theme]);
|
|
292
321
|
|
|
293
322
|
return (
|
|
294
|
-
<div className="ArchbaseImagePickerEditor">
|
|
295
|
-
{
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
type="file"
|
|
316
|
-
style={{ display: 'none' }}
|
|
317
|
-
id={'filePicker-' + uuidFilePicker}
|
|
318
|
-
onChange={handleFileSelect}
|
|
319
|
-
/>
|
|
320
|
-
</div>
|
|
321
|
-
</div>
|
|
322
|
-
)}
|
|
323
|
-
{loadImage && (
|
|
324
|
-
<div className="place-image">
|
|
325
|
-
<div
|
|
326
|
-
className="image-holder-loaded"
|
|
327
|
-
style={{
|
|
328
|
-
width: configuration.width,
|
|
329
|
-
height: configuration.height,
|
|
330
|
-
borderRadius: configuration.borderRadius,
|
|
331
|
-
aspectRatio: configuration.aspectRatio + '',
|
|
332
|
-
background: colorScheme === 'dark' ? theme.colors.dark[7] : theme.white,
|
|
333
|
-
border: `${rem(1)} solid ${colorScheme === 'dark' ? theme.colors.dark[4] : theme.colors.gray[3]}`,
|
|
334
|
-
}}
|
|
335
|
-
>
|
|
336
|
-
<img
|
|
337
|
-
src={imageSrc as string}
|
|
338
|
-
alt="image-loaded"
|
|
339
|
-
style={{
|
|
340
|
-
height: configuration.height,
|
|
341
|
-
borderRadius: configuration.borderRadius,
|
|
342
|
-
objectFit: configuration.objectFit,
|
|
343
|
-
backgroundColor: config.imageBackgroundColor ?? (colorScheme === 'dark' ? theme.colors.dark[7] : theme.white),
|
|
344
|
-
}}
|
|
345
|
-
/>
|
|
346
|
-
{!configuration.hideEditBtn && (
|
|
347
|
-
<Fragment>
|
|
348
|
-
<div className="curtain" onClick={onUpload}>
|
|
349
|
-
<Tooltip withinPortal withArrow label={`${t('archbase:Upload a image')}`}>
|
|
350
|
-
<ActionIcon component="button" color="blue" variant={variant}>
|
|
351
|
-
<IconCloudUpload color="white" />
|
|
352
|
-
</ActionIcon>
|
|
353
|
-
</Tooltip>
|
|
354
|
-
</div>
|
|
355
|
-
<input
|
|
356
|
-
ref={imagePicker}
|
|
357
|
-
type="file"
|
|
358
|
-
style={{ display: 'none' }}
|
|
359
|
-
id={'filePicker-' + uuidFilePicker}
|
|
360
|
-
onChange={handleFileSelect}
|
|
361
|
-
/>
|
|
362
|
-
</Fragment>
|
|
363
|
-
)}
|
|
364
|
-
</div>
|
|
365
|
-
{sizeImage && configuration.showImageSize && (
|
|
366
|
-
<div
|
|
367
|
-
className="caption image-caption"
|
|
368
|
-
style={{
|
|
369
|
-
color: sizeImage > 120 ? '#f44336' : 'unset',
|
|
370
|
-
fontWeight: sizeImage > 120 ? '500' : 'unset',
|
|
371
|
-
}}
|
|
372
|
-
>
|
|
373
|
-
<Text>{`${t('archbase:size')}: ${sizeImage}Kb ${state.format}`}</Text>
|
|
374
|
-
</div>
|
|
375
|
-
)}
|
|
376
|
-
|
|
377
|
-
<div
|
|
378
|
-
style={{
|
|
379
|
-
flexDirection: 'row',
|
|
380
|
-
boxSizing: 'border-box',
|
|
381
|
-
display: 'flex',
|
|
382
|
-
placeContent: 'flex-start',
|
|
383
|
-
alignItems: 'flex-start',
|
|
384
|
-
}}
|
|
385
|
-
className="editing-bar-btn"
|
|
386
|
-
>
|
|
387
|
-
{!configuration.hideAddBtn && (
|
|
388
|
-
<Tooltip withinPortal withArrow label={`${t('archbase:Upload a image')}`}>
|
|
389
|
-
<ActionIcon id="upload-img" color="blue" variant={variant} onClick={onUpload}>
|
|
390
|
-
<IconCloudUpload color="purple" />
|
|
391
|
-
</ActionIcon>
|
|
392
|
-
</Tooltip>
|
|
393
|
-
)}
|
|
394
|
-
|
|
395
|
-
{!configuration.hideEditBtn && (
|
|
396
|
-
<Tooltip withinPortal withArrow label={`${t('archbase:Open the editor panel')}`}>
|
|
397
|
-
<ActionIcon id="edit-img" color="blue" variant={variant} onClick={onOpenEditPanel}>
|
|
398
|
-
<IconPhotoEdit color="teal" />
|
|
399
|
-
</ActionIcon>
|
|
400
|
-
</Tooltip>
|
|
401
|
-
)}
|
|
402
|
-
{!configuration.hideDownloadBtn && (
|
|
403
|
-
<Tooltip withinPortal withArrow label={`${t('archbase:Download the image')}`}>
|
|
404
|
-
<ActionIcon
|
|
405
|
-
component="a"
|
|
406
|
-
id="download-img"
|
|
407
|
-
href={imageSrc as string}
|
|
408
|
-
color="green"
|
|
409
|
-
download={imageName.current}
|
|
410
|
-
variant={variant}
|
|
411
|
-
>
|
|
412
|
-
<IconCloudDownload color="green" />
|
|
413
|
-
</ActionIcon>
|
|
414
|
-
</Tooltip>
|
|
415
|
-
)}
|
|
416
|
-
{!configuration.hideDeleteBtn && (
|
|
417
|
-
<Tooltip withinPortal withArrow label={`${t('archbase:Remove')}`}>
|
|
418
|
-
<ActionIcon id="delete-img" color="red" variant={variant} onClick={() => onRemove()}>
|
|
419
|
-
<IconTrash color="#C91A25" />
|
|
420
|
-
</ActionIcon>
|
|
421
|
-
</Tooltip>
|
|
422
|
-
)}
|
|
423
|
-
</div>
|
|
323
|
+
<div className="ArchbaseImagePickerEditor" style={containerStyle}>
|
|
324
|
+
{/* Componente react-image-picker-editor */}
|
|
325
|
+
<ReactImagePickerEditor
|
|
326
|
+
config={pickerConfig}
|
|
327
|
+
imageSrcProp={imageSrc || ''}
|
|
328
|
+
imageChanged={handleImageChanged}
|
|
329
|
+
color={color}
|
|
330
|
+
/>
|
|
331
|
+
|
|
332
|
+
{/* Informação de tamanho (quando habilitado, imagem carregada e é data URI) */}
|
|
333
|
+
{showSizeInfo && (
|
|
334
|
+
<div
|
|
335
|
+
className="caption image-caption"
|
|
336
|
+
style={{
|
|
337
|
+
color: sizeImage! > 120 ? '#f44336' : 'unset',
|
|
338
|
+
fontWeight: sizeImage! > 120 ? '500' : 'unset',
|
|
339
|
+
marginTop: '4px',
|
|
340
|
+
textAlign: 'center',
|
|
341
|
+
}}
|
|
342
|
+
>
|
|
343
|
+
<Text size="sm">{`${t('archbase:size')}: ${sizeImage}Kb ${format}`}</Text>
|
|
424
344
|
</div>
|
|
425
345
|
)}
|
|
426
|
-
{showEditPanel && (
|
|
427
|
-
<ArchbaseEditImage
|
|
428
|
-
saveUpdates={onCloseEditPanel}
|
|
429
|
-
color={color}
|
|
430
|
-
image={imageSrc}
|
|
431
|
-
initialState={state}
|
|
432
|
-
></ArchbaseEditImage>
|
|
433
|
-
)}
|
|
434
346
|
</div>
|
|
435
347
|
);
|
|
436
348
|
},
|
|
437
349
|
);
|
|
350
|
+
|
|
351
|
+
ArchbaseImagePickerEditor.displayName = 'ArchbaseImagePickerEditor';
|