@zvk/ui 0.1.8 → 0.1.9
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 +7 -0
- package/README.md +1 -1
- package/dist/components/alert-dialog/alert-dialog.d.ts +1 -1
- package/dist/components/alert-dialog/alert-dialog.js +18 -10
- package/dist/components/combobox/combobox.js +3 -1
- package/dist/components/command/command-filter.js +1 -1
- package/dist/components/command/command.d.ts +5 -0
- package/dist/components/command/command.js +9 -2
- package/dist/components/command/index.d.ts +1 -1
- package/dist/components/context-menu/context-menu.js +1 -1
- package/dist/components/date-range-picker/date-range-picker.d.ts +27 -0
- package/dist/components/date-range-picker/date-range-picker.js +193 -0
- package/dist/components/date-range-picker/index.d.ts +2 -0
- package/dist/components/date-range-picker/index.js +2 -0
- package/dist/components/dialog/dialog.d.ts +1 -1
- package/dist/components/dialog/dialog.js +19 -14
- package/dist/components/dropdown-menu/dropdown-menu.d.ts +2 -2
- package/dist/components/dropdown-menu/dropdown-menu.js +24 -19
- package/dist/components/file-dropzone/file-dropzone.d.ts +25 -0
- package/dist/components/file-dropzone/file-dropzone.js +171 -0
- package/dist/components/file-dropzone/index.d.ts +2 -0
- package/dist/components/file-dropzone/index.js +2 -0
- package/dist/components/form/form.d.ts +16 -1
- package/dist/components/form/form.js +13 -2
- package/dist/components/form/index.d.ts +1 -1
- package/dist/components/hover-card/hover-card.d.ts +1 -1
- package/dist/components/hover-card/hover-card.js +12 -3
- package/dist/components/index.d.ts +6 -0
- package/dist/components/index.js +3 -0
- package/dist/components/kbd/index.d.ts +2 -0
- package/dist/components/kbd/index.js +1 -0
- package/dist/components/kbd/kbd.d.ts +15 -0
- package/dist/components/kbd/kbd.js +10 -0
- package/dist/components/menubar/menubar.js +1 -1
- package/dist/components/popover/popover.d.ts +1 -1
- package/dist/components/popover/popover.js +14 -24
- package/dist/components/select/select.d.ts +1 -1
- package/dist/components/select/select.js +88 -8
- package/dist/components/sheet/sheet.d.ts +1 -1
- package/dist/components/sheet/sheet.js +17 -12
- package/dist/components/table/index.d.ts +1 -1
- package/dist/components/table/table.d.ts +37 -0
- package/dist/components/table/table.js +30 -2
- package/dist/components/toast/index.d.ts +1 -1
- package/dist/components/toast/toast.d.ts +18 -0
- package/dist/components/toast/toast.js +60 -0
- package/dist/components/tooltip/tooltip.d.ts +1 -1
- package/dist/components/tooltip/tooltip.js +12 -3
- package/dist/internal/floating/transform-origin.d.ts +2 -0
- package/dist/internal/floating/transform-origin.js +22 -0
- package/dist/internal/presence/index.d.ts +2 -0
- package/dist/internal/presence/index.js +2 -0
- package/dist/internal/presence/use-presence.d.ts +18 -0
- package/dist/internal/presence/use-presence.js +119 -0
- package/dist/styles.css +1710 -224
- package/dist/tokens/token-types.d.ts +4 -0
- package/dist/tokens/tokens.d.ts +41 -5
- package/dist/tokens/tokens.js +45 -9
- package/package.json +135 -61
|
@@ -8,6 +8,7 @@ import { Portal } from "../../internal/portal/index.js";
|
|
|
8
8
|
import { DismissableLayer } from "../../internal/dismissable-layer/dismissable-layer.js";
|
|
9
9
|
import { useFloatingPosition } from "../../internal/floating/index.js";
|
|
10
10
|
import { placementFromSideAlign, placementParts } from "../../internal/floating/placement-aliases.js";
|
|
11
|
+
import { usePresence } from "../../internal/presence/index.js";
|
|
11
12
|
import { Slot } from "../../internal/slot/index.js";
|
|
12
13
|
const defaultContentPositioning = {
|
|
13
14
|
sideOffset: 8,
|
|
@@ -15,6 +16,7 @@ const defaultContentPositioning = {
|
|
|
15
16
|
collisionPadding: 0,
|
|
16
17
|
matchTriggerWidth: false
|
|
17
18
|
};
|
|
19
|
+
const dropdownMenuExitDurationMs = 120;
|
|
18
20
|
const DropdownMenuContext = React.createContext(null);
|
|
19
21
|
function useDropdownMenuContext(calledBy) {
|
|
20
22
|
const context = React.useContext(DropdownMenuContext);
|
|
@@ -23,6 +25,9 @@ function useDropdownMenuContext(calledBy) {
|
|
|
23
25
|
}
|
|
24
26
|
return context;
|
|
25
27
|
}
|
|
28
|
+
function focusWithoutScroll(element) {
|
|
29
|
+
element?.focus({ preventScroll: true });
|
|
30
|
+
}
|
|
26
31
|
function composeRefs(...refs) {
|
|
27
32
|
return (node) => {
|
|
28
33
|
for (const ref of refs) {
|
|
@@ -111,7 +116,7 @@ function DropdownMenuRoot({ children, className, container, defaultOpen = false,
|
|
|
111
116
|
}, []);
|
|
112
117
|
React.useEffect(() => {
|
|
113
118
|
if (wasOpenRef.current && !open) {
|
|
114
|
-
triggerRef.current
|
|
119
|
+
focusWithoutScroll(triggerRef.current);
|
|
115
120
|
}
|
|
116
121
|
wasOpenRef.current = open;
|
|
117
122
|
}, [open]);
|
|
@@ -119,7 +124,7 @@ function DropdownMenuRoot({ children, className, container, defaultOpen = false,
|
|
|
119
124
|
if (open && triggerRef.current !== null) {
|
|
120
125
|
const focusFirstItem = () => {
|
|
121
126
|
const first = getEnabledItems()[0];
|
|
122
|
-
first?.ref
|
|
127
|
+
focusWithoutScroll(first?.ref);
|
|
123
128
|
};
|
|
124
129
|
queueMicrotask(focusFirstItem);
|
|
125
130
|
}
|
|
@@ -141,35 +146,31 @@ function DropdownMenuRoot({ children, className, container, defaultOpen = false,
|
|
|
141
146
|
...(container === undefined ? {} : { container })
|
|
142
147
|
}, children: _jsx("div", { ...props, className: cn("zvk-ui-dropdown-menu", className), "data-state": open ? "open" : "closed", children: children }) }));
|
|
143
148
|
}
|
|
144
|
-
function DropdownMenuTrigger({ asChild = false, className, disabled, onClick, ref, type = "button", ...props }) {
|
|
145
|
-
const { contentId, open, setOpen, triggerRef, triggerId, referenceRef
|
|
149
|
+
function DropdownMenuTrigger({ asChild = false, children, className, disabled, onClick, ref, type = "button", ...props }) {
|
|
150
|
+
const { contentId, open, setOpen, triggerRef, triggerId, referenceRef } = useDropdownMenuContext("DropdownMenu.Trigger");
|
|
146
151
|
const handleClick = () => {
|
|
147
152
|
if (disabled) {
|
|
148
153
|
return;
|
|
149
154
|
}
|
|
150
155
|
const nextOpen = !open;
|
|
151
156
|
setOpen(nextOpen);
|
|
152
|
-
if (!nextOpen) {
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
queueMicrotask(() => {
|
|
156
|
-
const first = getEnabledItems()[0];
|
|
157
|
-
first?.ref?.focus();
|
|
158
|
-
});
|
|
159
157
|
};
|
|
160
158
|
if (asChild) {
|
|
161
|
-
return (_jsx(Slot, { ...props, ref: composeRefs(ref, triggerRef, referenceRef), id: triggerId, "aria-controls": contentId, "aria-disabled": disabled ? true : undefined, "aria-expanded": open ? "true" : "false", "aria-haspopup": "menu", className: cn("zvk-ui-dropdown-menu__trigger", className), "data-disabled": disabled ? "true" : undefined, "data-state": open ? "open" : "closed", onClick: composeEventHandlers(onClick, handleClick), children:
|
|
159
|
+
return (_jsx(Slot, { ...props, ref: composeRefs(ref, triggerRef, referenceRef), id: triggerId, "aria-controls": contentId, "aria-disabled": disabled ? true : undefined, "aria-expanded": open ? "true" : "false", "aria-haspopup": "menu", className: cn("zvk-ui-dropdown-menu__trigger", className), "data-disabled": disabled ? "true" : undefined, "data-state": open ? "open" : "closed", onClick: composeEventHandlers(onClick, handleClick), children: children }));
|
|
162
160
|
}
|
|
163
|
-
return (
|
|
161
|
+
return (_jsxs("button", { ...props, ref: composeRefs(ref, triggerRef, referenceRef), type: type, disabled: disabled, id: triggerId, role: "button", "aria-haspopup": "menu", "aria-expanded": open ? "true" : "false", "aria-controls": contentId, className: cn("zvk-ui-dropdown-menu__trigger", className), "data-state": open ? "open" : "closed", onClick: composeEventHandlers(onClick, handleClick), children: [_jsx("span", { className: "zvk-ui-dropdown-menu__trigger-label", children: children }), _jsx("span", { className: "zvk-ui-dropdown-menu__trigger-indicator", "aria-hidden": "true" })] }));
|
|
164
162
|
}
|
|
165
|
-
function DropdownMenuContent({ align, alignOffset = defaultContentPositioning.alignOffset, children, className, forceMount = false, side, sideOffset = defaultContentPositioning.sideOffset, collisionPadding = defaultContentPositioning.collisionPadding, matchTriggerWidth = defaultContentPositioning.matchTriggerWidth, ref, onKeyDown, ...props }) {
|
|
163
|
+
function DropdownMenuContent({ align, alignOffset = defaultContentPositioning.alignOffset, children, className, forceMount = false, side, sideOffset = defaultContentPositioning.sideOffset, collisionPadding = defaultContentPositioning.collisionPadding, matchTriggerWidth = defaultContentPositioning.matchTriggerWidth, ref, onAnimationEnd, onKeyDown, ...props }) {
|
|
166
164
|
const { container, contentId, open, setOpen, updateContentPositioning, triggerId, floatingRef, floatingPlacement, floatingStyle, getEnabledItems } = useDropdownMenuContext("DropdownMenu.Content");
|
|
165
|
+
const presence = usePresence({ open, forceMount, exitDurationMs: dropdownMenuExitDurationMs });
|
|
166
|
+
const wasOpenRef = React.useRef(open);
|
|
167
|
+
const isClosing = wasOpenRef.current && !open;
|
|
167
168
|
const focusItem = React.useCallback((index, items) => {
|
|
168
169
|
if (items.length === 0) {
|
|
169
170
|
return;
|
|
170
171
|
}
|
|
171
172
|
const clampedIndex = ((index % items.length) + items.length) % items.length;
|
|
172
|
-
items[clampedIndex]?.ref
|
|
173
|
+
focusWithoutScroll(items[clampedIndex]?.ref);
|
|
173
174
|
}, []);
|
|
174
175
|
const findCurrentIndex = React.useCallback((items) => {
|
|
175
176
|
const activeElement = document.activeElement;
|
|
@@ -196,10 +197,10 @@ function DropdownMenuContent({ align, alignOffset = defaultContentPositioning.al
|
|
|
196
197
|
return;
|
|
197
198
|
}
|
|
198
199
|
if (isEnd) {
|
|
199
|
-
enabledItems.at(-1)?.ref
|
|
200
|
+
focusWithoutScroll(enabledItems.at(-1)?.ref);
|
|
200
201
|
return;
|
|
201
202
|
}
|
|
202
|
-
enabledItems[0]?.ref
|
|
203
|
+
focusWithoutScroll(enabledItems[0]?.ref);
|
|
203
204
|
}, [getEnabledItems]);
|
|
204
205
|
React.useEffect(() => {
|
|
205
206
|
updateContentPositioning({
|
|
@@ -213,9 +214,13 @@ function DropdownMenuContent({ align, alignOffset = defaultContentPositioning.al
|
|
|
213
214
|
updateContentPositioning(defaultContentPositioning);
|
|
214
215
|
};
|
|
215
216
|
}, [align, alignOffset, collisionPadding, matchTriggerWidth, side, sideOffset, updateContentPositioning]);
|
|
216
|
-
|
|
217
|
+
React.useEffect(() => {
|
|
218
|
+
wasOpenRef.current = open;
|
|
219
|
+
}, [open]);
|
|
220
|
+
if (!presence.present) {
|
|
217
221
|
return null;
|
|
218
222
|
}
|
|
223
|
+
const hidden = presence.inert && !isClosing && presence.motionState !== "exiting" ? true : undefined;
|
|
219
224
|
const content = (_jsx("div", { ...props, ref: (node) => {
|
|
220
225
|
floatingRef(node);
|
|
221
226
|
if (typeof ref === "function") {
|
|
@@ -224,7 +229,7 @@ function DropdownMenuContent({ align, alignOffset = defaultContentPositioning.al
|
|
|
224
229
|
else if (ref) {
|
|
225
230
|
ref.current = node;
|
|
226
231
|
}
|
|
227
|
-
}, id: contentId, role: "menu", "aria-labelledby": triggerId, className: cn("zvk-ui-dropdown-menu__content", className), style: floatingStyle, "data-align": placementParts(floatingPlacement).align, "data-side": placementParts(floatingPlacement).side, "data-state":
|
|
232
|
+
}, "aria-hidden": presence.inert ? true : undefined, id: contentId, inert: presence.inert ? true : undefined, role: "menu", "aria-labelledby": triggerId, className: cn("zvk-ui-dropdown-menu__content", className), style: floatingStyle, "data-align": placementParts(floatingPlacement).align, "data-side": placementParts(floatingPlacement).side, "data-state": presence.state, hidden: hidden, onAnimationEnd: composeEventHandlers(onAnimationEnd, presence.onExitComplete), onKeyDown: composeEventHandlers(onKeyDown, (event) => {
|
|
228
233
|
if (event.key === "ArrowDown") {
|
|
229
234
|
event.preventDefault();
|
|
230
235
|
moveFocus("next");
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface FileDropzoneProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
3
|
+
label?: React.ReactNode;
|
|
4
|
+
description?: React.ReactNode;
|
|
5
|
+
error?: React.ReactNode;
|
|
6
|
+
accept?: string;
|
|
7
|
+
multiple?: boolean;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
required?: boolean;
|
|
10
|
+
maxFiles?: number;
|
|
11
|
+
maxSizeBytes?: number;
|
|
12
|
+
value?: readonly File[];
|
|
13
|
+
defaultFiles?: readonly File[];
|
|
14
|
+
onFilesChange?: (files: readonly File[]) => void;
|
|
15
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
16
|
+
}
|
|
17
|
+
export interface FileDropzoneListProps extends React.HTMLAttributes<HTMLUListElement> {
|
|
18
|
+
ref?: React.Ref<HTMLUListElement>;
|
|
19
|
+
}
|
|
20
|
+
declare function FileDropzoneRoot({ accept, children, className, defaultFiles, description, disabled, error, id, label, maxFiles, maxSizeBytes, multiple, onFilesChange, ref, required, value, ...props }: FileDropzoneProps): React.JSX.Element;
|
|
21
|
+
declare function FileDropzoneList({ className, ref, ...props }: FileDropzoneListProps): React.JSX.Element | null;
|
|
22
|
+
export declare const FileDropzone: typeof FileDropzoneRoot & {
|
|
23
|
+
List: typeof FileDropzoneList;
|
|
24
|
+
};
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Field } from "../field/field.js";
|
|
5
|
+
import { cn } from "../../utils/cn.js";
|
|
6
|
+
import { useControllableState } from "../../hooks/use-controllable-state.js";
|
|
7
|
+
const FileDropzoneContext = React.createContext(null);
|
|
8
|
+
function useFileDropzoneContext(calledBy) {
|
|
9
|
+
const context = React.useContext(FileDropzoneContext);
|
|
10
|
+
if (context === null) {
|
|
11
|
+
throw new Error(`"${calledBy}" must be used within <FileDropzone />`);
|
|
12
|
+
}
|
|
13
|
+
return context;
|
|
14
|
+
}
|
|
15
|
+
function hasRenderableNode(value) {
|
|
16
|
+
return value !== undefined && value !== null && value !== false;
|
|
17
|
+
}
|
|
18
|
+
function joinIds(...ids) {
|
|
19
|
+
const value = ids.filter(Boolean).join(" ");
|
|
20
|
+
return value.length > 0 ? value : undefined;
|
|
21
|
+
}
|
|
22
|
+
function normalizeFiles(files) {
|
|
23
|
+
return Array.from(files ?? []);
|
|
24
|
+
}
|
|
25
|
+
function parseAccept(accept) {
|
|
26
|
+
return accept
|
|
27
|
+
?.split(",")
|
|
28
|
+
.map((value) => value.trim())
|
|
29
|
+
.filter(Boolean) ?? [];
|
|
30
|
+
}
|
|
31
|
+
function fileMatchesAccept(file, accept) {
|
|
32
|
+
const tokens = parseAccept(accept);
|
|
33
|
+
if (tokens.length === 0) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
const fileName = file.name.toLowerCase();
|
|
37
|
+
const mimeType = file.type.toLowerCase();
|
|
38
|
+
return tokens.some((token) => {
|
|
39
|
+
const normalized = token.toLowerCase();
|
|
40
|
+
if (normalized.startsWith(".")) {
|
|
41
|
+
return fileName.endsWith(normalized);
|
|
42
|
+
}
|
|
43
|
+
if (normalized.endsWith("/*")) {
|
|
44
|
+
return mimeType.startsWith(normalized.slice(0, -1));
|
|
45
|
+
}
|
|
46
|
+
return mimeType === normalized;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function formatFileSize(size) {
|
|
50
|
+
if (size < 1024) {
|
|
51
|
+
return `${size} B`;
|
|
52
|
+
}
|
|
53
|
+
const kib = size / 1024;
|
|
54
|
+
if (kib < 1024) {
|
|
55
|
+
return `${kib.toFixed(kib >= 10 ? 0 : 1)} KiB`;
|
|
56
|
+
}
|
|
57
|
+
const mib = kib / 1024;
|
|
58
|
+
return `${mib.toFixed(mib >= 10 ? 0 : 1)} MiB`;
|
|
59
|
+
}
|
|
60
|
+
function getValidationError(files, { accept, maxFiles, maxSizeBytes }) {
|
|
61
|
+
const tooManyFiles = maxFiles !== undefined && files.length > maxFiles;
|
|
62
|
+
if (tooManyFiles) {
|
|
63
|
+
return `You can add up to ${maxFiles} file${maxFiles === 1 ? "" : "s"}.`;
|
|
64
|
+
}
|
|
65
|
+
const tooLargeFile = maxSizeBytes !== undefined ? files.find((file) => file.size > maxSizeBytes) : undefined;
|
|
66
|
+
if (tooLargeFile) {
|
|
67
|
+
return `${tooLargeFile.name} is too large.`;
|
|
68
|
+
}
|
|
69
|
+
const rejectedFile = files.find((file) => !fileMatchesAccept(file, accept));
|
|
70
|
+
if (rejectedFile) {
|
|
71
|
+
return `${rejectedFile.name} does not match the accepted file types.`;
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
function getNextFiles(currentFiles, incomingFiles, multiple) {
|
|
76
|
+
if (!multiple) {
|
|
77
|
+
return incomingFiles.slice(0, 1);
|
|
78
|
+
}
|
|
79
|
+
return [...currentFiles, ...incomingFiles];
|
|
80
|
+
}
|
|
81
|
+
function FileDropzoneRoot({ accept, children, className, defaultFiles = [], description, disabled = false, error, id, label, maxFiles, maxSizeBytes, multiple = false, onFilesChange, ref, required, value, ...props }) {
|
|
82
|
+
const generatedId = React.useId();
|
|
83
|
+
const inputId = id ? `${id}-input` : generatedId;
|
|
84
|
+
const labelId = label !== undefined ? `${inputId}-label` : undefined;
|
|
85
|
+
const descriptionId = hasRenderableNode(description) ? `${inputId}-description` : undefined;
|
|
86
|
+
const errorId = `${inputId}-error`;
|
|
87
|
+
const [validationError, setValidationError] = React.useState(null);
|
|
88
|
+
const [files, setFiles] = useControllableState({
|
|
89
|
+
...(value !== undefined ? { value } : {}),
|
|
90
|
+
defaultValue: defaultFiles,
|
|
91
|
+
...(onFilesChange ? { onChange: onFilesChange } : {})
|
|
92
|
+
});
|
|
93
|
+
const effectiveMaxFiles = multiple ? maxFiles : 1;
|
|
94
|
+
const invalidState = Boolean(error) || validationError !== null;
|
|
95
|
+
const inputRequired = Boolean(required && files.length === 0);
|
|
96
|
+
const describedBy = joinIds(descriptionId, invalidState ? errorId : undefined);
|
|
97
|
+
const [isDragging, setIsDragging] = React.useState(false);
|
|
98
|
+
const dragCounterRef = React.useRef(0);
|
|
99
|
+
const removeFile = React.useCallback((file) => {
|
|
100
|
+
setValidationError(null);
|
|
101
|
+
setFiles((currentFiles) => currentFiles.filter((currentFile) => currentFile !== file));
|
|
102
|
+
}, [setFiles]);
|
|
103
|
+
const acceptFiles = React.useCallback((incomingFiles) => {
|
|
104
|
+
if (disabled) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const nextFiles = getNextFiles(files, incomingFiles, multiple);
|
|
108
|
+
const validationErrorMessage = getValidationError(nextFiles, {
|
|
109
|
+
...(accept !== undefined ? { accept } : {}),
|
|
110
|
+
...(effectiveMaxFiles !== undefined ? { maxFiles: effectiveMaxFiles } : {}),
|
|
111
|
+
...(maxSizeBytes !== undefined ? { maxSizeBytes } : {})
|
|
112
|
+
});
|
|
113
|
+
if (validationErrorMessage !== null) {
|
|
114
|
+
setValidationError(validationErrorMessage);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
setValidationError(null);
|
|
118
|
+
setFiles(nextFiles);
|
|
119
|
+
}, [accept, disabled, effectiveMaxFiles, files, maxSizeBytes, multiple, setFiles]);
|
|
120
|
+
const handleInputChange = React.useCallback((event) => {
|
|
121
|
+
acceptFiles(normalizeFiles(event.currentTarget.files));
|
|
122
|
+
event.currentTarget.value = "";
|
|
123
|
+
}, [acceptFiles]);
|
|
124
|
+
const handleDragEnter = React.useCallback((event) => {
|
|
125
|
+
if (disabled) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
event.preventDefault();
|
|
129
|
+
dragCounterRef.current += 1;
|
|
130
|
+
setIsDragging(true);
|
|
131
|
+
}, [disabled]);
|
|
132
|
+
const handleDragOver = React.useCallback((event) => {
|
|
133
|
+
event.preventDefault();
|
|
134
|
+
}, []);
|
|
135
|
+
const handleDragLeave = React.useCallback((event) => {
|
|
136
|
+
if (disabled) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
event.preventDefault();
|
|
140
|
+
dragCounterRef.current = Math.max(0, dragCounterRef.current - 1);
|
|
141
|
+
if (dragCounterRef.current === 0) {
|
|
142
|
+
setIsDragging(false);
|
|
143
|
+
}
|
|
144
|
+
}, [disabled]);
|
|
145
|
+
const handleDrop = React.useCallback((event) => {
|
|
146
|
+
if (disabled) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
event.preventDefault();
|
|
150
|
+
dragCounterRef.current = 0;
|
|
151
|
+
setIsDragging(false);
|
|
152
|
+
acceptFiles(normalizeFiles(event.dataTransfer.files));
|
|
153
|
+
}, [acceptFiles, disabled]);
|
|
154
|
+
const input = (_jsx("div", { className: "zvk-ui-file-dropzone__panel", "aria-hidden": "true", children: _jsx("span", { "aria-hidden": "true", className: "zvk-ui-file-dropzone__prompt", children: isDragging ? "Drop files to add them" : "Drop files here or browse" }) }));
|
|
155
|
+
const content = (_jsx(FileDropzoneContext.Provider, { value: {
|
|
156
|
+
disabled,
|
|
157
|
+
files,
|
|
158
|
+
removeFile
|
|
159
|
+
}, children: _jsxs(Field, { ...props, id: id, ...(label !== undefined || description !== undefined || error !== undefined ? { role: "group" } : {}), "aria-describedby": describedBy, "aria-labelledby": labelId, className: cn("zvk-ui-file-dropzone", className), "data-dragging": isDragging ? "true" : undefined, "data-disabled": disabled ? "true" : undefined, "data-invalid": invalidState ? "true" : undefined, onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, disabled: disabled, invalid: invalidState, ...(ref !== undefined ? { ref } : {}), required: Boolean(required), children: [hasRenderableNode(label) ? _jsx(Field.Label, { id: labelId, htmlFor: inputId, children: label }) : null, _jsxs("div", { className: "zvk-ui-file-dropzone__surface", children: [input, _jsx("input", { accept: accept, "aria-describedby": describedBy, "aria-invalid": invalidState ? true : undefined, "aria-labelledby": labelId, "aria-required": required ? true : undefined, className: "zvk-ui-file-dropzone__input", "data-disabled": disabled ? "true" : undefined, "data-invalid": invalidState ? "true" : undefined, disabled: disabled, id: inputId, multiple: multiple, onChange: handleInputChange, required: inputRequired, type: "file" })] }), hasRenderableNode(description) ? _jsx(Field.Description, { id: descriptionId, children: description }) : null, invalidState ? (_jsxs(Field.Error, { id: errorId, children: [validationError !== null ? _jsx("span", { children: validationError }) : null, validationError !== null && hasRenderableNode(error) ? _jsx("span", { children: " " }) : null, hasRenderableNode(error) ? error : null] })) : null, children] }) }));
|
|
160
|
+
return content;
|
|
161
|
+
}
|
|
162
|
+
function FileDropzoneList({ className, ref, ...props }) {
|
|
163
|
+
const { disabled, files, removeFile } = useFileDropzoneContext("FileDropzone.List");
|
|
164
|
+
if (files.length === 0) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
return (_jsx("ul", { ...props, ref: ref, "aria-label": props["aria-label"] ?? "Selected files", className: cn("zvk-ui-file-dropzone__list", className), children: files.map((file) => (_jsxs("li", { className: "zvk-ui-file-dropzone__item", children: [_jsx("span", { className: "zvk-ui-file-dropzone__item-name", children: file.name }), _jsx("span", { className: "zvk-ui-file-dropzone__item-meta", children: formatFileSize(file.size) }), _jsx("button", { "aria-label": `Remove ${file.name}`, className: "zvk-ui-file-dropzone__remove", disabled: disabled, type: "button", onClick: () => removeFile(file), children: "Remove" })] }, `${file.name}-${file.size}-${file.lastModified}`))) }));
|
|
168
|
+
}
|
|
169
|
+
export const FileDropzone = Object.assign(FileDropzoneRoot, {
|
|
170
|
+
List: FileDropzoneList
|
|
171
|
+
});
|
|
@@ -21,10 +21,25 @@ export interface FormControlProps {
|
|
|
21
21
|
export interface FormTextProps extends React.HTMLAttributes<HTMLParagraphElement> {
|
|
22
22
|
ref?: React.Ref<HTMLParagraphElement>;
|
|
23
23
|
}
|
|
24
|
-
export
|
|
24
|
+
export interface FormValidationSummaryError {
|
|
25
|
+
fieldId?: string;
|
|
26
|
+
href?: string;
|
|
27
|
+
message: string;
|
|
28
|
+
}
|
|
29
|
+
export interface FormValidationSummaryProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
30
|
+
errors: readonly FormValidationSummaryError[];
|
|
31
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
32
|
+
title: string;
|
|
33
|
+
}
|
|
34
|
+
declare function FormRoot({ className, ref, ...props }: FormProps): React.JSX.Element;
|
|
25
35
|
export declare function FormField({ children, className, disabled, id, invalid, required, ...props }: FormFieldProps): React.JSX.Element;
|
|
26
36
|
export declare function FormItem({ className, ref, ...props }: FormItemProps): React.JSX.Element;
|
|
27
37
|
export declare function FormLabel({ className, htmlFor, ref, ...props }: FormLabelProps): React.JSX.Element;
|
|
28
38
|
export declare function FormControl({ children }: FormControlProps): React.ReactElement<Record<string, unknown>, string | React.JSXElementConstructor<any>>;
|
|
29
39
|
export declare function FormDescription({ className, ...props }: FormTextProps): React.JSX.Element;
|
|
30
40
|
export declare function FormMessage({ className, role, ...props }: FormTextProps): React.JSX.Element;
|
|
41
|
+
declare function FormValidationSummary({ className, errors, ref, title, ...props }: FormValidationSummaryProps): React.JSX.Element | null;
|
|
42
|
+
export declare const Form: typeof FormRoot & {
|
|
43
|
+
ValidationSummary: typeof FormValidationSummary;
|
|
44
|
+
};
|
|
45
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "../../utils/cn.js";
|
|
5
5
|
const FormFieldContext = React.createContext(null);
|
|
@@ -14,7 +14,7 @@ function joinIds(ids) {
|
|
|
14
14
|
const value = ids.filter(Boolean).join(" ");
|
|
15
15
|
return value.length > 0 ? value : undefined;
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
function FormRoot({ className, ref, ...props }) {
|
|
18
18
|
return _jsx("form", { ...props, ref: ref, className: cn("zvk-ui-form", className) });
|
|
19
19
|
}
|
|
20
20
|
export function FormField({ children, className, disabled, id, invalid, required, ...props }) {
|
|
@@ -86,3 +86,14 @@ export function FormDescription({ className, ...props }) {
|
|
|
86
86
|
export function FormMessage({ className, role = "alert", ...props }) {
|
|
87
87
|
return _jsx(FormText, { ...props, className: cn("zvk-ui-form-message", className), role: role, textRole: "FormMessage" });
|
|
88
88
|
}
|
|
89
|
+
function FormValidationSummary({ className, errors, ref, title, ...props }) {
|
|
90
|
+
const titleId = React.useId();
|
|
91
|
+
if (errors.length === 0) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
return (_jsxs("div", { ...props, "aria-atomic": "true", "aria-live": "polite", "aria-labelledby": titleId, className: cn("zvk-ui-form-validation-summary", className), ref: ref, role: "status", children: [_jsx("p", { id: titleId, className: "zvk-ui-form-validation-summary__title", children: title }), _jsx("ul", { className: "zvk-ui-form-validation-summary__list", children: errors.map((error, index) => {
|
|
95
|
+
const href = error.href ?? (error.fieldId !== undefined ? `#${error.fieldId}` : undefined);
|
|
96
|
+
return (_jsx("li", { className: "zvk-ui-form-validation-summary__item", children: href !== undefined ? (_jsx("a", { className: "zvk-ui-form-validation-summary__link", href: href, children: error.message })) : (error.message) }, `${error.message}-${index}`));
|
|
97
|
+
}) })] }));
|
|
98
|
+
}
|
|
99
|
+
export const Form = Object.assign(FormRoot, { ValidationSummary: FormValidationSummary });
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "./form.js";
|
|
2
|
-
export type { FormControlProps, FormFieldProps, FormItemProps, FormLabelProps, FormProps, FormTextProps } from "./form.js";
|
|
2
|
+
export type { FormControlProps, FormFieldProps, FormItemProps, FormLabelProps, FormProps, FormTextProps, FormValidationSummaryError, FormValidationSummaryProps } from "./form.js";
|
|
@@ -29,7 +29,7 @@ export interface HoverCardContentProps extends React.HTMLAttributes<HTMLDivEleme
|
|
|
29
29
|
}
|
|
30
30
|
declare function HoverCardRoot({ children, closeDelay, defaultOpen, disabled, onOpenChange, open: openProp, openDelay, placement }: HoverCardProps): React.JSX.Element;
|
|
31
31
|
declare function HoverCardTrigger({ children, asChild: _asChild }: HoverCardTriggerProps): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
|
|
32
|
-
declare function HoverCardContent({ align, alignOffset, children, className, collisionPadding, container, forceMount, id, onBlur, onFocus, onMouseEnter, onMouseLeave, onPointerEnter, onPointerLeave, placement, ref, side, sideOffset, style, ...props }: HoverCardContentProps): React.JSX.Element | null;
|
|
32
|
+
declare function HoverCardContent({ align, alignOffset, children, className, collisionPadding, container, forceMount, id, onAnimationEnd, onBlur, onFocus, onMouseEnter, onMouseLeave, onPointerEnter, onPointerLeave, placement, ref, side, sideOffset, style, ...props }: HoverCardContentProps): React.JSX.Element | null;
|
|
33
33
|
export declare const HoverCard: typeof HoverCardRoot & {
|
|
34
34
|
Trigger: typeof HoverCardTrigger;
|
|
35
35
|
Content: typeof HoverCardContent;
|
|
@@ -5,6 +5,7 @@ import { DismissableLayer } from "../../internal/dismissable-layer/index.js";
|
|
|
5
5
|
import { useFloatingPosition } from "../../internal/floating/index.js";
|
|
6
6
|
import { placementFromSideAlign } from "../../internal/floating/placement-aliases.js";
|
|
7
7
|
import { Portal } from "../../internal/portal/index.js";
|
|
8
|
+
import { usePresence } from "../../internal/presence/index.js";
|
|
8
9
|
import { useControllableState } from "../../hooks/use-controllable-state.js";
|
|
9
10
|
import { cn } from "../../utils/cn.js";
|
|
10
11
|
import { composeEventHandlers } from "../../utils/compose-event-handlers.js";
|
|
@@ -13,6 +14,7 @@ const defaultContentPositioning = {
|
|
|
13
14
|
alignOffset: 0,
|
|
14
15
|
collisionPadding: 0
|
|
15
16
|
};
|
|
17
|
+
const hoverCardExitDurationMs = 120;
|
|
16
18
|
const HoverCardContext = React.createContext(null);
|
|
17
19
|
function useHoverCardContext(calledBy) {
|
|
18
20
|
const context = React.useContext(HoverCardContext);
|
|
@@ -236,8 +238,11 @@ function HoverCardTrigger({ children, asChild: _asChild = false }) {
|
|
|
236
238
|
onPointerLeave: composeEventHandlers(childProps.onPointerLeave, context.closeWithDelay)
|
|
237
239
|
});
|
|
238
240
|
}
|
|
239
|
-
function HoverCardContent({ align, alignOffset = defaultContentPositioning.alignOffset, children, className, collisionPadding = defaultContentPositioning.collisionPadding, container, forceMount = false, id, onBlur, onFocus, onMouseEnter, onMouseLeave, onPointerEnter, onPointerLeave, placement, ref, side, sideOffset = defaultContentPositioning.sideOffset, style, ...props }) {
|
|
241
|
+
function HoverCardContent({ align, alignOffset = defaultContentPositioning.alignOffset, children, className, collisionPadding = defaultContentPositioning.collisionPadding, container, forceMount = false, id, onAnimationEnd, onBlur, onFocus, onMouseEnter, onMouseLeave, onPointerEnter, onPointerLeave, placement, ref, side, sideOffset = defaultContentPositioning.sideOffset, style, ...props }) {
|
|
240
242
|
const context = useHoverCardContext("HoverCard.Content");
|
|
243
|
+
const presence = usePresence({ open: context.open, forceMount, exitDurationMs: hoverCardExitDurationMs });
|
|
244
|
+
const wasOpenRef = React.useRef(context.open);
|
|
245
|
+
const isClosing = wasOpenRef.current && !context.open;
|
|
241
246
|
const contentId = id ?? context.contentId;
|
|
242
247
|
const placementParts = getPlacementParts(context.resolvedPlacement);
|
|
243
248
|
const { setContentPositioning } = context;
|
|
@@ -251,10 +256,14 @@ function HoverCardContent({ align, alignOffset = defaultContentPositioning.align
|
|
|
251
256
|
});
|
|
252
257
|
return () => setContentPositioning(defaultContentPositioning);
|
|
253
258
|
}, [align, alignOffset, collisionPadding, placement, setContentPositioning, side, sideOffset]);
|
|
254
|
-
|
|
259
|
+
React.useEffect(() => {
|
|
260
|
+
wasOpenRef.current = context.open;
|
|
261
|
+
}, [context.open]);
|
|
262
|
+
if (!presence.present) {
|
|
255
263
|
return null;
|
|
256
264
|
}
|
|
257
|
-
const
|
|
265
|
+
const hidden = presence.inert && !isClosing && presence.motionState !== "exiting" ? true : undefined;
|
|
266
|
+
const content = (_jsx("div", { ...props, ref: composeRefs(ref, context.floatingRef), "aria-hidden": presence.inert ? true : undefined, id: contentId, inert: presence.inert ? true : undefined, role: "dialog", className: cn("zvk-ui-hover-card__content", className), "data-align": placementParts.align, "data-side": placementParts.side, "data-state": presence.state, hidden: hidden, onAnimationEnd: composeEventHandlers(onAnimationEnd, presence.onExitComplete), onBlur: composeEventHandlers(onBlur, (event) => {
|
|
258
267
|
context.closeAfterFocusLeaves(event.relatedTarget);
|
|
259
268
|
}), onFocus: composeEventHandlers(onFocus, context.openImmediately), onMouseEnter: composeEventHandlers(onMouseEnter, context.cancelClose), onMouseLeave: composeEventHandlers(onMouseLeave, context.closeWithDelay), onPointerEnter: composeEventHandlers(onPointerEnter, context.cancelClose), onPointerLeave: composeEventHandlers(onPointerLeave, context.closeWithDelay), style: { ...context.floatingStyle, ...style }, children: children }));
|
|
260
269
|
if (!context.open) {
|
|
@@ -26,6 +26,8 @@ export { CopyButton, CopyableText } from "./copy-button/index.js";
|
|
|
26
26
|
export type { CopyButtonProps, CopyButtonSize, CopyStatus, CopyableTextProps } from "./copy-button/index.js";
|
|
27
27
|
export { DatePicker } from "./date-picker/index.js";
|
|
28
28
|
export type { DatePickerProps } from "./date-picker/index.js";
|
|
29
|
+
export { DateRangePicker } from "./date-range-picker/index.js";
|
|
30
|
+
export type { DateRange, DateRangePickerProps } from "./date-range-picker/index.js";
|
|
29
31
|
export { Dialog } from "./dialog/index.js";
|
|
30
32
|
export type { DialogCloseProps, DialogContentProps, DialogDescriptionProps, DialogFooterProps, DialogHeaderProps, DialogOverlayProps, DialogProps, DialogTitleProps, DialogTriggerProps } from "./dialog/index.js";
|
|
31
33
|
export { DropdownMenu } from "./dropdown-menu/index.js";
|
|
@@ -54,12 +56,16 @@ export { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, For
|
|
|
54
56
|
export type { FormControlProps, FormFieldProps, FormItemProps, FormLabelProps, FormProps, FormTextProps } from "./form/index.js";
|
|
55
57
|
export { FileUploadInput } from "./file-upload-input/index.js";
|
|
56
58
|
export type { FileUploadInputProps, FileUploadInputSize } from "./file-upload-input/index.js";
|
|
59
|
+
export { FileDropzone } from "./file-dropzone/index.js";
|
|
60
|
+
export type { FileDropzoneListProps, FileDropzoneProps } from "./file-dropzone/index.js";
|
|
57
61
|
export { HoverCard } from "./hover-card/index.js";
|
|
58
62
|
export type { HoverCardContentProps, HoverCardProps, HoverCardTriggerProps } from "./hover-card/index.js";
|
|
59
63
|
export { IconButton } from "./icon-button/index.js";
|
|
60
64
|
export type { IconButtonProps } from "./icon-button/index.js";
|
|
61
65
|
export { Input } from "./input/index.js";
|
|
62
66
|
export type { InputProps, InputSize } from "./input/index.js";
|
|
67
|
+
export { Kbd } from "./kbd/index.js";
|
|
68
|
+
export type { KbdProps, KbdSize } from "./kbd/index.js";
|
|
63
69
|
export { Label } from "./label/index.js";
|
|
64
70
|
export type { LabelProps, LabelSize } from "./label/index.js";
|
|
65
71
|
export { Pagination } from "./pagination/index.js";
|
package/dist/components/index.js
CHANGED
|
@@ -12,6 +12,7 @@ export { CodeBlock } from "./code-block/index.js";
|
|
|
12
12
|
export { Collapsible } from "./collapsible/index.js";
|
|
13
13
|
export { CopyButton, CopyableText } from "./copy-button/index.js";
|
|
14
14
|
export { DatePicker } from "./date-picker/index.js";
|
|
15
|
+
export { DateRangePicker } from "./date-range-picker/index.js";
|
|
15
16
|
export { Dialog } from "./dialog/index.js";
|
|
16
17
|
export { DropdownMenu } from "./dropdown-menu/index.js";
|
|
17
18
|
export { AlertDialog } from "./alert-dialog/index.js";
|
|
@@ -26,9 +27,11 @@ export { ErrorBoundary, ErrorFallback } from "./error-boundary/index.js";
|
|
|
26
27
|
export { Field } from "./field/index.js";
|
|
27
28
|
export { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "./form/index.js";
|
|
28
29
|
export { FileUploadInput } from "./file-upload-input/index.js";
|
|
30
|
+
export { FileDropzone } from "./file-dropzone/index.js";
|
|
29
31
|
export { HoverCard } from "./hover-card/index.js";
|
|
30
32
|
export { IconButton } from "./icon-button/index.js";
|
|
31
33
|
export { Input } from "./input/index.js";
|
|
34
|
+
export { Kbd } from "./kbd/index.js";
|
|
32
35
|
export { Label } from "./label/index.js";
|
|
33
36
|
export { Pagination } from "./pagination/index.js";
|
|
34
37
|
export { Popover } from "./popover/index.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Kbd } from "./kbd.js";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export type KbdSize = "sm" | "md";
|
|
3
|
+
type KbdSharedProps = Omit<React.HTMLAttributes<HTMLElement>, "children"> & {
|
|
4
|
+
size?: KbdSize;
|
|
5
|
+
ref?: React.Ref<HTMLElement>;
|
|
6
|
+
};
|
|
7
|
+
export type KbdProps = (KbdSharedProps & {
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
keys?: never;
|
|
10
|
+
}) | (KbdSharedProps & {
|
|
11
|
+
children?: never;
|
|
12
|
+
keys: readonly string[];
|
|
13
|
+
});
|
|
14
|
+
export declare function Kbd({ children, className, keys, ref, size, ...props }: KbdProps): React.JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "../../utils/cn.js";
|
|
4
|
+
export function Kbd({ children, className, keys, ref, size = "md", ...props }) {
|
|
5
|
+
if (keys !== undefined) {
|
|
6
|
+
const label = props["aria-label"] ?? keys.join(" + ");
|
|
7
|
+
return (_jsx("span", { ...props, ref: ref, "aria-label": label, className: cn("zvk-ui-kbd-group", className), "data-size": size, children: keys.map((key, index) => (_jsxs(React.Fragment, { children: [index > 0 ? (_jsx("span", { "aria-hidden": "true", className: "zvk-ui-kbd__separator", children: " + " })) : null, _jsx("kbd", { className: "zvk-ui-kbd", "data-size": size, children: key })] }, `${index}-${key}`))) }));
|
|
8
|
+
}
|
|
9
|
+
return (_jsx("kbd", { ...props, ref: ref, className: cn("zvk-ui-kbd", className), "data-size": size, children: children }));
|
|
10
|
+
}
|
|
@@ -208,7 +208,7 @@ function MenubarContent({ align, alignOffset = 0, children, className, onKeyDown
|
|
|
208
208
|
return (_jsx(Portal, { children: _jsx(DismissableLayer, { open: menu.open, onDismiss: () => menubar.setOpenValue(undefined), children: _jsx("div", { ...props, ref: (node) => {
|
|
209
209
|
floatingRef(node);
|
|
210
210
|
setForwardedRef(ref, node);
|
|
211
|
-
}, id: menu.contentId, role: "menu", "aria-label": menu.label, "aria-labelledby": menu.triggerId, className: cn("zvk-ui-menubar__content", className), style: { ...style, ...floatingStyle }, "data-align": placementParts(resolvedPlacement).align, "data-side": placementParts(resolvedPlacement).side, onKeyDown: composeEventHandlers(onKeyDown, (event) => {
|
|
211
|
+
}, id: menu.contentId, role: "menu", "aria-label": menu.label, "aria-labelledby": menu.triggerId, className: cn("zvk-ui-menubar__content", className), style: { ...style, ...floatingStyle }, "data-align": placementParts(resolvedPlacement).align, "data-side": placementParts(resolvedPlacement).side, "data-state": "open", onKeyDown: composeEventHandlers(onKeyDown, (event) => {
|
|
212
212
|
const items = menu.getItems();
|
|
213
213
|
const index = activeIndex(items);
|
|
214
214
|
if (event.key === "ArrowDown") {
|
|
@@ -30,7 +30,7 @@ export interface PopoverContentProps extends React.HTMLAttributes<HTMLDivElement
|
|
|
30
30
|
}
|
|
31
31
|
declare function PopoverRoot({ children, className, defaultOpen, modal, onOpenChange, open, placement, ref, ...props }: PopoverProps): React.JSX.Element;
|
|
32
32
|
declare function PopoverTrigger({ asChild, className, disabled, onClick, ref, type, ...props }: PopoverTriggerProps): React.JSX.Element;
|
|
33
|
-
declare function PopoverContent({ align, alignOffset, className, container, forceMount, id, disableEscapeKeyDown, disableOutsidePointerDown, ref, placement, side, sideOffset, collisionPadding, matchTriggerWidth, style, ...props }: PopoverContentProps): React.JSX.Element | null;
|
|
33
|
+
declare function PopoverContent({ align, alignOffset, className, container, forceMount, id, disableEscapeKeyDown, disableOutsidePointerDown, onAnimationEnd, ref, placement, side, sideOffset, collisionPadding, matchTriggerWidth, style, ...props }: PopoverContentProps): React.JSX.Element | null;
|
|
34
34
|
export declare const Popover: typeof PopoverRoot & {
|
|
35
35
|
Trigger: typeof PopoverTrigger;
|
|
36
36
|
Content: typeof PopoverContent;
|