@tinybigui/react 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +3210 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +53 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.cts +3096 -11
- package/dist/index.d.ts +3096 -11
- package/dist/index.js +3170 -21
- package/dist/index.js.map +1 -1
- package/dist/styles.css +87 -2
- package/dist/styles.css.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1,15 +1,49 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
|
-
import {
|
|
2
|
+
import { extendTailwindMerge } from 'tailwind-merge';
|
|
3
3
|
import { argbFromHex, themeFromSourceColor } from '@material/material-color-utilities';
|
|
4
4
|
export { argbFromHex, hexFromArgb } from '@material/material-color-utilities';
|
|
5
|
-
import { forwardRef, useRef, useEffect, createContext, useContext,
|
|
6
|
-
import {
|
|
7
|
-
import { mergeProps, filterDOMProps } from '@react-aria/utils';
|
|
8
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
5
|
+
import { forwardRef, useRef, useEffect, createContext, useContext, useMemo, useCallback, useState, useLayoutEffect, useId, Children, isValidElement, cloneElement } from 'react';
|
|
6
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
9
7
|
import { cva } from 'class-variance-authority';
|
|
10
|
-
import {
|
|
8
|
+
import { useButton, useTextField, useFocusRing, useCheckbox, VisuallyHidden, mergeProps as mergeProps$1, useSwitch, useRadioGroup, useRadio, useTabList, useTab, useTabPanel, FocusScope, usePreventScroll, useDialog, useOverlay, useLink, useProgressBar } from 'react-aria';
|
|
9
|
+
import { mergeProps, filterDOMProps } from '@react-aria/utils';
|
|
10
|
+
import { useToggleState, useRadioGroupState, useTabListState, Item, useOverlayTriggerState } from 'react-stately';
|
|
11
|
+
import { MenuItem as MenuItem$1, Menu as Menu$1, MenuTrigger as MenuTrigger$1, Popover, MenuSection as MenuSection$1, Separator, Header, useSlottedContext, ButtonContext } from 'react-aria-components';
|
|
12
|
+
import { createPortal } from 'react-dom';
|
|
11
13
|
|
|
12
14
|
// src/utils/cn.ts
|
|
15
|
+
var twMerge = extendTailwindMerge({
|
|
16
|
+
extend: {
|
|
17
|
+
classGroups: {
|
|
18
|
+
"font-size": [
|
|
19
|
+
{
|
|
20
|
+
text: [
|
|
21
|
+
// MD3 Display scale
|
|
22
|
+
"display-large",
|
|
23
|
+
"display-medium",
|
|
24
|
+
"display-small",
|
|
25
|
+
// MD3 Headline scale
|
|
26
|
+
"headline-large",
|
|
27
|
+
"headline-medium",
|
|
28
|
+
"headline-small",
|
|
29
|
+
// MD3 Title scale
|
|
30
|
+
"title-large",
|
|
31
|
+
"title-medium",
|
|
32
|
+
"title-small",
|
|
33
|
+
// MD3 Body scale
|
|
34
|
+
"body-large",
|
|
35
|
+
"body-medium",
|
|
36
|
+
"body-small",
|
|
37
|
+
// MD3 Label scale
|
|
38
|
+
"label-large",
|
|
39
|
+
"label-medium",
|
|
40
|
+
"label-small"
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
});
|
|
13
47
|
function cn(...inputs) {
|
|
14
48
|
return twMerge(clsx(inputs));
|
|
15
49
|
}
|
|
@@ -130,6 +164,162 @@ function truncateText(lines = 1) {
|
|
|
130
164
|
textOverflow: "ellipsis"
|
|
131
165
|
};
|
|
132
166
|
}
|
|
167
|
+
function useScrollElevation(options = {}) {
|
|
168
|
+
const { scrolled: controlledScrolled, onScrollStateChange, threshold = 0 } = options;
|
|
169
|
+
const isControlled = controlledScrolled !== void 0;
|
|
170
|
+
const [internalScrolled, setInternalScrolled] = useState(false);
|
|
171
|
+
const handleScroll = useCallback(() => {
|
|
172
|
+
const currentlyScrolled = window.scrollY > threshold;
|
|
173
|
+
setInternalScrolled((prev) => {
|
|
174
|
+
if (prev !== currentlyScrolled) {
|
|
175
|
+
onScrollStateChange?.(currentlyScrolled);
|
|
176
|
+
return currentlyScrolled;
|
|
177
|
+
}
|
|
178
|
+
return prev;
|
|
179
|
+
});
|
|
180
|
+
}, [threshold, onScrollStateChange]);
|
|
181
|
+
useEffect(() => {
|
|
182
|
+
if (isControlled) return;
|
|
183
|
+
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
184
|
+
handleScroll();
|
|
185
|
+
return () => {
|
|
186
|
+
window.removeEventListener("scroll", handleScroll);
|
|
187
|
+
};
|
|
188
|
+
}, [isControlled, handleScroll]);
|
|
189
|
+
return {
|
|
190
|
+
isScrolled: isControlled ? controlledScrolled : internalScrolled
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
var AppBarHeadless = forwardRef(
|
|
194
|
+
({ className, children, scrolled: scrolledProp, onScrollStateChange }, ref) => {
|
|
195
|
+
const { isScrolled } = useScrollElevation({
|
|
196
|
+
scrolled: scrolledProp,
|
|
197
|
+
onScrollStateChange
|
|
198
|
+
});
|
|
199
|
+
return /* @__PURE__ */ jsx("header", { ref, role: "banner", className, "data-scrolled": isScrolled, children });
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
AppBarHeadless.displayName = "AppBarHeadless";
|
|
203
|
+
var appBarVariants = cva(
|
|
204
|
+
[
|
|
205
|
+
// Base classes (always applied)
|
|
206
|
+
"w-full",
|
|
207
|
+
"bg-surface text-on-surface",
|
|
208
|
+
"flex flex-col",
|
|
209
|
+
// Elevation transition using MD3 motion tokens
|
|
210
|
+
"transition-shadow duration-medium2 ease-standard"
|
|
211
|
+
],
|
|
212
|
+
{
|
|
213
|
+
variants: {
|
|
214
|
+
/**
|
|
215
|
+
* Size variant (MD3 specification)
|
|
216
|
+
* Controls bar height, title placement, and type scale
|
|
217
|
+
*/
|
|
218
|
+
variant: {
|
|
219
|
+
/** 64dp, title left-aligned, title-large */
|
|
220
|
+
small: "h-appbar-small",
|
|
221
|
+
/** 64dp, title centered, title-large */
|
|
222
|
+
"center-aligned": "h-appbar-small variant-center-aligned",
|
|
223
|
+
/** 112dp, title bottom-left, headline-small */
|
|
224
|
+
medium: "h-appbar-medium",
|
|
225
|
+
/** 152dp, title bottom-left, display-small */
|
|
226
|
+
large: "h-appbar-large"
|
|
227
|
+
},
|
|
228
|
+
/**
|
|
229
|
+
* Scroll state — controls surface elevation
|
|
230
|
+
* MD3: flat at rest, elevated on scroll
|
|
231
|
+
*/
|
|
232
|
+
scrolled: {
|
|
233
|
+
false: "shadow-elevation-0",
|
|
234
|
+
true: "shadow-elevation-2"
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
defaultVariants: {
|
|
238
|
+
variant: "small",
|
|
239
|
+
scrolled: false
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
);
|
|
243
|
+
var appBarTitleVariants = cva("text-on-surface font-normal truncate", {
|
|
244
|
+
variants: {
|
|
245
|
+
variant: {
|
|
246
|
+
small: "text-title-large",
|
|
247
|
+
"center-aligned": "text-title-large",
|
|
248
|
+
medium: "text-headline-small",
|
|
249
|
+
large: "text-display-small"
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
defaultVariants: {
|
|
253
|
+
variant: "small"
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
var AppBar = forwardRef(
|
|
257
|
+
({
|
|
258
|
+
variant = "small",
|
|
259
|
+
title,
|
|
260
|
+
navigationIcon,
|
|
261
|
+
actions,
|
|
262
|
+
scrolled: scrolledProp,
|
|
263
|
+
onScrollStateChange,
|
|
264
|
+
className
|
|
265
|
+
}, ref) => {
|
|
266
|
+
const { isScrolled } = useScrollElevation({
|
|
267
|
+
scrolled: scrolledProp,
|
|
268
|
+
onScrollStateChange
|
|
269
|
+
});
|
|
270
|
+
const isExpandedVariant = variant === "medium" || variant === "large";
|
|
271
|
+
return /* @__PURE__ */ jsxs(
|
|
272
|
+
AppBarHeadless,
|
|
273
|
+
{
|
|
274
|
+
ref,
|
|
275
|
+
scrolled: isScrolled,
|
|
276
|
+
className: cn(appBarVariants({ variant, scrolled: isScrolled }), className),
|
|
277
|
+
children: [
|
|
278
|
+
/* @__PURE__ */ jsxs(
|
|
279
|
+
"div",
|
|
280
|
+
{
|
|
281
|
+
"data-slot": "top-row",
|
|
282
|
+
className: cn(
|
|
283
|
+
"flex items-center",
|
|
284
|
+
"px-1",
|
|
285
|
+
// Small and center-aligned: fill the full bar height
|
|
286
|
+
!isExpandedVariant && "flex-1",
|
|
287
|
+
// Expanded variants: fixed height for the top row (64dp)
|
|
288
|
+
isExpandedVariant && "h-16 shrink-0"
|
|
289
|
+
),
|
|
290
|
+
children: [
|
|
291
|
+
navigationIcon != null && /* @__PURE__ */ jsx("div", { "data-slot": "navigation", className: "flex shrink-0 items-center", children: navigationIcon }),
|
|
292
|
+
!isExpandedVariant && /* @__PURE__ */ jsx(
|
|
293
|
+
"span",
|
|
294
|
+
{
|
|
295
|
+
"data-testid": "appbar-title",
|
|
296
|
+
className: cn(
|
|
297
|
+
"min-w-0 flex-1 px-1",
|
|
298
|
+
appBarTitleVariants({ variant }),
|
|
299
|
+
// Center-aligned: center the title text
|
|
300
|
+
variant === "center-aligned" && "text-center"
|
|
301
|
+
),
|
|
302
|
+
children: title
|
|
303
|
+
}
|
|
304
|
+
),
|
|
305
|
+
actions != null && /* @__PURE__ */ jsx("div", { "data-slot": "actions", className: "flex shrink-0 items-center gap-0.5", children: actions })
|
|
306
|
+
]
|
|
307
|
+
}
|
|
308
|
+
),
|
|
309
|
+
isExpandedVariant && /* @__PURE__ */ jsx("div", { "data-slot": "expanded-title", className: cn("flex flex-1 items-end", "px-4 pb-4"), children: /* @__PURE__ */ jsx(
|
|
310
|
+
"span",
|
|
311
|
+
{
|
|
312
|
+
"data-testid": "appbar-title",
|
|
313
|
+
className: cn("min-w-0 truncate", appBarTitleVariants({ variant })),
|
|
314
|
+
children: title
|
|
315
|
+
}
|
|
316
|
+
) })
|
|
317
|
+
]
|
|
318
|
+
}
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
);
|
|
322
|
+
AppBar.displayName = "AppBar";
|
|
133
323
|
var ButtonHeadless = forwardRef(
|
|
134
324
|
({ className, children, tabIndex = 0, onMouseDown, type, ...restProps }, forwardedRef) => {
|
|
135
325
|
const internalRef = useRef(null);
|
|
@@ -303,7 +493,7 @@ var buttonVariants = cva(
|
|
|
303
493
|
{
|
|
304
494
|
variant: "tonal",
|
|
305
495
|
color: "primary",
|
|
306
|
-
className: "bg-
|
|
496
|
+
className: "bg-primary-container text-on-primary-container"
|
|
307
497
|
},
|
|
308
498
|
{
|
|
309
499
|
variant: "tonal",
|
|
@@ -574,7 +764,7 @@ IconButtonHeadless.displayName = "IconButtonHeadless";
|
|
|
574
764
|
var iconButtonVariants = cva(
|
|
575
765
|
[
|
|
576
766
|
// Base classes (always applied)
|
|
577
|
-
"relative inline-flex items-center justify-center",
|
|
767
|
+
"relative inline-flex items-center justify-center cursor-pointer",
|
|
578
768
|
"overflow-hidden rounded-full",
|
|
579
769
|
// Circular shape
|
|
580
770
|
"transition-all duration-200",
|
|
@@ -888,7 +1078,7 @@ FABHeadless.displayName = "FABHeadless";
|
|
|
888
1078
|
var fabVariants = cva(
|
|
889
1079
|
[
|
|
890
1080
|
// Base classes (always applied)
|
|
891
|
-
"relative inline-flex items-center justify-center",
|
|
1081
|
+
"relative inline-flex items-center justify-center cursor-pointer",
|
|
892
1082
|
"overflow-hidden",
|
|
893
1083
|
"transition-all duration-200",
|
|
894
1084
|
"focus-visible:outline-primary focus-visible:outline-2 focus-visible:outline-offset-2",
|
|
@@ -1329,8 +1519,8 @@ var textFieldLabelVariants = cva(
|
|
|
1329
1519
|
{
|
|
1330
1520
|
variants: {
|
|
1331
1521
|
variant: {
|
|
1332
|
-
filled: "top-
|
|
1333
|
-
outlined: "top-
|
|
1522
|
+
filled: "top-2.5",
|
|
1523
|
+
outlined: "top-2.5 bg-surface px-1"
|
|
1334
1524
|
},
|
|
1335
1525
|
size: {
|
|
1336
1526
|
small: "text-sm",
|
|
@@ -1338,7 +1528,7 @@ var textFieldLabelVariants = cva(
|
|
|
1338
1528
|
large: "text-lg"
|
|
1339
1529
|
},
|
|
1340
1530
|
floating: {
|
|
1341
|
-
true: "-translate-y-
|
|
1531
|
+
true: "-translate-y-5 scale-75",
|
|
1342
1532
|
false: "scale-100"
|
|
1343
1533
|
},
|
|
1344
1534
|
focused: {
|
|
@@ -1363,7 +1553,7 @@ var textFieldLabelVariants = cva(
|
|
|
1363
1553
|
{
|
|
1364
1554
|
variant: "outlined",
|
|
1365
1555
|
floating: true,
|
|
1366
|
-
className: "
|
|
1556
|
+
className: "top-2.5"
|
|
1367
1557
|
}
|
|
1368
1558
|
],
|
|
1369
1559
|
defaultVariants: {
|
|
@@ -1676,7 +1866,8 @@ var TextField = forwardRef(
|
|
|
1676
1866
|
hasLeadingIcon: !!leadingIcon,
|
|
1677
1867
|
hasTrailingIcon: !!trailingIcon,
|
|
1678
1868
|
multiline: true
|
|
1679
|
-
})
|
|
1869
|
+
}),
|
|
1870
|
+
label && "placeholder:opacity-0"
|
|
1680
1871
|
),
|
|
1681
1872
|
rows,
|
|
1682
1873
|
spellCheck: spellCheckProp
|
|
@@ -1694,7 +1885,9 @@ var TextField = forwardRef(
|
|
|
1694
1885
|
hasLeadingIcon: !!leadingIcon,
|
|
1695
1886
|
hasTrailingIcon: !!trailingIcon,
|
|
1696
1887
|
multiline: false
|
|
1697
|
-
})
|
|
1888
|
+
}),
|
|
1889
|
+
label && "placeholder:opacity-0"
|
|
1890
|
+
// Hide placeholder when there's a value to prevent overlap with floating label
|
|
1698
1891
|
),
|
|
1699
1892
|
spellCheck: spellCheckProp
|
|
1700
1893
|
}
|
|
@@ -1928,8 +2121,7 @@ var checkboxLabelVariants = cva(
|
|
|
1928
2121
|
// MD3: Body Medium (14px)
|
|
1929
2122
|
"text-on-surface",
|
|
1930
2123
|
"select-none",
|
|
1931
|
-
"ml-
|
|
1932
|
-
// 16px spacing between checkbox and label (MD3 standard)
|
|
2124
|
+
"ml-1.5"
|
|
1933
2125
|
],
|
|
1934
2126
|
{
|
|
1935
2127
|
variants: {
|
|
@@ -2191,7 +2383,7 @@ var switchHandleContainerVariants = cva(
|
|
|
2191
2383
|
*/
|
|
2192
2384
|
selected: {
|
|
2193
2385
|
true: [
|
|
2194
|
-
"left-[
|
|
2386
|
+
"left-[24px]",
|
|
2195
2387
|
// Position when ON (52px - 24px = 28px)
|
|
2196
2388
|
"text-primary"
|
|
2197
2389
|
// State layer color
|
|
@@ -2731,8 +2923,7 @@ var radioLabelVariants = cva(
|
|
|
2731
2923
|
// MD3: Body Medium (14px)
|
|
2732
2924
|
"text-on-surface",
|
|
2733
2925
|
"select-none",
|
|
2734
|
-
"ml-
|
|
2735
|
-
// 16px spacing between radio and label (MD3 standard)
|
|
2926
|
+
"ml-1.5"
|
|
2736
2927
|
],
|
|
2737
2928
|
{
|
|
2738
2929
|
variants: {
|
|
@@ -2984,7 +3175,2965 @@ var RadioHeadless = forwardRef(
|
|
|
2984
3175
|
}
|
|
2985
3176
|
);
|
|
2986
3177
|
RadioHeadless.displayName = "RadioHeadless";
|
|
3178
|
+
var HeadlessTabsContext = createContext(null);
|
|
3179
|
+
function useHeadlessTabsContext(componentName) {
|
|
3180
|
+
const context = useContext(HeadlessTabsContext);
|
|
3181
|
+
if (!context) {
|
|
3182
|
+
throw new Error(`${componentName} must be used within a Tabs component`);
|
|
3183
|
+
}
|
|
3184
|
+
return context;
|
|
3185
|
+
}
|
|
3186
|
+
var HeadlessTabList = forwardRef(
|
|
3187
|
+
({ children, className }, forwardedRef) => {
|
|
3188
|
+
const { state } = useHeadlessTabsContext("HeadlessTabList");
|
|
3189
|
+
const internalRef = useRef(null);
|
|
3190
|
+
const ref = forwardedRef ?? internalRef;
|
|
3191
|
+
const { tabListProps } = useTabList({}, state, ref);
|
|
3192
|
+
return /* @__PURE__ */ jsx("div", { ...tabListProps, ref, className, children });
|
|
3193
|
+
}
|
|
3194
|
+
);
|
|
3195
|
+
HeadlessTabList.displayName = "HeadlessTabList";
|
|
3196
|
+
var HeadlessTab = forwardRef(
|
|
3197
|
+
({ item, className, onMouseDown, children, ...props }, forwardedRef) => {
|
|
3198
|
+
const { state } = useHeadlessTabsContext("HeadlessTab");
|
|
3199
|
+
const internalRef = useRef(null);
|
|
3200
|
+
const ref = forwardedRef ?? internalRef;
|
|
3201
|
+
const { tabProps, isSelected, isDisabled, isPressed } = useTab(
|
|
3202
|
+
{
|
|
3203
|
+
key: item.key,
|
|
3204
|
+
...item.isDisabled !== void 0 && { isDisabled: item.isDisabled }
|
|
3205
|
+
},
|
|
3206
|
+
state,
|
|
3207
|
+
ref
|
|
3208
|
+
);
|
|
3209
|
+
const { isFocusVisible, focusProps } = useFocusRing();
|
|
3210
|
+
const mergedProps = mergeProps(tabProps, focusProps, {
|
|
3211
|
+
className,
|
|
3212
|
+
onMouseDown
|
|
3213
|
+
});
|
|
3214
|
+
return /* @__PURE__ */ jsx("button", { ...mergedProps, ref, type: "button", "data-key": String(item.key), ...props, children: typeof children === "function" ? children({ isSelected, isDisabled, isFocusVisible, isPressed }) : children });
|
|
3215
|
+
}
|
|
3216
|
+
);
|
|
3217
|
+
HeadlessTab.displayName = "HeadlessTab";
|
|
3218
|
+
var HeadlessTabPanel = forwardRef(
|
|
3219
|
+
({ children, className, ...props }, forwardedRef) => {
|
|
3220
|
+
const { state } = useHeadlessTabsContext("HeadlessTabPanel");
|
|
3221
|
+
const internalRef = useRef(null);
|
|
3222
|
+
const ref = forwardedRef ?? internalRef;
|
|
3223
|
+
const { tabPanelProps } = useTabPanel(props, state, ref);
|
|
3224
|
+
return /* @__PURE__ */ jsx("div", { ...tabPanelProps, ref, className, children });
|
|
3225
|
+
}
|
|
3226
|
+
);
|
|
3227
|
+
HeadlessTabPanel.displayName = "HeadlessTabPanel";
|
|
3228
|
+
var TabsContext = createContext(null);
|
|
3229
|
+
function useTabsContext() {
|
|
3230
|
+
const context = useContext(TabsContext);
|
|
3231
|
+
if (!context) {
|
|
3232
|
+
throw new Error("Component must be used within a Tabs component");
|
|
3233
|
+
}
|
|
3234
|
+
return context;
|
|
3235
|
+
}
|
|
3236
|
+
function getComponentName(type) {
|
|
3237
|
+
if (typeof type === "string") return type;
|
|
3238
|
+
const exotic = type;
|
|
3239
|
+
return exotic.displayName ?? exotic.name ?? "";
|
|
3240
|
+
}
|
|
3241
|
+
function extractTabItemsFromChildren(children) {
|
|
3242
|
+
const items = [];
|
|
3243
|
+
Children.forEach(children, (child) => {
|
|
3244
|
+
if (!isValidElement(child)) return;
|
|
3245
|
+
if (getComponentName(child.type) === "TabList") {
|
|
3246
|
+
const tabListProps = child.props;
|
|
3247
|
+
Children.forEach(tabListProps.children, (tabChild) => {
|
|
3248
|
+
if (!isValidElement(tabChild)) return;
|
|
3249
|
+
if (getComponentName(tabChild.type) === "Tab") {
|
|
3250
|
+
const tabProps = tabChild.props;
|
|
3251
|
+
items.push({
|
|
3252
|
+
key: tabProps.id,
|
|
3253
|
+
...tabProps.label !== void 0 && { label: tabProps.label },
|
|
3254
|
+
...tabProps.icon !== void 0 && { icon: tabProps.icon },
|
|
3255
|
+
...tabProps.isDisabled !== void 0 && { isDisabled: tabProps.isDisabled },
|
|
3256
|
+
...tabProps["aria-label"] !== void 0 && { "aria-label": tabProps["aria-label"] }
|
|
3257
|
+
});
|
|
3258
|
+
}
|
|
3259
|
+
});
|
|
3260
|
+
}
|
|
3261
|
+
});
|
|
3262
|
+
return items;
|
|
3263
|
+
}
|
|
3264
|
+
var Tabs = forwardRef(
|
|
3265
|
+
({
|
|
3266
|
+
selectedKey,
|
|
3267
|
+
defaultSelectedKey,
|
|
3268
|
+
onSelectionChange,
|
|
3269
|
+
variant = "primary",
|
|
3270
|
+
layout = "fixed",
|
|
3271
|
+
children,
|
|
3272
|
+
className,
|
|
3273
|
+
"aria-label": ariaLabel,
|
|
3274
|
+
"aria-labelledby": ariaLabelledBy
|
|
3275
|
+
}, ref) => {
|
|
3276
|
+
const tabItems = useMemo(() => extractTabItemsFromChildren(children), [children]);
|
|
3277
|
+
const state = useTabListState({
|
|
3278
|
+
...selectedKey !== void 0 && { selectedKey },
|
|
3279
|
+
...defaultSelectedKey !== void 0 && { defaultSelectedKey },
|
|
3280
|
+
...onSelectionChange !== void 0 && { onSelectionChange },
|
|
3281
|
+
disabledKeys: tabItems.filter((t) => t.isDisabled).map((t) => t.key),
|
|
3282
|
+
children: tabItems.map((item) => /* @__PURE__ */ jsx(Item, { textValue: item.label ?? item["aria-label"] ?? String(item.key), children: item.label ?? item["aria-label"] ?? "" }, item.key))
|
|
3283
|
+
});
|
|
3284
|
+
const tabsContextValue = useMemo(
|
|
3285
|
+
() => ({
|
|
3286
|
+
selectedKey: state.selectedKey,
|
|
3287
|
+
variant,
|
|
3288
|
+
layout,
|
|
3289
|
+
disabledKeys: tabItems.filter((t) => t.isDisabled).map((t) => t.key),
|
|
3290
|
+
...ariaLabel !== void 0 && { "aria-label": ariaLabel },
|
|
3291
|
+
...ariaLabelledBy !== void 0 && { "aria-labelledby": ariaLabelledBy }
|
|
3292
|
+
}),
|
|
3293
|
+
[state.selectedKey, variant, layout, tabItems, ariaLabel, ariaLabelledBy]
|
|
3294
|
+
);
|
|
3295
|
+
const headlessContextValue = useMemo(() => ({ state }), [state]);
|
|
3296
|
+
return /* @__PURE__ */ jsx(HeadlessTabsContext.Provider, { value: headlessContextValue, children: /* @__PURE__ */ jsx(TabsContext.Provider, { value: tabsContextValue, children: /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-col", className), children }) }) });
|
|
3297
|
+
}
|
|
3298
|
+
);
|
|
3299
|
+
Tabs.displayName = "Tabs";
|
|
3300
|
+
var tabListVariants = cva(
|
|
3301
|
+
[
|
|
3302
|
+
// Base classes
|
|
3303
|
+
"relative flex",
|
|
3304
|
+
"bg-surface",
|
|
3305
|
+
// Bottom divider line (MD3 spec)
|
|
3306
|
+
"border-b border-outline-variant"
|
|
3307
|
+
],
|
|
3308
|
+
{
|
|
3309
|
+
variants: {
|
|
3310
|
+
layout: {
|
|
3311
|
+
fixed: "w-full",
|
|
3312
|
+
scrollable: "overflow-x-auto scrollbar-none"
|
|
3313
|
+
}
|
|
3314
|
+
},
|
|
3315
|
+
defaultVariants: {
|
|
3316
|
+
layout: "fixed"
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3319
|
+
);
|
|
3320
|
+
var tabVariants = cva(
|
|
3321
|
+
[
|
|
3322
|
+
// Base layout
|
|
3323
|
+
"relative flex flex-col items-center justify-center",
|
|
3324
|
+
"min-h-12 px-4",
|
|
3325
|
+
"cursor-pointer select-none",
|
|
3326
|
+
"overflow-hidden",
|
|
3327
|
+
// Typography: MD3 Title Small
|
|
3328
|
+
"text-sm font-medium tracking-[0.1px]",
|
|
3329
|
+
// Transition
|
|
3330
|
+
"transition-colors duration-200",
|
|
3331
|
+
// Focus visible
|
|
3332
|
+
"focus-visible:outline-none",
|
|
3333
|
+
// State layer via before pseudo-element
|
|
3334
|
+
"before:absolute before:inset-0 before:transition-opacity before:duration-200",
|
|
3335
|
+
"before:bg-current before:opacity-0",
|
|
3336
|
+
"hover:before:opacity-8",
|
|
3337
|
+
"active:before:opacity-12",
|
|
3338
|
+
"focus-visible:before:opacity-12"
|
|
3339
|
+
],
|
|
3340
|
+
{
|
|
3341
|
+
variants: {
|
|
3342
|
+
/**
|
|
3343
|
+
* Tab variant (Primary or Secondary)
|
|
3344
|
+
*/
|
|
3345
|
+
variant: {
|
|
3346
|
+
primary: "",
|
|
3347
|
+
secondary: ""
|
|
3348
|
+
},
|
|
3349
|
+
/**
|
|
3350
|
+
* Selected state
|
|
3351
|
+
*/
|
|
3352
|
+
selected: {
|
|
3353
|
+
true: "",
|
|
3354
|
+
false: ""
|
|
3355
|
+
},
|
|
3356
|
+
/**
|
|
3357
|
+
* Disabled state
|
|
3358
|
+
*/
|
|
3359
|
+
disabled: {
|
|
3360
|
+
true: "opacity-38 cursor-not-allowed pointer-events-none",
|
|
3361
|
+
false: ""
|
|
3362
|
+
},
|
|
3363
|
+
/**
|
|
3364
|
+
* Layout determines min-width behavior
|
|
3365
|
+
*/
|
|
3366
|
+
layout: {
|
|
3367
|
+
fixed: "flex-1",
|
|
3368
|
+
scrollable: "min-w-[90px] shrink-0"
|
|
3369
|
+
}
|
|
3370
|
+
},
|
|
3371
|
+
compoundVariants: [
|
|
3372
|
+
// Primary + selected
|
|
3373
|
+
{
|
|
3374
|
+
variant: "primary",
|
|
3375
|
+
selected: true,
|
|
3376
|
+
disabled: false,
|
|
3377
|
+
className: "text-primary"
|
|
3378
|
+
},
|
|
3379
|
+
// Primary + unselected
|
|
3380
|
+
{
|
|
3381
|
+
variant: "primary",
|
|
3382
|
+
selected: false,
|
|
3383
|
+
disabled: false,
|
|
3384
|
+
className: "text-on-surface-variant"
|
|
3385
|
+
},
|
|
3386
|
+
// Secondary + selected
|
|
3387
|
+
{
|
|
3388
|
+
variant: "secondary",
|
|
3389
|
+
selected: true,
|
|
3390
|
+
disabled: false,
|
|
3391
|
+
className: "text-on-surface"
|
|
3392
|
+
},
|
|
3393
|
+
// Secondary + unselected
|
|
3394
|
+
{
|
|
3395
|
+
variant: "secondary",
|
|
3396
|
+
selected: false,
|
|
3397
|
+
disabled: false,
|
|
3398
|
+
className: "text-on-surface-variant"
|
|
3399
|
+
}
|
|
3400
|
+
],
|
|
3401
|
+
defaultVariants: {
|
|
3402
|
+
variant: "primary",
|
|
3403
|
+
selected: false,
|
|
3404
|
+
disabled: false,
|
|
3405
|
+
layout: "fixed"
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
);
|
|
3409
|
+
var tabIndicatorVariants = cva(
|
|
3410
|
+
[
|
|
3411
|
+
// Base: absolutely positioned at bottom
|
|
3412
|
+
"absolute bottom-0 left-0",
|
|
3413
|
+
"pointer-events-none",
|
|
3414
|
+
// Transition using MD3 motion tokens (medium2 duration, emphasized easing)
|
|
3415
|
+
"transition-[left,width]",
|
|
3416
|
+
"duration-medium2",
|
|
3417
|
+
"ease-emphasized"
|
|
3418
|
+
],
|
|
3419
|
+
{
|
|
3420
|
+
variants: {
|
|
3421
|
+
variant: {
|
|
3422
|
+
primary: ["h-[3px]", "bg-primary", "rounded-t-sm"],
|
|
3423
|
+
secondary: ["h-[2px]", "bg-on-surface-variant"]
|
|
3424
|
+
}
|
|
3425
|
+
},
|
|
3426
|
+
defaultVariants: {
|
|
3427
|
+
variant: "primary"
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
);
|
|
3431
|
+
var tabPanelVariants = cva(
|
|
3432
|
+
[
|
|
3433
|
+
// Base panel styles
|
|
3434
|
+
"outline-none",
|
|
3435
|
+
"focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2"
|
|
3436
|
+
],
|
|
3437
|
+
{
|
|
3438
|
+
variants: {},
|
|
3439
|
+
defaultVariants: {}
|
|
3440
|
+
}
|
|
3441
|
+
);
|
|
3442
|
+
var tabBadgeVariants = cva(
|
|
3443
|
+
[
|
|
3444
|
+
// Base badge
|
|
3445
|
+
"absolute",
|
|
3446
|
+
"inline-flex items-center justify-center",
|
|
3447
|
+
"bg-error text-on-error",
|
|
3448
|
+
"font-medium leading-none",
|
|
3449
|
+
"pointer-events-none"
|
|
3450
|
+
],
|
|
3451
|
+
{
|
|
3452
|
+
variants: {
|
|
3453
|
+
type: {
|
|
3454
|
+
dot: ["top-1 right-1", "w-1.5 h-1.5", "rounded-full"],
|
|
3455
|
+
count: ["-top-1 -right-1", "min-w-[16px] h-4", "px-1", "rounded-full", "text-[11px]"]
|
|
3456
|
+
}
|
|
3457
|
+
},
|
|
3458
|
+
defaultVariants: {
|
|
3459
|
+
type: "count"
|
|
3460
|
+
}
|
|
3461
|
+
}
|
|
3462
|
+
);
|
|
3463
|
+
var tabIconVariants = cva(
|
|
3464
|
+
["relative", "inline-flex items-center justify-center", "w-6 h-6"],
|
|
3465
|
+
{
|
|
3466
|
+
variants: {
|
|
3467
|
+
hasLabel: {
|
|
3468
|
+
true: "mb-1",
|
|
3469
|
+
false: ""
|
|
3470
|
+
}
|
|
3471
|
+
},
|
|
3472
|
+
defaultVariants: {
|
|
3473
|
+
hasLabel: false
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
);
|
|
3477
|
+
var TabList = forwardRef(
|
|
3478
|
+
({ children, className }, forwardedRef) => {
|
|
3479
|
+
const { state } = useHeadlessTabsContext("TabList");
|
|
3480
|
+
const {
|
|
3481
|
+
variant,
|
|
3482
|
+
layout,
|
|
3483
|
+
"aria-label": ariaLabel,
|
|
3484
|
+
"aria-labelledby": ariaLabelledBy
|
|
3485
|
+
} = useTabsContext();
|
|
3486
|
+
const internalRef = useRef(null);
|
|
3487
|
+
const ref = forwardedRef ?? internalRef;
|
|
3488
|
+
const { tabListProps } = useTabList(
|
|
3489
|
+
{
|
|
3490
|
+
...ariaLabel !== void 0 && { "aria-label": ariaLabel },
|
|
3491
|
+
...ariaLabelledBy !== void 0 && { "aria-labelledby": ariaLabelledBy }
|
|
3492
|
+
},
|
|
3493
|
+
state,
|
|
3494
|
+
ref
|
|
3495
|
+
);
|
|
3496
|
+
const handleNavKeyDown = useCallback(
|
|
3497
|
+
(e) => {
|
|
3498
|
+
if (!["ArrowRight", "ArrowLeft", "Home", "End"].includes(e.key)) return;
|
|
3499
|
+
const container = ref.current;
|
|
3500
|
+
if (!container) return;
|
|
3501
|
+
const focusedEl = document.activeElement;
|
|
3502
|
+
const currentKey = focusedEl?.dataset?.key;
|
|
3503
|
+
if (!currentKey) return;
|
|
3504
|
+
const allTabEls = [...container.querySelectorAll("[data-key]")];
|
|
3505
|
+
const allKeys = allTabEls.map((el) => el.dataset.key).filter(Boolean);
|
|
3506
|
+
const enabledKeys = allKeys.filter(
|
|
3507
|
+
(k) => allTabEls.find((el) => el.dataset.key === k)?.getAttribute("aria-disabled") !== "true"
|
|
3508
|
+
);
|
|
3509
|
+
const currentIndex = enabledKeys.indexOf(currentKey);
|
|
3510
|
+
if (currentIndex === -1) return;
|
|
3511
|
+
let nextKey = null;
|
|
3512
|
+
switch (e.key) {
|
|
3513
|
+
case "ArrowRight":
|
|
3514
|
+
nextKey = enabledKeys[(currentIndex + 1) % enabledKeys.length] ?? null;
|
|
3515
|
+
break;
|
|
3516
|
+
case "ArrowLeft":
|
|
3517
|
+
nextKey = enabledKeys[(currentIndex - 1 + enabledKeys.length) % enabledKeys.length] ?? null;
|
|
3518
|
+
break;
|
|
3519
|
+
case "Home":
|
|
3520
|
+
nextKey = enabledKeys[0] ?? null;
|
|
3521
|
+
break;
|
|
3522
|
+
case "End":
|
|
3523
|
+
nextKey = enabledKeys[enabledKeys.length - 1] ?? null;
|
|
3524
|
+
break;
|
|
3525
|
+
}
|
|
3526
|
+
if (nextKey != null) {
|
|
3527
|
+
e.preventDefault();
|
|
3528
|
+
state.setSelectedKey(nextKey);
|
|
3529
|
+
const nextEl = container.querySelector(
|
|
3530
|
+
`[data-key="${CSS.escape(nextKey)}"]`
|
|
3531
|
+
);
|
|
3532
|
+
nextEl?.focus();
|
|
3533
|
+
}
|
|
3534
|
+
},
|
|
3535
|
+
[state, ref]
|
|
3536
|
+
);
|
|
3537
|
+
const { onKeyDown: tabListKeyDown } = tabListProps;
|
|
3538
|
+
const handleKeyDown = useCallback(
|
|
3539
|
+
(e) => {
|
|
3540
|
+
handleNavKeyDown(e);
|
|
3541
|
+
if (!e.defaultPrevented) {
|
|
3542
|
+
tabListKeyDown?.(e);
|
|
3543
|
+
}
|
|
3544
|
+
},
|
|
3545
|
+
[handleNavKeyDown, tabListKeyDown]
|
|
3546
|
+
);
|
|
3547
|
+
const [indicatorStyle, setIndicatorStyle] = useState({ left: 0, width: 0 });
|
|
3548
|
+
const [indicatorReady, setIndicatorReady] = useState(false);
|
|
3549
|
+
const updateIndicator = useCallback(() => {
|
|
3550
|
+
const container = ref.current;
|
|
3551
|
+
if (!container) return;
|
|
3552
|
+
const selectedTab = container.querySelector('[aria-selected="true"]');
|
|
3553
|
+
if (!selectedTab) return;
|
|
3554
|
+
const containerRect = container.getBoundingClientRect();
|
|
3555
|
+
const tabRect = selectedTab.getBoundingClientRect();
|
|
3556
|
+
const newLeft = tabRect.left - containerRect.left + container.scrollLeft;
|
|
3557
|
+
const newWidth = tabRect.width;
|
|
3558
|
+
setIndicatorStyle((prev) => {
|
|
3559
|
+
if (prev.left === newLeft && prev.width === newWidth) return prev;
|
|
3560
|
+
return { left: newLeft, width: newWidth };
|
|
3561
|
+
});
|
|
3562
|
+
setIndicatorReady(true);
|
|
3563
|
+
}, [ref]);
|
|
3564
|
+
useLayoutEffect(() => {
|
|
3565
|
+
updateIndicator();
|
|
3566
|
+
}, [state.selectedKey, updateIndicator]);
|
|
3567
|
+
const mergedTabListProps = { ...tabListProps, onKeyDown: handleKeyDown };
|
|
3568
|
+
return /* @__PURE__ */ jsxs("div", { ...mergedTabListProps, ref, className: cn(tabListVariants({ layout }), className), children: [
|
|
3569
|
+
children,
|
|
3570
|
+
/* @__PURE__ */ jsx(
|
|
3571
|
+
"span",
|
|
3572
|
+
{
|
|
3573
|
+
"data-tab-indicator": true,
|
|
3574
|
+
"aria-hidden": "true",
|
|
3575
|
+
className: cn(
|
|
3576
|
+
tabIndicatorVariants({ variant }),
|
|
3577
|
+
// Hide until first position is calculated to avoid flash
|
|
3578
|
+
!indicatorReady && "opacity-0"
|
|
3579
|
+
),
|
|
3580
|
+
style: {
|
|
3581
|
+
// Dynamic left/width values from DOM measurements
|
|
3582
|
+
left: `${indicatorStyle.left}px`,
|
|
3583
|
+
width: `${indicatorStyle.width}px`
|
|
3584
|
+
}
|
|
3585
|
+
}
|
|
3586
|
+
)
|
|
3587
|
+
] });
|
|
3588
|
+
}
|
|
3589
|
+
);
|
|
3590
|
+
TabList.displayName = "TabList";
|
|
3591
|
+
function resolveBadgeDisplay(badge) {
|
|
3592
|
+
if (badge === void 0 || badge === false) return null;
|
|
3593
|
+
if (badge === true) return "dot";
|
|
3594
|
+
if (typeof badge === "number") {
|
|
3595
|
+
if (badge === 0) return null;
|
|
3596
|
+
return badge > 999 ? "999+" : String(badge);
|
|
3597
|
+
}
|
|
3598
|
+
return null;
|
|
3599
|
+
}
|
|
3600
|
+
var Tab = forwardRef(
|
|
3601
|
+
({ id, icon, label, badge, isDisabled = false, disableRipple = false, className, ...htmlProps }, forwardedRef) => {
|
|
3602
|
+
const { state } = useHeadlessTabsContext("Tab");
|
|
3603
|
+
const { variant, layout } = useTabsContext();
|
|
3604
|
+
const internalRef = useRef(null);
|
|
3605
|
+
const ref = forwardedRef ?? internalRef;
|
|
3606
|
+
const {
|
|
3607
|
+
tabProps,
|
|
3608
|
+
isSelected,
|
|
3609
|
+
isDisabled: ariaIsDisabled
|
|
3610
|
+
} = useTab({ key: id, isDisabled }, state, ref);
|
|
3611
|
+
const { isFocusVisible, focusProps } = useFocusRing();
|
|
3612
|
+
const finalIsDisabled = isDisabled || ariaIsDisabled;
|
|
3613
|
+
const { onMouseDown: handleRipple, ripples } = useRipple({
|
|
3614
|
+
disabled: finalIsDisabled || disableRipple
|
|
3615
|
+
});
|
|
3616
|
+
const handleClick = useCallback(() => {
|
|
3617
|
+
if (!finalIsDisabled) {
|
|
3618
|
+
state.setSelectedKey(id);
|
|
3619
|
+
}
|
|
3620
|
+
}, [state, id, finalIsDisabled]);
|
|
3621
|
+
const handleFocus = useCallback(() => {
|
|
3622
|
+
if (!finalIsDisabled) {
|
|
3623
|
+
state.selectionManager.setFocusedKey(id);
|
|
3624
|
+
}
|
|
3625
|
+
}, [state.selectionManager, id, finalIsDisabled]);
|
|
3626
|
+
const mergedProps = mergeProps(tabProps, focusProps, {
|
|
3627
|
+
onMouseDown: disableRipple ? void 0 : handleRipple,
|
|
3628
|
+
onClick: handleClick,
|
|
3629
|
+
onFocus: handleFocus
|
|
3630
|
+
});
|
|
3631
|
+
const badgeDisplay = resolveBadgeDisplay(badge);
|
|
3632
|
+
const isDotBadge = badgeDisplay === "dot";
|
|
3633
|
+
const hasIcon = Boolean(icon);
|
|
3634
|
+
const hasLabel = Boolean(label);
|
|
3635
|
+
return /* @__PURE__ */ jsxs(
|
|
3636
|
+
"button",
|
|
3637
|
+
{
|
|
3638
|
+
...mergedProps,
|
|
3639
|
+
ref,
|
|
3640
|
+
type: "button",
|
|
3641
|
+
"data-key": String(id),
|
|
3642
|
+
tabIndex: finalIsDisabled ? -1 : isSelected ? 0 : -1,
|
|
3643
|
+
className: cn(
|
|
3644
|
+
tabVariants({
|
|
3645
|
+
variant,
|
|
3646
|
+
selected: isSelected,
|
|
3647
|
+
disabled: finalIsDisabled,
|
|
3648
|
+
layout
|
|
3649
|
+
}),
|
|
3650
|
+
isFocusVisible && "outline-primary outline-2 outline-offset-2",
|
|
3651
|
+
hasLabel && hasIcon && "min-h-16",
|
|
3652
|
+
className
|
|
3653
|
+
),
|
|
3654
|
+
...htmlProps,
|
|
3655
|
+
children: [
|
|
3656
|
+
!disableRipple && ripples,
|
|
3657
|
+
hasIcon && /* @__PURE__ */ jsxs("span", { className: cn(tabIconVariants({ hasLabel })), children: [
|
|
3658
|
+
icon,
|
|
3659
|
+
badgeDisplay && /* @__PURE__ */ jsx(
|
|
3660
|
+
"span",
|
|
3661
|
+
{
|
|
3662
|
+
"data-badge-type": isDotBadge ? "dot" : "count",
|
|
3663
|
+
"aria-hidden": "true",
|
|
3664
|
+
className: cn(tabBadgeVariants({ type: isDotBadge ? "dot" : "count" })),
|
|
3665
|
+
children: !isDotBadge && badgeDisplay
|
|
3666
|
+
}
|
|
3667
|
+
)
|
|
3668
|
+
] }),
|
|
3669
|
+
hasLabel && /* @__PURE__ */ jsxs("span", { className: "relative z-10 truncate", children: [
|
|
3670
|
+
label,
|
|
3671
|
+
!hasIcon && badgeDisplay && /* @__PURE__ */ jsx(
|
|
3672
|
+
"span",
|
|
3673
|
+
{
|
|
3674
|
+
"data-badge-type": isDotBadge ? "dot" : "count",
|
|
3675
|
+
"aria-hidden": "true",
|
|
3676
|
+
className: cn(
|
|
3677
|
+
"relative ml-1",
|
|
3678
|
+
tabBadgeVariants({ type: isDotBadge ? "dot" : "count" })
|
|
3679
|
+
),
|
|
3680
|
+
children: !isDotBadge && badgeDisplay
|
|
3681
|
+
}
|
|
3682
|
+
)
|
|
3683
|
+
] })
|
|
3684
|
+
]
|
|
3685
|
+
}
|
|
3686
|
+
);
|
|
3687
|
+
}
|
|
3688
|
+
);
|
|
3689
|
+
Tab.displayName = "Tab";
|
|
3690
|
+
var TabPanel = forwardRef(
|
|
3691
|
+
({ id, children, className }, forwardedRef) => {
|
|
3692
|
+
const { state } = useHeadlessTabsContext("TabPanel");
|
|
3693
|
+
useTabsContext();
|
|
3694
|
+
const internalRef = useRef(null);
|
|
3695
|
+
const ref = forwardedRef ?? internalRef;
|
|
3696
|
+
const { tabPanelProps } = useTabPanel({}, state, ref);
|
|
3697
|
+
if (state.selectedKey !== id) {
|
|
3698
|
+
return null;
|
|
3699
|
+
}
|
|
3700
|
+
return /* @__PURE__ */ jsx("div", { ...tabPanelProps, ref, className: cn(tabPanelVariants(), className), children });
|
|
3701
|
+
}
|
|
3702
|
+
);
|
|
3703
|
+
TabPanel.displayName = "TabPanel";
|
|
3704
|
+
var NavigationBarContext = createContext(null);
|
|
3705
|
+
function useNavigationBarContext() {
|
|
3706
|
+
const ctx = useContext(NavigationBarContext);
|
|
3707
|
+
if (ctx === null) {
|
|
3708
|
+
throw new Error("HeadlessNavigationBarItem must be rendered inside HeadlessNavigationBar.");
|
|
3709
|
+
}
|
|
3710
|
+
return ctx;
|
|
3711
|
+
}
|
|
3712
|
+
var HeadlessNavigationBar = forwardRef(
|
|
3713
|
+
({
|
|
3714
|
+
items,
|
|
3715
|
+
selectedKey,
|
|
3716
|
+
defaultSelectedKey,
|
|
3717
|
+
onSelectionChange,
|
|
3718
|
+
"aria-label": ariaLabel,
|
|
3719
|
+
className,
|
|
3720
|
+
renderItem
|
|
3721
|
+
}, ref) => {
|
|
3722
|
+
const collectionChildren = items.map((item) => /* @__PURE__ */ jsx(Item, { textValue: item.label ?? item["aria-label"] ?? item.key, children: item.key }, item.key));
|
|
3723
|
+
const disabledKeys = items.filter((item) => item.isDisabled).map((item) => item.key);
|
|
3724
|
+
const state = useTabListState({
|
|
3725
|
+
children: collectionChildren,
|
|
3726
|
+
...selectedKey !== void 0 ? { selectedKey } : {},
|
|
3727
|
+
...defaultSelectedKey !== void 0 ? { defaultSelectedKey } : {},
|
|
3728
|
+
...onSelectionChange ? { onSelectionChange } : {},
|
|
3729
|
+
disabledKeys
|
|
3730
|
+
});
|
|
3731
|
+
const tabListRef = useRef(null);
|
|
3732
|
+
const { tabListProps } = useTabList({ "aria-label": ariaLabel }, state, tabListRef);
|
|
3733
|
+
return /* @__PURE__ */ jsx(
|
|
3734
|
+
"nav",
|
|
3735
|
+
{
|
|
3736
|
+
ref,
|
|
3737
|
+
role: "navigation",
|
|
3738
|
+
"aria-label": ariaLabel,
|
|
3739
|
+
className,
|
|
3740
|
+
children: /* @__PURE__ */ jsx(NavigationBarContext.Provider, { value: { state, hideLabels: false, disableRipple: false }, children: /* @__PURE__ */ jsx("div", { ...tabListProps, ref: tabListRef, className: "flex h-full w-full items-stretch", children: [...state.collection].map((collectionItem) => {
|
|
3741
|
+
const itemConfig = items.find((i) => String(i.key) === String(collectionItem.key));
|
|
3742
|
+
if (!itemConfig) return null;
|
|
3743
|
+
return renderItem(itemConfig);
|
|
3744
|
+
}) }) })
|
|
3745
|
+
}
|
|
3746
|
+
);
|
|
3747
|
+
}
|
|
3748
|
+
);
|
|
3749
|
+
HeadlessNavigationBar.displayName = "HeadlessNavigationBar";
|
|
3750
|
+
var HeadlessNavigationBarItem = forwardRef(({ itemKey, children, className, "aria-label": ariaLabel }, ref) => {
|
|
3751
|
+
const { state } = useNavigationBarContext();
|
|
3752
|
+
const internalRef = useRef(null);
|
|
3753
|
+
const resolvedRef = ref ?? internalRef;
|
|
3754
|
+
const { tabProps, isSelected } = useTab({ key: itemKey }, state, resolvedRef);
|
|
3755
|
+
const { "aria-controls": _controls, ...tabPropsWithoutControls } = tabProps;
|
|
3756
|
+
const { focusProps, isFocusVisible } = useFocusRing();
|
|
3757
|
+
const content = typeof children === "function" ? children({ isSelected, isFocusVisible }) : children;
|
|
3758
|
+
return /* @__PURE__ */ jsx(
|
|
3759
|
+
"button",
|
|
3760
|
+
{
|
|
3761
|
+
type: "button",
|
|
3762
|
+
...mergeProps(tabPropsWithoutControls, focusProps),
|
|
3763
|
+
ref: resolvedRef,
|
|
3764
|
+
className,
|
|
3765
|
+
"aria-label": ariaLabel,
|
|
3766
|
+
"data-focus-visible": isFocusVisible || void 0,
|
|
3767
|
+
"data-selected": isSelected,
|
|
3768
|
+
children: content
|
|
3769
|
+
}
|
|
3770
|
+
);
|
|
3771
|
+
});
|
|
3772
|
+
HeadlessNavigationBarItem.displayName = "HeadlessNavigationBarItem";
|
|
3773
|
+
var navigationBarVariants = cva([
|
|
3774
|
+
// Layout
|
|
3775
|
+
"fixed bottom-0 left-0 right-0 z-10",
|
|
3776
|
+
"w-full",
|
|
3777
|
+
"flex flex-row items-stretch",
|
|
3778
|
+
// MD3 surface
|
|
3779
|
+
"bg-surface-container",
|
|
3780
|
+
// MD3 height: 80dp
|
|
3781
|
+
"h-20",
|
|
3782
|
+
// Safe-area bottom (for mobile devices)
|
|
3783
|
+
"pb-safe"
|
|
3784
|
+
]);
|
|
3785
|
+
var navigationBarItemVariants = cva(
|
|
3786
|
+
[
|
|
3787
|
+
// Layout
|
|
3788
|
+
"relative flex flex-1 flex-col items-center justify-center",
|
|
3789
|
+
"cursor-pointer select-none outline-none",
|
|
3790
|
+
// State layer pseudo-element (covers the full item area)
|
|
3791
|
+
"before:absolute before:inset-0 before:rounded-none before:transition-opacity before:duration-short2 before:ease-standard",
|
|
3792
|
+
// Focus-visible ring
|
|
3793
|
+
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary",
|
|
3794
|
+
// Transition for color changes
|
|
3795
|
+
"transition-colors duration-short2 ease-standard"
|
|
3796
|
+
],
|
|
3797
|
+
{
|
|
3798
|
+
variants: {
|
|
3799
|
+
/**
|
|
3800
|
+
* Whether this item is the currently selected destination.
|
|
3801
|
+
* Controls icon and label colors per MD3 spec.
|
|
3802
|
+
*/
|
|
3803
|
+
isActive: {
|
|
3804
|
+
true: [
|
|
3805
|
+
// Active icon color applied via text-color on the icon wrapper
|
|
3806
|
+
"[&>[data-icon]]:text-on-secondary-container",
|
|
3807
|
+
// Active label color
|
|
3808
|
+
"[&>[data-label]]:text-on-surface",
|
|
3809
|
+
// State layer color for active item
|
|
3810
|
+
"before:bg-on-surface-variant"
|
|
3811
|
+
],
|
|
3812
|
+
false: [
|
|
3813
|
+
// Inactive icon color
|
|
3814
|
+
"[&>[data-icon]]:text-on-surface-variant",
|
|
3815
|
+
// Inactive label color
|
|
3816
|
+
"[&>[data-label]]:text-on-surface-variant",
|
|
3817
|
+
// State layer color for inactive item
|
|
3818
|
+
"before:bg-on-surface-variant"
|
|
3819
|
+
]
|
|
3820
|
+
},
|
|
3821
|
+
/**
|
|
3822
|
+
* Whether the item is disabled.
|
|
3823
|
+
* Applies `opacity-38` per MD3 disabled state.
|
|
3824
|
+
*/
|
|
3825
|
+
isDisabled: {
|
|
3826
|
+
true: ["cursor-not-allowed opacity-38 pointer-events-none"],
|
|
3827
|
+
false: []
|
|
3828
|
+
},
|
|
3829
|
+
/**
|
|
3830
|
+
* Hover and press state layer opacities.
|
|
3831
|
+
* Applied via compound variants on hover/active pseudo-classes.
|
|
3832
|
+
*/
|
|
3833
|
+
isHovered: {
|
|
3834
|
+
true: ["before:opacity-8"],
|
|
3835
|
+
false: ["before:opacity-0"]
|
|
3836
|
+
},
|
|
3837
|
+
isPressed: {
|
|
3838
|
+
true: ["before:opacity-12"],
|
|
3839
|
+
false: []
|
|
3840
|
+
}
|
|
3841
|
+
},
|
|
3842
|
+
compoundVariants: [
|
|
3843
|
+
// When not hovered or pressed, state layer is invisible
|
|
3844
|
+
{
|
|
3845
|
+
isHovered: false,
|
|
3846
|
+
isPressed: false,
|
|
3847
|
+
className: "before:opacity-0"
|
|
3848
|
+
}
|
|
3849
|
+
],
|
|
3850
|
+
defaultVariants: {
|
|
3851
|
+
isActive: false,
|
|
3852
|
+
isDisabled: false,
|
|
3853
|
+
isHovered: false,
|
|
3854
|
+
isPressed: false
|
|
3855
|
+
}
|
|
3856
|
+
}
|
|
3857
|
+
);
|
|
3858
|
+
var indicatorPillVariants = cva(
|
|
3859
|
+
[
|
|
3860
|
+
"absolute left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2",
|
|
3861
|
+
"w-16 h-8",
|
|
3862
|
+
"rounded-full bg-secondary-container",
|
|
3863
|
+
"transition-[transform,opacity] duration-medium2 ease-emphasized",
|
|
3864
|
+
"origin-center"
|
|
3865
|
+
],
|
|
3866
|
+
{
|
|
3867
|
+
variants: {
|
|
3868
|
+
isActive: {
|
|
3869
|
+
true: ["scale-x-100 opacity-100"],
|
|
3870
|
+
false: ["scale-x-0 opacity-0"]
|
|
3871
|
+
}
|
|
3872
|
+
},
|
|
3873
|
+
defaultVariants: {
|
|
3874
|
+
isActive: false
|
|
3875
|
+
}
|
|
3876
|
+
}
|
|
3877
|
+
);
|
|
3878
|
+
var badgeVariants = cva(
|
|
3879
|
+
[
|
|
3880
|
+
"absolute",
|
|
3881
|
+
"flex items-center justify-center",
|
|
3882
|
+
"bg-error text-on-error",
|
|
3883
|
+
"font-medium leading-none",
|
|
3884
|
+
// MD3 label-small
|
|
3885
|
+
"text-[0.6875rem]"
|
|
3886
|
+
],
|
|
3887
|
+
{
|
|
3888
|
+
variants: {
|
|
3889
|
+
isDot: {
|
|
3890
|
+
true: [
|
|
3891
|
+
// Dot: 6dp diameter, top-right of icon
|
|
3892
|
+
"top-0 right-0.5 z-10",
|
|
3893
|
+
"w-1.5 h-1.5 min-w-0 rounded-full"
|
|
3894
|
+
],
|
|
3895
|
+
false: [
|
|
3896
|
+
// Numeric: pill shape, top-right of icon
|
|
3897
|
+
"-top-1 left-3 z-10",
|
|
3898
|
+
"min-w-[1rem] h-4 px-1 rounded-full"
|
|
3899
|
+
]
|
|
3900
|
+
}
|
|
3901
|
+
},
|
|
3902
|
+
defaultVariants: {
|
|
3903
|
+
isDot: false
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
);
|
|
3907
|
+
var iconWrapperVariants = cva([
|
|
3908
|
+
"relative z-10 flex items-center justify-center",
|
|
3909
|
+
"w-6 h-6"
|
|
3910
|
+
]);
|
|
3911
|
+
var labelVariants = cva([
|
|
3912
|
+
"mt-1 select-none",
|
|
3913
|
+
"text-label-medium",
|
|
3914
|
+
"transition-colors duration-short2 ease-standard",
|
|
3915
|
+
"truncate max-w-full"
|
|
3916
|
+
]);
|
|
3917
|
+
var MIN_ITEMS = 3;
|
|
3918
|
+
var MAX_ITEMS = 5;
|
|
3919
|
+
function validateItemCount(count) {
|
|
3920
|
+
if (process.env.NODE_ENV !== "production" && (count < MIN_ITEMS || count > MAX_ITEMS)) {
|
|
3921
|
+
console.warn(
|
|
3922
|
+
`[NavigationBar] MD3 Navigation Bar requires between ${MIN_ITEMS} and ${MAX_ITEMS} destination items. Received ${count}. See: https://m3.material.io/components/navigation-bar/overview`
|
|
3923
|
+
);
|
|
3924
|
+
}
|
|
3925
|
+
}
|
|
3926
|
+
function getBadgeText(badge) {
|
|
3927
|
+
if (badge === void 0 || badge === 0) return null;
|
|
3928
|
+
if (badge === true) return null;
|
|
3929
|
+
if (badge > 999) return "999+";
|
|
3930
|
+
return String(badge);
|
|
3931
|
+
}
|
|
3932
|
+
function isBadgeVisible(badge) {
|
|
3933
|
+
if (badge === void 0) return false;
|
|
3934
|
+
if (badge === 0) return false;
|
|
3935
|
+
return true;
|
|
3936
|
+
}
|
|
3937
|
+
function ItemVisual({ config, isActive, hideLabels, disableRipple }) {
|
|
3938
|
+
const isItemDisabled = config.isDisabled === true;
|
|
3939
|
+
const { onMouseDown, ripples } = useRipple({
|
|
3940
|
+
...disableRipple || isItemDisabled ? { disabled: true } : {}
|
|
3941
|
+
});
|
|
3942
|
+
const showBadge = isBadgeVisible(config.badge);
|
|
3943
|
+
const isDot = config.badge === true;
|
|
3944
|
+
const badgeText = getBadgeText(config.badge);
|
|
3945
|
+
return (
|
|
3946
|
+
// Overflow-hidden wrapper required for ripple containment.
|
|
3947
|
+
// pointer-events-none is intentional: the parent <button> handles all interaction.
|
|
3948
|
+
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
|
3949
|
+
/* @__PURE__ */ jsxs(
|
|
3950
|
+
"span",
|
|
3951
|
+
{
|
|
3952
|
+
onMouseDown,
|
|
3953
|
+
className: "pointer-events-none relative flex h-full w-full flex-col items-center justify-center overflow-hidden",
|
|
3954
|
+
children: [
|
|
3955
|
+
ripples,
|
|
3956
|
+
/* @__PURE__ */ jsx(
|
|
3957
|
+
"span",
|
|
3958
|
+
{
|
|
3959
|
+
"data-indicator-pill": true,
|
|
3960
|
+
"data-active": isActive,
|
|
3961
|
+
"aria-hidden": "true",
|
|
3962
|
+
className: cn(indicatorPillVariants({ isActive }), !hideLabels && "-mt-3.5")
|
|
3963
|
+
}
|
|
3964
|
+
),
|
|
3965
|
+
/* @__PURE__ */ jsxs(
|
|
3966
|
+
"span",
|
|
3967
|
+
{
|
|
3968
|
+
"data-icon": true,
|
|
3969
|
+
className: cn(
|
|
3970
|
+
iconWrapperVariants(),
|
|
3971
|
+
isActive ? "text-on-secondary-container" : "text-on-surface-variant"
|
|
3972
|
+
),
|
|
3973
|
+
children: [
|
|
3974
|
+
/* @__PURE__ */ jsx("span", { className: "relative z-10 flex h-6 w-6 items-center justify-center", children: config.icon }),
|
|
3975
|
+
showBadge && /* @__PURE__ */ jsx(
|
|
3976
|
+
"span",
|
|
3977
|
+
{
|
|
3978
|
+
"data-badge": true,
|
|
3979
|
+
"data-badge-dot": isDot || void 0,
|
|
3980
|
+
"aria-label": isDot ? "notification" : badgeText ? `${badgeText} notifications` : void 0,
|
|
3981
|
+
"aria-live": "polite",
|
|
3982
|
+
className: cn(badgeVariants({ isDot })),
|
|
3983
|
+
children: isDot ? null : badgeText
|
|
3984
|
+
}
|
|
3985
|
+
)
|
|
3986
|
+
]
|
|
3987
|
+
}
|
|
3988
|
+
),
|
|
3989
|
+
!hideLabels && config.label && /* @__PURE__ */ jsx(
|
|
3990
|
+
"span",
|
|
3991
|
+
{
|
|
3992
|
+
"data-label": true,
|
|
3993
|
+
className: cn(labelVariants(), isActive ? "text-on-surface" : "text-on-surface-variant"),
|
|
3994
|
+
children: config.label
|
|
3995
|
+
}
|
|
3996
|
+
)
|
|
3997
|
+
]
|
|
3998
|
+
}
|
|
3999
|
+
)
|
|
4000
|
+
);
|
|
4001
|
+
}
|
|
4002
|
+
var NavigationBar = forwardRef(
|
|
4003
|
+
({
|
|
4004
|
+
items,
|
|
4005
|
+
activeKey,
|
|
4006
|
+
defaultActiveKey,
|
|
4007
|
+
onActiveChange,
|
|
4008
|
+
hideLabels = false,
|
|
4009
|
+
"aria-label": ariaLabel,
|
|
4010
|
+
disableRipple = false,
|
|
4011
|
+
className
|
|
4012
|
+
}, ref) => {
|
|
4013
|
+
validateItemCount(items.length);
|
|
4014
|
+
return /* @__PURE__ */ jsx(
|
|
4015
|
+
HeadlessNavigationBar,
|
|
4016
|
+
{
|
|
4017
|
+
ref,
|
|
4018
|
+
items,
|
|
4019
|
+
...activeKey !== void 0 ? { selectedKey: activeKey } : {},
|
|
4020
|
+
...defaultActiveKey !== void 0 ? { defaultSelectedKey: defaultActiveKey } : {},
|
|
4021
|
+
...onActiveChange ? { onSelectionChange: onActiveChange } : {},
|
|
4022
|
+
"aria-label": ariaLabel,
|
|
4023
|
+
className: cn(navigationBarVariants(), className),
|
|
4024
|
+
renderItem: (config) => (
|
|
4025
|
+
// HeadlessNavigationBarItem renders the <button role="tab"> with all
|
|
4026
|
+
// ARIA semantics. ItemVisual renders the icon/pill/badge/label inside
|
|
4027
|
+
// — no nested <button> elements.
|
|
4028
|
+
/* @__PURE__ */ jsx(
|
|
4029
|
+
HeadlessNavigationBarItem,
|
|
4030
|
+
{
|
|
4031
|
+
itemKey: config.key,
|
|
4032
|
+
...config["aria-label"] !== void 0 ? { "aria-label": config["aria-label"] } : {},
|
|
4033
|
+
className: cn(
|
|
4034
|
+
"relative flex flex-1 flex-col items-center justify-center",
|
|
4035
|
+
"cursor-pointer outline-none select-none",
|
|
4036
|
+
"duration-short2 ease-standard transition-colors",
|
|
4037
|
+
// State layer pseudo-element
|
|
4038
|
+
"before:absolute before:inset-0 before:rounded-none",
|
|
4039
|
+
"before:bg-on-surface-variant before:opacity-0",
|
|
4040
|
+
"before:duration-short2 before:ease-standard before:transition-opacity",
|
|
4041
|
+
"hover:before:opacity-8",
|
|
4042
|
+
"active:before:opacity-12",
|
|
4043
|
+
// Focus ring
|
|
4044
|
+
"focus-visible:outline-primary focus-visible:outline-2 focus-visible:outline-offset-2",
|
|
4045
|
+
// Disabled styling
|
|
4046
|
+
config.isDisabled && "pointer-events-none cursor-not-allowed opacity-38"
|
|
4047
|
+
),
|
|
4048
|
+
children: ({ isSelected }) => /* @__PURE__ */ jsx(
|
|
4049
|
+
ItemVisual,
|
|
4050
|
+
{
|
|
4051
|
+
config,
|
|
4052
|
+
isActive: isSelected,
|
|
4053
|
+
hideLabels,
|
|
4054
|
+
disableRipple
|
|
4055
|
+
}
|
|
4056
|
+
)
|
|
4057
|
+
},
|
|
4058
|
+
config.key
|
|
4059
|
+
)
|
|
4060
|
+
)
|
|
4061
|
+
}
|
|
4062
|
+
);
|
|
4063
|
+
}
|
|
4064
|
+
);
|
|
4065
|
+
NavigationBar.displayName = "NavigationBar";
|
|
4066
|
+
function getBadgeContent(badge) {
|
|
4067
|
+
if (badge === true) return null;
|
|
4068
|
+
if (badge === 0) return "";
|
|
4069
|
+
if (badge > 999) return "999+";
|
|
4070
|
+
return String(badge);
|
|
4071
|
+
}
|
|
4072
|
+
function isBadgeVisible2(badge) {
|
|
4073
|
+
if (badge === void 0) return false;
|
|
4074
|
+
if (badge === 0) return false;
|
|
4075
|
+
return true;
|
|
4076
|
+
}
|
|
4077
|
+
var NavigationBarItem = forwardRef(
|
|
4078
|
+
({
|
|
4079
|
+
itemKey: _itemKey,
|
|
4080
|
+
icon,
|
|
4081
|
+
label,
|
|
4082
|
+
badge,
|
|
4083
|
+
isActive = false,
|
|
4084
|
+
hideLabels = false,
|
|
4085
|
+
isDisabled = false,
|
|
4086
|
+
disableRipple = false,
|
|
4087
|
+
className,
|
|
4088
|
+
"aria-label": ariaLabel,
|
|
4089
|
+
// Spread remaining props (e.g. tabProps from useTab)
|
|
4090
|
+
...rest
|
|
4091
|
+
}, ref) => {
|
|
4092
|
+
const { onMouseDown, ripples } = useRipple({
|
|
4093
|
+
...disableRipple || isDisabled ? { disabled: true } : {}
|
|
4094
|
+
});
|
|
4095
|
+
const showBadge = isBadgeVisible2(badge);
|
|
4096
|
+
const isDot = badge === true;
|
|
4097
|
+
const badgeContent = badge !== void 0 ? getBadgeContent(badge) : null;
|
|
4098
|
+
return /* @__PURE__ */ jsx(
|
|
4099
|
+
"button",
|
|
4100
|
+
{
|
|
4101
|
+
type: "button",
|
|
4102
|
+
ref,
|
|
4103
|
+
onMouseDown,
|
|
4104
|
+
disabled: isDisabled,
|
|
4105
|
+
"aria-label": ariaLabel,
|
|
4106
|
+
className: cn(
|
|
4107
|
+
navigationBarItemVariants({
|
|
4108
|
+
isActive,
|
|
4109
|
+
isDisabled
|
|
4110
|
+
}),
|
|
4111
|
+
className
|
|
4112
|
+
),
|
|
4113
|
+
...rest,
|
|
4114
|
+
children: /* @__PURE__ */ jsxs("span", { className: "relative flex h-full w-full flex-col items-center justify-center overflow-hidden rounded-none", children: [
|
|
4115
|
+
ripples,
|
|
4116
|
+
/* @__PURE__ */ jsx(
|
|
4117
|
+
"span",
|
|
4118
|
+
{
|
|
4119
|
+
"data-indicator-pill": true,
|
|
4120
|
+
"data-active": isActive,
|
|
4121
|
+
className: cn(indicatorPillVariants({ isActive }), !hideLabels && "-mt-3.5"),
|
|
4122
|
+
"aria-hidden": "true"
|
|
4123
|
+
}
|
|
4124
|
+
),
|
|
4125
|
+
/* @__PURE__ */ jsxs("span", { "data-icon": true, className: cn(iconWrapperVariants()), children: [
|
|
4126
|
+
/* @__PURE__ */ jsx("span", { className: "relative z-10 flex h-6 w-6 items-center justify-center", children: icon }),
|
|
4127
|
+
showBadge && /* @__PURE__ */ jsx(
|
|
4128
|
+
"span",
|
|
4129
|
+
{
|
|
4130
|
+
"data-badge": true,
|
|
4131
|
+
"data-badge-dot": isDot || void 0,
|
|
4132
|
+
"aria-label": isDot ? "notification" : badgeContent ? `${badgeContent} notifications` : void 0,
|
|
4133
|
+
"aria-live": "polite",
|
|
4134
|
+
className: cn(badgeVariants({ isDot })),
|
|
4135
|
+
children: isDot ? null : badgeContent
|
|
4136
|
+
}
|
|
4137
|
+
)
|
|
4138
|
+
] }),
|
|
4139
|
+
!hideLabels && label && /* @__PURE__ */ jsx("span", { "data-label": true, className: cn(labelVariants()), children: label })
|
|
4140
|
+
] })
|
|
4141
|
+
}
|
|
4142
|
+
);
|
|
4143
|
+
}
|
|
4144
|
+
);
|
|
4145
|
+
NavigationBarItem.displayName = "NavigationBarItem";
|
|
4146
|
+
var DrawerContext = createContext(null);
|
|
4147
|
+
var HeadlessDrawer = forwardRef(
|
|
4148
|
+
({
|
|
4149
|
+
variant = "standard",
|
|
4150
|
+
open,
|
|
4151
|
+
defaultOpen = false,
|
|
4152
|
+
onOpenChange,
|
|
4153
|
+
"aria-label": ariaLabel,
|
|
4154
|
+
children,
|
|
4155
|
+
className,
|
|
4156
|
+
scrimClassName,
|
|
4157
|
+
disableRipple = false
|
|
4158
|
+
}, ref) => {
|
|
4159
|
+
const state = useOverlayTriggerState({
|
|
4160
|
+
...open !== void 0 ? { isOpen: open } : {},
|
|
4161
|
+
...defaultOpen !== void 0 ? { defaultOpen } : {},
|
|
4162
|
+
...onOpenChange !== void 0 ? { onOpenChange } : {}
|
|
4163
|
+
});
|
|
4164
|
+
const isOpen = state.isOpen;
|
|
4165
|
+
const close = useCallback(() => {
|
|
4166
|
+
state.close();
|
|
4167
|
+
}, [state]);
|
|
4168
|
+
const contextValue = {
|
|
4169
|
+
isOpen,
|
|
4170
|
+
close,
|
|
4171
|
+
disableRipple
|
|
4172
|
+
};
|
|
4173
|
+
if (variant === "modal") {
|
|
4174
|
+
return /* @__PURE__ */ jsx(DrawerContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx("nav", { ref, role: "navigation", "aria-label": ariaLabel, children: isOpen && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4175
|
+
/* @__PURE__ */ jsx(
|
|
4176
|
+
"div",
|
|
4177
|
+
{
|
|
4178
|
+
"data-testid": "drawer-scrim",
|
|
4179
|
+
className: scrimClassName,
|
|
4180
|
+
onClick: () => state.close(),
|
|
4181
|
+
"aria-hidden": "true"
|
|
4182
|
+
}
|
|
4183
|
+
),
|
|
4184
|
+
/* @__PURE__ */ jsx(FocusScope, { contain: true, restoreFocus: true, autoFocus: true, children: /* @__PURE__ */ jsx(
|
|
4185
|
+
ModalDrawerPanel,
|
|
4186
|
+
{
|
|
4187
|
+
ariaLabel,
|
|
4188
|
+
onClose: () => state.close(),
|
|
4189
|
+
className,
|
|
4190
|
+
children
|
|
4191
|
+
}
|
|
4192
|
+
) })
|
|
4193
|
+
] }) }) });
|
|
4194
|
+
}
|
|
4195
|
+
return /* @__PURE__ */ jsx(DrawerContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
|
|
4196
|
+
"nav",
|
|
4197
|
+
{
|
|
4198
|
+
ref,
|
|
4199
|
+
role: "navigation",
|
|
4200
|
+
"aria-label": ariaLabel,
|
|
4201
|
+
className,
|
|
4202
|
+
children
|
|
4203
|
+
}
|
|
4204
|
+
) });
|
|
4205
|
+
}
|
|
4206
|
+
);
|
|
4207
|
+
HeadlessDrawer.displayName = "HeadlessDrawer";
|
|
4208
|
+
var ModalDrawerPanel = ({
|
|
4209
|
+
ariaLabel,
|
|
4210
|
+
onClose,
|
|
4211
|
+
className,
|
|
4212
|
+
children
|
|
4213
|
+
}) => {
|
|
4214
|
+
const panelRef = useRef(null);
|
|
4215
|
+
usePreventScroll();
|
|
4216
|
+
const { dialogProps } = useDialog({ "aria-label": ariaLabel }, panelRef);
|
|
4217
|
+
const { overlayProps } = useOverlay(
|
|
4218
|
+
{
|
|
4219
|
+
isOpen: true,
|
|
4220
|
+
onClose,
|
|
4221
|
+
isDismissable: true,
|
|
4222
|
+
shouldCloseOnBlur: false
|
|
4223
|
+
},
|
|
4224
|
+
panelRef
|
|
4225
|
+
);
|
|
4226
|
+
return /* @__PURE__ */ jsx(
|
|
4227
|
+
"div",
|
|
4228
|
+
{
|
|
4229
|
+
...mergeProps(overlayProps, dialogProps),
|
|
4230
|
+
ref: panelRef,
|
|
4231
|
+
className,
|
|
4232
|
+
"aria-modal": "true",
|
|
4233
|
+
children
|
|
4234
|
+
}
|
|
4235
|
+
);
|
|
4236
|
+
};
|
|
4237
|
+
ModalDrawerPanel.displayName = "ModalDrawerPanel";
|
|
4238
|
+
var HeadlessDrawerItem = forwardRef(
|
|
4239
|
+
({
|
|
4240
|
+
href,
|
|
4241
|
+
isActive = false,
|
|
4242
|
+
children,
|
|
4243
|
+
className,
|
|
4244
|
+
isDisabled,
|
|
4245
|
+
onMouseDown,
|
|
4246
|
+
onPress,
|
|
4247
|
+
onPressStart,
|
|
4248
|
+
onPressEnd,
|
|
4249
|
+
onPressChange,
|
|
4250
|
+
onPressUp,
|
|
4251
|
+
...restProps
|
|
4252
|
+
}, forwardedRef) => {
|
|
4253
|
+
const internalRef = useRef(null);
|
|
4254
|
+
const { isFocusVisible, focusProps } = useFocusRing();
|
|
4255
|
+
if (href) {
|
|
4256
|
+
const linkRef = forwardedRef ?? internalRef;
|
|
4257
|
+
const { linkProps } = useLink(
|
|
4258
|
+
{
|
|
4259
|
+
href,
|
|
4260
|
+
...isDisabled !== void 0 ? { isDisabled } : {},
|
|
4261
|
+
...onPress !== void 0 ? { onPress } : {}
|
|
4262
|
+
},
|
|
4263
|
+
linkRef
|
|
4264
|
+
);
|
|
4265
|
+
return /* @__PURE__ */ jsx(
|
|
4266
|
+
"a",
|
|
4267
|
+
{
|
|
4268
|
+
...mergeProps(linkProps, focusProps, { onMouseDown }),
|
|
4269
|
+
ref: linkRef,
|
|
4270
|
+
href,
|
|
4271
|
+
className,
|
|
4272
|
+
"aria-current": isActive ? "page" : void 0,
|
|
4273
|
+
"data-focus-visible": isFocusVisible || void 0,
|
|
4274
|
+
"data-active": isActive || void 0,
|
|
4275
|
+
children
|
|
4276
|
+
}
|
|
4277
|
+
);
|
|
4278
|
+
}
|
|
4279
|
+
const buttonRef = forwardedRef ?? internalRef;
|
|
4280
|
+
const { buttonProps } = useButton(
|
|
4281
|
+
{
|
|
4282
|
+
...restProps,
|
|
4283
|
+
...isDisabled !== void 0 ? { isDisabled } : {},
|
|
4284
|
+
...onPress !== void 0 ? { onPress } : {},
|
|
4285
|
+
...onPressStart !== void 0 ? { onPressStart } : {},
|
|
4286
|
+
...onPressEnd !== void 0 ? { onPressEnd } : {},
|
|
4287
|
+
...onPressChange !== void 0 ? { onPressChange } : {},
|
|
4288
|
+
...onPressUp !== void 0 ? { onPressUp } : {},
|
|
4289
|
+
elementType: "button"
|
|
4290
|
+
},
|
|
4291
|
+
buttonRef
|
|
4292
|
+
);
|
|
4293
|
+
return /* @__PURE__ */ jsx(
|
|
4294
|
+
"button",
|
|
4295
|
+
{
|
|
4296
|
+
type: "button",
|
|
4297
|
+
...mergeProps(buttonProps, focusProps, { onMouseDown }),
|
|
4298
|
+
ref: buttonRef,
|
|
4299
|
+
className,
|
|
4300
|
+
"aria-current": isActive ? "page" : void 0,
|
|
4301
|
+
"data-focus-visible": isFocusVisible || void 0,
|
|
4302
|
+
"data-active": isActive || void 0,
|
|
4303
|
+
children
|
|
4304
|
+
}
|
|
4305
|
+
);
|
|
4306
|
+
}
|
|
4307
|
+
);
|
|
4308
|
+
HeadlessDrawerItem.displayName = "HeadlessDrawerItem";
|
|
4309
|
+
var drawerVariants = cva(
|
|
4310
|
+
[
|
|
4311
|
+
// Layout
|
|
4312
|
+
"fixed top-0 left-0 h-full w-drawer",
|
|
4313
|
+
"flex flex-col overflow-y-auto",
|
|
4314
|
+
// Stacking and shape
|
|
4315
|
+
"z-50",
|
|
4316
|
+
"rounded-r-xl",
|
|
4317
|
+
// Slide animation (transition applies to all open/closed state changes)
|
|
4318
|
+
"transition-transform duration-medium4 ease-emphasized-decelerate",
|
|
4319
|
+
// Focus outline removal (focus management handled by FocusScope / React Aria)
|
|
4320
|
+
"outline-none",
|
|
4321
|
+
// Padding for content spacing
|
|
4322
|
+
"px-3"
|
|
4323
|
+
],
|
|
4324
|
+
{
|
|
4325
|
+
variants: {
|
|
4326
|
+
/**
|
|
4327
|
+
* Structural variant — drives surface color and elevation.
|
|
4328
|
+
* - `standard`: inline nav panel, lower-elevation surface
|
|
4329
|
+
* - `modal`: overlay dialog with elevation shadow
|
|
4330
|
+
*/
|
|
4331
|
+
variant: {
|
|
4332
|
+
standard: ["bg-surface-container-low"],
|
|
4333
|
+
modal: ["bg-surface-container", "shadow-elevation-1"]
|
|
4334
|
+
},
|
|
4335
|
+
/**
|
|
4336
|
+
* Open/closed state — drives translation.
|
|
4337
|
+
* - `true`: drawer visible (`translate-x-0`)
|
|
4338
|
+
* - `false`: drawer off-screen (`-translate-x-full`)
|
|
4339
|
+
*/
|
|
4340
|
+
open: {
|
|
4341
|
+
true: ["translate-x-0"],
|
|
4342
|
+
false: ["-translate-x-full"]
|
|
4343
|
+
}
|
|
4344
|
+
},
|
|
4345
|
+
defaultVariants: {
|
|
4346
|
+
variant: "standard",
|
|
4347
|
+
open: false
|
|
4348
|
+
}
|
|
4349
|
+
}
|
|
4350
|
+
);
|
|
4351
|
+
var drawerItemVariants = cva(
|
|
4352
|
+
[
|
|
4353
|
+
// Layout
|
|
4354
|
+
"relative flex w-full items-center gap-3",
|
|
4355
|
+
"h-14 px-4",
|
|
4356
|
+
"rounded-full",
|
|
4357
|
+
// Typography
|
|
4358
|
+
"text-label-large",
|
|
4359
|
+
// Interaction
|
|
4360
|
+
"cursor-pointer select-none outline-none",
|
|
4361
|
+
// State layer pseudo-element
|
|
4362
|
+
"before:absolute before:inset-0 before:rounded-full",
|
|
4363
|
+
"before:transition-opacity before:duration-short2 before:ease-standard",
|
|
4364
|
+
"before:opacity-0",
|
|
4365
|
+
// Hover and focus visible state layers
|
|
4366
|
+
"hover:before:opacity-8",
|
|
4367
|
+
"focus-visible:before:opacity-12",
|
|
4368
|
+
// Active pressed state
|
|
4369
|
+
"active:before:opacity-12",
|
|
4370
|
+
// Transition for color changes
|
|
4371
|
+
"transition-colors duration-short2 ease-standard"
|
|
4372
|
+
],
|
|
4373
|
+
{
|
|
4374
|
+
variants: {
|
|
4375
|
+
/**
|
|
4376
|
+
* Whether this item is the currently active destination.
|
|
4377
|
+
* Controls background, text color, and icon color per MD3 spec.
|
|
4378
|
+
*/
|
|
4379
|
+
isActive: {
|
|
4380
|
+
true: [
|
|
4381
|
+
"bg-secondary-container",
|
|
4382
|
+
"text-on-secondary-container",
|
|
4383
|
+
"before:bg-on-secondary-container"
|
|
4384
|
+
],
|
|
4385
|
+
false: ["bg-transparent", "text-on-surface-variant", "before:bg-on-surface-variant"]
|
|
4386
|
+
},
|
|
4387
|
+
/**
|
|
4388
|
+
* Whether the item is disabled.
|
|
4389
|
+
* Applies `opacity-38` per MD3 disabled state spec.
|
|
4390
|
+
*/
|
|
4391
|
+
isDisabled: {
|
|
4392
|
+
true: ["opacity-38 cursor-not-allowed pointer-events-none"],
|
|
4393
|
+
false: []
|
|
4394
|
+
}
|
|
4395
|
+
},
|
|
4396
|
+
defaultVariants: {
|
|
4397
|
+
isActive: false,
|
|
4398
|
+
isDisabled: false
|
|
4399
|
+
}
|
|
4400
|
+
}
|
|
4401
|
+
);
|
|
4402
|
+
var scrimVariants = cva([
|
|
4403
|
+
"fixed inset-0 z-40",
|
|
4404
|
+
"bg-scrim opacity-32",
|
|
4405
|
+
"transition-opacity duration-medium2 ease-standard"
|
|
4406
|
+
]);
|
|
4407
|
+
var drawerSectionVariants = cva(["flex flex-col w-full"]);
|
|
4408
|
+
var drawerSectionHeaderVariants = cva([
|
|
4409
|
+
"px-4 pt-4 pb-2",
|
|
4410
|
+
"text-title-small text-on-surface-variant",
|
|
4411
|
+
"select-none"
|
|
4412
|
+
]);
|
|
4413
|
+
var drawerDividerVariants = cva(["border-t border-outline-variant", "mx-4 my-2"]);
|
|
4414
|
+
var Drawer = forwardRef(
|
|
4415
|
+
({
|
|
4416
|
+
variant = "standard",
|
|
4417
|
+
open,
|
|
4418
|
+
defaultOpen = false,
|
|
4419
|
+
onOpenChange,
|
|
4420
|
+
"aria-label": ariaLabel,
|
|
4421
|
+
children,
|
|
4422
|
+
className,
|
|
4423
|
+
disableRipple = false,
|
|
4424
|
+
...restProps
|
|
4425
|
+
}, ref) => {
|
|
4426
|
+
const isOpen = open ?? defaultOpen;
|
|
4427
|
+
const drawerPanelClass = cn(
|
|
4428
|
+
drawerVariants({
|
|
4429
|
+
variant,
|
|
4430
|
+
open: isOpen
|
|
4431
|
+
}),
|
|
4432
|
+
className
|
|
4433
|
+
);
|
|
4434
|
+
const scrimClass = scrimVariants();
|
|
4435
|
+
return /* @__PURE__ */ jsx(
|
|
4436
|
+
HeadlessDrawer,
|
|
4437
|
+
{
|
|
4438
|
+
ref,
|
|
4439
|
+
variant,
|
|
4440
|
+
...open !== void 0 ? { open } : {},
|
|
4441
|
+
...defaultOpen !== void 0 ? { defaultOpen } : {},
|
|
4442
|
+
...onOpenChange !== void 0 ? { onOpenChange } : {},
|
|
4443
|
+
"aria-label": ariaLabel,
|
|
4444
|
+
className: drawerPanelClass,
|
|
4445
|
+
scrimClassName: scrimClass,
|
|
4446
|
+
disableRipple,
|
|
4447
|
+
...restProps,
|
|
4448
|
+
children
|
|
4449
|
+
}
|
|
4450
|
+
);
|
|
4451
|
+
}
|
|
4452
|
+
);
|
|
4453
|
+
Drawer.displayName = "Drawer";
|
|
4454
|
+
var DrawerItem = forwardRef(
|
|
4455
|
+
({
|
|
4456
|
+
href,
|
|
4457
|
+
icon,
|
|
4458
|
+
label,
|
|
4459
|
+
badge,
|
|
4460
|
+
secondaryText,
|
|
4461
|
+
isActive = false,
|
|
4462
|
+
isDisabled = false,
|
|
4463
|
+
disableRipple = false,
|
|
4464
|
+
className,
|
|
4465
|
+
onPress,
|
|
4466
|
+
onPressStart,
|
|
4467
|
+
onPressEnd,
|
|
4468
|
+
onPressChange,
|
|
4469
|
+
onPressUp,
|
|
4470
|
+
...restProps
|
|
4471
|
+
}, ref) => {
|
|
4472
|
+
const isItemDisabled = isDisabled;
|
|
4473
|
+
const { onMouseDown: handleRipple, ripples } = useRipple({
|
|
4474
|
+
disabled: isItemDisabled || disableRipple
|
|
4475
|
+
});
|
|
4476
|
+
return /* @__PURE__ */ jsxs(
|
|
4477
|
+
HeadlessDrawerItem,
|
|
4478
|
+
{
|
|
4479
|
+
...restProps,
|
|
4480
|
+
ref,
|
|
4481
|
+
...href !== void 0 ? { href } : {},
|
|
4482
|
+
isActive,
|
|
4483
|
+
...isItemDisabled !== void 0 ? { isDisabled: isItemDisabled } : {},
|
|
4484
|
+
...onPress !== void 0 ? { onPress } : {},
|
|
4485
|
+
...onPressStart !== void 0 ? { onPressStart } : {},
|
|
4486
|
+
...onPressEnd !== void 0 ? { onPressEnd } : {},
|
|
4487
|
+
...onPressChange !== void 0 ? { onPressChange } : {},
|
|
4488
|
+
...onPressUp !== void 0 ? { onPressUp } : {},
|
|
4489
|
+
onMouseDown: handleRipple,
|
|
4490
|
+
className: cn(
|
|
4491
|
+
drawerItemVariants({
|
|
4492
|
+
isActive,
|
|
4493
|
+
isDisabled: isItemDisabled
|
|
4494
|
+
}),
|
|
4495
|
+
className
|
|
4496
|
+
),
|
|
4497
|
+
children: [
|
|
4498
|
+
ripples,
|
|
4499
|
+
icon && /* @__PURE__ */ jsx(
|
|
4500
|
+
"span",
|
|
4501
|
+
{
|
|
4502
|
+
className: "relative z-10 flex shrink-0 items-center justify-center",
|
|
4503
|
+
"aria-hidden": "true",
|
|
4504
|
+
children: icon
|
|
4505
|
+
}
|
|
4506
|
+
),
|
|
4507
|
+
/* @__PURE__ */ jsxs("span", { className: "relative z-10 flex min-w-0 flex-1 flex-col text-left", children: [
|
|
4508
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: label }),
|
|
4509
|
+
secondaryText && /* @__PURE__ */ jsx("span", { className: "text-body-small truncate opacity-70", children: secondaryText })
|
|
4510
|
+
] }),
|
|
4511
|
+
badge && /* @__PURE__ */ jsx(
|
|
4512
|
+
"span",
|
|
4513
|
+
{
|
|
4514
|
+
className: "relative z-10 ml-auto flex shrink-0 items-center pr-2",
|
|
4515
|
+
"aria-hidden": "true",
|
|
4516
|
+
children: badge
|
|
4517
|
+
}
|
|
4518
|
+
)
|
|
4519
|
+
]
|
|
4520
|
+
}
|
|
4521
|
+
);
|
|
4522
|
+
}
|
|
4523
|
+
);
|
|
4524
|
+
DrawerItem.displayName = "DrawerItem";
|
|
4525
|
+
var DrawerSection = forwardRef(
|
|
4526
|
+
({ header, children, showDivider = false, className }, ref) => {
|
|
4527
|
+
return /* @__PURE__ */ jsxs("div", { ref, className: cn(drawerSectionVariants(), className), children: [
|
|
4528
|
+
showDivider && /* @__PURE__ */ jsx("hr", { role: "separator", "aria-hidden": "true", className: drawerDividerVariants() }),
|
|
4529
|
+
header && /* @__PURE__ */ jsx("span", { className: drawerSectionHeaderVariants(), children: header }),
|
|
4530
|
+
children
|
|
4531
|
+
] });
|
|
4532
|
+
}
|
|
4533
|
+
);
|
|
4534
|
+
DrawerSection.displayName = "DrawerSection";
|
|
4535
|
+
var progressContainerVariants = cva(["inline-flex", "flex-col", "gap-1"], {
|
|
4536
|
+
variants: {
|
|
4537
|
+
/**
|
|
4538
|
+
* The visual type of the indicator.
|
|
4539
|
+
*/
|
|
4540
|
+
type: {
|
|
4541
|
+
linear: "w-full",
|
|
4542
|
+
circular: "items-center justify-center w-auto"
|
|
4543
|
+
}
|
|
4544
|
+
},
|
|
4545
|
+
defaultVariants: {
|
|
4546
|
+
type: "linear"
|
|
4547
|
+
}
|
|
4548
|
+
});
|
|
4549
|
+
var progressTrackVariants = cva([
|
|
4550
|
+
"relative",
|
|
4551
|
+
"w-full",
|
|
4552
|
+
"h-1",
|
|
4553
|
+
// MD3: 4dp track height
|
|
4554
|
+
"rounded-full",
|
|
4555
|
+
// MD3: full corner radius
|
|
4556
|
+
"overflow-hidden",
|
|
4557
|
+
"bg-surface-container-highest"
|
|
4558
|
+
// MD3: inactive track color
|
|
4559
|
+
]);
|
|
4560
|
+
var progressIndicatorVariants = cva([
|
|
4561
|
+
"absolute",
|
|
4562
|
+
"left-0",
|
|
4563
|
+
"top-0",
|
|
4564
|
+
"h-full",
|
|
4565
|
+
"rounded-full",
|
|
4566
|
+
"bg-primary",
|
|
4567
|
+
// MD3: active track color
|
|
4568
|
+
"transition-[width]",
|
|
4569
|
+
"duration-medium4",
|
|
4570
|
+
// MD3: 400ms for value transitions
|
|
4571
|
+
"ease-standard"
|
|
4572
|
+
// MD3: cubic-bezier(0.2, 0, 0, 1)
|
|
4573
|
+
]);
|
|
4574
|
+
var progressStopIndicatorVariants = cva([
|
|
4575
|
+
"absolute",
|
|
4576
|
+
"right-0",
|
|
4577
|
+
"top-1/2",
|
|
4578
|
+
"-translate-y-1/2",
|
|
4579
|
+
"w-1",
|
|
4580
|
+
"h-1",
|
|
4581
|
+
"rounded-full",
|
|
4582
|
+
"bg-primary"
|
|
4583
|
+
// MD3: stop indicator uses primary color
|
|
4584
|
+
]);
|
|
4585
|
+
var progressCircularSizeVariants = cva(
|
|
4586
|
+
["relative", "flex", "items-center", "justify-center", "flex-shrink-0"],
|
|
4587
|
+
{
|
|
4588
|
+
variants: {
|
|
4589
|
+
size: {
|
|
4590
|
+
small: "h-6 w-6",
|
|
4591
|
+
// MD3: 24dp
|
|
4592
|
+
medium: "h-12 w-12",
|
|
4593
|
+
// MD3: 48dp (default)
|
|
4594
|
+
large: "h-16 w-16"
|
|
4595
|
+
// MD3: 64dp
|
|
4596
|
+
}
|
|
4597
|
+
},
|
|
4598
|
+
defaultVariants: {
|
|
4599
|
+
size: "medium"
|
|
4600
|
+
}
|
|
4601
|
+
}
|
|
4602
|
+
);
|
|
4603
|
+
var progressLabelVariants = cva([
|
|
4604
|
+
"text-body-small",
|
|
4605
|
+
// MD3: body-small type scale (12px)
|
|
4606
|
+
"text-on-surface",
|
|
4607
|
+
// MD3: on-surface color role
|
|
4608
|
+
"select-none"
|
|
4609
|
+
]);
|
|
4610
|
+
var STROKE_WIDTH = 4;
|
|
4611
|
+
var CIRCULAR_SIZE_PX = {
|
|
4612
|
+
small: 24,
|
|
4613
|
+
medium: 48,
|
|
4614
|
+
large: 64
|
|
4615
|
+
};
|
|
4616
|
+
function getCircularGeometry(size) {
|
|
4617
|
+
const diameter = CIRCULAR_SIZE_PX[size];
|
|
4618
|
+
const radius = (diameter - STROKE_WIDTH) / 2;
|
|
4619
|
+
const circumference = 2 * Math.PI * radius;
|
|
4620
|
+
const viewBox = `0 0 ${diameter} ${diameter}`;
|
|
4621
|
+
const cx = diameter / 2;
|
|
4622
|
+
const cy = diameter / 2;
|
|
4623
|
+
return { diameter, radius, circumference, viewBox, cx, cy };
|
|
4624
|
+
}
|
|
4625
|
+
var Progress = forwardRef(
|
|
4626
|
+
({
|
|
4627
|
+
type = "linear",
|
|
4628
|
+
indeterminate = false,
|
|
4629
|
+
size = "medium",
|
|
4630
|
+
className,
|
|
4631
|
+
label,
|
|
4632
|
+
value = 0,
|
|
4633
|
+
minValue = 0,
|
|
4634
|
+
maxValue = 100,
|
|
4635
|
+
...restProps
|
|
4636
|
+
}, forwardedRef) => {
|
|
4637
|
+
const internalRef = useRef(null);
|
|
4638
|
+
const ref = forwardedRef ?? internalRef;
|
|
4639
|
+
const { progressBarProps, labelProps } = useProgressBar({
|
|
4640
|
+
label,
|
|
4641
|
+
value,
|
|
4642
|
+
minValue,
|
|
4643
|
+
maxValue,
|
|
4644
|
+
isIndeterminate: indeterminate,
|
|
4645
|
+
...restProps
|
|
4646
|
+
});
|
|
4647
|
+
const percentage = indeterminate ? 0 : Math.min(100, Math.max(0, (value - minValue) / (maxValue - minValue) * 100));
|
|
4648
|
+
if (process.env.NODE_ENV !== "production") {
|
|
4649
|
+
const ariaProps = restProps;
|
|
4650
|
+
if (!label && !ariaProps["aria-label"] && !ariaProps["aria-labelledby"]) {
|
|
4651
|
+
console.warn(
|
|
4652
|
+
"[Progress] Progress indicator should have a visible label prop or aria-label for accessibility."
|
|
4653
|
+
);
|
|
4654
|
+
}
|
|
4655
|
+
}
|
|
4656
|
+
return /* @__PURE__ */ jsxs(
|
|
4657
|
+
"div",
|
|
4658
|
+
{
|
|
4659
|
+
...progressBarProps,
|
|
4660
|
+
ref,
|
|
4661
|
+
className: cn(progressContainerVariants({ type }), className),
|
|
4662
|
+
children: [
|
|
4663
|
+
label && /* @__PURE__ */ jsx("span", { ...labelProps, className: cn(progressLabelVariants()), children: label }),
|
|
4664
|
+
type === "linear" ? /* @__PURE__ */ jsx(LinearProgress, { percentage, indeterminate }) : /* @__PURE__ */ jsx(CircularProgress, { percentage, indeterminate, size })
|
|
4665
|
+
]
|
|
4666
|
+
}
|
|
4667
|
+
);
|
|
4668
|
+
}
|
|
4669
|
+
);
|
|
4670
|
+
Progress.displayName = "Progress";
|
|
4671
|
+
function LinearProgress({ percentage, indeterminate }) {
|
|
4672
|
+
if (indeterminate) {
|
|
4673
|
+
return /* @__PURE__ */ jsx("div", { "data-progress-track": "", className: cn(progressTrackVariants()), children: /* @__PURE__ */ jsxs(
|
|
4674
|
+
"div",
|
|
4675
|
+
{
|
|
4676
|
+
"data-progress-indeterminate": "",
|
|
4677
|
+
className: "absolute inset-0 overflow-hidden rounded-full",
|
|
4678
|
+
children: [
|
|
4679
|
+
/* @__PURE__ */ jsx(
|
|
4680
|
+
"div",
|
|
4681
|
+
{
|
|
4682
|
+
className: cn(
|
|
4683
|
+
"bg-primary absolute top-0 h-full rounded-full",
|
|
4684
|
+
"animate-progress-linear-indeterminate-1"
|
|
4685
|
+
)
|
|
4686
|
+
}
|
|
4687
|
+
),
|
|
4688
|
+
/* @__PURE__ */ jsx(
|
|
4689
|
+
"div",
|
|
4690
|
+
{
|
|
4691
|
+
className: cn(
|
|
4692
|
+
"bg-primary absolute top-0 h-full rounded-full",
|
|
4693
|
+
"animate-progress-linear-indeterminate-2"
|
|
4694
|
+
)
|
|
4695
|
+
}
|
|
4696
|
+
)
|
|
4697
|
+
]
|
|
4698
|
+
}
|
|
4699
|
+
) });
|
|
4700
|
+
}
|
|
4701
|
+
return /* @__PURE__ */ jsxs("div", { "data-progress-track": "", className: cn(progressTrackVariants()), children: [
|
|
4702
|
+
/* @__PURE__ */ jsx(
|
|
4703
|
+
"div",
|
|
4704
|
+
{
|
|
4705
|
+
"data-progress-indicator": "",
|
|
4706
|
+
className: cn(progressIndicatorVariants()),
|
|
4707
|
+
style: { width: `${percentage}%` }
|
|
4708
|
+
}
|
|
4709
|
+
),
|
|
4710
|
+
/* @__PURE__ */ jsx(
|
|
4711
|
+
"div",
|
|
4712
|
+
{
|
|
4713
|
+
"data-stop-indicator": "",
|
|
4714
|
+
className: cn(progressStopIndicatorVariants()),
|
|
4715
|
+
"aria-hidden": "true"
|
|
4716
|
+
}
|
|
4717
|
+
)
|
|
4718
|
+
] });
|
|
4719
|
+
}
|
|
4720
|
+
function CircularProgress({
|
|
4721
|
+
percentage,
|
|
4722
|
+
indeterminate,
|
|
4723
|
+
size
|
|
4724
|
+
}) {
|
|
4725
|
+
const { radius, circumference, viewBox, cx, cy } = getCircularGeometry(size);
|
|
4726
|
+
const strokeDashoffset = (1 - percentage / 100) * circumference;
|
|
4727
|
+
if (indeterminate) {
|
|
4728
|
+
return /* @__PURE__ */ jsx(
|
|
4729
|
+
"div",
|
|
4730
|
+
{
|
|
4731
|
+
"data-progress-size": size,
|
|
4732
|
+
"data-progress-indeterminate": "",
|
|
4733
|
+
className: cn(progressCircularSizeVariants({ size })),
|
|
4734
|
+
children: /* @__PURE__ */ jsxs(
|
|
4735
|
+
"svg",
|
|
4736
|
+
{
|
|
4737
|
+
viewBox,
|
|
4738
|
+
className: "animate-progress-circular-rotate h-full w-full",
|
|
4739
|
+
"aria-hidden": "true",
|
|
4740
|
+
children: [
|
|
4741
|
+
/* @__PURE__ */ jsx(
|
|
4742
|
+
"circle",
|
|
4743
|
+
{
|
|
4744
|
+
cx,
|
|
4745
|
+
cy,
|
|
4746
|
+
r: radius,
|
|
4747
|
+
fill: "none",
|
|
4748
|
+
stroke: "currentColor",
|
|
4749
|
+
strokeWidth: STROKE_WIDTH,
|
|
4750
|
+
className: "text-surface-container-highest"
|
|
4751
|
+
}
|
|
4752
|
+
),
|
|
4753
|
+
/* @__PURE__ */ jsx(
|
|
4754
|
+
"circle",
|
|
4755
|
+
{
|
|
4756
|
+
cx,
|
|
4757
|
+
cy,
|
|
4758
|
+
r: radius,
|
|
4759
|
+
fill: "none",
|
|
4760
|
+
stroke: "currentColor",
|
|
4761
|
+
strokeWidth: STROKE_WIDTH,
|
|
4762
|
+
className: "animate-progress-circular-dash text-primary",
|
|
4763
|
+
strokeLinecap: "round"
|
|
4764
|
+
}
|
|
4765
|
+
)
|
|
4766
|
+
]
|
|
4767
|
+
}
|
|
4768
|
+
)
|
|
4769
|
+
}
|
|
4770
|
+
);
|
|
4771
|
+
}
|
|
4772
|
+
return /* @__PURE__ */ jsx("div", { "data-progress-size": size, className: cn(progressCircularSizeVariants({ size })), children: /* @__PURE__ */ jsxs("svg", { viewBox, className: "h-full w-full -rotate-90", "aria-hidden": "true", children: [
|
|
4773
|
+
/* @__PURE__ */ jsx(
|
|
4774
|
+
"circle",
|
|
4775
|
+
{
|
|
4776
|
+
cx,
|
|
4777
|
+
cy,
|
|
4778
|
+
r: radius,
|
|
4779
|
+
fill: "none",
|
|
4780
|
+
stroke: "currentColor",
|
|
4781
|
+
strokeWidth: STROKE_WIDTH,
|
|
4782
|
+
className: "text-surface-container-highest"
|
|
4783
|
+
}
|
|
4784
|
+
),
|
|
4785
|
+
/* @__PURE__ */ jsx(
|
|
4786
|
+
"circle",
|
|
4787
|
+
{
|
|
4788
|
+
cx,
|
|
4789
|
+
cy,
|
|
4790
|
+
r: radius,
|
|
4791
|
+
fill: "none",
|
|
4792
|
+
stroke: "currentColor",
|
|
4793
|
+
strokeWidth: STROKE_WIDTH,
|
|
4794
|
+
strokeLinecap: "round",
|
|
4795
|
+
className: "text-primary duration-medium4 ease-standard transition-[stroke-dashoffset]",
|
|
4796
|
+
strokeDasharray: circumference,
|
|
4797
|
+
strokeDashoffset
|
|
4798
|
+
}
|
|
4799
|
+
)
|
|
4800
|
+
] }) });
|
|
4801
|
+
}
|
|
4802
|
+
var ProgressHeadless = forwardRef(
|
|
4803
|
+
({
|
|
4804
|
+
type = "linear",
|
|
4805
|
+
indeterminate = false,
|
|
4806
|
+
size = "medium",
|
|
4807
|
+
className,
|
|
4808
|
+
children,
|
|
4809
|
+
renderProgress,
|
|
4810
|
+
label,
|
|
4811
|
+
value = 0,
|
|
4812
|
+
minValue = 0,
|
|
4813
|
+
maxValue = 100,
|
|
4814
|
+
...restProps
|
|
4815
|
+
}, forwardedRef) => {
|
|
4816
|
+
const internalRef = useRef(null);
|
|
4817
|
+
const ref = forwardedRef ?? internalRef;
|
|
4818
|
+
const { progressBarProps, labelProps } = useProgressBar({
|
|
4819
|
+
label,
|
|
4820
|
+
value,
|
|
4821
|
+
minValue,
|
|
4822
|
+
maxValue,
|
|
4823
|
+
isIndeterminate: indeterminate,
|
|
4824
|
+
...restProps
|
|
4825
|
+
});
|
|
4826
|
+
const percentage = indeterminate ? 0 : (value - minValue) / (maxValue - minValue) * 100;
|
|
4827
|
+
return /* @__PURE__ */ jsxs("div", { ...progressBarProps, ref, className, children: [
|
|
4828
|
+
label && /* @__PURE__ */ jsx("span", { ...labelProps, children: label }),
|
|
4829
|
+
renderProgress?.({
|
|
4830
|
+
percentage,
|
|
4831
|
+
isIndeterminate: indeterminate,
|
|
4832
|
+
type,
|
|
4833
|
+
size
|
|
4834
|
+
}),
|
|
4835
|
+
children
|
|
4836
|
+
] });
|
|
4837
|
+
}
|
|
4838
|
+
);
|
|
4839
|
+
ProgressHeadless.displayName = "ProgressHeadless";
|
|
4840
|
+
var MenuContext = createContext(null);
|
|
4841
|
+
function useMenuContext() {
|
|
4842
|
+
return useContext(MenuContext);
|
|
4843
|
+
}
|
|
4844
|
+
function TriggerBridge({ children }) {
|
|
4845
|
+
const ctx = useSlottedContext(ButtonContext);
|
|
4846
|
+
const localRef = useRef(null);
|
|
4847
|
+
const { ref: contextRef, ...ctxProps } = ctx ?? {};
|
|
4848
|
+
const mergedCallbackRef = useCallback(
|
|
4849
|
+
(node) => {
|
|
4850
|
+
localRef.current = node;
|
|
4851
|
+
if (!contextRef) return;
|
|
4852
|
+
if (typeof contextRef === "function") {
|
|
4853
|
+
contextRef(node);
|
|
4854
|
+
} else {
|
|
4855
|
+
contextRef.current = node;
|
|
4856
|
+
}
|
|
4857
|
+
},
|
|
4858
|
+
[contextRef]
|
|
4859
|
+
);
|
|
4860
|
+
const { buttonProps } = useButton({ ...ctxProps, elementType: "button" }, localRef);
|
|
4861
|
+
if (!isValidElement(children)) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
4862
|
+
return cloneElement(
|
|
4863
|
+
children,
|
|
4864
|
+
{ ...buttonProps, ref: mergedCallbackRef }
|
|
4865
|
+
);
|
|
4866
|
+
}
|
|
4867
|
+
function HeadlessMenuTrigger({
|
|
4868
|
+
children,
|
|
4869
|
+
placement = "bottom start",
|
|
4870
|
+
shouldFlip = true,
|
|
4871
|
+
...rest
|
|
4872
|
+
}) {
|
|
4873
|
+
const childrenArray = Array.isArray(children) ? children : [children];
|
|
4874
|
+
const [triggerChild, menuChild] = childrenArray;
|
|
4875
|
+
return /* @__PURE__ */ jsxs(MenuTrigger$1, { ...rest, children: [
|
|
4876
|
+
/* @__PURE__ */ jsx(TriggerBridge, { children: triggerChild }),
|
|
4877
|
+
/* @__PURE__ */ jsx(Popover, { placement, shouldFlip, offset: 4, children: menuChild })
|
|
4878
|
+
] });
|
|
4879
|
+
}
|
|
4880
|
+
function HeadlessMenu({
|
|
4881
|
+
className,
|
|
4882
|
+
children,
|
|
4883
|
+
"aria-label": ariaLabel,
|
|
4884
|
+
...props
|
|
4885
|
+
}) {
|
|
4886
|
+
const menuRef = useRef(null);
|
|
4887
|
+
useLayoutEffect(() => {
|
|
4888
|
+
if (ariaLabel && menuRef.current) {
|
|
4889
|
+
menuRef.current.removeAttribute("aria-labelledby");
|
|
4890
|
+
}
|
|
4891
|
+
});
|
|
4892
|
+
return /* @__PURE__ */ jsx(
|
|
4893
|
+
Menu$1,
|
|
4894
|
+
{
|
|
4895
|
+
...props,
|
|
4896
|
+
ref: menuRef,
|
|
4897
|
+
...ariaLabel !== void 0 ? { "aria-label": ariaLabel } : {},
|
|
4898
|
+
className: className ?? "",
|
|
4899
|
+
children
|
|
4900
|
+
}
|
|
4901
|
+
);
|
|
4902
|
+
}
|
|
4903
|
+
HeadlessMenuTrigger.Menu = HeadlessMenu;
|
|
4904
|
+
var HeadlessMenuItem = forwardRef(
|
|
4905
|
+
function HeadlessMenuItem2({ children, className, ...props }, ref) {
|
|
4906
|
+
return /* @__PURE__ */ jsx(MenuItem$1, { ...props, ref, className: className ?? "", children });
|
|
4907
|
+
}
|
|
4908
|
+
);
|
|
4909
|
+
function HeadlessMenuSection({
|
|
4910
|
+
children,
|
|
4911
|
+
"aria-label": ariaLabel,
|
|
4912
|
+
className
|
|
4913
|
+
}) {
|
|
4914
|
+
return /* @__PURE__ */ jsx(
|
|
4915
|
+
MenuSection$1,
|
|
4916
|
+
{
|
|
4917
|
+
...ariaLabel !== void 0 ? { "aria-label": ariaLabel } : {},
|
|
4918
|
+
className: className ?? "",
|
|
4919
|
+
children
|
|
4920
|
+
}
|
|
4921
|
+
);
|
|
4922
|
+
}
|
|
4923
|
+
function HeadlessMenuDivider({
|
|
4924
|
+
className,
|
|
4925
|
+
...props
|
|
4926
|
+
}) {
|
|
4927
|
+
return /* @__PURE__ */ jsx(Separator, { ...props, className: className ?? "" });
|
|
4928
|
+
}
|
|
4929
|
+
var menuContainerVariants = cva(
|
|
4930
|
+
[
|
|
4931
|
+
// Elevation
|
|
4932
|
+
"shadow-elevation-2",
|
|
4933
|
+
// Width constraints per MD3 spec (112dp min / 280dp max)
|
|
4934
|
+
"min-w-28 max-w-70",
|
|
4935
|
+
// Layout
|
|
4936
|
+
"py-2",
|
|
4937
|
+
// Scroll: show scrollbar when content overflows; max height avoids clipping
|
|
4938
|
+
"overflow-y-auto",
|
|
4939
|
+
"max-h-[calc(var(--visual-viewport-height,100vh)-2rem)]",
|
|
4940
|
+
// Stacking
|
|
4941
|
+
"z-50",
|
|
4942
|
+
// Focus outline handled by React Aria
|
|
4943
|
+
"outline-none",
|
|
4944
|
+
// GPU compositing — promotes menu to its own compositor layer so
|
|
4945
|
+
// scale + opacity animations run without triggering layout reflow.
|
|
4946
|
+
"will-change-[transform,opacity]",
|
|
4947
|
+
// Pointer events blocked during animation to prevent accidental clicks
|
|
4948
|
+
// on menu items while the panel is still animating in or out.
|
|
4949
|
+
"data-[entering]:pointer-events-none data-[exiting]:pointer-events-none",
|
|
4950
|
+
// ── Enter animation ────────────────────────────────────────────────────
|
|
4951
|
+
// @keyframes menu-enter (defined in styles.css): scale(0.8)+opacity:0 →
|
|
4952
|
+
// scale(1)+opacity:1 in 120ms with cubic-bezier(0,0,0.2,1) (standard
|
|
4953
|
+
// decelerate — matches Angular Material's _mat-menu-enter keyframe).
|
|
4954
|
+
"data-[entering]:animate-[menu-enter_120ms_cubic-bezier(0,0,0.2,1)_both]",
|
|
4955
|
+
// ── Exit animation ─────────────────────────────────────────────────────
|
|
4956
|
+
// @keyframes menu-exit (defined in styles.css): opacity:1 → opacity:0
|
|
4957
|
+
// in 100ms after 25ms delay, linear — matches Angular Material's
|
|
4958
|
+
// _mat-menu-exit keyframe (fade-only, no reverse scale).
|
|
4959
|
+
"data-[exiting]:animate-[menu-exit_100ms_25ms_linear_both]",
|
|
4960
|
+
// ── Transform origin (placement-aware) ────────────────────────────────
|
|
4961
|
+
// RAC sets data-placement="bottom|top|left|right" on the Popover element.
|
|
4962
|
+
// Default (bottom): origin at top edge (menu expands downward).
|
|
4963
|
+
"origin-top",
|
|
4964
|
+
// top: origin at bottom edge (menu expands upward)
|
|
4965
|
+
"data-[placement=top]:origin-bottom",
|
|
4966
|
+
// left: origin at right edge
|
|
4967
|
+
"data-[placement=left]:origin-right",
|
|
4968
|
+
// right: origin at left edge
|
|
4969
|
+
"data-[placement=right]:origin-left",
|
|
4970
|
+
// ── Reduced motion ────────────────────────────────────────────────────
|
|
4971
|
+
// Skip both animations entirely for users who prefer reduced motion.
|
|
4972
|
+
"motion-reduce:data-[entering]:animate-none motion-reduce:data-[exiting]:animate-none"
|
|
4973
|
+
],
|
|
4974
|
+
{
|
|
4975
|
+
variants: {
|
|
4976
|
+
/**
|
|
4977
|
+
* Color scheme — drives the container background.
|
|
4978
|
+
* baseline+standard uses a separate compound variant.
|
|
4979
|
+
*/
|
|
4980
|
+
colorScheme: {
|
|
4981
|
+
standard: [],
|
|
4982
|
+
vibrant: []
|
|
4983
|
+
},
|
|
4984
|
+
/**
|
|
4985
|
+
* Visual style — drives corner radius and baseline vs vertical background.
|
|
4986
|
+
*/
|
|
4987
|
+
menuStyle: {
|
|
4988
|
+
baseline: ["rounded-xs", "bg-surface-container"],
|
|
4989
|
+
vertical: ["rounded-lg", "bg-surface-container-low"]
|
|
4990
|
+
}
|
|
4991
|
+
},
|
|
4992
|
+
compoundVariants: [
|
|
4993
|
+
// Vertical + vibrant: tertiary container background
|
|
4994
|
+
{
|
|
4995
|
+
menuStyle: "vertical",
|
|
4996
|
+
colorScheme: "vibrant",
|
|
4997
|
+
class: ["bg-tertiary-container"]
|
|
4998
|
+
}
|
|
4999
|
+
],
|
|
5000
|
+
defaultVariants: {
|
|
5001
|
+
colorScheme: "standard",
|
|
5002
|
+
menuStyle: "baseline"
|
|
5003
|
+
}
|
|
5004
|
+
}
|
|
5005
|
+
);
|
|
5006
|
+
var menuItemVariants = cva(
|
|
5007
|
+
[
|
|
5008
|
+
// Layout — height set by density context in MenuItem component
|
|
5009
|
+
"relative flex w-full items-center",
|
|
5010
|
+
"px-3 gap-3",
|
|
5011
|
+
// Typography: Body Large per MD3 baseline spec
|
|
5012
|
+
"text-body-large",
|
|
5013
|
+
// Interaction
|
|
5014
|
+
"cursor-pointer select-none outline-none",
|
|
5015
|
+
// State layer pseudo-element
|
|
5016
|
+
"before:absolute before:inset-0 before:rounded-[inherit]",
|
|
5017
|
+
"before:transition-opacity before:duration-short2 before:ease-standard",
|
|
5018
|
+
"before:opacity-0",
|
|
5019
|
+
// Hover state layer
|
|
5020
|
+
"hover:before:opacity-8",
|
|
5021
|
+
// Focus visible state layer
|
|
5022
|
+
"focus-visible:before:opacity-12",
|
|
5023
|
+
// Active pressed state layer
|
|
5024
|
+
"active:before:opacity-12",
|
|
5025
|
+
// Color transition for selection
|
|
5026
|
+
"transition-colors duration-short2 ease-standard"
|
|
5027
|
+
],
|
|
5028
|
+
{
|
|
5029
|
+
variants: {
|
|
5030
|
+
/**
|
|
5031
|
+
* Disabled state: reduces opacity and blocks interaction.
|
|
5032
|
+
*/
|
|
5033
|
+
isDisabled: {
|
|
5034
|
+
true: ["opacity-38 cursor-not-allowed pointer-events-none"],
|
|
5035
|
+
false: []
|
|
5036
|
+
},
|
|
5037
|
+
/**
|
|
5038
|
+
* Selected state: background and text color driven by compound variants.
|
|
5039
|
+
*/
|
|
5040
|
+
isSelected: {
|
|
5041
|
+
true: [],
|
|
5042
|
+
false: []
|
|
5043
|
+
},
|
|
5044
|
+
/**
|
|
5045
|
+
* Color scheme: drives default text and state layer colors.
|
|
5046
|
+
* - standard: on-surface text, on-surface state layer
|
|
5047
|
+
* - vibrant (vertical only): on-tertiary-container text + state layer
|
|
5048
|
+
*/
|
|
5049
|
+
colorScheme: {
|
|
5050
|
+
standard: ["text-on-surface", "before:bg-on-surface"],
|
|
5051
|
+
vibrant: ["text-on-tertiary-container", "before:bg-on-tertiary-container"]
|
|
5052
|
+
},
|
|
5053
|
+
/**
|
|
5054
|
+
* Visual style: drives corner radius on items (vertical uses rounded-lg
|
|
5055
|
+
* inherited from container, items stay flat inside).
|
|
5056
|
+
*/
|
|
5057
|
+
menuStyle: {
|
|
5058
|
+
baseline: [],
|
|
5059
|
+
vertical: []
|
|
5060
|
+
}
|
|
5061
|
+
},
|
|
5062
|
+
compoundVariants: [
|
|
5063
|
+
// ── Baseline selection (both colorSchemes) ──────────────────────────
|
|
5064
|
+
{
|
|
5065
|
+
isSelected: true,
|
|
5066
|
+
menuStyle: "baseline",
|
|
5067
|
+
class: [
|
|
5068
|
+
"bg-surface-container-highest"
|
|
5069
|
+
// text-on-surface already applied by standard colorScheme variant
|
|
5070
|
+
]
|
|
5071
|
+
},
|
|
5072
|
+
// ── Vertical + Standard selection ───────────────────────────────────
|
|
5073
|
+
{
|
|
5074
|
+
isSelected: true,
|
|
5075
|
+
menuStyle: "vertical",
|
|
5076
|
+
colorScheme: "standard",
|
|
5077
|
+
class: [
|
|
5078
|
+
"bg-tertiary-container",
|
|
5079
|
+
"text-on-tertiary-container",
|
|
5080
|
+
"before:bg-on-tertiary-container"
|
|
5081
|
+
]
|
|
5082
|
+
},
|
|
5083
|
+
// ── Vertical + Vibrant selection ─────────────────────────────────────
|
|
5084
|
+
{
|
|
5085
|
+
isSelected: true,
|
|
5086
|
+
menuStyle: "vertical",
|
|
5087
|
+
colorScheme: "vibrant",
|
|
5088
|
+
class: ["bg-tertiary", "text-on-tertiary", "before:bg-on-tertiary"]
|
|
5089
|
+
}
|
|
5090
|
+
],
|
|
5091
|
+
defaultVariants: {
|
|
5092
|
+
isDisabled: false,
|
|
5093
|
+
isSelected: false,
|
|
5094
|
+
colorScheme: "standard",
|
|
5095
|
+
menuStyle: "baseline"
|
|
5096
|
+
}
|
|
5097
|
+
}
|
|
5098
|
+
);
|
|
5099
|
+
var menuSectionVariants = cva(["flex flex-col w-full"]);
|
|
5100
|
+
var menuSectionHeaderVariants = cva([
|
|
5101
|
+
"px-3 pt-2 pb-1",
|
|
5102
|
+
"text-title-small text-on-surface-variant",
|
|
5103
|
+
"select-none"
|
|
5104
|
+
]);
|
|
5105
|
+
var menuDividerVariants = cva(["border-t border-outline-variant", "my-2 mx-0"]);
|
|
5106
|
+
cva(["h-2 w-full"]);
|
|
5107
|
+
var menuItemTrailingTextVariants = cva([
|
|
5108
|
+
"ml-auto shrink-0 text-label-large text-on-surface-variant",
|
|
5109
|
+
"select-none"
|
|
5110
|
+
]);
|
|
5111
|
+
var menuItemDescriptionVariants = cva([
|
|
5112
|
+
"text-body-medium text-on-surface-variant",
|
|
5113
|
+
"select-none"
|
|
5114
|
+
]);
|
|
5115
|
+
var Menu = forwardRef(function Menu2({
|
|
5116
|
+
children,
|
|
5117
|
+
className,
|
|
5118
|
+
colorScheme = "standard",
|
|
5119
|
+
menuStyle = "baseline",
|
|
5120
|
+
density = 0,
|
|
5121
|
+
disableRipple = false,
|
|
5122
|
+
selectionMode,
|
|
5123
|
+
selectedKeys,
|
|
5124
|
+
onSelectionChange,
|
|
5125
|
+
...props
|
|
5126
|
+
}, _ref) {
|
|
5127
|
+
const close = () => {
|
|
5128
|
+
};
|
|
5129
|
+
const contextValue = {
|
|
5130
|
+
close,
|
|
5131
|
+
disableRipple,
|
|
5132
|
+
colorScheme,
|
|
5133
|
+
menuStyle,
|
|
5134
|
+
density,
|
|
5135
|
+
...selectionMode !== void 0 ? { selectionMode } : {},
|
|
5136
|
+
...selectedKeys !== void 0 ? { selectedKeys } : {}
|
|
5137
|
+
};
|
|
5138
|
+
return /* @__PURE__ */ jsx(MenuContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
|
|
5139
|
+
HeadlessMenu,
|
|
5140
|
+
{
|
|
5141
|
+
...props,
|
|
5142
|
+
...selectionMode !== void 0 ? { selectionMode } : {},
|
|
5143
|
+
...selectedKeys !== void 0 ? { selectedKeys } : {},
|
|
5144
|
+
...onSelectionChange !== void 0 ? { onSelectionChange } : {},
|
|
5145
|
+
className: cn(menuContainerVariants({ colorScheme, menuStyle }), className),
|
|
5146
|
+
children
|
|
5147
|
+
}
|
|
5148
|
+
) });
|
|
5149
|
+
});
|
|
5150
|
+
function MenuTrigger({
|
|
5151
|
+
children,
|
|
5152
|
+
placement = "bottom start",
|
|
5153
|
+
shouldFlip = true,
|
|
5154
|
+
...rest
|
|
5155
|
+
}) {
|
|
5156
|
+
return /* @__PURE__ */ jsx(HeadlessMenuTrigger, { placement, shouldFlip, ...rest, children });
|
|
5157
|
+
}
|
|
5158
|
+
MenuTrigger.Menu = Menu;
|
|
5159
|
+
function CheckIcon() {
|
|
5160
|
+
return /* @__PURE__ */ jsx(
|
|
5161
|
+
"svg",
|
|
5162
|
+
{
|
|
5163
|
+
"data-testid": "check-icon",
|
|
5164
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
5165
|
+
viewBox: "0 0 24 24",
|
|
5166
|
+
fill: "currentColor",
|
|
5167
|
+
"aria-hidden": "true",
|
|
5168
|
+
focusable: "false",
|
|
5169
|
+
className: "h-full w-full",
|
|
5170
|
+
children: /* @__PURE__ */ jsx("path", { d: "M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" })
|
|
5171
|
+
}
|
|
5172
|
+
);
|
|
5173
|
+
}
|
|
5174
|
+
var DENSITY_HEIGHT = {
|
|
5175
|
+
0: "h-12",
|
|
5176
|
+
[-1]: "h-11",
|
|
5177
|
+
[-2]: "h-10",
|
|
5178
|
+
[-3]: "h-9"
|
|
5179
|
+
};
|
|
5180
|
+
var MenuItem = forwardRef(function MenuItem2({
|
|
5181
|
+
children,
|
|
5182
|
+
leadingIcon,
|
|
5183
|
+
trailingIcon,
|
|
5184
|
+
trailingText,
|
|
5185
|
+
description,
|
|
5186
|
+
badge,
|
|
5187
|
+
className,
|
|
5188
|
+
disableRipple: itemDisableRipple,
|
|
5189
|
+
...props
|
|
5190
|
+
}, ref) {
|
|
5191
|
+
const ctx = useMenuContext();
|
|
5192
|
+
const disableRipple = itemDisableRipple ?? ctx?.disableRipple ?? false;
|
|
5193
|
+
const colorScheme = ctx?.colorScheme ?? "standard";
|
|
5194
|
+
const menuStyle = ctx?.menuStyle ?? "baseline";
|
|
5195
|
+
const density = ctx?.density ?? 0;
|
|
5196
|
+
const selectionMode = ctx?.selectionMode;
|
|
5197
|
+
const heightClass = DENSITY_HEIGHT[density];
|
|
5198
|
+
const isSelectionMenu = selectionMode != null;
|
|
5199
|
+
const { ripples, onMouseDown } = useRipple({ disabled: disableRipple });
|
|
5200
|
+
const computeClassName = ({ isDisabled, isSelected }) => cn(
|
|
5201
|
+
menuItemVariants({
|
|
5202
|
+
isDisabled,
|
|
5203
|
+
isSelected: isSelected ?? false,
|
|
5204
|
+
colorScheme,
|
|
5205
|
+
menuStyle
|
|
5206
|
+
}),
|
|
5207
|
+
// Height: auto when description is present (multi-line), otherwise density
|
|
5208
|
+
description ? "min-h-12 py-2 h-auto items-start" : heightClass,
|
|
5209
|
+
className
|
|
5210
|
+
);
|
|
5211
|
+
return /* @__PURE__ */ jsx(
|
|
5212
|
+
HeadlessMenuItem,
|
|
5213
|
+
{
|
|
5214
|
+
...props,
|
|
5215
|
+
ref,
|
|
5216
|
+
className: computeClassName,
|
|
5217
|
+
onMouseDown,
|
|
5218
|
+
children: ({ isSelected }) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5219
|
+
!disableRipple && /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute inset-0 z-0 overflow-hidden rounded-[inherit]", children: ripples }),
|
|
5220
|
+
(leadingIcon != null || isSelectionMenu) && /* @__PURE__ */ jsx(
|
|
5221
|
+
"span",
|
|
5222
|
+
{
|
|
5223
|
+
className: "text-on-surface-variant relative z-10 flex h-6 w-6 shrink-0 items-center justify-center",
|
|
5224
|
+
"aria-hidden": "true",
|
|
5225
|
+
children: isSelectionMenu && leadingIcon == null ? isSelected ? /* @__PURE__ */ jsx(CheckIcon, {}) : null : leadingIcon
|
|
5226
|
+
}
|
|
5227
|
+
),
|
|
5228
|
+
description != null ? /* @__PURE__ */ jsxs("span", { className: "relative z-10 flex min-w-0 flex-1 flex-col", children: [
|
|
5229
|
+
/* @__PURE__ */ jsx("span", { className: "text-body-large", children }),
|
|
5230
|
+
/* @__PURE__ */ jsx("span", { className: menuItemDescriptionVariants(), children: description })
|
|
5231
|
+
] }) : /* @__PURE__ */ jsx("span", { className: "text-body-large relative z-10 min-w-0 flex-1", children }),
|
|
5232
|
+
badge != null && /* @__PURE__ */ jsx("span", { className: "relative z-10 shrink-0", children: badge }),
|
|
5233
|
+
trailingIcon != null && trailingText == null && /* @__PURE__ */ jsx(
|
|
5234
|
+
"span",
|
|
5235
|
+
{
|
|
5236
|
+
className: "text-on-surface-variant relative z-10 ml-auto flex h-6 w-6 shrink-0 items-center justify-center",
|
|
5237
|
+
"aria-hidden": "true",
|
|
5238
|
+
children: trailingIcon
|
|
5239
|
+
}
|
|
5240
|
+
),
|
|
5241
|
+
trailingText != null && trailingIcon == null && /* @__PURE__ */ jsx(
|
|
5242
|
+
"span",
|
|
5243
|
+
{
|
|
5244
|
+
className: cn(menuItemTrailingTextVariants(), "relative z-10"),
|
|
5245
|
+
"aria-keyshortcuts": trailingText,
|
|
5246
|
+
children: trailingText
|
|
5247
|
+
}
|
|
5248
|
+
)
|
|
5249
|
+
] })
|
|
5250
|
+
}
|
|
5251
|
+
);
|
|
5252
|
+
});
|
|
5253
|
+
function MenuSection({
|
|
5254
|
+
children,
|
|
5255
|
+
header,
|
|
5256
|
+
showDivider = false,
|
|
5257
|
+
className,
|
|
5258
|
+
"aria-label": ariaLabel
|
|
5259
|
+
}) {
|
|
5260
|
+
const sectionAriaLabel = ariaLabel ?? header;
|
|
5261
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5262
|
+
showDivider && /* @__PURE__ */ jsx(HeadlessMenuDivider, { className: menuDividerVariants() }),
|
|
5263
|
+
/* @__PURE__ */ jsxs(
|
|
5264
|
+
HeadlessMenuSection,
|
|
5265
|
+
{
|
|
5266
|
+
"aria-label": sectionAriaLabel,
|
|
5267
|
+
className: cn(menuSectionVariants(), className),
|
|
5268
|
+
children: [
|
|
5269
|
+
header && /* @__PURE__ */ jsx(Header, { className: menuSectionHeaderVariants(), "aria-hidden": "true", children: header }),
|
|
5270
|
+
children
|
|
5271
|
+
]
|
|
5272
|
+
}
|
|
5273
|
+
)
|
|
5274
|
+
] });
|
|
5275
|
+
}
|
|
5276
|
+
function MenuDivider({ className }) {
|
|
5277
|
+
return /* @__PURE__ */ jsx(HeadlessMenuDivider, { className: cn(menuDividerVariants(), className) });
|
|
5278
|
+
}
|
|
5279
|
+
var SnackbarHeadless = forwardRef(
|
|
5280
|
+
function SnackbarHeadless2({
|
|
5281
|
+
message,
|
|
5282
|
+
supportingText,
|
|
5283
|
+
action,
|
|
5284
|
+
showClose,
|
|
5285
|
+
duration = 4e3,
|
|
5286
|
+
severity = "default",
|
|
5287
|
+
position = "bottom-center",
|
|
5288
|
+
onClose,
|
|
5289
|
+
children,
|
|
5290
|
+
className,
|
|
5291
|
+
getAnimationClassName
|
|
5292
|
+
}, ref) {
|
|
5293
|
+
const [animationState, setAnimationState] = useState("entering");
|
|
5294
|
+
const remainingRef = useRef(duration);
|
|
5295
|
+
const startedAtRef = useRef(Date.now());
|
|
5296
|
+
const dismissTimerRef = useRef(null);
|
|
5297
|
+
const exitFallbackRef = useRef(null);
|
|
5298
|
+
const pausedRef = useRef(false);
|
|
5299
|
+
const closedRef = useRef(false);
|
|
5300
|
+
const clearDismissTimer = useCallback(() => {
|
|
5301
|
+
if (dismissTimerRef.current !== null) {
|
|
5302
|
+
clearTimeout(dismissTimerRef.current);
|
|
5303
|
+
dismissTimerRef.current = null;
|
|
5304
|
+
}
|
|
5305
|
+
}, []);
|
|
5306
|
+
const triggerExit = useCallback(() => {
|
|
5307
|
+
if (closedRef.current) return;
|
|
5308
|
+
clearDismissTimer();
|
|
5309
|
+
setAnimationState("exiting");
|
|
5310
|
+
exitFallbackRef.current = setTimeout(() => {
|
|
5311
|
+
if (!closedRef.current) {
|
|
5312
|
+
closedRef.current = true;
|
|
5313
|
+
setAnimationState("exited");
|
|
5314
|
+
onClose?.();
|
|
5315
|
+
}
|
|
5316
|
+
}, 250);
|
|
5317
|
+
}, [clearDismissTimer, onClose]);
|
|
5318
|
+
const startDismissTimer = useCallback(
|
|
5319
|
+
(ms) => {
|
|
5320
|
+
if (ms <= 0) return;
|
|
5321
|
+
clearDismissTimer();
|
|
5322
|
+
startedAtRef.current = Date.now();
|
|
5323
|
+
dismissTimerRef.current = setTimeout(triggerExit, ms);
|
|
5324
|
+
},
|
|
5325
|
+
[clearDismissTimer, triggerExit]
|
|
5326
|
+
);
|
|
5327
|
+
useEffect(() => {
|
|
5328
|
+
let frameId;
|
|
5329
|
+
frameId = requestAnimationFrame(() => {
|
|
5330
|
+
frameId = requestAnimationFrame(() => {
|
|
5331
|
+
setAnimationState("visible");
|
|
5332
|
+
});
|
|
5333
|
+
});
|
|
5334
|
+
return () => cancelAnimationFrame(frameId);
|
|
5335
|
+
}, []);
|
|
5336
|
+
useEffect(() => {
|
|
5337
|
+
if (animationState !== "visible") return;
|
|
5338
|
+
if (duration <= 0) return;
|
|
5339
|
+
remainingRef.current = duration;
|
|
5340
|
+
startDismissTimer(duration);
|
|
5341
|
+
return clearDismissTimer;
|
|
5342
|
+
}, [animationState, duration, startDismissTimer, clearDismissTimer]);
|
|
5343
|
+
useEffect(
|
|
5344
|
+
() => () => {
|
|
5345
|
+
clearDismissTimer();
|
|
5346
|
+
if (exitFallbackRef.current !== null) {
|
|
5347
|
+
clearTimeout(exitFallbackRef.current);
|
|
5348
|
+
exitFallbackRef.current = null;
|
|
5349
|
+
}
|
|
5350
|
+
},
|
|
5351
|
+
[clearDismissTimer]
|
|
5352
|
+
);
|
|
5353
|
+
const handleTransitionEnd = useCallback(() => {
|
|
5354
|
+
if (animationState === "exiting" && !closedRef.current) {
|
|
5355
|
+
if (exitFallbackRef.current !== null) {
|
|
5356
|
+
clearTimeout(exitFallbackRef.current);
|
|
5357
|
+
exitFallbackRef.current = null;
|
|
5358
|
+
}
|
|
5359
|
+
closedRef.current = true;
|
|
5360
|
+
setAnimationState("exited");
|
|
5361
|
+
onClose?.();
|
|
5362
|
+
}
|
|
5363
|
+
}, [animationState, onClose]);
|
|
5364
|
+
const handleMouseEnter = useCallback(() => {
|
|
5365
|
+
if (pausedRef.current || animationState !== "visible") return;
|
|
5366
|
+
pausedRef.current = true;
|
|
5367
|
+
const elapsed = Date.now() - startedAtRef.current;
|
|
5368
|
+
remainingRef.current = Math.max(remainingRef.current - elapsed, 0);
|
|
5369
|
+
clearDismissTimer();
|
|
5370
|
+
}, [animationState, clearDismissTimer]);
|
|
5371
|
+
const handleMouseLeave = useCallback(() => {
|
|
5372
|
+
if (!pausedRef.current || animationState !== "visible") return;
|
|
5373
|
+
pausedRef.current = false;
|
|
5374
|
+
if (duration > 0) startDismissTimer(remainingRef.current);
|
|
5375
|
+
}, [animationState, duration, startDismissTimer]);
|
|
5376
|
+
const handleFocusIn = useCallback(() => {
|
|
5377
|
+
if (pausedRef.current || animationState !== "visible") return;
|
|
5378
|
+
pausedRef.current = true;
|
|
5379
|
+
const elapsed = Date.now() - startedAtRef.current;
|
|
5380
|
+
remainingRef.current = Math.max(remainingRef.current - elapsed, 0);
|
|
5381
|
+
clearDismissTimer();
|
|
5382
|
+
}, [animationState, clearDismissTimer]);
|
|
5383
|
+
const handleFocusOut = useCallback(() => {
|
|
5384
|
+
if (!pausedRef.current || animationState !== "visible") return;
|
|
5385
|
+
pausedRef.current = false;
|
|
5386
|
+
if (duration > 0) startDismissTimer(remainingRef.current);
|
|
5387
|
+
}, [animationState, duration, startDismissTimer]);
|
|
5388
|
+
const handleClose = useCallback(() => {
|
|
5389
|
+
triggerExit();
|
|
5390
|
+
}, [triggerExit]);
|
|
5391
|
+
const ariaProps = severity === "error" ? { role: "alert", "aria-live": "assertive", "aria-atomic": "true" } : { role: "status", "aria-live": "polite", "aria-atomic": "true" };
|
|
5392
|
+
const computedClassName = cn(className, getAnimationClassName?.(animationState, position));
|
|
5393
|
+
const childContent = typeof children === "function" ? children({ animationState, onClose: handleClose }) : children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5394
|
+
/* @__PURE__ */ jsx("span", { children: message }),
|
|
5395
|
+
supportingText && /* @__PURE__ */ jsx("span", { children: supportingText }),
|
|
5396
|
+
action && /* @__PURE__ */ jsx("button", { type: "button", onClick: action.onAction, children: action.label }),
|
|
5397
|
+
showClose && /* @__PURE__ */ jsx("button", { type: "button", "aria-label": "Close", onClick: handleClose, children: "\u2715" })
|
|
5398
|
+
] });
|
|
5399
|
+
return (
|
|
5400
|
+
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
|
5401
|
+
/* @__PURE__ */ jsx(
|
|
5402
|
+
"div",
|
|
5403
|
+
{
|
|
5404
|
+
ref,
|
|
5405
|
+
className: computedClassName,
|
|
5406
|
+
...ariaProps,
|
|
5407
|
+
onMouseEnter: handleMouseEnter,
|
|
5408
|
+
onMouseLeave: handleMouseLeave,
|
|
5409
|
+
onFocus: handleFocusIn,
|
|
5410
|
+
onBlur: handleFocusOut,
|
|
5411
|
+
onTransitionEnd: handleTransitionEnd,
|
|
5412
|
+
"data-animation-state": animationState,
|
|
5413
|
+
children: childContent
|
|
5414
|
+
}
|
|
5415
|
+
)
|
|
5416
|
+
);
|
|
5417
|
+
}
|
|
5418
|
+
);
|
|
5419
|
+
SnackbarHeadless.displayName = "SnackbarHeadless";
|
|
5420
|
+
var snackbarStackContainerVariants = cva(
|
|
5421
|
+
["fixed", "z-50", "flex", "gap-2", "pointer-events-none"],
|
|
5422
|
+
{
|
|
5423
|
+
variants: {
|
|
5424
|
+
position: {
|
|
5425
|
+
"bottom-center": [
|
|
5426
|
+
"bottom-4",
|
|
5427
|
+
"left-1/2",
|
|
5428
|
+
"-translate-x-1/2",
|
|
5429
|
+
"flex-col-reverse",
|
|
5430
|
+
"items-center"
|
|
5431
|
+
],
|
|
5432
|
+
"bottom-left": ["bottom-4", "left-4", "flex-col-reverse", "items-start"],
|
|
5433
|
+
"bottom-right": ["bottom-4", "right-4", "flex-col-reverse", "items-end"],
|
|
5434
|
+
"top-center": ["top-4", "left-1/2", "-translate-x-1/2", "flex-col", "items-center"],
|
|
5435
|
+
"top-left": ["top-4", "left-4", "flex-col", "items-start"],
|
|
5436
|
+
"top-right": ["top-4", "right-4", "flex-col", "items-end"]
|
|
5437
|
+
}
|
|
5438
|
+
},
|
|
5439
|
+
defaultVariants: { position: "bottom-center" }
|
|
5440
|
+
}
|
|
5441
|
+
);
|
|
5442
|
+
var snackbarBaseVariants = cva(
|
|
5443
|
+
[
|
|
5444
|
+
// Sizing (MD3 spec: 288dp min, 568dp max)
|
|
5445
|
+
"min-w-72",
|
|
5446
|
+
"max-w-snackbar-max",
|
|
5447
|
+
"w-max",
|
|
5448
|
+
"min-h-12",
|
|
5449
|
+
// Restore pointer events so hover/focus timer pause works
|
|
5450
|
+
"pointer-events-auto",
|
|
5451
|
+
// Surface
|
|
5452
|
+
"bg-inverse-surface",
|
|
5453
|
+
// Shape: MD3 extra-small corner = 4dp
|
|
5454
|
+
"rounded-xs",
|
|
5455
|
+
// Elevation level 3
|
|
5456
|
+
"shadow-elevation-3",
|
|
5457
|
+
// Layout
|
|
5458
|
+
"flex",
|
|
5459
|
+
"items-center",
|
|
5460
|
+
"gap-x-1",
|
|
5461
|
+
"pl-4 pr-2",
|
|
5462
|
+
// Typography
|
|
5463
|
+
"text-body-medium",
|
|
5464
|
+
"text-inverse-on-surface",
|
|
5465
|
+
// Transition (properties used by both entry and exit)
|
|
5466
|
+
"transition-[opacity,transform]",
|
|
5467
|
+
"will-change-[opacity,transform]"
|
|
5468
|
+
],
|
|
5469
|
+
{
|
|
5470
|
+
variants: {
|
|
5471
|
+
/**
|
|
5472
|
+
* Whether the Snackbar has supporting text (two-line layout).
|
|
5473
|
+
* Adjusts vertical padding to MD3 spec for two-line configuration.
|
|
5474
|
+
*/
|
|
5475
|
+
twoLine: {
|
|
5476
|
+
true: "py-1",
|
|
5477
|
+
false: "py-1"
|
|
5478
|
+
}
|
|
5479
|
+
},
|
|
5480
|
+
defaultVariants: {
|
|
5481
|
+
twoLine: false
|
|
5482
|
+
}
|
|
5483
|
+
}
|
|
5484
|
+
);
|
|
5485
|
+
cva("", {
|
|
5486
|
+
variants: {
|
|
5487
|
+
position: {
|
|
5488
|
+
"bottom-center": ["bottom-4", "left-1/2", "-translate-x-1/2"],
|
|
5489
|
+
"bottom-left": ["bottom-4", "left-4"],
|
|
5490
|
+
"bottom-right": ["bottom-4", "right-4"],
|
|
5491
|
+
"top-center": ["top-4", "left-1/2", "-translate-x-1/2"],
|
|
5492
|
+
"top-left": ["top-4", "left-4"],
|
|
5493
|
+
"top-right": ["top-4", "right-4"]
|
|
5494
|
+
}
|
|
5495
|
+
},
|
|
5496
|
+
defaultVariants: {
|
|
5497
|
+
position: "bottom-center"
|
|
5498
|
+
}
|
|
5499
|
+
});
|
|
5500
|
+
var snackbarAnimationVariants = cva("", {
|
|
5501
|
+
variants: {
|
|
5502
|
+
animationState: {
|
|
5503
|
+
entering: ["opacity-0", "scale-75"],
|
|
5504
|
+
visible: ["scale-100", "opacity-100", "duration-medium1", "ease-emphasized-decelerate"],
|
|
5505
|
+
exiting: ["scale-75", "opacity-0", "duration-short4", "ease-standard-accelerate"],
|
|
5506
|
+
exited: ["scale-75", "opacity-0", "duration-short4", "ease-standard-accelerate"]
|
|
5507
|
+
},
|
|
5508
|
+
enterDirection: {
|
|
5509
|
+
up: ["origin-bottom"],
|
|
5510
|
+
down: ["origin-top"]
|
|
5511
|
+
}
|
|
5512
|
+
},
|
|
5513
|
+
defaultVariants: {
|
|
5514
|
+
animationState: "entering",
|
|
5515
|
+
enterDirection: "up"
|
|
5516
|
+
}
|
|
5517
|
+
});
|
|
5518
|
+
cva([...snackbarBaseVariants()], {
|
|
5519
|
+
variants: {
|
|
5520
|
+
animationState: {
|
|
5521
|
+
entering: ["opacity-0", "scale-75"],
|
|
5522
|
+
visible: ["scale-100", "opacity-100", "duration-medium1", "ease-emphasized-decelerate"],
|
|
5523
|
+
exiting: ["scale-75", "opacity-0", "duration-short4", "ease-standard-accelerate"],
|
|
5524
|
+
exited: ["scale-75", "opacity-0", "duration-short4", "ease-standard-accelerate"]
|
|
5525
|
+
},
|
|
5526
|
+
enterDirection: {
|
|
5527
|
+
up: ["origin-bottom"],
|
|
5528
|
+
down: ["origin-top"]
|
|
5529
|
+
},
|
|
5530
|
+
position: {
|
|
5531
|
+
"bottom-center": ["bottom-4", "left-1/2", "-translate-x-1/2"],
|
|
5532
|
+
"bottom-left": ["bottom-4", "left-4"],
|
|
5533
|
+
"bottom-right": ["bottom-4", "right-4"],
|
|
5534
|
+
"top-center": ["top-4", "left-1/2", "-translate-x-1/2"],
|
|
5535
|
+
"top-left": ["top-4", "left-4"],
|
|
5536
|
+
"top-right": ["top-4", "right-4"]
|
|
5537
|
+
},
|
|
5538
|
+
twoLine: {
|
|
5539
|
+
true: "py-1",
|
|
5540
|
+
false: "py-1"
|
|
5541
|
+
}
|
|
5542
|
+
},
|
|
5543
|
+
defaultVariants: {
|
|
5544
|
+
animationState: "entering",
|
|
5545
|
+
enterDirection: "up",
|
|
5546
|
+
position: "bottom-center",
|
|
5547
|
+
twoLine: false
|
|
5548
|
+
}
|
|
5549
|
+
});
|
|
5550
|
+
var snackbarMessageVariants = cva([
|
|
5551
|
+
"flex-1",
|
|
5552
|
+
"text-body-medium",
|
|
5553
|
+
"text-inverse-on-surface"
|
|
5554
|
+
]);
|
|
5555
|
+
var snackbarSupportingTextVariants = cva([
|
|
5556
|
+
"text-body-medium",
|
|
5557
|
+
"text-inverse-on-surface",
|
|
5558
|
+
"opacity-80"
|
|
5559
|
+
]);
|
|
5560
|
+
var snackbarActionVariants = cva(["shrink-0", "text-inverse-primary"]);
|
|
5561
|
+
var snackbarCloseVariants = cva(["shrink-0", "text-inverse-on-surface"]);
|
|
5562
|
+
var snackbarContentVariants = cva(["flex", "flex-col", "flex-1", "min-w-0 py-2 pr-2"]);
|
|
5563
|
+
cva(["scale-75", "opacity-0"]);
|
|
5564
|
+
function getEnterDirection(position) {
|
|
5565
|
+
return position.startsWith("top") ? "down" : "up";
|
|
5566
|
+
}
|
|
5567
|
+
function CloseIcon() {
|
|
5568
|
+
return /* @__PURE__ */ jsx(
|
|
5569
|
+
"svg",
|
|
5570
|
+
{
|
|
5571
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
5572
|
+
width: "24",
|
|
5573
|
+
height: "24",
|
|
5574
|
+
viewBox: "0 0 24 24",
|
|
5575
|
+
fill: "currentColor",
|
|
5576
|
+
"aria-hidden": "true",
|
|
5577
|
+
children: /* @__PURE__ */ jsx("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" })
|
|
5578
|
+
}
|
|
5579
|
+
);
|
|
5580
|
+
}
|
|
5581
|
+
var Snackbar = forwardRef(function Snackbar2({
|
|
5582
|
+
message,
|
|
5583
|
+
supportingText,
|
|
5584
|
+
action,
|
|
5585
|
+
showClose = false,
|
|
5586
|
+
duration = 4e3,
|
|
5587
|
+
severity = "default",
|
|
5588
|
+
position = "bottom-center",
|
|
5589
|
+
onClose,
|
|
5590
|
+
className
|
|
5591
|
+
}, ref) {
|
|
5592
|
+
const isTwoLine = Boolean(supportingText);
|
|
5593
|
+
const baseClassName = cn(snackbarBaseVariants({ twoLine: isTwoLine }), className);
|
|
5594
|
+
return /* @__PURE__ */ jsx(
|
|
5595
|
+
SnackbarHeadless,
|
|
5596
|
+
{
|
|
5597
|
+
ref,
|
|
5598
|
+
message,
|
|
5599
|
+
...supportingText !== void 0 && { supportingText },
|
|
5600
|
+
...action !== void 0 && { action },
|
|
5601
|
+
showClose,
|
|
5602
|
+
duration,
|
|
5603
|
+
severity,
|
|
5604
|
+
position,
|
|
5605
|
+
...onClose !== void 0 && { onClose },
|
|
5606
|
+
className: baseClassName,
|
|
5607
|
+
getAnimationClassName: (state, pos) => snackbarAnimationVariants({ animationState: state, enterDirection: getEnterDirection(pos) }),
|
|
5608
|
+
children: ({ onClose: triggerClose }) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5609
|
+
/* @__PURE__ */ jsxs("div", { className: snackbarContentVariants(), children: [
|
|
5610
|
+
/* @__PURE__ */ jsx("span", { className: snackbarMessageVariants(), children: message }),
|
|
5611
|
+
supportingText && /* @__PURE__ */ jsx("span", { className: snackbarSupportingTextVariants(), children: supportingText })
|
|
5612
|
+
] }),
|
|
5613
|
+
action && /* @__PURE__ */ jsx("span", { className: snackbarActionVariants(), children: /* @__PURE__ */ jsx(
|
|
5614
|
+
Button,
|
|
5615
|
+
{
|
|
5616
|
+
variant: "text",
|
|
5617
|
+
onPress: action.onAction,
|
|
5618
|
+
className: "text-inverse-primary hover:text-inverse-primary",
|
|
5619
|
+
children: action.label
|
|
5620
|
+
}
|
|
5621
|
+
) }),
|
|
5622
|
+
showClose && /* @__PURE__ */ jsx("span", { className: snackbarCloseVariants(), children: /* @__PURE__ */ jsx(
|
|
5623
|
+
IconButton,
|
|
5624
|
+
{
|
|
5625
|
+
variant: "standard",
|
|
5626
|
+
"aria-label": "Close",
|
|
5627
|
+
onPress: triggerClose,
|
|
5628
|
+
className: "text-inverse-on-surface hover:text-inverse-on-surface",
|
|
5629
|
+
children: /* @__PURE__ */ jsx(CloseIcon, {})
|
|
5630
|
+
}
|
|
5631
|
+
) })
|
|
5632
|
+
] })
|
|
5633
|
+
}
|
|
5634
|
+
);
|
|
5635
|
+
});
|
|
5636
|
+
Snackbar.displayName = "Snackbar";
|
|
5637
|
+
var SnackbarContext = createContext(null);
|
|
5638
|
+
function useSnackbar() {
|
|
5639
|
+
const ctx = useContext(SnackbarContext);
|
|
5640
|
+
if (!ctx) {
|
|
5641
|
+
throw new Error(
|
|
5642
|
+
"[Snackbar] useSnackbar must be used inside <SnackbarProvider>. Wrap your application (or Storybook decorator) with <SnackbarProvider>."
|
|
5643
|
+
);
|
|
5644
|
+
}
|
|
5645
|
+
return ctx;
|
|
5646
|
+
}
|
|
5647
|
+
function SnackbarProvider({ children, maxVisible = 5 }) {
|
|
5648
|
+
const [queue, setQueue] = useState([]);
|
|
5649
|
+
const counterRef = useRef(0);
|
|
5650
|
+
const baseId = useId();
|
|
5651
|
+
const showSnackbar = useCallback(
|
|
5652
|
+
(options) => {
|
|
5653
|
+
const id = `${baseId}-snackbar-${++counterRef.current}`;
|
|
5654
|
+
setQueue((prev) => [...prev, { ...options, id }]);
|
|
5655
|
+
return id;
|
|
5656
|
+
},
|
|
5657
|
+
[baseId]
|
|
5658
|
+
);
|
|
5659
|
+
const closeSnackbar = useCallback(() => {
|
|
5660
|
+
setQueue((prev) => {
|
|
5661
|
+
if (prev.length === 0) return prev;
|
|
5662
|
+
return prev.slice(1);
|
|
5663
|
+
});
|
|
5664
|
+
}, []);
|
|
5665
|
+
const removeById = useCallback((id) => {
|
|
5666
|
+
setQueue((prev) => prev.filter((item) => item.id !== id));
|
|
5667
|
+
}, []);
|
|
5668
|
+
const contextValue = { showSnackbar, closeSnackbar };
|
|
5669
|
+
const positionGroups = useMemo(() => {
|
|
5670
|
+
const groups = /* @__PURE__ */ new Map();
|
|
5671
|
+
const countByPosition = /* @__PURE__ */ new Map();
|
|
5672
|
+
for (const item of queue) {
|
|
5673
|
+
const pos = item.position ?? "bottom-center";
|
|
5674
|
+
const count = countByPosition.get(pos) ?? 0;
|
|
5675
|
+
if (count < maxVisible) {
|
|
5676
|
+
const existing = groups.get(pos) ?? [];
|
|
5677
|
+
groups.set(pos, [...existing, item]);
|
|
5678
|
+
countByPosition.set(pos, count + 1);
|
|
5679
|
+
}
|
|
5680
|
+
}
|
|
5681
|
+
return groups;
|
|
5682
|
+
}, [queue, maxVisible]);
|
|
5683
|
+
return /* @__PURE__ */ jsxs(SnackbarContext.Provider, { value: contextValue, children: [
|
|
5684
|
+
children,
|
|
5685
|
+
typeof document !== "undefined" && createPortal(
|
|
5686
|
+
/* @__PURE__ */ jsx(Fragment, { children: Array.from(positionGroups.entries()).map(([position, items]) => /* @__PURE__ */ jsx("div", { className: snackbarStackContainerVariants({ position }), children: items.map((item) => /* @__PURE__ */ jsx(
|
|
5687
|
+
Snackbar,
|
|
5688
|
+
{
|
|
5689
|
+
message: item.message,
|
|
5690
|
+
...item.supportingText !== void 0 && {
|
|
5691
|
+
supportingText: item.supportingText
|
|
5692
|
+
},
|
|
5693
|
+
...item.action !== void 0 && { action: item.action },
|
|
5694
|
+
...item.showClose !== void 0 && { showClose: item.showClose },
|
|
5695
|
+
...item.duration !== void 0 && { duration: item.duration },
|
|
5696
|
+
...item.severity !== void 0 && { severity: item.severity },
|
|
5697
|
+
...item.position !== void 0 && { position: item.position },
|
|
5698
|
+
...item.className !== void 0 && { className: item.className },
|
|
5699
|
+
onClose: () => {
|
|
5700
|
+
item.onClose?.();
|
|
5701
|
+
removeById(item.id);
|
|
5702
|
+
}
|
|
5703
|
+
},
|
|
5704
|
+
item.id
|
|
5705
|
+
)) }, position)) }),
|
|
5706
|
+
document.body
|
|
5707
|
+
)
|
|
5708
|
+
] });
|
|
5709
|
+
}
|
|
5710
|
+
var DialogContext = createContext(null);
|
|
5711
|
+
function useDialogContext() {
|
|
5712
|
+
const ctx = useContext(DialogContext);
|
|
5713
|
+
if (ctx === null) {
|
|
5714
|
+
throw new Error(
|
|
5715
|
+
"[Dialog] DialogHeadline, DialogContent, and DialogActions must be rendered inside a <Dialog> or <DialogHeadless> component."
|
|
5716
|
+
);
|
|
5717
|
+
}
|
|
5718
|
+
return ctx;
|
|
5719
|
+
}
|
|
5720
|
+
var DialogPanel = ({
|
|
5721
|
+
ariaLabel,
|
|
5722
|
+
headlineId,
|
|
5723
|
+
contentId,
|
|
5724
|
+
onClose,
|
|
5725
|
+
onTransitionEnd,
|
|
5726
|
+
variant,
|
|
5727
|
+
isDismissable,
|
|
5728
|
+
wrapperClassName,
|
|
5729
|
+
className,
|
|
5730
|
+
animationState,
|
|
5731
|
+
getAnimationClassName,
|
|
5732
|
+
children
|
|
5733
|
+
}) => {
|
|
5734
|
+
const panelRef = useRef(null);
|
|
5735
|
+
usePreventScroll();
|
|
5736
|
+
const { dialogProps } = useDialog(
|
|
5737
|
+
{
|
|
5738
|
+
...ariaLabel ? { "aria-label": ariaLabel } : {},
|
|
5739
|
+
"aria-labelledby": headlineId,
|
|
5740
|
+
"aria-describedby": contentId
|
|
5741
|
+
},
|
|
5742
|
+
panelRef
|
|
5743
|
+
);
|
|
5744
|
+
const { overlayProps } = useOverlay(
|
|
5745
|
+
{
|
|
5746
|
+
isOpen: true,
|
|
5747
|
+
onClose,
|
|
5748
|
+
isDismissable,
|
|
5749
|
+
shouldCloseOnBlur: false
|
|
5750
|
+
},
|
|
5751
|
+
panelRef
|
|
5752
|
+
);
|
|
5753
|
+
const panelClassName = cn(className, getAnimationClassName?.(animationState));
|
|
5754
|
+
return (
|
|
5755
|
+
// Centering/positioning wrapper — structural only, no ARIA role
|
|
5756
|
+
/* @__PURE__ */ jsx("div", { className: wrapperClassName, children: /* @__PURE__ */ jsx(
|
|
5757
|
+
"div",
|
|
5758
|
+
{
|
|
5759
|
+
...mergeProps(overlayProps, dialogProps),
|
|
5760
|
+
ref: panelRef,
|
|
5761
|
+
"aria-modal": "true",
|
|
5762
|
+
className: panelClassName,
|
|
5763
|
+
"data-animation-state": animationState,
|
|
5764
|
+
"data-variant": variant,
|
|
5765
|
+
onTransitionEnd,
|
|
5766
|
+
children
|
|
5767
|
+
}
|
|
5768
|
+
) })
|
|
5769
|
+
);
|
|
5770
|
+
};
|
|
5771
|
+
DialogPanel.displayName = "DialogPanel";
|
|
5772
|
+
var DialogHeadless = forwardRef(
|
|
5773
|
+
function DialogHeadless2({
|
|
5774
|
+
variant = "basic",
|
|
5775
|
+
open,
|
|
5776
|
+
defaultOpen = false,
|
|
5777
|
+
onOpenChange,
|
|
5778
|
+
"aria-label": ariaLabel,
|
|
5779
|
+
children,
|
|
5780
|
+
className,
|
|
5781
|
+
scrimClassName,
|
|
5782
|
+
getAnimationClassName
|
|
5783
|
+
}, _ref) {
|
|
5784
|
+
const state = useOverlayTriggerState({
|
|
5785
|
+
...open !== void 0 ? { isOpen: open } : {},
|
|
5786
|
+
...defaultOpen !== void 0 ? { defaultOpen } : {},
|
|
5787
|
+
...onOpenChange !== void 0 ? { onOpenChange } : {}
|
|
5788
|
+
});
|
|
5789
|
+
const isOpen = state.isOpen;
|
|
5790
|
+
const close = useCallback(() => {
|
|
5791
|
+
state.close();
|
|
5792
|
+
}, [state]);
|
|
5793
|
+
const [animationState, setAnimationState] = useState("exited");
|
|
5794
|
+
const closedRef = useRef(false);
|
|
5795
|
+
const exitFallbackRef = useRef(null);
|
|
5796
|
+
useEffect(() => {
|
|
5797
|
+
if (!isOpen) return;
|
|
5798
|
+
closedRef.current = false;
|
|
5799
|
+
setAnimationState("entering");
|
|
5800
|
+
const id = setTimeout(() => {
|
|
5801
|
+
setAnimationState("visible");
|
|
5802
|
+
}, 0);
|
|
5803
|
+
return () => clearTimeout(id);
|
|
5804
|
+
}, [isOpen]);
|
|
5805
|
+
useEffect(() => {
|
|
5806
|
+
if (isOpen) return;
|
|
5807
|
+
if (animationState === "exited" || animationState === "entering") return;
|
|
5808
|
+
if (animationState === "visible") {
|
|
5809
|
+
setAnimationState("exiting");
|
|
5810
|
+
exitFallbackRef.current = setTimeout(() => {
|
|
5811
|
+
if (!closedRef.current) {
|
|
5812
|
+
closedRef.current = true;
|
|
5813
|
+
setAnimationState("exited");
|
|
5814
|
+
}
|
|
5815
|
+
}, 150);
|
|
5816
|
+
}
|
|
5817
|
+
}, [isOpen, animationState]);
|
|
5818
|
+
useEffect(
|
|
5819
|
+
() => () => {
|
|
5820
|
+
if (exitFallbackRef.current !== null) {
|
|
5821
|
+
clearTimeout(exitFallbackRef.current);
|
|
5822
|
+
}
|
|
5823
|
+
},
|
|
5824
|
+
[]
|
|
5825
|
+
);
|
|
5826
|
+
const handleTransitionEnd = useCallback(() => {
|
|
5827
|
+
if (animationState === "exiting" && !closedRef.current) {
|
|
5828
|
+
if (exitFallbackRef.current !== null) {
|
|
5829
|
+
clearTimeout(exitFallbackRef.current);
|
|
5830
|
+
exitFallbackRef.current = null;
|
|
5831
|
+
}
|
|
5832
|
+
closedRef.current = true;
|
|
5833
|
+
setAnimationState("exited");
|
|
5834
|
+
}
|
|
5835
|
+
}, [animationState]);
|
|
5836
|
+
const baseId = useId();
|
|
5837
|
+
const headlineId = `${baseId}-dialog-headline`;
|
|
5838
|
+
const contentId = `${baseId}-dialog-content`;
|
|
5839
|
+
const contextValue = {
|
|
5840
|
+
headlineId,
|
|
5841
|
+
contentId,
|
|
5842
|
+
close,
|
|
5843
|
+
variant
|
|
5844
|
+
};
|
|
5845
|
+
const handleScrimClick = useCallback(() => {
|
|
5846
|
+
if (variant === "basic") {
|
|
5847
|
+
close();
|
|
5848
|
+
}
|
|
5849
|
+
}, [variant, close]);
|
|
5850
|
+
if (!isOpen && animationState === "exited") {
|
|
5851
|
+
return null;
|
|
5852
|
+
}
|
|
5853
|
+
const content = /* @__PURE__ */ jsxs(DialogContext.Provider, { value: contextValue, children: [
|
|
5854
|
+
/* @__PURE__ */ jsx(
|
|
5855
|
+
"div",
|
|
5856
|
+
{
|
|
5857
|
+
"data-testid": "dialog-scrim",
|
|
5858
|
+
className: scrimClassName,
|
|
5859
|
+
onClick: handleScrimClick,
|
|
5860
|
+
"aria-hidden": "true"
|
|
5861
|
+
}
|
|
5862
|
+
),
|
|
5863
|
+
/* @__PURE__ */ jsx(FocusScope, { contain: true, restoreFocus: true, autoFocus: true, children: /* @__PURE__ */ jsx(
|
|
5864
|
+
DialogPanel,
|
|
5865
|
+
{
|
|
5866
|
+
ariaLabel,
|
|
5867
|
+
headlineId,
|
|
5868
|
+
contentId,
|
|
5869
|
+
onClose: close,
|
|
5870
|
+
onTransitionEnd: handleTransitionEnd,
|
|
5871
|
+
variant,
|
|
5872
|
+
isDismissable: variant === "basic",
|
|
5873
|
+
wrapperClassName: variant === "basic" ? "fixed inset-0 z-50 flex items-center justify-center px-4" : "fixed inset-0 z-50",
|
|
5874
|
+
className,
|
|
5875
|
+
animationState,
|
|
5876
|
+
getAnimationClassName,
|
|
5877
|
+
children
|
|
5878
|
+
}
|
|
5879
|
+
) })
|
|
5880
|
+
] });
|
|
5881
|
+
if (typeof document === "undefined") return null;
|
|
5882
|
+
return createPortal(content, document.body);
|
|
5883
|
+
}
|
|
5884
|
+
);
|
|
5885
|
+
DialogHeadless.displayName = "DialogHeadless";
|
|
5886
|
+
var dialogScrimVariants = cva([
|
|
5887
|
+
"fixed",
|
|
5888
|
+
"inset-0",
|
|
5889
|
+
"z-40",
|
|
5890
|
+
"bg-scrim",
|
|
5891
|
+
"opacity-32",
|
|
5892
|
+
"transition-opacity",
|
|
5893
|
+
"duration-medium2",
|
|
5894
|
+
"ease-standard"
|
|
5895
|
+
]);
|
|
5896
|
+
var dialogPanelVariants = cva(
|
|
5897
|
+
[
|
|
5898
|
+
// Stacking above scrim
|
|
5899
|
+
"z-50",
|
|
5900
|
+
// Surface
|
|
5901
|
+
"bg-surface-container-high",
|
|
5902
|
+
// Flex column layout for slots
|
|
5903
|
+
"flex",
|
|
5904
|
+
"flex-col",
|
|
5905
|
+
// Transition for animation state changes
|
|
5906
|
+
"transition-[opacity,transform]",
|
|
5907
|
+
"will-change-[opacity,transform]"
|
|
5908
|
+
],
|
|
5909
|
+
{
|
|
5910
|
+
variants: {
|
|
5911
|
+
variant: {
|
|
5912
|
+
basic: [
|
|
5913
|
+
// Shape: MD3 extra-large = 28dp
|
|
5914
|
+
"rounded-xl",
|
|
5915
|
+
// Elevation level 3
|
|
5916
|
+
"shadow-elevation-3",
|
|
5917
|
+
// Width constraints per MD3 spec (280dp min, 560dp max)
|
|
5918
|
+
"min-w-70",
|
|
5919
|
+
"max-w-dialog-max",
|
|
5920
|
+
"w-full",
|
|
5921
|
+
// Internal spacing
|
|
5922
|
+
"pt-6",
|
|
5923
|
+
"pb-3",
|
|
5924
|
+
"px-6",
|
|
5925
|
+
// Positioned in viewport center
|
|
5926
|
+
"relative"
|
|
5927
|
+
],
|
|
5928
|
+
fullscreen: [
|
|
5929
|
+
// Full viewport
|
|
5930
|
+
"w-full",
|
|
5931
|
+
"h-full",
|
|
5932
|
+
// No rounded corners on fullscreen
|
|
5933
|
+
"rounded-none",
|
|
5934
|
+
// No elevation shadow on fullscreen
|
|
5935
|
+
"shadow-none",
|
|
5936
|
+
// Positioned to fill portal
|
|
5937
|
+
"relative"
|
|
5938
|
+
]
|
|
5939
|
+
}
|
|
5940
|
+
},
|
|
5941
|
+
defaultVariants: {
|
|
5942
|
+
variant: "basic"
|
|
5943
|
+
}
|
|
5944
|
+
}
|
|
5945
|
+
);
|
|
5946
|
+
cva([], {
|
|
5947
|
+
variants: {
|
|
5948
|
+
variant: {
|
|
5949
|
+
basic: ["fixed", "inset-0", "z-50", "flex", "items-center", "justify-center", "px-4"],
|
|
5950
|
+
fullscreen: ["fixed", "inset-0", "z-50"]
|
|
5951
|
+
}
|
|
5952
|
+
},
|
|
5953
|
+
defaultVariants: {
|
|
5954
|
+
variant: "basic"
|
|
5955
|
+
}
|
|
5956
|
+
});
|
|
5957
|
+
var dialogAnimationVariants = cva("", {
|
|
5958
|
+
variants: {
|
|
5959
|
+
animationState: {
|
|
5960
|
+
entering: [],
|
|
5961
|
+
visible: [],
|
|
5962
|
+
exiting: [],
|
|
5963
|
+
exited: []
|
|
5964
|
+
},
|
|
5965
|
+
variant: {
|
|
5966
|
+
basic: [],
|
|
5967
|
+
fullscreen: []
|
|
5968
|
+
}
|
|
5969
|
+
},
|
|
5970
|
+
compoundVariants: [
|
|
5971
|
+
// Basic: entering — start scaled down + transparent
|
|
5972
|
+
{
|
|
5973
|
+
animationState: "entering",
|
|
5974
|
+
variant: "basic",
|
|
5975
|
+
className: ["scale-90", "opacity-0"]
|
|
5976
|
+
},
|
|
5977
|
+
// Basic: visible — scale to full + fade in
|
|
5978
|
+
{
|
|
5979
|
+
animationState: "visible",
|
|
5980
|
+
variant: "basic",
|
|
5981
|
+
className: ["scale-100", "opacity-100", "duration-medium4", "ease-emphasized-decelerate"]
|
|
5982
|
+
},
|
|
5983
|
+
// Basic: exiting — fade out (scale stays at 1)
|
|
5984
|
+
{
|
|
5985
|
+
animationState: "exiting",
|
|
5986
|
+
variant: "basic",
|
|
5987
|
+
className: ["scale-100", "opacity-0", "duration-short2", "ease-emphasized-accelerate"]
|
|
5988
|
+
},
|
|
5989
|
+
// Basic: exited — fully transparent
|
|
5990
|
+
{
|
|
5991
|
+
animationState: "exited",
|
|
5992
|
+
variant: "basic",
|
|
5993
|
+
className: ["scale-100", "opacity-0"]
|
|
5994
|
+
},
|
|
5995
|
+
// Fullscreen: entering — start below viewport + transparent
|
|
5996
|
+
{
|
|
5997
|
+
animationState: "entering",
|
|
5998
|
+
variant: "fullscreen",
|
|
5999
|
+
className: ["translate-y-full", "opacity-0"]
|
|
6000
|
+
},
|
|
6001
|
+
// Fullscreen: visible — slide up + fade in
|
|
6002
|
+
{
|
|
6003
|
+
animationState: "visible",
|
|
6004
|
+
variant: "fullscreen",
|
|
6005
|
+
className: ["translate-y-0", "opacity-100", "duration-medium4", "ease-emphasized-decelerate"]
|
|
6006
|
+
},
|
|
6007
|
+
// Fullscreen: exiting — slide down + fade out
|
|
6008
|
+
{
|
|
6009
|
+
animationState: "exiting",
|
|
6010
|
+
variant: "fullscreen",
|
|
6011
|
+
className: ["translate-y-full", "opacity-0", "duration-short2", "ease-emphasized-accelerate"]
|
|
6012
|
+
},
|
|
6013
|
+
// Fullscreen: exited — fully off-screen
|
|
6014
|
+
{
|
|
6015
|
+
animationState: "exited",
|
|
6016
|
+
variant: "fullscreen",
|
|
6017
|
+
className: ["translate-y-full", "opacity-0"]
|
|
6018
|
+
}
|
|
6019
|
+
],
|
|
6020
|
+
defaultVariants: {
|
|
6021
|
+
animationState: "entering",
|
|
6022
|
+
variant: "basic"
|
|
6023
|
+
}
|
|
6024
|
+
});
|
|
6025
|
+
var dialogHeadlineVariants = cva(["text-headline-small", "text-on-surface"], {
|
|
6026
|
+
variants: {
|
|
6027
|
+
variant: {
|
|
6028
|
+
basic: ["mb-4"],
|
|
6029
|
+
fullscreen: [
|
|
6030
|
+
// Top app bar row in fullscreen: flex, items-center, gap
|
|
6031
|
+
"flex",
|
|
6032
|
+
"items-center",
|
|
6033
|
+
"gap-4",
|
|
6034
|
+
"px-4",
|
|
6035
|
+
"h-14",
|
|
6036
|
+
"shrink-0",
|
|
6037
|
+
"border-b",
|
|
6038
|
+
"border-outline-variant"
|
|
6039
|
+
]
|
|
6040
|
+
}
|
|
6041
|
+
},
|
|
6042
|
+
defaultVariants: {
|
|
6043
|
+
variant: "basic"
|
|
6044
|
+
}
|
|
6045
|
+
});
|
|
6046
|
+
var dialogHeadlineTitleVariants = cva([
|
|
6047
|
+
"flex-1",
|
|
6048
|
+
"text-headline-small",
|
|
6049
|
+
"text-on-surface",
|
|
6050
|
+
"truncate"
|
|
6051
|
+
]);
|
|
6052
|
+
var dialogContentVariants = cva(
|
|
6053
|
+
["text-body-medium", "text-on-surface-variant", "overflow-y-auto", "flex-1"],
|
|
6054
|
+
{
|
|
6055
|
+
variants: {
|
|
6056
|
+
variant: {
|
|
6057
|
+
basic: ["mb-6"],
|
|
6058
|
+
fullscreen: ["px-6", "py-4"]
|
|
6059
|
+
}
|
|
6060
|
+
},
|
|
6061
|
+
defaultVariants: {
|
|
6062
|
+
variant: "basic"
|
|
6063
|
+
}
|
|
6064
|
+
}
|
|
6065
|
+
);
|
|
6066
|
+
var dialogActionsVariants = cva([
|
|
6067
|
+
"flex",
|
|
6068
|
+
"items-center",
|
|
6069
|
+
"justify-end",
|
|
6070
|
+
"gap-2",
|
|
6071
|
+
"pt-3",
|
|
6072
|
+
"shrink-0"
|
|
6073
|
+
]);
|
|
6074
|
+
var Dialog = forwardRef(function Dialog2({
|
|
6075
|
+
variant = "basic",
|
|
6076
|
+
open,
|
|
6077
|
+
defaultOpen = false,
|
|
6078
|
+
onOpenChange,
|
|
6079
|
+
"aria-label": ariaLabel,
|
|
6080
|
+
children,
|
|
6081
|
+
className
|
|
6082
|
+
}, _ref) {
|
|
6083
|
+
const panelClassName = cn(dialogPanelVariants({ variant }), className);
|
|
6084
|
+
const scrimClass = dialogScrimVariants();
|
|
6085
|
+
return /* @__PURE__ */ jsx(
|
|
6086
|
+
DialogHeadless,
|
|
6087
|
+
{
|
|
6088
|
+
variant,
|
|
6089
|
+
...open !== void 0 ? { open } : {},
|
|
6090
|
+
...defaultOpen !== void 0 ? { defaultOpen } : {},
|
|
6091
|
+
...onOpenChange !== void 0 ? { onOpenChange } : {},
|
|
6092
|
+
...ariaLabel ? { "aria-label": ariaLabel } : {},
|
|
6093
|
+
className: panelClassName,
|
|
6094
|
+
scrimClassName: scrimClass,
|
|
6095
|
+
getAnimationClassName: (state) => dialogAnimationVariants({ animationState: state, variant }),
|
|
6096
|
+
children
|
|
6097
|
+
}
|
|
6098
|
+
);
|
|
6099
|
+
});
|
|
6100
|
+
Dialog.displayName = "Dialog";
|
|
6101
|
+
var DialogHeadline = forwardRef(
|
|
6102
|
+
function DialogHeadline2({ children, className, closeButton, confirmButton }, ref) {
|
|
6103
|
+
const { headlineId, variant } = useDialogContext();
|
|
6104
|
+
if (variant === "fullscreen") {
|
|
6105
|
+
return (
|
|
6106
|
+
// Top app bar row for fullscreen variant
|
|
6107
|
+
/* @__PURE__ */ jsxs("div", { className: cn(dialogHeadlineVariants({ variant: "fullscreen" }), className), children: [
|
|
6108
|
+
closeButton,
|
|
6109
|
+
/* @__PURE__ */ jsx("h2", { id: headlineId, className: dialogHeadlineTitleVariants(), children }),
|
|
6110
|
+
confirmButton
|
|
6111
|
+
] })
|
|
6112
|
+
);
|
|
6113
|
+
}
|
|
6114
|
+
return /* @__PURE__ */ jsx(
|
|
6115
|
+
"h2",
|
|
6116
|
+
{
|
|
6117
|
+
ref,
|
|
6118
|
+
id: headlineId,
|
|
6119
|
+
className: cn(dialogHeadlineVariants({ variant: "basic" }), className),
|
|
6120
|
+
children
|
|
6121
|
+
}
|
|
6122
|
+
);
|
|
6123
|
+
}
|
|
6124
|
+
);
|
|
6125
|
+
DialogHeadline.displayName = "DialogHeadline";
|
|
6126
|
+
var DialogContent = forwardRef(function DialogContent2({ children, className }, ref) {
|
|
6127
|
+
const { contentId, variant } = useDialogContext();
|
|
6128
|
+
return /* @__PURE__ */ jsx("div", { ref, id: contentId, className: cn(dialogContentVariants({ variant }), className), children });
|
|
6129
|
+
});
|
|
6130
|
+
DialogContent.displayName = "DialogContent";
|
|
6131
|
+
var DialogActions = forwardRef(function DialogActions2({ children, className }, ref) {
|
|
6132
|
+
useDialogContext();
|
|
6133
|
+
return /* @__PURE__ */ jsx("div", { ref, className: cn(dialogActionsVariants(), className), children });
|
|
6134
|
+
});
|
|
6135
|
+
DialogActions.displayName = "DialogActions";
|
|
2987
6136
|
|
|
2988
|
-
export { Button, Checkbox, FAB, FABHeadless, IconButton, IconButtonHeadless, Radio, RadioGroup, RadioGroupHeadless, RadioHeadless, STATE_LAYER_OPACITY, Switch, TYPOGRAPHY_ELEMENT_MAP, TYPOGRAPHY_USAGE, TextField, applyStateLayer, cn, generateMD3Theme, getColorValue, getFontFamily, getMD3Color, getResponsiveTypography, getTypographyClassName, getTypographyForElement, getTypographyStyle, getTypographyToken, hexToRgb, pxToRem, remToPx, rgbToHex, truncateText, withOpacity };
|
|
6137
|
+
export { AppBar, AppBarHeadless, Button, Checkbox, Dialog, DialogActions, DialogContent, DialogContext, DialogHeadless, DialogHeadline, Drawer, DrawerItem, DrawerSection, FAB, FABHeadless, HeadlessDrawer, HeadlessDrawerItem, HeadlessMenu, HeadlessMenuDivider, HeadlessMenuItem, HeadlessMenuSection, HeadlessMenuTrigger, HeadlessNavigationBar, HeadlessNavigationBarItem, HeadlessTab, HeadlessTabList, HeadlessTabPanel, IconButton, IconButtonHeadless, Menu, MenuContext, MenuDivider, MenuItem, MenuSection, MenuTrigger, NavigationBar, NavigationBarItem, Progress, ProgressHeadless, Radio, RadioGroup, RadioGroupHeadless, RadioHeadless, STATE_LAYER_OPACITY, Snackbar, SnackbarContext, SnackbarHeadless, SnackbarProvider, Switch, TYPOGRAPHY_ELEMENT_MAP, TYPOGRAPHY_USAGE, Tab, TabList, TabPanel, Tabs, TextField, applyStateLayer, cn, generateMD3Theme, getColorValue, getFontFamily, getMD3Color, getResponsiveTypography, getTypographyClassName, getTypographyForElement, getTypographyStyle, getTypographyToken, hexToRgb, pxToRem, remToPx, rgbToHex, truncateText, useDialogContext, useMenuContext, useSnackbar, withOpacity };
|
|
2989
6138
|
//# sourceMappingURL=index.js.map
|
|
2990
6139
|
//# sourceMappingURL=index.js.map
|