@particle-academy/react-fancy 5.64.0 → 5.64.1
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/CHANGELOG.md +30 -0
- package/dist/chunk-BXPLERIJ.js +293 -0
- package/dist/chunk-BXPLERIJ.js.map +1 -0
- package/dist/chunk-XZOBGKZL.cjs +296 -0
- package/dist/chunk-XZOBGKZL.cjs.map +1 -0
- package/dist/dropdown.cjs +3 -3
- package/dist/dropdown.d.cts +10 -0
- package/dist/dropdown.d.ts +10 -0
- package/dist/dropdown.js +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.js +1 -1
- package/docs/ReorderableGrid.md +2 -0
- package/package.json +1 -1
- package/dist/chunk-A6HWKQ4D.cjs +0 -172
- package/dist/chunk-A6HWKQ4D.cjs.map +0 -1
- package/dist/chunk-JG2DJK5A.js +0 -169
- package/dist/chunk-JG2DJK5A.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [5.64.1] - 2026-10-27
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **`Dropdown`: the menu can be driven from the keyboard again.** Its
|
|
19
|
+
arrow-key listener was attached in an effect keyed on `open` that returned
|
|
20
|
+
early while the menu element was still null (the menu mounts a render later,
|
|
21
|
+
behind `useAnimation`, and `Portal` takes one more), and nothing in its
|
|
22
|
+
dependencies changed once the element appeared, so it never re-ran.
|
|
23
|
+
ArrowDown did nothing and no item of any Dropdown was reachable by keyboard.
|
|
24
|
+
Found building the showcase's Features menu. Keys are now handled on the menu
|
|
25
|
+
element itself, and the element is tracked as state, so the timing cannot
|
|
26
|
+
recur. The menu now follows the WAI-ARIA menu button pattern:
|
|
27
|
+
- On the trigger, Enter, Space or ArrowDown opens the menu on its first
|
|
28
|
+
enabled item and ArrowUp on its last. A non-button trigger (an avatar
|
|
29
|
+
`span` with `tabIndex`) opens on Enter / Space too.
|
|
30
|
+
- In the menu, ArrowDown / ArrowUp move and wrap, Home / End jump, a typed
|
|
31
|
+
character jumps to the next item starting with it, and disabled items are
|
|
32
|
+
skipped.
|
|
33
|
+
- Escape closes the menu (still through the `useEscapeKey` layer stack, so
|
|
34
|
+
only the innermost layer closes) and returns focus to the trigger. Tab
|
|
35
|
+
closes it and continues from the trigger's place in the tab order.
|
|
36
|
+
Activating an item returns focus to the trigger unless its `onClick`
|
|
37
|
+
moved focus somewhere else.
|
|
38
|
+
|
|
39
|
+
Nothing to do as a consumer: the props are unchanged, and opening by mouse
|
|
40
|
+
still leaves focus on the trigger. If you worked around this with your own
|
|
41
|
+
arrow-key handler, you can delete it. `DropdownContextValue` gains
|
|
42
|
+
`focusTrigger`, plus two `@internal` refs.
|
|
43
|
+
|
|
14
44
|
## [5.64.0] - 2026-10-26
|
|
15
45
|
|
|
16
46
|
### Added
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { useAnimation } from './chunk-3K2CEAI7.js';
|
|
2
|
+
import { useFloatingPosition } from './chunk-MQ6VGG62.js';
|
|
3
|
+
import { Portal } from './chunk-RBCMKPBX.js';
|
|
4
|
+
import { useOutsideClick } from './chunk-RGANGU7X.js';
|
|
5
|
+
import { useEscapeKey } from './chunk-YX6FTJ6N.js';
|
|
6
|
+
import { cn } from './chunk-MMW2JGKZ.js';
|
|
7
|
+
import { createContext, useState, useRef, useCallback, useMemo, useEffect, useContext } from 'react';
|
|
8
|
+
import { jsx } from 'react/jsx-runtime';
|
|
9
|
+
|
|
10
|
+
var DropdownContext = createContext(null);
|
|
11
|
+
function useDropdown() {
|
|
12
|
+
const ctx = useContext(DropdownContext);
|
|
13
|
+
if (!ctx) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
"Dropdown compound components must be used within <Dropdown>"
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
return ctx;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/components/Dropdown/dropdown-focus.ts
|
|
22
|
+
function menuItems(menu) {
|
|
23
|
+
if (!menu) return [];
|
|
24
|
+
return Array.from(
|
|
25
|
+
menu.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"]):not([disabled])')
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
function focusMenuItem(menu, move) {
|
|
29
|
+
const items = menuItems(menu);
|
|
30
|
+
if (items.length === 0) return -1;
|
|
31
|
+
const current = items.indexOf(document.activeElement);
|
|
32
|
+
let next;
|
|
33
|
+
if (move === "first") next = 0;
|
|
34
|
+
else if (move === "last") next = items.length - 1;
|
|
35
|
+
else if (current === -1) next = move === "next" ? 0 : items.length - 1;
|
|
36
|
+
else next = (current + (move === "next" ? 1 : -1) + items.length) % items.length;
|
|
37
|
+
items[next].focus();
|
|
38
|
+
return next;
|
|
39
|
+
}
|
|
40
|
+
function focusByChar(menu, char) {
|
|
41
|
+
const items = menuItems(menu);
|
|
42
|
+
if (items.length === 0) return -1;
|
|
43
|
+
const needle = char.toLowerCase();
|
|
44
|
+
const start = items.indexOf(document.activeElement);
|
|
45
|
+
for (let step = 1; step <= items.length; step++) {
|
|
46
|
+
const i = (start + step + items.length) % items.length;
|
|
47
|
+
if ((items[i].textContent ?? "").trim().toLowerCase().startsWith(needle)) {
|
|
48
|
+
items[i].focus();
|
|
49
|
+
return i;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return -1;
|
|
53
|
+
}
|
|
54
|
+
function activatesNatively(el) {
|
|
55
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
56
|
+
const tag = el.tagName;
|
|
57
|
+
if (tag === "BUTTON" || tag === "SUMMARY") return true;
|
|
58
|
+
if (tag === "A") return el.hasAttribute("href");
|
|
59
|
+
if (tag === "INPUT") {
|
|
60
|
+
const type = el.type;
|
|
61
|
+
return type === "button" || type === "submit" || type === "reset" || type === "image";
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
function DropdownTrigger({ children }) {
|
|
66
|
+
const { setOpen, open, anchorRef, pendingFocusRef, menuRef } = useDropdown();
|
|
67
|
+
const onKeyDown = (e) => {
|
|
68
|
+
if (e.defaultPrevented) return;
|
|
69
|
+
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
70
|
+
e.preventDefault();
|
|
71
|
+
const target = e.key === "ArrowDown" ? "first" : "last";
|
|
72
|
+
if (open && menuRef.current) {
|
|
73
|
+
focusMenuItem(menuRef.current, target);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
pendingFocusRef.current = target;
|
|
77
|
+
setOpen(true);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
81
|
+
if (!open) pendingFocusRef.current = "first";
|
|
82
|
+
if (activatesNatively(e.target)) return;
|
|
83
|
+
e.preventDefault();
|
|
84
|
+
if (open) pendingFocusRef.current = null;
|
|
85
|
+
setOpen(!open);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const onClick = (_e) => {
|
|
89
|
+
if (open) pendingFocusRef.current = null;
|
|
90
|
+
setOpen(!open);
|
|
91
|
+
};
|
|
92
|
+
return /* @__PURE__ */ jsx(
|
|
93
|
+
"span",
|
|
94
|
+
{
|
|
95
|
+
ref: anchorRef,
|
|
96
|
+
"data-react-fancy-dropdown-trigger": "",
|
|
97
|
+
className: "inline-flex",
|
|
98
|
+
onClick,
|
|
99
|
+
onKeyDown,
|
|
100
|
+
"aria-expanded": open,
|
|
101
|
+
"aria-haspopup": "menu",
|
|
102
|
+
children
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
DropdownTrigger.displayName = "DropdownTrigger";
|
|
107
|
+
function DropdownItems({ children, className }) {
|
|
108
|
+
const {
|
|
109
|
+
open,
|
|
110
|
+
setOpen,
|
|
111
|
+
anchorRef,
|
|
112
|
+
setActiveIndex,
|
|
113
|
+
placement,
|
|
114
|
+
offset,
|
|
115
|
+
pendingFocusRef,
|
|
116
|
+
menuRef,
|
|
117
|
+
focusTrigger
|
|
118
|
+
} = useDropdown();
|
|
119
|
+
const floatingRef = useRef(null);
|
|
120
|
+
const outsideRef = useRef(null);
|
|
121
|
+
const position = useFloatingPosition(anchorRef, floatingRef, {
|
|
122
|
+
placement,
|
|
123
|
+
offset,
|
|
124
|
+
enabled: open
|
|
125
|
+
});
|
|
126
|
+
const close = useCallback(() => {
|
|
127
|
+
pendingFocusRef.current = null;
|
|
128
|
+
setOpen(false);
|
|
129
|
+
setActiveIndex(-1);
|
|
130
|
+
}, [setOpen, setActiveIndex, pendingFocusRef]);
|
|
131
|
+
useOutsideClick(outsideRef, close, open, anchorRef);
|
|
132
|
+
useEscapeKey(() => {
|
|
133
|
+
close();
|
|
134
|
+
focusTrigger();
|
|
135
|
+
}, open);
|
|
136
|
+
const { mounted, className: animClass, ref: animRef } = useAnimation({
|
|
137
|
+
open,
|
|
138
|
+
enterClass: "fancy-scale-in",
|
|
139
|
+
exitClass: "fancy-fade-out"
|
|
140
|
+
});
|
|
141
|
+
const [menuEl, setMenuEl] = useState(null);
|
|
142
|
+
const setMenuRef = useCallback(
|
|
143
|
+
(node) => {
|
|
144
|
+
outsideRef.current = node;
|
|
145
|
+
floatingRef.current = node;
|
|
146
|
+
animRef.current = node;
|
|
147
|
+
menuRef.current = node;
|
|
148
|
+
setMenuEl(node);
|
|
149
|
+
},
|
|
150
|
+
[animRef, menuRef]
|
|
151
|
+
);
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
if (!open || !menuEl) return;
|
|
154
|
+
const target = pendingFocusRef.current;
|
|
155
|
+
if (!target) return;
|
|
156
|
+
pendingFocusRef.current = null;
|
|
157
|
+
setActiveIndex(focusMenuItem(menuEl, target));
|
|
158
|
+
}, [open, menuEl, pendingFocusRef, setActiveIndex]);
|
|
159
|
+
const onKeyDown = (e) => {
|
|
160
|
+
const menu = e.currentTarget;
|
|
161
|
+
const moves = {
|
|
162
|
+
ArrowDown: "next",
|
|
163
|
+
ArrowUp: "prev",
|
|
164
|
+
Home: "first",
|
|
165
|
+
End: "last"
|
|
166
|
+
};
|
|
167
|
+
const move = moves[e.key];
|
|
168
|
+
if (move) {
|
|
169
|
+
e.preventDefault();
|
|
170
|
+
setActiveIndex(focusMenuItem(menu, move));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (e.key === "Tab") {
|
|
174
|
+
close();
|
|
175
|
+
focusTrigger();
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (e.key.length === 1 && e.key !== " " && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
179
|
+
const i = focusByChar(menu, e.key);
|
|
180
|
+
if (i >= 0) setActiveIndex(i);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
if (!mounted) return null;
|
|
184
|
+
return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(
|
|
185
|
+
"div",
|
|
186
|
+
{
|
|
187
|
+
ref: setMenuRef,
|
|
188
|
+
"data-react-fancy-dropdown": "",
|
|
189
|
+
role: "menu",
|
|
190
|
+
onKeyDown,
|
|
191
|
+
className: cn(
|
|
192
|
+
"fixed z-50 min-w-[8rem] rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] p-1 shadow-lg dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]",
|
|
193
|
+
animClass,
|
|
194
|
+
className
|
|
195
|
+
),
|
|
196
|
+
style: { left: position.x, top: position.y },
|
|
197
|
+
children
|
|
198
|
+
}
|
|
199
|
+
) });
|
|
200
|
+
}
|
|
201
|
+
DropdownItems.displayName = "DropdownItems";
|
|
202
|
+
function DropdownItem({
|
|
203
|
+
children,
|
|
204
|
+
onClick,
|
|
205
|
+
disabled = false,
|
|
206
|
+
danger = false,
|
|
207
|
+
className
|
|
208
|
+
}) {
|
|
209
|
+
const { setOpen, focusTrigger } = useDropdown();
|
|
210
|
+
return /* @__PURE__ */ jsx(
|
|
211
|
+
"button",
|
|
212
|
+
{
|
|
213
|
+
"data-react-fancy-dropdown-item": "",
|
|
214
|
+
type: "button",
|
|
215
|
+
role: "menuitem",
|
|
216
|
+
disabled,
|
|
217
|
+
"aria-disabled": disabled,
|
|
218
|
+
className: cn(
|
|
219
|
+
"flex w-full items-center rounded-lg px-3 py-2 text-left text-sm transition-colors",
|
|
220
|
+
danger ? "text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950" : "text-[color:var(--fancy-text-soft,var(--color-zinc-700))] hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]",
|
|
221
|
+
disabled && "cursor-not-allowed opacity-50",
|
|
222
|
+
className
|
|
223
|
+
),
|
|
224
|
+
onClick: (e) => {
|
|
225
|
+
if (disabled) return;
|
|
226
|
+
const hadFocus = document.activeElement === e.currentTarget;
|
|
227
|
+
onClick?.();
|
|
228
|
+
setOpen(false);
|
|
229
|
+
if (hadFocus && document.activeElement === e.currentTarget) focusTrigger();
|
|
230
|
+
},
|
|
231
|
+
children
|
|
232
|
+
}
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
DropdownItem.displayName = "DropdownItem";
|
|
236
|
+
function DropdownSeparator({ className }) {
|
|
237
|
+
return /* @__PURE__ */ jsx(
|
|
238
|
+
"div",
|
|
239
|
+
{
|
|
240
|
+
"data-react-fancy-dropdown-separator": "",
|
|
241
|
+
role: "separator",
|
|
242
|
+
className: cn(
|
|
243
|
+
"my-1 h-px bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]",
|
|
244
|
+
className
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
DropdownSeparator.displayName = "DropdownSeparator";
|
|
250
|
+
var TRIGGER_FOCUSABLE = 'button:not([disabled]), a[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
251
|
+
function DropdownRoot({
|
|
252
|
+
children,
|
|
253
|
+
placement = "bottom-start",
|
|
254
|
+
offset = 4
|
|
255
|
+
}) {
|
|
256
|
+
const [open, setOpen] = useState(false);
|
|
257
|
+
const [activeIndex, setActiveIndex] = useState(-1);
|
|
258
|
+
const anchorRef = useRef(null);
|
|
259
|
+
const pendingFocusRef = useRef(null);
|
|
260
|
+
const menuRef = useRef(null);
|
|
261
|
+
const focusTrigger = useCallback(() => {
|
|
262
|
+
const anchor = anchorRef.current;
|
|
263
|
+
if (!anchor) return;
|
|
264
|
+
const target = anchor.querySelector(TRIGGER_FOCUSABLE) ?? (anchor.tabIndex >= 0 ? anchor : null);
|
|
265
|
+
target?.focus();
|
|
266
|
+
}, []);
|
|
267
|
+
const ctx = useMemo(
|
|
268
|
+
() => ({
|
|
269
|
+
open,
|
|
270
|
+
setOpen,
|
|
271
|
+
anchorRef,
|
|
272
|
+
activeIndex,
|
|
273
|
+
setActiveIndex,
|
|
274
|
+
placement,
|
|
275
|
+
offset,
|
|
276
|
+
pendingFocusRef,
|
|
277
|
+
menuRef,
|
|
278
|
+
focusTrigger
|
|
279
|
+
}),
|
|
280
|
+
[open, anchorRef, activeIndex, placement, offset, focusTrigger]
|
|
281
|
+
);
|
|
282
|
+
return /* @__PURE__ */ jsx(DropdownContext.Provider, { value: ctx, children });
|
|
283
|
+
}
|
|
284
|
+
var Dropdown = Object.assign(DropdownRoot, {
|
|
285
|
+
Trigger: DropdownTrigger,
|
|
286
|
+
Items: DropdownItems,
|
|
287
|
+
Item: DropdownItem,
|
|
288
|
+
Separator: DropdownSeparator
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
export { Dropdown, useDropdown };
|
|
292
|
+
//# sourceMappingURL=chunk-BXPLERIJ.js.map
|
|
293
|
+
//# sourceMappingURL=chunk-BXPLERIJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Dropdown/Dropdown.context.ts","../src/components/Dropdown/dropdown-focus.ts","../src/components/Dropdown/DropdownTrigger.tsx","../src/components/Dropdown/DropdownItems.tsx","../src/components/Dropdown/DropdownItem.tsx","../src/components/Dropdown/DropdownSeparator.tsx","../src/components/Dropdown/Dropdown.tsx"],"names":["jsx","useState","useRef","useCallback"],"mappings":";;;;;;;;;AAGO,IAAM,eAAA,GAAkB,cAA2C,IAAI,CAAA;AAEvE,SAAS,WAAA,GAAoC;AAClD,EAAA,MAAM,GAAA,GAAM,WAAW,eAAe,CAAA;AACtC,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;;;ACZO,SAAS,UAAU,IAAA,EAAyC;AACjE,EAAA,IAAI,CAAC,IAAA,EAAM,OAAO,EAAC;AACnB,EAAA,OAAO,KAAA,CAAM,IAAA;AAAA,IACX,IAAA,CAAK,iBAA8B,+DAA+D;AAAA,GACpG;AACF;AAQO,SAAS,aAAA,CAAc,MAA0B,IAAA,EAAyB;AAC/E,EAAA,MAAM,KAAA,GAAQ,UAAU,IAAI,CAAA;AAC5B,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,EAAA;AAC/B,EAAA,MAAM,OAAA,GAAU,KAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,aAA4B,CAAA;AACnE,EAAA,IAAI,IAAA;AACJ,EAAA,IAAI,IAAA,KAAS,SAAS,IAAA,GAAO,CAAA;AAAA,OAAA,IACpB,IAAA,KAAS,MAAA,EAAQ,IAAA,GAAO,KAAA,CAAM,MAAA,GAAS,CAAA;AAAA,OAAA,IACvC,YAAY,EAAA,EAAI,IAAA,GAAO,SAAS,MAAA,GAAS,CAAA,GAAI,MAAM,MAAA,GAAS,CAAA;AAAA,OAChE,IAAA,GAAA,CAAQ,WAAW,IAAA,KAAS,MAAA,GAAS,IAAI,EAAA,CAAA,GAAM,KAAA,CAAM,UAAU,KAAA,CAAM,MAAA;AAC1E,EAAA,KAAA,CAAM,IAAI,EAAG,KAAA,EAAM;AACnB,EAAA,OAAO,IAAA;AACT;AAMO,SAAS,WAAA,CAAY,MAA0B,IAAA,EAAsB;AAC1E,EAAA,MAAM,KAAA,GAAQ,UAAU,IAAI,CAAA;AAC5B,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,EAAA;AAC/B,EAAA,MAAM,MAAA,GAAS,KAAK,WAAA,EAAY;AAChC,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,aAA4B,CAAA;AACjE,EAAA,KAAA,IAAS,IAAA,GAAO,CAAA,EAAG,IAAA,IAAQ,KAAA,CAAM,QAAQ,IAAA,EAAA,EAAQ;AAC/C,IAAA,MAAM,CAAA,GAAA,CAAK,KAAA,GAAQ,IAAA,GAAO,KAAA,CAAM,UAAU,KAAA,CAAM,MAAA;AAChD,IAAA,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA,CAAG,WAAA,IAAe,EAAA,EAAI,IAAA,EAAK,CAAE,WAAA,EAAY,CAAE,UAAA,CAAW,MAAM,CAAA,EAAG;AACzE,MAAA,KAAA,CAAM,CAAC,EAAG,KAAA,EAAM;AAChB,MAAA,OAAO,CAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,OAAO,EAAA;AACT;AClCA,SAAS,kBAAkB,EAAA,EAAiC;AAC1D,EAAA,IAAI,EAAE,EAAA,YAAc,WAAA,CAAA,EAAc,OAAO,KAAA;AACzC,EAAA,MAAM,MAAM,EAAA,CAAG,OAAA;AACf,EAAA,IAAI,GAAA,KAAQ,QAAA,IAAY,GAAA,KAAQ,SAAA,EAAW,OAAO,IAAA;AAClD,EAAA,IAAI,GAAA,KAAQ,GAAA,EAAK,OAAO,EAAA,CAAG,aAAa,MAAM,CAAA;AAC9C,EAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,IAAA,MAAM,OAAQ,EAAA,CAAwB,IAAA;AACtC,IAAA,OAAO,SAAS,QAAA,IAAY,IAAA,KAAS,QAAA,IAAY,IAAA,KAAS,WAAW,IAAA,KAAS,OAAA;AAAA,EAChF;AACA,EAAA,OAAO,KAAA;AACT;AAEO,SAAS,eAAA,CAAgB,EAAE,QAAA,EAAS,EAAyB;AAClE,EAAA,MAAM,EAAE,OAAA,EAAS,IAAA,EAAM,WAAW,eAAA,EAAiB,OAAA,KAAY,WAAA,EAAY;AAI3E,EAAA,MAAM,SAAA,GAAY,CAAC,CAAA,KAAsC;AACvD,IAAA,IAAI,EAAE,gBAAA,EAAkB;AAExB,IAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,WAAA,IAAe,CAAA,CAAE,QAAQ,SAAA,EAAW;AAChD,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,MAAM,MAAA,GAAS,CAAA,CAAE,GAAA,KAAQ,WAAA,GAAc,OAAA,GAAU,MAAA;AACjD,MAAA,IAAI,IAAA,IAAQ,QAAQ,OAAA,EAAS;AAC3B,QAAA,aAAA,CAAc,OAAA,CAAQ,SAAS,MAAM,CAAA;AACrC,QAAA;AAAA,MACF;AACA,MAAA,eAAA,CAAgB,OAAA,GAAU,MAAA;AAC1B,MAAA,OAAA,CAAQ,IAAI,CAAA;AACZ,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,OAAA,IAAW,CAAA,CAAE,QAAQ,GAAA,EAAK;AACtC,MAAA,IAAI,CAAC,IAAA,EAAM,eAAA,CAAgB,OAAA,GAAU,OAAA;AACrC,MAAA,IAAI,iBAAA,CAAkB,CAAA,CAAE,MAAM,CAAA,EAAG;AACjC,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,IAAI,IAAA,kBAAsB,OAAA,GAAU,IAAA;AACpC,MAAA,OAAA,CAAQ,CAAC,IAAI,CAAA;AAAA,IACf;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,OAAA,GAAU,CAAC,EAAA,KAAoC;AACnD,IAAA,IAAI,IAAA,kBAAsB,OAAA,GAAU,IAAA;AACpC,IAAA,OAAA,CAAQ,CAAC,IAAI,CAAA;AAAA,EACf,CAAA;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,SAAA;AAAA,MACL,mCAAA,EAAkC,EAAA;AAAA,MAClC,SAAA,EAAU,aAAA;AAAA,MACV,OAAA;AAAA,MACA,SAAA;AAAA,MACA,eAAA,EAAe,IAAA;AAAA,MACf,eAAA,EAAc,MAAA;AAAA,MAEb;AAAA;AAAA,GACH;AAEJ;AAEA,eAAA,CAAgB,WAAA,GAAc,iBAAA;AC5DvB,SAAS,aAAA,CAAc,EAAE,QAAA,EAAU,SAAA,EAAU,EAAuB;AACzE,EAAA,MAAM;AAAA,IACJ,IAAA;AAAA,IACA,OAAA;AAAA,IACA,SAAA;AAAA,IACA,cAAA;AAAA,IACA,SAAA;AAAA,IACA,MAAA;AAAA,IACA,eAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,MACE,WAAA,EAAY;AAChB,EAAA,MAAM,WAAA,GAAc,OAAuB,IAAI,CAAA;AAC/C,EAAA,MAAM,UAAA,GAAa,OAAuB,IAAI,CAAA;AAE9C,EAAA,MAAM,QAAA,GAAW,mBAAA,CAAoB,SAAA,EAAW,WAAA,EAAa;AAAA,IAC3D,SAAA;AAAA,IACA,MAAA;AAAA,IACA,OAAA,EAAS;AAAA,GACV,CAAA;AAED,EAAA,MAAM,KAAA,GAAQ,YAAY,MAAM;AAC9B,IAAA,eAAA,CAAgB,OAAA,GAAU,IAAA;AAC1B,IAAA,OAAA,CAAQ,KAAK,CAAA;AACb,IAAA,cAAA,CAAe,EAAE,CAAA;AAAA,EACnB,CAAA,EAAG,CAAC,OAAA,EAAS,cAAA,EAAgB,eAAe,CAAC,CAAA;AAE7C,EAAA,eAAA,CAAgB,UAAA,EAAY,KAAA,EAAO,IAAA,EAAM,SAAS,CAAA;AAIlD,EAAA,YAAA,CAAa,MAAM;AACjB,IAAA,KAAA,EAAM;AACN,IAAA,YAAA,EAAa;AAAA,EACf,GAAG,IAAI,CAAA;AAEP,EAAA,MAAM,EAAE,OAAA,EAAS,SAAA,EAAW,WAAW,GAAA,EAAK,OAAA,KAAY,YAAA,CAAa;AAAA,IACnE,IAAA;AAAA,IACA,UAAA,EAAY,gBAAA;AAAA,IACZ,SAAA,EAAW;AAAA,GACZ,CAAA;AAQD,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,SAAgC,IAAI,CAAA;AAEhE,EAAA,MAAM,UAAA,GAAa,WAAA;AAAA,IACjB,CAAC,IAAA,KAAgC;AAC/B,MAAA,UAAA,CAAW,OAAA,GAAU,IAAA;AACrB,MAAA,WAAA,CAAY,OAAA,GAAU,IAAA;AACtB,MAAC,QAAuD,OAAA,GAAU,IAAA;AAClE,MAAA,OAAA,CAAQ,OAAA,GAAU,IAAA;AAClB,MAAA,SAAA,CAAU,IAAI,CAAA;AAAA,IAChB,CAAA;AAAA,IACA,CAAC,SAAS,OAAO;AAAA,GACnB;AAGA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,MAAA,EAAQ;AACtB,IAAA,MAAM,SAAS,eAAA,CAAgB,OAAA;AAC/B,IAAA,IAAI,CAAC,MAAA,EAAQ;AACb,IAAA,eAAA,CAAgB,OAAA,GAAU,IAAA;AAC1B,IAAA,cAAA,CAAe,aAAA,CAAc,MAAA,EAAQ,MAAM,CAAC,CAAA;AAAA,EAC9C,GAAG,CAAC,IAAA,EAAM,MAAA,EAAQ,eAAA,EAAiB,cAAc,CAAC,CAAA;AAIlD,EAAA,MAAM,SAAA,GAAY,CAAC,CAAA,KAA2C;AAC5D,IAAA,MAAM,OAAO,CAAA,CAAE,aAAA;AACf,IAAA,MAAM,KAAA,GAAmC;AAAA,MACvC,SAAA,EAAW,MAAA;AAAA,MACX,OAAA,EAAS,MAAA;AAAA,MACT,IAAA,EAAM,OAAA;AAAA,MACN,GAAA,EAAK;AAAA,KACP;AACA,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,CAAA,CAAE,GAAG,CAAA;AACxB,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,cAAA,CAAe,aAAA,CAAc,IAAA,EAAM,IAAI,CAAC,CAAA;AACxC,MAAA;AAAA,IACF;AACA,IAAA,IAAI,CAAA,CAAE,QAAQ,KAAA,EAAO;AAKnB,MAAA,KAAA,EAAM;AACN,MAAA,YAAA,EAAa;AACb,MAAA;AAAA,IACF;AACA,IAAA,IAAI,CAAA,CAAE,GAAA,CAAI,MAAA,KAAW,CAAA,IAAK,EAAE,GAAA,KAAQ,GAAA,IAAO,CAAC,CAAA,CAAE,WAAW,CAAC,CAAA,CAAE,OAAA,IAAW,CAAC,EAAE,MAAA,EAAQ;AAChF,MAAA,MAAM,CAAA,GAAI,WAAA,CAAY,IAAA,EAAM,CAAA,CAAE,GAAG,CAAA;AACjC,MAAA,IAAI,CAAA,IAAK,CAAA,EAAG,cAAA,CAAe,CAAC,CAAA;AAAA,IAC9B;AAAA,EACF,CAAA;AAEA,EAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AAErB,EAAA,uBACEA,GAAAA,CAAC,MAAA,EAAA,EACC,QAAA,kBAAAA,GAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,UAAA;AAAA,MACL,2BAAA,EAA0B,EAAA;AAAA,MAC1B,IAAA,EAAK,MAAA;AAAA,MACL,SAAA;AAAA,MACA,SAAA,EAAW,EAAA;AAAA,QACT,wSAAA;AAAA,QACA,SAAA;AAAA,QACA;AAAA,OACF;AAAA,MACA,OAAO,EAAE,IAAA,EAAM,SAAS,CAAA,EAAG,GAAA,EAAK,SAAS,CAAA,EAAE;AAAA,MAE1C;AAAA;AAAA,GACH,EACF,CAAA;AAEJ;AAEA,aAAA,CAAc,WAAA,GAAc,eAAA;AClIrB,SAAS,YAAA,CAAa;AAAA,EAC3B,QAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA,GAAW,KAAA;AAAA,EACX,MAAA,GAAS,KAAA;AAAA,EACT;AACF,CAAA,EAAsB;AACpB,EAAA,MAAM,EAAE,OAAA,EAAS,YAAA,EAAa,GAAI,WAAA,EAAY;AAE9C,EAAA,uBACEA,GAAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,gCAAA,EAA+B,EAAA;AAAA,MAC/B,IAAA,EAAK,QAAA;AAAA,MACL,IAAA,EAAK,UAAA;AAAA,MACL,QAAA;AAAA,MACA,eAAA,EAAe,QAAA;AAAA,MACf,SAAA,EAAW,EAAA;AAAA,QACT,mFAAA;AAAA,QACA,SACI,sEAAA,GACA,6QAAA;AAAA,QACJ,QAAA,IAAY,+BAAA;AAAA,QACZ;AAAA,OACF;AAAA,MACA,OAAA,EAAS,CAAC,CAAA,KAAM;AACd,QAAA,IAAI,QAAA,EAAU;AACd,QAAA,MAAM,QAAA,GAAW,QAAA,CAAS,aAAA,KAAkB,CAAA,CAAE,aAAA;AAC9C,QAAA,OAAA,IAAU;AACV,QAAA,OAAA,CAAQ,KAAK,CAAA;AAGb,QAAA,IAAI,QAAA,IAAY,QAAA,CAAS,aAAA,KAAkB,CAAA,CAAE,eAAe,YAAA,EAAa;AAAA,MAC3E,CAAA;AAAA,MAEC;AAAA;AAAA,GACH;AAEJ;AAEA,YAAA,CAAa,WAAA,GAAc,cAAA;ACxCpB,SAAS,iBAAA,CAAkB,EAAE,SAAA,EAAU,EAA2B;AACvE,EAAA,uBACEA,GAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,qCAAA,EAAoC,EAAA;AAAA,MACpC,IAAA,EAAK,WAAA;AAAA,MACL,SAAA,EAAW,EAAA;AAAA,QACT,+HAAA;AAAA,QACA;AAAA;AACF;AAAA,GACF;AAEJ;AAEA,iBAAA,CAAkB,WAAA,GAAc,mBAAA;ACRhC,IAAM,iBAAA,GACJ,2IAAA;AAEF,SAAS,YAAA,CAAa;AAAA,EACpB,QAAA;AAAA,EACA,SAAA,GAAY,cAAA;AAAA,EACZ,MAAA,GAAS;AACX,CAAA,EAAkB;AAChB,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAIC,SAAS,KAAK,CAAA;AACtC,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAIA,SAAS,EAAE,CAAA;AACjD,EAAA,MAAM,SAAA,GAAYC,OAAoB,IAAI,CAAA;AAC1C,EAAA,MAAM,eAAA,GAAkBA,OAAgC,IAAI,CAAA;AAC5D,EAAA,MAAM,OAAA,GAAUA,OAA2B,IAAI,CAAA;AAE/C,EAAA,MAAM,YAAA,GAAeC,YAAY,MAAM;AACrC,IAAA,MAAM,SAAS,SAAA,CAAU,OAAA;AACzB,IAAA,IAAI,CAAC,MAAA,EAAQ;AACb,IAAA,MAAM,MAAA,GACJ,OAAO,aAAA,CAA2B,iBAAiB,MAAM,MAAA,CAAO,QAAA,IAAY,IAAI,MAAA,GAAS,IAAA,CAAA;AAC3F,IAAA,MAAA,EAAQ,KAAA,EAAM;AAAA,EAChB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,GAAA,GAAM,OAAA;AAAA,IACV,OAAO;AAAA,MACL,IAAA;AAAA,MACA,OAAA;AAAA,MACA,SAAA;AAAA,MACA,WAAA;AAAA,MACA,cAAA;AAAA,MACA,SAAA;AAAA,MACA,MAAA;AAAA,MACA,eAAA;AAAA,MACA,OAAA;AAAA,MACA;AAAA,KACF,CAAA;AAAA,IACA,CAAC,IAAA,EAAM,SAAA,EAAW,WAAA,EAAa,SAAA,EAAW,QAAQ,YAAY;AAAA,GAChE;AAEA,EAAA,uBACEH,GAAAA,CAAC,eAAA,CAAgB,UAAhB,EAAyB,KAAA,EAAO,KAAM,QAAA,EAAS,CAAA;AAEpD;AAEO,IAAM,QAAA,GAAW,MAAA,CAAO,MAAA,CAAO,YAAA,EAAc;AAAA,EAClD,OAAA,EAAS,eAAA;AAAA,EACT,KAAA,EAAO,aAAA;AAAA,EACP,IAAA,EAAM,YAAA;AAAA,EACN,SAAA,EAAW;AACb,CAAC","file":"chunk-BXPLERIJ.js","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { DropdownContextValue } from \"./Dropdown.types\";\n\nexport const DropdownContext = createContext<DropdownContextValue | null>(null);\n\nexport function useDropdown(): DropdownContextValue {\n const ctx = useContext(DropdownContext);\n if (!ctx) {\n throw new Error(\n \"Dropdown compound components must be used within <Dropdown>\",\n );\n }\n return ctx;\n}\n","/** Enabled menu items, in DOM order. Disabled items are skipped. */\nexport function menuItems(menu: HTMLElement | null): HTMLElement[] {\n if (!menu) return [];\n return Array.from(\n menu.querySelectorAll<HTMLElement>('[role=\"menuitem\"]:not([aria-disabled=\"true\"]):not([disabled])'),\n );\n}\n\nexport type FocusMove = \"first\" | \"last\" | \"next\" | \"prev\";\n\n/**\n * Focus an item relative to the one that currently has focus, wrapping at both\n * ends. Returns the index focused, or -1 when the menu has no enabled item.\n */\nexport function focusMenuItem(menu: HTMLElement | null, move: FocusMove): number {\n const items = menuItems(menu);\n if (items.length === 0) return -1;\n const current = items.indexOf(document.activeElement as HTMLElement);\n let next: number;\n if (move === \"first\") next = 0;\n else if (move === \"last\") next = items.length - 1;\n else if (current === -1) next = move === \"next\" ? 0 : items.length - 1;\n else next = (current + (move === \"next\" ? 1 : -1) + items.length) % items.length;\n items[next]!.focus();\n return next;\n}\n\n/**\n * Typeahead: focus the next enabled item (after the focused one, wrapping)\n * whose text starts with `char`. Returns the index focused, or -1 for no match.\n */\nexport function focusByChar(menu: HTMLElement | null, char: string): number {\n const items = menuItems(menu);\n if (items.length === 0) return -1;\n const needle = char.toLowerCase();\n const start = items.indexOf(document.activeElement as HTMLElement);\n for (let step = 1; step <= items.length; step++) {\n const i = (start + step + items.length) % items.length;\n if ((items[i]!.textContent ?? \"\").trim().toLowerCase().startsWith(needle)) {\n items[i]!.focus();\n return i;\n }\n }\n return -1;\n}\n","import type { KeyboardEvent, MouseEvent } from \"react\";\nimport { useDropdown } from \"./Dropdown.context\";\nimport { focusMenuItem } from \"./dropdown-focus\";\nimport type { DropdownTriggerProps } from \"./Dropdown.types\";\n\n/**\n * Elements the browser itself turns Enter / Space into a click on. For these\n * the trigger lets the native click open the menu; for anything else (an\n * avatar span with `tabIndex`) it opens the menu itself.\n */\nfunction activatesNatively(el: EventTarget | null): boolean {\n if (!(el instanceof HTMLElement)) return false;\n const tag = el.tagName;\n if (tag === \"BUTTON\" || tag === \"SUMMARY\") return true;\n if (tag === \"A\") return el.hasAttribute(\"href\");\n if (tag === \"INPUT\") {\n const type = (el as HTMLInputElement).type;\n return type === \"button\" || type === \"submit\" || type === \"reset\" || type === \"image\";\n }\n return false;\n}\n\nexport function DropdownTrigger({ children }: DropdownTriggerProps) {\n const { setOpen, open, anchorRef, pendingFocusRef, menuRef } = useDropdown();\n\n // WAI-ARIA menu button: Enter / Space / ArrowDown open the menu on its first\n // item, ArrowUp on its last.\n const onKeyDown = (e: KeyboardEvent<HTMLSpanElement>) => {\n if (e.defaultPrevented) return;\n\n if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\") {\n e.preventDefault();\n const target = e.key === \"ArrowDown\" ? \"first\" : \"last\";\n if (open && menuRef.current) {\n focusMenuItem(menuRef.current, target);\n return;\n }\n pendingFocusRef.current = target;\n setOpen(true);\n return;\n }\n\n if (e.key === \"Enter\" || e.key === \" \") {\n if (!open) pendingFocusRef.current = \"first\";\n if (activatesNatively(e.target)) return; // its click toggles the menu\n e.preventDefault();\n if (open) pendingFocusRef.current = null;\n setOpen(!open);\n }\n };\n\n const onClick = (_e: MouseEvent<HTMLSpanElement>) => {\n if (open) pendingFocusRef.current = null;\n setOpen(!open);\n };\n\n return (\n <span\n ref={anchorRef as React.RefObject<HTMLSpanElement>}\n data-react-fancy-dropdown-trigger=\"\"\n className=\"inline-flex\"\n onClick={onClick}\n onKeyDown={onKeyDown}\n aria-expanded={open}\n aria-haspopup=\"menu\"\n >\n {children}\n </span>\n );\n}\n\nDropdownTrigger.displayName = \"DropdownTrigger\";\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { focusByChar, focusMenuItem, type FocusMove } from \"./dropdown-focus\";\nimport { cn } from \"../../utils/cn\";\nimport { Portal } from \"../Portal\";\nimport { useDropdown } from \"./Dropdown.context\";\nimport { useFloatingPosition } from \"../../hooks/use-floating-position\";\nimport { useOutsideClick } from \"../../hooks/use-outside-click\";\nimport { useEscapeKey } from \"../../hooks/use-escape-key\";\nimport { useAnimation } from \"../../hooks/use-animation\";\nimport type { DropdownItemsProps } from \"./Dropdown.types\";\n\nexport function DropdownItems({ children, className }: DropdownItemsProps) {\n const {\n open,\n setOpen,\n anchorRef,\n setActiveIndex,\n placement,\n offset,\n pendingFocusRef,\n menuRef,\n focusTrigger,\n } = useDropdown();\n const floatingRef = useRef<HTMLDivElement>(null);\n const outsideRef = useRef<HTMLDivElement>(null);\n\n const position = useFloatingPosition(anchorRef, floatingRef, {\n placement,\n offset,\n enabled: open,\n });\n\n const close = useCallback(() => {\n pendingFocusRef.current = null;\n setOpen(false);\n setActiveIndex(-1);\n }, [setOpen, setActiveIndex, pendingFocusRef]);\n\n useOutsideClick(outsideRef, close, open, anchorRef);\n // Escape closes THIS menu (the innermost layer) and returns focus to where\n // the menu came from. An outside click does not move focus: the user put it\n // somewhere already.\n useEscapeKey(() => {\n close();\n focusTrigger();\n }, open);\n\n const { mounted, className: animClass, ref: animRef } = useAnimation({\n open,\n enterClass: \"fancy-scale-in\",\n exitClass: \"fancy-fade-out\",\n });\n\n // The menu element, as STATE. It does not exist when `open` first turns true:\n // `useAnimation` mounts it a render later and `Portal` a render after that.\n // Before 5.64.1 the key handler lived in an effect keyed on `open` that\n // returned early while the element was null and never re-ran, so the arrow\n // keys did nothing in any Dropdown. Keying on the element itself fixes the\n // timing for good.\n const [menuEl, setMenuEl] = useState<HTMLDivElement | null>(null);\n\n const setMenuRef = useCallback(\n (node: HTMLDivElement | null) => {\n outsideRef.current = node;\n floatingRef.current = node;\n (animRef as React.MutableRefObject<HTMLElement | null>).current = node;\n menuRef.current = node;\n setMenuEl(node);\n },\n [animRef, menuRef],\n );\n\n // Opened from the keyboard: focus the first / last item once it exists.\n useEffect(() => {\n if (!open || !menuEl) return;\n const target = pendingFocusRef.current;\n if (!target) return;\n pendingFocusRef.current = null;\n setActiveIndex(focusMenuItem(menuEl, target));\n }, [open, menuEl, pendingFocusRef, setActiveIndex]);\n\n // Keys are handled on the menu itself: focus is inside it while it is driven\n // from the keyboard, and React delivers the event through the portal.\n const onKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n const menu = e.currentTarget;\n const moves: Record<string, FocusMove> = {\n ArrowDown: \"next\",\n ArrowUp: \"prev\",\n Home: \"first\",\n End: \"last\",\n };\n const move = moves[e.key];\n if (move) {\n e.preventDefault();\n setActiveIndex(focusMenuItem(menu, move));\n return;\n }\n if (e.key === \"Tab\") {\n // Close, and put focus back on the trigger WITHOUT preventing the\n // default: the browser's own Tab then moves on from the trigger's place\n // in the tab order rather than from the end of <body>, where the\n // portalled menu lives.\n close();\n focusTrigger();\n return;\n }\n if (e.key.length === 1 && e.key !== \" \" && !e.ctrlKey && !e.metaKey && !e.altKey) {\n const i = focusByChar(menu, e.key);\n if (i >= 0) setActiveIndex(i);\n }\n };\n\n if (!mounted) return null;\n\n return (\n <Portal>\n <div\n ref={setMenuRef}\n data-react-fancy-dropdown=\"\"\n role=\"menu\"\n onKeyDown={onKeyDown}\n className={cn(\n \"fixed z-50 min-w-[8rem] rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] p-1 shadow-lg dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n animClass,\n className,\n )}\n style={{ left: position.x, top: position.y }}\n >\n {children}\n </div>\n </Portal>\n );\n}\n\nDropdownItems.displayName = \"DropdownItems\";\n","import { cn } from \"../../utils/cn\";\nimport { useDropdown } from \"./Dropdown.context\";\nimport type { DropdownItemProps } from \"./Dropdown.types\";\n\nexport function DropdownItem({\n children,\n onClick,\n disabled = false,\n danger = false,\n className,\n}: DropdownItemProps) {\n const { setOpen, focusTrigger } = useDropdown();\n\n return (\n <button\n data-react-fancy-dropdown-item=\"\"\n type=\"button\"\n role=\"menuitem\"\n disabled={disabled}\n aria-disabled={disabled}\n className={cn(\n \"flex w-full items-center rounded-lg px-3 py-2 text-left text-sm transition-colors\",\n danger\n ? \"text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950\"\n : \"text-[color:var(--fancy-text-soft,var(--color-zinc-700))] hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n disabled && \"cursor-not-allowed opacity-50\",\n className,\n )}\n onClick={(e) => {\n if (disabled) return;\n const hadFocus = document.activeElement === e.currentTarget;\n onClick?.();\n setOpen(false);\n // The item is about to unmount; without this, focus falls to <body>.\n // Only when the item held focus, and only if onClick did not move it.\n if (hadFocus && document.activeElement === e.currentTarget) focusTrigger();\n }}\n >\n {children}\n </button>\n );\n}\n\nDropdownItem.displayName = \"DropdownItem\";\n","import { cn } from \"../../utils/cn\";\nimport type { DropdownSeparatorProps } from \"./Dropdown.types\";\n\nexport function DropdownSeparator({ className }: DropdownSeparatorProps) {\n return (\n <div\n data-react-fancy-dropdown-separator=\"\"\n role=\"separator\"\n className={cn(\n \"my-1 h-px bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n />\n );\n}\n\nDropdownSeparator.displayName = \"DropdownSeparator\";\n","import { useCallback, useMemo, useRef, useState } from \"react\";\nimport { DropdownContext } from \"./Dropdown.context\";\nimport { DropdownTrigger } from \"./DropdownTrigger\";\nimport { DropdownItems } from \"./DropdownItems\";\nimport { DropdownItem } from \"./DropdownItem\";\nimport { DropdownSeparator } from \"./DropdownSeparator\";\nimport type { DropdownProps } from \"./Dropdown.types\";\n\nconst TRIGGER_FOCUSABLE =\n 'button:not([disabled]), a[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\nfunction DropdownRoot({\n children,\n placement = \"bottom-start\",\n offset = 4,\n}: DropdownProps) {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState(-1);\n const anchorRef = useRef<HTMLElement>(null);\n const pendingFocusRef = useRef<\"first\" | \"last\" | null>(null);\n const menuRef = useRef<HTMLElement | null>(null);\n\n const focusTrigger = useCallback(() => {\n const anchor = anchorRef.current;\n if (!anchor) return;\n const target =\n anchor.querySelector<HTMLElement>(TRIGGER_FOCUSABLE) ?? (anchor.tabIndex >= 0 ? anchor : null);\n target?.focus();\n }, []);\n\n const ctx = useMemo(\n () => ({\n open,\n setOpen,\n anchorRef,\n activeIndex,\n setActiveIndex,\n placement,\n offset,\n pendingFocusRef,\n menuRef,\n focusTrigger,\n }),\n [open, anchorRef, activeIndex, placement, offset, focusTrigger],\n );\n\n return (\n <DropdownContext.Provider value={ctx}>{children}</DropdownContext.Provider>\n );\n}\n\nexport const Dropdown = Object.assign(DropdownRoot, {\n Trigger: DropdownTrigger,\n Items: DropdownItems,\n Item: DropdownItem,\n Separator: DropdownSeparator,\n});\n"]}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkFQZE5N2N_cjs = require('./chunk-FQZE5N2N.cjs');
|
|
4
|
+
var chunkJ2DI6RMR_cjs = require('./chunk-J2DI6RMR.cjs');
|
|
5
|
+
var chunkTI7D4QDM_cjs = require('./chunk-TI7D4QDM.cjs');
|
|
6
|
+
var chunk57FQGQJK_cjs = require('./chunk-57FQGQJK.cjs');
|
|
7
|
+
var chunkOUYC6EIL_cjs = require('./chunk-OUYC6EIL.cjs');
|
|
8
|
+
var chunkZU5Q77OT_cjs = require('./chunk-ZU5Q77OT.cjs');
|
|
9
|
+
var react = require('react');
|
|
10
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
11
|
+
|
|
12
|
+
var DropdownContext = react.createContext(null);
|
|
13
|
+
function useDropdown() {
|
|
14
|
+
const ctx = react.useContext(DropdownContext);
|
|
15
|
+
if (!ctx) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
"Dropdown compound components must be used within <Dropdown>"
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
return ctx;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/components/Dropdown/dropdown-focus.ts
|
|
24
|
+
function menuItems(menu) {
|
|
25
|
+
if (!menu) return [];
|
|
26
|
+
return Array.from(
|
|
27
|
+
menu.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"]):not([disabled])')
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
function focusMenuItem(menu, move) {
|
|
31
|
+
const items = menuItems(menu);
|
|
32
|
+
if (items.length === 0) return -1;
|
|
33
|
+
const current = items.indexOf(document.activeElement);
|
|
34
|
+
let next;
|
|
35
|
+
if (move === "first") next = 0;
|
|
36
|
+
else if (move === "last") next = items.length - 1;
|
|
37
|
+
else if (current === -1) next = move === "next" ? 0 : items.length - 1;
|
|
38
|
+
else next = (current + (move === "next" ? 1 : -1) + items.length) % items.length;
|
|
39
|
+
items[next].focus();
|
|
40
|
+
return next;
|
|
41
|
+
}
|
|
42
|
+
function focusByChar(menu, char) {
|
|
43
|
+
const items = menuItems(menu);
|
|
44
|
+
if (items.length === 0) return -1;
|
|
45
|
+
const needle = char.toLowerCase();
|
|
46
|
+
const start = items.indexOf(document.activeElement);
|
|
47
|
+
for (let step = 1; step <= items.length; step++) {
|
|
48
|
+
const i = (start + step + items.length) % items.length;
|
|
49
|
+
if ((items[i].textContent ?? "").trim().toLowerCase().startsWith(needle)) {
|
|
50
|
+
items[i].focus();
|
|
51
|
+
return i;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return -1;
|
|
55
|
+
}
|
|
56
|
+
function activatesNatively(el) {
|
|
57
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
58
|
+
const tag = el.tagName;
|
|
59
|
+
if (tag === "BUTTON" || tag === "SUMMARY") return true;
|
|
60
|
+
if (tag === "A") return el.hasAttribute("href");
|
|
61
|
+
if (tag === "INPUT") {
|
|
62
|
+
const type = el.type;
|
|
63
|
+
return type === "button" || type === "submit" || type === "reset" || type === "image";
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
function DropdownTrigger({ children }) {
|
|
68
|
+
const { setOpen, open, anchorRef, pendingFocusRef, menuRef } = useDropdown();
|
|
69
|
+
const onKeyDown = (e) => {
|
|
70
|
+
if (e.defaultPrevented) return;
|
|
71
|
+
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
const target = e.key === "ArrowDown" ? "first" : "last";
|
|
74
|
+
if (open && menuRef.current) {
|
|
75
|
+
focusMenuItem(menuRef.current, target);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
pendingFocusRef.current = target;
|
|
79
|
+
setOpen(true);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
83
|
+
if (!open) pendingFocusRef.current = "first";
|
|
84
|
+
if (activatesNatively(e.target)) return;
|
|
85
|
+
e.preventDefault();
|
|
86
|
+
if (open) pendingFocusRef.current = null;
|
|
87
|
+
setOpen(!open);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const onClick = (_e) => {
|
|
91
|
+
if (open) pendingFocusRef.current = null;
|
|
92
|
+
setOpen(!open);
|
|
93
|
+
};
|
|
94
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
95
|
+
"span",
|
|
96
|
+
{
|
|
97
|
+
ref: anchorRef,
|
|
98
|
+
"data-react-fancy-dropdown-trigger": "",
|
|
99
|
+
className: "inline-flex",
|
|
100
|
+
onClick,
|
|
101
|
+
onKeyDown,
|
|
102
|
+
"aria-expanded": open,
|
|
103
|
+
"aria-haspopup": "menu",
|
|
104
|
+
children
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
DropdownTrigger.displayName = "DropdownTrigger";
|
|
109
|
+
function DropdownItems({ children, className }) {
|
|
110
|
+
const {
|
|
111
|
+
open,
|
|
112
|
+
setOpen,
|
|
113
|
+
anchorRef,
|
|
114
|
+
setActiveIndex,
|
|
115
|
+
placement,
|
|
116
|
+
offset,
|
|
117
|
+
pendingFocusRef,
|
|
118
|
+
menuRef,
|
|
119
|
+
focusTrigger
|
|
120
|
+
} = useDropdown();
|
|
121
|
+
const floatingRef = react.useRef(null);
|
|
122
|
+
const outsideRef = react.useRef(null);
|
|
123
|
+
const position = chunkJ2DI6RMR_cjs.useFloatingPosition(anchorRef, floatingRef, {
|
|
124
|
+
placement,
|
|
125
|
+
offset,
|
|
126
|
+
enabled: open
|
|
127
|
+
});
|
|
128
|
+
const close = react.useCallback(() => {
|
|
129
|
+
pendingFocusRef.current = null;
|
|
130
|
+
setOpen(false);
|
|
131
|
+
setActiveIndex(-1);
|
|
132
|
+
}, [setOpen, setActiveIndex, pendingFocusRef]);
|
|
133
|
+
chunk57FQGQJK_cjs.useOutsideClick(outsideRef, close, open, anchorRef);
|
|
134
|
+
chunkOUYC6EIL_cjs.useEscapeKey(() => {
|
|
135
|
+
close();
|
|
136
|
+
focusTrigger();
|
|
137
|
+
}, open);
|
|
138
|
+
const { mounted, className: animClass, ref: animRef } = chunkFQZE5N2N_cjs.useAnimation({
|
|
139
|
+
open,
|
|
140
|
+
enterClass: "fancy-scale-in",
|
|
141
|
+
exitClass: "fancy-fade-out"
|
|
142
|
+
});
|
|
143
|
+
const [menuEl, setMenuEl] = react.useState(null);
|
|
144
|
+
const setMenuRef = react.useCallback(
|
|
145
|
+
(node) => {
|
|
146
|
+
outsideRef.current = node;
|
|
147
|
+
floatingRef.current = node;
|
|
148
|
+
animRef.current = node;
|
|
149
|
+
menuRef.current = node;
|
|
150
|
+
setMenuEl(node);
|
|
151
|
+
},
|
|
152
|
+
[animRef, menuRef]
|
|
153
|
+
);
|
|
154
|
+
react.useEffect(() => {
|
|
155
|
+
if (!open || !menuEl) return;
|
|
156
|
+
const target = pendingFocusRef.current;
|
|
157
|
+
if (!target) return;
|
|
158
|
+
pendingFocusRef.current = null;
|
|
159
|
+
setActiveIndex(focusMenuItem(menuEl, target));
|
|
160
|
+
}, [open, menuEl, pendingFocusRef, setActiveIndex]);
|
|
161
|
+
const onKeyDown = (e) => {
|
|
162
|
+
const menu = e.currentTarget;
|
|
163
|
+
const moves = {
|
|
164
|
+
ArrowDown: "next",
|
|
165
|
+
ArrowUp: "prev",
|
|
166
|
+
Home: "first",
|
|
167
|
+
End: "last"
|
|
168
|
+
};
|
|
169
|
+
const move = moves[e.key];
|
|
170
|
+
if (move) {
|
|
171
|
+
e.preventDefault();
|
|
172
|
+
setActiveIndex(focusMenuItem(menu, move));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (e.key === "Tab") {
|
|
176
|
+
close();
|
|
177
|
+
focusTrigger();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (e.key.length === 1 && e.key !== " " && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
181
|
+
const i = focusByChar(menu, e.key);
|
|
182
|
+
if (i >= 0) setActiveIndex(i);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
if (!mounted) return null;
|
|
186
|
+
return /* @__PURE__ */ jsxRuntime.jsx(chunkTI7D4QDM_cjs.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
187
|
+
"div",
|
|
188
|
+
{
|
|
189
|
+
ref: setMenuRef,
|
|
190
|
+
"data-react-fancy-dropdown": "",
|
|
191
|
+
role: "menu",
|
|
192
|
+
onKeyDown,
|
|
193
|
+
className: chunkZU5Q77OT_cjs.cn(
|
|
194
|
+
"fixed z-50 min-w-[8rem] rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] p-1 shadow-lg dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]",
|
|
195
|
+
animClass,
|
|
196
|
+
className
|
|
197
|
+
),
|
|
198
|
+
style: { left: position.x, top: position.y },
|
|
199
|
+
children
|
|
200
|
+
}
|
|
201
|
+
) });
|
|
202
|
+
}
|
|
203
|
+
DropdownItems.displayName = "DropdownItems";
|
|
204
|
+
function DropdownItem({
|
|
205
|
+
children,
|
|
206
|
+
onClick,
|
|
207
|
+
disabled = false,
|
|
208
|
+
danger = false,
|
|
209
|
+
className
|
|
210
|
+
}) {
|
|
211
|
+
const { setOpen, focusTrigger } = useDropdown();
|
|
212
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
213
|
+
"button",
|
|
214
|
+
{
|
|
215
|
+
"data-react-fancy-dropdown-item": "",
|
|
216
|
+
type: "button",
|
|
217
|
+
role: "menuitem",
|
|
218
|
+
disabled,
|
|
219
|
+
"aria-disabled": disabled,
|
|
220
|
+
className: chunkZU5Q77OT_cjs.cn(
|
|
221
|
+
"flex w-full items-center rounded-lg px-3 py-2 text-left text-sm transition-colors",
|
|
222
|
+
danger ? "text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950" : "text-[color:var(--fancy-text-soft,var(--color-zinc-700))] hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]",
|
|
223
|
+
disabled && "cursor-not-allowed opacity-50",
|
|
224
|
+
className
|
|
225
|
+
),
|
|
226
|
+
onClick: (e) => {
|
|
227
|
+
if (disabled) return;
|
|
228
|
+
const hadFocus = document.activeElement === e.currentTarget;
|
|
229
|
+
onClick?.();
|
|
230
|
+
setOpen(false);
|
|
231
|
+
if (hadFocus && document.activeElement === e.currentTarget) focusTrigger();
|
|
232
|
+
},
|
|
233
|
+
children
|
|
234
|
+
}
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
DropdownItem.displayName = "DropdownItem";
|
|
238
|
+
function DropdownSeparator({ className }) {
|
|
239
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
240
|
+
"div",
|
|
241
|
+
{
|
|
242
|
+
"data-react-fancy-dropdown-separator": "",
|
|
243
|
+
role: "separator",
|
|
244
|
+
className: chunkZU5Q77OT_cjs.cn(
|
|
245
|
+
"my-1 h-px bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]",
|
|
246
|
+
className
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
DropdownSeparator.displayName = "DropdownSeparator";
|
|
252
|
+
var TRIGGER_FOCUSABLE = 'button:not([disabled]), a[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
253
|
+
function DropdownRoot({
|
|
254
|
+
children,
|
|
255
|
+
placement = "bottom-start",
|
|
256
|
+
offset = 4
|
|
257
|
+
}) {
|
|
258
|
+
const [open, setOpen] = react.useState(false);
|
|
259
|
+
const [activeIndex, setActiveIndex] = react.useState(-1);
|
|
260
|
+
const anchorRef = react.useRef(null);
|
|
261
|
+
const pendingFocusRef = react.useRef(null);
|
|
262
|
+
const menuRef = react.useRef(null);
|
|
263
|
+
const focusTrigger = react.useCallback(() => {
|
|
264
|
+
const anchor = anchorRef.current;
|
|
265
|
+
if (!anchor) return;
|
|
266
|
+
const target = anchor.querySelector(TRIGGER_FOCUSABLE) ?? (anchor.tabIndex >= 0 ? anchor : null);
|
|
267
|
+
target?.focus();
|
|
268
|
+
}, []);
|
|
269
|
+
const ctx = react.useMemo(
|
|
270
|
+
() => ({
|
|
271
|
+
open,
|
|
272
|
+
setOpen,
|
|
273
|
+
anchorRef,
|
|
274
|
+
activeIndex,
|
|
275
|
+
setActiveIndex,
|
|
276
|
+
placement,
|
|
277
|
+
offset,
|
|
278
|
+
pendingFocusRef,
|
|
279
|
+
menuRef,
|
|
280
|
+
focusTrigger
|
|
281
|
+
}),
|
|
282
|
+
[open, anchorRef, activeIndex, placement, offset, focusTrigger]
|
|
283
|
+
);
|
|
284
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DropdownContext.Provider, { value: ctx, children });
|
|
285
|
+
}
|
|
286
|
+
var Dropdown = Object.assign(DropdownRoot, {
|
|
287
|
+
Trigger: DropdownTrigger,
|
|
288
|
+
Items: DropdownItems,
|
|
289
|
+
Item: DropdownItem,
|
|
290
|
+
Separator: DropdownSeparator
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
exports.Dropdown = Dropdown;
|
|
294
|
+
exports.useDropdown = useDropdown;
|
|
295
|
+
//# sourceMappingURL=chunk-XZOBGKZL.cjs.map
|
|
296
|
+
//# sourceMappingURL=chunk-XZOBGKZL.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Dropdown/Dropdown.context.ts","../src/components/Dropdown/dropdown-focus.ts","../src/components/Dropdown/DropdownTrigger.tsx","../src/components/Dropdown/DropdownItems.tsx","../src/components/Dropdown/DropdownItem.tsx","../src/components/Dropdown/DropdownSeparator.tsx","../src/components/Dropdown/Dropdown.tsx"],"names":["createContext","useContext","jsx","useRef","useFloatingPosition","useCallback","useOutsideClick","useEscapeKey","useAnimation","useState","useEffect","Portal","cn","useMemo"],"mappings":";;;;;;;;;;;AAGO,IAAM,eAAA,GAAkBA,oBAA2C,IAAI,CAAA;AAEvE,SAAS,WAAA,GAAoC;AAClD,EAAA,MAAM,GAAA,GAAMC,iBAAW,eAAe,CAAA;AACtC,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;;;ACZO,SAAS,UAAU,IAAA,EAAyC;AACjE,EAAA,IAAI,CAAC,IAAA,EAAM,OAAO,EAAC;AACnB,EAAA,OAAO,KAAA,CAAM,IAAA;AAAA,IACX,IAAA,CAAK,iBAA8B,+DAA+D;AAAA,GACpG;AACF;AAQO,SAAS,aAAA,CAAc,MAA0B,IAAA,EAAyB;AAC/E,EAAA,MAAM,KAAA,GAAQ,UAAU,IAAI,CAAA;AAC5B,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,EAAA;AAC/B,EAAA,MAAM,OAAA,GAAU,KAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,aAA4B,CAAA;AACnE,EAAA,IAAI,IAAA;AACJ,EAAA,IAAI,IAAA,KAAS,SAAS,IAAA,GAAO,CAAA;AAAA,OAAA,IACpB,IAAA,KAAS,MAAA,EAAQ,IAAA,GAAO,KAAA,CAAM,MAAA,GAAS,CAAA;AAAA,OAAA,IACvC,YAAY,EAAA,EAAI,IAAA,GAAO,SAAS,MAAA,GAAS,CAAA,GAAI,MAAM,MAAA,GAAS,CAAA;AAAA,OAChE,IAAA,GAAA,CAAQ,WAAW,IAAA,KAAS,MAAA,GAAS,IAAI,EAAA,CAAA,GAAM,KAAA,CAAM,UAAU,KAAA,CAAM,MAAA;AAC1E,EAAA,KAAA,CAAM,IAAI,EAAG,KAAA,EAAM;AACnB,EAAA,OAAO,IAAA;AACT;AAMO,SAAS,WAAA,CAAY,MAA0B,IAAA,EAAsB;AAC1E,EAAA,MAAM,KAAA,GAAQ,UAAU,IAAI,CAAA;AAC5B,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,EAAA;AAC/B,EAAA,MAAM,MAAA,GAAS,KAAK,WAAA,EAAY;AAChC,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,aAA4B,CAAA;AACjE,EAAA,KAAA,IAAS,IAAA,GAAO,CAAA,EAAG,IAAA,IAAQ,KAAA,CAAM,QAAQ,IAAA,EAAA,EAAQ;AAC/C,IAAA,MAAM,CAAA,GAAA,CAAK,KAAA,GAAQ,IAAA,GAAO,KAAA,CAAM,UAAU,KAAA,CAAM,MAAA;AAChD,IAAA,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA,CAAG,WAAA,IAAe,EAAA,EAAI,IAAA,EAAK,CAAE,WAAA,EAAY,CAAE,UAAA,CAAW,MAAM,CAAA,EAAG;AACzE,MAAA,KAAA,CAAM,CAAC,EAAG,KAAA,EAAM;AAChB,MAAA,OAAO,CAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,OAAO,EAAA;AACT;AClCA,SAAS,kBAAkB,EAAA,EAAiC;AAC1D,EAAA,IAAI,EAAE,EAAA,YAAc,WAAA,CAAA,EAAc,OAAO,KAAA;AACzC,EAAA,MAAM,MAAM,EAAA,CAAG,OAAA;AACf,EAAA,IAAI,GAAA,KAAQ,QAAA,IAAY,GAAA,KAAQ,SAAA,EAAW,OAAO,IAAA;AAClD,EAAA,IAAI,GAAA,KAAQ,GAAA,EAAK,OAAO,EAAA,CAAG,aAAa,MAAM,CAAA;AAC9C,EAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,IAAA,MAAM,OAAQ,EAAA,CAAwB,IAAA;AACtC,IAAA,OAAO,SAAS,QAAA,IAAY,IAAA,KAAS,QAAA,IAAY,IAAA,KAAS,WAAW,IAAA,KAAS,OAAA;AAAA,EAChF;AACA,EAAA,OAAO,KAAA;AACT;AAEO,SAAS,eAAA,CAAgB,EAAE,QAAA,EAAS,EAAyB;AAClE,EAAA,MAAM,EAAE,OAAA,EAAS,IAAA,EAAM,WAAW,eAAA,EAAiB,OAAA,KAAY,WAAA,EAAY;AAI3E,EAAA,MAAM,SAAA,GAAY,CAAC,CAAA,KAAsC;AACvD,IAAA,IAAI,EAAE,gBAAA,EAAkB;AAExB,IAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,WAAA,IAAe,CAAA,CAAE,QAAQ,SAAA,EAAW;AAChD,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,MAAM,MAAA,GAAS,CAAA,CAAE,GAAA,KAAQ,WAAA,GAAc,OAAA,GAAU,MAAA;AACjD,MAAA,IAAI,IAAA,IAAQ,QAAQ,OAAA,EAAS;AAC3B,QAAA,aAAA,CAAc,OAAA,CAAQ,SAAS,MAAM,CAAA;AACrC,QAAA;AAAA,MACF;AACA,MAAA,eAAA,CAAgB,OAAA,GAAU,MAAA;AAC1B,MAAA,OAAA,CAAQ,IAAI,CAAA;AACZ,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,OAAA,IAAW,CAAA,CAAE,QAAQ,GAAA,EAAK;AACtC,MAAA,IAAI,CAAC,IAAA,EAAM,eAAA,CAAgB,OAAA,GAAU,OAAA;AACrC,MAAA,IAAI,iBAAA,CAAkB,CAAA,CAAE,MAAM,CAAA,EAAG;AACjC,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,IAAI,IAAA,kBAAsB,OAAA,GAAU,IAAA;AACpC,MAAA,OAAA,CAAQ,CAAC,IAAI,CAAA;AAAA,IACf;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,OAAA,GAAU,CAAC,EAAA,KAAoC;AACnD,IAAA,IAAI,IAAA,kBAAsB,OAAA,GAAU,IAAA;AACpC,IAAA,OAAA,CAAQ,CAAC,IAAI,CAAA;AAAA,EACf,CAAA;AAEA,EAAA,uBACEC,cAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,SAAA;AAAA,MACL,mCAAA,EAAkC,EAAA;AAAA,MAClC,SAAA,EAAU,aAAA;AAAA,MACV,OAAA;AAAA,MACA,SAAA;AAAA,MACA,eAAA,EAAe,IAAA;AAAA,MACf,eAAA,EAAc,MAAA;AAAA,MAEb;AAAA;AAAA,GACH;AAEJ;AAEA,eAAA,CAAgB,WAAA,GAAc,iBAAA;AC5DvB,SAAS,aAAA,CAAc,EAAE,QAAA,EAAU,SAAA,EAAU,EAAuB;AACzE,EAAA,MAAM;AAAA,IACJ,IAAA;AAAA,IACA,OAAA;AAAA,IACA,SAAA;AAAA,IACA,cAAA;AAAA,IACA,SAAA;AAAA,IACA,MAAA;AAAA,IACA,eAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,MACE,WAAA,EAAY;AAChB,EAAA,MAAM,WAAA,GAAcC,aAAuB,IAAI,CAAA;AAC/C,EAAA,MAAM,UAAA,GAAaA,aAAuB,IAAI,CAAA;AAE9C,EAAA,MAAM,QAAA,GAAWC,qCAAA,CAAoB,SAAA,EAAW,WAAA,EAAa;AAAA,IAC3D,SAAA;AAAA,IACA,MAAA;AAAA,IACA,OAAA,EAAS;AAAA,GACV,CAAA;AAED,EAAA,MAAM,KAAA,GAAQC,kBAAY,MAAM;AAC9B,IAAA,eAAA,CAAgB,OAAA,GAAU,IAAA;AAC1B,IAAA,OAAA,CAAQ,KAAK,CAAA;AACb,IAAA,cAAA,CAAe,EAAE,CAAA;AAAA,EACnB,CAAA,EAAG,CAAC,OAAA,EAAS,cAAA,EAAgB,eAAe,CAAC,CAAA;AAE7C,EAAAC,iCAAA,CAAgB,UAAA,EAAY,KAAA,EAAO,IAAA,EAAM,SAAS,CAAA;AAIlD,EAAAC,8BAAA,CAAa,MAAM;AACjB,IAAA,KAAA,EAAM;AACN,IAAA,YAAA,EAAa;AAAA,EACf,GAAG,IAAI,CAAA;AAEP,EAAA,MAAM,EAAE,OAAA,EAAS,SAAA,EAAW,WAAW,GAAA,EAAK,OAAA,KAAYC,8BAAA,CAAa;AAAA,IACnE,IAAA;AAAA,IACA,UAAA,EAAY,gBAAA;AAAA,IACZ,SAAA,EAAW;AAAA,GACZ,CAAA;AAQD,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAIC,eAAgC,IAAI,CAAA;AAEhE,EAAA,MAAM,UAAA,GAAaJ,iBAAA;AAAA,IACjB,CAAC,IAAA,KAAgC;AAC/B,MAAA,UAAA,CAAW,OAAA,GAAU,IAAA;AACrB,MAAA,WAAA,CAAY,OAAA,GAAU,IAAA;AACtB,MAAC,QAAuD,OAAA,GAAU,IAAA;AAClE,MAAA,OAAA,CAAQ,OAAA,GAAU,IAAA;AAClB,MAAA,SAAA,CAAU,IAAI,CAAA;AAAA,IAChB,CAAA;AAAA,IACA,CAAC,SAAS,OAAO;AAAA,GACnB;AAGA,EAAAK,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,MAAA,EAAQ;AACtB,IAAA,MAAM,SAAS,eAAA,CAAgB,OAAA;AAC/B,IAAA,IAAI,CAAC,MAAA,EAAQ;AACb,IAAA,eAAA,CAAgB,OAAA,GAAU,IAAA;AAC1B,IAAA,cAAA,CAAe,aAAA,CAAc,MAAA,EAAQ,MAAM,CAAC,CAAA;AAAA,EAC9C,GAAG,CAAC,IAAA,EAAM,MAAA,EAAQ,eAAA,EAAiB,cAAc,CAAC,CAAA;AAIlD,EAAA,MAAM,SAAA,GAAY,CAAC,CAAA,KAA2C;AAC5D,IAAA,MAAM,OAAO,CAAA,CAAE,aAAA;AACf,IAAA,MAAM,KAAA,GAAmC;AAAA,MACvC,SAAA,EAAW,MAAA;AAAA,MACX,OAAA,EAAS,MAAA;AAAA,MACT,IAAA,EAAM,OAAA;AAAA,MACN,GAAA,EAAK;AAAA,KACP;AACA,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,CAAA,CAAE,GAAG,CAAA;AACxB,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,cAAA,CAAe,aAAA,CAAc,IAAA,EAAM,IAAI,CAAC,CAAA;AACxC,MAAA;AAAA,IACF;AACA,IAAA,IAAI,CAAA,CAAE,QAAQ,KAAA,EAAO;AAKnB,MAAA,KAAA,EAAM;AACN,MAAA,YAAA,EAAa;AACb,MAAA;AAAA,IACF;AACA,IAAA,IAAI,CAAA,CAAE,GAAA,CAAI,MAAA,KAAW,CAAA,IAAK,EAAE,GAAA,KAAQ,GAAA,IAAO,CAAC,CAAA,CAAE,WAAW,CAAC,CAAA,CAAE,OAAA,IAAW,CAAC,EAAE,MAAA,EAAQ;AAChF,MAAA,MAAM,CAAA,GAAI,WAAA,CAAY,IAAA,EAAM,CAAA,CAAE,GAAG,CAAA;AACjC,MAAA,IAAI,CAAA,IAAK,CAAA,EAAG,cAAA,CAAe,CAAC,CAAA;AAAA,IAC9B;AAAA,EACF,CAAA;AAEA,EAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AAErB,EAAA,uBACER,cAAAA,CAACS,wBAAA,EAAA,EACC,QAAA,kBAAAT,cAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,UAAA;AAAA,MACL,2BAAA,EAA0B,EAAA;AAAA,MAC1B,IAAA,EAAK,MAAA;AAAA,MACL,SAAA;AAAA,MACA,SAAA,EAAWU,oBAAA;AAAA,QACT,wSAAA;AAAA,QACA,SAAA;AAAA,QACA;AAAA,OACF;AAAA,MACA,OAAO,EAAE,IAAA,EAAM,SAAS,CAAA,EAAG,GAAA,EAAK,SAAS,CAAA,EAAE;AAAA,MAE1C;AAAA;AAAA,GACH,EACF,CAAA;AAEJ;AAEA,aAAA,CAAc,WAAA,GAAc,eAAA;AClIrB,SAAS,YAAA,CAAa;AAAA,EAC3B,QAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA,GAAW,KAAA;AAAA,EACX,MAAA,GAAS,KAAA;AAAA,EACT;AACF,CAAA,EAAsB;AACpB,EAAA,MAAM,EAAE,OAAA,EAAS,YAAA,EAAa,GAAI,WAAA,EAAY;AAE9C,EAAA,uBACEV,cAAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,gCAAA,EAA+B,EAAA;AAAA,MAC/B,IAAA,EAAK,QAAA;AAAA,MACL,IAAA,EAAK,UAAA;AAAA,MACL,QAAA;AAAA,MACA,eAAA,EAAe,QAAA;AAAA,MACf,SAAA,EAAWU,oBAAA;AAAA,QACT,mFAAA;AAAA,QACA,SACI,sEAAA,GACA,6QAAA;AAAA,QACJ,QAAA,IAAY,+BAAA;AAAA,QACZ;AAAA,OACF;AAAA,MACA,OAAA,EAAS,CAAC,CAAA,KAAM;AACd,QAAA,IAAI,QAAA,EAAU;AACd,QAAA,MAAM,QAAA,GAAW,QAAA,CAAS,aAAA,KAAkB,CAAA,CAAE,aAAA;AAC9C,QAAA,OAAA,IAAU;AACV,QAAA,OAAA,CAAQ,KAAK,CAAA;AAGb,QAAA,IAAI,QAAA,IAAY,QAAA,CAAS,aAAA,KAAkB,CAAA,CAAE,eAAe,YAAA,EAAa;AAAA,MAC3E,CAAA;AAAA,MAEC;AAAA;AAAA,GACH;AAEJ;AAEA,YAAA,CAAa,WAAA,GAAc,cAAA;ACxCpB,SAAS,iBAAA,CAAkB,EAAE,SAAA,EAAU,EAA2B;AACvE,EAAA,uBACEV,cAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,qCAAA,EAAoC,EAAA;AAAA,MACpC,IAAA,EAAK,WAAA;AAAA,MACL,SAAA,EAAWU,oBAAA;AAAA,QACT,+HAAA;AAAA,QACA;AAAA;AACF;AAAA,GACF;AAEJ;AAEA,iBAAA,CAAkB,WAAA,GAAc,mBAAA;ACRhC,IAAM,iBAAA,GACJ,2IAAA;AAEF,SAAS,YAAA,CAAa;AAAA,EACpB,QAAA;AAAA,EACA,SAAA,GAAY,cAAA;AAAA,EACZ,MAAA,GAAS;AACX,CAAA,EAAkB;AAChB,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAIH,eAAS,KAAK,CAAA;AACtC,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAIA,eAAS,EAAE,CAAA;AACjD,EAAA,MAAM,SAAA,GAAYN,aAAoB,IAAI,CAAA;AAC1C,EAAA,MAAM,eAAA,GAAkBA,aAAgC,IAAI,CAAA;AAC5D,EAAA,MAAM,OAAA,GAAUA,aAA2B,IAAI,CAAA;AAE/C,EAAA,MAAM,YAAA,GAAeE,kBAAY,MAAM;AACrC,IAAA,MAAM,SAAS,SAAA,CAAU,OAAA;AACzB,IAAA,IAAI,CAAC,MAAA,EAAQ;AACb,IAAA,MAAM,MAAA,GACJ,OAAO,aAAA,CAA2B,iBAAiB,MAAM,MAAA,CAAO,QAAA,IAAY,IAAI,MAAA,GAAS,IAAA,CAAA;AAC3F,IAAA,MAAA,EAAQ,KAAA,EAAM;AAAA,EAChB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,GAAA,GAAMQ,aAAA;AAAA,IACV,OAAO;AAAA,MACL,IAAA;AAAA,MACA,OAAA;AAAA,MACA,SAAA;AAAA,MACA,WAAA;AAAA,MACA,cAAA;AAAA,MACA,SAAA;AAAA,MACA,MAAA;AAAA,MACA,eAAA;AAAA,MACA,OAAA;AAAA,MACA;AAAA,KACF,CAAA;AAAA,IACA,CAAC,IAAA,EAAM,SAAA,EAAW,WAAA,EAAa,SAAA,EAAW,QAAQ,YAAY;AAAA,GAChE;AAEA,EAAA,uBACEX,cAAAA,CAAC,eAAA,CAAgB,UAAhB,EAAyB,KAAA,EAAO,KAAM,QAAA,EAAS,CAAA;AAEpD;AAEO,IAAM,QAAA,GAAW,MAAA,CAAO,MAAA,CAAO,YAAA,EAAc;AAAA,EAClD,OAAA,EAAS,eAAA;AAAA,EACT,KAAA,EAAO,aAAA;AAAA,EACP,IAAA,EAAM,YAAA;AAAA,EACN,SAAA,EAAW;AACb,CAAC","file":"chunk-XZOBGKZL.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { DropdownContextValue } from \"./Dropdown.types\";\n\nexport const DropdownContext = createContext<DropdownContextValue | null>(null);\n\nexport function useDropdown(): DropdownContextValue {\n const ctx = useContext(DropdownContext);\n if (!ctx) {\n throw new Error(\n \"Dropdown compound components must be used within <Dropdown>\",\n );\n }\n return ctx;\n}\n","/** Enabled menu items, in DOM order. Disabled items are skipped. */\nexport function menuItems(menu: HTMLElement | null): HTMLElement[] {\n if (!menu) return [];\n return Array.from(\n menu.querySelectorAll<HTMLElement>('[role=\"menuitem\"]:not([aria-disabled=\"true\"]):not([disabled])'),\n );\n}\n\nexport type FocusMove = \"first\" | \"last\" | \"next\" | \"prev\";\n\n/**\n * Focus an item relative to the one that currently has focus, wrapping at both\n * ends. Returns the index focused, or -1 when the menu has no enabled item.\n */\nexport function focusMenuItem(menu: HTMLElement | null, move: FocusMove): number {\n const items = menuItems(menu);\n if (items.length === 0) return -1;\n const current = items.indexOf(document.activeElement as HTMLElement);\n let next: number;\n if (move === \"first\") next = 0;\n else if (move === \"last\") next = items.length - 1;\n else if (current === -1) next = move === \"next\" ? 0 : items.length - 1;\n else next = (current + (move === \"next\" ? 1 : -1) + items.length) % items.length;\n items[next]!.focus();\n return next;\n}\n\n/**\n * Typeahead: focus the next enabled item (after the focused one, wrapping)\n * whose text starts with `char`. Returns the index focused, or -1 for no match.\n */\nexport function focusByChar(menu: HTMLElement | null, char: string): number {\n const items = menuItems(menu);\n if (items.length === 0) return -1;\n const needle = char.toLowerCase();\n const start = items.indexOf(document.activeElement as HTMLElement);\n for (let step = 1; step <= items.length; step++) {\n const i = (start + step + items.length) % items.length;\n if ((items[i]!.textContent ?? \"\").trim().toLowerCase().startsWith(needle)) {\n items[i]!.focus();\n return i;\n }\n }\n return -1;\n}\n","import type { KeyboardEvent, MouseEvent } from \"react\";\nimport { useDropdown } from \"./Dropdown.context\";\nimport { focusMenuItem } from \"./dropdown-focus\";\nimport type { DropdownTriggerProps } from \"./Dropdown.types\";\n\n/**\n * Elements the browser itself turns Enter / Space into a click on. For these\n * the trigger lets the native click open the menu; for anything else (an\n * avatar span with `tabIndex`) it opens the menu itself.\n */\nfunction activatesNatively(el: EventTarget | null): boolean {\n if (!(el instanceof HTMLElement)) return false;\n const tag = el.tagName;\n if (tag === \"BUTTON\" || tag === \"SUMMARY\") return true;\n if (tag === \"A\") return el.hasAttribute(\"href\");\n if (tag === \"INPUT\") {\n const type = (el as HTMLInputElement).type;\n return type === \"button\" || type === \"submit\" || type === \"reset\" || type === \"image\";\n }\n return false;\n}\n\nexport function DropdownTrigger({ children }: DropdownTriggerProps) {\n const { setOpen, open, anchorRef, pendingFocusRef, menuRef } = useDropdown();\n\n // WAI-ARIA menu button: Enter / Space / ArrowDown open the menu on its first\n // item, ArrowUp on its last.\n const onKeyDown = (e: KeyboardEvent<HTMLSpanElement>) => {\n if (e.defaultPrevented) return;\n\n if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\") {\n e.preventDefault();\n const target = e.key === \"ArrowDown\" ? \"first\" : \"last\";\n if (open && menuRef.current) {\n focusMenuItem(menuRef.current, target);\n return;\n }\n pendingFocusRef.current = target;\n setOpen(true);\n return;\n }\n\n if (e.key === \"Enter\" || e.key === \" \") {\n if (!open) pendingFocusRef.current = \"first\";\n if (activatesNatively(e.target)) return; // its click toggles the menu\n e.preventDefault();\n if (open) pendingFocusRef.current = null;\n setOpen(!open);\n }\n };\n\n const onClick = (_e: MouseEvent<HTMLSpanElement>) => {\n if (open) pendingFocusRef.current = null;\n setOpen(!open);\n };\n\n return (\n <span\n ref={anchorRef as React.RefObject<HTMLSpanElement>}\n data-react-fancy-dropdown-trigger=\"\"\n className=\"inline-flex\"\n onClick={onClick}\n onKeyDown={onKeyDown}\n aria-expanded={open}\n aria-haspopup=\"menu\"\n >\n {children}\n </span>\n );\n}\n\nDropdownTrigger.displayName = \"DropdownTrigger\";\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { focusByChar, focusMenuItem, type FocusMove } from \"./dropdown-focus\";\nimport { cn } from \"../../utils/cn\";\nimport { Portal } from \"../Portal\";\nimport { useDropdown } from \"./Dropdown.context\";\nimport { useFloatingPosition } from \"../../hooks/use-floating-position\";\nimport { useOutsideClick } from \"../../hooks/use-outside-click\";\nimport { useEscapeKey } from \"../../hooks/use-escape-key\";\nimport { useAnimation } from \"../../hooks/use-animation\";\nimport type { DropdownItemsProps } from \"./Dropdown.types\";\n\nexport function DropdownItems({ children, className }: DropdownItemsProps) {\n const {\n open,\n setOpen,\n anchorRef,\n setActiveIndex,\n placement,\n offset,\n pendingFocusRef,\n menuRef,\n focusTrigger,\n } = useDropdown();\n const floatingRef = useRef<HTMLDivElement>(null);\n const outsideRef = useRef<HTMLDivElement>(null);\n\n const position = useFloatingPosition(anchorRef, floatingRef, {\n placement,\n offset,\n enabled: open,\n });\n\n const close = useCallback(() => {\n pendingFocusRef.current = null;\n setOpen(false);\n setActiveIndex(-1);\n }, [setOpen, setActiveIndex, pendingFocusRef]);\n\n useOutsideClick(outsideRef, close, open, anchorRef);\n // Escape closes THIS menu (the innermost layer) and returns focus to where\n // the menu came from. An outside click does not move focus: the user put it\n // somewhere already.\n useEscapeKey(() => {\n close();\n focusTrigger();\n }, open);\n\n const { mounted, className: animClass, ref: animRef } = useAnimation({\n open,\n enterClass: \"fancy-scale-in\",\n exitClass: \"fancy-fade-out\",\n });\n\n // The menu element, as STATE. It does not exist when `open` first turns true:\n // `useAnimation` mounts it a render later and `Portal` a render after that.\n // Before 5.64.1 the key handler lived in an effect keyed on `open` that\n // returned early while the element was null and never re-ran, so the arrow\n // keys did nothing in any Dropdown. Keying on the element itself fixes the\n // timing for good.\n const [menuEl, setMenuEl] = useState<HTMLDivElement | null>(null);\n\n const setMenuRef = useCallback(\n (node: HTMLDivElement | null) => {\n outsideRef.current = node;\n floatingRef.current = node;\n (animRef as React.MutableRefObject<HTMLElement | null>).current = node;\n menuRef.current = node;\n setMenuEl(node);\n },\n [animRef, menuRef],\n );\n\n // Opened from the keyboard: focus the first / last item once it exists.\n useEffect(() => {\n if (!open || !menuEl) return;\n const target = pendingFocusRef.current;\n if (!target) return;\n pendingFocusRef.current = null;\n setActiveIndex(focusMenuItem(menuEl, target));\n }, [open, menuEl, pendingFocusRef, setActiveIndex]);\n\n // Keys are handled on the menu itself: focus is inside it while it is driven\n // from the keyboard, and React delivers the event through the portal.\n const onKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n const menu = e.currentTarget;\n const moves: Record<string, FocusMove> = {\n ArrowDown: \"next\",\n ArrowUp: \"prev\",\n Home: \"first\",\n End: \"last\",\n };\n const move = moves[e.key];\n if (move) {\n e.preventDefault();\n setActiveIndex(focusMenuItem(menu, move));\n return;\n }\n if (e.key === \"Tab\") {\n // Close, and put focus back on the trigger WITHOUT preventing the\n // default: the browser's own Tab then moves on from the trigger's place\n // in the tab order rather than from the end of <body>, where the\n // portalled menu lives.\n close();\n focusTrigger();\n return;\n }\n if (e.key.length === 1 && e.key !== \" \" && !e.ctrlKey && !e.metaKey && !e.altKey) {\n const i = focusByChar(menu, e.key);\n if (i >= 0) setActiveIndex(i);\n }\n };\n\n if (!mounted) return null;\n\n return (\n <Portal>\n <div\n ref={setMenuRef}\n data-react-fancy-dropdown=\"\"\n role=\"menu\"\n onKeyDown={onKeyDown}\n className={cn(\n \"fixed z-50 min-w-[8rem] rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] p-1 shadow-lg dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n animClass,\n className,\n )}\n style={{ left: position.x, top: position.y }}\n >\n {children}\n </div>\n </Portal>\n );\n}\n\nDropdownItems.displayName = \"DropdownItems\";\n","import { cn } from \"../../utils/cn\";\nimport { useDropdown } from \"./Dropdown.context\";\nimport type { DropdownItemProps } from \"./Dropdown.types\";\n\nexport function DropdownItem({\n children,\n onClick,\n disabled = false,\n danger = false,\n className,\n}: DropdownItemProps) {\n const { setOpen, focusTrigger } = useDropdown();\n\n return (\n <button\n data-react-fancy-dropdown-item=\"\"\n type=\"button\"\n role=\"menuitem\"\n disabled={disabled}\n aria-disabled={disabled}\n className={cn(\n \"flex w-full items-center rounded-lg px-3 py-2 text-left text-sm transition-colors\",\n danger\n ? \"text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950\"\n : \"text-[color:var(--fancy-text-soft,var(--color-zinc-700))] hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n disabled && \"cursor-not-allowed opacity-50\",\n className,\n )}\n onClick={(e) => {\n if (disabled) return;\n const hadFocus = document.activeElement === e.currentTarget;\n onClick?.();\n setOpen(false);\n // The item is about to unmount; without this, focus falls to <body>.\n // Only when the item held focus, and only if onClick did not move it.\n if (hadFocus && document.activeElement === e.currentTarget) focusTrigger();\n }}\n >\n {children}\n </button>\n );\n}\n\nDropdownItem.displayName = \"DropdownItem\";\n","import { cn } from \"../../utils/cn\";\nimport type { DropdownSeparatorProps } from \"./Dropdown.types\";\n\nexport function DropdownSeparator({ className }: DropdownSeparatorProps) {\n return (\n <div\n data-react-fancy-dropdown-separator=\"\"\n role=\"separator\"\n className={cn(\n \"my-1 h-px bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n />\n );\n}\n\nDropdownSeparator.displayName = \"DropdownSeparator\";\n","import { useCallback, useMemo, useRef, useState } from \"react\";\nimport { DropdownContext } from \"./Dropdown.context\";\nimport { DropdownTrigger } from \"./DropdownTrigger\";\nimport { DropdownItems } from \"./DropdownItems\";\nimport { DropdownItem } from \"./DropdownItem\";\nimport { DropdownSeparator } from \"./DropdownSeparator\";\nimport type { DropdownProps } from \"./Dropdown.types\";\n\nconst TRIGGER_FOCUSABLE =\n 'button:not([disabled]), a[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\nfunction DropdownRoot({\n children,\n placement = \"bottom-start\",\n offset = 4,\n}: DropdownProps) {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState(-1);\n const anchorRef = useRef<HTMLElement>(null);\n const pendingFocusRef = useRef<\"first\" | \"last\" | null>(null);\n const menuRef = useRef<HTMLElement | null>(null);\n\n const focusTrigger = useCallback(() => {\n const anchor = anchorRef.current;\n if (!anchor) return;\n const target =\n anchor.querySelector<HTMLElement>(TRIGGER_FOCUSABLE) ?? (anchor.tabIndex >= 0 ? anchor : null);\n target?.focus();\n }, []);\n\n const ctx = useMemo(\n () => ({\n open,\n setOpen,\n anchorRef,\n activeIndex,\n setActiveIndex,\n placement,\n offset,\n pendingFocusRef,\n menuRef,\n focusTrigger,\n }),\n [open, anchorRef, activeIndex, placement, offset, focusTrigger],\n );\n\n return (\n <DropdownContext.Provider value={ctx}>{children}</DropdownContext.Provider>\n );\n}\n\nexport const Dropdown = Object.assign(DropdownRoot, {\n Trigger: DropdownTrigger,\n Items: DropdownItems,\n Item: DropdownItem,\n Separator: DropdownSeparator,\n});\n"]}
|
package/dist/dropdown.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkXZOBGKZL_cjs = require('./chunk-XZOBGKZL.cjs');
|
|
4
4
|
require('./chunk-FQZE5N2N.cjs');
|
|
5
5
|
require('./chunk-J2DI6RMR.cjs');
|
|
6
6
|
require('./chunk-MKHYENKR.cjs');
|
|
@@ -13,11 +13,11 @@ require('./chunk-ZU5Q77OT.cjs');
|
|
|
13
13
|
|
|
14
14
|
Object.defineProperty(exports, "Dropdown", {
|
|
15
15
|
enumerable: true,
|
|
16
|
-
get: function () { return
|
|
16
|
+
get: function () { return chunkXZOBGKZL_cjs.Dropdown; }
|
|
17
17
|
});
|
|
18
18
|
Object.defineProperty(exports, "useDropdown", {
|
|
19
19
|
enumerable: true,
|
|
20
|
-
get: function () { return
|
|
20
|
+
get: function () { return chunkXZOBGKZL_cjs.useDropdown; }
|
|
21
21
|
});
|
|
22
22
|
//# sourceMappingURL=dropdown.cjs.map
|
|
23
23
|
//# sourceMappingURL=dropdown.cjs.map
|
package/dist/dropdown.d.cts
CHANGED
|
@@ -10,6 +10,16 @@ interface DropdownContextValue {
|
|
|
10
10
|
setActiveIndex: (index: number) => void;
|
|
11
11
|
placement: Placement;
|
|
12
12
|
offset: number;
|
|
13
|
+
/**
|
|
14
|
+
* Which item the menu focuses when it next mounts: set by the trigger when
|
|
15
|
+
* the menu is opened from the keyboard, consumed by `Dropdown.Items`.
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
pendingFocusRef: React.MutableRefObject<"first" | "last" | null>;
|
|
19
|
+
/** The open menu element, or null while closed. @internal */
|
|
20
|
+
menuRef: React.MutableRefObject<HTMLElement | null>;
|
|
21
|
+
/** Move focus back to the trigger (its first focusable element). */
|
|
22
|
+
focusTrigger: () => void;
|
|
13
23
|
}
|
|
14
24
|
interface DropdownProps {
|
|
15
25
|
children: ReactNode;
|
package/dist/dropdown.d.ts
CHANGED
|
@@ -10,6 +10,16 @@ interface DropdownContextValue {
|
|
|
10
10
|
setActiveIndex: (index: number) => void;
|
|
11
11
|
placement: Placement;
|
|
12
12
|
offset: number;
|
|
13
|
+
/**
|
|
14
|
+
* Which item the menu focuses when it next mounts: set by the trigger when
|
|
15
|
+
* the menu is opened from the keyboard, consumed by `Dropdown.Items`.
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
pendingFocusRef: React.MutableRefObject<"first" | "last" | null>;
|
|
19
|
+
/** The open menu element, or null while closed. @internal */
|
|
20
|
+
menuRef: React.MutableRefObject<HTMLElement | null>;
|
|
21
|
+
/** Move focus back to the trigger (its first focusable element). */
|
|
22
|
+
focusTrigger: () => void;
|
|
13
23
|
}
|
|
14
24
|
interface DropdownProps {
|
|
15
25
|
children: ReactNode;
|
package/dist/dropdown.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -77,7 +77,7 @@ var chunkFQSSVOVQ_cjs = require('./chunk-FQSSVOVQ.cjs');
|
|
|
77
77
|
require('./chunk-S3TRSECD.cjs');
|
|
78
78
|
var chunkZ7SQMQTF_cjs = require('./chunk-Z7SQMQTF.cjs');
|
|
79
79
|
var chunkAJPYGRLA_cjs = require('./chunk-AJPYGRLA.cjs');
|
|
80
|
-
var
|
|
80
|
+
var chunkXZOBGKZL_cjs = require('./chunk-XZOBGKZL.cjs');
|
|
81
81
|
var chunkATIDUWY6_cjs = require('./chunk-ATIDUWY6.cjs');
|
|
82
82
|
var chunkA2JUTNE6_cjs = require('./chunk-A2JUTNE6.cjs');
|
|
83
83
|
var chunkT6YELZPJ_cjs = require('./chunk-T6YELZPJ.cjs');
|
|
@@ -637,11 +637,11 @@ Object.defineProperty(exports, "useDrawer", {
|
|
|
637
637
|
});
|
|
638
638
|
Object.defineProperty(exports, "Dropdown", {
|
|
639
639
|
enumerable: true,
|
|
640
|
-
get: function () { return
|
|
640
|
+
get: function () { return chunkXZOBGKZL_cjs.Dropdown; }
|
|
641
641
|
});
|
|
642
642
|
Object.defineProperty(exports, "useDropdown", {
|
|
643
643
|
enumerable: true,
|
|
644
|
-
get: function () { return
|
|
644
|
+
get: function () { return chunkXZOBGKZL_cjs.useDropdown; }
|
|
645
645
|
});
|
|
646
646
|
Object.defineProperty(exports, "Editor", {
|
|
647
647
|
enumerable: true,
|
package/dist/index.js
CHANGED
|
@@ -75,7 +75,7 @@ export { Container } from './chunk-VKMPVFQK.js';
|
|
|
75
75
|
import './chunk-AUTW5O4W.js';
|
|
76
76
|
export { ContextMenu, useContextMenu } from './chunk-NSJBVV7R.js';
|
|
77
77
|
export { Drawer, useDrawer } from './chunk-PXZRYL5I.js';
|
|
78
|
-
export { Dropdown, useDropdown } from './chunk-
|
|
78
|
+
export { Dropdown, useDropdown } from './chunk-BXPLERIJ.js';
|
|
79
79
|
export { Editor, useEditor } from './chunk-CAUEKGMR.js';
|
|
80
80
|
export { THEME_STORAGE_KEY, getThemePreference, initTheme, resolveTheme, setThemePreference, subscribeTheme, useId, useNodeRegistry, useTheme } from './chunk-2BXMMVQS.js';
|
|
81
81
|
export { usePanZoom } from './chunk-CPY7NHXM.js';
|
package/docs/ReorderableGrid.md
CHANGED
|
@@ -42,6 +42,8 @@ const [order, setOrder] = useState(fields.map((f) => f.id));
|
|
|
42
42
|
|
|
43
43
|
Uncontrolled: leave out `value`, optionally pass `defaultValue`, and listen to `onReorder`.
|
|
44
44
|
|
|
45
|
+
**The cell is already framed.** By default every cell draws its own border, surface and padding, so `renderItem` should return bare content, as above. If you wrap it in your own bordered card you get a frame inside a frame. To draw the cell yourself, pass `unstyled` (drag, keyboard and announcements are unchanged), or adjust the default frame with `cellClassName`.
|
|
46
|
+
|
|
45
47
|
## Order: `value` is a list of ids
|
|
46
48
|
|
|
47
49
|
`items` carries the data and `value` (or `defaultValue`) carries the order. The order is not taken from `items`. This follows the kit's controlled pattern (`value` / `defaultValue`, see `useControllableState`), and it suits the two consumers:
|
package/package.json
CHANGED
package/dist/chunk-A6HWKQ4D.cjs
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var chunkFQZE5N2N_cjs = require('./chunk-FQZE5N2N.cjs');
|
|
4
|
-
var chunkJ2DI6RMR_cjs = require('./chunk-J2DI6RMR.cjs');
|
|
5
|
-
var chunkTI7D4QDM_cjs = require('./chunk-TI7D4QDM.cjs');
|
|
6
|
-
var chunk57FQGQJK_cjs = require('./chunk-57FQGQJK.cjs');
|
|
7
|
-
var chunkOUYC6EIL_cjs = require('./chunk-OUYC6EIL.cjs');
|
|
8
|
-
var chunkZU5Q77OT_cjs = require('./chunk-ZU5Q77OT.cjs');
|
|
9
|
-
var react = require('react');
|
|
10
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
11
|
-
|
|
12
|
-
var DropdownContext = react.createContext(null);
|
|
13
|
-
function useDropdown() {
|
|
14
|
-
const ctx = react.useContext(DropdownContext);
|
|
15
|
-
if (!ctx) {
|
|
16
|
-
throw new Error(
|
|
17
|
-
"Dropdown compound components must be used within <Dropdown>"
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
return ctx;
|
|
21
|
-
}
|
|
22
|
-
function DropdownTrigger({ children }) {
|
|
23
|
-
const { setOpen, open, anchorRef } = useDropdown();
|
|
24
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
25
|
-
"span",
|
|
26
|
-
{
|
|
27
|
-
ref: anchorRef,
|
|
28
|
-
"data-react-fancy-dropdown-trigger": "",
|
|
29
|
-
className: "inline-flex",
|
|
30
|
-
onClick: () => setOpen(!open),
|
|
31
|
-
"aria-expanded": open,
|
|
32
|
-
"aria-haspopup": "menu",
|
|
33
|
-
children
|
|
34
|
-
}
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
DropdownTrigger.displayName = "DropdownTrigger";
|
|
38
|
-
function DropdownItems({ children, className }) {
|
|
39
|
-
const { open, setOpen, anchorRef, activeIndex, setActiveIndex, placement, offset } = useDropdown();
|
|
40
|
-
const floatingRef = react.useRef(null);
|
|
41
|
-
const outsideRef = react.useRef(null);
|
|
42
|
-
const position = chunkJ2DI6RMR_cjs.useFloatingPosition(anchorRef, floatingRef, {
|
|
43
|
-
placement,
|
|
44
|
-
offset,
|
|
45
|
-
enabled: open
|
|
46
|
-
});
|
|
47
|
-
const close = react.useCallback(() => {
|
|
48
|
-
setOpen(false);
|
|
49
|
-
setActiveIndex(-1);
|
|
50
|
-
}, [setOpen, setActiveIndex]);
|
|
51
|
-
chunk57FQGQJK_cjs.useOutsideClick(outsideRef, close, open, anchorRef);
|
|
52
|
-
chunkOUYC6EIL_cjs.useEscapeKey(close, open);
|
|
53
|
-
const { mounted, className: animClass, ref: animRef } = chunkFQZE5N2N_cjs.useAnimation({
|
|
54
|
-
open,
|
|
55
|
-
enterClass: "fancy-scale-in",
|
|
56
|
-
exitClass: "fancy-fade-out"
|
|
57
|
-
});
|
|
58
|
-
react.useEffect(() => {
|
|
59
|
-
if (!open || !floatingRef.current) return;
|
|
60
|
-
const handleKeyDown = (e) => {
|
|
61
|
-
const items = floatingRef.current?.querySelectorAll(
|
|
62
|
-
'[role="menuitem"]:not([aria-disabled="true"])'
|
|
63
|
-
);
|
|
64
|
-
if (!items?.length) return;
|
|
65
|
-
if (e.key === "ArrowDown") {
|
|
66
|
-
e.preventDefault();
|
|
67
|
-
const next = activeIndex < items.length - 1 ? activeIndex + 1 : 0;
|
|
68
|
-
setActiveIndex(next);
|
|
69
|
-
items[next]?.focus();
|
|
70
|
-
} else if (e.key === "ArrowUp") {
|
|
71
|
-
e.preventDefault();
|
|
72
|
-
const prev = activeIndex > 0 ? activeIndex - 1 : items.length - 1;
|
|
73
|
-
setActiveIndex(prev);
|
|
74
|
-
items[prev]?.focus();
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
78
|
-
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
79
|
-
}, [open, activeIndex, setActiveIndex]);
|
|
80
|
-
if (!mounted) return null;
|
|
81
|
-
return /* @__PURE__ */ jsxRuntime.jsx(chunkTI7D4QDM_cjs.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
82
|
-
"div",
|
|
83
|
-
{
|
|
84
|
-
ref: (node) => {
|
|
85
|
-
outsideRef.current = node;
|
|
86
|
-
floatingRef.current = node;
|
|
87
|
-
animRef.current = node;
|
|
88
|
-
},
|
|
89
|
-
"data-react-fancy-dropdown": "",
|
|
90
|
-
role: "menu",
|
|
91
|
-
className: chunkZU5Q77OT_cjs.cn(
|
|
92
|
-
"fixed z-50 min-w-[8rem] rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] p-1 shadow-lg dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]",
|
|
93
|
-
animClass,
|
|
94
|
-
className
|
|
95
|
-
),
|
|
96
|
-
style: { left: position.x, top: position.y },
|
|
97
|
-
children
|
|
98
|
-
}
|
|
99
|
-
) });
|
|
100
|
-
}
|
|
101
|
-
DropdownItems.displayName = "DropdownItems";
|
|
102
|
-
function DropdownItem({
|
|
103
|
-
children,
|
|
104
|
-
onClick,
|
|
105
|
-
disabled = false,
|
|
106
|
-
danger = false,
|
|
107
|
-
className
|
|
108
|
-
}) {
|
|
109
|
-
const { setOpen } = useDropdown();
|
|
110
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
111
|
-
"button",
|
|
112
|
-
{
|
|
113
|
-
"data-react-fancy-dropdown-item": "",
|
|
114
|
-
type: "button",
|
|
115
|
-
role: "menuitem",
|
|
116
|
-
disabled,
|
|
117
|
-
"aria-disabled": disabled,
|
|
118
|
-
className: chunkZU5Q77OT_cjs.cn(
|
|
119
|
-
"flex w-full items-center rounded-lg px-3 py-2 text-left text-sm transition-colors",
|
|
120
|
-
danger ? "text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950" : "text-[color:var(--fancy-text-soft,var(--color-zinc-700))] hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]",
|
|
121
|
-
disabled && "cursor-not-allowed opacity-50",
|
|
122
|
-
className
|
|
123
|
-
),
|
|
124
|
-
onClick: () => {
|
|
125
|
-
if (disabled) return;
|
|
126
|
-
onClick?.();
|
|
127
|
-
setOpen(false);
|
|
128
|
-
},
|
|
129
|
-
children
|
|
130
|
-
}
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
DropdownItem.displayName = "DropdownItem";
|
|
134
|
-
function DropdownSeparator({ className }) {
|
|
135
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
136
|
-
"div",
|
|
137
|
-
{
|
|
138
|
-
"data-react-fancy-dropdown-separator": "",
|
|
139
|
-
role: "separator",
|
|
140
|
-
className: chunkZU5Q77OT_cjs.cn(
|
|
141
|
-
"my-1 h-px bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]",
|
|
142
|
-
className
|
|
143
|
-
)
|
|
144
|
-
}
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
DropdownSeparator.displayName = "DropdownSeparator";
|
|
148
|
-
function DropdownRoot({
|
|
149
|
-
children,
|
|
150
|
-
placement = "bottom-start",
|
|
151
|
-
offset = 4
|
|
152
|
-
}) {
|
|
153
|
-
const [open, setOpen] = react.useState(false);
|
|
154
|
-
const [activeIndex, setActiveIndex] = react.useState(-1);
|
|
155
|
-
const anchorRef = react.useRef(null);
|
|
156
|
-
const ctx = react.useMemo(
|
|
157
|
-
() => ({ open, setOpen, anchorRef, activeIndex, setActiveIndex, placement, offset }),
|
|
158
|
-
[open, anchorRef, activeIndex, placement, offset]
|
|
159
|
-
);
|
|
160
|
-
return /* @__PURE__ */ jsxRuntime.jsx(DropdownContext.Provider, { value: ctx, children });
|
|
161
|
-
}
|
|
162
|
-
var Dropdown = Object.assign(DropdownRoot, {
|
|
163
|
-
Trigger: DropdownTrigger,
|
|
164
|
-
Items: DropdownItems,
|
|
165
|
-
Item: DropdownItem,
|
|
166
|
-
Separator: DropdownSeparator
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
exports.Dropdown = Dropdown;
|
|
170
|
-
exports.useDropdown = useDropdown;
|
|
171
|
-
//# sourceMappingURL=chunk-A6HWKQ4D.cjs.map
|
|
172
|
-
//# sourceMappingURL=chunk-A6HWKQ4D.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/Dropdown/Dropdown.context.ts","../src/components/Dropdown/DropdownTrigger.tsx","../src/components/Dropdown/DropdownItems.tsx","../src/components/Dropdown/DropdownItem.tsx","../src/components/Dropdown/DropdownSeparator.tsx","../src/components/Dropdown/Dropdown.tsx"],"names":["createContext","useContext","jsx","useRef","useFloatingPosition","useCallback","useOutsideClick","useEscapeKey","useAnimation","useEffect","Portal","cn","useState","useMemo"],"mappings":"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","file":"chunk-A6HWKQ4D.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { DropdownContextValue } from \"./Dropdown.types\";\n\nexport const DropdownContext = createContext<DropdownContextValue | null>(null);\n\nexport function useDropdown(): DropdownContextValue {\n const ctx = useContext(DropdownContext);\n if (!ctx) {\n throw new Error(\n \"Dropdown compound components must be used within <Dropdown>\",\n );\n }\n return ctx;\n}\n","import { useDropdown } from \"./Dropdown.context\";\nimport type { DropdownTriggerProps } from \"./Dropdown.types\";\n\nexport function DropdownTrigger({ children }: DropdownTriggerProps) {\n const { setOpen, open, anchorRef } = useDropdown();\n\n return (\n <span\n ref={anchorRef as React.RefObject<HTMLSpanElement>}\n data-react-fancy-dropdown-trigger=\"\"\n className=\"inline-flex\"\n onClick={() => setOpen(!open)}\n aria-expanded={open}\n aria-haspopup=\"menu\"\n >\n {children}\n </span>\n );\n}\n\nDropdownTrigger.displayName = \"DropdownTrigger\";\n","import { useCallback, useEffect, useRef } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Portal } from \"../Portal\";\nimport { useDropdown } from \"./Dropdown.context\";\nimport { useFloatingPosition } from \"../../hooks/use-floating-position\";\nimport { useOutsideClick } from \"../../hooks/use-outside-click\";\nimport { useEscapeKey } from \"../../hooks/use-escape-key\";\nimport { useAnimation } from \"../../hooks/use-animation\";\nimport type { DropdownItemsProps } from \"./Dropdown.types\";\n\nexport function DropdownItems({ children, className }: DropdownItemsProps) {\n const { open, setOpen, anchorRef, activeIndex, setActiveIndex, placement, offset } =\n useDropdown();\n const floatingRef = useRef<HTMLDivElement>(null);\n const outsideRef = useRef<HTMLDivElement>(null);\n\n const position = useFloatingPosition(anchorRef, floatingRef, {\n placement,\n offset,\n enabled: open,\n });\n\n const close = useCallback(() => {\n setOpen(false);\n setActiveIndex(-1);\n }, [setOpen, setActiveIndex]);\n\n useOutsideClick(outsideRef, close, open, anchorRef);\n useEscapeKey(close, open);\n\n const { mounted, className: animClass, ref: animRef } = useAnimation({\n open,\n enterClass: \"fancy-scale-in\",\n exitClass: \"fancy-fade-out\",\n });\n\n useEffect(() => {\n if (!open || !floatingRef.current) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n const items = floatingRef.current?.querySelectorAll<HTMLElement>(\n '[role=\"menuitem\"]:not([aria-disabled=\"true\"])',\n );\n if (!items?.length) return;\n\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n const next = activeIndex < items.length - 1 ? activeIndex + 1 : 0;\n setActiveIndex(next);\n items[next]?.focus();\n } else if (e.key === \"ArrowUp\") {\n e.preventDefault();\n const prev = activeIndex > 0 ? activeIndex - 1 : items.length - 1;\n setActiveIndex(prev);\n items[prev]?.focus();\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [open, activeIndex, setActiveIndex]);\n\n if (!mounted) return null;\n\n return (\n <Portal>\n <div\n ref={(node) => {\n outsideRef.current = node;\n floatingRef.current = node;\n (animRef as React.MutableRefObject<HTMLElement | null>).current = node;\n }}\n data-react-fancy-dropdown=\"\"\n role=\"menu\"\n className={cn(\n \"fixed z-50 min-w-[8rem] rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] p-1 shadow-lg dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n animClass,\n className,\n )}\n style={{ left: position.x, top: position.y }}\n >\n {children}\n </div>\n </Portal>\n );\n}\n\nDropdownItems.displayName = \"DropdownItems\";\n","import { cn } from \"../../utils/cn\";\nimport { useDropdown } from \"./Dropdown.context\";\nimport type { DropdownItemProps } from \"./Dropdown.types\";\n\nexport function DropdownItem({\n children,\n onClick,\n disabled = false,\n danger = false,\n className,\n}: DropdownItemProps) {\n const { setOpen } = useDropdown();\n\n return (\n <button\n data-react-fancy-dropdown-item=\"\"\n type=\"button\"\n role=\"menuitem\"\n disabled={disabled}\n aria-disabled={disabled}\n className={cn(\n \"flex w-full items-center rounded-lg px-3 py-2 text-left text-sm transition-colors\",\n danger\n ? \"text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950\"\n : \"text-[color:var(--fancy-text-soft,var(--color-zinc-700))] hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n disabled && \"cursor-not-allowed opacity-50\",\n className,\n )}\n onClick={() => {\n if (disabled) return;\n onClick?.();\n setOpen(false);\n }}\n >\n {children}\n </button>\n );\n}\n\nDropdownItem.displayName = \"DropdownItem\";\n","import { cn } from \"../../utils/cn\";\nimport type { DropdownSeparatorProps } from \"./Dropdown.types\";\n\nexport function DropdownSeparator({ className }: DropdownSeparatorProps) {\n return (\n <div\n data-react-fancy-dropdown-separator=\"\"\n role=\"separator\"\n className={cn(\n \"my-1 h-px bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n />\n );\n}\n\nDropdownSeparator.displayName = \"DropdownSeparator\";\n","import { useMemo, useRef, useState } from \"react\";\nimport { DropdownContext } from \"./Dropdown.context\";\nimport { DropdownTrigger } from \"./DropdownTrigger\";\nimport { DropdownItems } from \"./DropdownItems\";\nimport { DropdownItem } from \"./DropdownItem\";\nimport { DropdownSeparator } from \"./DropdownSeparator\";\nimport type { DropdownProps } from \"./Dropdown.types\";\n\nfunction DropdownRoot({\n children,\n placement = \"bottom-start\",\n offset = 4,\n}: DropdownProps) {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState(-1);\n const anchorRef = useRef<HTMLElement>(null);\n\n const ctx = useMemo(\n () => ({ open, setOpen, anchorRef, activeIndex, setActiveIndex, placement, offset }),\n [open, anchorRef, activeIndex, placement, offset],\n );\n\n return (\n <DropdownContext.Provider value={ctx}>{children}</DropdownContext.Provider>\n );\n}\n\nexport const Dropdown = Object.assign(DropdownRoot, {\n Trigger: DropdownTrigger,\n Items: DropdownItems,\n Item: DropdownItem,\n Separator: DropdownSeparator,\n});\n"]}
|
package/dist/chunk-JG2DJK5A.js
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import { useAnimation } from './chunk-3K2CEAI7.js';
|
|
2
|
-
import { useFloatingPosition } from './chunk-MQ6VGG62.js';
|
|
3
|
-
import { Portal } from './chunk-RBCMKPBX.js';
|
|
4
|
-
import { useOutsideClick } from './chunk-RGANGU7X.js';
|
|
5
|
-
import { useEscapeKey } from './chunk-YX6FTJ6N.js';
|
|
6
|
-
import { cn } from './chunk-MMW2JGKZ.js';
|
|
7
|
-
import { createContext, useState, useRef, useMemo, useCallback, useEffect, useContext } from 'react';
|
|
8
|
-
import { jsx } from 'react/jsx-runtime';
|
|
9
|
-
|
|
10
|
-
var DropdownContext = createContext(null);
|
|
11
|
-
function useDropdown() {
|
|
12
|
-
const ctx = useContext(DropdownContext);
|
|
13
|
-
if (!ctx) {
|
|
14
|
-
throw new Error(
|
|
15
|
-
"Dropdown compound components must be used within <Dropdown>"
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
return ctx;
|
|
19
|
-
}
|
|
20
|
-
function DropdownTrigger({ children }) {
|
|
21
|
-
const { setOpen, open, anchorRef } = useDropdown();
|
|
22
|
-
return /* @__PURE__ */ jsx(
|
|
23
|
-
"span",
|
|
24
|
-
{
|
|
25
|
-
ref: anchorRef,
|
|
26
|
-
"data-react-fancy-dropdown-trigger": "",
|
|
27
|
-
className: "inline-flex",
|
|
28
|
-
onClick: () => setOpen(!open),
|
|
29
|
-
"aria-expanded": open,
|
|
30
|
-
"aria-haspopup": "menu",
|
|
31
|
-
children
|
|
32
|
-
}
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
DropdownTrigger.displayName = "DropdownTrigger";
|
|
36
|
-
function DropdownItems({ children, className }) {
|
|
37
|
-
const { open, setOpen, anchorRef, activeIndex, setActiveIndex, placement, offset } = useDropdown();
|
|
38
|
-
const floatingRef = useRef(null);
|
|
39
|
-
const outsideRef = useRef(null);
|
|
40
|
-
const position = useFloatingPosition(anchorRef, floatingRef, {
|
|
41
|
-
placement,
|
|
42
|
-
offset,
|
|
43
|
-
enabled: open
|
|
44
|
-
});
|
|
45
|
-
const close = useCallback(() => {
|
|
46
|
-
setOpen(false);
|
|
47
|
-
setActiveIndex(-1);
|
|
48
|
-
}, [setOpen, setActiveIndex]);
|
|
49
|
-
useOutsideClick(outsideRef, close, open, anchorRef);
|
|
50
|
-
useEscapeKey(close, open);
|
|
51
|
-
const { mounted, className: animClass, ref: animRef } = useAnimation({
|
|
52
|
-
open,
|
|
53
|
-
enterClass: "fancy-scale-in",
|
|
54
|
-
exitClass: "fancy-fade-out"
|
|
55
|
-
});
|
|
56
|
-
useEffect(() => {
|
|
57
|
-
if (!open || !floatingRef.current) return;
|
|
58
|
-
const handleKeyDown = (e) => {
|
|
59
|
-
const items = floatingRef.current?.querySelectorAll(
|
|
60
|
-
'[role="menuitem"]:not([aria-disabled="true"])'
|
|
61
|
-
);
|
|
62
|
-
if (!items?.length) return;
|
|
63
|
-
if (e.key === "ArrowDown") {
|
|
64
|
-
e.preventDefault();
|
|
65
|
-
const next = activeIndex < items.length - 1 ? activeIndex + 1 : 0;
|
|
66
|
-
setActiveIndex(next);
|
|
67
|
-
items[next]?.focus();
|
|
68
|
-
} else if (e.key === "ArrowUp") {
|
|
69
|
-
e.preventDefault();
|
|
70
|
-
const prev = activeIndex > 0 ? activeIndex - 1 : items.length - 1;
|
|
71
|
-
setActiveIndex(prev);
|
|
72
|
-
items[prev]?.focus();
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
76
|
-
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
77
|
-
}, [open, activeIndex, setActiveIndex]);
|
|
78
|
-
if (!mounted) return null;
|
|
79
|
-
return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(
|
|
80
|
-
"div",
|
|
81
|
-
{
|
|
82
|
-
ref: (node) => {
|
|
83
|
-
outsideRef.current = node;
|
|
84
|
-
floatingRef.current = node;
|
|
85
|
-
animRef.current = node;
|
|
86
|
-
},
|
|
87
|
-
"data-react-fancy-dropdown": "",
|
|
88
|
-
role: "menu",
|
|
89
|
-
className: cn(
|
|
90
|
-
"fixed z-50 min-w-[8rem] rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] p-1 shadow-lg dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]",
|
|
91
|
-
animClass,
|
|
92
|
-
className
|
|
93
|
-
),
|
|
94
|
-
style: { left: position.x, top: position.y },
|
|
95
|
-
children
|
|
96
|
-
}
|
|
97
|
-
) });
|
|
98
|
-
}
|
|
99
|
-
DropdownItems.displayName = "DropdownItems";
|
|
100
|
-
function DropdownItem({
|
|
101
|
-
children,
|
|
102
|
-
onClick,
|
|
103
|
-
disabled = false,
|
|
104
|
-
danger = false,
|
|
105
|
-
className
|
|
106
|
-
}) {
|
|
107
|
-
const { setOpen } = useDropdown();
|
|
108
|
-
return /* @__PURE__ */ jsx(
|
|
109
|
-
"button",
|
|
110
|
-
{
|
|
111
|
-
"data-react-fancy-dropdown-item": "",
|
|
112
|
-
type: "button",
|
|
113
|
-
role: "menuitem",
|
|
114
|
-
disabled,
|
|
115
|
-
"aria-disabled": disabled,
|
|
116
|
-
className: cn(
|
|
117
|
-
"flex w-full items-center rounded-lg px-3 py-2 text-left text-sm transition-colors",
|
|
118
|
-
danger ? "text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950" : "text-[color:var(--fancy-text-soft,var(--color-zinc-700))] hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]",
|
|
119
|
-
disabled && "cursor-not-allowed opacity-50",
|
|
120
|
-
className
|
|
121
|
-
),
|
|
122
|
-
onClick: () => {
|
|
123
|
-
if (disabled) return;
|
|
124
|
-
onClick?.();
|
|
125
|
-
setOpen(false);
|
|
126
|
-
},
|
|
127
|
-
children
|
|
128
|
-
}
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
DropdownItem.displayName = "DropdownItem";
|
|
132
|
-
function DropdownSeparator({ className }) {
|
|
133
|
-
return /* @__PURE__ */ jsx(
|
|
134
|
-
"div",
|
|
135
|
-
{
|
|
136
|
-
"data-react-fancy-dropdown-separator": "",
|
|
137
|
-
role: "separator",
|
|
138
|
-
className: cn(
|
|
139
|
-
"my-1 h-px bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]",
|
|
140
|
-
className
|
|
141
|
-
)
|
|
142
|
-
}
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
DropdownSeparator.displayName = "DropdownSeparator";
|
|
146
|
-
function DropdownRoot({
|
|
147
|
-
children,
|
|
148
|
-
placement = "bottom-start",
|
|
149
|
-
offset = 4
|
|
150
|
-
}) {
|
|
151
|
-
const [open, setOpen] = useState(false);
|
|
152
|
-
const [activeIndex, setActiveIndex] = useState(-1);
|
|
153
|
-
const anchorRef = useRef(null);
|
|
154
|
-
const ctx = useMemo(
|
|
155
|
-
() => ({ open, setOpen, anchorRef, activeIndex, setActiveIndex, placement, offset }),
|
|
156
|
-
[open, anchorRef, activeIndex, placement, offset]
|
|
157
|
-
);
|
|
158
|
-
return /* @__PURE__ */ jsx(DropdownContext.Provider, { value: ctx, children });
|
|
159
|
-
}
|
|
160
|
-
var Dropdown = Object.assign(DropdownRoot, {
|
|
161
|
-
Trigger: DropdownTrigger,
|
|
162
|
-
Items: DropdownItems,
|
|
163
|
-
Item: DropdownItem,
|
|
164
|
-
Separator: DropdownSeparator
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
export { Dropdown, useDropdown };
|
|
168
|
-
//# sourceMappingURL=chunk-JG2DJK5A.js.map
|
|
169
|
-
//# sourceMappingURL=chunk-JG2DJK5A.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/Dropdown/Dropdown.context.ts","../src/components/Dropdown/DropdownTrigger.tsx","../src/components/Dropdown/DropdownItems.tsx","../src/components/Dropdown/DropdownItem.tsx","../src/components/Dropdown/DropdownSeparator.tsx","../src/components/Dropdown/Dropdown.tsx"],"names":["jsx","useRef"],"mappings":"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","file":"chunk-JG2DJK5A.js","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { DropdownContextValue } from \"./Dropdown.types\";\n\nexport const DropdownContext = createContext<DropdownContextValue | null>(null);\n\nexport function useDropdown(): DropdownContextValue {\n const ctx = useContext(DropdownContext);\n if (!ctx) {\n throw new Error(\n \"Dropdown compound components must be used within <Dropdown>\",\n );\n }\n return ctx;\n}\n","import { useDropdown } from \"./Dropdown.context\";\nimport type { DropdownTriggerProps } from \"./Dropdown.types\";\n\nexport function DropdownTrigger({ children }: DropdownTriggerProps) {\n const { setOpen, open, anchorRef } = useDropdown();\n\n return (\n <span\n ref={anchorRef as React.RefObject<HTMLSpanElement>}\n data-react-fancy-dropdown-trigger=\"\"\n className=\"inline-flex\"\n onClick={() => setOpen(!open)}\n aria-expanded={open}\n aria-haspopup=\"menu\"\n >\n {children}\n </span>\n );\n}\n\nDropdownTrigger.displayName = \"DropdownTrigger\";\n","import { useCallback, useEffect, useRef } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Portal } from \"../Portal\";\nimport { useDropdown } from \"./Dropdown.context\";\nimport { useFloatingPosition } from \"../../hooks/use-floating-position\";\nimport { useOutsideClick } from \"../../hooks/use-outside-click\";\nimport { useEscapeKey } from \"../../hooks/use-escape-key\";\nimport { useAnimation } from \"../../hooks/use-animation\";\nimport type { DropdownItemsProps } from \"./Dropdown.types\";\n\nexport function DropdownItems({ children, className }: DropdownItemsProps) {\n const { open, setOpen, anchorRef, activeIndex, setActiveIndex, placement, offset } =\n useDropdown();\n const floatingRef = useRef<HTMLDivElement>(null);\n const outsideRef = useRef<HTMLDivElement>(null);\n\n const position = useFloatingPosition(anchorRef, floatingRef, {\n placement,\n offset,\n enabled: open,\n });\n\n const close = useCallback(() => {\n setOpen(false);\n setActiveIndex(-1);\n }, [setOpen, setActiveIndex]);\n\n useOutsideClick(outsideRef, close, open, anchorRef);\n useEscapeKey(close, open);\n\n const { mounted, className: animClass, ref: animRef } = useAnimation({\n open,\n enterClass: \"fancy-scale-in\",\n exitClass: \"fancy-fade-out\",\n });\n\n useEffect(() => {\n if (!open || !floatingRef.current) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n const items = floatingRef.current?.querySelectorAll<HTMLElement>(\n '[role=\"menuitem\"]:not([aria-disabled=\"true\"])',\n );\n if (!items?.length) return;\n\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n const next = activeIndex < items.length - 1 ? activeIndex + 1 : 0;\n setActiveIndex(next);\n items[next]?.focus();\n } else if (e.key === \"ArrowUp\") {\n e.preventDefault();\n const prev = activeIndex > 0 ? activeIndex - 1 : items.length - 1;\n setActiveIndex(prev);\n items[prev]?.focus();\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [open, activeIndex, setActiveIndex]);\n\n if (!mounted) return null;\n\n return (\n <Portal>\n <div\n ref={(node) => {\n outsideRef.current = node;\n floatingRef.current = node;\n (animRef as React.MutableRefObject<HTMLElement | null>).current = node;\n }}\n data-react-fancy-dropdown=\"\"\n role=\"menu\"\n className={cn(\n \"fixed z-50 min-w-[8rem] rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] p-1 shadow-lg dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n animClass,\n className,\n )}\n style={{ left: position.x, top: position.y }}\n >\n {children}\n </div>\n </Portal>\n );\n}\n\nDropdownItems.displayName = \"DropdownItems\";\n","import { cn } from \"../../utils/cn\";\nimport { useDropdown } from \"./Dropdown.context\";\nimport type { DropdownItemProps } from \"./Dropdown.types\";\n\nexport function DropdownItem({\n children,\n onClick,\n disabled = false,\n danger = false,\n className,\n}: DropdownItemProps) {\n const { setOpen } = useDropdown();\n\n return (\n <button\n data-react-fancy-dropdown-item=\"\"\n type=\"button\"\n role=\"menuitem\"\n disabled={disabled}\n aria-disabled={disabled}\n className={cn(\n \"flex w-full items-center rounded-lg px-3 py-2 text-left text-sm transition-colors\",\n danger\n ? \"text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950\"\n : \"text-[color:var(--fancy-text-soft,var(--color-zinc-700))] hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n disabled && \"cursor-not-allowed opacity-50\",\n className,\n )}\n onClick={() => {\n if (disabled) return;\n onClick?.();\n setOpen(false);\n }}\n >\n {children}\n </button>\n );\n}\n\nDropdownItem.displayName = \"DropdownItem\";\n","import { cn } from \"../../utils/cn\";\nimport type { DropdownSeparatorProps } from \"./Dropdown.types\";\n\nexport function DropdownSeparator({ className }: DropdownSeparatorProps) {\n return (\n <div\n data-react-fancy-dropdown-separator=\"\"\n role=\"separator\"\n className={cn(\n \"my-1 h-px bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n />\n );\n}\n\nDropdownSeparator.displayName = \"DropdownSeparator\";\n","import { useMemo, useRef, useState } from \"react\";\nimport { DropdownContext } from \"./Dropdown.context\";\nimport { DropdownTrigger } from \"./DropdownTrigger\";\nimport { DropdownItems } from \"./DropdownItems\";\nimport { DropdownItem } from \"./DropdownItem\";\nimport { DropdownSeparator } from \"./DropdownSeparator\";\nimport type { DropdownProps } from \"./Dropdown.types\";\n\nfunction DropdownRoot({\n children,\n placement = \"bottom-start\",\n offset = 4,\n}: DropdownProps) {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState(-1);\n const anchorRef = useRef<HTMLElement>(null);\n\n const ctx = useMemo(\n () => ({ open, setOpen, anchorRef, activeIndex, setActiveIndex, placement, offset }),\n [open, anchorRef, activeIndex, placement, offset],\n );\n\n return (\n <DropdownContext.Provider value={ctx}>{children}</DropdownContext.Provider>\n );\n}\n\nexport const Dropdown = Object.assign(DropdownRoot, {\n Trigger: DropdownTrigger,\n Items: DropdownItems,\n Item: DropdownItem,\n Separator: DropdownSeparator,\n});\n"]}
|