@arun-dev/headless 4.3.0 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/anchoring-Btxi0QwC.d.cts +12 -0
- package/dist/anchoring-Btxi0QwC.d.ts +12 -0
- package/dist/chunk-HCOCPQXS.js +27 -0
- package/dist/popover/index.cjs +322 -0
- package/dist/popover/index.d.cts +99 -0
- package/dist/popover/index.d.ts +99 -0
- package/dist/popover/index.js +164 -0
- package/dist/tooltip/index.cjs +364 -0
- package/dist/tooltip/index.d.cts +92 -0
- package/dist/tooltip/index.d.ts +92 -0
- package/dist/tooltip/index.js +206 -0
- package/package.json +22 -2
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import {
|
|
2
|
+
anchorNameFor,
|
|
3
|
+
anchoredDataAttributes,
|
|
4
|
+
anchoredPopupStyle
|
|
5
|
+
} from "../chunk-HCOCPQXS.js";
|
|
6
|
+
import {
|
|
7
|
+
useControlled
|
|
8
|
+
} from "../chunk-UEIB3I3T.js";
|
|
9
|
+
import {
|
|
10
|
+
__export,
|
|
11
|
+
useRender
|
|
12
|
+
} from "../chunk-OQTWWZ4B.js";
|
|
13
|
+
|
|
14
|
+
// src/popover/index.parts.ts
|
|
15
|
+
var index_parts_exports = {};
|
|
16
|
+
__export(index_parts_exports, {
|
|
17
|
+
Close: () => PopoverClose,
|
|
18
|
+
Popup: () => PopoverPopup,
|
|
19
|
+
Root: () => PopoverRoot,
|
|
20
|
+
Trigger: () => PopoverTrigger
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// src/popover/PopoverRoot.tsx
|
|
24
|
+
import { useCallback, useId, useMemo, useRef } from "react";
|
|
25
|
+
|
|
26
|
+
// src/popover/PopoverRootContext.ts
|
|
27
|
+
import { createContext, useContext } from "react";
|
|
28
|
+
var PopoverRootContext = createContext(null);
|
|
29
|
+
function usePopoverRootContext(part) {
|
|
30
|
+
const context = useContext(PopoverRootContext);
|
|
31
|
+
if (context === null) {
|
|
32
|
+
throw new Error(`<Popover.${part}> must be rendered inside <Popover.Root>.`);
|
|
33
|
+
}
|
|
34
|
+
return context;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/popover/PopoverRoot.tsx
|
|
38
|
+
import { jsx } from "react/jsx-runtime";
|
|
39
|
+
function PopoverRoot({
|
|
40
|
+
open: openProp,
|
|
41
|
+
defaultOpen,
|
|
42
|
+
onOpenChange,
|
|
43
|
+
children
|
|
44
|
+
}) {
|
|
45
|
+
const [open, setOpenState] = useControlled({
|
|
46
|
+
controlled: openProp,
|
|
47
|
+
default: defaultOpen ?? false,
|
|
48
|
+
name: "Popover.Root",
|
|
49
|
+
state: "open"
|
|
50
|
+
});
|
|
51
|
+
const setOpen = useCallback(
|
|
52
|
+
(next) => {
|
|
53
|
+
if (next === open) return;
|
|
54
|
+
setOpenState(next);
|
|
55
|
+
onOpenChange?.(next);
|
|
56
|
+
},
|
|
57
|
+
[open, setOpenState, onOpenChange]
|
|
58
|
+
);
|
|
59
|
+
const id = useId();
|
|
60
|
+
const triggerRef = useRef(null);
|
|
61
|
+
const context = useMemo(
|
|
62
|
+
() => ({ open, setOpen, popupId: id, anchorName: anchorNameFor(id), triggerRef }),
|
|
63
|
+
[open, setOpen, id]
|
|
64
|
+
);
|
|
65
|
+
return /* @__PURE__ */ jsx(PopoverRootContext.Provider, { value: context, children });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/popover/popoverDataAttributes.ts
|
|
69
|
+
function popoverDataAttributes({ open }) {
|
|
70
|
+
return {
|
|
71
|
+
"data-open": open ? "" : void 0,
|
|
72
|
+
"data-closed": open ? void 0 : ""
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/popover/PopoverTrigger.tsx
|
|
77
|
+
function PopoverTrigger({ className, children, render, ...rest }) {
|
|
78
|
+
const { open, setOpen, popupId, anchorName, triggerRef } = usePopoverRootContext("Trigger");
|
|
79
|
+
return useRender({
|
|
80
|
+
render,
|
|
81
|
+
defaultTagName: "button",
|
|
82
|
+
props: {
|
|
83
|
+
type: "button",
|
|
84
|
+
"aria-haspopup": "dialog",
|
|
85
|
+
"aria-expanded": open,
|
|
86
|
+
"aria-controls": popupId,
|
|
87
|
+
...popoverDataAttributes({ open }),
|
|
88
|
+
style: { anchorName },
|
|
89
|
+
className,
|
|
90
|
+
children,
|
|
91
|
+
ref: triggerRef,
|
|
92
|
+
onClick() {
|
|
93
|
+
setOpen(!open);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
consumerProps: rest
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/popover/PopoverPopup.tsx
|
|
101
|
+
import { useEffect, useReducer, useRef as useRef2 } from "react";
|
|
102
|
+
function PopoverPopup({
|
|
103
|
+
side = "bottom",
|
|
104
|
+
align = "center",
|
|
105
|
+
className,
|
|
106
|
+
children,
|
|
107
|
+
render,
|
|
108
|
+
...rest
|
|
109
|
+
}) {
|
|
110
|
+
const { open, setOpen, popupId, anchorName, triggerRef } = usePopoverRootContext("Popup");
|
|
111
|
+
const elementRef = useRef2(null);
|
|
112
|
+
const [, resync] = useReducer((n) => n + 1, 0);
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
const popup = elementRef.current;
|
|
115
|
+
if (!popup || typeof popup.showPopover !== "function") return;
|
|
116
|
+
const shown = popup.matches(":popover-open");
|
|
117
|
+
if (open && !shown) {
|
|
118
|
+
popup.showPopover({ source: triggerRef.current ?? void 0 });
|
|
119
|
+
} else if (!open && shown) popup.hidePopover();
|
|
120
|
+
});
|
|
121
|
+
return useRender({
|
|
122
|
+
render,
|
|
123
|
+
defaultTagName: "div",
|
|
124
|
+
props: {
|
|
125
|
+
id: popupId,
|
|
126
|
+
popover: "auto",
|
|
127
|
+
role: "dialog",
|
|
128
|
+
...anchoredDataAttributes(side, align),
|
|
129
|
+
...popoverDataAttributes({ open }),
|
|
130
|
+
style: anchoredPopupStyle(anchorName, side, align),
|
|
131
|
+
className,
|
|
132
|
+
children,
|
|
133
|
+
ref: elementRef,
|
|
134
|
+
onToggle(event) {
|
|
135
|
+
if (event.target !== event.currentTarget) return;
|
|
136
|
+
if (event.nativeEvent.newState !== "closed") return;
|
|
137
|
+
setOpen(false);
|
|
138
|
+
resync();
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
consumerProps: rest
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/popover/PopoverClose.tsx
|
|
146
|
+
function PopoverClose({ className, children, render, ...rest }) {
|
|
147
|
+
const { setOpen } = usePopoverRootContext("Close");
|
|
148
|
+
return useRender({
|
|
149
|
+
render,
|
|
150
|
+
defaultTagName: "button",
|
|
151
|
+
props: {
|
|
152
|
+
type: "button",
|
|
153
|
+
className,
|
|
154
|
+
children,
|
|
155
|
+
onClick() {
|
|
156
|
+
setOpen(false);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
consumerProps: rest
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
export {
|
|
163
|
+
index_parts_exports as Popover
|
|
164
|
+
};
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/tooltip/index.ts
|
|
21
|
+
var tooltip_exports = {};
|
|
22
|
+
__export(tooltip_exports, {
|
|
23
|
+
Tooltip: () => index_parts_exports
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(tooltip_exports);
|
|
26
|
+
|
|
27
|
+
// src/tooltip/index.parts.ts
|
|
28
|
+
var index_parts_exports = {};
|
|
29
|
+
__export(index_parts_exports, {
|
|
30
|
+
Popup: () => TooltipPopup,
|
|
31
|
+
Root: () => TooltipRoot,
|
|
32
|
+
Trigger: () => TooltipTrigger
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// src/tooltip/TooltipRoot.tsx
|
|
36
|
+
var import_react3 = require("react");
|
|
37
|
+
|
|
38
|
+
// src/core/anchoring.ts
|
|
39
|
+
function anchorNameFor(id) {
|
|
40
|
+
return `--hl-anchor-${id.replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
41
|
+
}
|
|
42
|
+
var POSITION_AREA = {
|
|
43
|
+
bottom: { start: "bottom span-right", center: "bottom", end: "bottom span-left" },
|
|
44
|
+
top: { start: "top span-right", center: "top", end: "top span-left" },
|
|
45
|
+
right: { start: "right span-bottom", center: "right", end: "right span-top" },
|
|
46
|
+
left: { start: "left span-bottom", center: "left", end: "left span-top" }
|
|
47
|
+
};
|
|
48
|
+
function anchoredPopupStyle(anchorName, side, align) {
|
|
49
|
+
return {
|
|
50
|
+
positionAnchor: anchorName,
|
|
51
|
+
positionArea: POSITION_AREA[side][align],
|
|
52
|
+
// Flip across the axis it opens on; the other axis is left alone.
|
|
53
|
+
positionTryFallbacks: side === "top" || side === "bottom" ? "flip-block" : "flip-inline"
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function anchoredDataAttributes(side, align) {
|
|
57
|
+
return { "data-side": side, "data-align": align };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/core/useControlled.ts
|
|
61
|
+
var import_react = require("react");
|
|
62
|
+
function useControlled({
|
|
63
|
+
controlled,
|
|
64
|
+
default: defaultValue,
|
|
65
|
+
name = "Component",
|
|
66
|
+
state = "value"
|
|
67
|
+
}) {
|
|
68
|
+
const { current: isControlled } = (0, import_react.useRef)(controlled !== void 0);
|
|
69
|
+
const [uncontrolled, setUncontrolled] = (0, import_react.useState)(defaultValue);
|
|
70
|
+
const defaultRef = (0, import_react.useRef)(defaultValue);
|
|
71
|
+
const value = isControlled ? controlled : uncontrolled;
|
|
72
|
+
if (process.env.NODE_ENV !== "production") {
|
|
73
|
+
if (isControlled !== (controlled !== void 0)) {
|
|
74
|
+
console.error(
|
|
75
|
+
`${name}: cannot switch between controlled and uncontrolled \`${state}\`. Decide which one this component is for the whole of its life.`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
if (!isControlled && defaultRef.current !== defaultValue) {
|
|
79
|
+
console.error(
|
|
80
|
+
`${name}: cannot change the default \`${state}\` after mount. It is only read once, so later changes are silently ignored.`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const setValue = (0, import_react.useCallback)(
|
|
85
|
+
(next) => {
|
|
86
|
+
if (!isControlled) setUncontrolled(next);
|
|
87
|
+
},
|
|
88
|
+
[isControlled]
|
|
89
|
+
);
|
|
90
|
+
return [value, setValue];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/tooltip/TooltipRootContext.ts
|
|
94
|
+
var import_react2 = require("react");
|
|
95
|
+
var TooltipRootContext = (0, import_react2.createContext)(null);
|
|
96
|
+
function useTooltipRootContext(part) {
|
|
97
|
+
const context = (0, import_react2.useContext)(TooltipRootContext);
|
|
98
|
+
if (context === null) {
|
|
99
|
+
throw new Error(`<Tooltip.${part}> must be rendered inside <Tooltip.Root>.`);
|
|
100
|
+
}
|
|
101
|
+
return context;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/tooltip/TooltipRoot.tsx
|
|
105
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
106
|
+
var WARM_WINDOW = 300;
|
|
107
|
+
var openCount = 0;
|
|
108
|
+
var lastClosedAt = Number.NEGATIVE_INFINITY;
|
|
109
|
+
function isWarm() {
|
|
110
|
+
return openCount > 0 || Date.now() - lastClosedAt < WARM_WINDOW;
|
|
111
|
+
}
|
|
112
|
+
function TooltipRoot({
|
|
113
|
+
open: openProp,
|
|
114
|
+
defaultOpen,
|
|
115
|
+
onOpenChange,
|
|
116
|
+
delay = 600,
|
|
117
|
+
closeDelay = 100,
|
|
118
|
+
children
|
|
119
|
+
}) {
|
|
120
|
+
const [open, setOpenState] = useControlled({
|
|
121
|
+
controlled: openProp,
|
|
122
|
+
default: defaultOpen ?? false,
|
|
123
|
+
name: "Tooltip.Root",
|
|
124
|
+
state: "open"
|
|
125
|
+
});
|
|
126
|
+
const timer = (0, import_react3.useRef)(void 0);
|
|
127
|
+
const cancelPending = (0, import_react3.useCallback)(() => {
|
|
128
|
+
clearTimeout(timer.current);
|
|
129
|
+
timer.current = void 0;
|
|
130
|
+
}, []);
|
|
131
|
+
const setOpen = (0, import_react3.useCallback)(
|
|
132
|
+
(next) => {
|
|
133
|
+
cancelPending();
|
|
134
|
+
if (next === open) return;
|
|
135
|
+
setOpenState(next);
|
|
136
|
+
onOpenChange?.(next);
|
|
137
|
+
},
|
|
138
|
+
[open, setOpenState, onOpenChange, cancelPending]
|
|
139
|
+
);
|
|
140
|
+
const openAfterDelay = (0, import_react3.useCallback)(() => {
|
|
141
|
+
cancelPending();
|
|
142
|
+
if (open) return;
|
|
143
|
+
if (isWarm()) setOpen(true);
|
|
144
|
+
else timer.current = setTimeout(() => setOpen(true), delay);
|
|
145
|
+
}, [open, delay, setOpen, cancelPending]);
|
|
146
|
+
const closeAfterGrace = (0, import_react3.useCallback)(() => {
|
|
147
|
+
cancelPending();
|
|
148
|
+
if (!open) return;
|
|
149
|
+
timer.current = setTimeout(() => setOpen(false), closeDelay);
|
|
150
|
+
}, [open, closeDelay, setOpen, cancelPending]);
|
|
151
|
+
(0, import_react3.useEffect)(() => cancelPending, [cancelPending]);
|
|
152
|
+
(0, import_react3.useEffect)(() => {
|
|
153
|
+
if (!open) return;
|
|
154
|
+
openCount += 1;
|
|
155
|
+
return () => {
|
|
156
|
+
openCount -= 1;
|
|
157
|
+
lastClosedAt = Date.now();
|
|
158
|
+
};
|
|
159
|
+
}, [open]);
|
|
160
|
+
const id = (0, import_react3.useId)();
|
|
161
|
+
const context = (0, import_react3.useMemo)(
|
|
162
|
+
() => ({
|
|
163
|
+
open,
|
|
164
|
+
setOpen,
|
|
165
|
+
openAfterDelay,
|
|
166
|
+
closeAfterGrace,
|
|
167
|
+
cancelPending,
|
|
168
|
+
popupId: id,
|
|
169
|
+
anchorName: anchorNameFor(id)
|
|
170
|
+
}),
|
|
171
|
+
[open, setOpen, openAfterDelay, closeAfterGrace, cancelPending, id]
|
|
172
|
+
);
|
|
173
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipRootContext.Provider, { value: context, children });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// src/core/useRender.ts
|
|
177
|
+
var import_react4 = require("react");
|
|
178
|
+
|
|
179
|
+
// src/core/mergeProps.ts
|
|
180
|
+
function isEventHandler(key) {
|
|
181
|
+
return key.length > 2 && key.startsWith("on") && key[2] === key[2]?.toUpperCase();
|
|
182
|
+
}
|
|
183
|
+
function mergeRefs(...refs) {
|
|
184
|
+
return (node) => {
|
|
185
|
+
const cleanups = refs.map((ref) => {
|
|
186
|
+
if (typeof ref === "function") {
|
|
187
|
+
const result = ref(node);
|
|
188
|
+
return typeof result === "function" ? result : () => ref(null);
|
|
189
|
+
}
|
|
190
|
+
if (ref && typeof ref === "object") {
|
|
191
|
+
ref.current = node;
|
|
192
|
+
return () => {
|
|
193
|
+
ref.current = null;
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return void 0;
|
|
197
|
+
});
|
|
198
|
+
return () => {
|
|
199
|
+
cleanups.forEach((cleanup) => {
|
|
200
|
+
if (typeof cleanup === "function") cleanup();
|
|
201
|
+
});
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function isSyntheticEvent(event) {
|
|
206
|
+
return typeof event === "object" && event !== null && "nativeEvent" in event;
|
|
207
|
+
}
|
|
208
|
+
function makeEventPreventable(event) {
|
|
209
|
+
if (Object.prototype.hasOwnProperty.call(event, "preventComponentHandler")) return;
|
|
210
|
+
const target = event;
|
|
211
|
+
target.preventComponentHandler = () => {
|
|
212
|
+
target.componentHandlerPrevented = true;
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function isComponentHandlerPrevented(event) {
|
|
216
|
+
return typeof event === "object" && event !== null && event.componentHandlerPrevented === true;
|
|
217
|
+
}
|
|
218
|
+
function chainHandlers(earlier, value) {
|
|
219
|
+
return (...args) => {
|
|
220
|
+
const event = args[0];
|
|
221
|
+
if (isSyntheticEvent(event)) makeEventPreventable(event);
|
|
222
|
+
value(...args);
|
|
223
|
+
if (isComponentHandlerPrevented(event)) return;
|
|
224
|
+
earlier(...args);
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function mergeProps(...objects) {
|
|
228
|
+
const merged = {};
|
|
229
|
+
for (const props of objects) {
|
|
230
|
+
if (!props) continue;
|
|
231
|
+
for (const key of Object.keys(props)) {
|
|
232
|
+
const value = props[key];
|
|
233
|
+
if (value === void 0) continue;
|
|
234
|
+
if (isEventHandler(key) && typeof value === "function") {
|
|
235
|
+
const earlier = merged[key];
|
|
236
|
+
merged[key] = typeof earlier === "function" ? chainHandlers(earlier, value) : value;
|
|
237
|
+
} else if (key === "className") {
|
|
238
|
+
merged.className = merged.className ? `${String(merged.className)} ${String(value)}` : value;
|
|
239
|
+
} else if (key === "style") {
|
|
240
|
+
merged.style = { ...merged.style, ...value };
|
|
241
|
+
} else if (key === "ref") {
|
|
242
|
+
merged.ref = merged.ref ? mergeRefs(merged.ref, value) : value;
|
|
243
|
+
} else {
|
|
244
|
+
merged[key] = value;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return merged;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// src/core/useRender.ts
|
|
252
|
+
function useRender({
|
|
253
|
+
render,
|
|
254
|
+
defaultTagName,
|
|
255
|
+
props,
|
|
256
|
+
consumerProps
|
|
257
|
+
}) {
|
|
258
|
+
const merged = mergeProps(props, consumerProps);
|
|
259
|
+
if ((0, import_react4.isValidElement)(render)) {
|
|
260
|
+
return (0, import_react4.cloneElement)(render, mergeProps(merged, render.props));
|
|
261
|
+
}
|
|
262
|
+
return (0, import_react4.createElement)(defaultTagName, merged);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// src/tooltip/tooltipDataAttributes.ts
|
|
266
|
+
function tooltipDataAttributes({ open }) {
|
|
267
|
+
return {
|
|
268
|
+
"data-open": open ? "" : void 0,
|
|
269
|
+
"data-closed": open ? void 0 : ""
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// src/tooltip/TooltipTrigger.tsx
|
|
274
|
+
function TooltipTrigger({
|
|
275
|
+
className,
|
|
276
|
+
children,
|
|
277
|
+
render,
|
|
278
|
+
"aria-describedby": describedBy,
|
|
279
|
+
...rest
|
|
280
|
+
}) {
|
|
281
|
+
const { open, setOpen, openAfterDelay, closeAfterGrace, popupId, anchorName } = useTooltipRootContext("Trigger");
|
|
282
|
+
return useRender({
|
|
283
|
+
render,
|
|
284
|
+
defaultTagName: "button",
|
|
285
|
+
props: {
|
|
286
|
+
type: "button",
|
|
287
|
+
"aria-describedby": describedBy ? `${describedBy} ${popupId}` : popupId,
|
|
288
|
+
...tooltipDataAttributes({ open }),
|
|
289
|
+
style: { anchorName },
|
|
290
|
+
className,
|
|
291
|
+
children,
|
|
292
|
+
onPointerEnter(event) {
|
|
293
|
+
if (event.pointerType !== "touch") openAfterDelay();
|
|
294
|
+
},
|
|
295
|
+
onPointerLeave(event) {
|
|
296
|
+
if (event.pointerType !== "touch") closeAfterGrace();
|
|
297
|
+
},
|
|
298
|
+
onPointerDown() {
|
|
299
|
+
setOpen(false);
|
|
300
|
+
},
|
|
301
|
+
onFocus(event) {
|
|
302
|
+
if (event.currentTarget.matches(":focus-visible")) setOpen(true);
|
|
303
|
+
},
|
|
304
|
+
onBlur() {
|
|
305
|
+
setOpen(false);
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
consumerProps: rest
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// src/tooltip/TooltipPopup.tsx
|
|
313
|
+
var import_react5 = require("react");
|
|
314
|
+
function TooltipPopup({
|
|
315
|
+
side = "top",
|
|
316
|
+
align = "center",
|
|
317
|
+
className,
|
|
318
|
+
children,
|
|
319
|
+
render,
|
|
320
|
+
...rest
|
|
321
|
+
}) {
|
|
322
|
+
const { open, setOpen, cancelPending, closeAfterGrace, popupId, anchorName } = useTooltipRootContext("Popup");
|
|
323
|
+
const elementRef = (0, import_react5.useRef)(null);
|
|
324
|
+
(0, import_react5.useEffect)(() => {
|
|
325
|
+
const popup = elementRef.current;
|
|
326
|
+
if (!popup || typeof popup.showPopover !== "function") return;
|
|
327
|
+
const shown = popup.matches(":popover-open");
|
|
328
|
+
if (open && !shown) popup.showPopover();
|
|
329
|
+
else if (!open && shown) popup.hidePopover();
|
|
330
|
+
});
|
|
331
|
+
(0, import_react5.useEffect)(() => {
|
|
332
|
+
if (!open) return;
|
|
333
|
+
function onKeyDown(event) {
|
|
334
|
+
if (event.key !== "Escape") return;
|
|
335
|
+
event.preventDefault();
|
|
336
|
+
event.stopPropagation();
|
|
337
|
+
setOpen(false);
|
|
338
|
+
}
|
|
339
|
+
document.addEventListener("keydown", onKeyDown, true);
|
|
340
|
+
return () => document.removeEventListener("keydown", onKeyDown, true);
|
|
341
|
+
}, [open, setOpen]);
|
|
342
|
+
return useRender({
|
|
343
|
+
render,
|
|
344
|
+
defaultTagName: "div",
|
|
345
|
+
props: {
|
|
346
|
+
id: popupId,
|
|
347
|
+
popover: "manual",
|
|
348
|
+
role: "tooltip",
|
|
349
|
+
...anchoredDataAttributes(side, align),
|
|
350
|
+
...tooltipDataAttributes({ open }),
|
|
351
|
+
style: anchoredPopupStyle(anchorName, side, align),
|
|
352
|
+
className,
|
|
353
|
+
children,
|
|
354
|
+
ref: elementRef,
|
|
355
|
+
onPointerEnter: cancelPending,
|
|
356
|
+
onPointerLeave: closeAfterGrace
|
|
357
|
+
},
|
|
358
|
+
consumerProps: rest
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
362
|
+
0 && (module.exports = {
|
|
363
|
+
Tooltip
|
|
364
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode, ReactElement, Ref, ComponentPropsWithRef } from 'react';
|
|
3
|
+
import { A as AnchorSide, a as AnchorAlign } from '../anchoring-Btxi0QwC.cjs';
|
|
4
|
+
|
|
5
|
+
type TooltipRootProps = {
|
|
6
|
+
/**
|
|
7
|
+
* Controlled state. Provide `onOpenChange` alongside it.
|
|
8
|
+
*
|
|
9
|
+
* Never `undefined` once mounted: the mode is latched at mount, as for Switch.
|
|
10
|
+
*/
|
|
11
|
+
open?: boolean;
|
|
12
|
+
/** Initial state when uncontrolled. Read once, at mount. */
|
|
13
|
+
defaultOpen?: boolean;
|
|
14
|
+
/** Called on every open and close, after any delay has run. */
|
|
15
|
+
onOpenChange?: (open: boolean) => void;
|
|
16
|
+
/** Milliseconds a pointer rests on the trigger before it opens. Focus opens at once. */
|
|
17
|
+
delay?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Milliseconds between the pointer leaving and the tooltip closing — time to move onto
|
|
20
|
+
* the tooltip, which then stays open while hovered (WCAG 1.4.13).
|
|
21
|
+
*/
|
|
22
|
+
closeDelay?: number;
|
|
23
|
+
children?: ReactNode;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Holds a tooltip's state and timers, and shares them with its parts. Renders no element.
|
|
27
|
+
*
|
|
28
|
+
* The popup is a native `popover="manual"`, anchored like Popover (decision 12). `hint`
|
|
29
|
+
* would be the natural fit, but Safari lacks it; `manual` behaves the same everywhere and,
|
|
30
|
+
* like `hint`, does not close an open Popover when it shows. What `manual` leaves out —
|
|
31
|
+
* opening on hover and focus, closing on leave, blur and Esc — is the timing below.
|
|
32
|
+
*/
|
|
33
|
+
declare function TooltipRoot({ open: openProp, defaultOpen, onOpenChange, delay, closeDelay, children, }: TooltipRootProps): react.JSX.Element;
|
|
34
|
+
|
|
35
|
+
type TooltipTriggerOwnProps = {
|
|
36
|
+
/** Element to render instead of the default `<button>`. Props and ref are merged onto it. */
|
|
37
|
+
render?: ReactElement;
|
|
38
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
39
|
+
ref?: Ref<HTMLElement>;
|
|
40
|
+
};
|
|
41
|
+
type TooltipTriggerProps = TooltipTriggerOwnProps & Omit<ComponentPropsWithRef<'button'>, keyof TooltipTriggerOwnProps>;
|
|
42
|
+
/**
|
|
43
|
+
* The element the tooltip describes and is anchored to. A native `<button>` by default;
|
|
44
|
+
* render another focusable element when the tooltip belongs to one — a Popover.Trigger,
|
|
45
|
+
* say, or a link.
|
|
46
|
+
*
|
|
47
|
+
* - A mouse or pen resting on it opens the tooltip after the Root's `delay`; leaving
|
|
48
|
+
* closes it after `closeDelay`. Touch never opens it: there is no hover to end it.
|
|
49
|
+
* - Keyboard focus opens it at once; blur closes it. Focus from a click does not open it —
|
|
50
|
+
* only `:focus-visible` does.
|
|
51
|
+
* - Pressing it closes the tooltip, since the press is what the user came to do.
|
|
52
|
+
*
|
|
53
|
+
* `aria-describedby` points at the tooltip at all times — the description is read from the
|
|
54
|
+
* hidden element too — and is joined with any of yours.
|
|
55
|
+
*/
|
|
56
|
+
declare function TooltipTrigger({ className, children, render, 'aria-describedby': describedBy, ...rest }: TooltipTriggerProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
57
|
+
|
|
58
|
+
type TooltipSide = AnchorSide;
|
|
59
|
+
type TooltipAlign = AnchorAlign;
|
|
60
|
+
type TooltipPopupOwnProps = {
|
|
61
|
+
/** Which side of the trigger to show on. Flips to the opposite side when there is no room. */
|
|
62
|
+
side?: TooltipSide;
|
|
63
|
+
/** Where along that side: flush with the trigger's start or end edge, or centred on it. */
|
|
64
|
+
align?: TooltipAlign;
|
|
65
|
+
/** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
|
|
66
|
+
render?: ReactElement;
|
|
67
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
68
|
+
ref?: Ref<HTMLElement>;
|
|
69
|
+
};
|
|
70
|
+
type TooltipPopupProps = TooltipPopupOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof TooltipPopupOwnProps | 'popover'>;
|
|
71
|
+
/**
|
|
72
|
+
* The tooltip: a native `popover="manual"` with `role="tooltip"`, anchored to the trigger
|
|
73
|
+
* as Popover is (decision 12). State drives the element, never the reverse.
|
|
74
|
+
*
|
|
75
|
+
* It stays open while the pointer is on it, so its text can be read and selected, and
|
|
76
|
+
* closes on Esc — which closes only the tooltip, not a Dialog or Popover beneath it.
|
|
77
|
+
*
|
|
78
|
+
* Text only, by the ARIA pattern: it describes the trigger and is never focused, so it must
|
|
79
|
+
* not hold anything interactive. Use a Popover for that.
|
|
80
|
+
*/
|
|
81
|
+
declare function TooltipPopup({ side, align, className, children, render, ...rest }: TooltipPopupProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
82
|
+
|
|
83
|
+
declare namespace index_parts {
|
|
84
|
+
export { TooltipPopup as Popup, TooltipRoot as Root, TooltipTrigger as Trigger };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** The state Tooltip.Root shares with its parts, and projects as `data-*` attributes. */
|
|
88
|
+
type TooltipState = {
|
|
89
|
+
open: boolean;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export { index_parts as Tooltip, type TooltipAlign, type TooltipPopupProps, type TooltipRootProps, type TooltipSide, type TooltipState, type TooltipTriggerProps };
|