@lets-events/react 7.0.1 → 7.1.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/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +6 -0
- package/dist/index.d.mts +6 -375
- package/dist/index.d.ts +6 -375
- package/dist/index.js +270 -202
- package/dist/index.mjs +234 -165
- package/package.json +1 -1
- package/src/components/Calendar/index.tsx +46 -37
- package/src/components/Calendar/styledComponents.ts +17 -6
- package/src/components/TextField.tsx +16 -13
- package/src/components/TimePicker.tsx +64 -42
- package/src/hooks/useOnClickOutside.tsx +20 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import React, { ComponentProps, useEffect, useState } from "react";
|
|
1
|
+
import React, { ComponentProps, useRef, useEffect, useState } from "react";
|
|
2
|
+
import { useOnClickOutside } from "../../hooks/useOnClickOutside";
|
|
2
3
|
import { DayPicker } from "react-day-picker";
|
|
3
4
|
import { ptBR } from "date-fns/locale";
|
|
4
|
-
import { Dialog as Calendaradix } from "@radix-ui/themes";
|
|
5
5
|
import { parse, isValid, format, addYears } from "date-fns";
|
|
6
6
|
import { Button } from "../Button";
|
|
7
7
|
import { Box } from "../Box";
|
|
@@ -12,16 +12,16 @@ import {
|
|
|
12
12
|
CalendarFooterStyled,
|
|
13
13
|
CalendarStyled,
|
|
14
14
|
DayPickerWrapperStyled,
|
|
15
|
-
|
|
15
|
+
CalendarButtonStyled,
|
|
16
16
|
} from "./styledComponents";
|
|
17
17
|
|
|
18
18
|
export type CalendarProps = ComponentProps<typeof CalendarStyled> & {
|
|
19
19
|
calendarLayout?: "label" | "dropdown" | "dropdown-months" | "dropdown-years";
|
|
20
20
|
selected: Date | undefined;
|
|
21
21
|
setSelected: React.Dispatch<React.SetStateAction<Date | undefined>>;
|
|
22
|
+
position?: "top" | "bottom";
|
|
22
23
|
action?: boolean;
|
|
23
24
|
actionText?: string;
|
|
24
|
-
onAction?: () => void;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
function formatToDateMask(value: string): string {
|
|
@@ -38,16 +38,22 @@ export function Calendar({
|
|
|
38
38
|
calendarLayout,
|
|
39
39
|
selected,
|
|
40
40
|
setSelected,
|
|
41
|
-
|
|
41
|
+
position = "bottom",
|
|
42
42
|
...props
|
|
43
43
|
}: CalendarProps) {
|
|
44
44
|
const [inputValue, setInputValue] = useState("");
|
|
45
|
+
const [showContainer, setShowCalendar] = useState(false);
|
|
45
46
|
|
|
47
|
+
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
48
|
+
|
|
49
|
+
useOnClickOutside(dropdownRef, () => {
|
|
50
|
+
console.warn("Clicked outside");
|
|
51
|
+
setShowCalendar(false);
|
|
52
|
+
});
|
|
46
53
|
const today = new Date();
|
|
47
54
|
const maxDate = addYears(today, 20);
|
|
48
55
|
|
|
49
56
|
useEffect(() => {
|
|
50
|
-
console.log("selected", selected);
|
|
51
57
|
if (selected) {
|
|
52
58
|
setInputValue(format(selected, "dd/MM/yyyy"));
|
|
53
59
|
} else {
|
|
@@ -68,57 +74,60 @@ export function Calendar({
|
|
|
68
74
|
};
|
|
69
75
|
|
|
70
76
|
return (
|
|
71
|
-
<
|
|
72
|
-
<CalendarStyled {...props}>
|
|
73
|
-
<
|
|
77
|
+
<div>
|
|
78
|
+
<CalendarStyled {...props} ref={dropdownRef}>
|
|
79
|
+
<CalendarButtonStyled onClick={() => setShowCalendar((prev) => !prev)}>
|
|
74
80
|
<TextField
|
|
75
81
|
placeholder="00/00/0000"
|
|
76
82
|
type="text"
|
|
77
83
|
value={inputValue}
|
|
78
84
|
onChange={handleInputChange}
|
|
79
85
|
inputMode="numeric"
|
|
86
|
+
textAlign={"right"}
|
|
80
87
|
>
|
|
81
88
|
<TextFieldSlot>
|
|
82
89
|
<Icon name="calendar" size={"xl"} />
|
|
83
90
|
</TextFieldSlot>
|
|
84
91
|
</TextField>
|
|
85
|
-
</
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
<
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
92
|
+
</CalendarButtonStyled>
|
|
93
|
+
{showContainer && (
|
|
94
|
+
<CalendarContentStyled
|
|
95
|
+
style={position === "top" ? { bottom: "110%" } : { top: "110%" }}
|
|
96
|
+
>
|
|
97
|
+
<Box>
|
|
98
|
+
<DayPickerWrapperStyled>
|
|
99
|
+
<DayPicker
|
|
100
|
+
mode="single"
|
|
101
|
+
captionLayout={calendarLayout}
|
|
102
|
+
selected={selected}
|
|
103
|
+
onSelect={setSelected}
|
|
104
|
+
required
|
|
105
|
+
locale={ptBR}
|
|
106
|
+
disabled={{ before: today }}
|
|
107
|
+
startMonth={today}
|
|
108
|
+
endMonth={maxDate}
|
|
109
|
+
/>
|
|
110
|
+
</DayPickerWrapperStyled>
|
|
111
|
+
</Box>
|
|
104
112
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
<Calendaradix.Close>
|
|
113
|
+
{action && (
|
|
114
|
+
<CalendarFooterStyled>
|
|
108
115
|
<Button
|
|
109
116
|
variant="text"
|
|
110
117
|
color="brand"
|
|
111
|
-
onClick={
|
|
118
|
+
onClick={() => {
|
|
119
|
+
setShowCalendar(false);
|
|
120
|
+
}}
|
|
112
121
|
typography="buttonMedium"
|
|
113
122
|
fontWeight="medium"
|
|
114
123
|
>
|
|
115
124
|
{actionText ?? "Aplicar"}
|
|
116
125
|
</Button>
|
|
117
|
-
</
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
126
|
+
</CalendarFooterStyled>
|
|
127
|
+
)}
|
|
128
|
+
</CalendarContentStyled>
|
|
129
|
+
)}
|
|
121
130
|
</CalendarStyled>
|
|
122
|
-
</
|
|
131
|
+
</div>
|
|
123
132
|
);
|
|
124
133
|
}
|
|
@@ -1,18 +1,25 @@
|
|
|
1
|
-
import { Dialog as Calendaradix } from "@radix-ui/themes";
|
|
2
|
-
|
|
3
1
|
import { styled } from "../../styles"
|
|
4
2
|
|
|
5
3
|
export const CalendarStyled = styled('div', {
|
|
6
4
|
fontFamily: '$default',
|
|
7
5
|
lineHeight: '$base',
|
|
8
6
|
fontSize: '$14',
|
|
9
|
-
maxWidth: '200px',
|
|
10
7
|
borderRadius: '$sm',
|
|
8
|
+
position: 'relative',
|
|
11
9
|
})
|
|
12
|
-
export const
|
|
13
|
-
|
|
10
|
+
export const CalendarButtonStyled = styled('button', {
|
|
11
|
+
backgroundColor: 'transparent',
|
|
12
|
+
border: 'none',
|
|
13
|
+
maxWidth: '200px',
|
|
14
|
+
padding: '0',
|
|
15
|
+
'> div > div': {
|
|
16
|
+
paddingLeft: '1rem',
|
|
17
|
+
'input': {
|
|
18
|
+
textAlign: 'right',
|
|
19
|
+
}
|
|
20
|
+
},
|
|
14
21
|
})
|
|
15
|
-
export const CalendarContentStyled = styled(
|
|
22
|
+
export const CalendarContentStyled = styled('div', {
|
|
16
23
|
fontFamily: '$default',
|
|
17
24
|
lineHeight: '$base',
|
|
18
25
|
fontSize: '$14',
|
|
@@ -21,6 +28,10 @@ export const CalendarContentStyled = styled(Calendaradix.Content, {
|
|
|
21
28
|
border: '1px solid $neutral300',
|
|
22
29
|
borderRadius: '$sm',
|
|
23
30
|
boxShadow: '0px 2px 8px 0px $shadow50',
|
|
31
|
+
position: 'absolute',
|
|
32
|
+
left: '0',
|
|
33
|
+
backgroundColor: '$neutral50',
|
|
34
|
+
zIndex: '999999',
|
|
24
35
|
})
|
|
25
36
|
|
|
26
37
|
export const CalendarFooterStyled = styled('div', {
|
|
@@ -157,7 +157,7 @@ export const TextFieldSlotStyled = styled(TextFieldRadix.Slot, {
|
|
|
157
157
|
})
|
|
158
158
|
|
|
159
159
|
export type TextFieldProps = ComponentProps<typeof TextFieldStyled> & {
|
|
160
|
-
addon?:string
|
|
160
|
+
addon?: string
|
|
161
161
|
placeholder?: string
|
|
162
162
|
children?: React.ReactNode
|
|
163
163
|
isValid?: boolean
|
|
@@ -182,14 +182,17 @@ export type TextFieldSlotProps = Omit<ComponentProps<typeof TextFieldStyled>, 'c
|
|
|
182
182
|
const InputAddon = styled(TextStyle, {
|
|
183
183
|
boxSizing: 'border-box',
|
|
184
184
|
border: '1px solid $dark300',
|
|
185
|
-
height: '$40',
|
|
186
|
-
padding: '0',
|
|
185
|
+
height: '$40',
|
|
186
|
+
padding: '0 $12',
|
|
187
187
|
color: '$dark600',
|
|
188
188
|
borderRadius: '$sm 0px 0px $sm',
|
|
189
189
|
borderRightWidth: '0px',
|
|
190
190
|
margin: 'auto 0',
|
|
191
191
|
display: 'flex',
|
|
192
|
-
|
|
192
|
+
flexWrap: 'nowrap',
|
|
193
|
+
alignItems: 'center',
|
|
194
|
+
whiteSpace: 'nowrap',
|
|
195
|
+
lineHeight: 1,
|
|
193
196
|
})
|
|
194
197
|
|
|
195
198
|
export function TextField({
|
|
@@ -204,7 +207,7 @@ export function TextField({
|
|
|
204
207
|
...props
|
|
205
208
|
}: TextFieldProps) {
|
|
206
209
|
return (
|
|
207
|
-
<Flex gap={'0'} css={{width: '100%'}}>
|
|
210
|
+
<Flex gap={'0'} css={{ width: '100%' }}>
|
|
208
211
|
{!!addon && (
|
|
209
212
|
<InputAddon typography="labelSmall">{addon}</InputAddon>
|
|
210
213
|
)}
|
|
@@ -215,18 +218,18 @@ export function TextField({
|
|
|
215
218
|
typography={typography}
|
|
216
219
|
fontWeight={fontWeight}
|
|
217
220
|
textAlign={textAlign}
|
|
218
|
-
style={!!addon ? {borderTopLeftRadius:'0px', borderBottomLeftRadius: '0px'}: undefined}
|
|
221
|
+
style={!!addon ? { borderTopLeftRadius: '0px', borderBottomLeftRadius: '0px' } : undefined}
|
|
219
222
|
{...props}
|
|
220
|
-
|
|
223
|
+
>
|
|
221
224
|
{children}
|
|
222
225
|
{isValid && (
|
|
223
226
|
<TextFieldSlot
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
227
|
+
position='flex-end'
|
|
228
|
+
name={name}
|
|
229
|
+
color={color as TextFieldSlotProps['color']}
|
|
230
|
+
typography={typography}
|
|
231
|
+
fontWeight={fontWeight}
|
|
232
|
+
textAlign={textAlign}
|
|
230
233
|
>
|
|
231
234
|
<Icon name='check' />
|
|
232
235
|
</TextFieldSlot>
|
|
@@ -1,29 +1,39 @@
|
|
|
1
|
-
import React, { useCallback, useState } from "react";
|
|
2
|
-
import { Dialog as TimePickerRadix } from "@radix-ui/themes";
|
|
1
|
+
import React, { useCallback, useRef, useState } from "react";
|
|
3
2
|
import { Box } from "./Box";
|
|
4
3
|
import { Button } from "./Button";
|
|
5
4
|
import { TextField, TextFieldSlot } from "./TextField";
|
|
6
5
|
import { Text } from "./Text";
|
|
7
6
|
import Icon from "./Icon";
|
|
8
7
|
import { styled } from "../styles";
|
|
8
|
+
import { useOnClickOutside } from "../hooks/useOnClickOutside";
|
|
9
9
|
|
|
10
10
|
export const TimePickerStyled = styled("div", {
|
|
11
|
+
position: "relative",
|
|
11
12
|
fontFamily: "$default",
|
|
12
13
|
lineHeight: "$base",
|
|
13
14
|
fontSize: "$14",
|
|
14
15
|
maxWidth: "200px",
|
|
15
16
|
borderRadius: "$sm",
|
|
17
|
+
"> div > div": {
|
|
18
|
+
paddingLeft: "1rem",
|
|
19
|
+
input: {
|
|
20
|
+
textAlign: "right",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
16
23
|
});
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
|
|
25
|
+
export const TimePickerDropdownStyled = styled("div", {
|
|
26
|
+
position: "absolute",
|
|
27
|
+
left: 0,
|
|
28
|
+
zIndex: 10,
|
|
21
29
|
width: "100%",
|
|
22
30
|
maxWidth: "8.875rem",
|
|
31
|
+
backgroundColor: "$neutral50",
|
|
23
32
|
border: "1px solid $neutral300",
|
|
24
33
|
borderRadius: "$sm",
|
|
25
34
|
boxShadow: "0px 2px 8px 0px $shadow50",
|
|
26
35
|
});
|
|
36
|
+
|
|
27
37
|
export const TimePickerFooterStyled = styled("div", {
|
|
28
38
|
borderTop: "2px solid $neutral100",
|
|
29
39
|
padding: "$4 $16",
|
|
@@ -32,6 +42,7 @@ export const TimePickerFooterStyled = styled("div", {
|
|
|
32
42
|
alignItems: "center",
|
|
33
43
|
height: "3rem",
|
|
34
44
|
});
|
|
45
|
+
|
|
35
46
|
export const TimerPickerContentStyled = styled("div", {
|
|
36
47
|
display: "flex",
|
|
37
48
|
gap: "$16",
|
|
@@ -40,21 +51,36 @@ export const TimerPickerContentStyled = styled("div", {
|
|
|
40
51
|
"& > div:nth-child(2)": {
|
|
41
52
|
order: 2,
|
|
42
53
|
},
|
|
54
|
+
input: {
|
|
55
|
+
padding: "0",
|
|
56
|
+
textAlign: "center!important",
|
|
57
|
+
},
|
|
43
58
|
});
|
|
59
|
+
|
|
44
60
|
export type TimePickerProps = {
|
|
45
61
|
selected: string | undefined;
|
|
46
62
|
setSelected: React.Dispatch<React.SetStateAction<string | undefined>>;
|
|
63
|
+
position?: "bottom" | "top";
|
|
47
64
|
};
|
|
48
65
|
|
|
49
|
-
export function TimePicker({
|
|
66
|
+
export function TimePicker({
|
|
67
|
+
selected,
|
|
68
|
+
setSelected,
|
|
69
|
+
position = "bottom",
|
|
70
|
+
}: TimePickerProps) {
|
|
50
71
|
const [hours, setHours] = useState("00");
|
|
51
72
|
const [minutes, setMinutes] = useState("00");
|
|
73
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
74
|
+
const dropdownRef = useRef(null);
|
|
75
|
+
|
|
76
|
+
useOnClickOutside(dropdownRef, () => setIsOpen(false));
|
|
52
77
|
|
|
53
78
|
const pad = (num: number) => String(num).padStart(2, "0");
|
|
54
79
|
|
|
55
80
|
const handleInputValue = useCallback(
|
|
56
81
|
(time: string) => {
|
|
57
82
|
setSelected(time);
|
|
83
|
+
setIsOpen(false);
|
|
58
84
|
},
|
|
59
85
|
[setSelected]
|
|
60
86
|
);
|
|
@@ -71,7 +97,6 @@ export function TimePicker({ selected, setSelected }: TimePickerProps) {
|
|
|
71
97
|
},
|
|
72
98
|
[hours, minutes]
|
|
73
99
|
);
|
|
74
|
-
|
|
75
100
|
const handleDecrement = useCallback(
|
|
76
101
|
(type: "hours" | "minutes") => {
|
|
77
102
|
if (type === "hours") {
|
|
@@ -86,24 +111,25 @@ export function TimePicker({ selected, setSelected }: TimePickerProps) {
|
|
|
86
111
|
);
|
|
87
112
|
|
|
88
113
|
return (
|
|
89
|
-
<
|
|
90
|
-
<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
<
|
|
106
|
-
|
|
114
|
+
<TimePickerStyled ref={dropdownRef}>
|
|
115
|
+
<TextField
|
|
116
|
+
value={selected}
|
|
117
|
+
readOnly
|
|
118
|
+
type="text"
|
|
119
|
+
placeholder="00:00"
|
|
120
|
+
typography="labelSmall"
|
|
121
|
+
fontWeight="regular"
|
|
122
|
+
onClick={() => setIsOpen((prev) => !prev)}
|
|
123
|
+
>
|
|
124
|
+
<TextFieldSlot>
|
|
125
|
+
<Icon name="clock" size="xl" />
|
|
126
|
+
</TextFieldSlot>
|
|
127
|
+
</TextField>
|
|
128
|
+
|
|
129
|
+
{isOpen && (
|
|
130
|
+
<TimePickerDropdownStyled
|
|
131
|
+
style={position === "top" ? { bottom: "110%" } : { top: "110%" }}
|
|
132
|
+
>
|
|
107
133
|
<TimerPickerContentStyled>
|
|
108
134
|
{["hours", "minutes"].map((unit) => (
|
|
109
135
|
<Box
|
|
@@ -143,9 +169,7 @@ export function TimePicker({ selected, setSelected }: TimePickerProps) {
|
|
|
143
169
|
typography="labelSmall"
|
|
144
170
|
fontWeight="regular"
|
|
145
171
|
textAlign="center"
|
|
146
|
-
style={{ padding: "4px" }}
|
|
147
172
|
/>
|
|
148
|
-
|
|
149
173
|
<Button
|
|
150
174
|
variant="text"
|
|
151
175
|
onClick={() => handleDecrement(unit as "hours" | "minutes")}
|
|
@@ -172,20 +196,18 @@ export function TimePicker({ selected, setSelected }: TimePickerProps) {
|
|
|
172
196
|
<Text>:</Text>
|
|
173
197
|
</TimerPickerContentStyled>
|
|
174
198
|
<TimePickerFooterStyled>
|
|
175
|
-
<
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
</Button>
|
|
185
|
-
</TimePickerRadix.Close>
|
|
199
|
+
<Button
|
|
200
|
+
variant="text"
|
|
201
|
+
color="brand"
|
|
202
|
+
onClick={() => handleInputValue(`${hours}:${minutes}`)}
|
|
203
|
+
typography="buttonMedium"
|
|
204
|
+
fontWeight="medium"
|
|
205
|
+
>
|
|
206
|
+
Aplicar
|
|
207
|
+
</Button>
|
|
186
208
|
</TimePickerFooterStyled>
|
|
187
|
-
</
|
|
188
|
-
|
|
189
|
-
</
|
|
209
|
+
</TimePickerDropdownStyled>
|
|
210
|
+
)}
|
|
211
|
+
</TimePickerStyled>
|
|
190
212
|
);
|
|
191
213
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
|
|
3
|
+
export function useOnClickOutside(ref: any, handler: () => void) {
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
const listener = (event: MouseEvent | TouchEvent) => {
|
|
6
|
+
if (!ref.current || ref.current.contains(event.target)) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
handler();
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
document.addEventListener("mousedown", listener);
|
|
13
|
+
document.addEventListener("touchstart", listener);
|
|
14
|
+
|
|
15
|
+
return () => {
|
|
16
|
+
document.removeEventListener("mousedown", listener);
|
|
17
|
+
document.removeEventListener("touchstart", listener);
|
|
18
|
+
};
|
|
19
|
+
}, [ref, handler]);
|
|
20
|
+
}
|