@zvk/ui 0.1.0 → 0.1.2
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 +7 -0
- package/dist/components/button/button.d.ts +1 -1
- package/dist/components/combobox/combobox.d.ts +13 -4
- package/dist/components/combobox/combobox.js +38 -7
- package/dist/components/combobox/index.d.ts +1 -1
- package/dist/components/context-menu/context-menu.d.ts +12 -0
- package/dist/components/context-menu/context-menu.js +42 -1
- package/dist/components/context-menu/index.d.ts +1 -1
- package/dist/components/dialog/dialog.d.ts +2 -1
- package/dist/components/dialog/dialog.js +7 -3
- package/dist/components/dropdown-menu/dropdown-menu.d.ts +34 -0
- package/dist/components/dropdown-menu/dropdown-menu.js +105 -1
- package/dist/components/dropdown-menu/index.d.ts +1 -1
- package/dist/components/menubar/index.d.ts +1 -1
- package/dist/components/menubar/menubar.d.ts +10 -0
- package/dist/components/menubar/menubar.js +8 -0
- package/dist/components/popover/popover.d.ts +5 -1
- package/dist/components/popover/popover.js +3 -3
- package/dist/components/select/index.d.ts +1 -1
- package/dist/components/select/select.d.ts +32 -6
- package/dist/components/select/select.js +34 -8
- package/dist/components/sheet/sheet.d.ts +7 -2
- package/dist/components/sheet/sheet.js +12 -5
- package/dist/components/tooltip/index.d.ts +1 -1
- package/dist/components/tooltip/tooltip.d.ts +37 -1
- package/dist/components/tooltip/tooltip.js +120 -29
- package/dist/internal/floating/use-floating-position.js +1 -0
- package/dist/styles.css +185 -11
- package/package.json +5 -1
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import type { PortalProps } from "../../internal/portal/index.js";
|
|
2
3
|
import type { FloatingPlacement } from "../../internal/floating/floating-types.js";
|
|
3
4
|
export type SelectSize = "sm" | "md" | "lg";
|
|
4
|
-
export
|
|
5
|
+
export type SelectValue = string | null;
|
|
6
|
+
export interface SelectProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "defaultValue" | "onChange"> {
|
|
5
7
|
name?: string;
|
|
6
|
-
value?:
|
|
7
|
-
defaultValue?:
|
|
8
|
-
onValueChange?: (value:
|
|
8
|
+
value?: SelectValue;
|
|
9
|
+
defaultValue?: SelectValue;
|
|
10
|
+
onValueChange?: (value: SelectValue) => void;
|
|
11
|
+
open?: boolean;
|
|
12
|
+
defaultOpen?: boolean;
|
|
13
|
+
onOpenChange?: (open: boolean) => void;
|
|
9
14
|
disabled?: boolean;
|
|
10
15
|
required?: boolean;
|
|
11
16
|
invalid?: boolean;
|
|
@@ -21,6 +26,7 @@ export interface SelectContentProps extends React.HTMLAttributes<HTMLDivElement>
|
|
|
21
26
|
collisionPadding?: number;
|
|
22
27
|
matchTriggerWidth?: boolean;
|
|
23
28
|
forceMount?: boolean;
|
|
29
|
+
container?: PortalProps["container"];
|
|
24
30
|
ref?: React.Ref<HTMLDivElement>;
|
|
25
31
|
}
|
|
26
32
|
export interface SelectItemProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
@@ -32,14 +38,34 @@ export interface SelectValueProps extends React.HTMLAttributes<HTMLSpanElement>
|
|
|
32
38
|
placeholder?: React.ReactNode;
|
|
33
39
|
ref?: React.Ref<HTMLSpanElement>;
|
|
34
40
|
}
|
|
35
|
-
|
|
41
|
+
export interface SelectGroupProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
42
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
43
|
+
}
|
|
44
|
+
export interface SelectLabelProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
45
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
46
|
+
}
|
|
47
|
+
export interface SelectSeparatorProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
48
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
49
|
+
}
|
|
50
|
+
export interface SelectClearProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
51
|
+
ref?: React.Ref<HTMLButtonElement>;
|
|
52
|
+
}
|
|
53
|
+
declare function SelectRoot({ children, className, defaultOpen, defaultValue, disabled, invalid, name, onOpenChange, onValueChange, open: openProp, placeholder, placement, required, size, value, ...props }: SelectProps): React.JSX.Element;
|
|
36
54
|
declare function SelectTrigger({ children, className, disabled, onClick, onKeyDown, ref, type, ...props }: SelectTriggerProps): React.JSX.Element;
|
|
37
55
|
declare function SelectValue({ className, placeholder, ref, ...props }: SelectValueProps): React.JSX.Element;
|
|
38
|
-
declare function SelectContent({ children, className, collisionPadding, forceMount, matchTriggerWidth, onKeyDown, ref, sideOffset, style, ...props }: SelectContentProps): React.JSX.Element | null;
|
|
56
|
+
declare function SelectContent({ children, className, collisionPadding, container, forceMount, matchTriggerWidth, onKeyDown, ref, sideOffset, style, ...props }: SelectContentProps): React.JSX.Element | null;
|
|
39
57
|
declare function SelectItem({ children, className, disabled, onClick, onKeyDown, ref, value, ...props }: SelectItemProps): React.JSX.Element;
|
|
58
|
+
declare function SelectGroup({ children, className, ref, ...props }: SelectGroupProps): React.JSX.Element;
|
|
59
|
+
declare function SelectLabel({ className, ref, ...props }: SelectLabelProps): React.JSX.Element;
|
|
60
|
+
declare function SelectSeparator({ className, ref, ...props }: SelectSeparatorProps): React.JSX.Element;
|
|
61
|
+
declare function SelectClear({ className, disabled, onClick, ref, type, ...props }: SelectClearProps): React.JSX.Element;
|
|
40
62
|
export declare const Select: typeof SelectRoot & {
|
|
41
63
|
Content: typeof SelectContent;
|
|
64
|
+
Clear: typeof SelectClear;
|
|
65
|
+
Group: typeof SelectGroup;
|
|
42
66
|
Item: typeof SelectItem;
|
|
67
|
+
Label: typeof SelectLabel;
|
|
68
|
+
Separator: typeof SelectSeparator;
|
|
43
69
|
Trigger: typeof SelectTrigger;
|
|
44
70
|
Value: typeof SelectValue;
|
|
45
71
|
};
|
|
@@ -35,7 +35,7 @@ function composeRefs(...refs) {
|
|
|
35
35
|
function getTextLabel(node) {
|
|
36
36
|
return node;
|
|
37
37
|
}
|
|
38
|
-
function SelectRoot({ children, className, defaultValue =
|
|
38
|
+
function SelectRoot({ children, className, defaultOpen = false, defaultValue = null, disabled, invalid, name, onOpenChange, onValueChange, open: openProp, placeholder, placement = "bottom-start", required, size = "md", value, ...props }) {
|
|
39
39
|
const instanceId = React.useId();
|
|
40
40
|
const triggerId = `${instanceId}-trigger`;
|
|
41
41
|
const contentId = `${instanceId}-content`;
|
|
@@ -43,7 +43,11 @@ function SelectRoot({ children, className, defaultValue = "", disabled, invalid,
|
|
|
43
43
|
const itemRegistry = React.useRef(new Map());
|
|
44
44
|
const itemOrder = React.useRef([]);
|
|
45
45
|
const wasOpenRef = React.useRef(false);
|
|
46
|
-
const [open, setOpen] =
|
|
46
|
+
const [open, setOpen] = useControllableState({
|
|
47
|
+
...(openProp !== undefined ? { value: openProp } : {}),
|
|
48
|
+
defaultValue: defaultOpen,
|
|
49
|
+
...(onOpenChange ? { onChange: onOpenChange } : {})
|
|
50
|
+
});
|
|
47
51
|
const [itemsVersion, setItemsVersion] = React.useState(0);
|
|
48
52
|
const [currentValue, setCurrentValue] = useControllableState({
|
|
49
53
|
...(value !== undefined ? { value } : {}),
|
|
@@ -80,7 +84,7 @@ function SelectRoot({ children, className, defaultValue = "", disabled, invalid,
|
|
|
80
84
|
const selectValue = React.useCallback((nextValue) => {
|
|
81
85
|
setCurrentValue(nextValue);
|
|
82
86
|
setOpen(false);
|
|
83
|
-
}, [setCurrentValue]);
|
|
87
|
+
}, [setCurrentValue, setOpen]);
|
|
84
88
|
const selectedLabel = React.useMemo(() => getOrderedItems().find((item) => item.value === currentValue)?.label, [currentValue, getOrderedItems, itemsVersion]);
|
|
85
89
|
React.useEffect(() => {
|
|
86
90
|
if (wasOpenRef.current && !open) {
|
|
@@ -108,8 +112,8 @@ function SelectRoot({ children, className, defaultValue = "", disabled, invalid,
|
|
|
108
112
|
referenceRef: floating.referenceRef,
|
|
109
113
|
floatingRef: floating.floatingRef,
|
|
110
114
|
floatingStyle: floating.floatingStyle
|
|
111
|
-
}), [contentId, currentValue, disabled, floating.floatingRef, floating.floatingStyle, floating.referenceRef, getEnabledItems, invalid, open, placeholder, registerItem, required, selectValue, selectedLabel, triggerId, unregisterItem]);
|
|
112
|
-
return (_jsx(SelectContext.Provider, { value: context, children: _jsxs("div", { ...props, className: cn("liano-select", className), "data-disabled": disabled ? "true" : undefined, "data-invalid": invalid ? "true" : undefined, "data-required": required ? "true" : undefined, "data-size": size, "data-state": open ? "open" : "closed", children: [name ? _jsx("input", { type: "hidden", name: name, value: currentValue }) : null, children] }) }));
|
|
115
|
+
}), [contentId, currentValue, disabled, floating.floatingRef, floating.floatingStyle, floating.referenceRef, getEnabledItems, invalid, open, placeholder, registerItem, required, selectValue, selectedLabel, setOpen, triggerId, unregisterItem]);
|
|
116
|
+
return (_jsx(SelectContext.Provider, { value: context, children: _jsxs("div", { ...props, className: cn("liano-select", className), "data-disabled": disabled ? "true" : undefined, "data-invalid": invalid ? "true" : undefined, "data-required": required ? "true" : undefined, "data-size": size, "data-state": open ? "open" : "closed", children: [name ? _jsx("input", { type: "hidden", name: name, value: currentValue ?? "" }) : null, children] }) }));
|
|
113
117
|
}
|
|
114
118
|
function focusItem(items, index) {
|
|
115
119
|
if (items.length === 0) {
|
|
@@ -156,7 +160,7 @@ function SelectValue({ className, placeholder, ref, ...props }) {
|
|
|
156
160
|
const displayPlaceholder = context.selectedLabel === undefined;
|
|
157
161
|
return (_jsx("span", { ...props, ref: ref, className: cn("liano-select__value", className), "data-placeholder": displayPlaceholder ? "true" : undefined, children: displayPlaceholder ? placeholder ?? context.placeholder : context.selectedLabel }));
|
|
158
162
|
}
|
|
159
|
-
function SelectContent({ children, className, collisionPadding = defaultContentPositioning.collisionPadding, forceMount = false, matchTriggerWidth = defaultContentPositioning.matchTriggerWidth, onKeyDown, ref, sideOffset = defaultContentPositioning.sideOffset, style, ...props }) {
|
|
163
|
+
function SelectContent({ children, className, collisionPadding = defaultContentPositioning.collisionPadding, container, forceMount = false, matchTriggerWidth = defaultContentPositioning.matchTriggerWidth, onKeyDown, ref, sideOffset = defaultContentPositioning.sideOffset, style, ...props }) {
|
|
160
164
|
const context = useSelectContext("Select.Content");
|
|
161
165
|
React.useEffect(() => {
|
|
162
166
|
context.updateContentPositioning({
|
|
@@ -200,9 +204,9 @@ function SelectContent({ children, className, collisionPadding = defaultContentP
|
|
|
200
204
|
}
|
|
201
205
|
}), role: "listbox", style: { ...context.floatingStyle, ...style }, tabIndex: -1, children: children }));
|
|
202
206
|
if (!context.open) {
|
|
203
|
-
return _jsx(Portal, { children: content });
|
|
207
|
+
return _jsx(Portal, { ...(container === undefined ? {} : { container }), children: content });
|
|
204
208
|
}
|
|
205
|
-
return (_jsx(Portal, { children: _jsx(DismissableLayer, { open: true, onDismiss: () => context.setOpen(false), children: content }) }));
|
|
209
|
+
return (_jsx(Portal, { ...(container === undefined ? {} : { container }), children: _jsx(DismissableLayer, { open: true, onDismiss: () => context.setOpen(false), children: content }) }));
|
|
206
210
|
}
|
|
207
211
|
function SelectItem({ children, className, disabled = false, onClick, onKeyDown, ref, value, ...props }) {
|
|
208
212
|
const context = useSelectContext("Select.Item");
|
|
@@ -231,9 +235,31 @@ function SelectItem({ children, className, disabled = false, onClick, onKeyDown,
|
|
|
231
235
|
}
|
|
232
236
|
}), role: "option", tabIndex: disabled ? undefined : -1, children: [_jsx("span", { className: "liano-select__item-indicator", "aria-hidden": "true", children: selected ? "✓" : "" }), _jsx("span", { className: "liano-select__item-label", children: children })] }));
|
|
233
237
|
}
|
|
238
|
+
function SelectGroup({ children, className, ref, ...props }) {
|
|
239
|
+
return (_jsx("div", { ...props, ref: ref, className: cn("liano-select__group", className), role: "group", children: children }));
|
|
240
|
+
}
|
|
241
|
+
function SelectLabel({ className, ref, ...props }) {
|
|
242
|
+
return (_jsx("div", { ...props, ref: ref, className: cn("liano-select__label", className) }));
|
|
243
|
+
}
|
|
244
|
+
function SelectSeparator({ className, ref, ...props }) {
|
|
245
|
+
return (_jsx("div", { ...props, ref: ref, "aria-hidden": "true", className: cn("liano-select__separator", className), role: "separator" }));
|
|
246
|
+
}
|
|
247
|
+
function SelectClear({ className, disabled, onClick, ref, type = "button", ...props }) {
|
|
248
|
+
const context = useSelectContext("Select.Clear");
|
|
249
|
+
const isDisabled = Boolean(context.disabled) || Boolean(disabled);
|
|
250
|
+
return (_jsx("button", { ...props, ref: ref, type: type, className: cn("liano-select__clear", className), "data-disabled": isDisabled ? "true" : undefined, disabled: isDisabled, onClick: composeEventHandlers(onClick, () => {
|
|
251
|
+
if (!isDisabled) {
|
|
252
|
+
context.selectValue(null);
|
|
253
|
+
}
|
|
254
|
+
}) }));
|
|
255
|
+
}
|
|
234
256
|
export const Select = Object.assign(SelectRoot, {
|
|
235
257
|
Content: SelectContent,
|
|
258
|
+
Clear: SelectClear,
|
|
259
|
+
Group: SelectGroup,
|
|
236
260
|
Item: SelectItem,
|
|
261
|
+
Label: SelectLabel,
|
|
262
|
+
Separator: SelectSeparator,
|
|
237
263
|
Trigger: SelectTrigger,
|
|
238
264
|
Value: SelectValue
|
|
239
265
|
});
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import type { PortalProps } from "../../internal/portal/index.js";
|
|
2
3
|
export type SheetSide = "left" | "right" | "top" | "bottom";
|
|
3
4
|
export interface SheetProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
container?: PortalProps["container"];
|
|
4
6
|
defaultOpen?: boolean;
|
|
7
|
+
disableEscapeKeyDown?: boolean;
|
|
8
|
+
disableOutsidePointerDown?: boolean;
|
|
5
9
|
onOpenChange?: (open: boolean) => void;
|
|
6
10
|
open?: boolean;
|
|
7
11
|
side?: SheetSide;
|
|
@@ -11,6 +15,7 @@ export interface SheetTriggerProps extends React.ButtonHTMLAttributes<HTMLButton
|
|
|
11
15
|
ref?: React.Ref<HTMLButtonElement>;
|
|
12
16
|
}
|
|
13
17
|
export interface SheetContentProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
18
|
+
forceMount?: boolean;
|
|
14
19
|
ref?: React.Ref<HTMLDivElement>;
|
|
15
20
|
}
|
|
16
21
|
export interface SheetHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
@@ -28,9 +33,9 @@ export interface SheetFooterProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
28
33
|
export interface SheetCloseProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
29
34
|
ref?: React.Ref<HTMLButtonElement>;
|
|
30
35
|
}
|
|
31
|
-
declare function SheetRoot({ children, className, defaultOpen, onOpenChange, open: openProp, ref, side, ...props }: SheetProps): React.JSX.Element;
|
|
36
|
+
declare function SheetRoot({ children, className, container, defaultOpen, disableEscapeKeyDown, disableOutsidePointerDown, onOpenChange, open: openProp, ref, side, ...props }: SheetProps): React.JSX.Element;
|
|
32
37
|
declare function SheetTrigger({ className, disabled, onClick, ref, type, ...props }: SheetTriggerProps): React.JSX.Element;
|
|
33
|
-
declare function SheetContent({ children, className, ref, ...props }: SheetContentProps): React.JSX.Element | null;
|
|
38
|
+
declare function SheetContent({ children, className, forceMount, ref, ...props }: SheetContentProps): React.JSX.Element | null;
|
|
34
39
|
declare function SheetHeader({ className, ref, ...props }: SheetHeaderProps): React.JSX.Element;
|
|
35
40
|
declare function SheetTitle({ className, ref, ...props }: SheetTitleProps): React.JSX.Element;
|
|
36
41
|
declare function SheetDescription({ className, ref, ...props }: SheetDescriptionProps): React.JSX.Element;
|
|
@@ -28,7 +28,7 @@ function composeRefs(...refs) {
|
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
-
function SheetRoot({ children, className, defaultOpen = false, onOpenChange, open: openProp, ref, side = "right", ...props }) {
|
|
31
|
+
function SheetRoot({ children, className, container, defaultOpen = false, disableEscapeKeyDown = false, disableOutsidePointerDown = false, onOpenChange, open: openProp, ref, side = "right", ...props }) {
|
|
32
32
|
const [open, setOpen] = useControllableState({
|
|
33
33
|
...(openProp !== undefined ? { value: openProp } : {}),
|
|
34
34
|
defaultValue: defaultOpen,
|
|
@@ -55,9 +55,12 @@ function SheetRoot({ children, className, defaultOpen = false, onOpenChange, ope
|
|
|
55
55
|
}, [open]);
|
|
56
56
|
return (_jsx(SheetContext.Provider, { value: {
|
|
57
57
|
close: () => setOpen(false),
|
|
58
|
+
...(container === undefined ? {} : { container }),
|
|
58
59
|
contentId: `${instanceId}-content`,
|
|
59
60
|
descriptionId: `${instanceId}-description`,
|
|
60
61
|
describedBy,
|
|
62
|
+
disableEscapeKeyDown,
|
|
63
|
+
disableOutsidePointerDown,
|
|
61
64
|
labelledBy,
|
|
62
65
|
open,
|
|
63
66
|
registerDescription,
|
|
@@ -76,12 +79,16 @@ function SheetTrigger({ className, disabled, onClick, ref, type = "button", ...p
|
|
|
76
79
|
}
|
|
77
80
|
}) }));
|
|
78
81
|
}
|
|
79
|
-
function SheetContent({ children, className, ref, ...props }) {
|
|
80
|
-
const { close, contentId, describedBy, labelledBy, open, side } = useSheetContext("Sheet.Content");
|
|
81
|
-
if (!open) {
|
|
82
|
+
function SheetContent({ children, className, forceMount = false, ref, ...props }) {
|
|
83
|
+
const { close, container, contentId, describedBy, disableEscapeKeyDown, disableOutsidePointerDown, labelledBy, open, side } = useSheetContext("Sheet.Content");
|
|
84
|
+
if (!open && !forceMount) {
|
|
82
85
|
return null;
|
|
83
86
|
}
|
|
84
|
-
|
|
87
|
+
const content = (_jsx("div", { ...props, ref: ref, id: contentId, role: "dialog", "aria-modal": "true", "aria-describedby": describedBy, "aria-labelledby": labelledBy, className: cn("liano-sheet__content", className), "data-side": side, "data-state": open ? "open" : "closed", hidden: open ? undefined : true, children: children }));
|
|
88
|
+
if (!open) {
|
|
89
|
+
return (_jsxs(Portal, { ...(container === undefined ? {} : { container }), children: [_jsx("div", { "aria-hidden": "true", className: "liano-sheet__overlay", hidden: true }), content] }));
|
|
90
|
+
}
|
|
91
|
+
return (_jsxs(Portal, { ...(container === undefined ? {} : { container }), children: [_jsx("div", { "aria-hidden": "true", className: "liano-sheet__overlay" }), _jsx(DismissableLayer, { open: open, disableEscapeKeyDown: disableEscapeKeyDown, disableOutsidePointerDown: disableOutsidePointerDown, onDismiss: close, children: _jsx(FocusScope, { active: open, trapped: true, children: content }) })] }));
|
|
85
92
|
}
|
|
86
93
|
function SheetHeader({ className, ref, ...props }) {
|
|
87
94
|
return _jsx("div", { ...props, ref: ref, className: cn("liano-sheet__header", className) });
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { Tooltip } from "./tooltip.js";
|
|
2
|
-
export type { TooltipProps } from "./tooltip.js";
|
|
2
|
+
export type { TooltipContentProps, TooltipProps, TooltipProviderProps, TooltipRootProps, TooltipTriggerProps } from "./tooltip.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import type { PortalProps } from "../../internal/portal/index.js";
|
|
2
3
|
import type { FloatingPlacement } from "../../internal/floating/index.js";
|
|
3
4
|
export interface TooltipProps {
|
|
4
5
|
children: React.ReactElement;
|
|
@@ -7,4 +8,39 @@ export interface TooltipProps {
|
|
|
7
8
|
delay?: number;
|
|
8
9
|
disabled?: boolean;
|
|
9
10
|
}
|
|
10
|
-
export
|
|
11
|
+
export interface TooltipProviderProps {
|
|
12
|
+
children?: React.ReactNode;
|
|
13
|
+
delayDuration?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface TooltipRootProps {
|
|
16
|
+
children?: React.ReactNode;
|
|
17
|
+
content?: React.ReactNode;
|
|
18
|
+
placement?: FloatingPlacement;
|
|
19
|
+
delay?: number;
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
open?: boolean;
|
|
22
|
+
defaultOpen?: boolean;
|
|
23
|
+
onOpenChange?: (open: boolean) => void;
|
|
24
|
+
}
|
|
25
|
+
export interface TooltipTriggerProps {
|
|
26
|
+
children: React.ReactElement;
|
|
27
|
+
}
|
|
28
|
+
export interface TooltipContentProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
29
|
+
sideOffset?: number;
|
|
30
|
+
collisionPadding?: number;
|
|
31
|
+
forceMount?: boolean;
|
|
32
|
+
container?: PortalProps["container"];
|
|
33
|
+
ref?: React.Ref<HTMLSpanElement>;
|
|
34
|
+
}
|
|
35
|
+
declare function TooltipProvider({ children, delayDuration }: TooltipProviderProps): React.JSX.Element;
|
|
36
|
+
declare function TooltipRoot({ children, defaultOpen, delay, disabled, onOpenChange, open: openProp, placement }: TooltipRootProps): React.JSX.Element;
|
|
37
|
+
declare function TooltipTrigger({ children }: TooltipTriggerProps): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
|
|
38
|
+
declare function TooltipContent({ children, className, collisionPadding, container, forceMount, ref, sideOffset, style, ...props }: TooltipContentProps): React.JSX.Element | null;
|
|
39
|
+
declare function TooltipWrapper({ children, content, delay, disabled, placement }: TooltipProps): React.JSX.Element;
|
|
40
|
+
export declare const Tooltip: typeof TooltipWrapper & {
|
|
41
|
+
Content: typeof TooltipContent;
|
|
42
|
+
Provider: typeof TooltipProvider;
|
|
43
|
+
Root: typeof TooltipRoot;
|
|
44
|
+
Trigger: typeof TooltipTrigger;
|
|
45
|
+
};
|
|
46
|
+
export {};
|
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx,
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Portal } from "../../internal/portal/index.js";
|
|
5
5
|
import { useFloatingPosition } from "../../internal/floating/index.js";
|
|
6
|
+
import { useControllableState } from "../../hooks/use-controllable-state.js";
|
|
6
7
|
import { composeEventHandlers } from "../../utils/compose-event-handlers.js";
|
|
8
|
+
import { cn } from "../../utils/cn.js";
|
|
9
|
+
const defaultContentPositioning = {
|
|
10
|
+
sideOffset: 8,
|
|
11
|
+
collisionPadding: 0
|
|
12
|
+
};
|
|
13
|
+
const TooltipProviderContext = React.createContext({
|
|
14
|
+
delayDuration: 0
|
|
15
|
+
});
|
|
16
|
+
const TooltipRootContext = React.createContext(null);
|
|
17
|
+
function useTooltipRootContext(calledBy) {
|
|
18
|
+
const context = React.useContext(TooltipRootContext);
|
|
19
|
+
if (context === null) {
|
|
20
|
+
throw new Error(`"${calledBy}" must be used within a <Tooltip.Root />`);
|
|
21
|
+
}
|
|
22
|
+
return context;
|
|
23
|
+
}
|
|
7
24
|
function composeRefs(...refs) {
|
|
8
25
|
return (node) => {
|
|
9
26
|
for (const ref of refs) {
|
|
@@ -24,15 +41,27 @@ function getPlacementParts(placement) {
|
|
|
24
41
|
const [side, align = "center"] = placement.split("-");
|
|
25
42
|
return { side, align };
|
|
26
43
|
}
|
|
27
|
-
|
|
44
|
+
function TooltipProvider({ children, delayDuration = 0 }) {
|
|
45
|
+
const contextValue = React.useMemo(() => ({ delayDuration }), [delayDuration]);
|
|
46
|
+
return (_jsx(TooltipProviderContext.Provider, { value: contextValue, children: children }));
|
|
47
|
+
}
|
|
48
|
+
function TooltipRoot({ children, defaultOpen = false, delay, disabled = false, onOpenChange, open: openProp, placement = "bottom" }) {
|
|
49
|
+
const provider = React.useContext(TooltipProviderContext);
|
|
28
50
|
const tooltipId = React.useId();
|
|
29
51
|
const tooltipElementId = `${tooltipId}-content`;
|
|
30
|
-
const
|
|
52
|
+
const resolvedDelay = delay ?? provider.delayDuration;
|
|
53
|
+
const [open, setOpen] = useControllableState({
|
|
54
|
+
...(openProp !== undefined ? { value: openProp } : {}),
|
|
55
|
+
defaultValue: defaultOpen,
|
|
56
|
+
...(onOpenChange ? { onChange: onOpenChange } : {})
|
|
57
|
+
});
|
|
58
|
+
const [contentPositioning, setContentPositioning] = React.useState(defaultContentPositioning);
|
|
31
59
|
const openTimeoutRef = React.useRef(null);
|
|
32
60
|
const { floatingRef, floatingStyle, placement: resolvedPlacement, referenceRef, updatePosition } = useFloatingPosition({
|
|
33
61
|
open,
|
|
34
62
|
placement,
|
|
35
|
-
offset:
|
|
63
|
+
offset: contentPositioning.sideOffset,
|
|
64
|
+
collisionPadding: contentPositioning.collisionPadding,
|
|
36
65
|
flip: true,
|
|
37
66
|
shift: true
|
|
38
67
|
});
|
|
@@ -48,53 +77,115 @@ export function Tooltip({ children, content, delay = 0, disabled = false, placem
|
|
|
48
77
|
return;
|
|
49
78
|
}
|
|
50
79
|
clearOpenTimeout();
|
|
51
|
-
if (
|
|
80
|
+
if (resolvedDelay > 0) {
|
|
52
81
|
openTimeoutRef.current = window.setTimeout(() => {
|
|
53
82
|
setOpen(true);
|
|
54
|
-
},
|
|
83
|
+
}, resolvedDelay);
|
|
55
84
|
return;
|
|
56
85
|
}
|
|
57
86
|
setOpen(true);
|
|
58
|
-
}, [clearOpenTimeout,
|
|
87
|
+
}, [clearOpenTimeout, disabled, resolvedDelay, setOpen]);
|
|
59
88
|
const closeTooltip = React.useCallback(() => {
|
|
60
89
|
clearOpenTimeout();
|
|
61
90
|
setOpen(false);
|
|
62
|
-
}, [clearOpenTimeout]);
|
|
91
|
+
}, [clearOpenTimeout, setOpen]);
|
|
63
92
|
React.useEffect(() => {
|
|
64
93
|
if (!open) {
|
|
65
94
|
return;
|
|
66
95
|
}
|
|
67
96
|
updatePosition();
|
|
68
|
-
}, [open,
|
|
97
|
+
}, [open, updatePosition]);
|
|
69
98
|
React.useEffect(() => {
|
|
70
99
|
if (disabled) {
|
|
71
100
|
closeTooltip();
|
|
72
101
|
}
|
|
73
102
|
}, [closeTooltip, disabled]);
|
|
103
|
+
React.useEffect(() => {
|
|
104
|
+
if (!open) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
const handleKeyDown = (event) => {
|
|
108
|
+
if (event.key === "Escape") {
|
|
109
|
+
closeTooltip();
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
113
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
114
|
+
}, [closeTooltip, open]);
|
|
74
115
|
React.useEffect(() => () => clearOpenTimeout(), [clearOpenTimeout]);
|
|
75
|
-
|
|
116
|
+
const contextValue = React.useMemo(() => ({
|
|
117
|
+
closeTooltip,
|
|
118
|
+
contentId: tooltipElementId,
|
|
119
|
+
disabled,
|
|
120
|
+
floatingRef,
|
|
121
|
+
floatingStyle,
|
|
122
|
+
open,
|
|
123
|
+
openWithDelay,
|
|
124
|
+
referenceRef,
|
|
125
|
+
resolvedPlacement,
|
|
126
|
+
setContentPositioning
|
|
127
|
+
}), [
|
|
128
|
+
closeTooltip,
|
|
129
|
+
disabled,
|
|
130
|
+
floatingRef,
|
|
131
|
+
floatingStyle,
|
|
132
|
+
open,
|
|
133
|
+
openWithDelay,
|
|
134
|
+
referenceRef,
|
|
135
|
+
resolvedPlacement,
|
|
136
|
+
tooltipElementId
|
|
137
|
+
]);
|
|
138
|
+
return (_jsx(TooltipRootContext.Provider, { value: contextValue, children: children }));
|
|
139
|
+
}
|
|
140
|
+
function TooltipTrigger({ children }) {
|
|
141
|
+
const context = useTooltipRootContext("Tooltip.Trigger");
|
|
142
|
+
if (!React.isValidElement(children) || context.disabled) {
|
|
76
143
|
return children;
|
|
77
144
|
}
|
|
78
145
|
const trigger = children;
|
|
79
146
|
const childProps = trigger.props;
|
|
80
147
|
const childAriaDescribedBy = childProps["aria-describedby"];
|
|
81
|
-
const triggerAriaDescribedBy = joinIds(typeof childAriaDescribedBy === "string" ? childAriaDescribedBy : undefined, open ?
|
|
82
|
-
const mergedRef = composeRefs(childProps.ref, referenceRef);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
148
|
+
const triggerAriaDescribedBy = joinIds(typeof childAriaDescribedBy === "string" ? childAriaDescribedBy : undefined, context.open ? context.contentId : undefined);
|
|
149
|
+
const mergedRef = composeRefs(childProps.ref, context.referenceRef);
|
|
150
|
+
return React.cloneElement(trigger, {
|
|
151
|
+
...childProps,
|
|
152
|
+
ref: mergedRef,
|
|
153
|
+
"aria-describedby": triggerAriaDescribedBy,
|
|
154
|
+
onMouseEnter: composeEventHandlers(childProps.onMouseEnter, context.openWithDelay),
|
|
155
|
+
onPointerEnter: composeEventHandlers(childProps.onPointerEnter, context.openWithDelay),
|
|
156
|
+
onMouseLeave: composeEventHandlers(childProps.onMouseLeave, context.closeTooltip),
|
|
157
|
+
onPointerLeave: composeEventHandlers(childProps.onPointerLeave, context.closeTooltip),
|
|
158
|
+
onFocus: composeEventHandlers(childProps.onFocus, context.openWithDelay),
|
|
159
|
+
onBlur: composeEventHandlers(childProps.onBlur, context.closeTooltip),
|
|
160
|
+
onKeyDown: composeEventHandlers(childProps.onKeyDown, (event) => {
|
|
161
|
+
if (event.key === "Escape") {
|
|
162
|
+
context.closeTooltip();
|
|
163
|
+
}
|
|
164
|
+
}, { checkDefaultPrevented: false })
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
function TooltipContent({ children, className, collisionPadding = defaultContentPositioning.collisionPadding, container, forceMount = false, ref, sideOffset = defaultContentPositioning.sideOffset, style, ...props }) {
|
|
168
|
+
const context = useTooltipRootContext("Tooltip.Content");
|
|
169
|
+
React.useEffect(() => {
|
|
170
|
+
context.setContentPositioning({
|
|
171
|
+
sideOffset,
|
|
172
|
+
collisionPadding
|
|
173
|
+
});
|
|
174
|
+
return () => context.setContentPositioning(defaultContentPositioning);
|
|
175
|
+
}, [collisionPadding, context, sideOffset]);
|
|
176
|
+
if (!context.open && !forceMount) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
const placementParts = getPlacementParts(context.resolvedPlacement);
|
|
180
|
+
const content = (_jsx("span", { ...props, ref: composeRefs(ref, context.floatingRef), id: context.contentId, className: cn("liano-tooltip", className), "data-align": placementParts.align, "data-side": placementParts.side, hidden: context.open ? undefined : true, role: "tooltip", style: { ...context.floatingStyle, ...style }, children: children }));
|
|
181
|
+
return (_jsx(Portal, { ...(container === undefined ? {} : { container }), children: content }));
|
|
182
|
+
}
|
|
183
|
+
function TooltipWrapper({ children, content, delay = 0, disabled = false, placement = "bottom" }) {
|
|
184
|
+
return (_jsxs(TooltipRoot, { delay: delay, disabled: disabled, placement: placement, children: [_jsx(TooltipTrigger, { children: children }), _jsx(TooltipContent, { children: content })] }));
|
|
100
185
|
}
|
|
186
|
+
export const Tooltip = Object.assign(TooltipWrapper, {
|
|
187
|
+
Content: TooltipContent,
|
|
188
|
+
Provider: TooltipProvider,
|
|
189
|
+
Root: TooltipRoot,
|
|
190
|
+
Trigger: TooltipTrigger
|
|
191
|
+
});
|
|
@@ -73,6 +73,7 @@ export function useFloatingPosition(options) {
|
|
|
73
73
|
left: `${computed.x}px`,
|
|
74
74
|
top: `${computed.y}px`,
|
|
75
75
|
width: matchReferenceWidth ? `${referenceEl?.offsetWidth ?? computed.referenceWidth}px` : "auto",
|
|
76
|
+
"--liano-floating-reference-width": `${computed.referenceWidth}px`,
|
|
76
77
|
"--liano-floating-available-width": `${computed.availableWidth}px`,
|
|
77
78
|
"--liano-floating-available-height": `${computed.availableHeight}px`
|
|
78
79
|
});
|