@lets-events/react 12.0.0 → 12.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.
@@ -19,6 +19,7 @@ export type CalendarFormFieldProps = Omit<
19
19
  RegisterOptions<FieldValues, string>,
20
20
  "valueAsNumber" | "valueAsDate" | "setValueAs" | "disabled"
21
21
  >;
22
+ allowPastDates?: boolean;
22
23
  };
23
24
 
24
25
  export const CalendarFormField = ({
@@ -29,6 +30,7 @@ export const CalendarFormField = ({
29
30
  validationErrorMessage = "Este campo é obrigatório.",
30
31
  rules,
31
32
  onChange,
33
+ allowPastDates,
32
34
  ...calendarProps
33
35
  }: CalendarFormFieldProps) => {
34
36
  const handleValidate = useCallback(
@@ -63,7 +65,7 @@ export const CalendarFormField = ({
63
65
  };
64
66
 
65
67
  return (
66
- <Flex direction="column">
68
+ <Flex direction="column" style={{ flex: "1" }}>
67
69
  {label && (
68
70
  <FormLabel
69
71
  name={name}
@@ -80,6 +82,7 @@ export const CalendarFormField = ({
80
82
  handleCalendarChange(date);
81
83
  }}
82
84
  hasError={haveError}
85
+ allowPastDates={allowPastDates}
83
86
  {...calendarProps}
84
87
  />
85
88
 
@@ -8,11 +8,6 @@ type IdentityDocumentNumberFormFieldProps = {
8
8
  validationErrorMessage: string;
9
9
  };
10
10
 
11
- const isValidRG = (rg: string): boolean => {
12
- const cleaned = rg.replace(/[^\d]/g, "");
13
- return /^\d{9}$/.test(cleaned);
14
- };
15
-
16
11
  export const IdentityDocumentNumberFormField = ({
17
12
  name,
18
13
  label,
@@ -31,9 +26,11 @@ export const IdentityDocumentNumberFormField = ({
31
26
  replacement: { _: /[0-9]/ },
32
27
  }}
33
28
  validate={(value: string) => {
34
- const isEmpty = value.replace(/[^\d]/g, "").length === 0;
29
+ const cleaned = value.replace(/[^\d]/g, "");
30
+ const isEmpty = cleaned.length === 0;
35
31
  if (!required && isEmpty) return true;
36
- return isValidRG(value) || validationErrorMessage;
32
+ if (cleaned.length >= 3) return true;
33
+ return false || validationErrorMessage;
37
34
  }}
38
35
  />
39
36
  );
@@ -10,6 +10,7 @@ export type MultiSelectFormFieldProps = MultiSelectProps & {
10
10
  required?: boolean;
11
11
  selectedOrientation?: "row" | "column";
12
12
  zIndex?: string;
13
+ maxHeight?: string;
13
14
  };
14
15
 
15
16
  export const MultiSelectFormField = ({
@@ -18,6 +19,7 @@ export const MultiSelectFormField = ({
18
19
  required,
19
20
  selectedOrientation = "column",
20
21
  zIndex,
22
+ maxHeight,
21
23
  ...rest
22
24
  }: MultiSelectFormFieldProps) => {
23
25
  const { field, fieldState } = useController({
@@ -53,6 +55,7 @@ export const MultiSelectFormField = ({
53
55
  color={haveError ? "error" : "default"}
54
56
  selectedOrientation={selectedOrientation}
55
57
  zIndex={zIndex}
58
+ maxHeight={maxHeight}
56
59
  {...rest}
57
60
  />
58
61
  <ErrorFormMessage message={errorMsg} />
@@ -62,7 +62,7 @@ export const TimePickerFormField = ({
62
62
  };
63
63
 
64
64
  return (
65
- <Flex direction="column">
65
+ <Flex direction="column" style={{ flex: "1" }}>
66
66
  {label && (
67
67
  <FormLabel
68
68
  name={name}
@@ -1,3 +1,4 @@
1
+ import React, { useCallback, useRef, useState } from "react";
1
2
  import { DropdownMenu, Theme } from "@radix-ui/themes";
2
3
  import { CheckboxGroup, CheckboxItem } from "./CheckboxGroup";
3
4
  import { styled } from "../styles";
@@ -9,10 +10,9 @@ import {
9
10
  faSquareXmark,
10
11
  } from "@fortawesome/free-solid-svg-icons";
11
12
  import { colors } from "@lets-events/tokens";
12
- import { ComponentProps, useCallback, useMemo, useRef, useState } from "react";
13
+ import { ComponentProps, useMemo } from "react";
13
14
  import { Flex } from "./Flex";
14
15
  import { CSS } from "@stitches/react";
15
- import React from "react";
16
16
 
17
17
  const StyledContent = styled(DropdownMenu.Content, {
18
18
  backgroundColor: "$dark50",
@@ -21,6 +21,9 @@ const StyledContent = styled(DropdownMenu.Content, {
21
21
  boxShadow: "0px 2px 4px 0px #23354329, 0px 4px 4px 0px #23354314",
22
22
  boxSizing: "border-box",
23
23
  border: "1px solid $dark300",
24
+ zIndex: 999999,
25
+ minWidth: "var(--radix-dropdown-menu-trigger-width)",
26
+ maxWidth: "var(--radix-dropdown-menu-trigger-width)",
24
27
  });
25
28
 
26
29
  const StyledTrigger = styled("div", {
@@ -33,6 +36,7 @@ const StyledTrigger = styled("div", {
33
36
  padding: "$6 $14",
34
37
  boxSizing: "border-box",
35
38
  gap: "4px",
39
+ width: "100%",
36
40
 
37
41
  variants: {
38
42
  color: {
@@ -77,6 +81,40 @@ const BadgeCloseBtn = styled("div", {
77
81
  cursor: "pointer",
78
82
  });
79
83
 
84
+ const StyledFlexWithMaxHeight = styled(Flex, {
85
+ variants: {
86
+ hasMaxHeight: {
87
+ true: {
88
+ overflowY: "auto",
89
+ "&::-webkit-scrollbar": {
90
+ width: "4px",
91
+ },
92
+ "&::-webkit-scrollbar-track": {
93
+ backgroundColor: "$dark100",
94
+ borderRadius: "2px",
95
+ },
96
+ "&::-webkit-scrollbar-thumb": {
97
+ backgroundColor: "$dark300",
98
+ borderRadius: "2px",
99
+ "&:hover": {
100
+ backgroundColor: "$dark400",
101
+ },
102
+ },
103
+ },
104
+ },
105
+ },
106
+
107
+ defaultVariants: {
108
+ hasMaxHeight: false,
109
+ },
110
+ });
111
+ const StyledText = styled(Text, {
112
+ flex: 1,
113
+ overflow: "hidden",
114
+ whiteSpace: "nowrap",
115
+ textOverflow: "ellipsis",
116
+ });
117
+
80
118
  export type MultiSelectProps = ComponentProps<typeof StyledTrigger> & {
81
119
  placeholder?: string;
82
120
  value?: string[];
@@ -91,6 +129,7 @@ export type MultiSelectProps = ComponentProps<typeof StyledTrigger> & {
91
129
  singleSelect?: boolean;
92
130
  selectedOrientation?: "row" | "column";
93
131
  disabled?: boolean;
132
+ maxHeight?: string;
94
133
  };
95
134
 
96
135
  export const MultiSelect = React.forwardRef<HTMLDivElement, MultiSelectProps>(
@@ -107,13 +146,13 @@ export const MultiSelect = React.forwardRef<HTMLDivElement, MultiSelectProps>(
107
146
  singleSelect = false,
108
147
  selectedOrientation = "column",
109
148
  disabled = false,
149
+ maxHeight,
110
150
  },
111
151
  fowardedRef
112
152
  ) => {
113
153
  const [isOpen, setIsOpen] = useState(false);
114
154
 
115
155
  const triggerRef = useRef<HTMLDivElement>(null);
116
-
117
156
  const labelByValue = useMemo(() => {
118
157
  return options.reduce<{ [key: string]: string }>((prev, curr) => {
119
158
  return {
@@ -131,8 +170,6 @@ export const MultiSelect = React.forwardRef<HTMLDivElement, MultiSelectProps>(
131
170
  [selectedValues, onValueChange]
132
171
  );
133
172
 
134
- const menuWidth = triggerRef.current?.offsetWidth;
135
-
136
173
  const text = useMemo(() => {
137
174
  if (selectedValues.length > 0 && singleSelect) {
138
175
  const value = selectedValues[0];
@@ -146,20 +183,21 @@ export const MultiSelect = React.forwardRef<HTMLDivElement, MultiSelectProps>(
146
183
  onValueChange?.([v]);
147
184
  setIsOpen(false);
148
185
  };
186
+ const handleToggle = useCallback(
187
+ (e: React.MouseEvent) => {
188
+ e.preventDefault();
189
+ e.stopPropagation();
190
+ if (disabled) return;
191
+ setIsOpen((prev) => !prev);
192
+ },
193
+ [disabled]
194
+ );
149
195
 
150
196
  return (
151
197
  <Theme>
152
- <DropdownMenu.Root open={isOpen} onOpenChange={() => setIsOpen(false)}>
153
- <DropdownMenu.Trigger
154
- onClick={() => {
155
- if (disabled) return;
156
- setIsOpen(true);
157
- }}
158
- >
198
+ <DropdownMenu.Root>
199
+ <DropdownMenu.Trigger>
159
200
  <StyledTrigger
160
- css={{
161
- width,
162
- }}
163
201
  ref={(r) => {
164
202
  if (!r) return;
165
203
  triggerRef.current = r;
@@ -172,19 +210,15 @@ export const MultiSelect = React.forwardRef<HTMLDivElement, MultiSelectProps>(
172
210
  }}
173
211
  color={color}
174
212
  disabled={disabled}
213
+ style={width !== "100%" ? { width } : undefined}
214
+ onClick={handleToggle}
175
215
  >
176
- <Text
216
+ <StyledText
177
217
  typography={"labelMedium"}
178
- css={{
179
- flex: 1,
180
- overflow: "hidden",
181
- whiteSpace: "nowrap",
182
- textOverflow: "ellipsis",
183
- }}
184
218
  color={disabled ? "dark400" : undefined}
185
219
  >
186
220
  {text}
187
- </Text>
221
+ </StyledText>
188
222
  <FontAwesomeIcon
189
223
  icon={isOpen ? faChevronUp : faChevronDown}
190
224
  size="sm"
@@ -194,8 +228,8 @@ export const MultiSelect = React.forwardRef<HTMLDivElement, MultiSelectProps>(
194
228
  </DropdownMenu.Trigger>
195
229
  <StyledContent
196
230
  css={{
197
- width: menuWidth ? menuWidth + "px" : width,
198
- zIndex,
231
+ width: "100%",
232
+ zIndex: zIndex === "auto" ? 999999 : zIndex,
199
233
  }}
200
234
  >
201
235
  {!singleSelect ? (
@@ -205,22 +239,32 @@ export const MultiSelect = React.forwardRef<HTMLDivElement, MultiSelectProps>(
205
239
  onValueChange?.(v);
206
240
  }}
207
241
  >
208
- <Flex direction={"column"} gap={8}>
242
+ <StyledFlexWithMaxHeight
243
+ direction={"column"}
244
+ gap={8}
245
+ hasMaxHeight={!!maxHeight}
246
+ style={maxHeight ? { maxHeight } : undefined}
247
+ >
209
248
  {options.map(({ value, label }, i) => (
210
249
  <CheckboxItem value={value} css={itemStyle} key={i}>
211
250
  <Text typography={"labelSmall"}>{label}</Text>
212
251
  </CheckboxItem>
213
252
  ))}
214
- </Flex>
253
+ </StyledFlexWithMaxHeight>
215
254
  </CheckboxGroup>
216
255
  ) : (
217
- <Flex direction={"column"} gap={8}>
256
+ <StyledFlexWithMaxHeight
257
+ direction={"column"}
258
+ gap={8}
259
+ hasMaxHeight={!!maxHeight}
260
+ style={maxHeight ? { maxHeight } : undefined}
261
+ >
218
262
  {options.map(({ value, label }, i) => (
219
263
  <StyledItem key={i} onClick={() => handleItemClick(value)}>
220
264
  <Text typography={"labelSmall"}>{label}</Text>
221
265
  </StyledItem>
222
266
  ))}
223
- </Flex>
267
+ </StyledFlexWithMaxHeight>
224
268
  )}
225
269
  </StyledContent>
226
270
  </DropdownMenu.Root>
@@ -229,34 +273,35 @@ export const MultiSelect = React.forwardRef<HTMLDivElement, MultiSelectProps>(
229
273
  <Flex
230
274
  direction={selectedOrientation}
231
275
  gap={8}
232
- align={"center"}
276
+ align={selectedOrientation === "column" ? "start" : "center"}
233
277
  justify={"start"}
234
- css={{
235
- margin: "8px 0",
236
- }}
278
+ css={{ margin: "8px 0" }}
237
279
  >
238
- {selectedValues.map((value) => {
239
- return (
240
- <Flex gap={4} align={"center"} css={{ flexWrap: "wrap" }}>
241
- <BadgeCloseBtn
242
- onClick={(e) => {
243
- e.stopPropagation();
244
- handleRemove(value);
245
- }}
246
- role="button"
247
- >
248
- <FontAwesomeIcon icon={faSquareXmark} size="sm" />
249
- </BadgeCloseBtn>
250
- <Text
251
- typography={"captionMedium"}
252
- fontWeight={"regular"}
253
- color="dark600"
254
- >
255
- {labelByValue[value]}
256
- </Text>
257
- </Flex>
258
- );
259
- })}
280
+ {selectedValues.map((value) => (
281
+ <Flex
282
+ key={value}
283
+ gap={4}
284
+ align={"center"}
285
+ css={{ flexWrap: "wrap" }}
286
+ >
287
+ <BadgeCloseBtn
288
+ onClick={(e) => {
289
+ e.stopPropagation();
290
+ handleRemove(value);
291
+ }}
292
+ role="button"
293
+ >
294
+ <FontAwesomeIcon icon={faSquareXmark} size="sm" />
295
+ </BadgeCloseBtn>
296
+ <Text
297
+ typography={"captionMedium"}
298
+ fontWeight={"regular"}
299
+ color="dark600"
300
+ >
301
+ {labelByValue[value]}
302
+ </Text>
303
+ </Flex>
304
+ ))}
260
305
  </Flex>
261
306
  </>
262
307
  )}
@@ -152,10 +152,8 @@ const QuillComponent: React.FC<QuillComponentProps> = ({
152
152
  const headerSelect = toolbarElement.querySelector(
153
153
  "select[data-value]"
154
154
  ) as HTMLSelectElement;
155
- console.log(headerSelect, "headerSelect");
156
155
  if (headerSelect) {
157
156
  const options = headerSelect.querySelectorAll("option");
158
- console.log(options, "options");
159
157
  options.forEach((option) => {
160
158
  if (option.value === "1") {
161
159
  option.textContent = "Título 1";
@@ -167,7 +165,7 @@ const QuillComponent: React.FC<QuillComponentProps> = ({
167
165
  });
168
166
  }
169
167
  }
170
- }, 100);
168
+ }, 2000);
171
169
  }
172
170
  }, [quill, onChange, handleImageUpload]);
173
171
 
@@ -12,19 +12,25 @@ export const TimePickerStyled = styled("div", {
12
12
  fontFamily: "$default",
13
13
  lineHeight: "$base",
14
14
  fontSize: "$14",
15
- maxWidth: "200px",
16
15
  borderRadius: "$sm",
17
16
  "> div > div": {
18
- paddingLeft: "1rem",
19
17
  input: {
20
18
  textAlign: "right",
21
19
  },
22
20
  },
21
+ variants: {
22
+ expand: {
23
+ true: {
24
+ width: "100%",
25
+ flex: "1",
26
+ display: "flex",
27
+ },
28
+ },
29
+ },
23
30
  });
24
31
 
25
32
  export const TimePickerDropdownStyled = styled("div", {
26
33
  position: "absolute",
27
- left: 0,
28
34
  zIndex: 10,
29
35
  width: "100%",
30
36
  maxWidth: "8.875rem",
@@ -98,22 +104,30 @@ export const InputStyled = styled("input", {
98
104
  export const TimePickerButtonStyled = styled("button", {
99
105
  backgroundColor: "transparent",
100
106
  border: "none",
101
- maxWidth: "200px",
102
107
  padding: "0",
103
108
  cursor: "pointer",
104
109
  "> div > div": {
105
- paddingLeft: "1rem",
106
110
  input: {
107
111
  textAlign: "right",
108
112
  },
109
113
  },
114
+ variants: {
115
+ expand: {
116
+ true: {
117
+ flex: "1",
118
+ display: "flex",
119
+ maxWidth: "100%",
120
+ },
121
+ },
122
+ },
110
123
  });
111
124
 
112
125
  export type TimePickerProps = {
113
126
  selected: string | undefined;
114
127
  setSelected: React.Dispatch<React.SetStateAction<string | undefined>>;
115
- position?: "bottom" | "top";
128
+ position?: "bottom" | "top" | "top-right" | "bottom-right";
116
129
  hasError?: boolean;
130
+ expand?: boolean;
117
131
  };
118
132
  const pad = (num: number) => String(num).padStart(2, "0");
119
133
 
@@ -122,6 +136,7 @@ export function TimePicker({
122
136
  setSelected,
123
137
  position = "bottom",
124
138
  hasError,
139
+ expand = false,
125
140
  }: TimePickerProps) {
126
141
  const [hours, setHours] = useState("00");
127
142
  const [minutes, setMinutes] = useState("00");
@@ -162,10 +177,11 @@ export function TimePicker({
162
177
  );
163
178
 
164
179
  return (
165
- <TimePickerStyled ref={dropdownRef}>
180
+ <TimePickerStyled ref={dropdownRef} expand={expand}>
166
181
  <TimePickerButtonStyled
167
182
  type="button"
168
183
  onClick={() => setIsOpen((prev) => !prev)}
184
+ expand={expand}
169
185
  >
170
186
  <TextField
171
187
  value={selected}
@@ -184,7 +200,15 @@ export function TimePicker({
184
200
 
185
201
  {isOpen && (
186
202
  <TimePickerDropdownStyled
187
- style={position === "top" ? { bottom: "110%" } : { top: "110%" }}
203
+ style={
204
+ position === "top"
205
+ ? { bottom: "110%", left: "0" }
206
+ : position === "top-right"
207
+ ? { bottom: "110%", right: "0" }
208
+ : position === "bottom-right"
209
+ ? { top: "110%", right: "0" }
210
+ : { top: "110%", left: "0" }
211
+ }
188
212
  >
189
213
  <TimerPickerContentStyled>
190
214
  {["hours", "minutes"].map((unit) => (
@@ -1,12 +1,34 @@
1
1
  import { useEffect } from "react";
2
2
 
3
- export function useOnClickOutside(ref: any, handler: () => void) {
3
+ export function useOnClickOutside(
4
+ ref: any,
5
+ handler: (event?: MouseEvent | TouchEvent) => void
6
+ ) {
4
7
  useEffect(() => {
5
8
  const listener = (event: MouseEvent | TouchEvent) => {
6
- if (!ref.current || ref.current.contains(event.target)) {
9
+ if (!ref?.current || !event.target) {
7
10
  return;
8
11
  }
9
- handler();
12
+
13
+ const target = event.target as Element;
14
+ const currentTarget = event.currentTarget as Element;
15
+
16
+ if (ref.current.contains(target) || ref.current.contains(currentTarget)) {
17
+ return;
18
+ }
19
+
20
+ if (target.tagName === "HTML" || target.tagName === "BODY") {
21
+ const elementAtPoint = document.elementFromPoint(
22
+ (event as MouseEvent).clientX,
23
+ (event as MouseEvent).clientY
24
+ );
25
+
26
+ if (elementAtPoint && ref.current.contains(elementAtPoint)) {
27
+ return;
28
+ }
29
+ }
30
+
31
+ handler(event);
10
32
  };
11
33
 
12
34
  document.addEventListener("mousedown", listener);