@bug-on/md3-react 0.1.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.
Files changed (96) hide show
  1. package/README.md +215 -0
  2. package/dist/assets/fonts/GoogleSansFlex-VariableFont.woff2 +0 -0
  3. package/dist/assets/fonts/MaterialSymbolsOutlined-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
  4. package/dist/assets/fonts/MaterialSymbolsRounded-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
  5. package/dist/assets/fonts/MaterialSymbolsSharp-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
  6. package/dist/assets/loading-indicator.svg +19 -0
  7. package/dist/assets/material-symbols-cdn.css +65 -0
  8. package/dist/assets/material-symbols-self-hosted.css +109 -0
  9. package/dist/hooks/index.d.ts +3 -0
  10. package/dist/hooks/useMediaQuery.d.ts +11 -0
  11. package/dist/hooks/useRipple.d.ts +26 -0
  12. package/dist/index.d.ts +65 -0
  13. package/dist/index.js +9059 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/index.mjs +8929 -0
  16. package/dist/index.mjs.map +1 -0
  17. package/dist/lib/material-symbols-preconnect.d.ts +42 -0
  18. package/dist/lib/theme-utils.d.ts +63 -0
  19. package/dist/lib/utils.d.ts +2 -0
  20. package/dist/material-symbols-cdn.css +65 -0
  21. package/dist/material-symbols-self-hosted.css +109 -0
  22. package/dist/types/index.d.ts +1 -0
  23. package/dist/types/md3.d.ts +14 -0
  24. package/dist/typography.css +22 -0
  25. package/dist/ui/badge.d.ts +125 -0
  26. package/dist/ui/button-group.d.ts +59 -0
  27. package/dist/ui/button.d.ts +148 -0
  28. package/dist/ui/card.d.ts +62 -0
  29. package/dist/ui/checkbox.d.ts +82 -0
  30. package/dist/ui/chip.d.ts +110 -0
  31. package/dist/ui/code-block.d.ts +14 -0
  32. package/dist/ui/dialog.d.ts +111 -0
  33. package/dist/ui/divider.d.ts +164 -0
  34. package/dist/ui/drawer.d.ts +39 -0
  35. package/dist/ui/dropdown.d.ts +29 -0
  36. package/dist/ui/fab-menu.d.ts +204 -0
  37. package/dist/ui/fab.d.ts +162 -0
  38. package/dist/ui/icon-button.d.ts +131 -0
  39. package/dist/ui/icon.d.ts +88 -0
  40. package/dist/ui/loading-indicator.d.ts +42 -0
  41. package/dist/ui/navigation-rail.d.ts +29 -0
  42. package/dist/ui/progress-indicator/circular.d.ts +3 -0
  43. package/dist/ui/progress-indicator/hooks.d.ts +3 -0
  44. package/dist/ui/progress-indicator/index.d.ts +21 -0
  45. package/dist/ui/progress-indicator/linear-flat.d.ts +10 -0
  46. package/dist/ui/progress-indicator/linear-wavy.d.ts +18 -0
  47. package/dist/ui/progress-indicator/linear.d.ts +3 -0
  48. package/dist/ui/progress-indicator/types.d.ts +151 -0
  49. package/dist/ui/progress-indicator/utils.d.ts +3 -0
  50. package/dist/ui/radio-button.d.ts +106 -0
  51. package/dist/ui/ripple.d.ts +126 -0
  52. package/dist/ui/scroll-area.d.ts +27 -0
  53. package/dist/ui/shared/constants.d.ts +86 -0
  54. package/dist/ui/shared/touch-target.d.ts +38 -0
  55. package/dist/ui/snackbar/index.d.ts +6 -0
  56. package/dist/ui/snackbar/snackbar.d.ts +196 -0
  57. package/dist/ui/switch/index.d.ts +7 -0
  58. package/dist/ui/switch/switch.d.ts +30 -0
  59. package/dist/ui/switch/switch.stories.d.ts +48 -0
  60. package/dist/ui/switch/switch.tokens.d.ts +67 -0
  61. package/dist/ui/switch/switch.types.d.ts +59 -0
  62. package/dist/ui/tabs/index.d.ts +10 -0
  63. package/dist/ui/tabs/tab.d.ts +43 -0
  64. package/dist/ui/tabs/tabs-content.d.ts +36 -0
  65. package/dist/ui/tabs/tabs-list.d.ts +40 -0
  66. package/dist/ui/tabs/tabs.d.ts +60 -0
  67. package/dist/ui/tabs/tabs.tokens.d.ts +94 -0
  68. package/dist/ui/tabs/tabs.types.d.ts +172 -0
  69. package/dist/ui/text-field/index.d.ts +11 -0
  70. package/dist/ui/text-field/subcomponents/active-indicator.d.ts +24 -0
  71. package/dist/ui/text-field/subcomponents/floating-label.d.ts +43 -0
  72. package/dist/ui/text-field/subcomponents/leading-icon.d.ts +23 -0
  73. package/dist/ui/text-field/subcomponents/outline-container.d.ts +42 -0
  74. package/dist/ui/text-field/subcomponents/prefix-suffix.d.ts +24 -0
  75. package/dist/ui/text-field/subcomponents/supporting-text.d.ts +37 -0
  76. package/dist/ui/text-field/subcomponents/trailing-icon.d.ts +41 -0
  77. package/dist/ui/text-field/text-field.d.ts +49 -0
  78. package/dist/ui/text-field/text-field.tokens.d.ts +76 -0
  79. package/dist/ui/text-field/text-field.types.d.ts +126 -0
  80. package/dist/ui/theme-provider/index.d.ts +18 -0
  81. package/dist/ui/toc.d.ts +74 -0
  82. package/dist/ui/tooltip/index.d.ts +8 -0
  83. package/dist/ui/tooltip/plain-tooltip.d.ts +2 -0
  84. package/dist/ui/tooltip/rich-tooltip.d.ts +2 -0
  85. package/dist/ui/tooltip/tooltip-box.d.ts +2 -0
  86. package/dist/ui/tooltip/tooltip-caret-shape.d.ts +9 -0
  87. package/dist/ui/tooltip/tooltip.tokens.d.ts +26 -0
  88. package/dist/ui/tooltip/tooltip.types.d.ts +56 -0
  89. package/dist/ui/tooltip/use-tooltip-position.d.ts +8 -0
  90. package/dist/ui/tooltip/use-tooltip-state.d.ts +2 -0
  91. package/dist/ui/typography/index.d.ts +16 -0
  92. package/dist/ui/typography/type-scale-tokens.d.ts +162 -0
  93. package/dist/ui/typography/typography-key-tokens.d.ts +40 -0
  94. package/dist/ui/typography/typography-tokens.d.ts +220 -0
  95. package/dist/ui/typography/typography.d.ts +265 -0
  96. package/package.json +80 -0
@@ -0,0 +1,42 @@
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
+ export declare function MaterialSymbolsPreconnect({ variants, }: MaterialSymbolsPreconnectProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,63 @@
1
+ export type ThemeMode = "light" | "dark";
2
+ export interface MD3ColorScheme {
3
+ primary: string;
4
+ onPrimary: string;
5
+ primaryContainer: string;
6
+ onPrimaryContainer: string;
7
+ inversePrimary: string;
8
+ primaryFixed: string;
9
+ primaryFixedDim: string;
10
+ onPrimaryFixed: string;
11
+ onPrimaryFixedVariant: string;
12
+ secondary: string;
13
+ onSecondary: string;
14
+ secondaryContainer: string;
15
+ onSecondaryContainer: string;
16
+ secondaryFixed: string;
17
+ secondaryFixedDim: string;
18
+ onSecondaryFixed: string;
19
+ onSecondaryFixedVariant: string;
20
+ tertiary: string;
21
+ onTertiary: string;
22
+ tertiaryContainer: string;
23
+ onTertiaryContainer: string;
24
+ tertiaryFixed: string;
25
+ tertiaryFixedDim: string;
26
+ onTertiaryFixed: string;
27
+ onTertiaryFixedVariant: string;
28
+ error: string;
29
+ onError: string;
30
+ errorContainer: string;
31
+ onErrorContainer: string;
32
+ surface: string;
33
+ onSurface: string;
34
+ surfaceVariant: string;
35
+ onSurfaceVariant: string;
36
+ surfaceTint: string;
37
+ surfaceContainerLowest: string;
38
+ surfaceContainerLow: string;
39
+ surfaceContainer: string;
40
+ surfaceContainerHigh: string;
41
+ surfaceContainerHighest: string;
42
+ inverseSurface: string;
43
+ inverseOnSurface: string;
44
+ background: string;
45
+ onBackground: string;
46
+ outline: string;
47
+ outlineVariant: string;
48
+ shadow: string;
49
+ scrim: string;
50
+ }
51
+ /**
52
+ * Generate a complete MD3 color scheme from a source color hex string.
53
+ * Uses the HCT color space algorithm — same as Material You on Android.
54
+ */
55
+ export declare function generateM3Theme(sourceColorHex: string, mode?: ThemeMode): MD3ColorScheme;
56
+ /**
57
+ * Apply an MD3 dynamic color scheme to the document root as CSS custom properties.
58
+ * Sets both `--md-sys-color-*` tokens (used by components) and
59
+ * `--color-m3-*` tokens (used by Tailwind arbitrary values in apps).
60
+ *
61
+ * Also sets `data-theme` attribute for dark mode CSS selectors.
62
+ */
63
+ export declare function applyTheme(sourceColorHex: string, mode?: ThemeMode, root?: HTMLElement): void;
@@ -0,0 +1,2 @@
1
+ import { type ClassValue } from "clsx";
2
+ export declare function cn(...inputs: ClassValue[]): string;
@@ -0,0 +1,65 @@
1
+ /*
2
+ * Material Symbols — CDN version (Google Fonts)
3
+ *
4
+ * USAGE IN APP'S HTML <head>:
5
+ * Add preconnect hints BEFORE this CSS loads for fastest performance:
6
+ *
7
+ * <link rel="preconnect" href="https://fonts.googleapis.com" />
8
+ * <link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
9
+ *
10
+ * Then import this CSS:
11
+ * import '@bug-on/md3-react/material-symbols-cdn.css'
12
+ *
13
+ * WHY TWO preconnect?
14
+ * Google Fonts serves the CSS stylesheet from fonts.googleapis.com
15
+ * but the actual .woff2 font files come from fonts.gstatic.com (different origin).
16
+ * The crossorigin attribute is required for font files (CORS request).
17
+ *
18
+ * FONT-DISPLAY NOTE for icon fonts:
19
+ * We use font-display: block (NOT swap) because:
20
+ * - swap would briefly show raw text like "arrow_forward" before icon loads = jarring layout shift
21
+ * - block hides text during load period (max 3s), then shows icon = cleaner UX
22
+ * - optional is NOT suitable: if font misses the load window, icons never appear
23
+ */
24
+
25
+ /*
26
+ * INSTRUCTION:
27
+ * Use the <MaterialSymbolsPreconnect /> component to load the actual Google Fonts CSS!
28
+ *
29
+ * We NO LONGER use @import url(...) here because CSS bundlers (like Next.js/Webpack)
30
+ * often merge global CSS files and push @import statements below regular CSS rules.
31
+ * According to CSS spec, any @import MUST be at the very top, so it breaks randomly.
32
+ * Loading via `<link rel="stylesheet">` (handled by MaterialSymbolsPreconnect) is fully robust.
33
+ */
34
+
35
+ /* ─────────────────────────────────────────────────────────────────────────────
36
+ * BASE ICON STYLES
37
+ * ─────────────────────────────────────────────────────────────────────────────
38
+ *
39
+ * This class ensures that Material Symbols render correctly as ligatures
40
+ * across all browsers and prevents common layout issues.
41
+ */
42
+ .md-icon {
43
+ font-family: inherit; /* Set dynamically by the Icon component */
44
+ font-weight: normal;
45
+ font-style: normal;
46
+ display: inline-flex;
47
+ align-items: center;
48
+ justify-content: center;
49
+ width: 1em;
50
+ height: 1em;
51
+ line-height: normal;
52
+ text-transform: none;
53
+ letter-spacing: normal;
54
+ word-wrap: normal;
55
+ white-space: nowrap;
56
+ direction: ltr;
57
+
58
+ /* Enable ligature rendering support for modern browsers */
59
+ font-feature-settings: "liga";
60
+
61
+ /* Improved rendering quality */
62
+ -webkit-font-smoothing: antialiased;
63
+ -moz-osx-font-smoothing: grayscale;
64
+ text-rendering: optimizeLegibility;
65
+ }
@@ -0,0 +1,109 @@
1
+ /*
2
+ * Material Symbols — Self-Hosted version
3
+ *
4
+ * SETUP STEPS:
5
+ *
6
+ * Step 1 — Download font files
7
+ * Truy cập: https://github.com/google/material-design-icons/tree/master/variablefont
8
+ * Download file: MaterialSymbolsOutlined[FILL,GRAD,opsz,wght].woff2
9
+ * (và Rounded / Sharp nếu cần)
10
+ *
11
+ * Step 2 — Place font files in your app
12
+ * Recommended location: public/fonts/ hoặc src/assets/fonts/
13
+ * Example: public/fonts/MaterialSymbolsOutlined.woff2
14
+ *
15
+ * Step 3 — Copy và điều chỉnh @font-face block bên dưới
16
+ * Thay đổi src url() để trỏ đúng đường dẫn trong project của bạn
17
+ *
18
+ * Step 4 — Import file này:
19
+ * import '@bug-on/md3-react/material-symbols-self-hosted.css'
20
+ *
21
+ * PERFORMANCE NOTES:
22
+ * - Self-hosting tốt hơn CDN khi bạn dùng CDN + HTTP/2 cho static assets
23
+ * - Nếu không có CDN, Google Fonts CDN có thể nhanh hơn do cache sharing
24
+ * - Dùng font-display: block cho icon fonts (xem lý do ở file CDN)
25
+ * - WOFF2 là format duy nhất cần thiết (100% modern browser support, Brotli compression)
26
+ * - Không cần WOFF fallback nữa
27
+ *
28
+ * SUBSETTING (QUAN TRỌNG để giảm file size):
29
+ * Full variable font ~295 KB. Sau khi subset còn ~1-2 KB cho vài chục icons.
30
+ * Dùng tool: https://everythingfonts.com/subsetter
31
+ * Hoặc dùng Google Fonts text parameter để subset trên CDN:
32
+ * ?family=Material+Symbols+Outlined&text=home%20search%20settings
33
+ *
34
+ * GDPR NOTE:
35
+ * Self-hosting loại bỏ việc gửi user IP đến Google servers,
36
+ * giải quyết GDPR compliance requirement.
37
+ */
38
+
39
+ @font-face {
40
+ font-family: "Material Symbols Outlined";
41
+ font-style: normal;
42
+ /* Khai báo full axis range để browser biết đây là variable font */
43
+ font-weight: 100 700;
44
+ /* font-display: block là bắt buộc cho icon fonts:
45
+ * - Tránh render text thô "arrow_forward" thay vì icon
46
+ * - Max 3s block, sau đó icon xuất hiện
47
+ * Tham khảo: https://web.dev/articles/font-best-practices#icon_fonts */
48
+ font-display: block;
49
+ src: url("/fonts/MaterialSymbolsOutlined[FILL,GRAD,opsz,wght].woff2")
50
+ format("woff2");
51
+ /* unicode-range: chỉ load font khi page chứa ký tự trong range này.
52
+ * Material Symbols dùng PUA (Private Use Area) cho ligatures. */
53
+ }
54
+
55
+ /*
56
+ * Rounded variant — uncomment nếu dùng
57
+ *
58
+ @font-face {
59
+ font-family: 'Material Symbols Rounded';
60
+ font-style: normal;
61
+ font-weight: 100 700;
62
+ font-display: block;
63
+ src: url('/fonts/MaterialSymbolsRounded[FILL,GRAD,opsz,wght].woff2') format('woff2');
64
+ }
65
+ */
66
+
67
+ /*
68
+ * Sharp variant — uncomment nếu dùng
69
+ *
70
+ @font-face {
71
+ font-family: 'Material Symbols Sharp';
72
+ font-style: normal;
73
+ font-weight: 100 700;
74
+ font-display: block;
75
+ src: url('/fonts/MaterialSymbolsSharp[FILL,GRAD,opsz,wght].woff2') format('woff2');
76
+ }
77
+ */
78
+
79
+ /* ─────────────────────────────────────────────────────────────────────────────
80
+ * BASE ICON STYLES
81
+ * ─────────────────────────────────────────────────────────────────────────────
82
+ *
83
+ * This class ensures that Material Symbols render correctly as ligatures
84
+ * across all browsers and prevents common layout issues.
85
+ */
86
+ .md-icon {
87
+ font-family: inherit; /* Set dynamically by the Icon component */
88
+ font-weight: normal;
89
+ font-style: normal;
90
+ display: inline-flex;
91
+ align-items: center;
92
+ justify-content: center;
93
+ width: 1em;
94
+ height: 1em;
95
+ line-height: normal;
96
+ text-transform: none;
97
+ letter-spacing: normal;
98
+ word-wrap: normal;
99
+ white-space: nowrap;
100
+ direction: ltr;
101
+
102
+ /* Enable ligature rendering support for modern browsers */
103
+ font-feature-settings: "liga";
104
+
105
+ /* Improved rendering quality */
106
+ -webkit-font-smoothing: antialiased;
107
+ -moz-osx-font-smoothing: grayscale;
108
+ text-rendering: optimizeLegibility;
109
+ }
@@ -0,0 +1 @@
1
+ export type { MD3ColorStyle, MD3Shape, MD3Size, PolymorphicProps, PolymorphicRef, } from "./md3";
@@ -0,0 +1,14 @@
1
+ import type { ComponentPropsWithoutRef, ComponentPropsWithRef, ElementType, ReactNode } from "react";
2
+ /** MD3 button color variants */
3
+ export type MD3ColorStyle = "elevated" | "filled" | "tonal" | "outlined" | "text";
4
+ /** MD3 Expressive button sizes */
5
+ export type MD3Size = "xs" | "sm" | "md" | "lg" | "xl";
6
+ /** MD3 shape families */
7
+ export type MD3Shape = "round" | "square";
8
+ /** Helper: PolyMorphic component ref */
9
+ export type PolymorphicRef<C extends ElementType> = ComponentPropsWithRef<C>["ref"];
10
+ /** Helper: Props cho polymorphic components */
11
+ export type PolymorphicProps<C extends ElementType, Props = object> = Props & Omit<ComponentPropsWithoutRef<C>, keyof Props> & {
12
+ as?: C;
13
+ children?: ReactNode;
14
+ };
@@ -0,0 +1,22 @@
1
+ /* MD3 Expressive Typography - Google Sans Flex Variable Font */
2
+
3
+ @font-face {
4
+ font-family: "Google Sans Flex";
5
+ src: url('./assets/fonts/GoogleSansFlex-VariableFont.woff2')
6
+ format("woff2");
7
+ font-weight: 100 1000;
8
+ font-style: normal;
9
+ font-display: swap;
10
+ }
11
+
12
+ /**
13
+ * Applies Google Sans Flex with ROND axis at 100 (maximum roundness)
14
+ * as required by MD3 Expressive design spec.
15
+ *
16
+ * Usage: Add this class to any element or the root element to apply
17
+ * the MD3 Expressive typography defaults.
18
+ */
19
+ .font-md3-expressive {
20
+ font-family: "Google Sans Flex", system-ui, sans-serif;
21
+ font-variation-settings: "ROND" 100;
22
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * @file badge.tsx
3
+ *
4
+ * MD3 Expressive Badge component.
5
+ *
6
+ * - `Badge` → A small status indicator. Can be a dot (no content) or labeled (with content).
7
+ * - `BadgedBox` → Positions a Badge at the top-trailing corner of an anchor element.
8
+ *
9
+ * @remarks
10
+ * Token references (Kotlin source):
11
+ * BadgeTokens — Size=6dp (→ 6px), LargeSize=16dp (→ 16px), Shape=CornerFull, Color=Error,
12
+ * LargeLabelTextFont=LabelSmall, LargeLabelTextColor=OnError
13
+ *
14
+ * BadgedBox offsets:
15
+ * - Small (dot): BadgeOffset = 6dp → translate(50%, -50%)
16
+ * - Large (text): HOffset=12dp, VOffset=14dp → translate(35%, -35%)
17
+ *
18
+ * Architecture:
19
+ * - Styling: `cn` (clsx/tailwind-merge) + static Tailwind classes
20
+ * - Animation: Framer Motion (`LazyMotion` + `domMax`) spring mount/unmount
21
+ * - A11y: `role="status"` with `aria-label`, decorative dots use `aria-hidden="true"`
22
+ *
23
+ * @see https://m3.material.io/components/badge/overview
24
+ */
25
+ import * as React from "react";
26
+ type SafeHTMLSpanAttrs = Omit<React.HTMLAttributes<HTMLSpanElement>, "onDrag" | "onDragStart" | "onDragEnd" | "onDragEnter" | "onDragLeave" | "onDragOver" | "onDrop">;
27
+ export interface BadgeProps extends SafeHTMLSpanAttrs {
28
+ /**
29
+ * The content to display inside the badge.
30
+ * - Omitted / undefined → renders as a small 6×6px dot (decorative).
31
+ * - string | number → renders as a large badge (min 16px height) with label.
32
+ *
33
+ * Numbers exceeding `max` are displayed as `{max}+`.
34
+ * Strings longer than 4 characters are truncated to 4.
35
+ */
36
+ children?: React.ReactNode;
37
+ /**
38
+ * Maximum numeric value to display before appending "+".
39
+ * Only applies when `children` is a number.
40
+ * @example max={99} + children={150} → "99+"
41
+ */
42
+ max?: number;
43
+ /**
44
+ * Override the background (container) color.
45
+ * Accepts any valid CSS color value.
46
+ * Defaults to MD3 `error` token — `bg-m3-error`.
47
+ */
48
+ containerColor?: string;
49
+ /**
50
+ * Override the text/content color.
51
+ * Accepts any valid CSS color value.
52
+ * Defaults to MD3 `on-error` token — `text-m3-on-error`.
53
+ */
54
+ contentColor?: string;
55
+ }
56
+ export interface BadgedBoxProps {
57
+ /**
58
+ * The badge element to overlay on the anchor.
59
+ * Typically a `<Badge />`.
60
+ */
61
+ badge: React.ReactNode;
62
+ /**
63
+ * The anchor content that the badge is attached to.
64
+ */
65
+ children: React.ReactNode;
66
+ /**
67
+ * Additional className applied to the outer wrapper `span`.
68
+ */
69
+ className?: string;
70
+ /**
71
+ * Explicitly override size detection for badge positioning.
72
+ * - `'small'` → BadgeOffset = 6dp → translate(50%, -50%)
73
+ * - `'large'` → HOffset=12dp/VOffset=14dp → translate(35%, -35%)
74
+ * When omitted, BadgedBox auto-detects by inspecting `badge` children prop.
75
+ */
76
+ badgeSize?: "small" | "large";
77
+ }
78
+ /**
79
+ * MD3 Expressive Badge — dynamic status indicator.
80
+ *
81
+ * @example
82
+ * ```tsx
83
+ * // Small dot badge (no content) — decorative
84
+ * <Badge />
85
+ *
86
+ * // Large badge with number (truncated at max)
87
+ * <Badge max={99}>150</Badge>
88
+ * // → displays "99+"
89
+ *
90
+ * // Large badge with text label
91
+ * <Badge>NEW</Badge>
92
+ *
93
+ * // Custom colors
94
+ * <Badge containerColor="#6750A4" contentColor="#FFFFFF">3</Badge>
95
+ * ```
96
+ *
97
+ * @see https://m3.material.io/components/badge/overview
98
+ */
99
+ export declare const Badge: React.NamedExoticComponent<BadgeProps & React.RefAttributes<HTMLSpanElement>>;
100
+ /**
101
+ * MD3 BadgedBox — positions a Badge at the top-trailing corner of an anchor.
102
+ *
103
+ * Implements MD3 offset specs from Badge.kt:
104
+ * - Small badge (dot): `BadgeOffset = 6dp` → translate(50%, -50%)
105
+ * - Large badge (text): `BadgeWithContentHorizontalOffset = 12dp` / `VerticalOffset = 14dp`
106
+ * → translate(35%, -35%)
107
+ *
108
+ * Auto-detects badge size by inspecting the badge element's children prop,
109
+ * or accepts an explicit `badgeSize` override.
110
+ *
111
+ * @example
112
+ * ```tsx
113
+ * // Small dot on mail icon
114
+ * <BadgedBox badge={<Badge />}>
115
+ * <Icon name="mail" />
116
+ * </BadgedBox>
117
+ *
118
+ * // Count badge on notification icon
119
+ * <BadgedBox badge={<Badge max={99}>{count}</Badge>}>
120
+ * <Icon name="notifications" />
121
+ * </BadgedBox>
122
+ * ```
123
+ */
124
+ export declare function BadgedBox({ badge, children, className, badgeSize, }: BadgedBoxProps): import("react/jsx-runtime").JSX.Element;
125
+ export {};
@@ -0,0 +1,59 @@
1
+ import * as React from "react";
2
+ /**
3
+ * Thuộc tính truyền vào cho thành phần nhóm nút (Button Group).
4
+ */
5
+ export interface ButtonGroupProps extends React.FieldsetHTMLAttributes<HTMLFieldSetElement> {
6
+ /**
7
+ * Cấu trúc hiển thị của nhóm nút:
8
+ * - `standard`: Các nút cách xa và có khoảng cách độc lập với nhau (gap).
9
+ * - `connected`: Các nút nối liền khung viền với nhau để tạo thành dạng Segmented Button.
10
+ * @default "standard"
11
+ */
12
+ variant?: "standard" | "connected";
13
+ /**
14
+ * Hướng sắp xếp của các nút trong nhóm.
15
+ * @default "horizontal"
16
+ */
17
+ orientation?: "horizontal" | "vertical";
18
+ /**
19
+ * Đặt thành `true` nếu bạn muốn nhóm hiển thị dạng `standard` giãn đều lấp đầy toàn bộ khu vực chứa (container).
20
+ * @default false
21
+ */
22
+ fullWidth?: boolean;
23
+ /**
24
+ * Áp dụng thống nhất chung một kích thước (`size`) cho tất cả các con trong nhóm (ghi đè kích thước lẻ từng nút).
25
+ */
26
+ size?: "xs" | "sm" | "md" | "lg" | "xl";
27
+ /**
28
+ * Bật/tắt hiệu ứng thu phóng độ rộng / khoảng đệm (Morphing Width) khi nhấn vào các nút (áp dụng cho nhóm `standard`).
29
+ * @default true
30
+ */
31
+ morphingWidth?: boolean;
32
+ /**
33
+ * Tự động hiển thị biểu tượng (icon) Check khi một nút trạng thái nằm trong nhóm được chỉ định là `selected={true}`.
34
+ * @default false
35
+ */
36
+ showCheck?: boolean;
37
+ }
38
+ /**
39
+ * Component Nhóm Nút (Button Group) được sử dụng để gom nhóm nhiều nút có công năng tương tự lại với nhau.
40
+ * Hỗ trợ tạo các bộ nút độc lập (Standard) hoặc khối liên kết liền mạch (Connected/Segmented Buttons).
41
+ * Kế thừa cơ chế chuyển động kết hợp liền mạch của MD3 Expressive.
42
+ *
43
+ * @example
44
+ * ```tsx
45
+ * // Nhóm nút tiêu chuẩn rời rạc (Standard)
46
+ * <ButtonGroup variant="standard">
47
+ * <Button>Lựa chọn 1</Button>
48
+ * <Button>Lựa chọn 2</Button>
49
+ * </ButtonGroup>
50
+ *
51
+ * // Nhóm nút liền khối (Connected Segmented Button)
52
+ * <ButtonGroup variant="connected" showCheck fullWidth>
53
+ * <Button variant="toggle" selected={view === "day"} onClick={() => setView("day")}>Ngày</Button>
54
+ * <Button variant="toggle" selected={view === "week"} onClick={() => setView("week")}>Tuần</Button>
55
+ * <Button variant="toggle" selected={view === "month"} onClick={() => setView("month")}>Tháng</Button>
56
+ * </ButtonGroup>
57
+ * ```
58
+ */
59
+ export declare const ButtonGroup: React.NamedExoticComponent<ButtonGroupProps & React.RefAttributes<HTMLFieldSetElement>>;
@@ -0,0 +1,148 @@
1
+ /**
2
+ * @file button.tsx
3
+ *
4
+ * MD3 Expressive Button component.
5
+ *
6
+ * Spec: https://m3.material.io/components/buttons/overview
7
+ * Sizing (May 2025):
8
+ * XS → h:32dp | px: 12dp | icon: 18dp | gap: 8dp
9
+ * SM → h:40dp | px: 16dp | icon: 20dp | gap: 8dp
10
+ * MD → h:56dp | px: 24dp | icon: 24dp | gap: 8dp
11
+ * LG → h:96dp | px: 48dp | icon: 32dp | gap: 12dp
12
+ * XL → h:136dp | px: 48dp | icon: 40dp | gap: 12dp
13
+ */
14
+ import type { HTMLMotionProps } from "motion/react";
15
+ import * as React from "react";
16
+ type MotionButtonProps = Omit<HTMLMotionProps<"button">, "children" | "color">;
17
+ /**
18
+ * Base props shared between the standard and toggle button variants.
19
+ *
20
+ * @see {@link ButtonProps} for the complete discriminated union type.
21
+ * @see https://m3.material.io/components/buttons/overview
22
+ */
23
+ export interface BaseButtonProps extends MotionButtonProps {
24
+ /**
25
+ * Visual style variant following MD3 color roles.
26
+ * @default "filled"
27
+ */
28
+ colorStyle?: "elevated" | "filled" | "tonal" | "outlined" | "text";
29
+ /**
30
+ * Color style applied when the toggle button is in the *selected* state.
31
+ * Only meaningful when `variant="toggle"`.
32
+ * Falls back to `"filled"` when not specified.
33
+ */
34
+ selectedColorStyle?: "elevated" | "filled" | "tonal" | "outlined" | "text";
35
+ /**
36
+ * Button size following MD3 Expressive size scale.
37
+ * @default "sm"
38
+ */
39
+ size?: "xs" | "sm" | "md" | "lg" | "xl";
40
+ /**
41
+ * Container shape — controls border-radius morphing.
42
+ * - `round`: pill shape (CornerFull), morphs to rounded-square when toggled.
43
+ * - `square`: rounded-square, morphs to pill when toggled.
44
+ * @default "round"
45
+ */
46
+ shape?: "round" | "square";
47
+ /**
48
+ * Optional leading or trailing icon node.
49
+ * Size is automatically scaled to match the button's `size` prop.
50
+ */
51
+ icon?: React.ReactNode;
52
+ /**
53
+ * Position of the icon relative to the label text.
54
+ * @default "leading"
55
+ */
56
+ iconPosition?: "leading" | "trailing";
57
+ /**
58
+ * When `true`, replaces the icon with an animated loading indicator
59
+ * and prevents interaction.
60
+ * @default false
61
+ */
62
+ loading?: boolean;
63
+ /**
64
+ * Controls which loading spinner is shown while `loading={true}`.
65
+ * - `loading-indicator`: MD3 Expressive morphing shape (default).
66
+ * - `circular`: Classic circular spinner.
67
+ * @default "loading-indicator"
68
+ */
69
+ loadingVariant?: "loading-indicator" | "circular";
70
+ /**
71
+ * When `true`, the Button renders its child element directly (using Radix Slot),
72
+ * merging all button props (className, style, event handlers) onto it.
73
+ * Useful for rendering a Next.js `<Link>` with Button styles.
74
+ *
75
+ * @example
76
+ * ```tsx
77
+ * <Button asChild size="lg">
78
+ * <Link href="/components">Explore Components</Link>
79
+ * </Button>
80
+ * ```
81
+ * @default false
82
+ */
83
+ asChild?: boolean;
84
+ /** Button label — any React content, typically a string. */
85
+ children: React.ReactNode;
86
+ }
87
+ /**
88
+ * Complete `Button` props — discriminated union that enforces
89
+ * `selected` is only valid for `variant="toggle"`.
90
+ *
91
+ * @example
92
+ * ```tsx
93
+ * // Standard button
94
+ * <Button colorStyle="filled" size="md">Confirm</Button>
95
+ *
96
+ * // Toggle button (selected state required)
97
+ * <Button variant="toggle" selected={isActive} onClick={toggle}>Filter</Button>
98
+ *
99
+ * // With leading icon and loading state
100
+ * <Button icon={<CheckIcon />} loading={isSubmitting}>Save</Button>
101
+ * ```
102
+ *
103
+ * @see https://m3.material.io/components/buttons/overview
104
+ */
105
+ export type ButtonProps = BaseButtonProps & ({
106
+ variant?: "default";
107
+ selected?: never;
108
+ } | {
109
+ variant: "toggle";
110
+ selected: boolean;
111
+ });
112
+ /**
113
+ * MD3 Expressive Button component.
114
+ *
115
+ * Supports all five MD3 color styles, five sizes, shape morphing on toggle,
116
+ * leading/trailing icons, and an animated loading state.
117
+ *
118
+ * @remarks
119
+ * - `variant="toggle"` requires `selected: boolean` — enforced by the type system.
120
+ * - When `loading={true}`, the button is visually dimmed, pointer events are
121
+ * blocked, and `aria-busy` is set for screen readers.
122
+ * - Shape morphs smoothly between pill ↔ rounded-square when toggle state changes,
123
+ * using a critically-damped spring (no overshoot artefacts).
124
+ *
125
+ * @example
126
+ * ```tsx
127
+ * // Standard filled button
128
+ * <Button colorStyle="filled" size="md">Confirm</Button>
129
+ *
130
+ * // Button with icon
131
+ * <Button icon={<CheckIcon />} loading={isSubmitting}>Save</Button>
132
+ *
133
+ * // Toggle button
134
+ * <Button variant="toggle" selected={isActive} onClick={toggle}>
135
+ * Filter
136
+ * </Button>
137
+ * ```
138
+ *
139
+ * @see https://m3.material.io/components/buttons/overview
140
+ */
141
+ export declare const Button: React.NamedExoticComponent<(Omit<BaseButtonProps & {
142
+ variant?: "default";
143
+ selected?: never;
144
+ }, "ref"> | Omit<BaseButtonProps & {
145
+ variant: "toggle";
146
+ selected: boolean;
147
+ }, "ref">) & React.RefAttributes<HTMLButtonElement>>;
148
+ export {};