@bug-on/md3-react 2.0.3 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +42 -0
- package/CHANGELOG.md +69 -0
- package/dist/index.css +178 -0
- package/dist/index.css.d.ts +2 -0
- package/dist/index.d.mts +6135 -0
- package/dist/index.d.ts +6135 -71
- package/dist/index.js +1688 -631
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1600 -564
- 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/plugin.d.mts +1 -0
- package/dist/plugin.d.ts +1 -0
- package/dist/plugin.js +13 -0
- package/dist/plugin.js.map +1 -0
- package/dist/plugin.mjs +3 -0
- package/dist/plugin.mjs.map +1 -0
- package/dist/typography.css.d.ts +2 -0
- package/package.json +28 -19
- package/scripts/copy-assets.js +115 -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 +195 -0
- package/src/lib/utils.ts +6 -0
- package/src/plugin.ts +12 -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 +306 -0
- package/src/ui/button.tsx +665 -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 +607 -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 +135 -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 +215 -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
package/src/index.ts
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
export { useMediaQuery } from "./hooks/useMediaQuery";
|
|
2
|
+
// Hooks
|
|
3
|
+
/** @deprecated Use `useRippleState` (Framer Motion) from the main package instead. DOM-only ripple. */
|
|
4
|
+
export { useRipple as useDOMRipple } from "./hooks/useRipple";
|
|
5
|
+
export { MaterialSymbolsPreconnect } from "./lib/material-symbols-preconnect";
|
|
6
|
+
// Theme — MD3 Dynamic Color
|
|
7
|
+
export type { MD3ColorScheme, ThemeMode } from "./lib/theme-utils";
|
|
8
|
+
export { applyTheme, generateM3Theme, resolveMode } from "./lib/theme-utils";
|
|
9
|
+
// Utils
|
|
10
|
+
export { cn } from "./lib/utils";
|
|
11
|
+
// Types
|
|
12
|
+
export type {
|
|
13
|
+
MD3ColorStyle,
|
|
14
|
+
MD3Shape,
|
|
15
|
+
MD3Size,
|
|
16
|
+
PolymorphicProps,
|
|
17
|
+
PolymorphicRef,
|
|
18
|
+
} from "./types/md3";
|
|
19
|
+
// App Bar — MD3 Expressive App Bar system
|
|
20
|
+
export type {
|
|
21
|
+
AppBarColors,
|
|
22
|
+
AppBarColumnProps,
|
|
23
|
+
AppBarItem,
|
|
24
|
+
AppBarItemType,
|
|
25
|
+
AppBarMenuState,
|
|
26
|
+
AppBarOverflowIndicatorProps,
|
|
27
|
+
AppBarRowProps,
|
|
28
|
+
AppBarScrollBehavior,
|
|
29
|
+
BaseAppBarProps,
|
|
30
|
+
BottomAppBarProps,
|
|
31
|
+
DockedToolbarProps,
|
|
32
|
+
FlexibleAppBarProps,
|
|
33
|
+
SearchAppBarProps,
|
|
34
|
+
SearchBarVariant,
|
|
35
|
+
SearchViewProps,
|
|
36
|
+
SmallAppBarProps,
|
|
37
|
+
TitleAlignment,
|
|
38
|
+
UseAppBarScrollReturn,
|
|
39
|
+
} from "./ui/app-bar";
|
|
40
|
+
export {
|
|
41
|
+
APP_BAR_BOTTOM_SPRING,
|
|
42
|
+
APP_BAR_COLOR_TRANSITION,
|
|
43
|
+
APP_BAR_COLORS,
|
|
44
|
+
APP_BAR_ENTER_ALWAYS_SPRING,
|
|
45
|
+
APP_BAR_TITLE_FADE,
|
|
46
|
+
AppBarColumn,
|
|
47
|
+
AppBarOverflowIndicator,
|
|
48
|
+
AppBarRow,
|
|
49
|
+
AppBarTokens,
|
|
50
|
+
appBarTypography,
|
|
51
|
+
BottomAppBar,
|
|
52
|
+
DockedToolbar,
|
|
53
|
+
LargeFlexibleAppBar,
|
|
54
|
+
MediumFlexibleAppBar,
|
|
55
|
+
SEARCH_VIEW_SPRING,
|
|
56
|
+
SearchAppBar,
|
|
57
|
+
SearchView,
|
|
58
|
+
SearchViewContainer,
|
|
59
|
+
SmallAppBar,
|
|
60
|
+
useAppBarScroll,
|
|
61
|
+
} from "./ui/app-bar";
|
|
62
|
+
export type { BadgedBoxProps, BadgeProps } from "./ui/badge";
|
|
63
|
+
// Badge — MD3 Expressive status indicator
|
|
64
|
+
export { Badge, BadgedBox } from "./ui/badge";
|
|
65
|
+
export type { BaseButtonProps, ButtonProps } from "./ui/button";
|
|
66
|
+
export { Button } from "./ui/button";
|
|
67
|
+
export type { ButtonGroupProps } from "./ui/button-group";
|
|
68
|
+
export { ButtonGroup } from "./ui/button-group";
|
|
69
|
+
export type { CardProps } from "./ui/card";
|
|
70
|
+
export { Card } from "./ui/card";
|
|
71
|
+
// Checkbox — MD3 Expressive tri-state checkbox
|
|
72
|
+
export type {
|
|
73
|
+
CheckboxProps,
|
|
74
|
+
CheckboxState,
|
|
75
|
+
TriStateCheckboxProps,
|
|
76
|
+
} from "./ui/checkbox";
|
|
77
|
+
export { Checkbox, TriStateCheckbox } from "./ui/checkbox";
|
|
78
|
+
export type { ChipProps } from "./ui/chip";
|
|
79
|
+
export { Chip } from "./ui/chip";
|
|
80
|
+
export type { CodeBlockProps } from "./ui/code-block";
|
|
81
|
+
export { CodeBlock } from "./ui/code-block";
|
|
82
|
+
export type {
|
|
83
|
+
DialogContentProps,
|
|
84
|
+
DialogFullScreenContentProps,
|
|
85
|
+
DialogProps,
|
|
86
|
+
} from "./ui/dialog";
|
|
87
|
+
// Dialog
|
|
88
|
+
export {
|
|
89
|
+
Dialog,
|
|
90
|
+
DialogBody,
|
|
91
|
+
DialogClose,
|
|
92
|
+
DialogContent,
|
|
93
|
+
DialogDescription,
|
|
94
|
+
DialogFooter,
|
|
95
|
+
DialogFullScreenContent,
|
|
96
|
+
DialogHeader,
|
|
97
|
+
DialogIcon,
|
|
98
|
+
DialogOverlay,
|
|
99
|
+
DialogPortal,
|
|
100
|
+
DialogTitle,
|
|
101
|
+
DialogTrigger,
|
|
102
|
+
} from "./ui/dialog";
|
|
103
|
+
// Divider — MD3 Expressive divider line
|
|
104
|
+
export type { DividerProps } from "./ui/divider";
|
|
105
|
+
export { buildWavePath, Divider } from "./ui/divider";
|
|
106
|
+
export type { DrawerContentProps, DrawerProps } from "./ui/drawer";
|
|
107
|
+
// Drawer (Bottom Sheet)
|
|
108
|
+
export {
|
|
109
|
+
Drawer,
|
|
110
|
+
DrawerClose,
|
|
111
|
+
DrawerContent,
|
|
112
|
+
DrawerDescription,
|
|
113
|
+
DrawerFooter,
|
|
114
|
+
DrawerHeader,
|
|
115
|
+
DrawerOverlay,
|
|
116
|
+
DrawerPortal,
|
|
117
|
+
DrawerTitle,
|
|
118
|
+
DrawerTrigger,
|
|
119
|
+
} from "./ui/drawer";
|
|
120
|
+
export type { FABPositionProps, FABProps } from "./ui/fab";
|
|
121
|
+
// Floating Action Button
|
|
122
|
+
export { FAB, FABPosition } from "./ui/fab";
|
|
123
|
+
export type {
|
|
124
|
+
FABMenuItemData,
|
|
125
|
+
FABMenuItemProps,
|
|
126
|
+
FABMenuProps,
|
|
127
|
+
ToggleFABProps,
|
|
128
|
+
} from "./ui/fab-menu";
|
|
129
|
+
// FAB Menu
|
|
130
|
+
export { FABMenu, FABMenuItem, ToggleFAB } from "./ui/fab-menu";
|
|
131
|
+
// Icon — Material Symbols variable font
|
|
132
|
+
export type { IconProps } from "./ui/icon";
|
|
133
|
+
export { Icon } from "./ui/icon";
|
|
134
|
+
export type { BaseIconButtonProps, IconButtonProps } from "./ui/icon-button";
|
|
135
|
+
export { IconButton } from "./ui/icon-button";
|
|
136
|
+
export type { LoadingIndicatorProps } from "./ui/loading-indicator";
|
|
137
|
+
export { LoadingIndicator } from "./ui/loading-indicator";
|
|
138
|
+
// MD3 Expressive Menu — Standard + Vibrant + shape morphing
|
|
139
|
+
export type {
|
|
140
|
+
// ContextMenu
|
|
141
|
+
ContextMenuContentProps,
|
|
142
|
+
ContextMenuProps,
|
|
143
|
+
ContextMenuTriggerProps,
|
|
144
|
+
MenuColorVariant,
|
|
145
|
+
MenuContentProps,
|
|
146
|
+
MenuDividerProps,
|
|
147
|
+
MenuGroupPosition,
|
|
148
|
+
MenuGroupProps,
|
|
149
|
+
MenuItemPosition,
|
|
150
|
+
MenuItemProps,
|
|
151
|
+
MenuPrimitive,
|
|
152
|
+
MenuProps,
|
|
153
|
+
MenuTriggerProps,
|
|
154
|
+
MenuVariant,
|
|
155
|
+
SubMenuProps,
|
|
156
|
+
// Vertical Menu
|
|
157
|
+
VerticalMenuContentProps,
|
|
158
|
+
VerticalMenuDividerProps,
|
|
159
|
+
VerticalMenuGroupProps,
|
|
160
|
+
VerticalMenuProps,
|
|
161
|
+
VerticalMenuSeparatorStyle,
|
|
162
|
+
} from "./ui/menu";
|
|
163
|
+
export {
|
|
164
|
+
CHECK_ICON_VARIANTS,
|
|
165
|
+
ContextMenu,
|
|
166
|
+
ContextMenuContent,
|
|
167
|
+
ContextMenuTrigger,
|
|
168
|
+
DIVIDER_COLOR,
|
|
169
|
+
DIVIDER_PADDING,
|
|
170
|
+
FAST_EFFECTS_TRANSITION,
|
|
171
|
+
FAST_SPATIAL_SPRING,
|
|
172
|
+
GROUP_SHAPES,
|
|
173
|
+
ITEM_SHAPE_CLASSES,
|
|
174
|
+
MENU_CHECK_ICON_SIZE,
|
|
175
|
+
MENU_CONTAINER_VARIANTS,
|
|
176
|
+
MENU_GROUP_GAP,
|
|
177
|
+
MENU_ICON_SIZE,
|
|
178
|
+
MENU_ITEM_MIN_HEIGHT,
|
|
179
|
+
MENU_MAX_WIDTH,
|
|
180
|
+
MENU_MIN_WIDTH,
|
|
181
|
+
Menu,
|
|
182
|
+
MenuContent,
|
|
183
|
+
MenuDivider,
|
|
184
|
+
MenuGroup,
|
|
185
|
+
MenuItem,
|
|
186
|
+
MenuProvider,
|
|
187
|
+
MenuTrigger,
|
|
188
|
+
STANDARD_COLORS,
|
|
189
|
+
SUBMENU_CONTAINER_VARIANTS,
|
|
190
|
+
SubMenu,
|
|
191
|
+
useMenuContext,
|
|
192
|
+
// Vertical Menu (static, always-visible)
|
|
193
|
+
VerticalMenu,
|
|
194
|
+
VerticalMenuContent,
|
|
195
|
+
VerticalMenuDivider,
|
|
196
|
+
VerticalMenuGroup,
|
|
197
|
+
VIBRANT_COLORS,
|
|
198
|
+
} from "./ui/menu";
|
|
199
|
+
// Navigation Rail
|
|
200
|
+
export type {
|
|
201
|
+
NavigationRailItemProps,
|
|
202
|
+
NavigationRailLabelVisibility,
|
|
203
|
+
NavigationRailProps,
|
|
204
|
+
NavigationRailVariant,
|
|
205
|
+
} from "./ui/navigation-rail";
|
|
206
|
+
export { NavigationRail, NavigationRailItem } from "./ui/navigation-rail";
|
|
207
|
+
export type {
|
|
208
|
+
CircularProgressProps,
|
|
209
|
+
LinearProgressProps,
|
|
210
|
+
ProgressIndicatorProps,
|
|
211
|
+
} from "./ui/progress-indicator";
|
|
212
|
+
export { ProgressIndicator } from "./ui/progress-indicator";
|
|
213
|
+
// RadioButton — MD3 Expressive radio button
|
|
214
|
+
export type {
|
|
215
|
+
RadioButtonColors,
|
|
216
|
+
RadioButtonProps,
|
|
217
|
+
RadioGroupProps,
|
|
218
|
+
} from "./ui/radio-button";
|
|
219
|
+
export { RadioButton, RadioGroup } from "./ui/radio-button";
|
|
220
|
+
export type {
|
|
221
|
+
RippleOrigin,
|
|
222
|
+
RippleProps,
|
|
223
|
+
UseRippleStateOptions,
|
|
224
|
+
} from "./ui/ripple";
|
|
225
|
+
export { Ripple, useRipple, useRippleState } from "./ui/ripple";
|
|
226
|
+
// ScrollArea
|
|
227
|
+
export type {
|
|
228
|
+
ScrollAreaOrientation,
|
|
229
|
+
ScrollAreaProps,
|
|
230
|
+
ScrollAreaType,
|
|
231
|
+
} from "./ui/scroll-area";
|
|
232
|
+
export {
|
|
233
|
+
ScrollArea,
|
|
234
|
+
ScrollAreaScrollbar,
|
|
235
|
+
} from "./ui/scroll-area";
|
|
236
|
+
// Search — MD3 Expressive Search component
|
|
237
|
+
export type {
|
|
238
|
+
SearchProps,
|
|
239
|
+
SearchStyleType,
|
|
240
|
+
SearchVariant,
|
|
241
|
+
} from "./ui/search";
|
|
242
|
+
export {
|
|
243
|
+
SEARCH_BAR_EXPAND_SPRING,
|
|
244
|
+
SEARCH_COLORS,
|
|
245
|
+
SEARCH_DOCKED_REVEAL_SPRING,
|
|
246
|
+
SEARCH_FULLSCREEN_SPRING,
|
|
247
|
+
SEARCH_TYPOGRAPHY,
|
|
248
|
+
Search,
|
|
249
|
+
SearchBar,
|
|
250
|
+
SearchTokens,
|
|
251
|
+
SearchViewDocked,
|
|
252
|
+
SearchViewFullScreen,
|
|
253
|
+
useSearchKeyboard,
|
|
254
|
+
} from "./ui/search";
|
|
255
|
+
// Slider — MD3 Expressive
|
|
256
|
+
export type {
|
|
257
|
+
RangeSliderProps,
|
|
258
|
+
SliderOrientation,
|
|
259
|
+
SliderProps,
|
|
260
|
+
SliderTrackSize,
|
|
261
|
+
SliderVariant,
|
|
262
|
+
} from "./ui/slider";
|
|
263
|
+
export { RangeSlider, Slider, SliderColors, SliderTokens } from "./ui/slider";
|
|
264
|
+
// Snackbar — MD3 Expressive imperative toast system
|
|
265
|
+
export type {
|
|
266
|
+
SnackbarData,
|
|
267
|
+
SnackbarDuration,
|
|
268
|
+
SnackbarHostProps,
|
|
269
|
+
SnackbarProps,
|
|
270
|
+
SnackbarResult,
|
|
271
|
+
SnackbarVisuals,
|
|
272
|
+
UseSnackbarStateReturn,
|
|
273
|
+
} from "./ui/snackbar";
|
|
274
|
+
export {
|
|
275
|
+
Snackbar,
|
|
276
|
+
SnackbarHost,
|
|
277
|
+
SnackbarProvider,
|
|
278
|
+
useSnackbar,
|
|
279
|
+
useSnackbarState,
|
|
280
|
+
} from "./ui/snackbar";
|
|
281
|
+
// Switch — MD3 Expressive toggle
|
|
282
|
+
export type { SwitchProps } from "./ui/switch";
|
|
283
|
+
export { Switch, SwitchColors, SwitchTokens } from "./ui/switch";
|
|
284
|
+
export { Text, type TextProps } from "./ui/Text";
|
|
285
|
+
// Tabs — MD3 Expressive navigation tabs
|
|
286
|
+
export type {
|
|
287
|
+
TabProps,
|
|
288
|
+
TabsContentProps,
|
|
289
|
+
TabsListProps,
|
|
290
|
+
TabsProps,
|
|
291
|
+
TabsVariant,
|
|
292
|
+
} from "./ui/tabs";
|
|
293
|
+
export {
|
|
294
|
+
Tab,
|
|
295
|
+
Tabs,
|
|
296
|
+
TabsColors,
|
|
297
|
+
TabsContent,
|
|
298
|
+
TabsList,
|
|
299
|
+
TabsTokens,
|
|
300
|
+
} from "./ui/tabs";
|
|
301
|
+
// TextField — MD3 Expressive
|
|
302
|
+
export type {
|
|
303
|
+
TextFieldHandle,
|
|
304
|
+
TextFieldInputType,
|
|
305
|
+
TextFieldProps,
|
|
306
|
+
TextFieldTrailingIconMode,
|
|
307
|
+
TextFieldVariant,
|
|
308
|
+
} from "./ui/text-field";
|
|
309
|
+
export { TextField } from "./ui/text-field";
|
|
310
|
+
export type { MD3ThemeProviderProps } from "./ui/theme-provider";
|
|
311
|
+
export { MD3ThemeProvider, useTheme, useThemeMode } from "./ui/theme-provider";
|
|
312
|
+
export type { TableOfContentsProps, ToCItem } from "./ui/toc";
|
|
313
|
+
export { TableOfContents } from "./ui/toc";
|
|
314
|
+
// Tooltip — MD3 Expressive
|
|
315
|
+
export type {
|
|
316
|
+
CaretConfig,
|
|
317
|
+
PlainTooltipProps,
|
|
318
|
+
RichTooltipProps,
|
|
319
|
+
TooltipBoxProps,
|
|
320
|
+
TooltipPlacement,
|
|
321
|
+
TooltipState,
|
|
322
|
+
TooltipStateConfig,
|
|
323
|
+
TooltipTrigger,
|
|
324
|
+
} from "./ui/tooltip";
|
|
325
|
+
export {
|
|
326
|
+
PlainTooltip,
|
|
327
|
+
RichTooltip,
|
|
328
|
+
TooltipBox,
|
|
329
|
+
TooltipCaretShape,
|
|
330
|
+
TooltipTokens,
|
|
331
|
+
useTooltipPosition,
|
|
332
|
+
useTooltipState,
|
|
333
|
+
} from "./ui/tooltip";
|
|
334
|
+
export type {
|
|
335
|
+
TextStyle,
|
|
336
|
+
TypeScaleTokensType,
|
|
337
|
+
TypographyProviderProps,
|
|
338
|
+
} from "./ui/typography";
|
|
339
|
+
// Typography - MD3 Expressive
|
|
340
|
+
export {
|
|
341
|
+
MD3_EXPRESSIVE_FONT_VARIATION,
|
|
342
|
+
TypeScaleTokens,
|
|
343
|
+
Typography,
|
|
344
|
+
TypographyContext,
|
|
345
|
+
TypographyKeyTokens,
|
|
346
|
+
TypographyProvider,
|
|
347
|
+
TypographyTokens,
|
|
348
|
+
useTypography,
|
|
349
|
+
} from "./ui/typography";
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MaterialSymbolsPreconnect
|
|
3
|
+
*
|
|
4
|
+
* Inject preconnect resource hints cho Google Fonts CDN và <head>.
|
|
5
|
+
* Đặt component này CÀNG SỚM CÀNG TỐT trong app tree, lý tưởng là
|
|
6
|
+
* ngay trong <head> hoặc root layout.
|
|
7
|
+
*
|
|
8
|
+
* WHY THIS MATTERS:
|
|
9
|
+
* Nếu @import url() nằm trong CSS file, browser phải:
|
|
10
|
+
* 1. Parse HTML -> download JS bundle -> execute CSS -> gặp @import -> mới bắt đầu connect Google Fonts
|
|
11
|
+
* Preconnect hints cho phép browser bắt đầu TCP handshake + TLS ngay từ bước 1,
|
|
12
|
+
* tiết kiệm 100-500ms connection time tùy network.
|
|
13
|
+
*
|
|
14
|
+
* USAGE:
|
|
15
|
+
* ```tsx
|
|
16
|
+
* // app/layout.tsx (Next.js) hoặc index.html equivalent
|
|
17
|
+
* import { MaterialSymbolsPreconnect } from '@bug-on/md3-react';
|
|
18
|
+
*
|
|
19
|
+
* export default function RootLayout({ children }) {
|
|
20
|
+
* return (
|
|
21
|
+
* <html>
|
|
22
|
+
* <head>
|
|
23
|
+
* <MaterialSymbolsPreconnect />
|
|
24
|
+
* </head>
|
|
25
|
+
* <body>{children}</body>
|
|
26
|
+
* </html>
|
|
27
|
+
* );
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* NOTE: Chỉ dùng component này với CDN mode.
|
|
32
|
+
* Với self-hosted fonts thì không cần preconnect đến external origin.
|
|
33
|
+
*/
|
|
34
|
+
export interface MaterialSymbolsPreconnectProps {
|
|
35
|
+
/**
|
|
36
|
+
* Mảng các biến thể font Material Symbols cần tải.
|
|
37
|
+
* Chỉ nên chọn các biến thể mà ứng dụng thực sự sử dụng để tiết kiệm băng thông.
|
|
38
|
+
* @default ["outlined"]
|
|
39
|
+
*/
|
|
40
|
+
variants?: Array<"outlined" | "rounded" | "sharp">;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function MaterialSymbolsPreconnect({
|
|
44
|
+
variants = ["outlined"],
|
|
45
|
+
}: MaterialSymbolsPreconnectProps) {
|
|
46
|
+
return (
|
|
47
|
+
<>
|
|
48
|
+
{/* Preconnect cho CSS stylesheet */}
|
|
49
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
50
|
+
{/* Preconnect cho font files — crossorigin bắt buộc vì font download là CORS request */}
|
|
51
|
+
<link
|
|
52
|
+
rel="preconnect"
|
|
53
|
+
href="https://fonts.gstatic.com"
|
|
54
|
+
crossOrigin="anonymous"
|
|
55
|
+
/>
|
|
56
|
+
{/* Load các biến thể được chọn.
|
|
57
|
+
Sử dụng display=swap để tối ưu hóa hiệu năng (Lighthouse/Next.js recommendation).
|
|
58
|
+
Lưu ý: Có thể gây ra hiện tượng chớp chữ (ligature flicker) trong tích tắc. */}
|
|
59
|
+
{variants.includes("outlined") && (
|
|
60
|
+
<link
|
|
61
|
+
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap"
|
|
62
|
+
rel="stylesheet"
|
|
63
|
+
precedence="default"
|
|
64
|
+
/>
|
|
65
|
+
)}
|
|
66
|
+
{variants.includes("rounded") && (
|
|
67
|
+
<link
|
|
68
|
+
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap"
|
|
69
|
+
rel="stylesheet"
|
|
70
|
+
precedence="default"
|
|
71
|
+
/>
|
|
72
|
+
)}
|
|
73
|
+
{variants.includes("sharp") && (
|
|
74
|
+
<link
|
|
75
|
+
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap"
|
|
76
|
+
rel="stylesheet"
|
|
77
|
+
precedence="default"
|
|
78
|
+
/>
|
|
79
|
+
)}
|
|
80
|
+
</>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import {
|
|
2
|
+
argbFromHex,
|
|
3
|
+
hexFromArgb,
|
|
4
|
+
themeFromSourceColor,
|
|
5
|
+
} from "@material/material-color-utilities";
|
|
6
|
+
|
|
7
|
+
export type ThemeMode = "light" | "dark" | "system";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Resolves the effective color scheme from a ThemeMode.
|
|
11
|
+
* When mode is "system", reads the OS preference via matchMedia.
|
|
12
|
+
* Returns "light" as the safe default in SSR environments.
|
|
13
|
+
*/
|
|
14
|
+
export function resolveMode(mode: ThemeMode): "light" | "dark" {
|
|
15
|
+
if (mode !== "system") return mode;
|
|
16
|
+
if (typeof window === "undefined") return "light";
|
|
17
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
18
|
+
? "dark"
|
|
19
|
+
: "light";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface MD3ColorScheme {
|
|
23
|
+
primary: string;
|
|
24
|
+
onPrimary: string;
|
|
25
|
+
primaryContainer: string;
|
|
26
|
+
onPrimaryContainer: string;
|
|
27
|
+
inversePrimary: string;
|
|
28
|
+
primaryFixed: string;
|
|
29
|
+
primaryFixedDim: string;
|
|
30
|
+
onPrimaryFixed: string;
|
|
31
|
+
onPrimaryFixedVariant: string;
|
|
32
|
+
|
|
33
|
+
secondary: string;
|
|
34
|
+
onSecondary: string;
|
|
35
|
+
secondaryContainer: string;
|
|
36
|
+
onSecondaryContainer: string;
|
|
37
|
+
secondaryFixed: string;
|
|
38
|
+
secondaryFixedDim: string;
|
|
39
|
+
onSecondaryFixed: string;
|
|
40
|
+
onSecondaryFixedVariant: string;
|
|
41
|
+
|
|
42
|
+
tertiary: string;
|
|
43
|
+
onTertiary: string;
|
|
44
|
+
tertiaryContainer: string;
|
|
45
|
+
onTertiaryContainer: string;
|
|
46
|
+
tertiaryFixed: string;
|
|
47
|
+
tertiaryFixedDim: string;
|
|
48
|
+
onTertiaryFixed: string;
|
|
49
|
+
onTertiaryFixedVariant: string;
|
|
50
|
+
|
|
51
|
+
error: string;
|
|
52
|
+
onError: string;
|
|
53
|
+
errorContainer: string;
|
|
54
|
+
onErrorContainer: string;
|
|
55
|
+
|
|
56
|
+
surface: string;
|
|
57
|
+
onSurface: string;
|
|
58
|
+
surfaceVariant: string;
|
|
59
|
+
onSurfaceVariant: string;
|
|
60
|
+
surfaceTint: string;
|
|
61
|
+
surfaceContainerLowest: string;
|
|
62
|
+
surfaceContainerLow: string;
|
|
63
|
+
surfaceContainer: string;
|
|
64
|
+
surfaceContainerHigh: string;
|
|
65
|
+
surfaceContainerHighest: string;
|
|
66
|
+
|
|
67
|
+
inverseSurface: string;
|
|
68
|
+
inverseOnSurface: string;
|
|
69
|
+
|
|
70
|
+
background: string;
|
|
71
|
+
onBackground: string;
|
|
72
|
+
|
|
73
|
+
outline: string;
|
|
74
|
+
outlineVariant: string;
|
|
75
|
+
|
|
76
|
+
shadow: string;
|
|
77
|
+
scrim: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Generate a complete MD3 color scheme from a source color hex string.
|
|
82
|
+
* Uses the HCT color space algorithm — same as Material You on Android.
|
|
83
|
+
*/
|
|
84
|
+
export function generateM3Theme(
|
|
85
|
+
sourceColorHex: string,
|
|
86
|
+
mode: "light" | "dark" = "light",
|
|
87
|
+
): MD3ColorScheme {
|
|
88
|
+
const sourceColor = argbFromHex(sourceColorHex);
|
|
89
|
+
const theme = themeFromSourceColor(sourceColor);
|
|
90
|
+
|
|
91
|
+
const scheme = mode === "light" ? theme.schemes.light : theme.schemes.dark;
|
|
92
|
+
const palettes = theme.palettes;
|
|
93
|
+
|
|
94
|
+
const tone = (palette: (typeof palettes)[keyof typeof palettes], t: number) =>
|
|
95
|
+
hexFromArgb(palette.tone(t));
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
primary: hexFromArgb(scheme.primary),
|
|
99
|
+
onPrimary: hexFromArgb(scheme.onPrimary),
|
|
100
|
+
primaryContainer: hexFromArgb(scheme.primaryContainer),
|
|
101
|
+
onPrimaryContainer: hexFromArgb(scheme.onPrimaryContainer),
|
|
102
|
+
inversePrimary: hexFromArgb(scheme.inversePrimary),
|
|
103
|
+
primaryFixed: tone(palettes.primary, 90),
|
|
104
|
+
primaryFixedDim: tone(palettes.primary, 80),
|
|
105
|
+
onPrimaryFixed: tone(palettes.primary, 10),
|
|
106
|
+
onPrimaryFixedVariant: tone(palettes.primary, 30),
|
|
107
|
+
|
|
108
|
+
secondary: hexFromArgb(scheme.secondary),
|
|
109
|
+
onSecondary: hexFromArgb(scheme.onSecondary),
|
|
110
|
+
secondaryContainer: hexFromArgb(scheme.secondaryContainer),
|
|
111
|
+
onSecondaryContainer: hexFromArgb(scheme.onSecondaryContainer),
|
|
112
|
+
secondaryFixed: tone(palettes.secondary, 90),
|
|
113
|
+
secondaryFixedDim: tone(palettes.secondary, 80),
|
|
114
|
+
onSecondaryFixed: tone(palettes.secondary, 10),
|
|
115
|
+
onSecondaryFixedVariant: tone(palettes.secondary, 30),
|
|
116
|
+
|
|
117
|
+
tertiary: hexFromArgb(scheme.tertiary),
|
|
118
|
+
onTertiary: hexFromArgb(scheme.onTertiary),
|
|
119
|
+
tertiaryContainer: hexFromArgb(scheme.tertiaryContainer),
|
|
120
|
+
onTertiaryContainer: hexFromArgb(scheme.onTertiaryContainer),
|
|
121
|
+
tertiaryFixed: tone(palettes.tertiary, 90),
|
|
122
|
+
tertiaryFixedDim: tone(palettes.tertiary, 80),
|
|
123
|
+
onTertiaryFixed: tone(palettes.tertiary, 10),
|
|
124
|
+
onTertiaryFixedVariant: tone(palettes.tertiary, 30),
|
|
125
|
+
|
|
126
|
+
error: hexFromArgb(scheme.error),
|
|
127
|
+
onError: hexFromArgb(scheme.onError),
|
|
128
|
+
errorContainer: hexFromArgb(scheme.errorContainer),
|
|
129
|
+
onErrorContainer: hexFromArgb(scheme.onErrorContainer),
|
|
130
|
+
|
|
131
|
+
surface: hexFromArgb(scheme.surface),
|
|
132
|
+
onSurface: hexFromArgb(scheme.onSurface),
|
|
133
|
+
surfaceVariant: hexFromArgb(scheme.surfaceVariant),
|
|
134
|
+
onSurfaceVariant: hexFromArgb(scheme.onSurfaceVariant),
|
|
135
|
+
surfaceTint: hexFromArgb(scheme.primary),
|
|
136
|
+
// Surface container roles from neutral palette tones
|
|
137
|
+
surfaceContainerLowest:
|
|
138
|
+
mode === "light"
|
|
139
|
+
? tone(palettes.neutral, 100)
|
|
140
|
+
: tone(palettes.neutral, 4),
|
|
141
|
+
surfaceContainerLow:
|
|
142
|
+
mode === "light"
|
|
143
|
+
? tone(palettes.neutral, 96)
|
|
144
|
+
: tone(palettes.neutral, 10),
|
|
145
|
+
surfaceContainer:
|
|
146
|
+
mode === "light"
|
|
147
|
+
? tone(palettes.neutral, 94)
|
|
148
|
+
: tone(palettes.neutral, 12),
|
|
149
|
+
surfaceContainerHigh:
|
|
150
|
+
mode === "light"
|
|
151
|
+
? tone(palettes.neutral, 92)
|
|
152
|
+
: tone(palettes.neutral, 17),
|
|
153
|
+
surfaceContainerHighest:
|
|
154
|
+
mode === "light"
|
|
155
|
+
? tone(palettes.neutral, 90)
|
|
156
|
+
: tone(palettes.neutral, 22),
|
|
157
|
+
|
|
158
|
+
inverseSurface: hexFromArgb(scheme.inverseSurface),
|
|
159
|
+
inverseOnSurface: hexFromArgb(scheme.inverseOnSurface),
|
|
160
|
+
|
|
161
|
+
background: hexFromArgb(scheme.background),
|
|
162
|
+
onBackground: hexFromArgb(scheme.onBackground),
|
|
163
|
+
|
|
164
|
+
outline: hexFromArgb(scheme.outline),
|
|
165
|
+
outlineVariant: hexFromArgb(scheme.outlineVariant),
|
|
166
|
+
|
|
167
|
+
shadow: hexFromArgb(scheme.shadow),
|
|
168
|
+
scrim: hexFromArgb(scheme.scrim),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Apply an MD3 dynamic color scheme to the document root as CSS custom properties.
|
|
174
|
+
* Sets both `--md-sys-color-*` tokens (used by components) and
|
|
175
|
+
* `--color-m3-*` tokens (used by Tailwind arbitrary values in apps).
|
|
176
|
+
*
|
|
177
|
+
* Also sets `data-theme` attribute for dark mode CSS selectors.
|
|
178
|
+
*/
|
|
179
|
+
export function applyTheme(
|
|
180
|
+
sourceColorHex: string,
|
|
181
|
+
mode: ThemeMode = "light",
|
|
182
|
+
root: HTMLElement = document.documentElement,
|
|
183
|
+
): void {
|
|
184
|
+
const resolved = resolveMode(mode);
|
|
185
|
+
const colors = generateM3Theme(sourceColorHex, resolved);
|
|
186
|
+
|
|
187
|
+
for (const [key, value] of Object.entries(colors)) {
|
|
188
|
+
const kebabKey = key.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
189
|
+
root.style.setProperty(`--md-sys-color-${kebabKey}`, value);
|
|
190
|
+
root.style.setProperty(`--color-m3-${kebabKey}`, value);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// data-theme is always "light" or "dark" — never "system"
|
|
194
|
+
root.setAttribute("data-theme", resolved);
|
|
195
|
+
}
|
package/src/lib/utils.ts
ADDED
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Re-export the MD3 Tailwind plugin from @bug-on/md3-tailwind.
|
|
2
|
+
// This allows users to reference the plugin directly from @bug-on/md3-react
|
|
3
|
+
// without installing @bug-on/md3-tailwind separately.
|
|
4
|
+
//
|
|
5
|
+
// Usage in CSS (Tailwind v4):
|
|
6
|
+
// @import "@bug-on/md3-react/index.css";
|
|
7
|
+
// @plugin "@bug-on/md3-react/plugin";
|
|
8
|
+
//
|
|
9
|
+
// Usage in tailwind.config.ts (Tailwind v3):
|
|
10
|
+
// import md3Plugin from "@bug-on/md3-react/plugin";
|
|
11
|
+
// export default { plugins: [md3Plugin] };
|
|
12
|
+
export { default } from "@bug-on/md3-tailwind";
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
3
|
+
import { describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { Button } from "../ui/button";
|
|
5
|
+
|
|
6
|
+
describe("Button Loading State", () => {
|
|
7
|
+
it("does not render LoadingIndicator by default", () => {
|
|
8
|
+
render(<Button>Submit</Button>);
|
|
9
|
+
expect(screen.queryByRole("progressbar")).not.toBeInTheDocument();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("renders LoadingIndicator when loading=true", () => {
|
|
13
|
+
render(<Button loading>Submit</Button>);
|
|
14
|
+
const progressbar = screen.getByRole("progressbar");
|
|
15
|
+
expect(progressbar).toBeInTheDocument();
|
|
16
|
+
expect(progressbar).toHaveAttribute("aria-label", "Loading");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("applies aria-busy and aria-disabled when loading", () => {
|
|
20
|
+
render(<Button loading>Submit</Button>);
|
|
21
|
+
const button = screen.getByRole("button", { name: "Submit" });
|
|
22
|
+
expect(button).toHaveAttribute("aria-busy", "true");
|
|
23
|
+
expect(button).toHaveAttribute("aria-disabled", "true");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("prevents interactions when loading", async () => {
|
|
27
|
+
const handleClick = vi.fn();
|
|
28
|
+
render(
|
|
29
|
+
<Button loading onClick={handleClick}>
|
|
30
|
+
Submit
|
|
31
|
+
</Button>,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const button = screen.getByRole("button", { name: "Submit" });
|
|
35
|
+
await userEvent.click(button);
|
|
36
|
+
|
|
37
|
+
expect(handleClick).not.toHaveBeenCalled();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("replaces leading icon with loading indicator", () => {
|
|
41
|
+
const FakeIcon = <svg data-testid="fake-icon" />;
|
|
42
|
+
|
|
43
|
+
const { rerender } = render(
|
|
44
|
+
<Button icon={FakeIcon} iconPosition="leading">
|
|
45
|
+
Submit
|
|
46
|
+
</Button>,
|
|
47
|
+
);
|
|
48
|
+
expect(screen.getByTestId("fake-icon")).toBeInTheDocument();
|
|
49
|
+
expect(screen.queryByRole("progressbar")).not.toBeInTheDocument();
|
|
50
|
+
|
|
51
|
+
rerender(
|
|
52
|
+
<Button loading icon={FakeIcon} iconPosition="leading">
|
|
53
|
+
Submit
|
|
54
|
+
</Button>,
|
|
55
|
+
);
|
|
56
|
+
expect(screen.queryByTestId("fake-icon")).not.toBeInTheDocument();
|
|
57
|
+
expect(screen.getByRole("progressbar")).toBeInTheDocument();
|
|
58
|
+
});
|
|
59
|
+
});
|