@archbase/components 3.0.25 → 4.0.0
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
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
PinInput,
|
|
4
|
+
PinInputProps,
|
|
5
|
+
Stack,
|
|
6
|
+
Group,
|
|
7
|
+
Text,
|
|
8
|
+
Button,
|
|
9
|
+
Box,
|
|
10
|
+
} from '@mantine/core';
|
|
11
|
+
import { IconRefresh } from '@tabler/icons-react';
|
|
12
|
+
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// Types
|
|
15
|
+
// =============================================================================
|
|
16
|
+
|
|
17
|
+
export interface ArchbaseOTPInputProps extends Omit<PinInputProps, 'value' | 'onChange' | 'error'> {
|
|
18
|
+
/** Número de dígitos (padrão: 6) */
|
|
19
|
+
length?: number;
|
|
20
|
+
/** Valor atual */
|
|
21
|
+
value?: string;
|
|
22
|
+
/** Callback ao mudar */
|
|
23
|
+
onChange?: (value: string) => void;
|
|
24
|
+
/** Callback ao completar */
|
|
25
|
+
onComplete?: (value: string) => void;
|
|
26
|
+
/** Mostrar botão de reenviar */
|
|
27
|
+
showResend?: boolean;
|
|
28
|
+
/** Callback ao reenviar */
|
|
29
|
+
onResend?: () => void;
|
|
30
|
+
/** Tempo de espera para reenviar (segundos) */
|
|
31
|
+
resendCooldown?: number;
|
|
32
|
+
/** Texto do botão reenviar */
|
|
33
|
+
resendText?: string;
|
|
34
|
+
/** Erro */
|
|
35
|
+
errorMessage?: string;
|
|
36
|
+
/** Máscara */
|
|
37
|
+
mask?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// =============================================================================
|
|
41
|
+
// ArchbaseOTPInput Component
|
|
42
|
+
// =============================================================================
|
|
43
|
+
|
|
44
|
+
export function ArchbaseOTPInput({
|
|
45
|
+
length = 6,
|
|
46
|
+
value = '',
|
|
47
|
+
onChange,
|
|
48
|
+
onComplete,
|
|
49
|
+
showResend = false,
|
|
50
|
+
onResend,
|
|
51
|
+
resendCooldown = 60,
|
|
52
|
+
resendText = 'Reenviar código',
|
|
53
|
+
errorMessage,
|
|
54
|
+
mask = false,
|
|
55
|
+
...props
|
|
56
|
+
}: ArchbaseOTPInputProps) {
|
|
57
|
+
const [countdown, setCountdown] = useState(0);
|
|
58
|
+
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (countdown > 0) {
|
|
61
|
+
const timer = setTimeout(() => setCountdown(countdown - 1), 1000);
|
|
62
|
+
return () => clearTimeout(timer);
|
|
63
|
+
}
|
|
64
|
+
}, [countdown]);
|
|
65
|
+
|
|
66
|
+
const handleChange = useCallback(
|
|
67
|
+
(newValue: string) => {
|
|
68
|
+
onChange?.(newValue);
|
|
69
|
+
if (newValue.length === length) {
|
|
70
|
+
onComplete?.(newValue);
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
[onChange, onComplete, length]
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const handleResend = useCallback(() => {
|
|
77
|
+
onResend?.();
|
|
78
|
+
setCountdown(resendCooldown);
|
|
79
|
+
}, [onResend, resendCooldown]);
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<Stack gap="sm" align="center">
|
|
83
|
+
<PinInput
|
|
84
|
+
{...props}
|
|
85
|
+
length={length}
|
|
86
|
+
value={value}
|
|
87
|
+
onChange={handleChange}
|
|
88
|
+
type={mask ? 'number' : 'number'}
|
|
89
|
+
mask={mask}
|
|
90
|
+
error={!!errorMessage}
|
|
91
|
+
/>
|
|
92
|
+
|
|
93
|
+
{errorMessage && (
|
|
94
|
+
<Text size="sm" c="red">
|
|
95
|
+
{errorMessage}
|
|
96
|
+
</Text>
|
|
97
|
+
)}
|
|
98
|
+
|
|
99
|
+
{showResend && (
|
|
100
|
+
<Group gap="xs">
|
|
101
|
+
{countdown > 0 ? (
|
|
102
|
+
<Text size="sm" c="dimmed">
|
|
103
|
+
Reenviar em {countdown}s
|
|
104
|
+
</Text>
|
|
105
|
+
) : (
|
|
106
|
+
<Button
|
|
107
|
+
variant="subtle"
|
|
108
|
+
size="xs"
|
|
109
|
+
leftSection={<IconRefresh size={14} />}
|
|
110
|
+
onClick={handleResend}
|
|
111
|
+
>
|
|
112
|
+
{resendText}
|
|
113
|
+
</Button>
|
|
114
|
+
)}
|
|
115
|
+
</Group>
|
|
116
|
+
)}
|
|
117
|
+
</Stack>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// =============================================================================
|
|
122
|
+
// Convenience Components
|
|
123
|
+
// =============================================================================
|
|
124
|
+
|
|
125
|
+
export function ArchbaseOTP4Input(props: Omit<ArchbaseOTPInputProps, 'length'>) {
|
|
126
|
+
return <ArchbaseOTPInput length={4} {...props} />;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function ArchbaseOTP6Input(props: Omit<ArchbaseOTPInputProps, 'length'>) {
|
|
130
|
+
return <ArchbaseOTPInput length={6} {...props} />;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export default ArchbaseOTPInput;
|
|
@@ -185,7 +185,7 @@ export function ArchbaseOperatingHoursEditor({
|
|
|
185
185
|
<Group justify='space-between'>
|
|
186
186
|
<Group wrap='nowrap'>
|
|
187
187
|
<Text fw={700} size={"12px"}>{oh.days.map((day) => daysOfWeekTranslate.find((d) => d.value === day)?.label || 'Dia desconhecido').join(',')}</Text>
|
|
188
|
-
<Text size={"12px"} fw={700}
|
|
188
|
+
<Text size={"12px"} fw={700} c="blue">{oh.timeRange}</Text>
|
|
189
189
|
</Group>
|
|
190
190
|
<ActionIcon
|
|
191
191
|
color="red"
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArchbasePhoneInput — input de telefone internacional integrado ao DataSource (v1/v2)
|
|
3
|
+
* com suporte a validação, estado controlado e seleção de país.
|
|
4
|
+
* @status stable
|
|
5
|
+
*/
|
|
6
|
+
import { Input, MantineSize, useMantineColorScheme, useMantineTheme } from '@mantine/core';
|
|
7
|
+
import { useForceUpdate } from '@mantine/hooks';
|
|
8
|
+
import type { CSSProperties, FocusEventHandler } from 'react';
|
|
9
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
10
|
+
import PhoneInput from 'react-phone-number-input';
|
|
11
|
+
import 'react-phone-number-input/style.css';
|
|
12
|
+
import type { DataSourceEvent, IArchbaseDataSourceBase } from '@archbase/data';
|
|
13
|
+
import { DataSourceEventNames, useArchbaseDidUpdate, useArchbaseV1V2Compatibility } from '@archbase/data';
|
|
14
|
+
import { useValidationErrors } from '@archbase/core';
|
|
15
|
+
import type { CountryCode } from 'libphonenumber-js';
|
|
16
|
+
|
|
17
|
+
export interface ArchbasePhoneInputProps<T, ID> {
|
|
18
|
+
/** Fonte de dados onde será atribuído o valor do phone input (V1 ou V2) */
|
|
19
|
+
dataSource?: IArchbaseDataSourceBase<T>;
|
|
20
|
+
/** Campo onde deverá ser atribuído o valor do phone input na fonte de dados */
|
|
21
|
+
dataField?: string;
|
|
22
|
+
/** Valor controlado do phone input (modo standalone) */
|
|
23
|
+
value?: string;
|
|
24
|
+
/** Valor padrão do phone input (modo standalone) */
|
|
25
|
+
defaultValue?: string;
|
|
26
|
+
/** País padrão selecionado no seletor de bandeiras */
|
|
27
|
+
defaultCountry?: CountryCode;
|
|
28
|
+
/** Se true, exibe o número em formato internacional (+55 ...) */
|
|
29
|
+
international?: boolean;
|
|
30
|
+
/** Título/label do phone input */
|
|
31
|
+
label?: string;
|
|
32
|
+
/** Descrição exibida abaixo do label */
|
|
33
|
+
description?: string;
|
|
34
|
+
/** Mensagem de erro a ser exibida */
|
|
35
|
+
error?: string;
|
|
36
|
+
/** Texto de placeholder do phone input */
|
|
37
|
+
placeholder?: string;
|
|
38
|
+
/** Indicador se o preenchimento é obrigatório */
|
|
39
|
+
required?: boolean;
|
|
40
|
+
/** Indicador se o phone input está desabilitado */
|
|
41
|
+
disabled?: boolean;
|
|
42
|
+
/** Indicador se o phone input é somente leitura. Obs: usado em conjunto com o status da fonte de dados */
|
|
43
|
+
readOnly?: boolean;
|
|
44
|
+
/** Evento quando o valor do phone input é alterado */
|
|
45
|
+
onChangeValue?: (value: string | undefined) => void;
|
|
46
|
+
/** Evento quando o phone input recebe o foco */
|
|
47
|
+
onFocusEnter?: FocusEventHandler<HTMLInputElement> | undefined;
|
|
48
|
+
/** Evento quando o foco sai do phone input */
|
|
49
|
+
onFocusExit?: FocusEventHandler<HTMLInputElement> | undefined;
|
|
50
|
+
/** Referência para o componente interno */
|
|
51
|
+
innerRef?: React.RefObject<HTMLInputElement> | undefined;
|
|
52
|
+
/** Estilo CSS customizado aplicado ao wrapper */
|
|
53
|
+
style?: CSSProperties;
|
|
54
|
+
/** Classe CSS customizada aplicada ao wrapper */
|
|
55
|
+
className?: string;
|
|
56
|
+
/** Largura do componente */
|
|
57
|
+
width?: string | number;
|
|
58
|
+
/** Tamanho do componente seguindo o padrão Mantine */
|
|
59
|
+
size?: MantineSize;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function ArchbasePhoneInput<T, ID>({
|
|
63
|
+
dataSource,
|
|
64
|
+
dataField,
|
|
65
|
+
value: valueProp,
|
|
66
|
+
defaultValue = '',
|
|
67
|
+
defaultCountry = 'BR',
|
|
68
|
+
international = true,
|
|
69
|
+
label,
|
|
70
|
+
description,
|
|
71
|
+
error,
|
|
72
|
+
placeholder,
|
|
73
|
+
required,
|
|
74
|
+
disabled = false,
|
|
75
|
+
readOnly = false,
|
|
76
|
+
onChangeValue,
|
|
77
|
+
onFocusEnter,
|
|
78
|
+
onFocusExit,
|
|
79
|
+
innerRef,
|
|
80
|
+
style,
|
|
81
|
+
className,
|
|
82
|
+
width,
|
|
83
|
+
size = 'sm',
|
|
84
|
+
}: ArchbasePhoneInputProps<T, ID>) {
|
|
85
|
+
// Hook de compatibilidade V1/V2
|
|
86
|
+
const v1v2Compatibility = useArchbaseV1V2Compatibility<string>(
|
|
87
|
+
'ArchbasePhoneInput',
|
|
88
|
+
dataSource,
|
|
89
|
+
dataField,
|
|
90
|
+
'',
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const innerComponentRef = innerRef || useRef<any>(null);
|
|
94
|
+
const [currentValue, setCurrentValue] = useState<string>(valueProp || defaultValue || '');
|
|
95
|
+
const forceUpdate = useForceUpdate();
|
|
96
|
+
const [internalError, setInternalError] = useState<string | undefined>(error);
|
|
97
|
+
const theme = useMantineTheme();
|
|
98
|
+
const { colorScheme } = useMantineColorScheme();
|
|
99
|
+
|
|
100
|
+
// Contexto de validação (opcional - pode não existir)
|
|
101
|
+
const validationContext = useValidationErrors();
|
|
102
|
+
|
|
103
|
+
// Chave única para o field
|
|
104
|
+
const fieldKey = `${dataField}`;
|
|
105
|
+
|
|
106
|
+
// Recuperar erro do contexto se existir
|
|
107
|
+
const contextError = validationContext?.getError(fieldKey);
|
|
108
|
+
|
|
109
|
+
// Detecta automaticamente se é DataSource V2
|
|
110
|
+
const isDataSourceV2 =
|
|
111
|
+
dataSource &&
|
|
112
|
+
('appendToFieldArray' in dataSource || 'updateFieldArrayItem' in dataSource);
|
|
113
|
+
|
|
114
|
+
// Para V2: estado otimizado (sem re-renders desnecessários)
|
|
115
|
+
const [v2Value, setV2Value] = useState<string>('');
|
|
116
|
+
|
|
117
|
+
// Apenas atualizar se o prop error vier definido
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
if (error !== undefined && error !== internalError) {
|
|
120
|
+
setInternalError(error);
|
|
121
|
+
}
|
|
122
|
+
}, [error]);
|
|
123
|
+
|
|
124
|
+
const loadDataSourceFieldValue = () => {
|
|
125
|
+
let initialValue: any = valueProp || defaultValue || '';
|
|
126
|
+
|
|
127
|
+
if (dataSource && dataField) {
|
|
128
|
+
initialValue = dataSource.getFieldValue(dataField);
|
|
129
|
+
if (!initialValue) {
|
|
130
|
+
initialValue = '';
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (isDataSourceV2) {
|
|
135
|
+
setV2Value(initialValue);
|
|
136
|
+
} else {
|
|
137
|
+
setCurrentValue(initialValue);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const fieldChangedListener = useCallback(() => {
|
|
142
|
+
loadDataSourceFieldValue();
|
|
143
|
+
}, []);
|
|
144
|
+
|
|
145
|
+
const dataSourceEvent = useCallback(
|
|
146
|
+
(event: DataSourceEvent<T>) => {
|
|
147
|
+
if (dataSource && dataField) {
|
|
148
|
+
if (
|
|
149
|
+
event.type === DataSourceEventNames.dataChanged ||
|
|
150
|
+
event.type === DataSourceEventNames.recordChanged ||
|
|
151
|
+
event.type === DataSourceEventNames.afterScroll ||
|
|
152
|
+
event.type === DataSourceEventNames.afterCancel ||
|
|
153
|
+
event.type === DataSourceEventNames.afterEdit
|
|
154
|
+
) {
|
|
155
|
+
loadDataSourceFieldValue();
|
|
156
|
+
if (!isDataSourceV2) {
|
|
157
|
+
forceUpdate();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (
|
|
162
|
+
event.type === DataSourceEventNames.onFieldError &&
|
|
163
|
+
event.fieldName === dataField
|
|
164
|
+
) {
|
|
165
|
+
setInternalError(event.error);
|
|
166
|
+
validationContext?.setError(fieldKey, event.error);
|
|
167
|
+
if (!isDataSourceV2) {
|
|
168
|
+
forceUpdate();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
[isDataSourceV2, dataSource, dataField, validationContext, fieldKey],
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
// Ref para manter callback sempre atualizado (corrige problema de closure desatualizada)
|
|
177
|
+
const dataSourceEventRef = useRef(dataSourceEvent);
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
dataSourceEventRef.current = dataSourceEvent;
|
|
180
|
+
}, [dataSourceEvent]);
|
|
181
|
+
|
|
182
|
+
// Wrapper estável que delega para ref - nunca muda, então o listener permanece consistente
|
|
183
|
+
const stableDataSourceEvent = useCallback((event: DataSourceEvent<T>) => {
|
|
184
|
+
dataSourceEventRef.current(event);
|
|
185
|
+
}, []);
|
|
186
|
+
|
|
187
|
+
// Registrar listeners com cleanup apropriado
|
|
188
|
+
useEffect(() => {
|
|
189
|
+
loadDataSourceFieldValue();
|
|
190
|
+
if (dataSource && dataField) {
|
|
191
|
+
const hasFieldListener =
|
|
192
|
+
typeof (dataSource as any).addFieldChangeListener === 'function';
|
|
193
|
+
dataSource.addListener(stableDataSourceEvent);
|
|
194
|
+
if (hasFieldListener) {
|
|
195
|
+
(dataSource as any).addFieldChangeListener(dataField, fieldChangedListener);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return () => {
|
|
199
|
+
dataSource.removeListener(stableDataSourceEvent);
|
|
200
|
+
if (hasFieldListener) {
|
|
201
|
+
(dataSource as any).removeFieldChangeListener(dataField, fieldChangedListener);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}, [dataSource, dataField, stableDataSourceEvent, fieldChangedListener]);
|
|
206
|
+
|
|
207
|
+
useArchbaseDidUpdate(() => {
|
|
208
|
+
loadDataSourceFieldValue();
|
|
209
|
+
}, []);
|
|
210
|
+
|
|
211
|
+
const handleChange = (newValue: string | undefined) => {
|
|
212
|
+
const changedValue = newValue || '';
|
|
213
|
+
|
|
214
|
+
// Limpa erro quando usuário edita o campo
|
|
215
|
+
const hasError = internalError || contextError;
|
|
216
|
+
if (hasError) {
|
|
217
|
+
setInternalError(undefined);
|
|
218
|
+
validationContext?.clearError(fieldKey);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (isDataSourceV2) {
|
|
222
|
+
setV2Value(changedValue);
|
|
223
|
+
if (
|
|
224
|
+
dataSource &&
|
|
225
|
+
!dataSource.isBrowsing() &&
|
|
226
|
+
dataField &&
|
|
227
|
+
dataSource.getFieldValue(dataField) !== changedValue
|
|
228
|
+
) {
|
|
229
|
+
dataSource.setFieldValue(dataField, changedValue);
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
setCurrentValue(changedValue);
|
|
233
|
+
if (
|
|
234
|
+
dataSource &&
|
|
235
|
+
!dataSource.isBrowsing() &&
|
|
236
|
+
dataField &&
|
|
237
|
+
dataSource.getFieldValue(dataField) !== changedValue
|
|
238
|
+
) {
|
|
239
|
+
dataSource.setFieldValue(dataField, changedValue);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (onChangeValue) {
|
|
244
|
+
onChangeValue(newValue);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const handleOnFocusExit = (event: React.FocusEvent<HTMLInputElement>) => {
|
|
249
|
+
if (onFocusExit) {
|
|
250
|
+
onFocusExit(event);
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const handleOnFocusEnter = (event: React.FocusEvent<HTMLInputElement>) => {
|
|
255
|
+
if (onFocusEnter) {
|
|
256
|
+
onFocusEnter(event);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const isReadOnly = (): boolean => {
|
|
261
|
+
return readOnly || v1v2Compatibility.isReadOnly;
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// Erro a ser exibido: local ou do contexto
|
|
265
|
+
const displayError = internalError || contextError;
|
|
266
|
+
|
|
267
|
+
const isDark = colorScheme === 'dark';
|
|
268
|
+
|
|
269
|
+
// Mantine-compatible sizing
|
|
270
|
+
const inputHeight =
|
|
271
|
+
size === 'xs'
|
|
272
|
+
? 30
|
|
273
|
+
: size === 'sm'
|
|
274
|
+
? 36
|
|
275
|
+
: size === 'md'
|
|
276
|
+
? 42
|
|
277
|
+
: size === 'lg'
|
|
278
|
+
? 50
|
|
279
|
+
: size === 'xl'
|
|
280
|
+
? 60
|
|
281
|
+
: 36;
|
|
282
|
+
|
|
283
|
+
const fontSize =
|
|
284
|
+
size === 'xs'
|
|
285
|
+
? 12
|
|
286
|
+
: size === 'sm'
|
|
287
|
+
? 14
|
|
288
|
+
: size === 'md'
|
|
289
|
+
? 16
|
|
290
|
+
: size === 'lg'
|
|
291
|
+
? 18
|
|
292
|
+
: size === 'xl'
|
|
293
|
+
? 20
|
|
294
|
+
: 14;
|
|
295
|
+
|
|
296
|
+
return (
|
|
297
|
+
<Input.Wrapper
|
|
298
|
+
label={label}
|
|
299
|
+
description={description}
|
|
300
|
+
error={displayError}
|
|
301
|
+
required={required}
|
|
302
|
+
size={size}
|
|
303
|
+
style={{ width, ...style }}
|
|
304
|
+
className={className}
|
|
305
|
+
>
|
|
306
|
+
<PhoneInput
|
|
307
|
+
ref={innerComponentRef}
|
|
308
|
+
international={international}
|
|
309
|
+
defaultCountry={defaultCountry}
|
|
310
|
+
value={isDataSourceV2 ? v2Value : currentValue}
|
|
311
|
+
onChange={handleChange}
|
|
312
|
+
onFocus={handleOnFocusEnter}
|
|
313
|
+
onBlur={handleOnFocusExit}
|
|
314
|
+
disabled={disabled}
|
|
315
|
+
readOnly={isReadOnly()}
|
|
316
|
+
placeholder={placeholder}
|
|
317
|
+
style={{
|
|
318
|
+
'--PhoneInputCountryFlag-height': '1em',
|
|
319
|
+
'--PhoneInputCountrySelectArrow-color': isDark
|
|
320
|
+
? theme.colors.dark[1]
|
|
321
|
+
: theme.colors.gray[6],
|
|
322
|
+
'--PhoneInput-color--focus': theme.colors[theme.primaryColor][isDark ? 4 : 6],
|
|
323
|
+
} as React.CSSProperties}
|
|
324
|
+
numberInputProps={{
|
|
325
|
+
style: {
|
|
326
|
+
height: inputHeight,
|
|
327
|
+
fontSize,
|
|
328
|
+
backgroundColor: isDark ? theme.colors.dark[6] : theme.white,
|
|
329
|
+
color: isDark ? theme.colors.dark[0] : theme.black,
|
|
330
|
+
border: `1px solid ${
|
|
331
|
+
displayError
|
|
332
|
+
? theme.colors.red[isDark ? 4 : 6]
|
|
333
|
+
: isDark
|
|
334
|
+
? theme.colors.dark[4]
|
|
335
|
+
: theme.colors.gray[4]
|
|
336
|
+
}`,
|
|
337
|
+
borderRadius: theme.radius.sm,
|
|
338
|
+
paddingLeft: 8,
|
|
339
|
+
paddingRight: 8,
|
|
340
|
+
outline: 'none',
|
|
341
|
+
width: '100%',
|
|
342
|
+
},
|
|
343
|
+
}}
|
|
344
|
+
countrySelectProps={{
|
|
345
|
+
style: {
|
|
346
|
+
backgroundColor: isDark ? theme.colors.dark[6] : theme.white,
|
|
347
|
+
color: isDark ? theme.colors.dark[0] : theme.black,
|
|
348
|
+
},
|
|
349
|
+
}}
|
|
350
|
+
/>
|
|
351
|
+
</Input.Wrapper>
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
ArchbasePhoneInput.displayName = 'ArchbasePhoneInput';
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
RangeSlider,
|
|
4
|
+
RangeSliderProps,
|
|
5
|
+
Stack,
|
|
6
|
+
Group,
|
|
7
|
+
Text,
|
|
8
|
+
Box,
|
|
9
|
+
} from '@mantine/core';
|
|
10
|
+
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// Types
|
|
13
|
+
// =============================================================================
|
|
14
|
+
|
|
15
|
+
export type RangeValue = [number, number];
|
|
16
|
+
|
|
17
|
+
export interface ArchbaseRangeSliderProps extends Omit<RangeSliderProps, 'value' | 'onChange'> {
|
|
18
|
+
/** Valor atual [min, max] */
|
|
19
|
+
value?: RangeValue;
|
|
20
|
+
/** Callback ao mudar */
|
|
21
|
+
onChange?: (value: RangeValue) => void;
|
|
22
|
+
/** Callback ao soltar o slider */
|
|
23
|
+
onChangeEnd?: (value: RangeValue) => void;
|
|
24
|
+
/** Mostrar valores */
|
|
25
|
+
showValues?: boolean;
|
|
26
|
+
/** Formatador de valores */
|
|
27
|
+
formatValue?: (value: number) => string;
|
|
28
|
+
/** Prefixo dos valores */
|
|
29
|
+
valuePrefix?: string;
|
|
30
|
+
/** Sufixo dos valores */
|
|
31
|
+
valueSuffix?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// =============================================================================
|
|
35
|
+
// ArchbaseRangeSlider Component
|
|
36
|
+
// =============================================================================
|
|
37
|
+
|
|
38
|
+
export function ArchbaseRangeSlider({
|
|
39
|
+
value = [0, 100],
|
|
40
|
+
onChange,
|
|
41
|
+
onChangeEnd,
|
|
42
|
+
showValues = true,
|
|
43
|
+
formatValue,
|
|
44
|
+
valuePrefix = '',
|
|
45
|
+
valueSuffix = '',
|
|
46
|
+
min = 0,
|
|
47
|
+
max = 100,
|
|
48
|
+
...props
|
|
49
|
+
}: ArchbaseRangeSliderProps) {
|
|
50
|
+
const handleChange = useCallback(
|
|
51
|
+
(newValue: RangeValue) => {
|
|
52
|
+
onChange?.(newValue);
|
|
53
|
+
},
|
|
54
|
+
[onChange]
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const handleChangeEnd = useCallback(
|
|
58
|
+
(newValue: RangeValue) => {
|
|
59
|
+
onChangeEnd?.(newValue);
|
|
60
|
+
},
|
|
61
|
+
[onChangeEnd]
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const displayValue = (val: number) => {
|
|
65
|
+
if (formatValue) return formatValue(val);
|
|
66
|
+
return `${valuePrefix}${val}${valueSuffix}`;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<Stack gap="xs">
|
|
71
|
+
<RangeSlider
|
|
72
|
+
{...props}
|
|
73
|
+
min={min}
|
|
74
|
+
max={max}
|
|
75
|
+
value={value}
|
|
76
|
+
onChange={handleChange}
|
|
77
|
+
onChangeEnd={handleChangeEnd}
|
|
78
|
+
/>
|
|
79
|
+
{showValues && (
|
|
80
|
+
<Group justify="space-between">
|
|
81
|
+
<Text size="sm" c="dimmed">
|
|
82
|
+
Min: {displayValue(value[0])}
|
|
83
|
+
</Text>
|
|
84
|
+
<Text size="sm" c="dimmed">
|
|
85
|
+
Max: {displayValue(value[1])}
|
|
86
|
+
</Text>
|
|
87
|
+
</Group>
|
|
88
|
+
)}
|
|
89
|
+
</Stack>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// =============================================================================
|
|
94
|
+
// Convenience Components
|
|
95
|
+
// =============================================================================
|
|
96
|
+
|
|
97
|
+
export function ArchbasePriceRangeSlider(
|
|
98
|
+
props: Omit<ArchbaseRangeSliderProps, 'valuePrefix'>
|
|
99
|
+
) {
|
|
100
|
+
return <ArchbaseRangeSlider valuePrefix="R$ " {...props} />;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function ArchbasePercentRangeSlider(
|
|
104
|
+
props: Omit<ArchbaseRangeSliderProps, 'valueSuffix' | 'min' | 'max'>
|
|
105
|
+
) {
|
|
106
|
+
return <ArchbaseRangeSlider min={0} max={100} valueSuffix="%" {...props} />;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export default ArchbaseRangeSlider;
|