@evinvest/uikit 0.26.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -8
- package/dist/components/account-menu.js +70 -0
- package/dist/components/account-menu.js.map +1 -0
- package/dist/components/alert-dialog.js +4 -3
- package/dist/components/alert-dialog.js.map +1 -1
- package/dist/components/app-shell.js +11 -0
- package/dist/components/app-shell.js.map +1 -1
- package/dist/components/command.js +129 -20
- package/dist/components/command.js.map +1 -1
- package/dist/components/context-menu.js +8 -8
- package/dist/components/context-menu.js.map +1 -1
- package/dist/components/dialog.js +4 -3
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/drawer.js +9 -5
- package/dist/components/drawer.js.map +1 -1
- package/dist/components/dropdown-menu.js +13 -10
- package/dist/components/dropdown-menu.js.map +1 -1
- package/dist/components/info-tip.js +2 -2
- package/dist/components/info-tip.js.map +1 -1
- package/dist/components/menubar.js +18 -5
- package/dist/components/menubar.js.map +1 -1
- package/dist/components/navigation-menu.js +2 -2
- package/dist/components/navigation-menu.js.map +1 -1
- package/dist/components/popover.js +2 -2
- package/dist/components/popover.js.map +1 -1
- package/dist/components/select.js +2 -2
- package/dist/components/select.js.map +1 -1
- package/dist/components/sheet.js +4 -3
- package/dist/components/sheet.js.map +1 -1
- package/dist/components/sonner.js +1 -0
- package/dist/components/sonner.js.map +1 -1
- package/dist/components/table.js +24 -7
- package/dist/components/table.js.map +1 -1
- package/dist/components/toggle-group.js +1 -1
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/top-bar.js +21 -0
- package/dist/components/top-bar.js.map +1 -0
- package/dist/generated/context-menu.js +1 -1
- package/dist/generated/context-menu.js.map +1 -1
- package/dist/generated/dropdown-menu.js +1 -1
- package/dist/generated/dropdown-menu.js.map +1 -1
- package/dist/generated/menubar.js +1 -1
- package/dist/generated/menubar.js.map +1 -1
- package/dist/generated/table.js +20 -3
- package/dist/generated/table.js.map +1 -1
- package/dist/generated/toggle.js +2 -2
- package/dist/generated/toggle.js.map +1 -1
- package/dist/index.d.ts +168 -22
- package/dist/index.js +5 -2
- package/dist/primitives/dismissable-layer.js +80 -15
- package/dist/primitives/dismissable-layer.js.map +1 -1
- package/dist/primitives/use-roving-focus.js +13 -7
- package/dist/primitives/use-roving-focus.js.map +1 -1
- package/package.json +1 -1
- package/styles/ev.css +4 -2
- package/styles/theme.css +7 -0
- package/styles/tokens.css +15 -3
|
@@ -3,7 +3,9 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "../lib/cn.js";
|
|
5
5
|
import { useControllableState } from "../primitives/use-controllable-state.js";
|
|
6
|
+
import { DismissableLayerScope, useDismissableLayer } from "../primitives/dismissable-layer.js";
|
|
6
7
|
import { useFocusScope } from "../primitives/focus-scope.js";
|
|
8
|
+
import { mergeRefs } from "../primitives/merge-refs.js";
|
|
7
9
|
import { Portal } from "../primitives/portal.js";
|
|
8
10
|
import {
|
|
9
11
|
COMMAND_DIALOG_COMMAND,
|
|
@@ -25,12 +27,77 @@ function useCommand() {
|
|
|
25
27
|
if (!ctx) throw new Error("Command parts must be used within <Command>");
|
|
26
28
|
return ctx;
|
|
27
29
|
}
|
|
30
|
+
const ITEM_SELECTOR = '[data-slot="command-item"]:not([aria-disabled="true"])';
|
|
31
|
+
function useCommandNavigation(query, handlers) {
|
|
32
|
+
const rootRef = React.useRef(null);
|
|
33
|
+
const [activeId, setActiveId] = React.useState(null);
|
|
34
|
+
const userMoved = React.useRef(false);
|
|
35
|
+
const enabledItems = React.useCallback(() => {
|
|
36
|
+
const root = rootRef.current;
|
|
37
|
+
if (!root) return [];
|
|
38
|
+
return [...root.querySelectorAll(ITEM_SELECTOR)].filter(
|
|
39
|
+
(el) => el.closest('[data-slot="command"]') === root
|
|
40
|
+
);
|
|
41
|
+
}, []);
|
|
42
|
+
React.useEffect(() => {
|
|
43
|
+
userMoved.current = false;
|
|
44
|
+
}, [query]);
|
|
45
|
+
React.useEffect(() => {
|
|
46
|
+
const els = enabledItems();
|
|
47
|
+
const moved = userMoved.current;
|
|
48
|
+
setActiveId(
|
|
49
|
+
(prev) => moved && prev !== null && els.some((el) => el.id === prev) ? prev : els[0]?.id ?? null
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
const activate = React.useCallback((id) => {
|
|
53
|
+
userMoved.current = true;
|
|
54
|
+
setActiveId(id);
|
|
55
|
+
}, []);
|
|
56
|
+
const onKeyDown = (event) => {
|
|
57
|
+
if (event.nativeEvent.isComposing) return;
|
|
58
|
+
const field = event.target;
|
|
59
|
+
if (field.dataset["slot"] !== "command-input") return;
|
|
60
|
+
const els = enabledItems();
|
|
61
|
+
const current = els.findIndex((el) => el.id === activeId);
|
|
62
|
+
const last = els.length - 1;
|
|
63
|
+
let next;
|
|
64
|
+
switch (event.key) {
|
|
65
|
+
case "ArrowDown":
|
|
66
|
+
next = current < 0 ? 0 : Math.min(current + 1, last);
|
|
67
|
+
break;
|
|
68
|
+
case "ArrowUp":
|
|
69
|
+
next = current < 0 ? last : Math.max(current - 1, 0);
|
|
70
|
+
break;
|
|
71
|
+
// Home / End are left to the caret: the field is editable (ARIA APG
|
|
72
|
+
// editable combobox).
|
|
73
|
+
case "Enter": {
|
|
74
|
+
const select = activeId !== null && current >= 0 ? handlers.current.get(activeId) : void 0;
|
|
75
|
+
if (select) {
|
|
76
|
+
event.preventDefault();
|
|
77
|
+
select();
|
|
78
|
+
}
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
default:
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const target = els[next];
|
|
85
|
+
if (!target) return;
|
|
86
|
+
event.preventDefault();
|
|
87
|
+
activate(target.id);
|
|
88
|
+
target.scrollIntoView?.({ block: "nearest" });
|
|
89
|
+
};
|
|
90
|
+
return { rootRef, activeId, activate, onKeyDown };
|
|
91
|
+
}
|
|
28
92
|
function Command({
|
|
29
93
|
className,
|
|
30
94
|
search,
|
|
31
95
|
defaultSearch = "",
|
|
32
96
|
onSearchChange,
|
|
97
|
+
shouldFilter = true,
|
|
33
98
|
children,
|
|
99
|
+
ref,
|
|
100
|
+
onKeyDown,
|
|
34
101
|
...props
|
|
35
102
|
}) {
|
|
36
103
|
const [currentSearch, setSearch] = useControllableState({
|
|
@@ -41,15 +108,21 @@ function Command({
|
|
|
41
108
|
const [items, setItems] = React.useState(
|
|
42
109
|
() => /* @__PURE__ */ new Map()
|
|
43
110
|
);
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
111
|
+
const handlers = React.useRef(/* @__PURE__ */ new Map());
|
|
112
|
+
const registerItem = React.useCallback(
|
|
113
|
+
(id, value, select) => {
|
|
114
|
+
handlers.current.set(id, select);
|
|
115
|
+
setItems((prev) => {
|
|
116
|
+
if (prev.get(id) === value) return prev;
|
|
117
|
+
const next = new Map(prev);
|
|
118
|
+
next.set(id, value);
|
|
119
|
+
return next;
|
|
120
|
+
});
|
|
121
|
+
},
|
|
122
|
+
[]
|
|
123
|
+
);
|
|
52
124
|
const unregisterItem = React.useCallback((id) => {
|
|
125
|
+
handlers.current.delete(id);
|
|
53
126
|
setItems((prev) => {
|
|
54
127
|
if (!prev.has(id)) return prev;
|
|
55
128
|
const next = new Map(prev);
|
|
@@ -59,13 +132,15 @@ function Command({
|
|
|
59
132
|
}, []);
|
|
60
133
|
const query = currentSearch.trim().toLowerCase();
|
|
61
134
|
const matches = React.useCallback(
|
|
62
|
-
(value) => query === "" || value.toLowerCase().includes(query),
|
|
63
|
-
[query]
|
|
135
|
+
(value) => !shouldFilter || query === "" || value.toLowerCase().includes(query),
|
|
136
|
+
[query, shouldFilter]
|
|
64
137
|
);
|
|
65
138
|
const hasMatches = React.useMemo(
|
|
66
139
|
() => [...items.values()].some(matches),
|
|
67
140
|
[items, matches]
|
|
68
141
|
);
|
|
142
|
+
const listId = React.useId();
|
|
143
|
+
const nav = useCommandNavigation(query, handlers);
|
|
69
144
|
return /* @__PURE__ */ jsx(
|
|
70
145
|
CommandContext.Provider,
|
|
71
146
|
{
|
|
@@ -76,13 +151,21 @@ function Command({
|
|
|
76
151
|
unregisterItem,
|
|
77
152
|
query,
|
|
78
153
|
matches,
|
|
79
|
-
hasMatches
|
|
154
|
+
hasMatches,
|
|
155
|
+
listId,
|
|
156
|
+
activeId: nav.activeId,
|
|
157
|
+
activate: nav.activate
|
|
80
158
|
},
|
|
81
159
|
children: /* @__PURE__ */ jsx(
|
|
82
160
|
"div",
|
|
83
161
|
{
|
|
84
162
|
"data-slot": "command",
|
|
163
|
+
ref: mergeRefs(nav.rootRef, ref),
|
|
85
164
|
className: cn(COMMAND_ROOT, className),
|
|
165
|
+
onKeyDown: (e) => {
|
|
166
|
+
onKeyDown?.(e);
|
|
167
|
+
if (!e.defaultPrevented) nav.onKeyDown(e);
|
|
168
|
+
},
|
|
86
169
|
...props,
|
|
87
170
|
children
|
|
88
171
|
}
|
|
@@ -94,6 +177,7 @@ function CommandDialog({
|
|
|
94
177
|
open,
|
|
95
178
|
defaultOpen = false,
|
|
96
179
|
onOpenChange,
|
|
180
|
+
shouldFilter = true,
|
|
97
181
|
className,
|
|
98
182
|
children
|
|
99
183
|
}) {
|
|
@@ -103,6 +187,12 @@ function CommandDialog({
|
|
|
103
187
|
...onOpenChange ? { onChange: onOpenChange } : {}
|
|
104
188
|
});
|
|
105
189
|
const scopeRef = useFocusScope(isOpen);
|
|
190
|
+
const dismissRef = useDismissableLayer({
|
|
191
|
+
enabled: isOpen,
|
|
192
|
+
onDismiss: () => setOpen(false),
|
|
193
|
+
pointerOutside: false,
|
|
194
|
+
modal: true
|
|
195
|
+
});
|
|
106
196
|
if (!isOpen) return null;
|
|
107
197
|
return /* @__PURE__ */ jsxs(Portal, { children: [
|
|
108
198
|
/* @__PURE__ */ jsx(
|
|
@@ -119,12 +209,9 @@ function CommandDialog({
|
|
|
119
209
|
role: "dialog",
|
|
120
210
|
"aria-modal": "true",
|
|
121
211
|
"data-slot": "command-dialog",
|
|
122
|
-
ref: scopeRef,
|
|
123
|
-
onKeyDown: (e) => {
|
|
124
|
-
if (e.key === "Escape") setOpen(false);
|
|
125
|
-
},
|
|
212
|
+
ref: mergeRefs(scopeRef, dismissRef),
|
|
126
213
|
className: cn(COMMAND_DIALOG_CONTENT, className),
|
|
127
|
-
children: /* @__PURE__ */ jsx(Command, { className: COMMAND_DIALOG_COMMAND, children })
|
|
214
|
+
children: /* @__PURE__ */ jsx(Command, { className: COMMAND_DIALOG_COMMAND, shouldFilter, children: /* @__PURE__ */ jsx(DismissableLayerScope, { layer: dismissRef, children }) })
|
|
128
215
|
}
|
|
129
216
|
)
|
|
130
217
|
] });
|
|
@@ -133,7 +220,7 @@ function CommandInput({
|
|
|
133
220
|
className,
|
|
134
221
|
...props
|
|
135
222
|
}) {
|
|
136
|
-
const { search, setSearch } = useCommand();
|
|
223
|
+
const { search, setSearch, listId, activeId } = useCommand();
|
|
137
224
|
return /* @__PURE__ */ jsxs("div", { className: COMMAND_INPUT_WRAPPER, "data-slot": "command-input-wrapper", children: [
|
|
138
225
|
/* @__PURE__ */ jsxs(
|
|
139
226
|
"svg",
|
|
@@ -161,6 +248,11 @@ function CommandInput({
|
|
|
161
248
|
type: "text",
|
|
162
249
|
role: "combobox",
|
|
163
250
|
"data-slot": "command-input",
|
|
251
|
+
"aria-autocomplete": "list",
|
|
252
|
+
"aria-expanded": true,
|
|
253
|
+
"aria-controls": listId,
|
|
254
|
+
"aria-activedescendant": activeId ?? void 0,
|
|
255
|
+
autoComplete: "off",
|
|
164
256
|
value: search,
|
|
165
257
|
onChange: (e) => setSearch(e.target.value),
|
|
166
258
|
className: cn(COMMAND_INPUT, className),
|
|
@@ -170,10 +262,12 @@ function CommandInput({
|
|
|
170
262
|
] });
|
|
171
263
|
}
|
|
172
264
|
function CommandList({ className, ...props }) {
|
|
265
|
+
const { listId } = useCommand();
|
|
173
266
|
return /* @__PURE__ */ jsx(
|
|
174
267
|
"div",
|
|
175
268
|
{
|
|
176
269
|
role: "listbox",
|
|
270
|
+
id: listId,
|
|
177
271
|
"data-slot": "command-list",
|
|
178
272
|
className: cn(COMMAND_LIST, className),
|
|
179
273
|
...props
|
|
@@ -213,27 +307,42 @@ function CommandItem({
|
|
|
213
307
|
value,
|
|
214
308
|
disabled = false,
|
|
215
309
|
onSelect,
|
|
310
|
+
id: idProp,
|
|
311
|
+
onPointerMove,
|
|
216
312
|
children,
|
|
217
313
|
...props
|
|
218
314
|
}) {
|
|
219
|
-
const { registerItem, unregisterItem, matches } = useCommand();
|
|
220
|
-
const
|
|
315
|
+
const { registerItem, unregisterItem, matches, activeId, activate } = useCommand();
|
|
316
|
+
const reactId = React.useId();
|
|
317
|
+
const id = idProp ?? reactId;
|
|
318
|
+
const onSelectRef = React.useRef(onSelect);
|
|
319
|
+
React.useEffect(() => {
|
|
320
|
+
onSelectRef.current = onSelect;
|
|
321
|
+
});
|
|
221
322
|
React.useEffect(() => {
|
|
222
|
-
registerItem(id, value);
|
|
323
|
+
registerItem(id, value, () => onSelectRef.current?.(value));
|
|
223
324
|
return () => unregisterItem(id);
|
|
224
325
|
}, [id, value, registerItem, unregisterItem]);
|
|
225
326
|
if (!matches(value)) return null;
|
|
327
|
+
const selected = activeId === id;
|
|
226
328
|
return /* @__PURE__ */ jsx(
|
|
227
329
|
"div",
|
|
228
330
|
{
|
|
229
331
|
role: "option",
|
|
332
|
+
id,
|
|
230
333
|
"data-slot": "command-item",
|
|
231
334
|
"data-disabled": disabled,
|
|
335
|
+
"data-selected": selected,
|
|
336
|
+
"aria-selected": selected,
|
|
232
337
|
"aria-disabled": disabled || void 0,
|
|
233
338
|
tabIndex: -1,
|
|
234
339
|
onClick: () => {
|
|
235
340
|
if (!disabled) onSelect?.(value);
|
|
236
341
|
},
|
|
342
|
+
onPointerMove: (e) => {
|
|
343
|
+
onPointerMove?.(e);
|
|
344
|
+
if (!disabled && !selected) activate(id);
|
|
345
|
+
},
|
|
237
346
|
className: cn(COMMAND_ITEM, className),
|
|
238
347
|
...props,
|
|
239
348
|
children
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/command.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { useFocusScope } from \"../primitives/focus-scope\";\nimport { Portal } from \"../primitives/portal\";\nimport {\n COMMAND_DIALOG_COMMAND,\n COMMAND_DIALOG_CONTENT,\n COMMAND_DIALOG_OVERLAY,\n COMMAND_EMPTY,\n COMMAND_GROUP,\n COMMAND_INPUT,\n COMMAND_INPUT_WRAPPER,\n COMMAND_ITEM,\n COMMAND_LIST,\n COMMAND_ROOT,\n COMMAND_SEPARATOR,\n COMMAND_SHORTCUT,\n} from \"../generated/command\";\n\ninterface CommandContextValue {\n search: string;\n setSearch: (next: string) => void;\n /// Registers a `CommandItem`'s value so `CommandEmpty` can tell \"nothing\n /// matched\" from \"nothing is here\". Items register even while filtered out.\n registerItem: (id: string, value: string) => void;\n unregisterItem: (id: string) => void;\n /// The active query: trimmed, so blank input is not a search, and lowercased.\n /// Shared by the item filter and the empty-state gate so the two can never\n /// disagree.\n query: string;\n matches: (value: string) => boolean;\n hasMatches: boolean;\n}\n\nconst CommandContext = React.createContext<CommandContextValue | null>(null);\n\nfunction useCommand(): CommandContextValue {\n const ctx = React.useContext(CommandContext);\n if (!ctx) throw new Error(\"Command parts must be used within <Command>\");\n return ctx;\n}\n\nexport interface CommandProps extends React.ComponentProps<\"div\"> {\n search?: string;\n defaultSearch?: string;\n onSearchChange?: (search: string) => void;\n}\n\nexport function Command({\n className,\n search,\n defaultSearch = \"\",\n onSearchChange,\n children,\n ...props\n}: CommandProps) {\n const [currentSearch, setSearch] = useControllableState<string>({\n ...(search !== undefined ? { value: search } : {}),\n defaultValue: defaultSearch,\n ...(onSearchChange ? { onChange: onSearchChange } : {}),\n });\n\n const [items, setItems] = React.useState<ReadonlyMap<string, string>>(\n () => new Map(),\n );\n const registerItem = React.useCallback((id: string, value: string) => {\n setItems((prev) => {\n if (prev.get(id) === value) return prev;\n const next = new Map(prev);\n next.set(id, value);\n return next;\n });\n }, []);\n const unregisterItem = React.useCallback((id: string) => {\n setItems((prev) => {\n if (!prev.has(id)) return prev;\n const next = new Map(prev);\n next.delete(id);\n return next;\n });\n }, []);\n\n const query = currentSearch.trim().toLowerCase();\n const matches = React.useCallback(\n (value: string) => query === \"\" || value.toLowerCase().includes(query),\n [query],\n );\n const hasMatches = React.useMemo(\n () => [...items.values()].some(matches),\n [items, matches],\n );\n\n return (\n <CommandContext.Provider\n value={{\n search: currentSearch,\n setSearch,\n registerItem,\n unregisterItem,\n query,\n matches,\n hasMatches,\n }}\n >\n <div\n data-slot=\"command\"\n className={cn(COMMAND_ROOT, className)}\n {...props}\n >\n {children}\n </div>\n </CommandContext.Provider>\n );\n}\n\nexport interface CommandDialogProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n className?: string;\n children?: React.ReactNode;\n}\n\nexport function CommandDialog({\n open,\n defaultOpen = false,\n onOpenChange,\n className,\n children,\n}: CommandDialogProps) {\n const [isOpen, setOpen] = useControllableState<boolean>({\n ...(open !== undefined ? { value: open } : {}),\n defaultValue: defaultOpen,\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n const scopeRef = useFocusScope(isOpen);\n if (!isOpen) return null;\n return (\n <Portal>\n <div\n data-slot=\"command-overlay\"\n className={COMMAND_DIALOG_OVERLAY}\n onClick={() => setOpen(false)}\n />\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n data-slot=\"command-dialog\"\n ref={scopeRef}\n onKeyDown={(e) => {\n if (e.key === \"Escape\") setOpen(false);\n }}\n className={cn(COMMAND_DIALOG_CONTENT, className)}\n >\n <Command className={COMMAND_DIALOG_COMMAND}>\n {children}\n </Command>\n </div>\n </Portal>\n );\n}\n\nexport function CommandInput({\n className,\n ...props\n}: React.ComponentProps<\"input\">) {\n const { search, setSearch } = useCommand();\n return (\n <div className={COMMAND_INPUT_WRAPPER} data-slot=\"command-input-wrapper\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4 shrink-0 opacity-50\"\n aria-hidden=\"true\"\n >\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.3-4.3\" />\n </svg>\n <input\n type=\"text\"\n role=\"combobox\"\n data-slot=\"command-input\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className={cn(COMMAND_INPUT, className)}\n {...props}\n />\n </div>\n );\n}\n\nexport function CommandList({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n role=\"listbox\"\n data-slot=\"command-list\"\n className={cn(COMMAND_LIST, className)}\n {...props}\n />\n );\n}\n\n/// Renders only when a search is under way and no item matched it — never next\n/// to results, and never before the user has typed.\nexport function CommandEmpty({ className, children, ...props }: React.ComponentProps<\"div\">) {\n const { query, hasMatches } = useCommand();\n if (query === \"\" || hasMatches) return null;\n return (\n <div\n data-slot=\"command-empty\"\n className={cn(COMMAND_EMPTY, className)}\n {...props}\n >\n {children}\n </div>\n );\n}\n\nexport interface CommandGroupProps extends React.ComponentProps<\"div\"> {\n heading?: string;\n}\n\nexport function CommandGroup({ className, heading, children, ...props }: CommandGroupProps) {\n return (\n <div\n role=\"group\"\n data-slot=\"command-group\"\n className={cn(COMMAND_GROUP, className)}\n {...props}\n >\n {heading ? <div data-slot=\"command-group-heading\">{heading}</div> : null}\n {children}\n </div>\n );\n}\n\nexport interface CommandItemProps\n extends Omit<React.ComponentProps<\"div\">, \"onSelect\"> {\n value: string;\n disabled?: boolean;\n onSelect?: (value: string) => void;\n}\n\nexport function CommandItem({\n className,\n value,\n disabled = false,\n onSelect,\n children,\n ...props\n}: CommandItemProps) {\n const { registerItem, unregisterItem, matches } = useCommand();\n const id = React.useId();\n // Registered whether or not this item survives the filter below, so\n // `CommandEmpty` gates on the search, not on who happens to be mounted.\n React.useEffect(() => {\n registerItem(id, value);\n return () => unregisterItem(id);\n }, [id, value, registerItem, unregisterItem]);\n\n if (!matches(value)) return null;\n return (\n <div\n role=\"option\"\n data-slot=\"command-item\"\n data-disabled={disabled}\n aria-disabled={disabled || undefined}\n tabIndex={-1}\n onClick={() => {\n if (!disabled) onSelect?.(value);\n }}\n className={cn(COMMAND_ITEM, className)}\n {...props}\n >\n {children}\n </div>\n );\n}\n\nexport function CommandSeparator({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"command-separator\"\n className={cn(COMMAND_SEPARATOR, className)}\n {...props}\n />\n );\n}\n\nexport function CommandShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"command-shortcut\"\n className={cn(COMMAND_SHORTCUT, className)}\n {...props}\n />\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/command.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { DismissableLayerScope, useDismissableLayer } from \"../primitives/dismissable-layer\";\nimport { useFocusScope } from \"../primitives/focus-scope\";\nimport { mergeRefs } from \"../primitives/merge-refs\";\nimport { Portal } from \"../primitives/portal\";\nimport {\n COMMAND_DIALOG_COMMAND,\n COMMAND_DIALOG_CONTENT,\n COMMAND_DIALOG_OVERLAY,\n COMMAND_EMPTY,\n COMMAND_GROUP,\n COMMAND_INPUT,\n COMMAND_INPUT_WRAPPER,\n COMMAND_ITEM,\n COMMAND_LIST,\n COMMAND_ROOT,\n COMMAND_SEPARATOR,\n COMMAND_SHORTCUT,\n} from \"../generated/command\";\n\ninterface CommandContextValue {\n search: string;\n setSearch: (next: string) => void;\n /// Registers a `CommandItem`'s value so `CommandEmpty` can tell \"nothing\n /// matched\" from \"nothing is here\", and its select handler so Enter can fire\n /// it. Items register even while filtered out.\n registerItem: (id: string, value: string, select: () => void) => void;\n unregisterItem: (id: string) => void;\n /// The active query: trimmed, so blank input is not a search, and lowercased.\n /// Shared by the item filter and the empty-state gate so the two can never\n /// disagree.\n query: string;\n matches: (value: string) => boolean;\n hasMatches: boolean;\n listId: string;\n /// DOM id of the highlighted item. Focus stays in the input and points here\n /// through `aria-activedescendant` (the ARIA combobox pattern), so items\n /// never need to be tabbable.\n activeId: string | null;\n activate: (id: string) => void;\n}\n\nconst CommandContext = React.createContext<CommandContextValue | null>(null);\n\nfunction useCommand(): CommandContextValue {\n const ctx = React.useContext(CommandContext);\n if (!ctx) throw new Error(\"Command parts must be used within <Command>\");\n return ctx;\n}\n\nexport interface CommandProps extends React.ComponentProps<\"div\"> {\n search?: string;\n defaultSearch?: string;\n onSearchChange?: (search: string) => void;\n /// `false` hands filtering to the caller (e.g. rows that are a server\n /// response to the query): every mounted item renders, in the caller's\n /// order, and `CommandEmpty` counts mounted items. Defaults to `true`.\n shouldFilter?: boolean;\n}\n\nconst ITEM_SELECTOR = '[data-slot=\"command-item\"]:not([aria-disabled=\"true\"])';\n\n/// Keyboard highlight over the items currently in the DOM, in DOM order, so it\n/// follows whatever the filter (or the caller) rendered rather than the order\n/// items happened to register in.\nfunction useCommandNavigation(\n query: string,\n handlers: React.RefObject<Map<string, () => void>>,\n) {\n const rootRef = React.useRef<HTMLDivElement>(null);\n const [activeId, setActiveId] = React.useState<string | null>(null);\n // Until the user moves the highlight it tracks the first row, so results\n // that arrive after the keystroke (or reorder under it) put Enter on the top\n // hit. Once moved, it sticks for as long as that row stays.\n const userMoved = React.useRef(false);\n\n const enabledItems = React.useCallback((): HTMLElement[] => {\n const root = rootRef.current;\n if (!root) return [];\n return [...root.querySelectorAll<HTMLElement>(ITEM_SELECTOR)].filter(\n (el) => el.closest('[data-slot=\"command\"]') === root,\n );\n }, []);\n\n React.useEffect(() => {\n userMoved.current = false;\n }, [query]);\n\n // No deps on purpose: the caller can change the rendered rows on any render;\n // setting an unchanged id bails out without re-rendering.\n React.useEffect(() => {\n const els = enabledItems();\n const moved = userMoved.current;\n setActiveId((prev) =>\n moved && prev !== null && els.some((el) => el.id === prev)\n ? prev\n : (els[0]?.id ?? null),\n );\n });\n\n const activate = React.useCallback((id: string) => {\n userMoved.current = true;\n setActiveId(id);\n }, []);\n\n const onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.nativeEvent.isComposing) return;\n // Only the search field drives the list: Enter or an arrow on a button\n // inside Command (an action in `CommandEmpty`, a footer) stays its own.\n const field = event.target as HTMLElement;\n if (field.dataset[\"slot\"] !== \"command-input\") return;\n const els = enabledItems();\n const current = els.findIndex((el) => el.id === activeId);\n const last = els.length - 1;\n let next: number;\n switch (event.key) {\n case \"ArrowDown\":\n next = current < 0 ? 0 : Math.min(current + 1, last);\n break;\n case \"ArrowUp\":\n next = current < 0 ? last : Math.max(current - 1, 0);\n break;\n // Home / End are left to the caret: the field is editable (ARIA APG\n // editable combobox).\n case \"Enter\": {\n const select =\n activeId !== null && current >= 0\n ? handlers.current.get(activeId)\n : undefined;\n if (select) {\n event.preventDefault();\n select();\n }\n return;\n }\n default:\n return;\n }\n const target = els[next];\n if (!target) return;\n event.preventDefault();\n activate(target.id);\n // Optional call: jsdom implements no `scrollIntoView`.\n target.scrollIntoView?.({ block: \"nearest\" });\n };\n\n return { rootRef, activeId, activate, onKeyDown };\n}\n\nexport function Command({\n className,\n search,\n defaultSearch = \"\",\n onSearchChange,\n shouldFilter = true,\n children,\n ref,\n onKeyDown,\n ...props\n}: CommandProps) {\n const [currentSearch, setSearch] = useControllableState<string>({\n ...(search !== undefined ? { value: search } : {}),\n defaultValue: defaultSearch,\n ...(onSearchChange ? { onChange: onSearchChange } : {}),\n });\n\n const [items, setItems] = React.useState<ReadonlyMap<string, string>>(\n () => new Map(),\n );\n const handlers = React.useRef(new Map<string, () => void>());\n const registerItem = React.useCallback(\n (id: string, value: string, select: () => void) => {\n handlers.current.set(id, select);\n setItems((prev) => {\n if (prev.get(id) === value) return prev;\n const next = new Map(prev);\n next.set(id, value);\n return next;\n });\n },\n [],\n );\n const unregisterItem = React.useCallback((id: string) => {\n handlers.current.delete(id);\n setItems((prev) => {\n if (!prev.has(id)) return prev;\n const next = new Map(prev);\n next.delete(id);\n return next;\n });\n }, []);\n\n const query = currentSearch.trim().toLowerCase();\n const matches = React.useCallback(\n (value: string) =>\n !shouldFilter || query === \"\" || value.toLowerCase().includes(query),\n [query, shouldFilter],\n );\n const hasMatches = React.useMemo(\n () => [...items.values()].some(matches),\n [items, matches],\n );\n\n const listId = React.useId();\n const nav = useCommandNavigation(query, handlers);\n\n return (\n <CommandContext.Provider\n value={{\n search: currentSearch,\n setSearch,\n registerItem,\n unregisterItem,\n query,\n matches,\n hasMatches,\n listId,\n activeId: nav.activeId,\n activate: nav.activate,\n }}\n >\n <div\n data-slot=\"command\"\n ref={mergeRefs(nav.rootRef, ref)}\n className={cn(COMMAND_ROOT, className)}\n onKeyDown={(e) => {\n onKeyDown?.(e);\n if (!e.defaultPrevented) nav.onKeyDown(e);\n }}\n {...props}\n >\n {children}\n </div>\n </CommandContext.Provider>\n );\n}\n\nexport interface CommandDialogProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n /// See `CommandProps.shouldFilter`.\n shouldFilter?: boolean;\n className?: string;\n children?: React.ReactNode;\n}\n\nexport function CommandDialog({\n open,\n defaultOpen = false,\n onOpenChange,\n shouldFilter = true,\n className,\n children,\n}: CommandDialogProps) {\n const [isOpen, setOpen] = useControllableState<boolean>({\n ...(open !== undefined ? { value: open } : {}),\n defaultValue: defaultOpen,\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n const scopeRef = useFocusScope(isOpen);\n // Outside clicks stay with the overlay, as in Drawer: it is what \"outside\" means.\n const dismissRef = useDismissableLayer({\n enabled: isOpen,\n onDismiss: () => setOpen(false),\n pointerOutside: false,\n modal: true,\n });\n if (!isOpen) return null;\n return (\n <Portal>\n <div\n data-slot=\"command-overlay\"\n className={COMMAND_DIALOG_OVERLAY}\n onClick={() => setOpen(false)}\n />\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n data-slot=\"command-dialog\"\n ref={mergeRefs(scopeRef, dismissRef)}\n className={cn(COMMAND_DIALOG_CONTENT, className)}\n >\n <Command className={COMMAND_DIALOG_COMMAND} shouldFilter={shouldFilter}>\n <DismissableLayerScope layer={dismissRef}>{children}</DismissableLayerScope>\n </Command>\n </div>\n </Portal>\n );\n}\n\nexport function CommandInput({\n className,\n ...props\n}: React.ComponentProps<\"input\">) {\n const { search, setSearch, listId, activeId } = useCommand();\n return (\n <div className={COMMAND_INPUT_WRAPPER} data-slot=\"command-input-wrapper\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4 shrink-0 opacity-50\"\n aria-hidden=\"true\"\n >\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.3-4.3\" />\n </svg>\n <input\n type=\"text\"\n role=\"combobox\"\n data-slot=\"command-input\"\n aria-autocomplete=\"list\"\n aria-expanded={true}\n aria-controls={listId}\n aria-activedescendant={activeId ?? undefined}\n autoComplete=\"off\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className={cn(COMMAND_INPUT, className)}\n {...props}\n />\n </div>\n );\n}\n\nexport function CommandList({ className, ...props }: React.ComponentProps<\"div\">) {\n const { listId } = useCommand();\n return (\n <div\n role=\"listbox\"\n id={listId}\n data-slot=\"command-list\"\n className={cn(COMMAND_LIST, className)}\n {...props}\n />\n );\n}\n\n/// Renders only when a search is under way and no item matched it — never next\n/// to results, and never before the user has typed. With `shouldFilter={false}`\n/// \"matched\" means \"is mounted\": the caller's rows are the results.\nexport function CommandEmpty({ className, children, ...props }: React.ComponentProps<\"div\">) {\n const { query, hasMatches } = useCommand();\n if (query === \"\" || hasMatches) return null;\n return (\n <div\n data-slot=\"command-empty\"\n className={cn(COMMAND_EMPTY, className)}\n {...props}\n >\n {children}\n </div>\n );\n}\n\nexport interface CommandGroupProps extends React.ComponentProps<\"div\"> {\n heading?: string;\n}\n\nexport function CommandGroup({ className, heading, children, ...props }: CommandGroupProps) {\n return (\n <div\n role=\"group\"\n data-slot=\"command-group\"\n className={cn(COMMAND_GROUP, className)}\n {...props}\n >\n {heading ? <div data-slot=\"command-group-heading\">{heading}</div> : null}\n {children}\n </div>\n );\n}\n\nexport interface CommandItemProps\n extends Omit<React.ComponentProps<\"div\">, \"onSelect\"> {\n value: string;\n disabled?: boolean;\n onSelect?: (value: string) => void;\n}\n\nexport function CommandItem({\n className,\n value,\n disabled = false,\n onSelect,\n id: idProp,\n onPointerMove,\n children,\n ...props\n}: CommandItemProps) {\n const { registerItem, unregisterItem, matches, activeId, activate } = useCommand();\n const reactId = React.useId();\n // The DOM id is what `aria-activedescendant` points at, so a caller's own id\n // has to be the one registered.\n const id = idProp ?? reactId;\n // Read at select time, so an inline `onSelect` doesn't re-register every render.\n const onSelectRef = React.useRef(onSelect);\n React.useEffect(() => {\n onSelectRef.current = onSelect;\n });\n // Registered whether or not this item survives the filter below, so\n // `CommandEmpty` gates on the search, not on who happens to be mounted.\n React.useEffect(() => {\n registerItem(id, value, () => onSelectRef.current?.(value));\n return () => unregisterItem(id);\n }, [id, value, registerItem, unregisterItem]);\n\n if (!matches(value)) return null;\n const selected = activeId === id;\n return (\n <div\n role=\"option\"\n id={id}\n data-slot=\"command-item\"\n data-disabled={disabled}\n data-selected={selected}\n aria-selected={selected}\n aria-disabled={disabled || undefined}\n tabIndex={-1}\n onClick={() => {\n if (!disabled) onSelect?.(value);\n }}\n onPointerMove={(e) => {\n onPointerMove?.(e);\n if (!disabled && !selected) activate(id);\n }}\n className={cn(COMMAND_ITEM, className)}\n {...props}\n >\n {children}\n </div>\n );\n}\n\nexport function CommandSeparator({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"command-separator\"\n className={cn(COMMAND_SEPARATOR, className)}\n {...props}\n />\n );\n}\n\nexport function CommandShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"command-shortcut\"\n className={cn(COMMAND_SHORTCUT, className)}\n {...props}\n />\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -3,7 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "../lib/cn.js";
|
|
5
5
|
import { useControllableState } from "../primitives/use-controllable-state.js";
|
|
6
|
-
import { useDismissableLayer } from "../primitives/dismissable-layer.js";
|
|
6
|
+
import { DismissableLayerScope, useDismissableLayer } from "../primitives/dismissable-layer.js";
|
|
7
7
|
import { usePresence } from "../primitives/presence.js";
|
|
8
8
|
import { useRovingFocus } from "../primitives/use-roving-focus.js";
|
|
9
9
|
import { mergeRefs } from "../primitives/merge-refs.js";
|
|
@@ -70,11 +70,14 @@ function ContextMenuContent({ className, children, ...props }) {
|
|
|
70
70
|
const items = () => Array.from(
|
|
71
71
|
contentRef.current?.querySelectorAll("[role^='menuitem']") ?? []
|
|
72
72
|
).filter((el) => el.getAttribute("data-disabled") === null);
|
|
73
|
-
const { activeIndex, onKeyDown: rovingKeyDown } = useRovingFocus({
|
|
74
|
-
count:
|
|
73
|
+
const { activeIndex, setActiveIndex, onKeyDown: rovingKeyDown } = useRovingFocus({
|
|
74
|
+
count: () => items().length,
|
|
75
75
|
orientation: "vertical",
|
|
76
76
|
loop: true
|
|
77
77
|
});
|
|
78
|
+
React.useEffect(() => {
|
|
79
|
+
if (!open) setActiveIndex(0);
|
|
80
|
+
}, [open, setActiveIndex]);
|
|
78
81
|
React.useEffect(() => {
|
|
79
82
|
if (!open) return;
|
|
80
83
|
items()[activeIndex]?.focus();
|
|
@@ -89,10 +92,7 @@ function ContextMenuContent({ className, children, ...props }) {
|
|
|
89
92
|
"data-side": "bottom",
|
|
90
93
|
role: "menu",
|
|
91
94
|
tabIndex: -1,
|
|
92
|
-
onKeyDown:
|
|
93
|
-
rovingKeyDown(e);
|
|
94
|
-
if (e.key === "Escape") setOpen(false);
|
|
95
|
-
},
|
|
95
|
+
onKeyDown: rovingKeyDown,
|
|
96
96
|
ref: mergeRefs(dismissRef, presRef, contentRef),
|
|
97
97
|
style: { position: "fixed", top: point.y, left: point.x },
|
|
98
98
|
className: cn(
|
|
@@ -100,7 +100,7 @@ function ContextMenuContent({ className, children, ...props }) {
|
|
|
100
100
|
className
|
|
101
101
|
),
|
|
102
102
|
...props,
|
|
103
|
-
children
|
|
103
|
+
children: /* @__PURE__ */ jsx(DismissableLayerScope, { layer: dismissRef, children })
|
|
104
104
|
}
|
|
105
105
|
) }) });
|
|
106
106
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/context-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { useDismissableLayer } from \"../primitives/dismissable-layer\";\nimport { usePresence } from \"../primitives/presence\";\nimport { useRovingFocus } from \"../primitives/use-roving-focus\";\nimport { mergeRefs } from \"../primitives/merge-refs\";\nimport { Portal } from \"../primitives/portal\";\nimport { Slot } from \"../primitives/slot\";\nimport {\n CONTEXT_MENU_CHECK_ITEM,\n CONTEXT_MENU_CONTENT,\n CONTEXT_MENU_ITEM,\n CONTEXT_MENU_LABEL,\n CONTEXT_MENU_SEPARATOR,\n CONTEXT_MENU_SHORTCUT,\n CONTEXT_MENU_SUB_CONTENT,\n CONTEXT_MENU_SUB_TRIGGER,\n} from \"../generated/context-menu\";\n\ninterface ContextMenuContextValue {\n open: boolean;\n setOpen: (next: boolean) => void;\n point: { x: number; y: number };\n setPoint: (p: { x: number; y: number }) => void;\n}\n\nconst ContextMenuContext = React.createContext<ContextMenuContextValue | null>(null);\n\nfunction useContextMenu(): ContextMenuContextValue {\n const ctx = React.useContext(ContextMenuContext);\n if (!ctx) throw new Error(\"ContextMenu parts must be used within <ContextMenu>\");\n return ctx;\n}\n\nexport interface ContextMenuProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: React.ReactNode;\n}\n\nexport function ContextMenu({ open, defaultOpen = false, onOpenChange, children }: ContextMenuProps) {\n const [isOpen, setOpen] = useControllableState<boolean>({\n ...(open !== undefined ? { value: open } : {}),\n defaultValue: defaultOpen,\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n const [point, setPoint] = React.useState({ x: 0, y: 0 });\n return (\n <ContextMenuContext.Provider value={{ open: isOpen, setOpen, point, setPoint }}>\n <div data-slot=\"context-menu\">{children}</div>\n </ContextMenuContext.Provider>\n );\n}\n\nexport function ContextMenuPortal({ children }: { children?: React.ReactNode }) {\n return <Portal>{children}</Portal>;\n}\n\nexport interface ContextMenuTriggerProps extends React.ComponentProps<\"div\"> {\n asChild?: boolean;\n}\n\nexport function ContextMenuTrigger({ asChild = false, onContextMenu, ...props }: ContextMenuTriggerProps) {\n const { setOpen, setPoint } = useContextMenu();\n const Comp = asChild ? Slot : \"div\";\n return (\n <Comp\n data-slot=\"context-menu-trigger\"\n onContextMenu={(e: React.MouseEvent<HTMLDivElement>) => {\n onContextMenu?.(e);\n e.preventDefault();\n setPoint({ x: e.clientX, y: e.clientY });\n setOpen(true);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport function ContextMenuGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div data-slot=\"context-menu-group\" role=\"group\" className={className} {...props} />;\n}\n\nconst ContextMenuCloseContext = React.createContext<() => void>(() => {});\n\nexport function ContextMenuContent({ className, children, ...props }: React.ComponentProps<\"div\">) {\n const { open, setOpen, point } = useContextMenu();\n const { isPresent, ref: presRef } = usePresence(open);\n const dismissRef = useDismissableLayer({\n enabled: open,\n onDismiss: () => setOpen(false),\n });\n const contentRef = React.useRef<HTMLDivElement | null>(null);\n const items = () =>\n Array.from(\n contentRef.current?.querySelectorAll<HTMLElement>(\"[role^='menuitem']\") ?? [],\n ).filter((el) => el.getAttribute(\"data-disabled\") === null);\n const { activeIndex, onKeyDown: rovingKeyDown } = useRovingFocus({\n count: 64,\n orientation: \"vertical\",\n loop: true,\n });\n React.useEffect(() => {\n if (!open) return;\n items()[activeIndex]?.focus();\n }, [open, activeIndex]);\n const close = React.useCallback(() => setOpen(false), [setOpen]);\n if (!isPresent) return null;\n return (\n <Portal>\n <ContextMenuCloseContext.Provider value={close}>\n <div\n data-slot=\"context-menu-content\"\n data-state={open ? \"open\" : \"closed\"}\n data-side=\"bottom\"\n role=\"menu\"\n tabIndex={-1}\n onKeyDown={(e) => {\n rovingKeyDown(e);\n if (e.key === \"Escape\") setOpen(false);\n }}\n ref={mergeRefs(dismissRef, presRef, contentRef)}\n style={{ position: \"fixed\", top: point.y, left: point.x }}\n className={cn(\n CONTEXT_MENU_CONTENT,\n className,\n )}\n {...(props as Record<string, unknown>)}\n >\n {children}\n </div>\n </ContextMenuCloseContext.Provider>\n </Portal>\n );\n}\n\nexport interface ContextMenuItemProps extends React.ComponentProps<\"div\"> {\n inset?: boolean;\n variant?: \"neutral\" | \"destructive\";\n disabled?: boolean;\n}\n\nexport function ContextMenuItem({\n className,\n inset,\n variant = \"neutral\",\n disabled,\n onClick,\n onKeyDown,\n ...props\n}: ContextMenuItemProps) {\n const close = React.useContext(ContextMenuCloseContext);\n return (\n <div\n data-slot=\"context-menu-item\"\n data-inset={inset ? \"\" : undefined}\n data-variant={variant}\n data-disabled={disabled ? \"\" : undefined}\n role=\"menuitem\"\n tabIndex={disabled ? undefined : -1}\n className={cn(\n CONTEXT_MENU_ITEM,\n className,\n )}\n onClick={(e) => {\n if (disabled) return;\n onClick?.(e);\n close();\n }}\n onKeyDown={(e) => {\n onKeyDown?.(e);\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n (e.currentTarget as HTMLElement).click();\n }\n }}\n {...props}\n />\n );\n}\n\nexport interface ContextMenuCheckboxItemProps extends React.ComponentProps<\"div\"> {\n checked?: boolean;\n disabled?: boolean;\n}\n\nexport function ContextMenuCheckboxItem({\n className,\n children,\n checked,\n disabled,\n onClick,\n ...props\n}: ContextMenuCheckboxItemProps) {\n const close = React.useContext(ContextMenuCloseContext);\n return (\n <div\n data-slot=\"context-menu-checkbox-item\"\n role=\"menuitemcheckbox\"\n aria-checked={checked}\n data-state={checked ? \"checked\" : \"unchecked\"}\n data-disabled={disabled ? \"\" : undefined}\n tabIndex={disabled ? undefined : -1}\n className={cn(\n CONTEXT_MENU_CHECK_ITEM,\n className,\n )}\n onClick={(e) => {\n if (disabled) return;\n onClick?.(e);\n close();\n }}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n {checked ? (\n <svg className=\"size-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n ) : null}\n </span>\n {children}\n </div>\n );\n}\n\ninterface ContextMenuRadioGroupContextValue {\n value: string | undefined;\n setValue: (next: string) => void;\n}\n\nconst ContextMenuRadioGroupContext =\n React.createContext<ContextMenuRadioGroupContextValue | null>(null);\n\nexport interface ContextMenuRadioGroupProps extends Omit<React.ComponentProps<\"div\">, \"onChange\"> {\n value?: string;\n onValueChange?: (value: string) => void;\n}\n\nexport function ContextMenuRadioGroup({\n className,\n value,\n onValueChange,\n children,\n ...props\n}: ContextMenuRadioGroupProps) {\n const ctx = React.useMemo<ContextMenuRadioGroupContextValue>(\n () => ({ value, setValue: (next) => onValueChange?.(next) }),\n [value, onValueChange],\n );\n return (\n <ContextMenuRadioGroupContext.Provider value={ctx}>\n <div data-slot=\"context-menu-radio-group\" role=\"group\" className={className} {...props}>\n {children}\n </div>\n </ContextMenuRadioGroupContext.Provider>\n );\n}\n\nexport interface ContextMenuRadioItemProps extends React.ComponentProps<\"div\"> {\n value: string;\n disabled?: boolean;\n}\n\nexport function ContextMenuRadioItem({\n className,\n children,\n value,\n disabled,\n onClick,\n ...props\n}: ContextMenuRadioItemProps) {\n const close = React.useContext(ContextMenuCloseContext);\n const group = React.useContext(ContextMenuRadioGroupContext);\n const checked = group?.value === value;\n return (\n <div\n data-slot=\"context-menu-radio-item\"\n role=\"menuitemradio\"\n aria-checked={checked}\n data-state={checked ? \"checked\" : \"unchecked\"}\n data-disabled={disabled ? \"\" : undefined}\n tabIndex={disabled ? undefined : -1}\n className={cn(\n CONTEXT_MENU_CHECK_ITEM,\n className,\n )}\n onClick={(e) => {\n if (disabled) return;\n onClick?.(e);\n group?.setValue(value);\n close();\n }}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n {checked ? (\n <svg className=\"size-2 fill-current\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n </svg>\n ) : null}\n </span>\n {children}\n </div>\n );\n}\n\nexport interface ContextMenuLabelProps extends React.ComponentProps<\"div\"> {\n inset?: boolean;\n}\n\nexport function ContextMenuLabel({ className, inset, ...props }: ContextMenuLabelProps) {\n return (\n <div\n data-slot=\"context-menu-label\"\n data-inset={inset ? \"\" : undefined}\n className={cn(CONTEXT_MENU_LABEL, className)}\n {...props}\n />\n );\n}\n\nexport function ContextMenuSeparator({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"context-menu-separator\"\n role=\"separator\"\n className={cn(CONTEXT_MENU_SEPARATOR, className)}\n {...props}\n />\n );\n}\n\nexport function ContextMenuShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"context-menu-shortcut\"\n className={cn(CONTEXT_MENU_SHORTCUT, className)}\n {...props}\n />\n );\n}\n\n// Sub-menus render inline (nested) rather than in a separate floating layer:\n// the sub-content is revealed in place when its trigger toggles.\ninterface ContextMenuSubContextValue {\n open: boolean;\n setOpen: (next: boolean) => void;\n}\n\nconst ContextMenuSubContext = React.createContext<ContextMenuSubContextValue | null>(null);\n\nexport interface ContextMenuSubProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: React.ReactNode;\n}\n\nexport function ContextMenuSub({ open, defaultOpen = false, onOpenChange, children }: ContextMenuSubProps) {\n const [isOpen, setOpen] = useControllableState<boolean>({\n ...(open !== undefined ? { value: open } : {}),\n defaultValue: defaultOpen,\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n return (\n <ContextMenuSubContext.Provider value={{ open: isOpen, setOpen }}>\n <div data-slot=\"context-menu-sub\">{children}</div>\n </ContextMenuSubContext.Provider>\n );\n}\n\nexport interface ContextMenuSubTriggerProps extends React.ComponentProps<\"div\"> {\n inset?: boolean;\n}\n\nexport function ContextMenuSubTrigger({ className, inset, children, onClick, ...props }: ContextMenuSubTriggerProps) {\n const ctx = React.useContext(ContextMenuSubContext);\n return (\n <div\n data-slot=\"context-menu-sub-trigger\"\n data-inset={inset ? \"\" : undefined}\n data-state={ctx?.open ? \"open\" : \"closed\"}\n role=\"menuitem\"\n aria-haspopup=\"menu\"\n aria-expanded={ctx?.open}\n tabIndex={-1}\n className={cn(\n CONTEXT_MENU_SUB_TRIGGER,\n className,\n )}\n onClick={(e) => {\n onClick?.(e);\n ctx?.setOpen(!ctx.open);\n }}\n {...props}\n >\n {children}\n <svg className=\"ml-auto\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </div>\n );\n}\n\nexport function ContextMenuSubContent({ className, children, ...props }: React.ComponentProps<\"div\">) {\n const ctx = React.useContext(ContextMenuSubContext);\n if (!ctx?.open) return null;\n return (\n <div\n data-slot=\"context-menu-sub-content\"\n data-state=\"open\"\n role=\"menu\"\n className={cn(\n CONTEXT_MENU_SUB_CONTENT,\n className,\n )}\n {...props}\n >\n {children}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/context-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { DismissableLayerScope, useDismissableLayer } from \"../primitives/dismissable-layer\";\nimport { usePresence } from \"../primitives/presence\";\nimport { useRovingFocus } from \"../primitives/use-roving-focus\";\nimport { mergeRefs } from \"../primitives/merge-refs\";\nimport { Portal } from \"../primitives/portal\";\nimport { Slot } from \"../primitives/slot\";\nimport {\n CONTEXT_MENU_CHECK_ITEM,\n CONTEXT_MENU_CONTENT,\n CONTEXT_MENU_ITEM,\n CONTEXT_MENU_LABEL,\n CONTEXT_MENU_SEPARATOR,\n CONTEXT_MENU_SHORTCUT,\n CONTEXT_MENU_SUB_CONTENT,\n CONTEXT_MENU_SUB_TRIGGER,\n} from \"../generated/context-menu\";\n\ninterface ContextMenuContextValue {\n open: boolean;\n setOpen: (next: boolean) => void;\n point: { x: number; y: number };\n setPoint: (p: { x: number; y: number }) => void;\n}\n\nconst ContextMenuContext = React.createContext<ContextMenuContextValue | null>(null);\n\nfunction useContextMenu(): ContextMenuContextValue {\n const ctx = React.useContext(ContextMenuContext);\n if (!ctx) throw new Error(\"ContextMenu parts must be used within <ContextMenu>\");\n return ctx;\n}\n\nexport interface ContextMenuProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: React.ReactNode;\n}\n\nexport function ContextMenu({ open, defaultOpen = false, onOpenChange, children }: ContextMenuProps) {\n const [isOpen, setOpen] = useControllableState<boolean>({\n ...(open !== undefined ? { value: open } : {}),\n defaultValue: defaultOpen,\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n const [point, setPoint] = React.useState({ x: 0, y: 0 });\n return (\n <ContextMenuContext.Provider value={{ open: isOpen, setOpen, point, setPoint }}>\n <div data-slot=\"context-menu\">{children}</div>\n </ContextMenuContext.Provider>\n );\n}\n\nexport function ContextMenuPortal({ children }: { children?: React.ReactNode }) {\n return <Portal>{children}</Portal>;\n}\n\nexport interface ContextMenuTriggerProps extends React.ComponentProps<\"div\"> {\n asChild?: boolean;\n}\n\nexport function ContextMenuTrigger({ asChild = false, onContextMenu, ...props }: ContextMenuTriggerProps) {\n const { setOpen, setPoint } = useContextMenu();\n const Comp = asChild ? Slot : \"div\";\n return (\n <Comp\n data-slot=\"context-menu-trigger\"\n onContextMenu={(e: React.MouseEvent<HTMLDivElement>) => {\n onContextMenu?.(e);\n e.preventDefault();\n setPoint({ x: e.clientX, y: e.clientY });\n setOpen(true);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport function ContextMenuGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div data-slot=\"context-menu-group\" role=\"group\" className={className} {...props} />;\n}\n\nconst ContextMenuCloseContext = React.createContext<() => void>(() => {});\n\nexport function ContextMenuContent({ className, children, ...props }: React.ComponentProps<\"div\">) {\n const { open, setOpen, point } = useContextMenu();\n const { isPresent, ref: presRef } = usePresence(open);\n const dismissRef = useDismissableLayer({\n enabled: open,\n onDismiss: () => setOpen(false),\n });\n const contentRef = React.useRef<HTMLDivElement | null>(null);\n const items = () =>\n Array.from(\n contentRef.current?.querySelectorAll<HTMLElement>(\"[role^='menuitem']\") ?? [],\n ).filter((el) => el.getAttribute(\"data-disabled\") === null);\n const { activeIndex, setActiveIndex, onKeyDown: rovingKeyDown } = useRovingFocus({\n count: () => items().length,\n orientation: \"vertical\",\n loop: true,\n });\n // Reset on close: reopen on the first item, never on an index past the end.\n React.useEffect(() => {\n if (!open) setActiveIndex(0);\n }, [open, setActiveIndex]);\n React.useEffect(() => {\n if (!open) return;\n items()[activeIndex]?.focus();\n }, [open, activeIndex]);\n const close = React.useCallback(() => setOpen(false), [setOpen]);\n if (!isPresent) return null;\n return (\n <Portal>\n <ContextMenuCloseContext.Provider value={close}>\n <div\n data-slot=\"context-menu-content\"\n data-state={open ? \"open\" : \"closed\"}\n data-side=\"bottom\"\n role=\"menu\"\n tabIndex={-1}\n // Escape is the stack's (useDismissableLayer): it closes this menu\n // only while it is the top layer.\n onKeyDown={rovingKeyDown}\n ref={mergeRefs(dismissRef, presRef, contentRef)}\n style={{ position: \"fixed\", top: point.y, left: point.x }}\n className={cn(\n CONTEXT_MENU_CONTENT,\n className,\n )}\n {...(props as Record<string, unknown>)}\n >\n <DismissableLayerScope layer={dismissRef}>{children}</DismissableLayerScope>\n </div>\n </ContextMenuCloseContext.Provider>\n </Portal>\n );\n}\n\nexport interface ContextMenuItemProps extends React.ComponentProps<\"div\"> {\n inset?: boolean;\n variant?: \"neutral\" | \"destructive\";\n disabled?: boolean;\n}\n\nexport function ContextMenuItem({\n className,\n inset,\n variant = \"neutral\",\n disabled,\n onClick,\n onKeyDown,\n ...props\n}: ContextMenuItemProps) {\n const close = React.useContext(ContextMenuCloseContext);\n return (\n <div\n data-slot=\"context-menu-item\"\n data-inset={inset ? \"\" : undefined}\n data-variant={variant}\n data-disabled={disabled ? \"\" : undefined}\n role=\"menuitem\"\n tabIndex={disabled ? undefined : -1}\n className={cn(\n CONTEXT_MENU_ITEM,\n className,\n )}\n onClick={(e) => {\n if (disabled) return;\n onClick?.(e);\n close();\n }}\n onKeyDown={(e) => {\n onKeyDown?.(e);\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n (e.currentTarget as HTMLElement).click();\n }\n }}\n {...props}\n />\n );\n}\n\nexport interface ContextMenuCheckboxItemProps extends React.ComponentProps<\"div\"> {\n checked?: boolean;\n disabled?: boolean;\n}\n\nexport function ContextMenuCheckboxItem({\n className,\n children,\n checked,\n disabled,\n onClick,\n ...props\n}: ContextMenuCheckboxItemProps) {\n const close = React.useContext(ContextMenuCloseContext);\n return (\n <div\n data-slot=\"context-menu-checkbox-item\"\n role=\"menuitemcheckbox\"\n aria-checked={checked}\n data-state={checked ? \"checked\" : \"unchecked\"}\n data-disabled={disabled ? \"\" : undefined}\n tabIndex={disabled ? undefined : -1}\n className={cn(\n CONTEXT_MENU_CHECK_ITEM,\n className,\n )}\n onClick={(e) => {\n if (disabled) return;\n onClick?.(e);\n close();\n }}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n {checked ? (\n <svg className=\"size-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n ) : null}\n </span>\n {children}\n </div>\n );\n}\n\ninterface ContextMenuRadioGroupContextValue {\n value: string | undefined;\n setValue: (next: string) => void;\n}\n\nconst ContextMenuRadioGroupContext =\n React.createContext<ContextMenuRadioGroupContextValue | null>(null);\n\nexport interface ContextMenuRadioGroupProps extends Omit<React.ComponentProps<\"div\">, \"onChange\"> {\n value?: string;\n onValueChange?: (value: string) => void;\n}\n\nexport function ContextMenuRadioGroup({\n className,\n value,\n onValueChange,\n children,\n ...props\n}: ContextMenuRadioGroupProps) {\n const ctx = React.useMemo<ContextMenuRadioGroupContextValue>(\n () => ({ value, setValue: (next) => onValueChange?.(next) }),\n [value, onValueChange],\n );\n return (\n <ContextMenuRadioGroupContext.Provider value={ctx}>\n <div data-slot=\"context-menu-radio-group\" role=\"group\" className={className} {...props}>\n {children}\n </div>\n </ContextMenuRadioGroupContext.Provider>\n );\n}\n\nexport interface ContextMenuRadioItemProps extends React.ComponentProps<\"div\"> {\n value: string;\n disabled?: boolean;\n}\n\nexport function ContextMenuRadioItem({\n className,\n children,\n value,\n disabled,\n onClick,\n ...props\n}: ContextMenuRadioItemProps) {\n const close = React.useContext(ContextMenuCloseContext);\n const group = React.useContext(ContextMenuRadioGroupContext);\n const checked = group?.value === value;\n return (\n <div\n data-slot=\"context-menu-radio-item\"\n role=\"menuitemradio\"\n aria-checked={checked}\n data-state={checked ? \"checked\" : \"unchecked\"}\n data-disabled={disabled ? \"\" : undefined}\n tabIndex={disabled ? undefined : -1}\n className={cn(\n CONTEXT_MENU_CHECK_ITEM,\n className,\n )}\n onClick={(e) => {\n if (disabled) return;\n onClick?.(e);\n group?.setValue(value);\n close();\n }}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n {checked ? (\n <svg className=\"size-2 fill-current\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n </svg>\n ) : null}\n </span>\n {children}\n </div>\n );\n}\n\nexport interface ContextMenuLabelProps extends React.ComponentProps<\"div\"> {\n inset?: boolean;\n}\n\nexport function ContextMenuLabel({ className, inset, ...props }: ContextMenuLabelProps) {\n return (\n <div\n data-slot=\"context-menu-label\"\n data-inset={inset ? \"\" : undefined}\n className={cn(CONTEXT_MENU_LABEL, className)}\n {...props}\n />\n );\n}\n\nexport function ContextMenuSeparator({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"context-menu-separator\"\n role=\"separator\"\n className={cn(CONTEXT_MENU_SEPARATOR, className)}\n {...props}\n />\n );\n}\n\nexport function ContextMenuShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"context-menu-shortcut\"\n className={cn(CONTEXT_MENU_SHORTCUT, className)}\n {...props}\n />\n );\n}\n\n// Sub-menus render inline (nested) rather than in a separate floating layer:\n// the sub-content is revealed in place when its trigger toggles.\ninterface ContextMenuSubContextValue {\n open: boolean;\n setOpen: (next: boolean) => void;\n}\n\nconst ContextMenuSubContext = React.createContext<ContextMenuSubContextValue | null>(null);\n\nexport interface ContextMenuSubProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: React.ReactNode;\n}\n\nexport function ContextMenuSub({ open, defaultOpen = false, onOpenChange, children }: ContextMenuSubProps) {\n const [isOpen, setOpen] = useControllableState<boolean>({\n ...(open !== undefined ? { value: open } : {}),\n defaultValue: defaultOpen,\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n return (\n <ContextMenuSubContext.Provider value={{ open: isOpen, setOpen }}>\n <div data-slot=\"context-menu-sub\">{children}</div>\n </ContextMenuSubContext.Provider>\n );\n}\n\nexport interface ContextMenuSubTriggerProps extends React.ComponentProps<\"div\"> {\n inset?: boolean;\n}\n\nexport function ContextMenuSubTrigger({ className, inset, children, onClick, ...props }: ContextMenuSubTriggerProps) {\n const ctx = React.useContext(ContextMenuSubContext);\n return (\n <div\n data-slot=\"context-menu-sub-trigger\"\n data-inset={inset ? \"\" : undefined}\n data-state={ctx?.open ? \"open\" : \"closed\"}\n role=\"menuitem\"\n aria-haspopup=\"menu\"\n aria-expanded={ctx?.open}\n tabIndex={-1}\n className={cn(\n CONTEXT_MENU_SUB_TRIGGER,\n className,\n )}\n onClick={(e) => {\n onClick?.(e);\n ctx?.setOpen(!ctx.open);\n }}\n {...props}\n >\n {children}\n <svg className=\"ml-auto\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </div>\n );\n}\n\nexport function ContextMenuSubContent({ className, children, ...props }: React.ComponentProps<\"div\">) {\n const ctx = React.useContext(ContextMenuSubContext);\n if (!ctx?.open) return null;\n return (\n <div\n data-slot=\"context-menu-sub-content\"\n data-state=\"open\"\n role=\"menu\"\n className={cn(\n CONTEXT_MENU_SUB_CONTENT,\n className,\n )}\n {...props}\n >\n {children}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -3,7 +3,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "../lib/cn.js";
|
|
5
5
|
import { useControllableState } from "../primitives/use-controllable-state.js";
|
|
6
|
-
import { useDismissableLayer } from "../primitives/dismissable-layer.js";
|
|
6
|
+
import { DismissableLayerScope, useDismissableLayer } from "../primitives/dismissable-layer.js";
|
|
7
7
|
import { useFocusScope } from "../primitives/focus-scope.js";
|
|
8
8
|
import { usePresence } from "../primitives/presence.js";
|
|
9
9
|
import { mergeRefs } from "../primitives/merge-refs.js";
|
|
@@ -95,7 +95,8 @@ function DialogContent({
|
|
|
95
95
|
const dismissRef = useDismissableLayer({
|
|
96
96
|
enabled: open,
|
|
97
97
|
onDismiss: () => setOpen(false),
|
|
98
|
-
exclude: [triggerRef]
|
|
98
|
+
exclude: [triggerRef],
|
|
99
|
+
modal: true
|
|
99
100
|
});
|
|
100
101
|
React.useEffect(() => {
|
|
101
102
|
if (!open) return;
|
|
@@ -119,7 +120,7 @@ function DialogContent({
|
|
|
119
120
|
className: cn(DIALOG_CONTENT, className),
|
|
120
121
|
...props,
|
|
121
122
|
children: [
|
|
122
|
-
children,
|
|
123
|
+
/* @__PURE__ */ jsx(DismissableLayerScope, { layer: dismissRef, children }),
|
|
123
124
|
showCloseButton && /* @__PURE__ */ jsxs(
|
|
124
125
|
"button",
|
|
125
126
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { useDismissableLayer } from \"../primitives/dismissable-layer\";\nimport { type InitialFocus, useFocusScope } from \"../primitives/focus-scope\";\nimport { usePresence } from \"../primitives/presence\";\nimport { mergeRefs } from \"../primitives/merge-refs\";\nimport { Portal } from \"../primitives/portal\";\nimport { Slot } from \"../primitives/slot\";\nimport {\n DIALOG_CLOSE,\n DIALOG_CONTENT,\n DIALOG_DESCRIPTION,\n DIALOG_FOOTER,\n DIALOG_HEADER,\n DIALOG_OVERLAY,\n DIALOG_TITLE,\n} from \"../generated/dialog\";\n\ninterface DialogContextValue {\n open: boolean;\n setOpen: (next: boolean) => void;\n triggerRef: React.RefObject<HTMLElement | null>;\n}\n\nconst DialogContext = React.createContext<DialogContextValue | null>(null);\n\nfunction useDialog(): DialogContextValue {\n const ctx = React.useContext(DialogContext);\n if (!ctx) throw new Error(\"Dialog parts must be used within <Dialog>\");\n return ctx;\n}\n\nexport interface DialogProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: React.ReactNode;\n}\n\nexport function Dialog({ open, defaultOpen = false, onOpenChange, children }: DialogProps) {\n const [isOpen, setOpen] = useControllableState<boolean>({\n ...(open !== undefined ? { value: open } : {}),\n defaultValue: defaultOpen,\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n const triggerRef = React.useRef<HTMLElement | null>(null);\n return (\n <DialogContext.Provider value={{ open: isOpen, setOpen, triggerRef }}>\n {children}\n </DialogContext.Provider>\n );\n}\n\nexport interface DialogTriggerProps extends React.ComponentProps<\"button\"> {\n asChild?: boolean;\n}\n\nexport function DialogTrigger({ asChild = false, onClick, ...props }: DialogTriggerProps) {\n const { open, setOpen, triggerRef } = useDialog();\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n data-slot=\"dialog-trigger\"\n aria-expanded={open}\n ref={triggerRef as React.Ref<HTMLButtonElement>}\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n setOpen(true);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport type DialogPortalProps = { children?: React.ReactNode };\n\nexport function DialogPortal({ children }: DialogPortalProps) {\n return <>{children}</>;\n}\n\nexport interface DialogOverlayProps extends React.ComponentProps<\"div\"> {}\n\nexport function DialogOverlay({ className, ...props }: DialogOverlayProps) {\n const { open, setOpen } = useDialog();\n return (\n <div\n data-slot=\"dialog-overlay\"\n data-state={open ? \"open\" : \"closed\"}\n onClick={() => setOpen(false)}\n className={cn(DIALOG_OVERLAY, className)}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nconst XIcon = (props: React.SVGProps<SVGSVGElement>) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n);\n\nexport interface DialogContentProps extends React.ComponentProps<\"div\"> {\n showCloseButton?: boolean;\n /** Where focus lands on open. Default `first` — a dialog is usually a short form. */\n initialFocus?: InitialFocus;\n}\n\nexport function DialogContent({\n className,\n children,\n showCloseButton = true,\n initialFocus = \"first\",\n ...props\n}: DialogContentProps) {\n const { open, setOpen, triggerRef } = useDialog();\n const { isPresent, ref: presRef } = usePresence(open);\n const focusRef = useFocusScope(open, { initialFocus, returnFocusTo: triggerRef });\n const dismissRef = useDismissableLayer({\n enabled: open,\n onDismiss: () => setOpen(false),\n exclude: [triggerRef],\n });\n React.useEffect(() => {\n if (!open) return;\n const prev = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = prev;\n };\n }, [open]);\n if (!isPresent) return null;\n return (\n <Portal>\n <DialogOverlay />\n <div\n data-slot=\"dialog-content\"\n role=\"dialog\"\n aria-modal=\"true\"\n data-state={open ? \"open\" : \"closed\"}\n ref={mergeRefs(focusRef, dismissRef, presRef as React.Ref<HTMLDivElement>)}\n className={cn(DIALOG_CONTENT, className)}\n {...(props as Record<string, unknown>)}\n >\n {children}\n {showCloseButton && (\n <button\n type=\"button\"\n data-slot=\"dialog-close\"\n onClick={() => setOpen(false)}\n className={DIALOG_CLOSE}\n >\n <XIcon />\n <span className=\"sr-only\">Close</span>\n </button>\n )}\n </div>\n </Portal>\n );\n}\n\nexport interface DialogCloseProps extends React.ComponentProps<\"button\"> {\n asChild?: boolean;\n}\n\nexport function DialogClose({ asChild = false, onClick, ...props }: DialogCloseProps) {\n const { setOpen } = useDialog();\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n data-slot=\"dialog-close\"\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n setOpen(false);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport function DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(DIALOG_HEADER, className)}\n {...props}\n />\n );\n}\n\nexport function DialogFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(DIALOG_FOOTER, className)}\n {...props}\n />\n );\n}\n\nexport function DialogTitle({ className, ...props }: React.ComponentProps<\"h2\">) {\n return (\n <h2\n data-slot=\"dialog-title\"\n className={cn(DIALOG_TITLE, className)}\n {...props}\n />\n );\n}\n\nexport function DialogDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n return (\n <p\n data-slot=\"dialog-description\"\n className={cn(DIALOG_DESCRIPTION, className)}\n {...props}\n />\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { DismissableLayerScope, useDismissableLayer } from \"../primitives/dismissable-layer\";\nimport { type InitialFocus, useFocusScope } from \"../primitives/focus-scope\";\nimport { usePresence } from \"../primitives/presence\";\nimport { mergeRefs } from \"../primitives/merge-refs\";\nimport { Portal } from \"../primitives/portal\";\nimport { Slot } from \"../primitives/slot\";\nimport {\n DIALOG_CLOSE,\n DIALOG_CONTENT,\n DIALOG_DESCRIPTION,\n DIALOG_FOOTER,\n DIALOG_HEADER,\n DIALOG_OVERLAY,\n DIALOG_TITLE,\n} from \"../generated/dialog\";\n\ninterface DialogContextValue {\n open: boolean;\n setOpen: (next: boolean) => void;\n triggerRef: React.RefObject<HTMLElement | null>;\n}\n\nconst DialogContext = React.createContext<DialogContextValue | null>(null);\n\nfunction useDialog(): DialogContextValue {\n const ctx = React.useContext(DialogContext);\n if (!ctx) throw new Error(\"Dialog parts must be used within <Dialog>\");\n return ctx;\n}\n\nexport interface DialogProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: React.ReactNode;\n}\n\nexport function Dialog({ open, defaultOpen = false, onOpenChange, children }: DialogProps) {\n const [isOpen, setOpen] = useControllableState<boolean>({\n ...(open !== undefined ? { value: open } : {}),\n defaultValue: defaultOpen,\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n const triggerRef = React.useRef<HTMLElement | null>(null);\n return (\n <DialogContext.Provider value={{ open: isOpen, setOpen, triggerRef }}>\n {children}\n </DialogContext.Provider>\n );\n}\n\nexport interface DialogTriggerProps extends React.ComponentProps<\"button\"> {\n asChild?: boolean;\n}\n\nexport function DialogTrigger({ asChild = false, onClick, ...props }: DialogTriggerProps) {\n const { open, setOpen, triggerRef } = useDialog();\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n data-slot=\"dialog-trigger\"\n aria-expanded={open}\n ref={triggerRef as React.Ref<HTMLButtonElement>}\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n setOpen(true);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport type DialogPortalProps = { children?: React.ReactNode };\n\nexport function DialogPortal({ children }: DialogPortalProps) {\n return <>{children}</>;\n}\n\nexport interface DialogOverlayProps extends React.ComponentProps<\"div\"> {}\n\nexport function DialogOverlay({ className, ...props }: DialogOverlayProps) {\n const { open, setOpen } = useDialog();\n return (\n <div\n data-slot=\"dialog-overlay\"\n data-state={open ? \"open\" : \"closed\"}\n onClick={() => setOpen(false)}\n className={cn(DIALOG_OVERLAY, className)}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nconst XIcon = (props: React.SVGProps<SVGSVGElement>) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n);\n\nexport interface DialogContentProps extends React.ComponentProps<\"div\"> {\n showCloseButton?: boolean;\n /** Where focus lands on open. Default `first` — a dialog is usually a short form. */\n initialFocus?: InitialFocus;\n}\n\nexport function DialogContent({\n className,\n children,\n showCloseButton = true,\n initialFocus = \"first\",\n ...props\n}: DialogContentProps) {\n const { open, setOpen, triggerRef } = useDialog();\n const { isPresent, ref: presRef } = usePresence(open);\n const focusRef = useFocusScope(open, { initialFocus, returnFocusTo: triggerRef });\n const dismissRef = useDismissableLayer({\n enabled: open,\n onDismiss: () => setOpen(false),\n exclude: [triggerRef],\n modal: true,\n });\n React.useEffect(() => {\n if (!open) return;\n const prev = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = prev;\n };\n }, [open]);\n if (!isPresent) return null;\n return (\n <Portal>\n <DialogOverlay />\n <div\n data-slot=\"dialog-content\"\n role=\"dialog\"\n aria-modal=\"true\"\n data-state={open ? \"open\" : \"closed\"}\n ref={mergeRefs(focusRef, dismissRef, presRef as React.Ref<HTMLDivElement>)}\n className={cn(DIALOG_CONTENT, className)}\n {...(props as Record<string, unknown>)}\n >\n <DismissableLayerScope layer={dismissRef}>{children}</DismissableLayerScope>\n {showCloseButton && (\n <button\n type=\"button\"\n data-slot=\"dialog-close\"\n onClick={() => setOpen(false)}\n className={DIALOG_CLOSE}\n >\n <XIcon />\n <span className=\"sr-only\">Close</span>\n </button>\n )}\n </div>\n </Portal>\n );\n}\n\nexport interface DialogCloseProps extends React.ComponentProps<\"button\"> {\n asChild?: boolean;\n}\n\nexport function DialogClose({ asChild = false, onClick, ...props }: DialogCloseProps) {\n const { setOpen } = useDialog();\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n data-slot=\"dialog-close\"\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n setOpen(false);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport function DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(DIALOG_HEADER, className)}\n {...props}\n />\n );\n}\n\nexport function DialogFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(DIALOG_FOOTER, className)}\n {...props}\n />\n );\n}\n\nexport function DialogTitle({ className, ...props }: React.ComponentProps<\"h2\">) {\n return (\n <h2\n data-slot=\"dialog-title\"\n className={cn(DIALOG_TITLE, className)}\n {...props}\n />\n );\n}\n\nexport function DialogDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n return (\n <p\n data-slot=\"dialog-description\"\n className={cn(DIALOG_DESCRIPTION, className)}\n {...props}\n />\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -3,6 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "../lib/cn.js";
|
|
5
5
|
import { useControllableState } from "../primitives/use-controllable-state.js";
|
|
6
|
+
import { DismissableLayerScope, useDismissableLayer } from "../primitives/dismissable-layer.js";
|
|
6
7
|
import { useFocusScope } from "../primitives/focus-scope.js";
|
|
7
8
|
import { mergeRefs } from "../primitives/merge-refs.js";
|
|
8
9
|
import { Portal } from "../primitives/portal.js";
|
|
@@ -242,6 +243,12 @@ function DrawerContent({ className, children, ...props }) {
|
|
|
242
243
|
openedAtRef,
|
|
243
244
|
onClose: () => setOpen(false)
|
|
244
245
|
});
|
|
246
|
+
const dismissRef = useDismissableLayer({
|
|
247
|
+
enabled: open,
|
|
248
|
+
onDismiss: () => setOpen(false),
|
|
249
|
+
pointerOutside: false,
|
|
250
|
+
modal: true
|
|
251
|
+
});
|
|
245
252
|
if (!present) return null;
|
|
246
253
|
const state = closing ? "closed" : "open";
|
|
247
254
|
return /* @__PURE__ */ jsxs(Portal, { children: [
|
|
@@ -263,10 +270,7 @@ function DrawerContent({ className, children, ...props }) {
|
|
|
263
270
|
"data-slot": "drawer-content",
|
|
264
271
|
"data-state": state,
|
|
265
272
|
"data-vaul-drawer-direction": direction,
|
|
266
|
-
ref: mergeRefs(scopeRef, panelRef),
|
|
267
|
-
onKeyDown: (e) => {
|
|
268
|
-
if (e.key === "Escape") setOpen(false);
|
|
269
|
-
},
|
|
273
|
+
ref: mergeRefs(scopeRef, panelRef, dismissRef),
|
|
270
274
|
onTransitionEnd: (e) => {
|
|
271
275
|
if (closing && e.propertyName === "transform" && e.target === e.currentTarget) {
|
|
272
276
|
setPresent(false);
|
|
@@ -291,7 +295,7 @@ function DrawerContent({ className, children, ...props }) {
|
|
|
291
295
|
{
|
|
292
296
|
"data-slot": "drawer-body",
|
|
293
297
|
className: DRAWER_BODY,
|
|
294
|
-
children
|
|
298
|
+
children: /* @__PURE__ */ jsx(DismissableLayerScope, { layer: dismissRef, children })
|
|
295
299
|
}
|
|
296
300
|
)
|
|
297
301
|
]
|