@granto-umbrella/umbrella-components 3.0.32 → 3.0.34
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/package.json +4 -5
- package/src/components/atoms/DatePickerInput/DatePicker.styles.ts +75 -75
- package/src/components/atoms/DatePickerInput/DatePickerInput.tsx +154 -154
- package/src/components/atoms/DropDownMenu/DropdownMenu.styles.tsx +106 -106
- package/src/components/atoms/ErrorMessage/ErrorMessage.styles.tsx +6 -6
- package/src/components/atoms/Footer/Footer.styles.tsx +1 -1
- package/src/components/atoms/GenericContainer/GenericContainer.styles.tsx +6 -6
- package/src/components/atoms/Input/Input.tsx +80 -80
- package/src/components/atoms/Input/Input.types.ts +21 -21
- package/src/components/atoms/Label/Label.styles.ts +16 -16
- package/src/components/atoms/Loading/Loading.styles.tsx +7 -5
- package/src/components/atoms/Loading/index.tsx +1 -1
- package/src/components/atoms/LogoContainer/LogoContainer.styles.tsx +4 -4
- package/src/components/atoms/ModalAviso/ModalAviso.styles.tsx +10 -9
- package/src/components/atoms/MultiSelect/index.tsx +1 -1
- package/src/components/atoms/RadioButton/RadioButton.types.ts +9 -9
- package/src/components/atoms/ResendLink/index.tsx +2 -1
- package/src/components/atoms/Select/Select.types.ts +19 -19
- package/src/components/atoms/Skeleton/Skeleton.styles.ts +32 -32
- package/src/components/atoms/Skeleton/Skeleton.tsx +43 -43
- package/src/components/atoms/Skeleton/Skeleton.types.ts +13 -13
- package/src/components/atoms/Subtitle/Subtitle.styles.tsx +21 -21
- package/src/components/atoms/Switch/Switch.styles.ts +59 -59
- package/src/components/atoms/Switch/Switch.types.ts +7 -7
- package/src/components/atoms/TabBar/TabBar.tsx +24 -24
- package/src/components/atoms/TabBar/TabBar.types.ts +11 -11
- package/src/components/atoms/Text/Text.styles.tsx +18 -6
- package/src/components/atoms/Text/Text.tsx +9 -4
- package/src/components/atoms/Text/Text.types.ts +2 -1
- package/src/components/atoms/Textarea/Textarea.types.ts +7 -7
- package/src/components/atoms/Title/Title.styles.tsx +17 -17
- package/src/components/molecules/BannerAjuda/BannerAjuda.types.ts +5 -5
- package/src/components/molecules/ButtonGroup/ButtonGroup.tsx +27 -27
- package/src/components/molecules/CodeInputContainer/CodeInputContainer.tsx +32 -32
- package/src/components/molecules/HighlightsCard/HighlightsCard.tsx +1 -1
- package/src/components/molecules/InsuranceCard/InsuranceCard.styles.tsx +1 -1
- package/src/components/molecules/InsuranceCard/InsuranceCard.tsx +455 -455
- package/src/components/molecules/InsuranceCard/InsuranceCard.types.ts +41 -41
- package/src/components/molecules/ResultsChart/ResultsChart.styles.tsx +26 -26
- package/src/components/molecules/TimeLine/TimeLine.mapper.ts +69 -69
- package/src/components/molecules/TimeLine/TimeLine.styles.ts +154 -154
- package/src/components/molecules/TimeLine/TimeLine.tsx +96 -96
- package/src/components/molecules/TimeLine/TimeLine.types.ts +124 -124
- package/src/components/organisms/AlertDialog/AlertDialog.types.ts +14 -14
- package/src/components/organisms/Navbar/Navbar.styles.tsx +1 -1
- package/src/components/organisms/Navbar/Navbar.tsx +118 -118
- package/src/components/organisms/Navbar/Navbar.types.ts +34 -34
- package/src/components/organisms/TimelineModal/TimelineModal.styles.ts +49 -49
- package/src/components/organisms/TimelineModal/TimelineModal.tsx +49 -49
- package/src/index.ts +157 -157
- package/src/styles/tokens/colors.ts +296 -296
- package/src/styles/tokens/typography.ts +161 -161
- package/src/types/colors.types.ts +21 -21
- package/src/types/sizes.types.ts +4 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@granto-umbrella/umbrella-components",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.34",
|
|
4
4
|
"description": "Umbrella Components for React",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
"bump:major": "npm version major"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@granto-umbrella/umbrella-components": "^2.3.23",
|
|
34
33
|
"@phosphor-icons/react": "^2.1.10",
|
|
35
34
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
36
35
|
"@radix-ui/react-label": "^2.1.3",
|
|
@@ -54,13 +53,13 @@
|
|
|
54
53
|
"tailwind-merge": "^3.2.0"
|
|
55
54
|
},
|
|
56
55
|
"devDependencies": {
|
|
57
|
-
"@chromatic-com/storybook": "
|
|
56
|
+
"@chromatic-com/storybook": "4.1.1",
|
|
58
57
|
"@eslint/js": "^9.17.0",
|
|
59
58
|
"@storybook/addon-actions": "8.4.7",
|
|
60
59
|
"@storybook/addon-essentials": "8.4.7",
|
|
61
60
|
"@storybook/addon-interactions": "8.4.7",
|
|
62
|
-
"@storybook/addon-onboarding": "
|
|
63
|
-
"@storybook/react-vite": "
|
|
61
|
+
"@storybook/addon-onboarding": "9.1.15",
|
|
62
|
+
"@storybook/react-vite": "9.1.15",
|
|
64
63
|
"@testing-library/jest-dom": "^6.6.3",
|
|
65
64
|
"@testing-library/react": "^16.1.0",
|
|
66
65
|
"@testing-library/user-event": "^14.5.2",
|
|
@@ -1,75 +1,75 @@
|
|
|
1
|
-
// DatePicker.styles.ts
|
|
2
|
-
import { createGlobalStyle, styled } from 'styled-components';
|
|
3
|
-
import ReactDatePicker, {
|
|
4
|
-
DatePickerProps,
|
|
5
|
-
registerLocale,
|
|
6
|
-
} from 'react-datepicker';
|
|
7
|
-
import { ptBR } from 'date-fns/locale';
|
|
8
|
-
import {
|
|
9
|
-
semanticColors,
|
|
10
|
-
semanticRadius,
|
|
11
|
-
typographyTokens,
|
|
12
|
-
semanticSizes,
|
|
13
|
-
} from '../../../styles/tokens';
|
|
14
|
-
|
|
15
|
-
// register Portuguese‐Brazil locale
|
|
16
|
-
registerLocale('pt-BR', ptBR);
|
|
17
|
-
|
|
18
|
-
// 1) Global overrides for the popup calendar:
|
|
19
|
-
export const DatePickerGlobalStyles = createGlobalStyle`
|
|
20
|
-
.react-datepicker {
|
|
21
|
-
background: ${semanticColors.base.background};
|
|
22
|
-
border: 1px solid ${semanticColors.neutral[300]};
|
|
23
|
-
border-radius: ${semanticRadius.global.radius.md};
|
|
24
|
-
font-family: inherit;
|
|
25
|
-
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.react-datepicker__header {
|
|
29
|
-
background: ${semanticColors.neutral[100]};
|
|
30
|
-
border-bottom: none;
|
|
31
|
-
border-top-left-radius: ${semanticRadius.global.radius.md};
|
|
32
|
-
border-top-right-radius: ${semanticRadius.global.radius.md};
|
|
33
|
-
padding: ${semanticSizes.global.padding.sm};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
.react-datepicker__current-month {
|
|
37
|
-
font-size: ${typographyTokens.fontSizes.bodyM};
|
|
38
|
-
color: ${semanticColors.base.text};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
.react-datepicker__day-name,
|
|
42
|
-
.react-datepicker__day {
|
|
43
|
-
width: 2rem;
|
|
44
|
-
line-height: 2rem;
|
|
45
|
-
margin: 0.1rem;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.react-datepicker__day--selected {
|
|
49
|
-
background-color: ${semanticColors.branding.surface.enabled};
|
|
50
|
-
color: ${semanticColors.base.background};
|
|
51
|
-
border-radius: 50%;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.react-datepicker__triangle {
|
|
55
|
-
display: none;
|
|
56
|
-
}
|
|
57
|
-
`;
|
|
58
|
-
|
|
59
|
-
// 2) Styled input only:
|
|
60
|
-
export const StyledDatePicker = styled(
|
|
61
|
-
ReactDatePicker as unknown as React.ComponentType<DatePickerProps>
|
|
62
|
-
)`
|
|
63
|
-
width: 100%;
|
|
64
|
-
padding: 0.65rem ${semanticSizes.global.padding.md};
|
|
65
|
-
border: 1px solid ${semanticColors.neutral[300]};
|
|
66
|
-
border-radius: ${semanticRadius.global.radius.md};
|
|
67
|
-
font-size: ${typographyTokens.fontSizes.bodyS};
|
|
68
|
-
color: ${semanticColors.base.text};
|
|
69
|
-
|
|
70
|
-
&:focus {
|
|
71
|
-
outline: none;
|
|
72
|
-
border-color: ${semanticColors.branding.surface.enabled};
|
|
73
|
-
box-shadow: 0 0 0 2px ${semanticColors.branding.surface.enabled}33;
|
|
74
|
-
}
|
|
75
|
-
`;
|
|
1
|
+
// DatePicker.styles.ts
|
|
2
|
+
import { createGlobalStyle, styled } from 'styled-components';
|
|
3
|
+
import ReactDatePicker, {
|
|
4
|
+
DatePickerProps,
|
|
5
|
+
registerLocale,
|
|
6
|
+
} from 'react-datepicker';
|
|
7
|
+
import { ptBR } from 'date-fns/locale';
|
|
8
|
+
import {
|
|
9
|
+
semanticColors,
|
|
10
|
+
semanticRadius,
|
|
11
|
+
typographyTokens,
|
|
12
|
+
semanticSizes,
|
|
13
|
+
} from '../../../styles/tokens';
|
|
14
|
+
|
|
15
|
+
// register Portuguese‐Brazil locale
|
|
16
|
+
registerLocale('pt-BR', ptBR);
|
|
17
|
+
|
|
18
|
+
// 1) Global overrides for the popup calendar:
|
|
19
|
+
export const DatePickerGlobalStyles = createGlobalStyle`
|
|
20
|
+
.react-datepicker {
|
|
21
|
+
background: ${semanticColors.base.background};
|
|
22
|
+
border: 1px solid ${semanticColors.neutral[300]};
|
|
23
|
+
border-radius: ${semanticRadius.global.radius.md};
|
|
24
|
+
font-family: inherit;
|
|
25
|
+
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.react-datepicker__header {
|
|
29
|
+
background: ${semanticColors.neutral[100]};
|
|
30
|
+
border-bottom: none;
|
|
31
|
+
border-top-left-radius: ${semanticRadius.global.radius.md};
|
|
32
|
+
border-top-right-radius: ${semanticRadius.global.radius.md};
|
|
33
|
+
padding: ${semanticSizes.global.padding.sm};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.react-datepicker__current-month {
|
|
37
|
+
font-size: ${typographyTokens.fontSizes.bodyM};
|
|
38
|
+
color: ${semanticColors.base.text};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.react-datepicker__day-name,
|
|
42
|
+
.react-datepicker__day {
|
|
43
|
+
width: 2rem;
|
|
44
|
+
line-height: 2rem;
|
|
45
|
+
margin: 0.1rem;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.react-datepicker__day--selected {
|
|
49
|
+
background-color: ${semanticColors.branding.surface.enabled};
|
|
50
|
+
color: ${semanticColors.base.background};
|
|
51
|
+
border-radius: 50%;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.react-datepicker__triangle {
|
|
55
|
+
display: none;
|
|
56
|
+
}
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
// 2) Styled input only:
|
|
60
|
+
export const StyledDatePicker = styled(
|
|
61
|
+
ReactDatePicker as unknown as React.ComponentType<DatePickerProps>
|
|
62
|
+
)`
|
|
63
|
+
width: 100%;
|
|
64
|
+
padding: 0.65rem ${semanticSizes.global.padding.md};
|
|
65
|
+
border: 1px solid ${semanticColors.neutral[300]};
|
|
66
|
+
border-radius: ${semanticRadius.global.radius.md};
|
|
67
|
+
font-size: ${typographyTokens.fontSizes.bodyS};
|
|
68
|
+
color: ${semanticColors.base.text};
|
|
69
|
+
|
|
70
|
+
&:focus {
|
|
71
|
+
outline: none;
|
|
72
|
+
border-color: ${semanticColors.branding.surface.enabled};
|
|
73
|
+
box-shadow: 0 0 0 2px ${semanticColors.branding.surface.enabled}33;
|
|
74
|
+
}
|
|
75
|
+
`;
|
|
@@ -1,154 +1,154 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import { DatePickerGlobalStyles, StyledDatePicker } from './DatePicker.styles';
|
|
4
|
-
import 'react-datepicker/dist/react-datepicker.css';
|
|
5
|
-
import { parse, isValid, format } from 'date-fns';
|
|
6
|
-
|
|
7
|
-
export interface DatePickerInputProps {
|
|
8
|
-
mode?: 'single' | 'range';
|
|
9
|
-
selected: Date | [Date, Date];
|
|
10
|
-
onChange: (date: Date | [Date, Date]) => void;
|
|
11
|
-
minDate?: Date;
|
|
12
|
-
maxDate?: Date;
|
|
13
|
-
placeholder?: string;
|
|
14
|
-
[key: string]: any;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const DF = 'dd/MM/yyyy';
|
|
18
|
-
|
|
19
|
-
// ---------- helpers de parse/format ----------
|
|
20
|
-
function parseBRDate(s: string): Date | null {
|
|
21
|
-
const d = parse(s, DF, new Date());
|
|
22
|
-
return isValid(d) ? d : null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function maskDateDigits(digits: string) {
|
|
26
|
-
let v = digits.replace(/\D/g, '').slice(0, 8);
|
|
27
|
-
if (v.length >= 5) v = v.replace(/^(\d{2})(\d{2})(\d{0,4}).*/, '$1/$2/$3');
|
|
28
|
-
else if (v.length >= 3) v = v.replace(/^(\d{2})(\d{0,2}).*/, '$1/$2');
|
|
29
|
-
return v;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function autoMaskSingle(input: string) {
|
|
33
|
-
const digits = input.replace(/\D/g, '');
|
|
34
|
-
return maskDateDigits(digits);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function autoMaskRange(input: string) {
|
|
38
|
-
const parts = input.split(/\s*[-–]\s*/);
|
|
39
|
-
const left = autoMaskSingle(parts[0] || '');
|
|
40
|
-
const right = autoMaskSingle(parts[1] || '');
|
|
41
|
-
return right ? `${left} - ${right}` : left;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function tryCommitSingle(value: string): Date | null {
|
|
45
|
-
return parseBRDate(value);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function tryCommitRange(value: string): [Date | null, Date | null] {
|
|
49
|
-
const [a, b] = value.split(/\s*[-–]\s*/);
|
|
50
|
-
return [a ? parseBRDate(a) : null, b ? parseBRDate(b) : null];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Tipo que o StyledDatePicker espera em onChangeRaw
|
|
54
|
-
type RawEvt =
|
|
55
|
-
| React.MouseEvent<HTMLElement>
|
|
56
|
-
| React.KeyboardEvent<HTMLElement>
|
|
57
|
-
| undefined;
|
|
58
|
-
|
|
59
|
-
// Extrai com segurança o input do evento (alvo pode não ser input diretamente)
|
|
60
|
-
function getInputFromRawEvent(e: RawEvt): HTMLInputElement | null {
|
|
61
|
-
const target = e?.target as HTMLElement | undefined;
|
|
62
|
-
if (!target) return null;
|
|
63
|
-
if (target instanceof HTMLInputElement) return target;
|
|
64
|
-
const input = target.closest('input');
|
|
65
|
-
return (input as HTMLInputElement) || null;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export const DatePickerInput: React.FC<DatePickerInputProps> = ({
|
|
69
|
-
mode = 'single',
|
|
70
|
-
selected,
|
|
71
|
-
onChange,
|
|
72
|
-
minDate,
|
|
73
|
-
maxDate,
|
|
74
|
-
placeholder,
|
|
75
|
-
...rest
|
|
76
|
-
}) => {
|
|
77
|
-
// SINGLE
|
|
78
|
-
const handleChangeRawSingle = (e: RawEvt) => {
|
|
79
|
-
const el = getInputFromRawEvent(e);
|
|
80
|
-
if (!el) return;
|
|
81
|
-
el.value = autoMaskSingle(el.value);
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const handleBlurSingle = (e: React.FocusEvent<HTMLInputElement>) => {
|
|
85
|
-
const el = e.target;
|
|
86
|
-
const d = tryCommitSingle(el.value);
|
|
87
|
-
if (d) {
|
|
88
|
-
el.value = format(d, DF);
|
|
89
|
-
onChange(d);
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
// RANGE
|
|
94
|
-
const handleChangeRawRange = (e: RawEvt) => {
|
|
95
|
-
const el = getInputFromRawEvent(e);
|
|
96
|
-
if (!el) return;
|
|
97
|
-
el.value = autoMaskRange(el.value);
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const handleBlurRange = (e: React.FocusEvent<HTMLInputElement>) => {
|
|
101
|
-
const el = e.target;
|
|
102
|
-
const [d1, d2] = tryCommitRange(el.value);
|
|
103
|
-
|
|
104
|
-
if (d1) {
|
|
105
|
-
const oldRight = Array.isArray(selected) ? selected[1] : undefined;
|
|
106
|
-
const payload: [Date, Date] = [d1, (d2 || (oldRight as Date)) as Date];
|
|
107
|
-
const left = format(d1, DF);
|
|
108
|
-
const right = d2 ? format(d2, DF) : '';
|
|
109
|
-
el.value = right ? `${left} - ${right}` : left;
|
|
110
|
-
onChange(payload);
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
return (
|
|
115
|
-
<>
|
|
116
|
-
<DatePickerGlobalStyles />
|
|
117
|
-
|
|
118
|
-
{mode === 'range' ? (
|
|
119
|
-
<StyledDatePicker
|
|
120
|
-
locale="pt-BR"
|
|
121
|
-
selectsRange
|
|
122
|
-
startDate={(selected as [Date, Date])[0] || undefined}
|
|
123
|
-
endDate={(selected as [Date, Date])[1] || undefined}
|
|
124
|
-
selected={(selected as [Date, Date])[0] || undefined}
|
|
125
|
-
onChange={(d) => onChange(d as [Date, Date])}
|
|
126
|
-
minDate={minDate}
|
|
127
|
-
maxDate={maxDate}
|
|
128
|
-
placeholderText={placeholder}
|
|
129
|
-
dateFormat={DF}
|
|
130
|
-
onChangeRaw={handleChangeRawRange} // << tipo compatível
|
|
131
|
-
onBlur={handleBlurRange}
|
|
132
|
-
{...rest}
|
|
133
|
-
/>
|
|
134
|
-
) : (
|
|
135
|
-
<StyledDatePicker
|
|
136
|
-
locale="pt-BR"
|
|
137
|
-
selected={
|
|
138
|
-
Array.isArray(selected)
|
|
139
|
-
? selected[0]
|
|
140
|
-
: (selected as Date) || undefined
|
|
141
|
-
}
|
|
142
|
-
onChange={(d) => onChange(d as Date)}
|
|
143
|
-
minDate={minDate}
|
|
144
|
-
maxDate={maxDate}
|
|
145
|
-
placeholderText={placeholder}
|
|
146
|
-
dateFormat={DF}
|
|
147
|
-
onChangeRaw={handleChangeRawSingle} // << tipo compatível
|
|
148
|
-
onBlur={handleBlurSingle}
|
|
149
|
-
{...rest}
|
|
150
|
-
/>
|
|
151
|
-
)}
|
|
152
|
-
</>
|
|
153
|
-
);
|
|
154
|
-
};
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { DatePickerGlobalStyles, StyledDatePicker } from './DatePicker.styles';
|
|
4
|
+
import 'react-datepicker/dist/react-datepicker.css';
|
|
5
|
+
import { parse, isValid, format } from 'date-fns';
|
|
6
|
+
|
|
7
|
+
export interface DatePickerInputProps {
|
|
8
|
+
mode?: 'single' | 'range';
|
|
9
|
+
selected: Date | [Date, Date];
|
|
10
|
+
onChange: (date: Date | [Date, Date]) => void;
|
|
11
|
+
minDate?: Date;
|
|
12
|
+
maxDate?: Date;
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
[key: string]: any;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const DF = 'dd/MM/yyyy';
|
|
18
|
+
|
|
19
|
+
// ---------- helpers de parse/format ----------
|
|
20
|
+
function parseBRDate(s: string): Date | null {
|
|
21
|
+
const d = parse(s, DF, new Date());
|
|
22
|
+
return isValid(d) ? d : null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function maskDateDigits(digits: string) {
|
|
26
|
+
let v = digits.replace(/\D/g, '').slice(0, 8);
|
|
27
|
+
if (v.length >= 5) v = v.replace(/^(\d{2})(\d{2})(\d{0,4}).*/, '$1/$2/$3');
|
|
28
|
+
else if (v.length >= 3) v = v.replace(/^(\d{2})(\d{0,2}).*/, '$1/$2');
|
|
29
|
+
return v;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function autoMaskSingle(input: string) {
|
|
33
|
+
const digits = input.replace(/\D/g, '');
|
|
34
|
+
return maskDateDigits(digits);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function autoMaskRange(input: string) {
|
|
38
|
+
const parts = input.split(/\s*[-–]\s*/);
|
|
39
|
+
const left = autoMaskSingle(parts[0] || '');
|
|
40
|
+
const right = autoMaskSingle(parts[1] || '');
|
|
41
|
+
return right ? `${left} - ${right}` : left;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function tryCommitSingle(value: string): Date | null {
|
|
45
|
+
return parseBRDate(value);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function tryCommitRange(value: string): [Date | null, Date | null] {
|
|
49
|
+
const [a, b] = value.split(/\s*[-–]\s*/);
|
|
50
|
+
return [a ? parseBRDate(a) : null, b ? parseBRDate(b) : null];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Tipo que o StyledDatePicker espera em onChangeRaw
|
|
54
|
+
type RawEvt =
|
|
55
|
+
| React.MouseEvent<HTMLElement>
|
|
56
|
+
| React.KeyboardEvent<HTMLElement>
|
|
57
|
+
| undefined;
|
|
58
|
+
|
|
59
|
+
// Extrai com segurança o input do evento (alvo pode não ser input diretamente)
|
|
60
|
+
function getInputFromRawEvent(e: RawEvt): HTMLInputElement | null {
|
|
61
|
+
const target = e?.target as HTMLElement | undefined;
|
|
62
|
+
if (!target) return null;
|
|
63
|
+
if (target instanceof HTMLInputElement) return target;
|
|
64
|
+
const input = target.closest('input');
|
|
65
|
+
return (input as HTMLInputElement) || null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const DatePickerInput: React.FC<DatePickerInputProps> = ({
|
|
69
|
+
mode = 'single',
|
|
70
|
+
selected,
|
|
71
|
+
onChange,
|
|
72
|
+
minDate,
|
|
73
|
+
maxDate,
|
|
74
|
+
placeholder,
|
|
75
|
+
...rest
|
|
76
|
+
}) => {
|
|
77
|
+
// SINGLE
|
|
78
|
+
const handleChangeRawSingle = (e: RawEvt) => {
|
|
79
|
+
const el = getInputFromRawEvent(e);
|
|
80
|
+
if (!el) return;
|
|
81
|
+
el.value = autoMaskSingle(el.value);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const handleBlurSingle = (e: React.FocusEvent<HTMLInputElement>) => {
|
|
85
|
+
const el = e.target;
|
|
86
|
+
const d = tryCommitSingle(el.value);
|
|
87
|
+
if (d) {
|
|
88
|
+
el.value = format(d, DF);
|
|
89
|
+
onChange(d);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// RANGE
|
|
94
|
+
const handleChangeRawRange = (e: RawEvt) => {
|
|
95
|
+
const el = getInputFromRawEvent(e);
|
|
96
|
+
if (!el) return;
|
|
97
|
+
el.value = autoMaskRange(el.value);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const handleBlurRange = (e: React.FocusEvent<HTMLInputElement>) => {
|
|
101
|
+
const el = e.target;
|
|
102
|
+
const [d1, d2] = tryCommitRange(el.value);
|
|
103
|
+
|
|
104
|
+
if (d1) {
|
|
105
|
+
const oldRight = Array.isArray(selected) ? selected[1] : undefined;
|
|
106
|
+
const payload: [Date, Date] = [d1, (d2 || (oldRight as Date)) as Date];
|
|
107
|
+
const left = format(d1, DF);
|
|
108
|
+
const right = d2 ? format(d2, DF) : '';
|
|
109
|
+
el.value = right ? `${left} - ${right}` : left;
|
|
110
|
+
onChange(payload);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<>
|
|
116
|
+
<DatePickerGlobalStyles />
|
|
117
|
+
|
|
118
|
+
{mode === 'range' ? (
|
|
119
|
+
<StyledDatePicker
|
|
120
|
+
locale="pt-BR"
|
|
121
|
+
selectsRange
|
|
122
|
+
startDate={(selected as [Date, Date])[0] || undefined}
|
|
123
|
+
endDate={(selected as [Date, Date])[1] || undefined}
|
|
124
|
+
selected={(selected as [Date, Date])[0] || undefined}
|
|
125
|
+
onChange={(d) => onChange(d as [Date, Date])}
|
|
126
|
+
minDate={minDate}
|
|
127
|
+
maxDate={maxDate}
|
|
128
|
+
placeholderText={placeholder}
|
|
129
|
+
dateFormat={DF}
|
|
130
|
+
onChangeRaw={handleChangeRawRange} // << tipo compatível
|
|
131
|
+
onBlur={handleBlurRange}
|
|
132
|
+
{...rest}
|
|
133
|
+
/>
|
|
134
|
+
) : (
|
|
135
|
+
<StyledDatePicker
|
|
136
|
+
locale="pt-BR"
|
|
137
|
+
selected={
|
|
138
|
+
Array.isArray(selected)
|
|
139
|
+
? selected[0]
|
|
140
|
+
: (selected as Date) || undefined
|
|
141
|
+
}
|
|
142
|
+
onChange={(d) => onChange(d as Date)}
|
|
143
|
+
minDate={minDate}
|
|
144
|
+
maxDate={maxDate}
|
|
145
|
+
placeholderText={placeholder}
|
|
146
|
+
dateFormat={DF}
|
|
147
|
+
onChangeRaw={handleChangeRawSingle} // << tipo compatível
|
|
148
|
+
onBlur={handleBlurSingle}
|
|
149
|
+
{...rest}
|
|
150
|
+
/>
|
|
151
|
+
)}
|
|
152
|
+
</>
|
|
153
|
+
);
|
|
154
|
+
};
|