@mortenbrudvik/waveui 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -32
- package/dist/components/data-display/InfoButton.cjs +1 -1
- package/dist/components/data-display/InfoButton.cjs.map +1 -1
- package/dist/components/data-display/InfoButton.mjs +90 -82
- package/dist/components/data-display/InfoButton.mjs.map +1 -1
- package/dist/components/data-display/InfoLabel.cjs +1 -1
- package/dist/components/data-display/InfoLabel.cjs.map +1 -1
- package/dist/components/data-display/InfoLabel.mjs +19 -18
- package/dist/components/data-display/InfoLabel.mjs.map +1 -1
- package/dist/components/feedback/MessageBarGroup.cjs +2 -0
- package/dist/components/feedback/MessageBarGroup.cjs.map +1 -0
- package/dist/components/feedback/MessageBarGroup.mjs +63 -0
- package/dist/components/feedback/MessageBarGroup.mjs.map +1 -0
- package/dist/components/feedback/ProgressBar.cjs +1 -1
- package/dist/components/feedback/ProgressBar.cjs.map +1 -1
- package/dist/components/feedback/ProgressBar.mjs +41 -39
- package/dist/components/feedback/ProgressBar.mjs.map +1 -1
- package/dist/components/feedback/Toaster.cjs +1 -1
- package/dist/components/feedback/Toaster.cjs.map +1 -1
- package/dist/components/feedback/Toaster.mjs +222 -216
- package/dist/components/feedback/Toaster.mjs.map +1 -1
- package/dist/components/layout/Accordion.cjs +1 -1
- package/dist/components/layout/Accordion.cjs.map +1 -1
- package/dist/components/layout/Accordion.mjs +124 -109
- package/dist/components/layout/Accordion.mjs.map +1 -1
- package/dist/components/layout/Carousel.cjs +1 -1
- package/dist/components/layout/Carousel.cjs.map +1 -1
- package/dist/components/layout/Carousel.mjs +207 -193
- package/dist/components/layout/Carousel.mjs.map +1 -1
- package/dist/components/layout/TabList.cjs +1 -1
- package/dist/components/layout/TabList.cjs.map +1 -1
- package/dist/components/layout/TabList.mjs +149 -108
- package/dist/components/layout/TabList.mjs.map +1 -1
- package/dist/components/layout/Tree.cjs.map +1 -1
- package/dist/components/layout/Tree.context.cjs +1 -1
- package/dist/components/layout/Tree.context.cjs.map +1 -1
- package/dist/components/layout/Tree.context.mjs +2 -1
- package/dist/components/layout/Tree.context.mjs.map +1 -1
- package/dist/components/layout/Tree.core.cjs +1 -1
- package/dist/components/layout/Tree.core.cjs.map +1 -1
- package/dist/components/layout/Tree.core.mjs +178 -165
- package/dist/components/layout/Tree.core.mjs.map +1 -1
- package/dist/components/layout/Tree.mjs.map +1 -1
- package/dist/components/navigation/Menu.popover.cjs +1 -1
- package/dist/components/navigation/Menu.popover.cjs.map +1 -1
- package/dist/components/navigation/Menu.popover.mjs +154 -168
- package/dist/components/navigation/Menu.popover.mjs.map +1 -1
- package/dist/components/navigation/Nav.cjs +1 -1
- package/dist/components/navigation/Nav.cjs.map +1 -1
- package/dist/components/navigation/Nav.mjs +156 -136
- package/dist/components/navigation/Nav.mjs.map +1 -1
- package/dist/components/navigation/NavDrawer.cjs +1 -1
- package/dist/components/navigation/NavDrawer.cjs.map +1 -1
- package/dist/components/navigation/NavDrawer.mjs +48 -45
- package/dist/components/navigation/NavDrawer.mjs.map +1 -1
- package/dist/components/overlays/Dialog.cjs +1 -1
- package/dist/components/overlays/Dialog.cjs.map +1 -1
- package/dist/components/overlays/Dialog.mjs +128 -122
- package/dist/components/overlays/Dialog.mjs.map +1 -1
- package/dist/components/overlays/Dialog.slots.cjs +1 -1
- package/dist/components/overlays/Dialog.slots.cjs.map +1 -1
- package/dist/components/overlays/Dialog.slots.mjs +12 -12
- package/dist/components/overlays/Dialog.slots.mjs.map +1 -1
- package/dist/components/overlays/Drawer.cjs.map +1 -1
- package/dist/components/overlays/Drawer.mjs.map +1 -1
- package/dist/components/overlays/Drawer.panel.cjs +1 -1
- package/dist/components/overlays/Drawer.panel.cjs.map +1 -1
- package/dist/components/overlays/Drawer.panel.mjs +97 -81
- package/dist/components/overlays/Drawer.panel.mjs.map +1 -1
- package/dist/components/overlays/Drawer.root.cjs +1 -1
- package/dist/components/overlays/Drawer.root.cjs.map +1 -1
- package/dist/components/overlays/Drawer.root.mjs +34 -31
- package/dist/components/overlays/Drawer.root.mjs.map +1 -1
- package/dist/components/overlays/Popover.cjs +1 -1
- package/dist/components/overlays/Popover.cjs.map +1 -1
- package/dist/components/overlays/Popover.mjs +231 -216
- package/dist/components/overlays/Popover.mjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.cjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.mjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.parts.cjs +1 -1
- package/dist/components/overlays/TeachingPopover.parts.cjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.parts.mjs +202 -173
- package/dist/components/overlays/TeachingPopover.parts.mjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.root.cjs +1 -1
- package/dist/components/overlays/TeachingPopover.root.cjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.root.mjs +108 -99
- package/dist/components/overlays/TeachingPopover.root.mjs.map +1 -1
- package/dist/components/overlays/Tooltip.cjs +1 -1
- package/dist/components/overlays/Tooltip.cjs.map +1 -1
- package/dist/components/overlays/Tooltip.mjs +66 -61
- package/dist/components/overlays/Tooltip.mjs.map +1 -1
- package/dist/components/provider/WaveProvider.cjs +1 -1
- package/dist/components/provider/WaveProvider.cjs.map +1 -1
- package/dist/components/provider/WaveProvider.mjs +26 -23
- package/dist/components/provider/WaveProvider.mjs.map +1 -1
- package/dist/hooks/useCollapsePresence.cjs +2 -0
- package/dist/hooks/useCollapsePresence.cjs.map +1 -0
- package/dist/hooks/useCollapsePresence.mjs +21 -0
- package/dist/hooks/useCollapsePresence.mjs.map +1 -0
- package/dist/hooks/useFocusBeforeClose.cjs +2 -0
- package/dist/hooks/useFocusBeforeClose.cjs.map +1 -0
- package/dist/hooks/useFocusBeforeClose.mjs +33 -0
- package/dist/hooks/useFocusBeforeClose.mjs.map +1 -0
- package/dist/hooks/useMotion.cjs +2 -0
- package/dist/hooks/useMotion.cjs.map +1 -0
- package/dist/hooks/useMotion.mjs +14 -0
- package/dist/hooks/useMotion.mjs.map +1 -0
- package/dist/hooks/usePresenceGroup.cjs +2 -0
- package/dist/hooks/usePresenceGroup.cjs.map +1 -0
- package/dist/hooks/usePresenceGroup.mjs +68 -0
- package/dist/hooks/usePresenceGroup.mjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +721 -131
- package/dist/index.d.ts +721 -131
- package/dist/index.mjs +136 -135
- package/dist/lib/classNames.cjs +1 -1
- package/dist/lib/classNames.cjs.map +1 -1
- package/dist/lib/classNames.mjs +101 -90
- package/dist/lib/classNames.mjs.map +1 -1
- package/dist/lib/motion.cjs +2 -0
- package/dist/lib/motion.cjs.map +1 -0
- package/dist/lib/motion.mjs +43 -0
- package/dist/lib/motion.mjs.map +1 -0
- package/dist/styles.css +1 -1
- package/package.json +2 -1
|
@@ -8,64 +8,67 @@ import { useControllable as o } from "../../hooks/useControllable.mjs";
|
|
|
8
8
|
import { useId as s } from "../../hooks/useId.mjs";
|
|
9
9
|
import { FOCUSABLE_SELECTOR as c, getFirstTabbable as l } from "../../lib/focus.mjs";
|
|
10
10
|
import { useMergedRefs as u } from "../../hooks/useMergedRefs.mjs";
|
|
11
|
-
import { useIsClient as
|
|
12
|
-
import { joinIds as
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import
|
|
18
|
-
import {
|
|
11
|
+
import { useIsClient as ee } from "../../hooks/useIsClient.mjs";
|
|
12
|
+
import { joinIds as d } from "../../lib/aria.mjs";
|
|
13
|
+
import { usePresence as te } from "../../hooks/usePresence.mjs";
|
|
14
|
+
import { resolvePositioning as ne, usePopupPosition as re } from "../../hooks/usePopupPosition.mjs";
|
|
15
|
+
import { useDismiss as ie } from "../../hooks/useDismiss.mjs";
|
|
16
|
+
import { Portal as ae } from "../portal/Portal.mjs";
|
|
17
|
+
import { tooltipMotion as oe } from "../../lib/motion.mjs";
|
|
18
|
+
import { useMotionClasses as f } from "../../hooks/useMotion.mjs";
|
|
19
|
+
import { STATE_ARIA as p, isCloneableElement as se, renderTrigger as ce, unwrapFragment as le } from "../../lib/renderTrigger.mjs";
|
|
20
|
+
import * as m from "react";
|
|
21
|
+
import { jsx as h, jsxs as ue } from "react/jsx-runtime";
|
|
19
22
|
//#region src/components/overlays/Tooltip.tsx
|
|
20
|
-
var
|
|
23
|
+
var de = {
|
|
21
24
|
dark: "inverted",
|
|
22
25
|
light: "normal"
|
|
23
|
-
},
|
|
26
|
+
}, fe = {
|
|
24
27
|
inverted: "border border-inverted-border bg-inverted text-inverted-foreground",
|
|
25
28
|
normal: "border border-border bg-background text-foreground"
|
|
26
|
-
},
|
|
29
|
+
}, pe = `max-w-60 whitespace-normal break-words rounded px-3 py-1.5 text-caption-1 font-normal normal-case tracking-normal text-start shadow-8 ${[
|
|
27
30
|
"before:absolute",
|
|
28
31
|
"data-[side=top]:before:inset-x-0 data-[side=top]:before:top-full data-[side=top]:before:h-2",
|
|
29
32
|
"data-[side=bottom]:before:inset-x-0 data-[side=bottom]:before:bottom-full data-[side=bottom]:before:h-2",
|
|
30
33
|
"data-[side=left]:before:inset-y-0 data-[side=left]:before:left-full data-[side=left]:before:w-2",
|
|
31
34
|
"data-[side=right]:before:inset-y-0 data-[side=right]:before:right-full data-[side=right]:before:w-2"
|
|
32
|
-
].join(" ")}`,
|
|
33
|
-
function
|
|
35
|
+
].join(" ")}`, g = /^on[A-Z]/, me = ["aria-label", "aria-labelledby"];
|
|
36
|
+
function _(e) {
|
|
34
37
|
return e.currentTarget.contains(e.target);
|
|
35
38
|
}
|
|
36
|
-
function
|
|
39
|
+
function v(e) {
|
|
37
40
|
return (t) => {
|
|
38
|
-
|
|
41
|
+
_(t) && e(t);
|
|
39
42
|
};
|
|
40
43
|
}
|
|
41
|
-
function
|
|
44
|
+
function he(e) {
|
|
42
45
|
let t = {}, n = {};
|
|
43
|
-
for (let [r, i] of Object.entries(e)) r === "id" || r.startsWith("aria-") ? t[r] = i : n[r] =
|
|
46
|
+
for (let [r, i] of Object.entries(e)) r === "id" || r.startsWith("aria-") ? t[r] = i : n[r] = g.test(r) && typeof i == "function" ? v(i) : i;
|
|
44
47
|
return {
|
|
45
48
|
childProps: t,
|
|
46
49
|
wrapperProps: n
|
|
47
50
|
};
|
|
48
51
|
}
|
|
49
|
-
function
|
|
52
|
+
function ge(e, t) {
|
|
50
53
|
let n = { ...e };
|
|
51
54
|
for (let e of t) delete n[e];
|
|
52
55
|
return n;
|
|
53
56
|
}
|
|
54
|
-
function
|
|
57
|
+
function y(e, t, n) {
|
|
55
58
|
return (e.getAttribute(t) ?? "").split(/\s+/).includes(n);
|
|
56
59
|
}
|
|
57
|
-
function
|
|
60
|
+
function b(e, t, n) {
|
|
58
61
|
let r = (e.getAttribute(t) ?? "").split(/\s+/).filter((e) => e && e !== n);
|
|
59
62
|
r.length > 0 ? e.setAttribute(t, r.join(" ")) : e.removeAttribute(t);
|
|
60
63
|
}
|
|
61
|
-
function
|
|
62
|
-
let a =
|
|
63
|
-
|
|
64
|
+
function _e(e, t, r, i) {
|
|
65
|
+
let a = m.useRef(null), o = r === "label" ? "aria-labelledby" : "aria-describedby";
|
|
66
|
+
m.useLayoutEffect(() => {
|
|
64
67
|
let r = e.current;
|
|
65
68
|
if (!r) return;
|
|
66
69
|
let s = () => {
|
|
67
|
-
let e = a.current, s = Array.from(r.querySelectorAll(`[${o}]`)).filter((n) => !(e && e.element === n && e.attribute === o) &&
|
|
68
|
-
e && (e.element !== s || e.attribute !== o) && (
|
|
70
|
+
let e = a.current, s = Array.from(r.querySelectorAll(`[${o}]`)).filter((n) => !(e && e.element === n && e.attribute === o) && y(n, o, t)).some((e) => e.matches(c)) ? null : l(r);
|
|
71
|
+
e && (e.element !== s || e.attribute !== o) && (b(e.element, e.attribute, t), a.current = null), s && (y(s, o, t) || s.setAttribute(o, d(s.getAttribute(o), t) ?? ""), a.current = {
|
|
69
72
|
element: s,
|
|
70
73
|
attribute: o
|
|
71
74
|
}, i || n("Tooltip:focus-target", `Tooltip: the element that takes focus inside it did not get \`${o}\` (the child is not focusable, or is a component, such as a Popover or Dialog root, that does not pass the prop on), so it was added to the first focusable element inside. Put the Tooltip directly around the focusable element, e.g. <Popover.Trigger><Tooltip content="…"><Button /></Tooltip></Popover.Trigger>.`));
|
|
@@ -80,82 +83,84 @@ function pe(e, t, r, i) {
|
|
|
80
83
|
}), () => u.disconnect();
|
|
81
84
|
});
|
|
82
85
|
}
|
|
83
|
-
var
|
|
84
|
-
let
|
|
86
|
+
var x = ({ content: n, appearance: c, variant: l, openDelay: g, closeDelay: y = 100, delay: b, open: x, defaultOpen: S, onOpenChange: C, relationship: w = "description", side: T = "top", align: E = "center", positioning: D, motion: O, children: k, className: A, onMouseEnter: j, onMouseLeave: M, onFocus: N, onBlur: P, ref: F, ...I }) => {
|
|
87
|
+
let ve = t("Tooltip", c, l === void 0 ? void 0 : de[l], "variant", "appearance") ?? "inverted", L = t("Tooltip", g, b, "delay", "openDelay") ?? 200, [ye, R] = o(x, S ?? !1, C), be = ee(), z = ye && be, B = m.useRef(void 0), V = m.useRef(null), H = m.useRef(null), U = s("tooltip"), { isMounted: xe, ref: Se, presenceProps: Ce } = te(z, { appear: !0 }), we = f(O, oe), { setReference: Te, setFloating: Ee, floatingProps: De } = re({
|
|
85
88
|
open: z,
|
|
86
|
-
side:
|
|
87
|
-
align:
|
|
88
|
-
...
|
|
89
|
-
}),
|
|
89
|
+
side: T,
|
|
90
|
+
align: E,
|
|
91
|
+
...ne(D, { offset: 8 })
|
|
92
|
+
}), Oe = u(F, V, Te), ke = u(H, Ee, Se), W = a(() => {
|
|
90
93
|
B.current !== void 0 && (clearTimeout(B.current), B.current = void 0);
|
|
91
94
|
}), G = a(() => {
|
|
92
95
|
if (W(), !z) {
|
|
93
|
-
if (
|
|
96
|
+
if (L <= 0) {
|
|
94
97
|
R(!0);
|
|
95
98
|
return;
|
|
96
99
|
}
|
|
97
100
|
B.current = setTimeout(() => {
|
|
98
101
|
B.current = void 0, R(!0);
|
|
99
|
-
},
|
|
102
|
+
}, L);
|
|
100
103
|
}
|
|
101
104
|
}), K = a(() => {
|
|
102
105
|
W(), R(!1);
|
|
103
|
-
}),
|
|
106
|
+
}), q = a(() => {
|
|
104
107
|
W(), z && (B.current = setTimeout(() => {
|
|
105
108
|
B.current = void 0, R(!1);
|
|
106
|
-
},
|
|
109
|
+
}, y));
|
|
107
110
|
});
|
|
108
|
-
|
|
111
|
+
ie({
|
|
109
112
|
open: z,
|
|
110
113
|
onDismiss: K,
|
|
111
114
|
refs: [V, H],
|
|
112
115
|
anchorRef: V,
|
|
113
116
|
kind: "tooltip",
|
|
114
117
|
outsidePress: !1
|
|
115
|
-
}),
|
|
118
|
+
}), m.useEffect(() => {
|
|
116
119
|
let e = B;
|
|
117
120
|
return () => clearTimeout(e.current);
|
|
118
121
|
}, []);
|
|
119
|
-
let
|
|
120
|
-
(
|
|
121
|
-
}),
|
|
122
|
-
!
|
|
123
|
-
}), { childProps: J, wrapperProps: Y } =
|
|
124
|
-
(!Z ||
|
|
125
|
-
let
|
|
126
|
-
return
|
|
127
|
-
ref:
|
|
122
|
+
let Ae = a((e) => {
|
|
123
|
+
(_(e) || H.current?.contains(e.target)) && G();
|
|
124
|
+
}), je = a((e) => {
|
|
125
|
+
!_(e) && !H.current?.contains(e.target) && K();
|
|
126
|
+
}), { childProps: J, wrapperProps: Y } = he(I), X = le(k), Z = !se(X), Q = w === "label" ? "aria-labelledby" : "aria-describedby", $ = Z ? ge(J, [...p, ...me]) : { ...J };
|
|
127
|
+
(!Z || w === "description") && ($[Q] = d(J[Q], U));
|
|
128
|
+
let Me = ce(X, $, { componentName: "Tooltip" });
|
|
129
|
+
return _e(V, U, w, Z), /* @__PURE__ */ ue("span", {
|
|
130
|
+
ref: Oe,
|
|
128
131
|
...Y,
|
|
129
|
-
className: e(i.root, "inline-block",
|
|
130
|
-
onMouseEnter: r(
|
|
131
|
-
onMouseLeave: r(
|
|
132
|
-
onMouseOver: r(Y.onMouseOver,
|
|
133
|
-
onFocus:
|
|
134
|
-
onBlur:
|
|
132
|
+
className: e(i.root, "inline-block", A),
|
|
133
|
+
onMouseEnter: r(j, Ae),
|
|
134
|
+
onMouseLeave: r(M, q),
|
|
135
|
+
onMouseOver: r(Y.onMouseOver, je),
|
|
136
|
+
onFocus: v(r(N, G)),
|
|
137
|
+
onBlur: v(r(P, K)),
|
|
135
138
|
children: [
|
|
136
|
-
|
|
137
|
-
/* @__PURE__ */
|
|
139
|
+
Me,
|
|
140
|
+
/* @__PURE__ */ h("span", {
|
|
138
141
|
id: U,
|
|
139
142
|
role: "tooltip",
|
|
140
143
|
hidden: !0,
|
|
141
144
|
children: n
|
|
142
145
|
}),
|
|
143
|
-
|
|
146
|
+
xe && /* @__PURE__ */ h(ae, {
|
|
144
147
|
layer: "tooltip",
|
|
145
|
-
children: /* @__PURE__ */
|
|
146
|
-
ref:
|
|
148
|
+
children: /* @__PURE__ */ h("span", {
|
|
149
|
+
ref: ke,
|
|
147
150
|
"aria-hidden": "true",
|
|
148
151
|
"data-wave-tooltip-surface": "",
|
|
149
|
-
|
|
150
|
-
|
|
152
|
+
"data-state": z ? "open" : "closed",
|
|
153
|
+
...Ce,
|
|
154
|
+
...De,
|
|
155
|
+
className: e(i.content, pe, fe[ve], we),
|
|
151
156
|
children: n
|
|
152
157
|
})
|
|
153
158
|
})
|
|
154
159
|
]
|
|
155
160
|
});
|
|
156
161
|
};
|
|
157
|
-
|
|
162
|
+
x.displayName = "Tooltip";
|
|
158
163
|
//#endregion
|
|
159
|
-
export {
|
|
164
|
+
export { x as Tooltip };
|
|
160
165
|
|
|
161
166
|
//# sourceMappingURL=Tooltip.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tooltip.mjs","names":["useId","useId$1","React","VARIANT_APPEARANCE","appearanceClasses","surfaceClasses","HANDLER_KEY","NAMING_ARIA","isOwnEvent","event","ownEventsOnly","handler","splitProps","childProps","wrapperProps","key","value","props","omit","result","keys","hasIdRef","el","attribute","id","removeIdRef","rest","useRelationshipTarget","appliedRef","relationship","wrapper","wrapperRef","update","applied","target","tooltipId","FOCUSABLE_SELECTOR","getFirstTabbable","joinIds","childIsFallback","warnOnce","observer","Tooltip","appearanceProp","openDelayProp","openProp","appearance","resolveDeprecatedProp","variant","openDelay","delay","open","setOpen","useControllable","defaultOpen","onOpenChange","isClient","useIsClient","visible","timerRef","surfaceRef","usePopupPosition","resolvePositioning","positioning","wrapperElementRef","useMergedRefs","ref","setReference","surfaceElementRef","setFloating","clearTimer","useEventCallback","show","hide","scheduleHide","closeDelay","useDismiss","timer","showOnEnter","hideOverPortaledPopup","unwrapFragment","children","isCloneableElement","triggerProps","STATE_ARIA","child","renderTrigger","cn","tooltipClassNames","className","composeEventHandlers","onMouseEnter","onMouseLeave","onFocus","onBlur","content","Portal","floatingProps"],"sources":["../../../src/components/overlays/Tooltip.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { joinIds } from '../../lib/aria';\nimport { tooltipClassNames } from '../../lib/classNames';\nimport { resolveDeprecatedProp, warnOnce } from '../../lib/dev';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { FOCUSABLE_SELECTOR, getFirstTabbable } from '../../lib/focus';\nimport {\n isCloneableElement,\n renderTrigger,\n STATE_ARIA,\n unwrapFragment,\n} from '../../lib/renderTrigger';\nimport type { PopupAlign, PopupPositioningOptions, PopupSide } from '../../lib/types';\nimport { useControllable } from '../../hooks/useControllable';\nimport { useId } from '../../hooks/useId';\nimport { useIsClient } from '../../hooks/useIsClient';\nimport { useDismiss } from '../../hooks/useDismiss';\nimport { resolvePositioning, usePopupPosition } from '../../hooks/usePopupPosition';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { useEventCallback } from '../../hooks/useEventCallback';\nimport { Portal } from '../portal/Portal';\n\n/** Color treatment of a tooltip (`TooltipProps.appearance`). */\nexport type TooltipAppearance = 'inverted' | 'normal';\n\n/** Properties for the Tooltip component. */\nexport interface TooltipProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'content'> {\n /** Content to display inside the tooltip (short, non-interactive text). */\n content: React.ReactNode;\n /**\n * Color treatment: `inverted` (dark surface in the light theme) or `normal` (the page\n * background with a border).\n * @default 'inverted'\n */\n appearance?: TooltipAppearance;\n /**\n * @deprecated Use `appearance` (`'dark'` → `'inverted'`, `'light'` → `'normal'`).\n */\n variant?: 'dark' | 'light';\n /**\n * Milliseconds before the tooltip appears when the pointer rests on the child or the child\n * receives keyboard focus (Fluent's `showDelay`; unlike Menu's and Popover's `openDelay`, it\n * applies to focus too).\n * @default 200\n */\n openDelay?: number;\n /**\n * Milliseconds before it hides once the pointer has left the child and the tooltip (Fluent's\n * `hideDelay`); blur and Escape hide it at once.\n * @default 100\n */\n closeDelay?: number;\n /** @deprecated Use `openDelay`. */\n delay?: number;\n /**\n * Controlled open state (Fluent's `visible`). The tooltip still asks to open on hover and focus\n * (after `openDelay`) and to close on leave, blur and Escape through `onOpenChange`. The surface\n * renders only in the browser: an open tooltip is closed in the server HTML and opens once it\n * has hydrated.\n */\n open?: boolean;\n /**\n * Initial open state for uncontrolled usage.\n * @default false\n */\n defaultOpen?: boolean;\n /**\n * Called with the new open state when it changes (Fluent's `onVisibleChange`). It has no\n * `details` argument yet.\n */\n onOpenChange?: (open: boolean) => void;\n /**\n * How the tooltip relates to its child: `description` adds it to the child's\n * `aria-describedby`; `label` adds it to the child's `aria-labelledby`, naming an icon-only\n * button.\n * @default 'description'\n */\n relationship?: 'description' | 'label';\n /**\n * Side of the target the tooltip opens on. `start`/`end` follow the writing direction; the\n * tooltip flips to the opposite side when there is not enough room.\n * @default 'top'\n */\n side?: PopupSide;\n /** Alignment along the target's edge.\n * @default 'center'\n */\n align?: PopupAlign;\n /**\n * Advanced positioning (Fluent's `positioning`): `offset`, `fitViewport`, `matchReferenceWidth`\n * and `fallbackPlacements`. `side` and `align` stay separate props. The hover bridge across the\n * gap covers the default 8px, so a larger `offset` leaves a gap the pointer cannot cross.\n * @default `{ offset: 8 }`\n */\n positioning?: PopupPositioningOptions;\n /**\n * A single element that triggers the tooltip on hover and focus: it receives the\n * `aria-describedby`/`aria-labelledby`, plus any `id` and `aria-*` props given to the Tooltip\n * (a Fragment around one element counts as that element). Other children are wrapped in a\n * described `<span>` with a development warning, and their first focusable element gets the\n * relationship.\n */\n children: React.ReactElement;\n /** Ref to the wrapper `<span>` (the positioning anchor). */\n ref?: React.Ref<HTMLSpanElement>;\n}\n\nconst VARIANT_APPEARANCE: Record<'dark' | 'light', TooltipAppearance> = {\n dark: 'inverted',\n light: 'normal',\n};\n\nconst appearanceClasses: Record<TooltipAppearance, string> = {\n inverted: 'border border-inverted-border bg-inverted text-inverted-foreground',\n normal: 'border border-border bg-background text-foreground',\n};\n\n/**\n * Transparent `::before` hover bridge across the 8px gap (the positioning offset) on the side that\n * faces the trigger only, so the pointer can move onto the tooltip; the other sides get no bridge,\n * which would otherwise catch hovers and clicks meant for neighbouring controls.\n */\nconst bridgeClasses = [\n 'before:absolute',\n // Tooltip above the trigger: a strip below it.\n 'data-[side=top]:before:inset-x-0 data-[side=top]:before:top-full data-[side=top]:before:h-2',\n // Tooltip below the trigger: a strip above it.\n 'data-[side=bottom]:before:inset-x-0 data-[side=bottom]:before:bottom-full data-[side=bottom]:before:h-2',\n // wave-allow-physical: data-side is the physical side resolved by the positioning\n 'data-[side=left]:before:inset-y-0 data-[side=left]:before:left-full data-[side=left]:before:w-2',\n // wave-allow-physical: data-side is the physical side resolved by the positioning\n 'data-[side=right]:before:inset-y-0 data-[side=right]:before:right-full data-[side=right]:before:w-2',\n].join(' ');\n\n/**\n * The visual surface: wrapping text, typography reset against the host (header cells, muted\n * panels), and the hover bridge towards the trigger.\n */\nconst surfaceClasses = `max-w-60 whitespace-normal break-words rounded px-3 py-1.5 text-caption-1 font-normal normal-case tracking-normal text-start shadow-8 ${bridgeClasses}`;\n\ntype UnknownProps = Record<string, unknown>;\ntype Relationship = NonNullable<TooltipProps['relationship']>;\n\nconst HANDLER_KEY = /^on[A-Z]/;\n\n/** Naming attributes a generic element such as the fallback `<span>` must not carry. */\nconst NAMING_ARIA = ['aria-label', 'aria-labelledby'] as const;\n\n/**\n * Whether an event reached the wrapper from its own DOM subtree — the child — rather than through\n * a portal: React bubbles events from portaled content (the tooltip surface, a popup the child\n * renders in a portal) through the component tree.\n */\nfunction isOwnEvent(event: React.SyntheticEvent): boolean {\n return (event.currentTarget as Node).contains(event.target as Node);\n}\n\n/** `handler`, called only for the wrapper's own events (see {@link isOwnEvent}). */\nfunction ownEventsOnly<E extends React.SyntheticEvent>(\n handler: (event: E) => void,\n): (event: E) => void {\n return (event) => {\n if (isOwnEvent(event)) handler(event);\n };\n}\n\n/**\n * Splits the props the Tooltip receives, from its consumer or from a parent that clones its child\n * (Menu.Trigger, Popover.Trigger, Dialog.Trigger, Field): `id` and `aria-*` describe the element\n * the Tooltip is attached to, so they go to the child; the rest stays on the wrapper `<span>`.\n */\nfunction splitProps(props: UnknownProps): { childProps: UnknownProps; wrapperProps: UnknownProps } {\n const childProps: UnknownProps = {};\n const wrapperProps: UnknownProps = {};\n for (const [key, value] of Object.entries(props)) {\n if (key === 'id' || key.startsWith('aria-')) childProps[key] = value;\n else if (HANDLER_KEY.test(key) && typeof value === 'function') {\n // The wrapper sees the child's events as they bubble, but not those that reach it through\n // a portal, such as a click on the tooltip surface (it would toggle a parent trigger).\n wrapperProps[key] = ownEventsOnly(value as (event: React.SyntheticEvent) => void);\n } else wrapperProps[key] = value;\n }\n return { childProps, wrapperProps };\n}\n\nfunction omit(props: UnknownProps, keys: readonly string[]): UnknownProps {\n const result = { ...props };\n for (const key of keys) delete result[key];\n return result;\n}\n\nfunction hasIdRef(el: Element, attribute: string, id: string): boolean {\n return (el.getAttribute(attribute) ?? '').split(/\\s+/).includes(id);\n}\n\nfunction removeIdRef(el: Element, attribute: string, id: string): void {\n const rest = (el.getAttribute(attribute) ?? '')\n .split(/\\s+/)\n .filter((value) => value && value !== id);\n if (rest.length > 0) el.setAttribute(attribute, rest.join(' '));\n else el.removeAttribute(attribute);\n}\n\n/**\n * Makes sure the element people focus carries the tooltip relationship. After each commit, when no\n * focusable element inside the wrapper references `tooltipId` through `attribute` — children in the\n * fallback span, a non-focusable child element, or a child component that drops the prop (a\n * Popover or Dialog root) — the id is added to the first tabbable element inside the wrapper and\n * kept there while the subtree changes; a development warning explains the nesting, except for\n * the fallback span, whose children shape `renderTrigger` already warned about.\n */\nfunction useRelationshipTarget(\n wrapperRef: React.RefObject<HTMLSpanElement | null>,\n tooltipId: string,\n relationship: Relationship,\n childIsFallback: boolean,\n): void {\n const appliedRef = React.useRef<{ element: Element; attribute: string } | null>(null);\n const attribute = relationship === 'label' ? 'aria-labelledby' : 'aria-describedby';\n\n React.useLayoutEffect(() => {\n const wrapper = wrapperRef.current;\n if (!wrapper) return;\n\n const update = () => {\n const applied = appliedRef.current;\n const carriers = Array.from(wrapper.querySelectorAll(`[${attribute}]`)).filter(\n (el) =>\n !(applied && applied.element === el && applied.attribute === attribute) &&\n hasIdRef(el, attribute, tooltipId),\n );\n const reached = carriers.some((el) => el.matches(FOCUSABLE_SELECTOR));\n const target = reached ? null : getFirstTabbable(wrapper);\n if (applied && (applied.element !== target || applied.attribute !== attribute)) {\n removeIdRef(applied.element, applied.attribute, tooltipId);\n appliedRef.current = null;\n }\n if (!target) return;\n if (!hasIdRef(target, attribute, tooltipId)) {\n target.setAttribute(attribute, joinIds(target.getAttribute(attribute), tooltipId) ?? '');\n }\n appliedRef.current = { element: target, attribute };\n if (!childIsFallback) {\n warnOnce(\n 'Tooltip:focus-target',\n `Tooltip: the element that takes focus inside it did not get \\`${attribute}\\` (the child is not focusable, or is a component, such as a Popover or Dialog root, that does not pass the prop on), so it was added to the first focusable element inside. Put the Tooltip directly around the focusable element, e.g. <Popover.Trigger><Tooltip content=\"…\"><Button /></Tooltip></Popover.Trigger>.`,\n );\n }\n };\n\n update();\n if (!appliedRef.current || typeof MutationObserver === 'undefined') return;\n // Keep it on the focusable element when the child replaces it or rewrites the attribute\n // without the Tooltip re-rendering.\n const observer = new MutationObserver(update);\n observer.observe(wrapper, {\n subtree: true,\n childList: true,\n attributes: true,\n attributeFilter: [attribute],\n });\n return () => observer.disconnect();\n });\n}\n\n/**\n * Shows a short text label or description for its child on hover and keyboard focus.\n *\n * - The text is always present as a hidden `role=\"tooltip\"` element referenced by the child's\n * `aria-describedby` (or `aria-labelledby` with `relationship=\"label\"`), so screen readers get\n * it on focus without waiting for the delay; the child's own ids are kept.\n * - The visual surface is rendered in a portal only while visible (it is `aria-hidden`, a copy of\n * the description), positioned on `side`/`align` with flipping and shifting to stay in view.\n * - It appears `openDelay` after the pointer rests on the child or the child takes keyboard focus,\n * stays visible while the pointer moves onto it, hides `closeDelay` after the pointer leaves or\n * immediately on blur, and Escape hides it without closing an enclosing dialog or popover. Focus\n * and hover inside a popup the child renders in a portal (a DatePicker calendar, a listbox) do\n * not show it, and the pointer moving onto such a popup hides it.\n * - The open state is uncontrolled by default (`defaultOpen`). With `open` and `onOpenChange`\n * (Fluent's `visible` and `onVisibleChange`) the parent owns it: hover, focus, leave, blur and\n * Escape then only ask for a change, and `open` alone shows the surface.\n * - `id` and `aria-*` props given to the Tooltip go to its child, merged with the child's own (the\n * child's `id` wins, id lists are joined, `aria-expanded`/`aria-controls`/`aria-haspopup` from\n * the Tooltip win). `className`, `style`, `ref`, `data-*` and event handlers stay on the wrapper\n * `<span>`; the handlers see the child's events as they bubble, not events from portaled\n * content such as the tooltip surface or a popup the child renders in a portal (a DatePicker\n * calendar, a listbox). `onMouseEnter` and `onMouseLeave` are the exception: React fires them\n * along the component tree, so they also fire when the pointer moves onto or off the tooltip\n * surface or such a popup. So the Tooltip can sit inside a trigger, and the trigger's\n * id and state reach the button: `<Menu.Trigger><Tooltip content=\"…\"><Button /></Tooltip>\n * </Menu.Trigger>` (also `Popover.Trigger`, `Dialog.Trigger`, `Drawer.Trigger`). Around a\n * trigger works too, since triggers pass `aria-describedby` on to their child.\n * - Wrap the focusable element itself. When the element that takes focus does not get the\n * relationship (a Popover or Dialog root, a component that drops props, a non-focusable\n * wrapper element), it is added to the first focusable element inside, with a development\n * warning.\n *\n * @example\n * <Menu>\n * <Menu.Trigger>\n * <Tooltip content=\"Edit, duplicate or delete the item\">\n * <Button>Actions</Button>\n * </Tooltip>\n * </Menu.Trigger>\n * <Menu.Popover>…</Menu.Popover>\n * </Menu>\n */\nexport const Tooltip = ({\n content,\n appearance: appearanceProp,\n variant,\n openDelay: openDelayProp,\n closeDelay = 100,\n delay,\n open: openProp,\n defaultOpen,\n onOpenChange,\n relationship = 'description',\n side = 'top',\n align = 'center',\n positioning,\n children,\n className,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ref,\n ...rest\n}: TooltipProps) => {\n const appearance =\n resolveDeprecatedProp(\n 'Tooltip',\n appearanceProp,\n variant === undefined ? undefined : VARIANT_APPEARANCE[variant],\n 'variant',\n 'appearance',\n ) ?? 'inverted';\n const openDelay =\n resolveDeprecatedProp('Tooltip', openDelayProp, delay, 'delay', 'openDelay') ?? 200;\n const [open, setOpen] = useControllable(openProp, defaultOpen ?? false, onOpenChange);\n // The surface lives in a portal, which renders only in the browser: until then (the server HTML,\n // hydration) the tooltip is closed; the hidden description is rendered either way.\n const isClient = useIsClient();\n const visible = open && isClient;\n const timerRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const wrapperRef = React.useRef<HTMLSpanElement | null>(null);\n const surfaceRef = React.useRef<HTMLSpanElement | null>(null);\n const tooltipId = useId('tooltip');\n\n // Destructured: react-hooks/refs treats an object whose member is passed to `ref` as a ref.\n const { setReference, setFloating, floatingProps } = usePopupPosition({\n open: visible,\n side,\n align,\n ...resolvePositioning(positioning, { offset: 8 }),\n });\n const wrapperElementRef = useMergedRefs<HTMLSpanElement>(ref, wrapperRef, setReference);\n const surfaceElementRef = useMergedRefs<HTMLSpanElement>(surfaceRef, setFloating);\n\n // Timer handlers are event callbacks (stable, latest state): they are composed with the\n // consumer's handlers during render but only run from events and timers.\n const clearTimer = useEventCallback(() => {\n if (timerRef.current !== undefined) {\n clearTimeout(timerRef.current);\n timerRef.current = undefined;\n }\n });\n\n // show, hide and scheduleHide ask for the new state; a controlled tooltip changes only when the\n // parent updates `open`.\n const show = useEventCallback(() => {\n clearTimer();\n if (visible) return;\n if (openDelay <= 0) {\n setOpen(true);\n return;\n }\n timerRef.current = setTimeout(() => {\n timerRef.current = undefined;\n setOpen(true);\n }, openDelay);\n });\n\n const hide = useEventCallback(() => {\n clearTimer();\n setOpen(false);\n });\n\n // The grace period after the pointer leaves lets it move onto the tooltip.\n const scheduleHide = useEventCallback(() => {\n clearTimer();\n if (!visible) return;\n timerRef.current = setTimeout(() => {\n timerRef.current = undefined;\n setOpen(false);\n }, closeDelay);\n });\n\n // An Escape-only layer while visible: Escape hides the tooltip before any enclosing overlay. The\n // wrapper is its anchor (its trigger, not its surface): Tab from the child inside a focus trap\n // moves on from the popover, portal or dialog around the wrapper.\n useDismiss({\n open: visible,\n onDismiss: hide,\n refs: [wrapperRef, surfaceRef],\n anchorRef: wrapperRef,\n kind: 'tooltip',\n outsidePress: false,\n });\n\n React.useEffect(() => {\n const timer = timerRef;\n return () => clearTimeout(timer.current);\n }, []);\n\n // The tooltip's own reactions ignore a popup the child renders in a portal: its focus and blur\n // are not the child's, entering it does not show the tooltip, and the pointer moving onto it\n // (no mouseleave reaches the wrapper then) hides the tooltip, which would cover it. The tooltip\n // surface itself is portaled too but belongs to the tooltip: it stays hoverable.\n const showOnEnter = useEventCallback((event: React.MouseEvent<HTMLSpanElement>) => {\n if (isOwnEvent(event) || surfaceRef.current?.contains(event.target as Node)) show();\n });\n const hideOverPortaledPopup = useEventCallback((event: React.MouseEvent<HTMLSpanElement>) => {\n if (!isOwnEvent(event) && !surfaceRef.current?.contains(event.target as Node)) hide();\n });\n\n const { childProps, wrapperProps } = splitProps(rest);\n const target = unwrapFragment(children);\n const childIsFallback = !isCloneableElement(target);\n const attribute = relationship === 'label' ? 'aria-labelledby' : 'aria-describedby';\n // Props given to the Tooltip come first, as if the child had them; then the tooltip's own id.\n // The fallback span is a generic element: no state ARIA and no naming on it (the relationship\n // then reaches its first focusable element through useRelationshipTarget).\n const triggerProps: UnknownProps = childIsFallback\n ? omit(childProps, [...STATE_ARIA, ...NAMING_ARIA])\n : { ...childProps };\n if (!childIsFallback || relationship === 'description') {\n triggerProps[attribute] = joinIds(childProps[attribute] as string | undefined, tooltipId);\n }\n const child = renderTrigger(target, triggerProps, { componentName: 'Tooltip' });\n useRelationshipTarget(wrapperRef, tooltipId, relationship, childIsFallback);\n\n return (\n <span\n ref={wrapperElementRef}\n {...wrapperProps}\n className={cn(tooltipClassNames.root, 'inline-block', className)}\n onMouseEnter={composeEventHandlers(onMouseEnter, showOnEnter)}\n onMouseLeave={composeEventHandlers(onMouseLeave, scheduleHide)}\n onMouseOver={composeEventHandlers(\n wrapperProps.onMouseOver as React.MouseEventHandler<HTMLSpanElement> | undefined,\n hideOverPortaledPopup,\n )}\n onFocus={ownEventsOnly(composeEventHandlers(onFocus, show))}\n onBlur={ownEventsOnly(composeEventHandlers(onBlur, hide))}\n >\n {child}\n <span id={tooltipId} role=\"tooltip\" hidden>\n {content}\n </span>\n {visible && (\n <Portal layer=\"tooltip\">\n <span\n ref={surfaceElementRef}\n aria-hidden=\"true\"\n data-wave-tooltip-surface=\"\"\n {...floatingProps}\n className={cn(tooltipClassNames.content, surfaceClasses, appearanceClasses[appearance])}\n >\n {content}\n </span>\n </Portal>\n )}\n </span>\n );\n};\n\nTooltip.displayName = 'Tooltip';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Tooltip.mjs","names":["useId","useId$1","React","VARIANT_APPEARANCE","appearanceClasses","surfaceClasses","HANDLER_KEY","NAMING_ARIA","isOwnEvent","event","ownEventsOnly","handler","splitProps","childProps","wrapperProps","key","value","props","omit","result","keys","hasIdRef","el","attribute","id","removeIdRef","rest","useRelationshipTarget","appliedRef","relationship","wrapper","wrapperRef","update","applied","target","tooltipId","FOCUSABLE_SELECTOR","getFirstTabbable","joinIds","childIsFallback","warnOnce","observer","Tooltip","appearanceProp","openDelayProp","openProp","appearance","resolveDeprecatedProp","variant","openDelay","delay","open","setOpen","useControllable","defaultOpen","onOpenChange","isClient","useIsClient","visible","timerRef","surfaceRef","presenceRef","usePresence","motionClassName","useMotionClasses","motion","tooltipMotion","usePopupPosition","resolvePositioning","positioning","wrapperElementRef","useMergedRefs","ref","setReference","surfaceElementRef","setFloating","clearTimer","useEventCallback","show","hide","scheduleHide","closeDelay","useDismiss","timer","showOnEnter","hideOverPortaledPopup","unwrapFragment","children","isCloneableElement","triggerProps","STATE_ARIA","child","renderTrigger","cn","tooltipClassNames","className","composeEventHandlers","onMouseEnter","onMouseLeave","onFocus","onBlur","content","isMounted","Portal","presenceProps","floatingProps"],"sources":["../../../src/components/overlays/Tooltip.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { joinIds } from '../../lib/aria';\nimport { tooltipClassNames } from '../../lib/classNames';\nimport { resolveDeprecatedProp, warnOnce } from '../../lib/dev';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { FOCUSABLE_SELECTOR, getFirstTabbable } from '../../lib/focus';\nimport { tooltipMotion } from '../../lib/motion';\nimport {\n isCloneableElement,\n renderTrigger,\n STATE_ARIA,\n unwrapFragment,\n} from '../../lib/renderTrigger';\nimport type { MotionSlot, PopupAlign, PopupPositioningOptions, PopupSide } from '../../lib/types';\nimport { useControllable } from '../../hooks/useControllable';\nimport { useId } from '../../hooks/useId';\nimport { useIsClient } from '../../hooks/useIsClient';\nimport { useDismiss } from '../../hooks/useDismiss';\nimport { resolvePositioning, usePopupPosition } from '../../hooks/usePopupPosition';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { useMotionClasses } from '../../hooks/useMotion';\nimport { usePresence } from '../../hooks/usePresence';\nimport { useEventCallback } from '../../hooks/useEventCallback';\nimport { Portal } from '../portal/Portal';\n\n/** Color treatment of a tooltip (`TooltipProps.appearance`). */\nexport type TooltipAppearance = 'inverted' | 'normal';\n\n/** Properties for the Tooltip component. */\nexport interface TooltipProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'content'> {\n /** Content to display inside the tooltip (short, non-interactive text). */\n content: React.ReactNode;\n /**\n * Color treatment: `inverted` (dark surface in the light theme) or `normal` (the page\n * background with a border).\n * @default 'inverted'\n */\n appearance?: TooltipAppearance;\n /**\n * @deprecated Use `appearance` (`'dark'` → `'inverted'`, `'light'` → `'normal'`).\n */\n variant?: 'dark' | 'light';\n /**\n * Milliseconds before the tooltip appears when the pointer rests on the child or the child\n * receives keyboard focus (Fluent's `showDelay`; unlike Menu's and Popover's `openDelay`, it\n * applies to focus too).\n * @default 200\n */\n openDelay?: number;\n /**\n * Milliseconds before it hides once the pointer has left the child and the tooltip (Fluent's\n * `hideDelay`); blur and Escape hide it at once.\n * @default 100\n */\n closeDelay?: number;\n /** @deprecated Use `openDelay`. */\n delay?: number;\n /**\n * Controlled open state (Fluent's `visible`). The tooltip still asks to open on hover and focus\n * (after `openDelay`) and to close on leave, blur and Escape through `onOpenChange`. The surface\n * renders only in the browser: an open tooltip is closed in the server HTML and opens once it\n * has hydrated.\n */\n open?: boolean;\n /**\n * Initial open state for uncontrolled usage.\n * @default false\n */\n defaultOpen?: boolean;\n /**\n * Called with the new open state when it changes (Fluent's `onVisibleChange`). It has no\n * `details` argument yet.\n */\n onOpenChange?: (open: boolean) => void;\n /**\n * How the tooltip relates to its child: `description` adds it to the child's\n * `aria-describedby`; `label` adds it to the child's `aria-labelledby`, naming an icon-only\n * button.\n * @default 'description'\n */\n relationship?: 'description' | 'label';\n /**\n * Side of the target the tooltip opens on. `start`/`end` follow the writing direction; the\n * tooltip flips to the opposite side when there is not enough room.\n * @default 'top'\n */\n side?: PopupSide;\n /** Alignment along the target's edge.\n * @default 'center'\n */\n align?: PopupAlign;\n /**\n * Advanced positioning (Fluent's `positioning`): `offset`, `fitViewport`, `matchReferenceWidth`\n * and `fallbackPlacements`. `side` and `align` stay separate props. The hover bridge across the\n * gap covers the default 8px, so a larger `offset` leaves a gap the pointer cannot cross.\n * @default `{ offset: 8 }`\n */\n positioning?: PopupPositioningOptions;\n /**\n * The motion of the tooltip's surface (the portaled box; `className` stays on the wrapper\n * `<span>`). By default it fades in over 100 ms and out over 100 ms with the easy-ease curve,\n * without a slide; under reduced motion it appears and disappears at once. `null` turns the\n * motion off (the surface still renders its `data-presence` phases, which then end at once); a\n * class string or `{ className }` replaces the default classes with yours (see `MotionSlot`),\n * also under a `WaveProvider` with `motion=\"none\"`, which otherwise turns the default off. A\n * WaveUI addition: Fluent's Tooltip has no motion.\n * @default the fade above\n */\n motion?: MotionSlot | null;\n /**\n * A single element that triggers the tooltip on hover and focus: it receives the\n * `aria-describedby`/`aria-labelledby`, plus any `id` and `aria-*` props given to the Tooltip\n * (a Fragment around one element counts as that element). Other children are wrapped in a\n * described `<span>` with a development warning, and their first focusable element gets the\n * relationship.\n */\n children: React.ReactElement;\n /** Ref to the wrapper `<span>` (the positioning anchor). */\n ref?: React.Ref<HTMLSpanElement>;\n}\n\nconst VARIANT_APPEARANCE: Record<'dark' | 'light', TooltipAppearance> = {\n dark: 'inverted',\n light: 'normal',\n};\n\nconst appearanceClasses: Record<TooltipAppearance, string> = {\n inverted: 'border border-inverted-border bg-inverted text-inverted-foreground',\n normal: 'border border-border bg-background text-foreground',\n};\n\n/**\n * Transparent `::before` hover bridge across the 8px gap (the positioning offset) on the side that\n * faces the trigger only, so the pointer can move onto the tooltip; the other sides get no bridge,\n * which would otherwise catch hovers and clicks meant for neighbouring controls.\n */\nconst bridgeClasses = [\n 'before:absolute',\n // Tooltip above the trigger: a strip below it.\n 'data-[side=top]:before:inset-x-0 data-[side=top]:before:top-full data-[side=top]:before:h-2',\n // Tooltip below the trigger: a strip above it.\n 'data-[side=bottom]:before:inset-x-0 data-[side=bottom]:before:bottom-full data-[side=bottom]:before:h-2',\n // wave-allow-physical: data-side is the physical side resolved by the positioning\n 'data-[side=left]:before:inset-y-0 data-[side=left]:before:left-full data-[side=left]:before:w-2',\n // wave-allow-physical: data-side is the physical side resolved by the positioning\n 'data-[side=right]:before:inset-y-0 data-[side=right]:before:right-full data-[side=right]:before:w-2',\n].join(' ');\n\n/**\n * The visual surface: wrapping text, typography reset against the host (header cells, muted\n * panels), and the hover bridge towards the trigger.\n */\nconst surfaceClasses = `max-w-60 whitespace-normal break-words rounded px-3 py-1.5 text-caption-1 font-normal normal-case tracking-normal text-start shadow-8 ${bridgeClasses}`;\n\ntype UnknownProps = Record<string, unknown>;\ntype Relationship = NonNullable<TooltipProps['relationship']>;\n\nconst HANDLER_KEY = /^on[A-Z]/;\n\n/** Naming attributes a generic element such as the fallback `<span>` must not carry. */\nconst NAMING_ARIA = ['aria-label', 'aria-labelledby'] as const;\n\n/**\n * Whether an event reached the wrapper from its own DOM subtree — the child — rather than through\n * a portal: React bubbles events from portaled content (the tooltip surface, a popup the child\n * renders in a portal) through the component tree.\n */\nfunction isOwnEvent(event: React.SyntheticEvent): boolean {\n return (event.currentTarget as Node).contains(event.target as Node);\n}\n\n/** `handler`, called only for the wrapper's own events (see {@link isOwnEvent}). */\nfunction ownEventsOnly<E extends React.SyntheticEvent>(\n handler: (event: E) => void,\n): (event: E) => void {\n return (event) => {\n if (isOwnEvent(event)) handler(event);\n };\n}\n\n/**\n * Splits the props the Tooltip receives, from its consumer or from a parent that clones its child\n * (Menu.Trigger, Popover.Trigger, Dialog.Trigger, Field): `id` and `aria-*` describe the element\n * the Tooltip is attached to, so they go to the child; the rest stays on the wrapper `<span>`.\n */\nfunction splitProps(props: UnknownProps): { childProps: UnknownProps; wrapperProps: UnknownProps } {\n const childProps: UnknownProps = {};\n const wrapperProps: UnknownProps = {};\n for (const [key, value] of Object.entries(props)) {\n if (key === 'id' || key.startsWith('aria-')) childProps[key] = value;\n else if (HANDLER_KEY.test(key) && typeof value === 'function') {\n // The wrapper sees the child's events as they bubble, but not those that reach it through\n // a portal, such as a click on the tooltip surface (it would toggle a parent trigger).\n wrapperProps[key] = ownEventsOnly(value as (event: React.SyntheticEvent) => void);\n } else wrapperProps[key] = value;\n }\n return { childProps, wrapperProps };\n}\n\nfunction omit(props: UnknownProps, keys: readonly string[]): UnknownProps {\n const result = { ...props };\n for (const key of keys) delete result[key];\n return result;\n}\n\nfunction hasIdRef(el: Element, attribute: string, id: string): boolean {\n return (el.getAttribute(attribute) ?? '').split(/\\s+/).includes(id);\n}\n\nfunction removeIdRef(el: Element, attribute: string, id: string): void {\n const rest = (el.getAttribute(attribute) ?? '')\n .split(/\\s+/)\n .filter((value) => value && value !== id);\n if (rest.length > 0) el.setAttribute(attribute, rest.join(' '));\n else el.removeAttribute(attribute);\n}\n\n/**\n * Makes sure the element people focus carries the tooltip relationship. After each commit, when no\n * focusable element inside the wrapper references `tooltipId` through `attribute` — children in the\n * fallback span, a non-focusable child element, or a child component that drops the prop (a\n * Popover or Dialog root) — the id is added to the first tabbable element inside the wrapper and\n * kept there while the subtree changes; a development warning explains the nesting, except for\n * the fallback span, whose children shape `renderTrigger` already warned about.\n */\nfunction useRelationshipTarget(\n wrapperRef: React.RefObject<HTMLSpanElement | null>,\n tooltipId: string,\n relationship: Relationship,\n childIsFallback: boolean,\n): void {\n const appliedRef = React.useRef<{ element: Element; attribute: string } | null>(null);\n const attribute = relationship === 'label' ? 'aria-labelledby' : 'aria-describedby';\n\n React.useLayoutEffect(() => {\n const wrapper = wrapperRef.current;\n if (!wrapper) return;\n\n const update = () => {\n const applied = appliedRef.current;\n const carriers = Array.from(wrapper.querySelectorAll(`[${attribute}]`)).filter(\n (el) =>\n !(applied && applied.element === el && applied.attribute === attribute) &&\n hasIdRef(el, attribute, tooltipId),\n );\n const reached = carriers.some((el) => el.matches(FOCUSABLE_SELECTOR));\n const target = reached ? null : getFirstTabbable(wrapper);\n if (applied && (applied.element !== target || applied.attribute !== attribute)) {\n removeIdRef(applied.element, applied.attribute, tooltipId);\n appliedRef.current = null;\n }\n if (!target) return;\n if (!hasIdRef(target, attribute, tooltipId)) {\n target.setAttribute(attribute, joinIds(target.getAttribute(attribute), tooltipId) ?? '');\n }\n appliedRef.current = { element: target, attribute };\n if (!childIsFallback) {\n warnOnce(\n 'Tooltip:focus-target',\n `Tooltip: the element that takes focus inside it did not get \\`${attribute}\\` (the child is not focusable, or is a component, such as a Popover or Dialog root, that does not pass the prop on), so it was added to the first focusable element inside. Put the Tooltip directly around the focusable element, e.g. <Popover.Trigger><Tooltip content=\"…\"><Button /></Tooltip></Popover.Trigger>.`,\n );\n }\n };\n\n update();\n if (!appliedRef.current || typeof MutationObserver === 'undefined') return;\n // Keep it on the focusable element when the child replaces it or rewrites the attribute\n // without the Tooltip re-rendering.\n const observer = new MutationObserver(update);\n observer.observe(wrapper, {\n subtree: true,\n childList: true,\n attributes: true,\n attributeFilter: [attribute],\n });\n return () => observer.disconnect();\n });\n}\n\n/**\n * Shows a short text label or description for its child on hover and keyboard focus.\n *\n * - The text is always present as a hidden `role=\"tooltip\"` element referenced by the child's\n * `aria-describedby` (or `aria-labelledby` with `relationship=\"label\"`), so screen readers get\n * it on focus without waiting for the delay; the child's own ids are kept.\n * - The visual surface is rendered in a portal while visible (it is `aria-hidden`, a copy of the\n * description), positioned on `side`/`align` with flipping and shifting to stay in view.\n * - It fades in and out over 100 ms (see `motion`; at once under reduced motion). The surface\n * mounts through the presence core: it carries `data-presence` and `data-state=\"open\"`, and\n * while it fades out `data-state=\"closed\"` and `inert`, staying where it was until its exit\n * ends; the Escape layer goes as it hides, not after the fade. Shown again during its exit, the\n * same surface fades back in. The hidden description is unchanged in every phase.\n * - It appears `openDelay` after the pointer rests on the child or the child takes keyboard focus,\n * stays visible while the pointer moves onto it, hides `closeDelay` after the pointer leaves or\n * immediately on blur, and Escape hides it without closing an enclosing dialog or popover. Focus\n * and hover inside a popup the child renders in a portal (a DatePicker calendar, a listbox) do\n * not show it, and the pointer moving onto such a popup hides it.\n * - The open state is uncontrolled by default (`defaultOpen`). With `open` and `onOpenChange`\n * (Fluent's `visible` and `onVisibleChange`) the parent owns it: hover, focus, leave, blur and\n * Escape then only ask for a change, and `open` alone shows the surface.\n * - `id` and `aria-*` props given to the Tooltip go to its child, merged with the child's own (the\n * child's `id` wins, id lists are joined, `aria-expanded`/`aria-controls`/`aria-haspopup` from\n * the Tooltip win). `className`, `style`, `ref`, `data-*` and event handlers stay on the wrapper\n * `<span>`; the handlers see the child's events as they bubble, not events from portaled\n * content such as the tooltip surface or a popup the child renders in a portal (a DatePicker\n * calendar, a listbox). `onMouseEnter` and `onMouseLeave` are the exception: React fires them\n * along the component tree, so they also fire when the pointer moves onto or off the tooltip\n * surface or such a popup. So the Tooltip can sit inside a trigger, and the trigger's\n * id and state reach the button: `<Menu.Trigger><Tooltip content=\"…\"><Button /></Tooltip>\n * </Menu.Trigger>` (also `Popover.Trigger`, `Dialog.Trigger`, `Drawer.Trigger`). Around a\n * trigger works too, since triggers pass `aria-describedby` on to their child.\n * - Wrap the focusable element itself. When the element that takes focus does not get the\n * relationship (a Popover or Dialog root, a component that drops props, a non-focusable\n * wrapper element), it is added to the first focusable element inside, with a development\n * warning.\n *\n * @example\n * <Menu>\n * <Menu.Trigger>\n * <Tooltip content=\"Edit, duplicate or delete the item\">\n * <Button>Actions</Button>\n * </Tooltip>\n * </Menu.Trigger>\n * <Menu.Popover>…</Menu.Popover>\n * </Menu>\n */\nexport const Tooltip = ({\n content,\n appearance: appearanceProp,\n variant,\n openDelay: openDelayProp,\n closeDelay = 100,\n delay,\n open: openProp,\n defaultOpen,\n onOpenChange,\n relationship = 'description',\n side = 'top',\n align = 'center',\n positioning,\n motion,\n children,\n className,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ref,\n ...rest\n}: TooltipProps) => {\n const appearance =\n resolveDeprecatedProp(\n 'Tooltip',\n appearanceProp,\n variant === undefined ? undefined : VARIANT_APPEARANCE[variant],\n 'variant',\n 'appearance',\n ) ?? 'inverted';\n const openDelay =\n resolveDeprecatedProp('Tooltip', openDelayProp, delay, 'delay', 'openDelay') ?? 200;\n const [open, setOpen] = useControllable(openProp, defaultOpen ?? false, onOpenChange);\n // The surface lives in a portal, which renders only in the browser: until then (the server HTML,\n // hydration) the tooltip is closed; the hidden description is rendered either way.\n const isClient = useIsClient();\n const visible = open && isClient;\n const timerRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const wrapperRef = React.useRef<HTMLSpanElement | null>(null);\n const surfaceRef = React.useRef<HTMLSpanElement | null>(null);\n const tooltipId = useId('tooltip');\n\n // The surface mounts and unmounts through the presence core, and enters also when it mounts\n // visible (a tooltip open by default fades in on a client render as after hydration); the\n // positioning and the Escape layer key on `visible`, so they go as it hides, not after its exit.\n // Destructured: react-hooks/refs treats an object whose member is passed to `ref` as a ref.\n const { isMounted, ref: presenceRef, presenceProps } = usePresence(visible, { appear: true });\n const motionClassName = useMotionClasses(motion, tooltipMotion);\n const { setReference, setFloating, floatingProps } = usePopupPosition({\n open: visible,\n side,\n align,\n ...resolvePositioning(positioning, { offset: 8 }),\n });\n const wrapperElementRef = useMergedRefs<HTMLSpanElement>(ref, wrapperRef, setReference);\n const surfaceElementRef = useMergedRefs<HTMLSpanElement>(surfaceRef, setFloating, presenceRef);\n\n // Timer handlers are event callbacks (stable, latest state): they are composed with the\n // consumer's handlers during render but only run from events and timers.\n const clearTimer = useEventCallback(() => {\n if (timerRef.current !== undefined) {\n clearTimeout(timerRef.current);\n timerRef.current = undefined;\n }\n });\n\n // show, hide and scheduleHide ask for the new state; a controlled tooltip changes only when the\n // parent updates `open`.\n const show = useEventCallback(() => {\n clearTimer();\n if (visible) return;\n if (openDelay <= 0) {\n setOpen(true);\n return;\n }\n timerRef.current = setTimeout(() => {\n timerRef.current = undefined;\n setOpen(true);\n }, openDelay);\n });\n\n const hide = useEventCallback(() => {\n clearTimer();\n setOpen(false);\n });\n\n // The grace period after the pointer leaves lets it move onto the tooltip.\n const scheduleHide = useEventCallback(() => {\n clearTimer();\n if (!visible) return;\n timerRef.current = setTimeout(() => {\n timerRef.current = undefined;\n setOpen(false);\n }, closeDelay);\n });\n\n // An Escape-only layer while visible: Escape hides the tooltip before any enclosing overlay. The\n // wrapper is its anchor (its trigger, not its surface): Tab from the child inside a focus trap\n // moves on from the popover, portal or dialog around the wrapper.\n useDismiss({\n open: visible,\n onDismiss: hide,\n refs: [wrapperRef, surfaceRef],\n anchorRef: wrapperRef,\n kind: 'tooltip',\n outsidePress: false,\n });\n\n React.useEffect(() => {\n const timer = timerRef;\n return () => clearTimeout(timer.current);\n }, []);\n\n // The tooltip's own reactions ignore a popup the child renders in a portal: its focus and blur\n // are not the child's, entering it does not show the tooltip, and the pointer moving onto it\n // (no mouseleave reaches the wrapper then) hides the tooltip, which would cover it. The tooltip\n // surface itself is portaled too but belongs to the tooltip: it stays hoverable.\n const showOnEnter = useEventCallback((event: React.MouseEvent<HTMLSpanElement>) => {\n if (isOwnEvent(event) || surfaceRef.current?.contains(event.target as Node)) show();\n });\n const hideOverPortaledPopup = useEventCallback((event: React.MouseEvent<HTMLSpanElement>) => {\n if (!isOwnEvent(event) && !surfaceRef.current?.contains(event.target as Node)) hide();\n });\n\n const { childProps, wrapperProps } = splitProps(rest);\n const target = unwrapFragment(children);\n const childIsFallback = !isCloneableElement(target);\n const attribute = relationship === 'label' ? 'aria-labelledby' : 'aria-describedby';\n // Props given to the Tooltip come first, as if the child had them; then the tooltip's own id.\n // The fallback span is a generic element: no state ARIA and no naming on it (the relationship\n // then reaches its first focusable element through useRelationshipTarget).\n const triggerProps: UnknownProps = childIsFallback\n ? omit(childProps, [...STATE_ARIA, ...NAMING_ARIA])\n : { ...childProps };\n if (!childIsFallback || relationship === 'description') {\n triggerProps[attribute] = joinIds(childProps[attribute] as string | undefined, tooltipId);\n }\n const child = renderTrigger(target, triggerProps, { componentName: 'Tooltip' });\n useRelationshipTarget(wrapperRef, tooltipId, relationship, childIsFallback);\n\n return (\n <span\n ref={wrapperElementRef}\n {...wrapperProps}\n className={cn(tooltipClassNames.root, 'inline-block', className)}\n onMouseEnter={composeEventHandlers(onMouseEnter, showOnEnter)}\n onMouseLeave={composeEventHandlers(onMouseLeave, scheduleHide)}\n onMouseOver={composeEventHandlers(\n wrapperProps.onMouseOver as React.MouseEventHandler<HTMLSpanElement> | undefined,\n hideOverPortaledPopup,\n )}\n onFocus={ownEventsOnly(composeEventHandlers(onFocus, show))}\n onBlur={ownEventsOnly(composeEventHandlers(onBlur, hide))}\n >\n {child}\n <span id={tooltipId} role=\"tooltip\" hidden>\n {content}\n </span>\n {isMounted && (\n <Portal layer=\"tooltip\">\n <span\n ref={surfaceElementRef}\n aria-hidden=\"true\"\n data-wave-tooltip-surface=\"\"\n data-state={visible ? 'open' : 'closed'}\n {...presenceProps}\n {...floatingProps}\n className={cn(\n tooltipClassNames.content,\n surfaceClasses,\n appearanceClasses[appearance],\n motionClassName,\n )}\n >\n {content}\n </span>\n </Portal>\n )}\n </span>\n );\n};\n\nTooltip.displayName = 'Tooltip';\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/dev.cjs"),r=require("../../lib/classNames.cjs"),i=require("../../lib/theme.cjs");let a=require("react");a=e.__toESM(a,1);let o=require("react/jsx-runtime");var s=a.createContext({theme:`light`,dir:`ltr`,themeClassName:``,portalContainer:null,inputDefaults:{},hasProvider:!1});function c(){return a.useContext(s)}var l=({theme:e,dir:c,portalContainer:l,inputDefaults:u,
|
|
1
|
+
"use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/dev.cjs"),r=require("../../lib/classNames.cjs"),i=require("../../lib/theme.cjs");let a=require("react");a=e.__toESM(a,1);let o=require("react/jsx-runtime");var s=a.createContext({theme:`light`,dir:`ltr`,themeClassName:``,portalContainer:null,inputDefaults:{},motion:`default`,hasProvider:!1});function c(){return a.useContext(s)}var l=({theme:e,dir:c,portalContainer:l,inputDefaults:u,motion:d,children:f,className:p,ref:m,...h})=>{let g=a.useContext(s),_=e??g.theme,v=c??g.dir,y=d??g.motion,b=l===void 0?g.portalContainer:l,x=u?.size??g.inputDefaults.size,S=u?.appearance??g.inputDefaults.appearance,C=a.useMemo(()=>{let e={};return x!==void 0&&(e.size=x),S!==void 0&&(e.appearance=S),e},[x,S]),w=i.isWaveTheme(_)?_:`light`,T=i.getThemeClassName(w);a.useEffect(()=>{i.isWaveTheme(_)||n.warnOnce(`WaveProvider:theme:${String(_)}`,`WaveProvider: unknown theme "${String(_)}". Valid themes: ${i.WAVE_THEMES.join(`, `)}. Using "light".`)},[_]);let E=a.useMemo(()=>({theme:w,dir:v,themeClassName:T,portalContainer:b,inputDefaults:C,motion:y,hasProvider:!0}),[w,v,T,b,C,y]);return(0,o.jsx)(s.Provider,{value:E,children:(0,o.jsx)(`div`,{...h,ref:m,dir:v,"data-wave-theme":w,className:t.cn(r.waveProviderClassNames.root,T,`bg-background text-foreground font-wave text-body-1`,p),children:f})})};l.displayName=`WaveProvider`,exports.WaveProvider=l,exports.getThemeClassName=i.getThemeClassName,exports.useWaveTheme=c;
|
|
2
2
|
//# sourceMappingURL=WaveProvider.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WaveProvider.cjs","names":["React","isWaveTheme","getThemeClassName","WAVE_THEMES","cn","waveProviderClassNames","require_runtime","require_cn","require_dev","require_classNames","require_theme","react","react_jsx_runtime","WaveContext","useWaveTheme","WaveProvider","themeProp","dirProp","portalContainerProp","inputDefaultsProp","rest","parent","theme","dir","portalContainer","size","appearance","inputDefaults","merged","resolvedTheme","themeClassName","value","className"],"sources":["../../../src/components/provider/WaveProvider.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { warnOnce } from '../../lib/dev';\nimport { waveProviderClassNames } from '../../lib/classNames';\nimport { getThemeClassName, isWaveTheme, WAVE_THEMES, type WaveTheme } from '../../lib/theme';\nimport type { CoreSize, InputAppearance } from '../../lib/types';\n\n// The theme helper lives in server-safe src/lib (no \"use client\"), so React Server Components can\n// call it; it stays exported from here too.\nexport { getThemeClassName } from '../../lib/theme';\nexport type { WaveTheme } from '../../lib/theme';\n\n/** Text direction for bidirectional layout support. */\nexport type WaveDir = 'ltr' | 'rtl';\n\n/** Default size and appearance of the text inputs and pickers in a subtree. */\nexport interface InputDefaults {\n /** Default `size` of Input, Textarea, Select, SearchBox, SpinButton and the pickers. */\n size?: CoreSize;\n /** Default `appearance` of the same controls. */\n appearance?: InputAppearance;\n}\n\n/** Props accepted by {@link WaveProvider}. */\nexport interface WaveProviderProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Visual theme applied to the subtree. Providers can be nested in any order: each root declares\n * its theme's tokens, so a light panel inside a dark app (and the reverse) renders correctly.\n * @default the enclosing WaveProvider's theme, else 'light'\n */\n theme?: WaveTheme;\n /**\n * Text direction for the subtree (also read by portaled overlays and keyboard navigation).\n * @default the enclosing WaveProvider's direction, else 'ltr'\n */\n dir?: WaveDir;\n /**\n * Element that portaled overlays (dialogs, popovers, menus, toasts) render into. `null` renders\n * them into `document.body`, also inside a provider that sets a container.\n * @default the enclosing WaveProvider's container, else document.body\n */\n portalContainer?: HTMLElement | null;\n /**\n * Default `size` and `appearance` of the text inputs and pickers in the subtree (Input,\n * Textarea, Select, SearchBox, SpinButton, Combobox, Dropdown, DatePicker, TimePicker,\n * TagPicker). Their own props and a Field's `size` win. A nested provider merges its keys over\n * the enclosing provider's; an omitted key is inherited.\n * @default the enclosing WaveProvider's defaults, else none (`medium`, `outline`)\n */\n inputDefaults?: InputDefaults;\n /** Content rendered inside the themed container. */\n children: React.ReactNode;\n /** Ref to the themed root `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/** Value provided by {@link WaveProvider} and returned by {@link useWaveTheme}. */\nexport interface WaveContextValue {\n /** The active theme. */\n theme: WaveTheme;\n /** The active text direction. */\n dir: WaveDir;\n /**\n * Theme classes of the nearest provider (see {@link getThemeClassName}); portals put them on\n * their wrapper so overlays inherit the theme. `''` outside a provider (the page's own theme).\n */\n themeClassName: string;\n /** Element portaled overlays render into; `null` means `document.body`. */\n portalContainer: HTMLElement | null;\n /** The merged input defaults of the providers above (`{}` outside a provider). */\n inputDefaults: InputDefaults;\n /** `false` when no {@link WaveProvider} is above the caller (the defaults are returned). */\n hasProvider: boolean;\n}\n\nconst DEFAULT_CONTEXT: WaveContextValue = {\n theme: 'light',\n dir: 'ltr',\n themeClassName: '',\n portalContainer: null,\n inputDefaults: {},\n hasProvider: false,\n};\n\nconst WaveContext = React.createContext<WaveContextValue>(DEFAULT_CONTEXT);\n\n/**\n * Reads the theme, direction, theme classes and portal container of the nearest\n * {@link WaveProvider}. Outside a provider it returns light/ltr defaults with `hasProvider: false`.\n *\n * @returns The {@link WaveContextValue} of the nearest provider.\n */\nexport function useWaveTheme(): WaveContextValue {\n return React.useContext(WaveContext);\n}\n\n/**\n * Applies Wave theming to an application or subtree.\n *\n * - Renders a `<div class=\"wave-root wave-<theme>\">` that declares the theme's tokens and paints\n * the themed background, text colour and font (`bg-background text-foreground font-wave\n * text-body-1`; a `className` passed by you wins). The precompiled `./styles` entry scopes its base\n * styles and native-element reset to this root, so it is required there.\n * - Sets `dir` and `data-wave-theme` on the root. An unknown `theme` value (from untyped code)\n * falls back to `'light'` — classes, `data-wave-theme` and the context value — and warns once in\n * development.\n * - Provides theme, direction, theme classes and the portal container through context, so\n * portaled overlays render with the same theme and direction.\n * - Nests: a provider inherits every prop it omits (`theme`, `dir`, `portalContainer`) from the\n * enclosing provider, so `<WaveProvider theme=\"light\">` inside an RTL app is a light panel that\n * stays right-to-left and keeps the app's portal container. At the top level the defaults are\n * `'light'`, `'ltr'` and `document.body`.\n * - `inputDefaults` nests differently: a nested provider's defined keys override the enclosing\n * provider's, and an omitted key is inherited, so setting only `appearance` in a nested provider\n * keeps the enclosing provider's `size`.\n */\nexport const WaveProvider = ({\n theme: themeProp,\n dir: dirProp,\n portalContainer: portalContainerProp,\n inputDefaults: inputDefaultsProp,\n children,\n className,\n ref,\n ...rest\n}: WaveProviderProps) => {\n // Omitted props come from the enclosing provider; outside one, DEFAULT_CONTEXT supplies the\n // top-level defaults. An explicit `portalContainer={null}` means document.body, so only\n // `undefined` inherits.\n const parent = React.useContext(WaveContext);\n const theme = themeProp ?? parent.theme;\n const dir = dirProp ?? parent.dir;\n const portalContainer =\n portalContainerProp === undefined ? parent.portalContainer : portalContainerProp;\n\n // Defined keys override the enclosing provider's; the merged object keeps its identity while the\n // two values are equal, so an inline `inputDefaults={{ … }}` does not re-render every input.\n const size = inputDefaultsProp?.size ?? parent.inputDefaults.size;\n const appearance = inputDefaultsProp?.appearance ?? parent.inputDefaults.appearance;\n const inputDefaults = React.useMemo<InputDefaults>(() => {\n const merged: InputDefaults = {};\n if (size !== undefined) merged.size = size;\n if (appearance !== undefined) merged.appearance = appearance;\n return merged;\n }, [size, appearance]);\n\n // An unknown value (untyped callers) renders, and is reported, as the light theme.\n const resolvedTheme: WaveTheme = isWaveTheme(theme) ? theme : 'light';\n const themeClassName = getThemeClassName(resolvedTheme);\n\n React.useEffect(() => {\n if (!isWaveTheme(theme)) {\n warnOnce(\n `WaveProvider:theme:${String(theme)}`,\n `WaveProvider: unknown theme \"${String(theme)}\". Valid themes: ${WAVE_THEMES.join(', ')}. Using \"light\".`,\n );\n }\n }, [theme]);\n\n const value = React.useMemo<WaveContextValue>(\n () => ({\n theme: resolvedTheme,\n dir,\n themeClassName,\n portalContainer,\n inputDefaults,\n hasProvider: true,\n }),\n [resolvedTheme, dir, themeClassName, portalContainer, inputDefaults],\n );\n\n return (\n <WaveContext.Provider value={value}>\n <div\n {...rest}\n ref={ref}\n dir={dir}\n data-wave-theme={resolvedTheme}\n className={cn(\n waveProviderClassNames.root,\n themeClassName,\n 'bg-background text-foreground font-wave text-body-1',\n className,\n )}\n >\n {children}\n </div>\n </WaveContext.Provider>\n );\n};\n\nWaveProvider.displayName = 'WaveProvider';\n"],"mappings":"oRAoFA,IAAMa,EAAcb,EAAM,cAAgC,CARxD,MAAO,QACP,IAAK,MACL,eAAgB,GAChB,gBAAiB,KACjB,cAAe,CAAC,EAChB,YAAa,EAG2C,CAAe,EAQzE,SAAgBc,GAAiC,CAC/C,OAAOd,EAAM,WAAWa,CAAW,CACrC,CAsBA,IAAaE,GAAgB,CAC3B,MAAOC,EACP,IAAKC,EACL,gBAAiBC,EACjB,cAAeC,EACf,WACA,YACA,MACA,GAAGC,KACoB,CAIvB,IAAMC,EAASrB,EAAM,WAAWa,CAAW,EACrCS,EAAQN,GAAaK,EAAO,MAC5BE,EAAMN,GAAWI,EAAO,IACxBG,EACJN,IAAwB,IAAA,GAAYG,EAAO,gBAAkBH,EAIzDO,EAAON,GAAmB,MAAQE,EAAO,cAAc,KACvDK,EAAaP,GAAmB,YAAcE,EAAO,cAAc,WACnEM,EAAgB3B,EAAM,YAA6B,CACvD,IAAM4B,EAAwB,CAAC,EAG/B,OAFIH,IAAS,IAAA,KAAW,EAAO,KAAOA,GAClCC,IAAe,IAAA,KAAW,EAAO,WAAaA,GAC3CE,CACT,EAAG,CAACH,EAAMC,CAAU,CAAC,EAGfG,EAA2B5B,EAAAA,YAAYqB,CAAK,EAAIA,EAAQ,QACxDQ,EAAiB5B,EAAAA,kBAAkB2B,CAAa,EAEtDlB,EAAM,cAAgB,CACfV,EAAAA,YAAYqB,CAAK,GACpBd,EAAA,SACE,sBAAsB,OAAOc,CAAK,IAClC,gCAAgC,OAAOA,CAAK,EAAE,mBAAmBnB,EAAAA,YAAY,KAAK,IAAI,EAAE,iBAC1F,CAEJ,EAAG,CAACmB,CAAK,CAAC,EAEV,IAAMS,EAAQ/B,EAAM,aACX,CACL,MAAO6B,EACP,MACA,iBACA,kBACA,gBACA,YAAa,EACf,GACA,CAACA,EAAeN,EAAKO,EAAgBN,EAAiBG,CAAa,CACrE,EAEA,OACE,EAAAf,EAAA,IAAA,CAACC,EAAY,SAAb,CAA6B,QAC3B,UAAA,EAAAD,EAAA,IAAA,CAAC,MAAD,CACE,GAAIQ,EACC,MACA,MACL,kBAAiBS,EACjB,UAAWzB,EAAAA,GACTC,EAAAA,uBAAuB,KACvByB,EACA,sDACAE,CACF,EAEC,UACE,CAAA,CACe,CAAA,CAE1B,EAEA,EAAa,YAAc"}
|
|
1
|
+
{"version":3,"file":"WaveProvider.cjs","names":["React","isWaveTheme","getThemeClassName","WAVE_THEMES","cn","waveProviderClassNames","require_runtime","require_cn","require_dev","require_classNames","require_theme","react","react_jsx_runtime","WaveContext","useWaveTheme","WaveProvider","themeProp","dirProp","portalContainerProp","inputDefaultsProp","motionProp","rest","parent","theme","dir","motion","portalContainer","size","appearance","inputDefaults","merged","resolvedTheme","themeClassName","value","className"],"sources":["../../../src/components/provider/WaveProvider.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { warnOnce } from '../../lib/dev';\nimport { waveProviderClassNames } from '../../lib/classNames';\nimport { getThemeClassName, isWaveTheme, WAVE_THEMES, type WaveTheme } from '../../lib/theme';\nimport type { CoreSize, InputAppearance, MotionPreference } from '../../lib/types';\n\n// The theme helper lives in server-safe src/lib (no \"use client\"), so React Server Components can\n// call it; it stays exported from here too.\nexport { getThemeClassName } from '../../lib/theme';\nexport type { WaveTheme } from '../../lib/theme';\n\n/** Text direction for bidirectional layout support. */\nexport type WaveDir = 'ltr' | 'rtl';\n\n/** Default size and appearance of the text inputs and pickers in a subtree. */\nexport interface InputDefaults {\n /** Default `size` of Input, Textarea, Select, SearchBox, SpinButton and the pickers. */\n size?: CoreSize;\n /** Default `appearance` of the same controls. */\n appearance?: InputAppearance;\n}\n\n/** Props accepted by {@link WaveProvider}. */\nexport interface WaveProviderProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Visual theme applied to the subtree. Providers can be nested in any order: each root declares\n * its theme's tokens, so a light panel inside a dark app (and the reverse) renders correctly.\n * @default the enclosing WaveProvider's theme, else 'light'\n */\n theme?: WaveTheme;\n /**\n * Text direction for the subtree (also read by portaled overlays and keyboard navigation).\n * @default the enclosing WaveProvider's direction, else 'ltr'\n */\n dir?: WaveDir;\n /**\n * Element that portaled overlays (dialogs, popovers, menus, toasts) render into. `null` renders\n * them into `document.body`, also inside a provider that sets a container.\n * @default the enclosing WaveProvider's container, else document.body\n */\n portalContainer?: HTMLElement | null;\n /**\n * Default `size` and `appearance` of the text inputs and pickers in the subtree (Input,\n * Textarea, Select, SearchBox, SpinButton, Combobox, Dropdown, DatePicker, TimePicker,\n * TagPicker). Their own props and a Field's `size` win. A nested provider merges its keys over\n * the enclosing provider's; an omitted key is inherited.\n * @default the enclosing WaveProvider's defaults, else none (`medium`, `outline`)\n */\n inputDefaults?: InputDefaults;\n /**\n * Motion of the subtree. `'none'` makes every motion slot below it (a part's `motion`,\n * `backdropMotion`, `expandIconMotion` or `indeterminateMotion` left unset) behave as `null`,\n * which also stops the slide of TabList's selection line, and turns off Carousel's slide or fade\n * (a Carousel without its own `motion`) and the reveal of Nav item actions; a part's own value\n * still wins. Transitions that are no motion slot keep running: the turn of Accordion's and\n * Tree's chevrons, colour changes on hover and press. Fluent's `MotionBehaviourProvider` with `'skip'` is the counterpart, which\n * `@fluentui/react-components` does not export. A nested provider inherits an omitted `motion`\n * like `theme`.\n * @default the enclosing WaveProvider's motion, else 'default'\n */\n motion?: MotionPreference;\n /** Content rendered inside the themed container. */\n children: React.ReactNode;\n /** Ref to the themed root `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/** Value provided by {@link WaveProvider} and returned by {@link useWaveTheme}. */\nexport interface WaveContextValue {\n /** The active theme. */\n theme: WaveTheme;\n /** The active text direction. */\n dir: WaveDir;\n /**\n * Theme classes of the nearest provider (see {@link getThemeClassName}); portals put them on\n * their wrapper so overlays inherit the theme. `''` outside a provider (the page's own theme).\n */\n themeClassName: string;\n /** Element portaled overlays render into; `null` means `document.body`. */\n portalContainer: HTMLElement | null;\n /** The merged input defaults of the providers above (`{}` outside a provider). */\n inputDefaults: InputDefaults;\n /** The motion preference of the providers above (`'default'` outside a provider). */\n motion: MotionPreference;\n /** `false` when no {@link WaveProvider} is above the caller (the defaults are returned). */\n hasProvider: boolean;\n}\n\nconst DEFAULT_CONTEXT: WaveContextValue = {\n theme: 'light',\n dir: 'ltr',\n themeClassName: '',\n portalContainer: null,\n inputDefaults: {},\n motion: 'default',\n hasProvider: false,\n};\n\nconst WaveContext = React.createContext<WaveContextValue>(DEFAULT_CONTEXT);\n\n/**\n * Reads the theme, direction, theme classes and portal container of the nearest\n * {@link WaveProvider}. Outside a provider it returns light/ltr defaults with `hasProvider: false`.\n *\n * @returns The {@link WaveContextValue} of the nearest provider.\n */\nexport function useWaveTheme(): WaveContextValue {\n return React.useContext(WaveContext);\n}\n\n/**\n * Applies Wave theming to an application or subtree.\n *\n * - Renders a `<div class=\"wave-root wave-<theme>\">` that declares the theme's tokens and paints\n * the themed background, text colour and font (`bg-background text-foreground font-wave\n * text-body-1`; a `className` passed by you wins). The precompiled `./styles` entry scopes its base\n * styles and native-element reset to this root, so it is required there.\n * - Sets `dir` and `data-wave-theme` on the root. An unknown `theme` value (from untyped code)\n * falls back to `'light'` — classes, `data-wave-theme` and the context value — and warns once in\n * development.\n * - Provides theme, direction, theme classes and the portal container through context, so\n * portaled overlays render with the same theme and direction.\n * - Nests: a provider inherits every prop it omits (`theme`, `dir`, `portalContainer`) from the\n * enclosing provider, so `<WaveProvider theme=\"light\">` inside an RTL app is a light panel that\n * stays right-to-left and keeps the app's portal container. At the top level the defaults are\n * `'light'`, `'ltr'` and `document.body`.\n * - `inputDefaults` nests differently: a nested provider's defined keys override the enclosing\n * provider's, and an omitted key is inherited, so setting only `appearance` in a nested provider\n * keeps the enclosing provider's `size`.\n * - `motion=\"none\"` turns the default motion of every animated part below it off (its motion\n * slots, Carousel's slide, TabList's sliding line, the reveal of Nav item actions; a part's own\n * `motion` still wins), while transitions without a slot keep running (Accordion's and Tree's\n * chevron turns, colour changes); a nested provider inherits an omitted `motion` like `theme`.\n */\nexport const WaveProvider = ({\n theme: themeProp,\n dir: dirProp,\n portalContainer: portalContainerProp,\n inputDefaults: inputDefaultsProp,\n motion: motionProp,\n children,\n className,\n ref,\n ...rest\n}: WaveProviderProps) => {\n // Omitted props come from the enclosing provider; outside one, DEFAULT_CONTEXT supplies the\n // top-level defaults. An explicit `portalContainer={null}` means document.body, so only\n // `undefined` inherits.\n const parent = React.useContext(WaveContext);\n const theme = themeProp ?? parent.theme;\n const dir = dirProp ?? parent.dir;\n const motion = motionProp ?? parent.motion;\n const portalContainer =\n portalContainerProp === undefined ? parent.portalContainer : portalContainerProp;\n\n // Defined keys override the enclosing provider's; the merged object keeps its identity while the\n // two values are equal, so an inline `inputDefaults={{ … }}` does not re-render every input.\n const size = inputDefaultsProp?.size ?? parent.inputDefaults.size;\n const appearance = inputDefaultsProp?.appearance ?? parent.inputDefaults.appearance;\n const inputDefaults = React.useMemo<InputDefaults>(() => {\n const merged: InputDefaults = {};\n if (size !== undefined) merged.size = size;\n if (appearance !== undefined) merged.appearance = appearance;\n return merged;\n }, [size, appearance]);\n\n // An unknown value (untyped callers) renders, and is reported, as the light theme.\n const resolvedTheme: WaveTheme = isWaveTheme(theme) ? theme : 'light';\n const themeClassName = getThemeClassName(resolvedTheme);\n\n React.useEffect(() => {\n if (!isWaveTheme(theme)) {\n warnOnce(\n `WaveProvider:theme:${String(theme)}`,\n `WaveProvider: unknown theme \"${String(theme)}\". Valid themes: ${WAVE_THEMES.join(', ')}. Using \"light\".`,\n );\n }\n }, [theme]);\n\n const value = React.useMemo<WaveContextValue>(\n () => ({\n theme: resolvedTheme,\n dir,\n themeClassName,\n portalContainer,\n inputDefaults,\n motion,\n hasProvider: true,\n }),\n [resolvedTheme, dir, themeClassName, portalContainer, inputDefaults, motion],\n );\n\n return (\n <WaveContext.Provider value={value}>\n <div\n {...rest}\n ref={ref}\n dir={dir}\n data-wave-theme={resolvedTheme}\n className={cn(\n waveProviderClassNames.root,\n themeClassName,\n 'bg-background text-foreground font-wave text-body-1',\n className,\n )}\n >\n {children}\n </div>\n </WaveContext.Provider>\n );\n};\n\nWaveProvider.displayName = 'WaveProvider';\n"],"mappings":"oRAmGA,IAAMa,EAAcb,EAAM,cAAgC,CATxD,MAAO,QACP,IAAK,MACL,eAAgB,GAChB,gBAAiB,KACjB,cAAe,CAAC,EAChB,OAAQ,UACR,YAAa,EAG2C,CAAe,EAQzE,SAAgBc,GAAiC,CAC/C,OAAOd,EAAM,WAAWa,CAAW,CACrC,CA0BA,IAAaE,GAAgB,CAC3B,MAAOC,EACP,IAAKC,EACL,gBAAiBC,EACjB,cAAeC,EACf,OAAQC,EACR,WACA,YACA,MACA,GAAGC,KACoB,CAIvB,IAAMC,EAAStB,EAAM,WAAWa,CAAW,EACrCU,EAAQP,GAAaM,EAAO,MAC5BE,EAAMP,GAAWK,EAAO,IACxBG,EAASL,GAAcE,EAAO,OAC9BI,EACJR,IAAwB,IAAA,GAAYI,EAAO,gBAAkBJ,EAIzDS,EAAOR,GAAmB,MAAQG,EAAO,cAAc,KACvDM,EAAaT,GAAmB,YAAcG,EAAO,cAAc,WACnEO,EAAgB7B,EAAM,YAA6B,CACvD,IAAM8B,EAAwB,CAAC,EAG/B,OAFIH,IAAS,IAAA,KAAW,EAAO,KAAOA,GAClCC,IAAe,IAAA,KAAW,EAAO,WAAaA,GAC3CE,CACT,EAAG,CAACH,EAAMC,CAAU,CAAC,EAGfG,EAA2B9B,EAAAA,YAAYsB,CAAK,EAAIA,EAAQ,QACxDS,EAAiB9B,EAAAA,kBAAkB6B,CAAa,EAEtDpB,EAAM,cAAgB,CACfV,EAAAA,YAAYsB,CAAK,GACpBf,EAAA,SACE,sBAAsB,OAAOe,CAAK,IAClC,gCAAgC,OAAOA,CAAK,EAAE,mBAAmBpB,EAAAA,YAAY,KAAK,IAAI,EAAE,iBAC1F,CAEJ,EAAG,CAACoB,CAAK,CAAC,EAEV,IAAMU,EAAQjC,EAAM,aACX,CACL,MAAO+B,EACP,MACA,iBACA,kBACA,gBACA,SACA,YAAa,EACf,GACA,CAACA,EAAeP,EAAKQ,EAAgBN,EAAiBG,EAAeJ,CAAM,CAC7E,EAEA,OACE,EAAAb,EAAA,IAAA,CAACC,EAAY,SAAb,CAA6B,QAC3B,UAAA,EAAAD,EAAA,IAAA,CAAC,MAAD,CACE,GAAIS,EACC,MACA,MACL,kBAAiBU,EACjB,UAAW3B,EAAAA,GACTC,EAAAA,uBAAuB,KACvB2B,EACA,sDACAE,CACF,EAEC,UACE,CAAA,CACe,CAAA,CAE1B,EAEA,EAAa,YAAc"}
|
|
@@ -12,42 +12,45 @@ var c = o.createContext({
|
|
|
12
12
|
themeClassName: "",
|
|
13
13
|
portalContainer: null,
|
|
14
14
|
inputDefaults: {},
|
|
15
|
+
motion: "default",
|
|
15
16
|
hasProvider: !1
|
|
16
17
|
});
|
|
17
18
|
function l() {
|
|
18
19
|
return o.useContext(c);
|
|
19
20
|
}
|
|
20
|
-
var u = ({ theme: l, dir: u, portalContainer: d, inputDefaults: f,
|
|
21
|
-
let
|
|
21
|
+
var u = ({ theme: l, dir: u, portalContainer: d, inputDefaults: f, motion: p, children: m, className: h, ref: g, ..._ }) => {
|
|
22
|
+
let v = o.useContext(c), y = l ?? v.theme, b = u ?? v.dir, x = p ?? v.motion, S = d === void 0 ? v.portalContainer : d, C = f?.size ?? v.inputDefaults.size, w = f?.appearance ?? v.inputDefaults.appearance, T = o.useMemo(() => {
|
|
22
23
|
let e = {};
|
|
23
|
-
return
|
|
24
|
-
}, [
|
|
24
|
+
return C !== void 0 && (e.size = C), w !== void 0 && (e.appearance = w), e;
|
|
25
|
+
}, [C, w]), E = a(y) ? y : "light", D = i(E);
|
|
25
26
|
o.useEffect(() => {
|
|
26
|
-
a(
|
|
27
|
-
}, [
|
|
28
|
-
let
|
|
29
|
-
theme:
|
|
30
|
-
dir:
|
|
31
|
-
themeClassName:
|
|
32
|
-
portalContainer:
|
|
33
|
-
inputDefaults:
|
|
27
|
+
a(y) || t(`WaveProvider:theme:${String(y)}`, `WaveProvider: unknown theme "${String(y)}". Valid themes: ${r.join(", ")}. Using "light".`);
|
|
28
|
+
}, [y]);
|
|
29
|
+
let O = o.useMemo(() => ({
|
|
30
|
+
theme: E,
|
|
31
|
+
dir: b,
|
|
32
|
+
themeClassName: D,
|
|
33
|
+
portalContainer: S,
|
|
34
|
+
inputDefaults: T,
|
|
35
|
+
motion: x,
|
|
34
36
|
hasProvider: !0
|
|
35
37
|
}), [
|
|
36
|
-
|
|
37
|
-
y,
|
|
38
|
-
T,
|
|
38
|
+
E,
|
|
39
39
|
b,
|
|
40
|
-
|
|
40
|
+
D,
|
|
41
|
+
S,
|
|
42
|
+
T,
|
|
43
|
+
x
|
|
41
44
|
]);
|
|
42
45
|
return /* @__PURE__ */ s(c.Provider, {
|
|
43
|
-
value:
|
|
46
|
+
value: O,
|
|
44
47
|
children: /* @__PURE__ */ s("div", {
|
|
45
|
-
...
|
|
46
|
-
ref:
|
|
47
|
-
dir:
|
|
48
|
-
"data-wave-theme":
|
|
49
|
-
className: e(n.root,
|
|
50
|
-
children:
|
|
48
|
+
..._,
|
|
49
|
+
ref: g,
|
|
50
|
+
dir: b,
|
|
51
|
+
"data-wave-theme": E,
|
|
52
|
+
className: e(n.root, D, "bg-background text-foreground font-wave text-body-1", h),
|
|
53
|
+
children: m
|
|
51
54
|
})
|
|
52
55
|
});
|
|
53
56
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WaveProvider.mjs","names":["React","WaveContext","useWaveTheme","WaveProvider","themeProp","dirProp","portalContainerProp","inputDefaultsProp","rest","parent","theme","dir","portalContainer","size","appearance","inputDefaults","merged","resolvedTheme","isWaveTheme","themeClassName","getThemeClassName","warnOnce","WAVE_THEMES","value","cn","waveProviderClassNames","className"],"sources":["../../../src/components/provider/WaveProvider.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { warnOnce } from '../../lib/dev';\nimport { waveProviderClassNames } from '../../lib/classNames';\nimport { getThemeClassName, isWaveTheme, WAVE_THEMES, type WaveTheme } from '../../lib/theme';\nimport type { CoreSize, InputAppearance } from '../../lib/types';\n\n// The theme helper lives in server-safe src/lib (no \"use client\"), so React Server Components can\n// call it; it stays exported from here too.\nexport { getThemeClassName } from '../../lib/theme';\nexport type { WaveTheme } from '../../lib/theme';\n\n/** Text direction for bidirectional layout support. */\nexport type WaveDir = 'ltr' | 'rtl';\n\n/** Default size and appearance of the text inputs and pickers in a subtree. */\nexport interface InputDefaults {\n /** Default `size` of Input, Textarea, Select, SearchBox, SpinButton and the pickers. */\n size?: CoreSize;\n /** Default `appearance` of the same controls. */\n appearance?: InputAppearance;\n}\n\n/** Props accepted by {@link WaveProvider}. */\nexport interface WaveProviderProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Visual theme applied to the subtree. Providers can be nested in any order: each root declares\n * its theme's tokens, so a light panel inside a dark app (and the reverse) renders correctly.\n * @default the enclosing WaveProvider's theme, else 'light'\n */\n theme?: WaveTheme;\n /**\n * Text direction for the subtree (also read by portaled overlays and keyboard navigation).\n * @default the enclosing WaveProvider's direction, else 'ltr'\n */\n dir?: WaveDir;\n /**\n * Element that portaled overlays (dialogs, popovers, menus, toasts) render into. `null` renders\n * them into `document.body`, also inside a provider that sets a container.\n * @default the enclosing WaveProvider's container, else document.body\n */\n portalContainer?: HTMLElement | null;\n /**\n * Default `size` and `appearance` of the text inputs and pickers in the subtree (Input,\n * Textarea, Select, SearchBox, SpinButton, Combobox, Dropdown, DatePicker, TimePicker,\n * TagPicker). Their own props and a Field's `size` win. A nested provider merges its keys over\n * the enclosing provider's; an omitted key is inherited.\n * @default the enclosing WaveProvider's defaults, else none (`medium`, `outline`)\n */\n inputDefaults?: InputDefaults;\n /** Content rendered inside the themed container. */\n children: React.ReactNode;\n /** Ref to the themed root `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/** Value provided by {@link WaveProvider} and returned by {@link useWaveTheme}. */\nexport interface WaveContextValue {\n /** The active theme. */\n theme: WaveTheme;\n /** The active text direction. */\n dir: WaveDir;\n /**\n * Theme classes of the nearest provider (see {@link getThemeClassName}); portals put them on\n * their wrapper so overlays inherit the theme. `''` outside a provider (the page's own theme).\n */\n themeClassName: string;\n /** Element portaled overlays render into; `null` means `document.body`. */\n portalContainer: HTMLElement | null;\n /** The merged input defaults of the providers above (`{}` outside a provider). */\n inputDefaults: InputDefaults;\n /** `false` when no {@link WaveProvider} is above the caller (the defaults are returned). */\n hasProvider: boolean;\n}\n\nconst DEFAULT_CONTEXT: WaveContextValue = {\n theme: 'light',\n dir: 'ltr',\n themeClassName: '',\n portalContainer: null,\n inputDefaults: {},\n hasProvider: false,\n};\n\nconst WaveContext = React.createContext<WaveContextValue>(DEFAULT_CONTEXT);\n\n/**\n * Reads the theme, direction, theme classes and portal container of the nearest\n * {@link WaveProvider}. Outside a provider it returns light/ltr defaults with `hasProvider: false`.\n *\n * @returns The {@link WaveContextValue} of the nearest provider.\n */\nexport function useWaveTheme(): WaveContextValue {\n return React.useContext(WaveContext);\n}\n\n/**\n * Applies Wave theming to an application or subtree.\n *\n * - Renders a `<div class=\"wave-root wave-<theme>\">` that declares the theme's tokens and paints\n * the themed background, text colour and font (`bg-background text-foreground font-wave\n * text-body-1`; a `className` passed by you wins). The precompiled `./styles` entry scopes its base\n * styles and native-element reset to this root, so it is required there.\n * - Sets `dir` and `data-wave-theme` on the root. An unknown `theme` value (from untyped code)\n * falls back to `'light'` — classes, `data-wave-theme` and the context value — and warns once in\n * development.\n * - Provides theme, direction, theme classes and the portal container through context, so\n * portaled overlays render with the same theme and direction.\n * - Nests: a provider inherits every prop it omits (`theme`, `dir`, `portalContainer`) from the\n * enclosing provider, so `<WaveProvider theme=\"light\">` inside an RTL app is a light panel that\n * stays right-to-left and keeps the app's portal container. At the top level the defaults are\n * `'light'`, `'ltr'` and `document.body`.\n * - `inputDefaults` nests differently: a nested provider's defined keys override the enclosing\n * provider's, and an omitted key is inherited, so setting only `appearance` in a nested provider\n * keeps the enclosing provider's `size`.\n */\nexport const WaveProvider = ({\n theme: themeProp,\n dir: dirProp,\n portalContainer: portalContainerProp,\n inputDefaults: inputDefaultsProp,\n children,\n className,\n ref,\n ...rest\n}: WaveProviderProps) => {\n // Omitted props come from the enclosing provider; outside one, DEFAULT_CONTEXT supplies the\n // top-level defaults. An explicit `portalContainer={null}` means document.body, so only\n // `undefined` inherits.\n const parent = React.useContext(WaveContext);\n const theme = themeProp ?? parent.theme;\n const dir = dirProp ?? parent.dir;\n const portalContainer =\n portalContainerProp === undefined ? parent.portalContainer : portalContainerProp;\n\n // Defined keys override the enclosing provider's; the merged object keeps its identity while the\n // two values are equal, so an inline `inputDefaults={{ … }}` does not re-render every input.\n const size = inputDefaultsProp?.size ?? parent.inputDefaults.size;\n const appearance = inputDefaultsProp?.appearance ?? parent.inputDefaults.appearance;\n const inputDefaults = React.useMemo<InputDefaults>(() => {\n const merged: InputDefaults = {};\n if (size !== undefined) merged.size = size;\n if (appearance !== undefined) merged.appearance = appearance;\n return merged;\n }, [size, appearance]);\n\n // An unknown value (untyped callers) renders, and is reported, as the light theme.\n const resolvedTheme: WaveTheme = isWaveTheme(theme) ? theme : 'light';\n const themeClassName = getThemeClassName(resolvedTheme);\n\n React.useEffect(() => {\n if (!isWaveTheme(theme)) {\n warnOnce(\n `WaveProvider:theme:${String(theme)}`,\n `WaveProvider: unknown theme \"${String(theme)}\". Valid themes: ${WAVE_THEMES.join(', ')}. Using \"light\".`,\n );\n }\n }, [theme]);\n\n const value = React.useMemo<WaveContextValue>(\n () => ({\n theme: resolvedTheme,\n dir,\n themeClassName,\n portalContainer,\n inputDefaults,\n hasProvider: true,\n }),\n [resolvedTheme, dir, themeClassName, portalContainer, inputDefaults],\n );\n\n return (\n <WaveContext.Provider value={value}>\n <div\n {...rest}\n ref={ref}\n dir={dir}\n data-wave-theme={resolvedTheme}\n className={cn(\n waveProviderClassNames.root,\n themeClassName,\n 'bg-background text-foreground font-wave text-body-1',\n className,\n )}\n >\n {children}\n </div>\n </WaveContext.Provider>\n );\n};\n\nWaveProvider.displayName = 'WaveProvider';\n"],"mappings":";;;;;;;;AAoFA,IAAMC,IAAcD,EAAM,cAAgC;CARxD,OAAO;CACP,KAAK;CACL,gBAAgB;CAChB,iBAAiB;CACjB,eAAe,CAAC;CAChB,aAAa;AAG2C,CAAe;AAQzE,SAAgBE,IAAiC;CAC/C,OAAOF,EAAM,WAAWC,CAAW;AACrC;AAsBA,IAAaE,KAAgB,EAC3B,OAAOC,GACP,KAAKC,GACL,iBAAiBC,GACjB,eAAeC,GACf,aACA,cACA,QACA,GAAGC,QACoB;CAIvB,IAAMC,IAAST,EAAM,WAAWC,CAAW,GACrCS,IAAQN,KAAaK,EAAO,OAC5BE,IAAMN,KAAWI,EAAO,KACxBG,IACJN,MAAwB,KAAA,IAAYG,EAAO,kBAAkBH,GAIzDO,IAAON,GAAmB,QAAQE,EAAO,cAAc,MACvDK,IAAaP,GAAmB,cAAcE,EAAO,cAAc,YACnEM,IAAgBf,EAAM,cAA6B;EACvD,IAAMgB,IAAwB,CAAC;EAG/B,OAFIH,MAAS,KAAA,MAAW,EAAO,OAAOA,IAClCC,MAAe,KAAA,MAAW,EAAO,aAAaA,IAC3CE;CACT,GAAG,CAACH,GAAMC,CAAU,CAAC,GAGfG,IAA2BC,EAAYR,CAAK,IAAIA,IAAQ,SACxDS,IAAiBC,EAAkBH,CAAa;CAEtD,EAAM,gBAAgB;EACpB,AAAKC,EAAYR,CAAK,KACpBW,EACE,sBAAsB,OAAOX,CAAK,KAClC,gCAAgC,OAAOA,CAAK,EAAE,mBAAmBY,EAAY,KAAK,IAAI,EAAE,iBAC1F;CAEJ,GAAG,CAACZ,CAAK,CAAC;CAEV,IAAMa,IAAQvB,EAAM,eACX;EACL,OAAOiB;EACP;EACA;EACA;EACA;EACA,aAAa;CACf,IACA;EAACA;EAAeN;EAAKQ;EAAgBP;EAAiBG;CAAa,CACrE;CAEA,OACE,kBAACd,EAAY,UAAb;EAA6B;EAC3B,UAAA,kBAAC,OAAD;GACE,GAAIO;GACC;GACA;GACL,mBAAiBS;GACjB,WAAWO,EACTC,EAAuB,MACvBN,GACA,uDACAO,CACF;GAEC;EACE,CAAA;CACe,CAAA;AAE1B;AAEA,EAAa,cAAc"}
|
|
1
|
+
{"version":3,"file":"WaveProvider.mjs","names":["React","WaveContext","useWaveTheme","WaveProvider","themeProp","dirProp","portalContainerProp","inputDefaultsProp","motionProp","rest","parent","theme","dir","motion","portalContainer","size","appearance","inputDefaults","merged","resolvedTheme","isWaveTheme","themeClassName","getThemeClassName","warnOnce","WAVE_THEMES","value","cn","waveProviderClassNames","className"],"sources":["../../../src/components/provider/WaveProvider.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { warnOnce } from '../../lib/dev';\nimport { waveProviderClassNames } from '../../lib/classNames';\nimport { getThemeClassName, isWaveTheme, WAVE_THEMES, type WaveTheme } from '../../lib/theme';\nimport type { CoreSize, InputAppearance, MotionPreference } from '../../lib/types';\n\n// The theme helper lives in server-safe src/lib (no \"use client\"), so React Server Components can\n// call it; it stays exported from here too.\nexport { getThemeClassName } from '../../lib/theme';\nexport type { WaveTheme } from '../../lib/theme';\n\n/** Text direction for bidirectional layout support. */\nexport type WaveDir = 'ltr' | 'rtl';\n\n/** Default size and appearance of the text inputs and pickers in a subtree. */\nexport interface InputDefaults {\n /** Default `size` of Input, Textarea, Select, SearchBox, SpinButton and the pickers. */\n size?: CoreSize;\n /** Default `appearance` of the same controls. */\n appearance?: InputAppearance;\n}\n\n/** Props accepted by {@link WaveProvider}. */\nexport interface WaveProviderProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Visual theme applied to the subtree. Providers can be nested in any order: each root declares\n * its theme's tokens, so a light panel inside a dark app (and the reverse) renders correctly.\n * @default the enclosing WaveProvider's theme, else 'light'\n */\n theme?: WaveTheme;\n /**\n * Text direction for the subtree (also read by portaled overlays and keyboard navigation).\n * @default the enclosing WaveProvider's direction, else 'ltr'\n */\n dir?: WaveDir;\n /**\n * Element that portaled overlays (dialogs, popovers, menus, toasts) render into. `null` renders\n * them into `document.body`, also inside a provider that sets a container.\n * @default the enclosing WaveProvider's container, else document.body\n */\n portalContainer?: HTMLElement | null;\n /**\n * Default `size` and `appearance` of the text inputs and pickers in the subtree (Input,\n * Textarea, Select, SearchBox, SpinButton, Combobox, Dropdown, DatePicker, TimePicker,\n * TagPicker). Their own props and a Field's `size` win. A nested provider merges its keys over\n * the enclosing provider's; an omitted key is inherited.\n * @default the enclosing WaveProvider's defaults, else none (`medium`, `outline`)\n */\n inputDefaults?: InputDefaults;\n /**\n * Motion of the subtree. `'none'` makes every motion slot below it (a part's `motion`,\n * `backdropMotion`, `expandIconMotion` or `indeterminateMotion` left unset) behave as `null`,\n * which also stops the slide of TabList's selection line, and turns off Carousel's slide or fade\n * (a Carousel without its own `motion`) and the reveal of Nav item actions; a part's own value\n * still wins. Transitions that are no motion slot keep running: the turn of Accordion's and\n * Tree's chevrons, colour changes on hover and press. Fluent's `MotionBehaviourProvider` with `'skip'` is the counterpart, which\n * `@fluentui/react-components` does not export. A nested provider inherits an omitted `motion`\n * like `theme`.\n * @default the enclosing WaveProvider's motion, else 'default'\n */\n motion?: MotionPreference;\n /** Content rendered inside the themed container. */\n children: React.ReactNode;\n /** Ref to the themed root `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/** Value provided by {@link WaveProvider} and returned by {@link useWaveTheme}. */\nexport interface WaveContextValue {\n /** The active theme. */\n theme: WaveTheme;\n /** The active text direction. */\n dir: WaveDir;\n /**\n * Theme classes of the nearest provider (see {@link getThemeClassName}); portals put them on\n * their wrapper so overlays inherit the theme. `''` outside a provider (the page's own theme).\n */\n themeClassName: string;\n /** Element portaled overlays render into; `null` means `document.body`. */\n portalContainer: HTMLElement | null;\n /** The merged input defaults of the providers above (`{}` outside a provider). */\n inputDefaults: InputDefaults;\n /** The motion preference of the providers above (`'default'` outside a provider). */\n motion: MotionPreference;\n /** `false` when no {@link WaveProvider} is above the caller (the defaults are returned). */\n hasProvider: boolean;\n}\n\nconst DEFAULT_CONTEXT: WaveContextValue = {\n theme: 'light',\n dir: 'ltr',\n themeClassName: '',\n portalContainer: null,\n inputDefaults: {},\n motion: 'default',\n hasProvider: false,\n};\n\nconst WaveContext = React.createContext<WaveContextValue>(DEFAULT_CONTEXT);\n\n/**\n * Reads the theme, direction, theme classes and portal container of the nearest\n * {@link WaveProvider}. Outside a provider it returns light/ltr defaults with `hasProvider: false`.\n *\n * @returns The {@link WaveContextValue} of the nearest provider.\n */\nexport function useWaveTheme(): WaveContextValue {\n return React.useContext(WaveContext);\n}\n\n/**\n * Applies Wave theming to an application or subtree.\n *\n * - Renders a `<div class=\"wave-root wave-<theme>\">` that declares the theme's tokens and paints\n * the themed background, text colour and font (`bg-background text-foreground font-wave\n * text-body-1`; a `className` passed by you wins). The precompiled `./styles` entry scopes its base\n * styles and native-element reset to this root, so it is required there.\n * - Sets `dir` and `data-wave-theme` on the root. An unknown `theme` value (from untyped code)\n * falls back to `'light'` — classes, `data-wave-theme` and the context value — and warns once in\n * development.\n * - Provides theme, direction, theme classes and the portal container through context, so\n * portaled overlays render with the same theme and direction.\n * - Nests: a provider inherits every prop it omits (`theme`, `dir`, `portalContainer`) from the\n * enclosing provider, so `<WaveProvider theme=\"light\">` inside an RTL app is a light panel that\n * stays right-to-left and keeps the app's portal container. At the top level the defaults are\n * `'light'`, `'ltr'` and `document.body`.\n * - `inputDefaults` nests differently: a nested provider's defined keys override the enclosing\n * provider's, and an omitted key is inherited, so setting only `appearance` in a nested provider\n * keeps the enclosing provider's `size`.\n * - `motion=\"none\"` turns the default motion of every animated part below it off (its motion\n * slots, Carousel's slide, TabList's sliding line, the reveal of Nav item actions; a part's own\n * `motion` still wins), while transitions without a slot keep running (Accordion's and Tree's\n * chevron turns, colour changes); a nested provider inherits an omitted `motion` like `theme`.\n */\nexport const WaveProvider = ({\n theme: themeProp,\n dir: dirProp,\n portalContainer: portalContainerProp,\n inputDefaults: inputDefaultsProp,\n motion: motionProp,\n children,\n className,\n ref,\n ...rest\n}: WaveProviderProps) => {\n // Omitted props come from the enclosing provider; outside one, DEFAULT_CONTEXT supplies the\n // top-level defaults. An explicit `portalContainer={null}` means document.body, so only\n // `undefined` inherits.\n const parent = React.useContext(WaveContext);\n const theme = themeProp ?? parent.theme;\n const dir = dirProp ?? parent.dir;\n const motion = motionProp ?? parent.motion;\n const portalContainer =\n portalContainerProp === undefined ? parent.portalContainer : portalContainerProp;\n\n // Defined keys override the enclosing provider's; the merged object keeps its identity while the\n // two values are equal, so an inline `inputDefaults={{ … }}` does not re-render every input.\n const size = inputDefaultsProp?.size ?? parent.inputDefaults.size;\n const appearance = inputDefaultsProp?.appearance ?? parent.inputDefaults.appearance;\n const inputDefaults = React.useMemo<InputDefaults>(() => {\n const merged: InputDefaults = {};\n if (size !== undefined) merged.size = size;\n if (appearance !== undefined) merged.appearance = appearance;\n return merged;\n }, [size, appearance]);\n\n // An unknown value (untyped callers) renders, and is reported, as the light theme.\n const resolvedTheme: WaveTheme = isWaveTheme(theme) ? theme : 'light';\n const themeClassName = getThemeClassName(resolvedTheme);\n\n React.useEffect(() => {\n if (!isWaveTheme(theme)) {\n warnOnce(\n `WaveProvider:theme:${String(theme)}`,\n `WaveProvider: unknown theme \"${String(theme)}\". Valid themes: ${WAVE_THEMES.join(', ')}. Using \"light\".`,\n );\n }\n }, [theme]);\n\n const value = React.useMemo<WaveContextValue>(\n () => ({\n theme: resolvedTheme,\n dir,\n themeClassName,\n portalContainer,\n inputDefaults,\n motion,\n hasProvider: true,\n }),\n [resolvedTheme, dir, themeClassName, portalContainer, inputDefaults, motion],\n );\n\n return (\n <WaveContext.Provider value={value}>\n <div\n {...rest}\n ref={ref}\n dir={dir}\n data-wave-theme={resolvedTheme}\n className={cn(\n waveProviderClassNames.root,\n themeClassName,\n 'bg-background text-foreground font-wave text-body-1',\n className,\n )}\n >\n {children}\n </div>\n </WaveContext.Provider>\n );\n};\n\nWaveProvider.displayName = 'WaveProvider';\n"],"mappings":";;;;;;;;AAmGA,IAAMC,IAAcD,EAAM,cAAgC;CATxD,OAAO;CACP,KAAK;CACL,gBAAgB;CAChB,iBAAiB;CACjB,eAAe,CAAC;CAChB,QAAQ;CACR,aAAa;AAG2C,CAAe;AAQzE,SAAgBE,IAAiC;CAC/C,OAAOF,EAAM,WAAWC,CAAW;AACrC;AA0BA,IAAaE,KAAgB,EAC3B,OAAOC,GACP,KAAKC,GACL,iBAAiBC,GACjB,eAAeC,GACf,QAAQC,GACR,aACA,cACA,QACA,GAAGC,QACoB;CAIvB,IAAMC,IAASV,EAAM,WAAWC,CAAW,GACrCU,IAAQP,KAAaM,EAAO,OAC5BE,IAAMP,KAAWK,EAAO,KACxBG,IAASL,KAAcE,EAAO,QAC9BI,IACJR,MAAwB,KAAA,IAAYI,EAAO,kBAAkBJ,GAIzDS,IAAOR,GAAmB,QAAQG,EAAO,cAAc,MACvDM,IAAaT,GAAmB,cAAcG,EAAO,cAAc,YACnEO,IAAgBjB,EAAM,cAA6B;EACvD,IAAMkB,IAAwB,CAAC;EAG/B,OAFIH,MAAS,KAAA,MAAW,EAAO,OAAOA,IAClCC,MAAe,KAAA,MAAW,EAAO,aAAaA,IAC3CE;CACT,GAAG,CAACH,GAAMC,CAAU,CAAC,GAGfG,IAA2BC,EAAYT,CAAK,IAAIA,IAAQ,SACxDU,IAAiBC,EAAkBH,CAAa;CAEtD,EAAM,gBAAgB;EACpB,AAAKC,EAAYT,CAAK,KACpBY,EACE,sBAAsB,OAAOZ,CAAK,KAClC,gCAAgC,OAAOA,CAAK,EAAE,mBAAmBa,EAAY,KAAK,IAAI,EAAE,iBAC1F;CAEJ,GAAG,CAACb,CAAK,CAAC;CAEV,IAAMc,IAAQzB,EAAM,eACX;EACL,OAAOmB;EACP;EACA;EACA;EACA;EACA;EACA,aAAa;CACf,IACA;EAACA;EAAeP;EAAKS;EAAgBP;EAAiBG;EAAeJ;CAAM,CAC7E;CAEA,OACE,kBAACZ,EAAY,UAAb;EAA6B;EAC3B,UAAA,kBAAC,OAAD;GACE,GAAIQ;GACC;GACA;GACL,mBAAiBU;GACjB,WAAWO,EACTC,EAAuB,MACvBN,GACA,uDACAO,CACF;GAEC;EACE,CAAA;CACe,CAAA;AAE1B;AAEA,EAAa,cAAc"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";const e=require("../lib/cn.cjs"),t=require("./usePresence.cjs"),n=require("./usePreserveFocus.cjs"),r=require("../lib/motion.cjs"),i=require("./useMotion.cjs");function a(a,o){let{motion:s,partRef:c,getTrigger:l}=o,{isMounted:u,phase:d,ref:f,presenceProps:p}=t.usePresence(a),m=i.useMotionClasses(s,r.collapseMotion);return n.usePreserveFocus(c,l,{enabled:a}),{isMounted:u,phase:d,wrapperRef:f,presenceProps:p,className:e.cn(r.collapseStructure,m)}}exports.useCollapsePresence=a;
|
|
2
|
+
//# sourceMappingURL=useCollapsePresence.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCollapsePresence.cjs","names":["usePresence","useMotionClasses","collapseMotion","cn","collapseStructure","require_cn","require_usePresence","require_usePreserveFocus","require_motion","require_useMotion","useCollapsePresence","options","open","motionClassName","motion","partRef","getTrigger","ref"],"sources":["../../src/hooks/useCollapsePresence.ts"],"sourcesContent":["import type * as React from 'react';\nimport { cn } from '../lib/cn';\nimport { collapseMotion, collapseStructure } from '../lib/motion';\nimport type { MotionSlot } from '../lib/types';\nimport { useMotionClasses } from './useMotion';\nimport { usePreserveFocus } from './usePreserveFocus';\nimport { usePresence, type PresenceAttributes, type PresencePhase } from './usePresence';\n\n/** Options of {@link useCollapsePresence}. */\nexport interface UseCollapsePresenceOptions {\n /**\n * The disclosure's motion slot: `undefined` the default collapse, `null` none, a value replaces\n * it (see `MotionSlot`); the provider's `'none'` acts as `null` unless a value is given.\n */\n motion: MotionSlot | null | undefined;\n /** The part that may hold focus when it collapses. */\n partRef: React.RefObject<HTMLElement | null>;\n /** Where that focus goes: the disclosure's own trigger. */\n getTrigger: () => HTMLElement | null | undefined;\n}\n\n/** Returned by {@link useCollapsePresence}. */\nexport interface UseCollapsePresenceResult {\n /** Whether to render the wrapper and the part: while open, and while the part exits. */\n isMounted: boolean;\n /** The wrapper's phase. */\n phase: PresencePhase;\n /** Callback ref for the wrapper (stable): the presence element, whose motion ends the phases. */\n wrapperRef: React.RefCallback<HTMLElement>;\n /** Spread onto the wrapper: `data-presence` always, `inert` while the part exits. */\n presenceProps: PresenceAttributes;\n /** collapseStructure plus the resolved motion; the caller puts its stable class first. */\n className: string;\n}\n\n/**\n * The collapse of a disclosure's part (Accordion's region, a Tree group, a Nav category's list):\n * the part renders as the only child of a wrapper element, which mounts through the presence core\n * and carries the collapse structure and motion of `src/lib/motion.ts`. The part itself keeps its\n * own props, ref, id and role.\n *\n * - **Mounting.** `usePresence(open)` without `appear`: a part open at mount (on the server, when\n * hydrating and in a client render) is `entered`, since in-flow content never animates on mount.\n * Closing keeps the wrapper and the part mounted, `inert`, until the wrapper's motion ends;\n * opening again during the exit goes back to `entering` with the same elements, so the state\n * inside the part survives. Under reduced motion, with `motion: null` or the provider's `'none'`,\n * a phase ends in the commit that starts it.\n * - **Structure.** While entered the wrapper renders no box, so the part lays out in its parent as\n * it did without the wrapper; while it enters or exits, the wrapper is a clipping one-track grid\n * of rows whose minimum is zero, with the part at the top of the track at its own height (so it\n * is clipped, never squeezed), and the default motion moves that track between zero and the\n * part's height with a fade (200 ms both ways).\n * - **Focus.** Focus inside the part when the disclosure closes moves to `getTrigger()` in the\n * closing commit (`usePreserveFocus` with `enabled: open`), so it never stays in the `inert`,\n * exiting part. A part unmounted while open with focus inside (its disclosure removed) moves\n * focus there after the commit, when that trigger is still in the document.\n *\n * Destructure the result: react-hooks/refs treats an object whose member is passed to `ref` as a\n * ref, so reading its other members during render is an error.\n *\n * @example\n * const partRef = useRef<HTMLDivElement | null>(null);\n * const { isMounted, wrapperRef, presenceProps, className } = useCollapsePresence(open, {\n * motion,\n * partRef,\n * getTrigger: () => triggerRef.current,\n * });\n * return isMounted ? (\n * <div ref={wrapperRef} {...presenceProps} className={cn(stableCollapseClass, className)}>\n * <div ref={partRef} role=\"region\">…</div>\n * </div>\n * ) : null;\n *\n * @param open - Whether the disclosure is open.\n * @param options - See {@link UseCollapsePresenceOptions}.\n */\nexport function useCollapsePresence(\n open: boolean,\n options: UseCollapsePresenceOptions,\n): UseCollapsePresenceResult {\n const { motion, partRef, getTrigger } = options;\n const { isMounted, phase, ref, presenceProps } = usePresence(open);\n const motionClassName = useMotionClasses(motion, collapseMotion);\n usePreserveFocus(partRef, getTrigger, { enabled: open });\n return {\n isMounted,\n phase,\n wrapperRef: ref,\n presenceProps,\n className: cn(collapseStructure, motionClassName),\n };\n}\n"],"mappings":"6KA4EA,SAAgBU,EACd,EACA,EAC2B,CAC3B,GAAM,CAAE,SAAQ,UAAS,cAAeC,EAClC,CAAE,YAAW,QAAO,MAAK,iBAAkBX,EAAAA,YAAYY,CAAI,EAC3DC,EAAkBZ,EAAAA,iBAAiBa,EAAQZ,EAAAA,cAAc,EAE/D,OADAK,EAAA,iBAAiBQ,EAASC,EAAY,CAAE,QAASJ,CAAK,CAAC,EAChD,CACL,YACA,QACA,WAAYK,EACZ,gBACA,UAAWd,EAAAA,GAAGC,EAAAA,kBAAmBS,CAAe,CAClD,CACF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.mjs";
|
|
3
|
+
import { usePresence as t } from "./usePresence.mjs";
|
|
4
|
+
import { usePreserveFocus as n } from "./usePreserveFocus.mjs";
|
|
5
|
+
import { collapseMotion as r, collapseStructure as i } from "../lib/motion.mjs";
|
|
6
|
+
import { useMotionClasses as a } from "./useMotion.mjs";
|
|
7
|
+
//#region src/hooks/useCollapsePresence.ts
|
|
8
|
+
function o(o, s) {
|
|
9
|
+
let { motion: c, partRef: l, getTrigger: u } = s, { isMounted: d, phase: f, ref: p, presenceProps: m } = t(o), h = a(c, r);
|
|
10
|
+
return n(l, u, { enabled: o }), {
|
|
11
|
+
isMounted: d,
|
|
12
|
+
phase: f,
|
|
13
|
+
wrapperRef: p,
|
|
14
|
+
presenceProps: m,
|
|
15
|
+
className: e(i, h)
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { o as useCollapsePresence };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=useCollapsePresence.mjs.map
|