@bug-on/md3-react 2.0.3 → 3.0.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/.turbo/turbo-build.log +33 -0
- package/CHANGELOG.md +55 -0
- package/dist/index.css.d.ts +2 -0
- package/dist/index.d.mts +6127 -0
- package/dist/index.d.ts +6127 -71
- package/dist/index.js +1653 -614
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1566 -547
- package/dist/index.mjs.map +1 -1
- package/dist/material-symbols-cdn.css.d.ts +2 -0
- package/dist/material-symbols-self-hosted.css.d.ts +2 -0
- package/dist/typography.css.d.ts +2 -0
- package/package.json +22 -19
- package/scripts/copy-assets.js +82 -0
- package/src/assets/fonts/GoogleSansFlex-VariableFont.woff2 +0 -0
- package/src/assets/fonts/MaterialSymbolsOutlined-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
- package/src/assets/fonts/MaterialSymbolsRounded-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
- package/src/assets/fonts/MaterialSymbolsSharp-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
- package/src/assets/loading-indicator.svg +19 -0
- package/src/assets/material-symbols-cdn.css +65 -0
- package/src/assets/material-symbols-self-hosted.css +90 -0
- package/src/css.d.ts +20 -0
- package/src/hooks/useClickOutside.ts +37 -0
- package/src/hooks/useMediaQuery.ts +28 -0
- package/src/hooks/useRipple.ts +88 -0
- package/src/index.css +23 -0
- package/src/index.ts +349 -0
- package/src/lib/material-symbols-preconnect.tsx +82 -0
- package/src/lib/theme-utils.ts +180 -0
- package/src/lib/utils.ts +6 -0
- package/src/test/button.test.tsx +59 -0
- package/src/test/icon.test.tsx +91 -0
- package/src/test/loading-indicator.test.tsx +128 -0
- package/src/test/progress-indicator.test.tsx +306 -0
- package/src/test/setup.ts +80 -0
- package/src/test/typography.test.tsx +206 -0
- package/src/types/index.ts +7 -0
- package/src/types/md3.ts +31 -0
- package/src/ui/Text.tsx +60 -0
- package/src/ui/__snapshots__/divider.test.tsx.snap +63 -0
- package/src/ui/app-bar/app-bar-column.tsx +99 -0
- package/src/ui/app-bar/app-bar-item-button.tsx +71 -0
- package/src/ui/app-bar/app-bar-items.test.tsx +89 -0
- package/src/ui/app-bar/app-bar-overflow-indicator.tsx +108 -0
- package/src/ui/app-bar/app-bar-row.tsx +104 -0
- package/src/ui/app-bar/app-bar.test.tsx +87 -0
- package/src/ui/app-bar/app-bar.tokens.ts +223 -0
- package/src/ui/app-bar/app-bar.types.ts +441 -0
- package/src/ui/app-bar/bottom-app-bar.test.tsx +42 -0
- package/src/ui/app-bar/bottom-app-bar.tsx +84 -0
- package/src/ui/app-bar/docked-toolbar.test.tsx +34 -0
- package/src/ui/app-bar/docked-toolbar.tsx +54 -0
- package/src/ui/app-bar/flexible-app-bar.test.tsx +75 -0
- package/src/ui/app-bar/hooks/use-app-bar-scroll.ts +110 -0
- package/src/ui/app-bar/hooks/use-flexible-app-bar.ts +123 -0
- package/{dist/ui/app-bar/index.d.ts → src/ui/app-bar/index.ts} +35 -2
- package/src/ui/app-bar/large-flexible-app-bar.tsx +165 -0
- package/src/ui/app-bar/medium-flexible-app-bar.tsx +167 -0
- package/src/ui/app-bar/search-app-bar.test.tsx +49 -0
- package/src/ui/app-bar/search-app-bar.tsx +176 -0
- package/src/ui/app-bar/search-view.tsx +227 -0
- package/src/ui/app-bar/small-app-bar.test.tsx +48 -0
- package/src/ui/app-bar/small-app-bar.tsx +203 -0
- package/src/ui/badge.test.tsx +345 -0
- package/src/ui/badge.tsx +282 -0
- package/src/ui/button-group.test.tsx +71 -0
- package/src/ui/button-group.tsx +350 -0
- package/src/ui/button.test.tsx +297 -0
- package/src/ui/button.tsx +669 -0
- package/src/ui/card.test.tsx +187 -0
- package/src/ui/card.tsx +259 -0
- package/src/ui/checkbox.test.tsx +423 -0
- package/src/ui/checkbox.tsx +525 -0
- package/src/ui/chip.test.tsx +292 -0
- package/src/ui/chip.tsx +548 -0
- package/src/ui/code-block.tsx +219 -0
- package/src/ui/dialog.test.tsx +300 -0
- package/src/ui/dialog.tsx +384 -0
- package/src/ui/divider.test.tsx +314 -0
- package/src/ui/divider.tsx +412 -0
- package/src/ui/drawer.tsx +240 -0
- package/src/ui/fab-menu.test.tsx +494 -0
- package/src/ui/fab-menu.tsx +739 -0
- package/src/ui/fab.test.tsx +232 -0
- package/src/ui/fab.tsx +505 -0
- package/src/ui/icon-button.test.tsx +515 -0
- package/src/ui/icon-button.tsx +525 -0
- package/src/ui/icon.test.tsx +197 -0
- package/src/ui/icon.tsx +179 -0
- package/src/ui/loading-indicator.test.tsx +73 -0
- package/src/ui/loading-indicator.tsx +312 -0
- package/src/ui/menu/context-menu.tsx +275 -0
- package/src/ui/menu/index.ts +77 -0
- package/src/ui/menu/menu-animations.ts +102 -0
- package/src/ui/menu/menu-context.tsx +99 -0
- package/src/ui/menu/menu-divider.tsx +47 -0
- package/src/ui/menu/menu-group.tsx +200 -0
- package/src/ui/menu/menu-item.tsx +294 -0
- package/src/ui/menu/menu-tokens.ts +208 -0
- package/src/ui/menu/menu-types.ts +313 -0
- package/src/ui/menu/menu.test.tsx +624 -0
- package/src/ui/menu/menu.tsx +289 -0
- package/src/ui/menu/sub-menu.tsx +223 -0
- package/src/ui/menu/vertical-menu.tsx +382 -0
- package/src/ui/navigation-rail.test.tsx +404 -0
- package/src/ui/navigation-rail.tsx +604 -0
- package/src/ui/progress-indicator/circular.tsx +248 -0
- package/src/ui/progress-indicator/hooks.ts +51 -0
- package/{dist/ui/progress-indicator/index.d.ts → src/ui/progress-indicator/index.tsx} +20 -2
- package/src/ui/progress-indicator/linear-flat.tsx +83 -0
- package/src/ui/progress-indicator/linear-wavy.tsx +243 -0
- package/src/ui/progress-indicator/linear.tsx +143 -0
- package/src/ui/progress-indicator/types.ts +158 -0
- package/src/ui/progress-indicator/utils.ts +73 -0
- package/src/ui/radio-button.test.tsx +407 -0
- package/src/ui/radio-button.tsx +551 -0
- package/src/ui/ripple.test.tsx +72 -0
- package/src/ui/ripple.tsx +234 -0
- package/src/ui/scroll-area.test.tsx +58 -0
- package/src/ui/scroll-area.tsx +139 -0
- package/src/ui/search/animated-placeholder.tsx +145 -0
- package/src/ui/search/hooks/use-search-keyboard.test.ts +202 -0
- package/src/ui/search/hooks/use-search-keyboard.ts +104 -0
- package/src/ui/search/hooks/use-search-view-focus.test.ts +96 -0
- package/src/ui/search/hooks/use-search-view-focus.ts +24 -0
- package/src/ui/search/index.ts +44 -0
- package/src/ui/search/search-bar.tsx +220 -0
- package/src/ui/search/search-context.tsx +42 -0
- package/src/ui/search/search-view-docked.tsx +194 -0
- package/src/ui/search/search-view-fullscreen.tsx +247 -0
- package/src/ui/search/search.test.tsx +233 -0
- package/src/ui/search/search.tokens.ts +134 -0
- package/src/ui/search/search.tsx +131 -0
- package/src/ui/search/search.types.ts +154 -0
- package/src/ui/search/trailing-action.tsx +49 -0
- package/src/ui/shared/constants.ts +122 -0
- package/{dist/ui/shared/touch-target.d.ts → src/ui/shared/touch-target.tsx} +13 -1
- package/src/ui/slider/hooks/useSliderMath.ts +195 -0
- package/{dist/ui/slider/index.d.ts → src/ui/slider/index.ts} +12 -1
- package/src/ui/slider/range-slider.tsx +561 -0
- package/src/ui/slider/slider-thumb.tsx +379 -0
- package/src/ui/slider/slider-track.tsx +912 -0
- package/src/ui/slider/slider.tokens.ts +189 -0
- package/src/ui/slider/slider.tsx +259 -0
- package/src/ui/slider/slider.types.ts +288 -0
- package/src/ui/snackbar/index.ts +20 -0
- package/src/ui/snackbar/snackbar.test.tsx +338 -0
- package/src/ui/snackbar/snackbar.tsx +476 -0
- package/{dist/ui/switch/index.d.ts → src/ui/switch/index.ts} +1 -0
- package/src/ui/switch/switch.stories.tsx +309 -0
- package/src/ui/switch/switch.test.tsx +243 -0
- package/src/ui/switch/switch.tokens.ts +89 -0
- package/src/ui/switch/switch.tsx +504 -0
- package/src/ui/switch/switch.types.ts +62 -0
- package/{dist/ui/tabs/index.d.ts → src/ui/tabs/index.ts} +8 -1
- package/src/ui/tabs/tab.tsx +407 -0
- package/src/ui/tabs/tabs-content.tsx +89 -0
- package/src/ui/tabs/tabs-list.tsx +146 -0
- package/src/ui/tabs/tabs.test.tsx +290 -0
- package/src/ui/tabs/tabs.tokens.ts +121 -0
- package/src/ui/tabs/tabs.tsx +229 -0
- package/src/ui/tabs/tabs.types.ts +185 -0
- package/{dist/ui/text-field/index.d.ts → src/ui/text-field/index.ts} +8 -1
- package/src/ui/text-field/subcomponents/active-indicator.tsx +67 -0
- package/src/ui/text-field/subcomponents/floating-label.tsx +161 -0
- package/src/ui/text-field/subcomponents/leading-icon.tsx +46 -0
- package/src/ui/text-field/subcomponents/outline-container.tsx +170 -0
- package/src/ui/text-field/subcomponents/prefix-suffix.tsx +59 -0
- package/src/ui/text-field/subcomponents/supporting-text.tsx +145 -0
- package/src/ui/text-field/subcomponents/trailing-icon.tsx +199 -0
- package/src/ui/text-field/text-field.test.tsx +454 -0
- package/src/ui/text-field/text-field.tokens.ts +104 -0
- package/src/ui/text-field/text-field.tsx +548 -0
- package/src/ui/text-field/text-field.types.ts +180 -0
- package/src/ui/theme-provider/index.tsx +190 -0
- package/src/ui/toc.test.tsx +108 -0
- package/src/ui/toc.tsx +172 -0
- package/src/ui/tooltip/plain-tooltip.tsx +63 -0
- package/src/ui/tooltip/rich-tooltip.tsx +94 -0
- package/src/ui/tooltip/tooltip-box.tsx +266 -0
- package/src/ui/tooltip/tooltip-caret-shape.tsx +68 -0
- package/src/ui/tooltip/tooltip.tokens.ts +26 -0
- package/src/ui/tooltip/tooltip.types.ts +70 -0
- package/src/ui/tooltip/use-tooltip-position.ts +208 -0
- package/src/ui/tooltip/use-tooltip-state.ts +41 -0
- package/src/ui/typography/__tests__/typography.test.tsx +170 -0
- package/{dist/ui/typography/index.d.ts → src/ui/typography/index.ts} +21 -3
- package/src/ui/typography/type-scale-tokens.ts +205 -0
- package/src/ui/typography/typography-key-tokens.ts +43 -0
- package/src/ui/typography/typography-tokens.ts +360 -0
- package/src/ui/typography/typography.css +22 -0
- package/src/ui/typography/typography.tsx +559 -0
- package/test-render.tsx +4 -0
- package/test-shadow.html +26 -0
- package/test_output.txt +164 -0
- package/test_output_v2.txt +5 -0
- package/tsconfig.build.json +10 -0
- package/tsconfig.json +18 -0
- package/tsup.config.ts +20 -0
- package/vitest.config.ts +11 -0
- package/dist/hooks/useClickOutside.d.ts +0 -8
- package/dist/hooks/useMediaQuery.d.ts +0 -11
- package/dist/hooks/useRipple.d.ts +0 -26
- package/dist/lib/material-symbols-preconnect.d.ts +0 -42
- package/dist/lib/theme-utils.d.ts +0 -63
- package/dist/lib/utils.d.ts +0 -2
- package/dist/types/index.d.ts +0 -1
- package/dist/types/md3.d.ts +0 -14
- package/dist/ui/app-bar/app-bar-column.d.ts +0 -28
- package/dist/ui/app-bar/app-bar-item-button.d.ts +0 -16
- package/dist/ui/app-bar/app-bar-overflow-indicator.d.ts +0 -18
- package/dist/ui/app-bar/app-bar-row.d.ts +0 -36
- package/dist/ui/app-bar/app-bar.tokens.d.ts +0 -184
- package/dist/ui/app-bar/app-bar.types.d.ts +0 -392
- package/dist/ui/app-bar/bottom-app-bar.d.ts +0 -31
- package/dist/ui/app-bar/docked-toolbar.d.ts +0 -25
- package/dist/ui/app-bar/hooks/use-app-bar-scroll.d.ts +0 -42
- package/dist/ui/app-bar/hooks/use-flexible-app-bar.d.ts +0 -37
- package/dist/ui/app-bar/large-flexible-app-bar.d.ts +0 -26
- package/dist/ui/app-bar/medium-flexible-app-bar.d.ts +0 -28
- package/dist/ui/app-bar/search-app-bar.d.ts +0 -43
- package/dist/ui/app-bar/search-view.d.ts +0 -54
- package/dist/ui/app-bar/small-app-bar.d.ts +0 -37
- package/dist/ui/badge.d.ts +0 -125
- package/dist/ui/button-group.d.ts +0 -59
- package/dist/ui/button.d.ts +0 -148
- package/dist/ui/card.d.ts +0 -62
- package/dist/ui/checkbox.d.ts +0 -82
- package/dist/ui/chip.d.ts +0 -110
- package/dist/ui/code-block.d.ts +0 -14
- package/dist/ui/dialog.d.ts +0 -111
- package/dist/ui/divider.d.ts +0 -164
- package/dist/ui/drawer.d.ts +0 -39
- package/dist/ui/dropdown.d.ts +0 -29
- package/dist/ui/fab-menu.d.ts +0 -204
- package/dist/ui/fab.d.ts +0 -162
- package/dist/ui/icon-button.d.ts +0 -131
- package/dist/ui/icon.d.ts +0 -88
- package/dist/ui/loading-indicator.d.ts +0 -42
- package/dist/ui/navigation-rail.d.ts +0 -29
- package/dist/ui/progress-indicator/circular.d.ts +0 -3
- package/dist/ui/progress-indicator/hooks.d.ts +0 -3
- package/dist/ui/progress-indicator/linear-flat.d.ts +0 -10
- package/dist/ui/progress-indicator/linear-wavy.d.ts +0 -18
- package/dist/ui/progress-indicator/linear.d.ts +0 -3
- package/dist/ui/progress-indicator/types.d.ts +0 -151
- package/dist/ui/progress-indicator/utils.d.ts +0 -3
- package/dist/ui/radio-button.d.ts +0 -106
- package/dist/ui/ripple.d.ts +0 -126
- package/dist/ui/scroll-area.d.ts +0 -27
- package/dist/ui/search/animated-placeholder.d.ts +0 -54
- package/dist/ui/search/hooks/use-search-keyboard.d.ts +0 -32
- package/dist/ui/search/hooks/use-search-view-focus.d.ts +0 -6
- package/dist/ui/search/index.d.ts +0 -27
- package/dist/ui/search/search-bar.d.ts +0 -32
- package/dist/ui/search/search-context.d.ts +0 -24
- package/dist/ui/search/search-view-docked.d.ts +0 -25
- package/dist/ui/search/search-view-fullscreen.d.ts +0 -36
- package/dist/ui/search/search.d.ts +0 -50
- package/dist/ui/search/search.tokens.d.ts +0 -112
- package/dist/ui/search/search.types.d.ts +0 -131
- package/dist/ui/search/trailing-action.d.ts +0 -9
- package/dist/ui/shared/constants.d.ts +0 -86
- package/dist/ui/slider/hooks/useSliderMath.d.ts +0 -101
- package/dist/ui/slider/range-slider.d.ts +0 -47
- package/dist/ui/slider/slider-thumb.d.ts +0 -33
- package/dist/ui/slider/slider-track.d.ts +0 -25
- package/dist/ui/slider/slider.d.ts +0 -60
- package/dist/ui/slider/slider.tokens.d.ts +0 -151
- package/dist/ui/slider/slider.types.d.ts +0 -259
- package/dist/ui/snackbar/index.d.ts +0 -6
- package/dist/ui/snackbar/snackbar.d.ts +0 -197
- package/dist/ui/switch/switch.d.ts +0 -30
- package/dist/ui/switch/switch.stories.d.ts +0 -48
- package/dist/ui/switch/switch.tokens.d.ts +0 -67
- package/dist/ui/switch/switch.types.d.ts +0 -59
- package/dist/ui/tabs/tab.d.ts +0 -43
- package/dist/ui/tabs/tabs-content.d.ts +0 -36
- package/dist/ui/tabs/tabs-list.d.ts +0 -40
- package/dist/ui/tabs/tabs.d.ts +0 -60
- package/dist/ui/tabs/tabs.tokens.d.ts +0 -94
- package/dist/ui/tabs/tabs.types.d.ts +0 -172
- package/dist/ui/text-field/subcomponents/active-indicator.d.ts +0 -24
- package/dist/ui/text-field/subcomponents/floating-label.d.ts +0 -43
- package/dist/ui/text-field/subcomponents/leading-icon.d.ts +0 -23
- package/dist/ui/text-field/subcomponents/outline-container.d.ts +0 -42
- package/dist/ui/text-field/subcomponents/prefix-suffix.d.ts +0 -24
- package/dist/ui/text-field/subcomponents/supporting-text.d.ts +0 -37
- package/dist/ui/text-field/subcomponents/trailing-icon.d.ts +0 -41
- package/dist/ui/text-field/text-field.d.ts +0 -49
- package/dist/ui/text-field/text-field.tokens.d.ts +0 -76
- package/dist/ui/text-field/text-field.types.d.ts +0 -126
- package/dist/ui/theme-provider/index.d.ts +0 -48
- package/dist/ui/toc.d.ts +0 -80
- package/dist/ui/tooltip/plain-tooltip.d.ts +0 -2
- package/dist/ui/tooltip/rich-tooltip.d.ts +0 -2
- package/dist/ui/tooltip/tooltip-box.d.ts +0 -2
- package/dist/ui/tooltip/tooltip-caret-shape.d.ts +0 -9
- package/dist/ui/tooltip/tooltip.tokens.d.ts +0 -26
- package/dist/ui/tooltip/tooltip.types.d.ts +0 -56
- package/dist/ui/tooltip/use-tooltip-position.d.ts +0 -8
- package/dist/ui/tooltip/use-tooltip-state.d.ts +0 -2
- package/dist/ui/typography/type-scale-tokens.d.ts +0 -162
- package/dist/ui/typography/typography-key-tokens.d.ts +0 -40
- package/dist/ui/typography/typography-tokens.d.ts +0 -220
- package/dist/ui/typography/typography.d.ts +0 -265
- /package/{dist/hooks/index.d.ts → src/hooks/index.ts} +0 -0
- /package/{dist/ui/tooltip/index.d.ts → src/ui/tooltip/index.ts} +0 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file app-bar-overflow-indicator.tsx
|
|
3
|
+
* MD3 Expressive App Bar Overflow Indicator.
|
|
4
|
+
*
|
|
5
|
+
* Renders a "More" (more_vert) icon button that opens a Radix UI
|
|
6
|
+
* DropdownMenu containing overflow App Bar items.
|
|
7
|
+
*
|
|
8
|
+
* Used internally by <AppBarRow> and <AppBarColumn> when items exceed
|
|
9
|
+
* the visible count.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { cn } from "../../lib/utils";
|
|
13
|
+
import { Menu, MenuContent, MenuItem, MenuTrigger } from "../menu";
|
|
14
|
+
import { APP_BAR_COLORS, AppBarTokens } from "./app-bar.tokens";
|
|
15
|
+
import type { AppBarItem, AppBarOverflowIndicatorProps } from "./app-bar.types";
|
|
16
|
+
|
|
17
|
+
/** More vert icon for the overflow trigger button. */
|
|
18
|
+
function MoreVertIcon() {
|
|
19
|
+
return (
|
|
20
|
+
<span
|
|
21
|
+
className="material-symbols-rounded text-[24px] leading-none select-none"
|
|
22
|
+
aria-hidden="true"
|
|
23
|
+
>
|
|
24
|
+
more_vert
|
|
25
|
+
</span>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Renders a single overflow item in the dropdown based on its type.
|
|
31
|
+
*/
|
|
32
|
+
function OverflowItem({ item }: { item: AppBarItem }) {
|
|
33
|
+
if (item.type === "toggleable") {
|
|
34
|
+
return (
|
|
35
|
+
<MenuItem
|
|
36
|
+
role="menuitemcheckbox"
|
|
37
|
+
selected={item.checked ?? false}
|
|
38
|
+
onClick={() => item.onCheckedChange?.(!item.checked)}
|
|
39
|
+
disabled={item.enabled === false}
|
|
40
|
+
>
|
|
41
|
+
{item.label}
|
|
42
|
+
</MenuItem>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (item.type === "custom" && item.menuContent) {
|
|
47
|
+
return (
|
|
48
|
+
<>
|
|
49
|
+
{item.menuContent({
|
|
50
|
+
isOpen: true,
|
|
51
|
+
open: () => {},
|
|
52
|
+
close: () => {},
|
|
53
|
+
})}
|
|
54
|
+
</>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Default: clickable
|
|
59
|
+
return (
|
|
60
|
+
<MenuItem onClick={item.onClick} disabled={item.enabled === false}>
|
|
61
|
+
{item.label}
|
|
62
|
+
</MenuItem>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* MD3 App Bar Overflow Indicator.
|
|
68
|
+
*
|
|
69
|
+
* Renders a "more_vert" button that opens a dropdown menu
|
|
70
|
+
* with overflow action items.
|
|
71
|
+
*/
|
|
72
|
+
export function AppBarOverflowIndicator({
|
|
73
|
+
items,
|
|
74
|
+
className,
|
|
75
|
+
}: AppBarOverflowIndicatorProps) {
|
|
76
|
+
if (items.length === 0) return null;
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<Menu>
|
|
80
|
+
<MenuTrigger asChild>
|
|
81
|
+
<button
|
|
82
|
+
type="button"
|
|
83
|
+
className={cn(
|
|
84
|
+
"flex items-center justify-center rounded-full",
|
|
85
|
+
"focus-visible:outline-none focus-visible:ring-2",
|
|
86
|
+
className,
|
|
87
|
+
)}
|
|
88
|
+
style={{
|
|
89
|
+
width: AppBarTokens.iconButtonTouchTarget,
|
|
90
|
+
height: AppBarTokens.iconButtonTouchTarget,
|
|
91
|
+
color: APP_BAR_COLORS.actionIcon,
|
|
92
|
+
}}
|
|
93
|
+
aria-label="More actions"
|
|
94
|
+
aria-haspopup="menu"
|
|
95
|
+
>
|
|
96
|
+
<MoreVertIcon />
|
|
97
|
+
</button>
|
|
98
|
+
</MenuTrigger>
|
|
99
|
+
|
|
100
|
+
<MenuContent align="end" sideOffset={4}>
|
|
101
|
+
{items.map((item, index) => (
|
|
102
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: static list from props
|
|
103
|
+
<OverflowItem key={index} item={item} />
|
|
104
|
+
))}
|
|
105
|
+
</MenuContent>
|
|
106
|
+
</Menu>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file app-bar-row.tsx
|
|
3
|
+
* MD3 Expressive App Bar Row DSL.
|
|
4
|
+
*
|
|
5
|
+
* Displays App Bar action items in a horizontal row.
|
|
6
|
+
* Items that exceed `maxItemCount` or available width collapse into a
|
|
7
|
+
* dropdown menu via <AppBarOverflowIndicator>.
|
|
8
|
+
*
|
|
9
|
+
* Translated from OverflowMeasurePolicy / AppBarRow.kt in Jetpack Compose M3.
|
|
10
|
+
*
|
|
11
|
+
* @see docs/m3/app-bars/AppBarRow.kt
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import * as React from "react";
|
|
15
|
+
import { cn } from "../../lib/utils";
|
|
16
|
+
import { AppBarTokens } from "./app-bar.tokens";
|
|
17
|
+
import type { AppBarRowProps } from "./app-bar.types";
|
|
18
|
+
import { AppBarItemButton } from "./app-bar-item-button";
|
|
19
|
+
import { AppBarOverflowIndicator } from "./app-bar-overflow-indicator";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* MD3 Expressive App Bar Row.
|
|
23
|
+
*
|
|
24
|
+
* Renders action items in a row. Compatible with the `actions` prop of any App Bar.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```tsx
|
|
28
|
+
* <SmallAppBar
|
|
29
|
+
* title="Messages"
|
|
30
|
+
* actions={
|
|
31
|
+
* <AppBarRow
|
|
32
|
+
* maxItemCount={2}
|
|
33
|
+
* items={[
|
|
34
|
+
* { type: 'clickable', icon: <Icon>search</Icon>, label: 'Search', onClick: handleSearch },
|
|
35
|
+
* { type: 'clickable', icon: <Icon>bookmark</Icon>, label: 'Bookmarks', onClick: handleBookmark },
|
|
36
|
+
* { type: 'clickable', icon: <Icon>settings</Icon>, label: 'Settings', onClick: handleSettings },
|
|
37
|
+
* ]}
|
|
38
|
+
* />
|
|
39
|
+
* }
|
|
40
|
+
* />
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export function AppBarRow({ items, maxItemCount, className }: AppBarRowProps) {
|
|
44
|
+
const containerRef = React.useRef<HTMLDivElement>(null);
|
|
45
|
+
const [visibleCount, setVisibleCount] = React.useState(
|
|
46
|
+
maxItemCount ?? items.length,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
React.useEffect(() => {
|
|
50
|
+
if (maxItemCount !== undefined) {
|
|
51
|
+
setVisibleCount(Math.min(maxItemCount, items.length));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const container = containerRef.current;
|
|
56
|
+
if (!container) return;
|
|
57
|
+
|
|
58
|
+
let debounceTimer: ReturnType<typeof setTimeout>;
|
|
59
|
+
|
|
60
|
+
const observer = new ResizeObserver((entries) => {
|
|
61
|
+
clearTimeout(debounceTimer);
|
|
62
|
+
debounceTimer = setTimeout(() => {
|
|
63
|
+
const entry = entries[0];
|
|
64
|
+
if (!entry) return;
|
|
65
|
+
|
|
66
|
+
const available = entry.contentRect.width;
|
|
67
|
+
const itemWidth = AppBarTokens.iconButtonTouchTarget;
|
|
68
|
+
const hasOverflow = items.length > Math.floor(available / itemWidth);
|
|
69
|
+
const reservedWidth = hasOverflow ? itemWidth : 0;
|
|
70
|
+
const count = Math.max(
|
|
71
|
+
0,
|
|
72
|
+
Math.floor((available - reservedWidth) / itemWidth),
|
|
73
|
+
);
|
|
74
|
+
setVisibleCount(Math.min(count, items.length));
|
|
75
|
+
}, 100);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
observer.observe(container);
|
|
79
|
+
return () => {
|
|
80
|
+
clearTimeout(debounceTimer);
|
|
81
|
+
observer.disconnect();
|
|
82
|
+
};
|
|
83
|
+
}, [items.length, maxItemCount]);
|
|
84
|
+
|
|
85
|
+
const visibleItems = items.slice(0, visibleCount);
|
|
86
|
+
const overflowItems = items.slice(visibleCount);
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<div
|
|
90
|
+
ref={containerRef}
|
|
91
|
+
className={cn("flex items-center", className)}
|
|
92
|
+
style={{ gap: AppBarTokens.iconButtonSpace }}
|
|
93
|
+
>
|
|
94
|
+
{visibleItems.map((item, index) => (
|
|
95
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: items are static from props
|
|
96
|
+
<AppBarItemButton key={index} item={item} />
|
|
97
|
+
))}
|
|
98
|
+
|
|
99
|
+
{overflowItems.length > 0 && (
|
|
100
|
+
<AppBarOverflowIndicator items={overflowItems} />
|
|
101
|
+
)}
|
|
102
|
+
</div>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { fireEvent, render, renderHook, screen } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
BottomAppBar,
|
|
5
|
+
SearchAppBar,
|
|
6
|
+
SmallAppBar,
|
|
7
|
+
useAppBarScroll,
|
|
8
|
+
} from "./index";
|
|
9
|
+
|
|
10
|
+
describe("SmallAppBar", () => {
|
|
11
|
+
it("renders title correctly", () => {
|
|
12
|
+
render(<SmallAppBar title="My Title" />);
|
|
13
|
+
expect(screen.getByText("My Title")).toBeInTheDocument();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("renders subtitle correctly", () => {
|
|
17
|
+
render(<SmallAppBar title="Main" subtitle="Sub" />);
|
|
18
|
+
expect(screen.getByText("Sub")).toBeInTheDocument();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("applies role='banner' to the header", () => {
|
|
22
|
+
render(<SmallAppBar title="Role Test" />);
|
|
23
|
+
expect(screen.getByRole("banner")).toBeInTheDocument();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("renders navigation icon", () => {
|
|
27
|
+
render(
|
|
28
|
+
<SmallAppBar
|
|
29
|
+
title="Nav Test"
|
|
30
|
+
navigationIcon={<button type="button" aria-label="Menu" />}
|
|
31
|
+
/>,
|
|
32
|
+
);
|
|
33
|
+
expect(screen.getByRole("button", { name: "Menu" })).toBeInTheDocument();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("renders actions", () => {
|
|
37
|
+
render(
|
|
38
|
+
<SmallAppBar
|
|
39
|
+
title="Actions Test"
|
|
40
|
+
actions={<button type="button" aria-label="Search" />}
|
|
41
|
+
/>,
|
|
42
|
+
);
|
|
43
|
+
expect(screen.getByRole("button", { name: "Search" })).toBeInTheDocument();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe("SearchAppBar", () => {
|
|
48
|
+
it("renders search bar with role='search'", () => {
|
|
49
|
+
render(<SearchAppBar searchPlaceholder="Search inside" />);
|
|
50
|
+
const searchContainer = screen.getByRole("search");
|
|
51
|
+
expect(searchContainer).toBeInTheDocument();
|
|
52
|
+
expect(searchContainer).toHaveAttribute("aria-label", "Search inside");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("fires onSearchFocus when clicked", () => {
|
|
56
|
+
const onFocus = vi.fn();
|
|
57
|
+
render(<SearchAppBar onSearchFocus={onFocus} />);
|
|
58
|
+
const searchContainer = screen.getByRole("search");
|
|
59
|
+
fireEvent.click(searchContainer);
|
|
60
|
+
expect(onFocus).toHaveBeenCalled();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe("BottomAppBar", () => {
|
|
65
|
+
it("applies role='navigation' to the container", () => {
|
|
66
|
+
render(<BottomAppBar />);
|
|
67
|
+
expect(screen.getByRole("navigation")).toBeInTheDocument();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("renders FAB correctly", () => {
|
|
71
|
+
render(
|
|
72
|
+
<BottomAppBar
|
|
73
|
+
floatingActionButton={<button type="button" aria-label="Add" />}
|
|
74
|
+
/>,
|
|
75
|
+
);
|
|
76
|
+
expect(screen.getByRole("button", { name: "Add" })).toBeInTheDocument();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe("useAppBarScroll", () => {
|
|
81
|
+
it("initializes with not scrolled", () => {
|
|
82
|
+
const { result } = renderHook(() =>
|
|
83
|
+
useAppBarScroll({ behavior: "pinned" }),
|
|
84
|
+
);
|
|
85
|
+
expect(result.current.isScrolled).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file app-bar.tokens.ts
|
|
3
|
+
* MD3 Expressive App Bar — Design tokens ported from:
|
|
4
|
+
* - AppBarTokens.kt (shared tokens)
|
|
5
|
+
* - AppBarSmallTokens.kt
|
|
6
|
+
* - AppBarMediumFlexibleTokens.kt
|
|
7
|
+
* - AppBarLargeFlexibleTokens.kt
|
|
8
|
+
* - BottomAppBarTokens.kt
|
|
9
|
+
* - DockedToolbarTokens.kt
|
|
10
|
+
* - FabSecondaryContainerTokens.kt
|
|
11
|
+
*
|
|
12
|
+
* All dimensional values are in px (dp equivalents for web at 1dp = 1px).
|
|
13
|
+
* Colors reference CSS custom properties — do NOT hardcode hex.
|
|
14
|
+
* @see docs/m3/app-bars/
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// ─── Dimensional Tokens ───────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Height and spacing tokens for all App Bar variants.
|
|
21
|
+
* Maps directly from MD3 Kotlin token files.
|
|
22
|
+
*/
|
|
23
|
+
export const AppBarTokens = {
|
|
24
|
+
// ── Heights ─────────────────────────────────────────────────────────────
|
|
25
|
+
heights: {
|
|
26
|
+
/** SmallAppBar height. AppBarSmallTokens.ContainerHeight = 64dp */
|
|
27
|
+
small: 64,
|
|
28
|
+
/** Collapsed height for flexible variants. = SmallAppBar height. */
|
|
29
|
+
flexibleCollapsed: 64,
|
|
30
|
+
/** MediumFlexibleAppBar expanded height (without subtitle). AppBarMediumFlexibleTokens */
|
|
31
|
+
mediumFlexExpanded: 112,
|
|
32
|
+
/** MediumFlexibleAppBar expanded height (with subtitle). */
|
|
33
|
+
mediumFlexWithSubtitleExpanded: 136,
|
|
34
|
+
/** LargeFlexibleAppBar expanded height (without subtitle). AppBarLargeFlexibleTokens */
|
|
35
|
+
largeFlexExpanded: 120,
|
|
36
|
+
/** LargeFlexibleAppBar expanded height (with subtitle). */
|
|
37
|
+
largeFlexWithSubtitleExpanded: 152,
|
|
38
|
+
/** BottomAppBar height. BottomAppBarTokens.ContainerHeight = 80dp */
|
|
39
|
+
bottom: 80,
|
|
40
|
+
/** DockedToolbar height. DockedToolbarTokens.ContainerHeight = 64dp */
|
|
41
|
+
dockedToolbar: 64,
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
// ── Icon and Avatar sizes ──────────────────────────────────────────────
|
|
45
|
+
/** AppBarTokens.IconSize = 24dp */
|
|
46
|
+
iconSize: 24,
|
|
47
|
+
/** AppBarTokens.AvatarSize = 32dp */
|
|
48
|
+
avatarSize: 32,
|
|
49
|
+
|
|
50
|
+
// ── Spacing ──────────────────────────────────────────────────────────────
|
|
51
|
+
/** AppBarTokens.LeadingSpace = 4dp */
|
|
52
|
+
leadingSpace: 4,
|
|
53
|
+
/** AppBarTokens.TrailingSpace = 4dp */
|
|
54
|
+
trailingSpace: 4,
|
|
55
|
+
/** AppBarTokens.IconButtonSpace = 0dp (no gap between icon buttons) */
|
|
56
|
+
iconButtonSpace: 0,
|
|
57
|
+
|
|
58
|
+
// ── Docked Toolbar spacing ────────────────────────────────────────────
|
|
59
|
+
dockedToolbar: {
|
|
60
|
+
/** DockedToolbarTokens.ContainerLeadingSpace = 16dp */
|
|
61
|
+
leadingSpace: 16,
|
|
62
|
+
/** DockedToolbarTokens.ContainerTrailingSpace = 16dp */
|
|
63
|
+
trailingSpace: 16,
|
|
64
|
+
/** DockedToolbarTokens.ContainerMinSpacing = 4dp */
|
|
65
|
+
minSpacing: 4,
|
|
66
|
+
/** DockedToolbarTokens.ContainerMaxSpacing = 32dp */
|
|
67
|
+
maxSpacing: 32,
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
// ── Touch targets ─────────────────────────────────────────────────────
|
|
71
|
+
/** Minimum 48px touch target for icon buttons per MD3 accessibility spec. */
|
|
72
|
+
iconButtonTouchTarget: 48,
|
|
73
|
+
} as const;
|
|
74
|
+
|
|
75
|
+
// ─── Typography Tokens ────────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* MD3 type scale values mapped to Tailwind CSS class strings.
|
|
79
|
+
* Used across App Bar variants for consistent typography.
|
|
80
|
+
*
|
|
81
|
+
* Values derived from MD3 Material Type Scale specification.
|
|
82
|
+
*/
|
|
83
|
+
export const appBarTypography = {
|
|
84
|
+
/**
|
|
85
|
+
* SmallAppBar title (collapsed state for flexible variants).
|
|
86
|
+
* AppBarSmallTokens.TitleFont = TitleLarge
|
|
87
|
+
* Spec: 22sp / 28sp line-height / medium weight
|
|
88
|
+
*/
|
|
89
|
+
titleLarge: "text-[22px] leading-[28px] font-medium tracking-[0px]",
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* SmallAppBar subtitle.
|
|
93
|
+
* AppBarSmallTokens.SubtitleFont = LabelMedium
|
|
94
|
+
* Spec: 12sp / 16sp line-height / medium weight / 0.5px tracking
|
|
95
|
+
*/
|
|
96
|
+
labelMedium: "text-[12px] leading-[16px] font-medium tracking-[0.5px]",
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* MediumFlexibleAppBar expanded title.
|
|
100
|
+
* AppBarMediumFlexibleTokens.TitleFont = HeadlineMedium
|
|
101
|
+
* Spec: 28sp / 36sp line-height / normal weight
|
|
102
|
+
*/
|
|
103
|
+
headlineMedium: "text-[28px] leading-[36px] font-normal tracking-[0px]",
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* MediumFlexibleAppBar subtitle.
|
|
107
|
+
* AppBarMediumFlexibleTokens.SubtitleFont = LabelLarge
|
|
108
|
+
* Spec: 14sp / 20sp line-height / medium weight / 0.1px tracking
|
|
109
|
+
*/
|
|
110
|
+
labelLarge: "text-[14px] leading-[20px] font-medium tracking-[0.1px]",
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* LargeFlexibleAppBar expanded title.
|
|
114
|
+
* AppBarLargeFlexibleTokens.TitleFont = DisplaySmall
|
|
115
|
+
* Spec: 36sp / 44sp line-height / normal weight / -0.25px tracking
|
|
116
|
+
*/
|
|
117
|
+
displaySmall: "text-[36px] leading-[44px] font-normal tracking-[-0.25px]",
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* LargeFlexibleAppBar subtitle.
|
|
121
|
+
* AppBarLargeFlexibleTokens.SubtitleFont = TitleMedium
|
|
122
|
+
* Spec: 16sp / 24sp line-height / medium weight / 0.15px tracking
|
|
123
|
+
*/
|
|
124
|
+
titleMedium: "text-[16px] leading-6 font-medium tracking-[0.15px]",
|
|
125
|
+
} as const;
|
|
126
|
+
|
|
127
|
+
// ─── Color Tokens ─────────────────────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* CSS custom property references for App Bar colors.
|
|
131
|
+
* Maps to --md-sys-color-* tokens in the MD3 theme system.
|
|
132
|
+
*
|
|
133
|
+
* IMPORTANT: Never hardcode hex/rgba values here — these references
|
|
134
|
+
* automatically adapt to light/dark theme via the MD3ThemeProvider.
|
|
135
|
+
*
|
|
136
|
+
* AppBarTokens.kt spec:
|
|
137
|
+
* - ContainerColor → md-sys-color-surface
|
|
138
|
+
* - OnScrollContainerColor → md-sys-color-surface-container
|
|
139
|
+
*/
|
|
140
|
+
export const APP_BAR_COLORS = {
|
|
141
|
+
// ── Container ────────────────────────────────────────────────────────────
|
|
142
|
+
/** Default background. AppBarTokens.ContainerColor → surface */
|
|
143
|
+
container: "var(--md-sys-color-surface)",
|
|
144
|
+
/** Background when content is scrolled. AppBarTokens.OnScrollContainerColor → surface-container */
|
|
145
|
+
scrolledContainer: "var(--md-sys-color-surface-container)",
|
|
146
|
+
|
|
147
|
+
// ── Content ──────────────────────────────────────────────────────────────
|
|
148
|
+
/** Title color. AppBarTokens.TitleColor → on-surface */
|
|
149
|
+
title: "var(--md-sys-color-on-surface)",
|
|
150
|
+
/** Subtitle color. AppBarTokens.SubtitleColor → on-surface-variant */
|
|
151
|
+
subtitle: "var(--md-sys-color-on-surface-variant)",
|
|
152
|
+
/** Navigation icon color. AppBarTokens.LeadingIconColor → on-surface */
|
|
153
|
+
navigationIcon: "var(--md-sys-color-on-surface)",
|
|
154
|
+
/** Action icon color. AppBarTokens.TrailingIconColor → on-surface-variant */
|
|
155
|
+
actionIcon: "var(--md-sys-color-on-surface-variant)",
|
|
156
|
+
|
|
157
|
+
// ── Search Bar ───────────────────────────────────────────────────────────
|
|
158
|
+
/** Search bar pill background. → surface-container-high */
|
|
159
|
+
searchBarBg: "var(--md-sys-color-surface-container-high)",
|
|
160
|
+
/** Search bar text/icon color. */
|
|
161
|
+
searchBarContent: "var(--md-sys-color-on-surface-variant)",
|
|
162
|
+
|
|
163
|
+
// ── Bottom App Bar ───────────────────────────────────────────────────────
|
|
164
|
+
/** BottomAppBarTokens.ContainerColor → surface-container */
|
|
165
|
+
bottomContainer: "var(--md-sys-color-surface-container)",
|
|
166
|
+
|
|
167
|
+
// ── FAB on Bottom App Bar ────────────────────────────────────────────────
|
|
168
|
+
/** FabSecondaryContainerTokens.ContainerColor → secondary-container */
|
|
169
|
+
fabContainer: "var(--md-sys-color-secondary-container)",
|
|
170
|
+
/** FabSecondaryContainerTokens.IconColor → on-secondary-container */
|
|
171
|
+
fabIcon: "var(--md-sys-color-on-secondary-container)",
|
|
172
|
+
} as const;
|
|
173
|
+
|
|
174
|
+
// ─── Animation Constants ──────────────────────────────────────────────────────
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Color transition when App Bar background changes on scroll.
|
|
178
|
+
* MD3 Standard easing: cubic-bezier(0.2, 0, 0, 1), 200ms.
|
|
179
|
+
*/
|
|
180
|
+
export const APP_BAR_COLOR_TRANSITION = {
|
|
181
|
+
duration: 0.2,
|
|
182
|
+
ease: [0.2, 0, 0, 1] as [number, number, number, number],
|
|
183
|
+
} as const;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Spring animation for enterAlways behavior (hide/show on scroll direction).
|
|
187
|
+
* Equivalent to MD3 FastSpatial motion scheme.
|
|
188
|
+
*/
|
|
189
|
+
export const APP_BAR_ENTER_ALWAYS_SPRING = {
|
|
190
|
+
type: "spring",
|
|
191
|
+
stiffness: 380,
|
|
192
|
+
damping: 40,
|
|
193
|
+
mass: 1,
|
|
194
|
+
} as const;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Spring animation for Bottom App Bar hide/show.
|
|
198
|
+
* Slightly looser feel for bottom navigation.
|
|
199
|
+
*/
|
|
200
|
+
export const APP_BAR_BOTTOM_SPRING = {
|
|
201
|
+
type: "spring",
|
|
202
|
+
stiffness: 300,
|
|
203
|
+
damping: 30,
|
|
204
|
+
} as const;
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* SearchView appearance/disappearance transition.
|
|
208
|
+
* Uses spring for natural feel of expanding overlay.
|
|
209
|
+
*/
|
|
210
|
+
export const SEARCH_VIEW_SPRING = {
|
|
211
|
+
type: "spring",
|
|
212
|
+
stiffness: 400,
|
|
213
|
+
damping: 35,
|
|
214
|
+
} as const;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Title crossfade transition for flexible App Bars.
|
|
218
|
+
* Short duration keeps the collapse feeling snappy.
|
|
219
|
+
*/
|
|
220
|
+
export const APP_BAR_TITLE_FADE = {
|
|
221
|
+
duration: 0.15,
|
|
222
|
+
ease: "easeInOut",
|
|
223
|
+
} as const;
|