@lets-events/react 11.6.4 → 11.6.5

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.
@@ -1,21 +1,28 @@
1
- import { useState, ComponentProps, ElementType } from "react";
1
+ import React, {
2
+ useState,
3
+ useRef,
4
+ useEffect,
5
+ ElementType,
6
+ ReactNode,
7
+ KeyboardEvent,
8
+ MouseEvent,
9
+ } from "react";
2
10
  import { faChevronDown, faChevronUp } from "@fortawesome/free-solid-svg-icons";
3
- import { Theme, DropdownMenu as DropdownMenuRadix } from "@radix-ui/themes";
4
11
  import { typographyLabelValues } from "../types/typographyValues";
5
12
  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
6
13
  import { colors } from "@lets-events/tokens";
7
14
  import { styled } from "../styles";
8
15
 
9
- const DropdownMenuItemStyled = styled(DropdownMenuRadix.Item, {
16
+ const DropdownMenuItemStyled = styled("div", {
10
17
  fontFamily: "$default",
11
18
  color: "$dark600",
12
19
  letterSpacing: "0px",
13
20
  padding: "$8 $16",
21
+ cursor: "pointer",
14
22
  "&:hover, &:focus": {
15
23
  backgroundColor: "$dark100",
16
24
  border: "none",
17
25
  outline: "none",
18
- cursor: "pointer",
19
26
  },
20
27
  variants: {
21
28
  typography: typographyLabelValues,
@@ -29,16 +36,18 @@ const DropdownMenuItemStyled = styled(DropdownMenuRadix.Item, {
29
36
  });
30
37
 
31
38
  const DropdownMenuStyled = styled("div", {
39
+ boxSizing: "border-box",
32
40
  fontFamily: "$default",
33
41
  color: "$dark600",
34
42
  letterSpacing: "0px",
35
43
  cursor: "pointer",
36
44
  border: "1px solid $dark300",
37
45
  borderRadius: "$xs",
38
- padding: "$8 $12",
39
- width: "100%",
46
+ maxWidth: "100%",
40
47
  display: "flex",
48
+ position: "relative",
41
49
  button: {
50
+ boxSizing: "border-box",
42
51
  fontFamily: "$default",
43
52
  color: "$dark600",
44
53
  letterSpacing: "0px",
@@ -49,6 +58,7 @@ const DropdownMenuStyled = styled("div", {
49
58
  display: "flex",
50
59
  alignItems: "center",
51
60
  gap: "$8",
61
+ padding: "$8 $12",
52
62
  cursor: "pointer",
53
63
  svg: {
54
64
  marginLeft: "auto",
@@ -98,94 +108,192 @@ const DropdownMenuStyled = styled("div", {
98
108
  },
99
109
  });
100
110
 
101
- const DropdownMenuContentStyled = styled(DropdownMenuRadix.Content, {
111
+ const DropdownMenuContentStyled = styled("div", {
112
+ position: "absolute",
113
+ top: "100%",
114
+ left: 0,
115
+ width: "100%",
102
116
  background: "white",
103
117
  border: "1px solid $dark300",
104
118
  borderRadius: "$xs",
105
119
  boxShadow: "0px 4px 4px 0px rgba(35, 53, 67, 0.08)",
106
- width: "100%",
107
- minWidth: "100%",
108
120
  marginTop: "3px",
109
121
  maxHeight: "400px",
110
- height: "100%",
111
- position: "relative",
112
122
  overflow: "auto",
113
123
  zIndex: 9999999999,
114
124
  });
115
125
 
116
- export type DropdownMenuProps = ComponentProps<
117
- typeof DropdownMenuRadix.Root
118
- > & {
126
+ export type DropdownMenuProps = {
119
127
  as?: ElementType;
120
128
  placeholder?: string;
121
129
  fontWeight: "regular" | "medium" | "semibold" | "bold";
122
130
  typography: "labelExtraSmall" | "labelSmall" | "labelMedium" | "labelLarge";
123
- children: React.ReactNode;
131
+ children: ReactNode;
124
132
  color?: "default" | "error";
133
+ open?: boolean;
134
+ onOpenChange?: (open: boolean) => void;
135
+ defaultOpen?: boolean;
125
136
  };
126
137
 
127
- export type DropdownMenuItemProps = ComponentProps<
128
- typeof DropdownMenuItemStyled
129
- > & {
138
+ export type DropdownMenuItemProps = {
130
139
  as?: ElementType;
131
140
  value: string;
132
141
  typography: DropdownMenuProps["typography"];
133
142
  fontWeight: DropdownMenuProps["fontWeight"];
143
+ children: ReactNode;
144
+ onSelect?: (value: string) => void;
145
+ onClick?: (event: MouseEvent<HTMLDivElement>) => void;
134
146
  };
135
147
 
136
- /* Componente principal */
137
148
  export function DropdownMenu({
138
149
  children,
139
150
  placeholder,
140
151
  typography,
141
- color,
152
+ color = "default",
142
153
  fontWeight,
154
+ open: controlledOpen,
155
+ onOpenChange,
156
+ defaultOpen = false,
143
157
  ...props
144
158
  }: DropdownMenuProps) {
145
- const [isOpen, setIsOpen] = useState(false);
159
+ const [internalOpen, setInternalOpen] = useState(defaultOpen);
160
+ const dropdownRef = useRef<HTMLDivElement>(null);
161
+ const isControlled = controlledOpen !== undefined;
162
+ const isOpen = isControlled ? controlledOpen : internalOpen;
163
+
164
+ const handleToggle = () => {
165
+ const newOpen = !isOpen;
166
+ if (!isControlled) {
167
+ setInternalOpen(newOpen);
168
+ }
169
+ onOpenChange?.(newOpen);
170
+ };
171
+
172
+ const handleClose = () => {
173
+ if (!isControlled) {
174
+ setInternalOpen(false);
175
+ }
176
+ onOpenChange?.(false);
177
+ };
178
+
179
+ useEffect(() => {
180
+ const handleClickOutside = (event: Event) => {
181
+ if (
182
+ dropdownRef.current &&
183
+ !dropdownRef.current.contains(event.target as Node)
184
+ ) {
185
+ handleClose();
186
+ }
187
+ };
188
+
189
+ const handleEscape = (event: KeyboardEvent) => {
190
+ if (event.key === "Escape") {
191
+ handleClose();
192
+ }
193
+ };
194
+
195
+ if (isOpen) {
196
+ document.addEventListener("mousedown", handleClickOutside);
197
+ document.addEventListener("keydown", handleEscape as any);
198
+ }
199
+
200
+ return () => {
201
+ document.removeEventListener("mousedown", handleClickOutside);
202
+ document.removeEventListener("keydown", handleEscape as any);
203
+ };
204
+ }, [isOpen]);
205
+
146
206
  return (
147
- <Theme>
148
- <DropdownMenuRadix.Root open={isOpen} onOpenChange={setIsOpen} {...props}>
149
- <DropdownMenuStyled
150
- typography={typography}
151
- fontWeight={fontWeight}
152
- color={color}
153
- >
154
- <DropdownMenuRadix.Trigger>
155
- <button aria-label={placeholder || "Filtrar"}>
156
- <span>{placeholder || "Filtrar"}</span>
157
- <FontAwesomeIcon
158
- icon={isOpen ? faChevronUp : faChevronDown}
159
- size="sm"
160
- color={colors.dark600}
161
- />
162
- </button>
163
- </DropdownMenuRadix.Trigger>
164
- <DropdownMenuContentStyled
165
- container={document.body}
166
- avoidCollisions={false}
167
- align="start"
168
- alignOffset={-14}
169
- >
170
- <DropdownMenuRadix.Group>{children}</DropdownMenuRadix.Group>
171
- </DropdownMenuContentStyled>
172
- </DropdownMenuStyled>
173
- </DropdownMenuRadix.Root>
174
- </Theme>
207
+ <DropdownMenuStyled
208
+ ref={dropdownRef}
209
+ typography={typography}
210
+ fontWeight={fontWeight}
211
+ color={color}
212
+ {...props}
213
+ >
214
+ <button
215
+ aria-label={placeholder || "Filtrar"}
216
+ aria-expanded={isOpen}
217
+ aria-haspopup="listbox"
218
+ onClick={handleToggle}
219
+ type="button"
220
+ onKeyDown={(e) => {
221
+ if (e.key === "Enter" || e.key === " ") {
222
+ e.preventDefault();
223
+ handleToggle();
224
+ }
225
+ }}
226
+ >
227
+ <span>{placeholder || "Filtrar"}</span>
228
+ <FontAwesomeIcon
229
+ icon={isOpen ? faChevronUp : faChevronDown}
230
+ size="sm"
231
+ color={colors.dark600}
232
+ />
233
+ </button>
234
+
235
+ {isOpen && (
236
+ <DropdownMenuContentStyled role="listbox">
237
+ <DropdownMenuProvider onItemSelect={handleClose}>
238
+ {children}
239
+ </DropdownMenuProvider>
240
+ </DropdownMenuContentStyled>
241
+ )}
242
+ </DropdownMenuStyled>
243
+ );
244
+ }
245
+
246
+ const DropdownMenuContext = React.createContext<{
247
+ onItemSelect: () => void;
248
+ } | null>(null);
249
+
250
+ function DropdownMenuProvider({
251
+ children,
252
+ onItemSelect,
253
+ }: {
254
+ children: ReactNode;
255
+ onItemSelect: () => void;
256
+ }) {
257
+ return (
258
+ <DropdownMenuContext.Provider value={{ onItemSelect }}>
259
+ {children}
260
+ </DropdownMenuContext.Provider>
175
261
  );
176
262
  }
177
263
 
178
- /* Componente de item */
179
264
  export function DropdownMenuItem({
180
265
  children,
181
266
  typography,
182
267
  fontWeight,
268
+ value,
269
+ onSelect,
270
+ onClick,
183
271
  ...props
184
272
  }: DropdownMenuItemProps) {
273
+ const context = React.useContext(DropdownMenuContext);
274
+
275
+ const handleClick = (event: MouseEvent<HTMLDivElement>) => {
276
+ onClick?.(event);
277
+ onSelect?.(value);
278
+ context?.onItemSelect();
279
+ };
280
+
281
+ const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
282
+ if (event.key === "Enter" || event.key === " ") {
283
+ event.preventDefault();
284
+ onSelect?.(value);
285
+ context?.onItemSelect();
286
+ }
287
+ };
288
+
185
289
  return (
186
290
  <DropdownMenuItemStyled
187
291
  typography={typography}
188
292
  fontWeight={fontWeight}
293
+ onClick={handleClick}
294
+ onKeyDown={handleKeyDown}
295
+ tabIndex={0}
296
+ role="option"
189
297
  {...props}
190
298
  >
191
299
  {children}