@zvk/ui 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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 interface SelectProps extends React.HTMLAttributes<HTMLDivElement> {
5
+ export type SelectValue = string | null;
6
+ export interface SelectProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "defaultValue" | "onChange"> {
5
7
  name?: string;
6
- value?: string;
7
- defaultValue?: string;
8
- onValueChange?: (value: string) => void;
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
- declare function SelectRoot({ children, className, defaultValue, disabled, invalid, name, onValueChange, placeholder, placement, required, size, value, ...props }: SelectProps): React.JSX.Element;
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 = "", disabled, invalid, name, onValueChange, placeholder, placement = "bottom-start", required, size = "md", value, ...props }) {
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] = React.useState(false);
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
- return (_jsxs(Portal, { children: [_jsx("div", { "aria-hidden": "true", className: "liano-sheet__overlay" }), _jsx(DismissableLayer, { open: open, onDismiss: close, children: _jsx(FocusScope, { active: open, trapped: true, children: _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", children: children }) }) })] }));
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 declare function Tooltip({ children, content, delay, disabled, placement }: TooltipProps): React.JSX.Element;
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, Fragment as _Fragment, jsxs as _jsxs } 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 { 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
- export function Tooltip({ children, content, delay = 0, disabled = false, placement = "bottom" }) {
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 [open, setOpen] = React.useState(false);
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: 8,
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 (delay > 0) {
80
+ if (resolvedDelay > 0) {
52
81
  openTimeoutRef.current = window.setTimeout(() => {
53
82
  setOpen(true);
54
- }, delay);
83
+ }, resolvedDelay);
55
84
  return;
56
85
  }
57
86
  setOpen(true);
58
- }, [clearOpenTimeout, delay, disabled]);
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, content, updatePosition]);
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
- if (!React.isValidElement(children) || disabled) {
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 ? tooltipElementId : undefined);
82
- const mergedRef = composeRefs(childProps.ref, referenceRef);
83
- const placementParts = getPlacementParts(resolvedPlacement);
84
- return (_jsxs(_Fragment, { children: [React.cloneElement(trigger, {
85
- ...childProps,
86
- ref: mergedRef,
87
- "aria-describedby": triggerAriaDescribedBy,
88
- onMouseEnter: composeEventHandlers(childProps.onMouseEnter, openWithDelay),
89
- onPointerEnter: composeEventHandlers(childProps.onPointerEnter, openWithDelay),
90
- onMouseLeave: composeEventHandlers(childProps.onMouseLeave, closeTooltip),
91
- onPointerLeave: composeEventHandlers(childProps.onPointerLeave, closeTooltip),
92
- onFocus: composeEventHandlers(childProps.onFocus, openWithDelay),
93
- onBlur: composeEventHandlers(childProps.onBlur, closeTooltip),
94
- onKeyDown: composeEventHandlers(childProps.onKeyDown, (event) => {
95
- if (event.key === "Escape") {
96
- closeTooltip();
97
- }
98
- }, { checkDefaultPrevented: false })
99
- }), open ? (_jsx(Portal, { children: _jsx("span", { id: tooltipElementId, className: "liano-tooltip", "data-align": placementParts.align, "data-side": placementParts.side, role: "tooltip", ref: floatingRef, style: floatingStyle, children: content }) })) : null] }));
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
  });