@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.
Files changed (168) hide show
  1. package/dist/buttons/ArchbaseSpeedDial.d.ts +59 -0
  2. package/dist/buttons/index.d.ts +2 -0
  3. package/dist/charts/ArchbaseArcGauge.d.ts +55 -0
  4. package/dist/charts/ArchbaseGantt.d.ts +70 -0
  5. package/dist/charts/ArchbaseLinearGauge.d.ts +55 -0
  6. package/dist/charts/ArchbaseResourceTimeline.d.ts +61 -0
  7. package/dist/charts/ArchbaseSparkline.d.ts +45 -0
  8. package/dist/charts/index.d.ts +10 -0
  9. package/dist/containers/ArchbaseExpansionPanel.d.ts +60 -0
  10. package/dist/containers/ArchbaseFloatingPanel.d.ts +91 -0
  11. package/dist/containers/index.d.ts +4 -0
  12. package/dist/datagrid/export/ArchbaseDataGridExcelExport.d.ts +67 -0
  13. package/dist/datagrid/export/index.d.ts +2 -0
  14. package/dist/datagrid/index.d.ts +1 -0
  15. package/dist/display/ArchbaseAnimationWrapper.d.ts +73 -0
  16. package/dist/display/ArchbaseBarcodeGenerator.d.ts +50 -0
  17. package/dist/display/ArchbaseCarousel.d.ts +100 -0
  18. package/dist/display/ArchbaseCodeViewer.d.ts +63 -0
  19. package/dist/display/ArchbaseDiffViewer.d.ts +41 -0
  20. package/dist/display/ArchbaseJsonTree.d.ts +34 -0
  21. package/dist/display/ArchbaseLightbox.d.ts +89 -0
  22. package/dist/display/ArchbaseMarquee.d.ts +46 -0
  23. package/dist/display/ArchbasePhotoAlbum.d.ts +58 -0
  24. package/dist/display/ArchbaseQRCode.d.ts +44 -0
  25. package/dist/display/ArchbaseTooltipRich.d.ts +82 -0
  26. package/dist/display/ArchbaseVirtualList.d.ts +116 -0
  27. package/dist/display/index.d.ts +24 -0
  28. package/dist/editors/ArchbaseAIPromptInput.d.ts +69 -0
  29. package/dist/editors/ArchbaseAsyncMultiSelect.d.ts +1 -1
  30. package/dist/editors/ArchbaseAsyncSelect.d.ts +1 -1
  31. package/dist/editors/ArchbaseBarcodeScanner.d.ts +63 -0
  32. package/dist/editors/ArchbaseCepInput.d.ts +26 -0
  33. package/dist/editors/ArchbaseColorGradientPicker.d.ts +80 -0
  34. package/dist/editors/ArchbaseCpfCnpjInput.d.ts +21 -0
  35. package/dist/editors/ArchbaseCurrencyInput.d.ts +25 -0
  36. package/dist/editors/ArchbaseDualListbox.d.ts +61 -0
  37. package/dist/editors/ArchbaseInPlaceEditor.d.ts +67 -0
  38. package/dist/editors/ArchbaseLightGrid.d.ts +56 -0
  39. package/dist/editors/ArchbaseMentionInput.d.ts +75 -0
  40. package/dist/editors/ArchbaseMultiEmail.d.ts +53 -0
  41. package/dist/editors/ArchbaseMultiViewCalendar.d.ts +47 -0
  42. package/dist/editors/ArchbaseNumberStepper.d.ts +66 -0
  43. package/dist/editors/ArchbaseOTPInput.d.ts +27 -0
  44. package/dist/editors/ArchbasePhoneInput.d.ts +52 -0
  45. package/dist/editors/ArchbaseRangeSlider.d.ts +22 -0
  46. package/dist/editors/ArchbaseSignaturePad.d.ts +60 -0
  47. package/dist/editors/ArchbaseSpeechToTextInput.d.ts +106 -0
  48. package/dist/editors/ArchbaseSpreadsheet.d.ts +56 -0
  49. package/dist/editors/ArchbaseTagInput.d.ts +55 -0
  50. package/dist/editors/index.d.ts +40 -0
  51. package/dist/feedback/ArchbaseChunkProgressBar.d.ts +35 -0
  52. package/dist/feedback/ArchbaseRipple.d.ts +43 -0
  53. package/dist/feedback/ArchbaseSkeleton.d.ts +115 -0
  54. package/dist/feedback/index.d.ts +6 -0
  55. package/dist/filters/ArchbaseColumnSelector.d.ts +38 -0
  56. package/dist/filters/index.d.ts +2 -0
  57. package/dist/forms/ArchbaseFieldArray.d.ts +80 -0
  58. package/dist/forms/ArchbaseFormWizard.d.ts +73 -0
  59. package/dist/forms/ArchbaseStepper.d.ts +51 -0
  60. package/dist/forms/index.d.ts +8 -0
  61. package/dist/forms/useArchbaseFormWizard.d.ts +74 -0
  62. package/dist/hooks/index.d.ts +2 -0
  63. package/dist/hooks/useArchbaseConfirm.d.ts +59 -0
  64. package/dist/image/editor/index.d.ts +43 -4
  65. package/dist/image/index.d.ts +1 -0
  66. package/dist/index.css +1 -1
  67. package/dist/index.d.ts +1 -0
  68. package/dist/index.js +22510 -13820
  69. package/dist/list/ArchbaseTreeList.d.ts +76 -0
  70. package/dist/list/index.d.ts +2 -0
  71. package/dist/navigation/ArchbaseActionSheet.d.ts +70 -0
  72. package/dist/navigation/ArchbaseAppBar.d.ts +63 -0
  73. package/dist/navigation/ArchbaseBottomNavigation.d.ts +61 -0
  74. package/dist/navigation/ArchbaseContextMenu.d.ts +76 -0
  75. package/dist/navigation/ArchbaseKeyboardShortcuts.d.ts +85 -0
  76. package/dist/navigation/ArchbaseSpotlight.d.ts +69 -0
  77. package/dist/navigation/index.d.ts +12 -0
  78. package/dist/notification/ArchbaseNotificationCenter.d.ts +56 -0
  79. package/dist/notification/index.d.ts +2 -0
  80. package/dist/onboarding/ArchbaseOnboardingTour.d.ts +27 -76
  81. package/dist/viewers/ArchbaseDocViewer.d.ts +40 -0
  82. package/dist/viewers/ArchbasePdfBuilder.d.ts +53 -0
  83. package/dist/viewers/ArchbaseXmlViewer.d.ts +36 -0
  84. package/dist/viewers/index.d.ts +6 -0
  85. package/package.json +41 -18
  86. package/src/buttons/ArchbaseSpeedDial.tsx +282 -0
  87. package/src/buttons/index.ts +3 -0
  88. package/src/charts/ArchbaseArcGauge.tsx +325 -0
  89. package/src/charts/ArchbaseGantt.tsx +494 -0
  90. package/src/charts/ArchbaseLinearGauge.tsx +288 -0
  91. package/src/charts/ArchbaseResourceTimeline.tsx +433 -0
  92. package/src/charts/ArchbaseSparkline.tsx +193 -0
  93. package/src/charts/index.ts +20 -1
  94. package/src/containers/ArchbaseExpansionPanel.tsx +208 -0
  95. package/src/containers/ArchbaseFloatingPanel.tsx +448 -0
  96. package/src/containers/index.ts +17 -0
  97. package/src/datagrid/components/pagination/grid-pagination.tsx +2 -2
  98. package/src/datagrid/export/ArchbaseDataGridExcelExport.tsx +293 -0
  99. package/src/datagrid/export/index.ts +8 -0
  100. package/src/datagrid/index.tsx +2 -0
  101. package/src/display/ArchbaseAnimationWrapper.tsx +272 -0
  102. package/src/display/ArchbaseBarcodeGenerator.tsx +303 -0
  103. package/src/display/ArchbaseCarousel.tsx +679 -0
  104. package/src/display/ArchbaseCodeViewer.tsx +271 -0
  105. package/src/display/ArchbaseDiffViewer.tsx +108 -0
  106. package/src/display/ArchbaseJsonTree.tsx +150 -0
  107. package/src/display/ArchbaseLightbox.tsx +279 -0
  108. package/src/display/ArchbaseMarquee.tsx +268 -0
  109. package/src/display/ArchbasePhotoAlbum.tsx +415 -0
  110. package/src/display/ArchbaseQRCode.tsx +137 -0
  111. package/src/display/ArchbaseTooltipRich.tsx +317 -0
  112. package/src/display/ArchbaseVirtualList.tsx +618 -0
  113. package/src/display/index.ts +94 -1
  114. package/src/editors/ArchbaseAIPromptInput.tsx +432 -0
  115. package/src/editors/ArchbaseBarcodeScanner.tsx +570 -0
  116. package/src/editors/ArchbaseCepInput.tsx +173 -0
  117. package/src/editors/ArchbaseColorGradientPicker.tsx +460 -0
  118. package/src/editors/ArchbaseCpfCnpjInput.tsx +193 -0
  119. package/src/editors/ArchbaseCurrencyInput.tsx +100 -0
  120. package/src/editors/ArchbaseDualListbox.tsx +671 -0
  121. package/src/editors/ArchbaseInPlaceEditor.tsx +406 -0
  122. package/src/editors/ArchbaseLightGrid.tsx +538 -0
  123. package/src/editors/ArchbaseMentionInput.tsx +384 -0
  124. package/src/editors/ArchbaseMultiEmail.tsx +367 -0
  125. package/src/editors/ArchbaseMultiViewCalendar.tsx +439 -0
  126. package/src/editors/ArchbaseNumberStepper.tsx +219 -0
  127. package/src/editors/ArchbaseOTPInput.tsx +133 -0
  128. package/src/editors/ArchbaseOperationHoursEditor.tsx +1 -1
  129. package/src/editors/ArchbasePhoneInput.tsx +355 -0
  130. package/src/editors/ArchbaseRangeSlider.tsx +109 -0
  131. package/src/editors/ArchbaseSignaturePad.tsx +425 -0
  132. package/src/editors/ArchbaseSpeechToTextInput.tsx +444 -0
  133. package/src/editors/ArchbaseSpreadsheet.tsx +388 -0
  134. package/src/editors/ArchbaseTagInput.tsx +326 -0
  135. package/src/editors/index.tsx +92 -0
  136. package/src/feedback/ArchbaseChunkProgressBar.tsx +156 -0
  137. package/src/feedback/ArchbaseRipple.tsx +278 -0
  138. package/src/feedback/ArchbaseSkeleton.tsx +419 -0
  139. package/src/feedback/index.ts +22 -1
  140. package/src/filters/ArchbaseColumnSelector.tsx +268 -0
  141. package/src/filters/index.ts +2 -0
  142. package/src/forms/ArchbaseFieldArray.tsx +305 -0
  143. package/src/forms/ArchbaseFormWizard.tsx +284 -0
  144. package/src/forms/ArchbaseStepper.tsx +137 -0
  145. package/src/forms/index.ts +24 -2
  146. package/src/forms/useArchbaseFormWizard.ts +283 -0
  147. package/src/hooks/index.ts +12 -0
  148. package/src/hooks/useArchbaseConfirm.ts +293 -0
  149. package/src/image/editor/functions/image-processing.ts +5 -0
  150. package/src/image/editor/index.tsx +305 -391
  151. package/src/image/index.ts +1 -0
  152. package/src/index.ts +3 -0
  153. package/src/list/ArchbaseTreeList.tsx +422 -0
  154. package/src/list/index.ts +3 -0
  155. package/src/navigation/ArchbaseActionSheet.tsx +301 -0
  156. package/src/navigation/ArchbaseAppBar.tsx +233 -0
  157. package/src/navigation/ArchbaseBottomNavigation.tsx +243 -0
  158. package/src/navigation/ArchbaseContextMenu.tsx +298 -0
  159. package/src/navigation/ArchbaseKeyboardShortcuts.tsx +472 -0
  160. package/src/navigation/ArchbaseSpotlight.tsx +483 -0
  161. package/src/navigation/index.ts +48 -1
  162. package/src/notification/ArchbaseNotificationCenter.tsx +502 -0
  163. package/src/notification/index.ts +2 -0
  164. package/src/onboarding/ArchbaseOnboardingTour.tsx +229 -102
  165. package/src/viewers/ArchbaseDocViewer.tsx +584 -0
  166. package/src/viewers/ArchbasePdfBuilder.tsx +291 -0
  167. package/src/viewers/ArchbaseXmlViewer.tsx +142 -0
  168. package/src/viewers/index.ts +12 -0
@@ -0,0 +1,173 @@
1
+ import React, { useCallback, useState } from 'react';
2
+ import { TextInput, TextInputProps, Loader, ThemeIcon, Tooltip } from '@mantine/core';
3
+ import { IconMapPin, IconCheck, IconX } from '@tabler/icons-react';
4
+
5
+ // =============================================================================
6
+ // Types
7
+ // =============================================================================
8
+
9
+ export interface CepAddress {
10
+ cep: string;
11
+ state: string;
12
+ city: string;
13
+ neighborhood: string;
14
+ street: string;
15
+ service?: string;
16
+ }
17
+
18
+ export interface ArchbaseCepInputProps extends Omit<TextInputProps, 'value' | 'onChange' | 'onError'> {
19
+ /** Valor atual */
20
+ value?: string;
21
+ /** Callback ao mudar */
22
+ onChange?: (value: string) => void;
23
+ /** Callback ao encontrar endereço */
24
+ onAddressFound?: (address: CepAddress) => void;
25
+ /** Callback em caso de erro */
26
+ onCepError?: (error: Error) => void;
27
+ /** Auto-buscar ao completar CEP */
28
+ autoFetch?: boolean;
29
+ /** Mostrar ícone de status */
30
+ showStatusIcon?: boolean;
31
+ }
32
+
33
+ // =============================================================================
34
+ // CEP Utilities
35
+ // =============================================================================
36
+
37
+ function stripNonDigits(value: string): string {
38
+ return value.replace(/\D/g, '');
39
+ }
40
+
41
+ export function formatCep(cep: string): string {
42
+ const digits = stripNonDigits(cep);
43
+ if (digits.length <= 5) return digits;
44
+ return `${digits.slice(0, 5)}-${digits.slice(5, 8)}`;
45
+ }
46
+
47
+ async function fetchCepViaCep(cep: string): Promise<CepAddress | null> {
48
+ try {
49
+ const response = await fetch(`https://viacep.com.br/ws/${cep}/json/`);
50
+ const data = await response.json();
51
+ if (data.erro) return null;
52
+ return {
53
+ cep: data.cep,
54
+ state: data.uf,
55
+ city: data.localidade,
56
+ neighborhood: data.bairro,
57
+ street: data.logradouro,
58
+ service: 'viacep',
59
+ };
60
+ } catch {
61
+ return null;
62
+ }
63
+ }
64
+
65
+ async function fetchCepBrasilAPI(cep: string): Promise<CepAddress | null> {
66
+ try {
67
+ const response = await fetch(`https://brasilapi.com.br/api/cep/v1/${cep}`);
68
+ const data = await response.json();
69
+ if (data.errors) return null;
70
+ return {
71
+ cep: data.cep,
72
+ state: data.state,
73
+ city: data.city,
74
+ neighborhood: data.neighborhood,
75
+ street: data.street,
76
+ service: 'brasilapi',
77
+ };
78
+ } catch {
79
+ return null;
80
+ }
81
+ }
82
+
83
+ // =============================================================================
84
+ // ArchbaseCepInput Component
85
+ // =============================================================================
86
+
87
+ export function ArchbaseCepInput({
88
+ value = '',
89
+ onChange,
90
+ onAddressFound,
91
+ onCepError,
92
+ autoFetch = true,
93
+ showStatusIcon = true,
94
+ ...props
95
+ }: ArchbaseCepInputProps) {
96
+ const [isLoading, setIsLoading] = useState(false);
97
+ const [status, setStatus] = useState<'idle' | 'found' | 'notfound'>('idle');
98
+
99
+ const digits = stripNonDigits(value);
100
+
101
+ const fetchAddress = useCallback(async (cep: string) => {
102
+ if (cep.length !== 8) return;
103
+
104
+ setIsLoading(true);
105
+ setStatus('idle');
106
+
107
+ try {
108
+ let address = await fetchCepViaCep(cep);
109
+ if (!address) {
110
+ address = await fetchCepBrasilAPI(cep);
111
+ }
112
+
113
+ if (address) {
114
+ setStatus('found');
115
+ onAddressFound?.(address);
116
+ } else {
117
+ setStatus('notfound');
118
+ onCepError?.(new Error('CEP não encontrado'));
119
+ }
120
+ } catch (error) {
121
+ setStatus('notfound');
122
+ onCepError?.(error instanceof Error ? error : new Error('Erro ao buscar CEP'));
123
+ } finally {
124
+ setIsLoading(false);
125
+ }
126
+ }, [onAddressFound, onCepError]);
127
+
128
+ const handleChange = useCallback(
129
+ (e: React.ChangeEvent<HTMLInputElement>) => {
130
+ const newDigits = stripNonDigits(e.target.value).slice(0, 8);
131
+ onChange?.(newDigits);
132
+
133
+ if (autoFetch && newDigits.length === 8) {
134
+ fetchAddress(newDigits);
135
+ } else {
136
+ setStatus('idle');
137
+ }
138
+ },
139
+ [onChange, autoFetch, fetchAddress]
140
+ );
141
+
142
+ const rightSection = isLoading ? (
143
+ <Loader size="xs" />
144
+ ) : showStatusIcon && digits.length === 8 ? (
145
+ <Tooltip label={status === 'found' ? 'CEP encontrado' : 'CEP não encontrado'}>
146
+ <ThemeIcon
147
+ size="sm"
148
+ variant="light"
149
+ color={status === 'found' ? 'green' : status === 'notfound' ? 'red' : 'gray'}
150
+ radius="xl"
151
+ >
152
+ {status === 'found' ? <IconCheck size={14} /> : status === 'notfound' ? <IconX size={14} /> : <IconMapPin size={14} />}
153
+ </ThemeIcon>
154
+ </Tooltip>
155
+ ) : (
156
+ <ThemeIcon size="sm" variant="light" color="gray" radius="xl">
157
+ <IconMapPin size={14} />
158
+ </ThemeIcon>
159
+ );
160
+
161
+ return (
162
+ <TextInput
163
+ {...props}
164
+ value={formatCep(value)}
165
+ onChange={handleChange}
166
+ rightSection={rightSection}
167
+ placeholder="00000-000"
168
+ maxLength={9}
169
+ />
170
+ );
171
+ }
172
+
173
+ export default ArchbaseCepInput;
@@ -0,0 +1,460 @@
1
+ import React, { forwardRef, useCallback, useState } from 'react';
2
+ import {
3
+ ColorPicker,
4
+ ColorPickerProps,
5
+ ColorSwatch,
6
+ HueSlider,
7
+ AlphaSlider,
8
+ TextInput,
9
+ Group,
10
+ Stack,
11
+ Text,
12
+ Box,
13
+ Popover,
14
+ ActionIcon,
15
+ SimpleGrid,
16
+ MantineSize,
17
+ } from '@mantine/core';
18
+ import { IconPalette, IconColorPicker, IconCheck } from '@tabler/icons-react';
19
+ import { ArchbaseDataSource, DataSourceEvent, DataSourceEventNames } from '@archbase/data';
20
+ import { useArchbaseDidMount, useArchbaseDidUpdate, useArchbaseWillUnmount } from '@archbase/core';
21
+
22
+ export type ArchbaseColorFormat = 'hex' | 'hexa' | 'rgb' | 'rgba' | 'hsl' | 'hsla';
23
+
24
+ export interface ArchbaseColorGradientPickerProps extends Omit<ColorPickerProps, 'value' | 'onChange'> {
25
+ /** Fonte de dados */
26
+ dataSource?: ArchbaseDataSource<any, any>;
27
+ /** Campo do DataSource */
28
+ dataField?: string;
29
+ /** Valor controlado */
30
+ value?: string;
31
+ /** Valor padrão */
32
+ defaultValue?: string;
33
+ /** Callback de mudança */
34
+ onChange?: (value: string) => void;
35
+ /** Formato do valor */
36
+ format?: ArchbaseColorFormat;
37
+ /** Swatches de cores */
38
+ swatches?: string[];
39
+ /** Se deve mostrar swatches */
40
+ showSwatches?: boolean;
41
+ /** Se deve mostrar hue slider */
42
+ showHueSlider?: boolean;
43
+ /** Se deve mostrar alpha slider */
44
+ showAlphaSlider?: boolean;
45
+ /** Se deve mostrar input de texto */
46
+ showInput?: boolean;
47
+ /** Se deve mostrar eyedropper (quando suportado) */
48
+ showEyeDropper?: boolean;
49
+ /** Label */
50
+ label?: string;
51
+ /** Descrição */
52
+ description?: string;
53
+ /** Erro */
54
+ error?: string | boolean;
55
+ /** Tamanho */
56
+ size?: MantineSize;
57
+ /** Se está desabilitado */
58
+ disabled?: boolean;
59
+ /** Se é somente leitura */
60
+ readOnly?: boolean;
61
+ /** Largura */
62
+ width?: number | string;
63
+ /** Se deve usar popover */
64
+ withPopover?: boolean;
65
+ /** Classe CSS */
66
+ className?: string;
67
+ /** Estilo */
68
+ style?: React.CSSProperties;
69
+ }
70
+
71
+ // Swatches padrão com cores comuns
72
+ const DEFAULT_SWATCHES = [
73
+ '#25262b', '#868e96', '#fa5252', '#e64980', '#be4bdb',
74
+ '#7950f2', '#4c6ef5', '#228be6', '#15aabf', '#12b886',
75
+ '#40c057', '#82c91e', '#fab005', '#fd7e14', '#ffffff',
76
+ ];
77
+
78
+ export const ArchbaseColorGradientPicker = forwardRef<HTMLDivElement, ArchbaseColorGradientPickerProps>(
79
+ (
80
+ {
81
+ dataSource,
82
+ dataField,
83
+ value: controlledValue,
84
+ defaultValue = '#228be6',
85
+ onChange,
86
+ format = 'hex',
87
+ swatches = DEFAULT_SWATCHES,
88
+ showSwatches = true,
89
+ showHueSlider = true,
90
+ showAlphaSlider = false,
91
+ showInput = true,
92
+ showEyeDropper = true,
93
+ label,
94
+ description,
95
+ error,
96
+ size = 'sm',
97
+ disabled = false,
98
+ readOnly = false,
99
+ width,
100
+ withPopover = false,
101
+ className,
102
+ style,
103
+ ...rest
104
+ },
105
+ ref
106
+ ) => {
107
+ const [internalValue, setInternalValue] = useState(defaultValue);
108
+ const [popoverOpened, setPopoverOpened] = useState(false);
109
+
110
+ // Determina o valor atual
111
+ const currentValue = controlledValue !== undefined ? controlledValue : internalValue;
112
+
113
+ const loadDataSourceFieldValue = useCallback(() => {
114
+ if (dataSource && dataField) {
115
+ const fieldValue = dataSource.getFieldValue(dataField);
116
+ setInternalValue(fieldValue ?? defaultValue);
117
+ }
118
+ }, [dataSource, dataField, defaultValue]);
119
+
120
+ const fieldChangedListener = useCallback(() => {
121
+ loadDataSourceFieldValue();
122
+ }, [loadDataSourceFieldValue]);
123
+
124
+ const dataSourceEvent = useCallback(
125
+ (event: DataSourceEvent<any>) => {
126
+ if (dataSource && dataField) {
127
+ if (
128
+ event.type === DataSourceEventNames.dataChanged ||
129
+ event.type === DataSourceEventNames.recordChanged ||
130
+ event.type === DataSourceEventNames.afterScroll ||
131
+ event.type === DataSourceEventNames.afterCancel
132
+ ) {
133
+ loadDataSourceFieldValue();
134
+ }
135
+ }
136
+ },
137
+ [dataSource, dataField, loadDataSourceFieldValue]
138
+ );
139
+
140
+ useArchbaseDidMount(() => {
141
+ loadDataSourceFieldValue();
142
+ if (dataSource && dataField) {
143
+ dataSource.addListener(dataSourceEvent);
144
+ dataSource.addFieldChangeListener(dataField, fieldChangedListener);
145
+ }
146
+ });
147
+
148
+ useArchbaseWillUnmount(() => {
149
+ if (dataSource && dataField) {
150
+ dataSource.removeListener(dataSourceEvent);
151
+ dataSource.removeFieldChangeListener(dataField, fieldChangedListener);
152
+ }
153
+ });
154
+
155
+ useArchbaseDidUpdate(() => {
156
+ loadDataSourceFieldValue();
157
+ }, []);
158
+
159
+ const handleChange = useCallback(
160
+ (val: string) => {
161
+ // Atualiza DataSource se disponível
162
+ if (dataSource && dataField && !dataSource.isBrowsing()) {
163
+ dataSource.setFieldValue(dataField, val);
164
+ }
165
+
166
+ // Atualiza valor interno se não controlado
167
+ if (controlledValue === undefined) {
168
+ setInternalValue(val);
169
+ }
170
+
171
+ // Callback
172
+ onChange?.(val);
173
+ },
174
+ [controlledValue, dataField, dataSource, onChange]
175
+ );
176
+
177
+ const handleEyeDropper = useCallback(async () => {
178
+ if (!('EyeDropper' in window)) return;
179
+
180
+ try {
181
+ const eyeDropper = new (window as any).EyeDropper();
182
+ const result = await eyeDropper.open();
183
+ handleChange(result.sRGBHex);
184
+ } catch (e) {
185
+ // Usuário cancelou
186
+ }
187
+ }, [handleChange]);
188
+
189
+ const isReadOnly = readOnly || (dataSource ? dataSource.isBrowsing() : false);
190
+
191
+ const pickerContent = (
192
+ <Stack gap="sm">
193
+ {/* Color picker principal */}
194
+ <ColorPicker
195
+ value={currentValue}
196
+ onChange={handleChange}
197
+ format={format}
198
+ size={size}
199
+ {...rest}
200
+ />
201
+
202
+ {/* Hue slider */}
203
+ {showHueSlider && (
204
+ <Box>
205
+ <Text size="xs" c="dimmed" mb={4}>
206
+ Matiz
207
+ </Text>
208
+ <HueSlider
209
+ value={0} // Controlled by ColorPicker internally
210
+ onChange={() => {}}
211
+ size={size}
212
+ />
213
+ </Box>
214
+ )}
215
+
216
+ {/* Alpha slider */}
217
+ {showAlphaSlider && (format === 'hexa' || format === 'rgba' || format === 'hsla') && (
218
+ <Box>
219
+ <Text size="xs" c="dimmed" mb={4}>
220
+ Opacidade
221
+ </Text>
222
+ <AlphaSlider
223
+ color={currentValue}
224
+ value={1}
225
+ onChange={() => {}}
226
+ size={size}
227
+ />
228
+ </Box>
229
+ )}
230
+
231
+ {/* Swatches */}
232
+ {showSwatches && swatches.length > 0 && (
233
+ <Box>
234
+ <Text size="xs" c="dimmed" mb={4}>
235
+ Cores
236
+ </Text>
237
+ <SimpleGrid cols={5} spacing={4}>
238
+ {swatches.map((swatch) => (
239
+ <ColorSwatch
240
+ key={swatch}
241
+ color={swatch}
242
+ size={24}
243
+ style={{ cursor: isReadOnly || disabled ? 'default' : 'pointer' }}
244
+ onClick={() => !isReadOnly && !disabled && handleChange(swatch)}
245
+ >
246
+ {currentValue.toLowerCase() === swatch.toLowerCase() && (
247
+ <IconCheck size={12} color="white" style={{ mixBlendMode: 'difference' }} />
248
+ )}
249
+ </ColorSwatch>
250
+ ))}
251
+ </SimpleGrid>
252
+ </Box>
253
+ )}
254
+
255
+ {/* Input e eye dropper */}
256
+ {showInput && (
257
+ <Group gap="xs">
258
+ <TextInput
259
+ value={currentValue}
260
+ onChange={(e) => handleChange(e.target.value)}
261
+ size={size}
262
+ disabled={disabled}
263
+ readOnly={isReadOnly}
264
+ style={{ flex: 1 }}
265
+ leftSection={<ColorSwatch color={currentValue} size={16} />}
266
+ />
267
+ {showEyeDropper && 'EyeDropper' in window && (
268
+ <ActionIcon
269
+ variant="subtle"
270
+ color="gray"
271
+ size={size === 'xs' ? 'sm' : size}
272
+ onClick={handleEyeDropper}
273
+ disabled={disabled || isReadOnly}
274
+ title="Selecionar cor da tela"
275
+ >
276
+ <IconColorPicker size={16} />
277
+ </ActionIcon>
278
+ )}
279
+ </Group>
280
+ )}
281
+ </Stack>
282
+ );
283
+
284
+ // Com popover
285
+ if (withPopover) {
286
+ return (
287
+ <Stack ref={ref} gap={4} className={className} style={{ ...style, width }}>
288
+ {label && (
289
+ <Text size={size} fw={500}>
290
+ {label}
291
+ </Text>
292
+ )}
293
+ {description && (
294
+ <Text size="xs" c="dimmed">
295
+ {description}
296
+ </Text>
297
+ )}
298
+
299
+ <Popover opened={popoverOpened} onChange={setPopoverOpened} position="bottom-start">
300
+ <Popover.Target>
301
+ <Group
302
+ gap="xs"
303
+ style={{
304
+ cursor: disabled || isReadOnly ? 'default' : 'pointer',
305
+ padding: '8px 12px',
306
+ border: '1px solid var(--mantine-color-gray-4)',
307
+ borderRadius: 'var(--mantine-radius-sm)',
308
+ backgroundColor: 'var(--mantine-color-body)',
309
+ }}
310
+ onClick={() => !disabled && !isReadOnly && setPopoverOpened(true)}
311
+ >
312
+ <ColorSwatch color={currentValue} size={24} />
313
+ <Text size={size}>{currentValue}</Text>
314
+ </Group>
315
+ </Popover.Target>
316
+ <Popover.Dropdown>
317
+ {pickerContent}
318
+ </Popover.Dropdown>
319
+ </Popover>
320
+
321
+ {error && typeof error === 'string' && (
322
+ <Text size="xs" c="red">
323
+ {error}
324
+ </Text>
325
+ )}
326
+ </Stack>
327
+ );
328
+ }
329
+
330
+ // Sem popover
331
+ return (
332
+ <Stack ref={ref} gap={4} className={className} style={{ ...style, width }}>
333
+ {label && (
334
+ <Text size={size} fw={500}>
335
+ {label}
336
+ </Text>
337
+ )}
338
+ {description && (
339
+ <Text size="xs" c="dimmed">
340
+ {description}
341
+ </Text>
342
+ )}
343
+
344
+ {pickerContent}
345
+
346
+ {error && typeof error === 'string' && (
347
+ <Text size="xs" c="red">
348
+ {error}
349
+ </Text>
350
+ )}
351
+ </Stack>
352
+ );
353
+ }
354
+ );
355
+
356
+ ArchbaseColorGradientPicker.displayName = 'ArchbaseColorGradientPicker';
357
+
358
+ // ================== Flat Color Picker (apenas swatches) ==================
359
+
360
+ export interface ArchbaseFlatColorPickerProps {
361
+ /** Fonte de dados */
362
+ dataSource?: ArchbaseDataSource<any, any>;
363
+ /** Campo do DataSource */
364
+ dataField?: string;
365
+ /** Valor controlado */
366
+ value?: string;
367
+ /** Valor padrão */
368
+ defaultValue?: string;
369
+ /** Callback de mudança */
370
+ onChange?: (value: string) => void;
371
+ /** Swatches de cores */
372
+ swatches?: string[];
373
+ /** Número de colunas */
374
+ columns?: number;
375
+ /** Tamanho do swatch */
376
+ swatchSize?: number;
377
+ /** Espaçamento entre swatches */
378
+ spacing?: number;
379
+ /** Label */
380
+ label?: string;
381
+ /** Se está desabilitado */
382
+ disabled?: boolean;
383
+ /** Classe CSS */
384
+ className?: string;
385
+ /** Estilo */
386
+ style?: React.CSSProperties;
387
+ }
388
+
389
+ export const ArchbaseFlatColorPicker = forwardRef<HTMLDivElement, ArchbaseFlatColorPickerProps>(
390
+ (
391
+ {
392
+ dataSource,
393
+ dataField,
394
+ value: controlledValue,
395
+ defaultValue,
396
+ onChange,
397
+ swatches = DEFAULT_SWATCHES,
398
+ columns = 5,
399
+ swatchSize = 28,
400
+ spacing = 4,
401
+ label,
402
+ disabled = false,
403
+ className,
404
+ style,
405
+ },
406
+ ref
407
+ ) => {
408
+ const [internalValue, setInternalValue] = useState(defaultValue || swatches[0]);
409
+
410
+ const currentValue = controlledValue !== undefined ? controlledValue : internalValue;
411
+
412
+ const handleChange = useCallback(
413
+ (val: string) => {
414
+ if (dataSource && dataField && !dataSource.isBrowsing()) {
415
+ dataSource.setFieldValue(dataField, val);
416
+ }
417
+
418
+ if (controlledValue === undefined) {
419
+ setInternalValue(val);
420
+ }
421
+
422
+ onChange?.(val);
423
+ },
424
+ [controlledValue, dataField, dataSource, onChange]
425
+ );
426
+
427
+ return (
428
+ <Stack ref={ref} gap={4} className={className} style={style}>
429
+ {label && (
430
+ <Text size="sm" fw={500}>
431
+ {label}
432
+ </Text>
433
+ )}
434
+
435
+ <SimpleGrid cols={columns} spacing={spacing}>
436
+ {swatches.map((swatch) => (
437
+ <ColorSwatch
438
+ key={swatch}
439
+ color={swatch}
440
+ size={swatchSize}
441
+ style={{
442
+ cursor: disabled ? 'default' : 'pointer',
443
+ boxShadow: currentValue.toLowerCase() === swatch.toLowerCase()
444
+ ? `0 0 0 2px var(--mantine-color-blue-5)`
445
+ : undefined,
446
+ }}
447
+ onClick={() => !disabled && handleChange(swatch)}
448
+ >
449
+ {currentValue.toLowerCase() === swatch.toLowerCase() && (
450
+ <IconCheck size={14} color="white" style={{ mixBlendMode: 'difference' }} />
451
+ )}
452
+ </ColorSwatch>
453
+ ))}
454
+ </SimpleGrid>
455
+ </Stack>
456
+ );
457
+ }
458
+ );
459
+
460
+ ArchbaseFlatColorPicker.displayName = 'ArchbaseFlatColorPicker';