@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,21 @@
1
+ import * as React from "react";
2
+ import type { ProgressIndicatorProps } from "./types";
3
+ /**
4
+ * Thanh tiến trình (Progress Indicator) theo nguyên tắc Material Design 3 Expressive.
5
+ * Hỗ trợ linh hoạt 2 loại hình hiển thị: Linear (Đường thẳng) và Circular (Hình tròn),
6
+ * cùng với thiết kế Wavy (Lượn sóng) động rất mềm mại.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * // Determinate Flat Linear
11
+ * <ProgressIndicator variant="linear" value={60} aria-label="Loading profile..." />
12
+ *
13
+ * // Indeterminate Wavy Linear
14
+ * <ProgressIndicator variant="linear" shape="wavy" aria-label="Connecting to server..." />
15
+ *
16
+ * // Determinate Flat Circular
17
+ * <ProgressIndicator variant="circular" value={45} size={48} aria-label="Syncing..." />
18
+ * ```
19
+ */
20
+ export declare const ProgressIndicator: React.ForwardRefExoticComponent<ProgressIndicatorProps & React.RefAttributes<HTMLDivElement>>;
21
+ export type { CircularProgressProps, LinearProgressProps, ProgressIndicatorProps, } from "./types";
@@ -0,0 +1,10 @@
1
+ import * as React from "react";
2
+ export declare const FlatLinearTrack: React.NamedExoticComponent<{
3
+ trackHeight: number;
4
+ activeColor: string;
5
+ trackColor: string;
6
+ value?: number;
7
+ isRtl: boolean;
8
+ gapSize: number;
9
+ crawlerSpeed: number;
10
+ }>;
@@ -0,0 +1,18 @@
1
+ import * as React from "react";
2
+ export declare const WavyLinearTrack: React.NamedExoticComponent<{
3
+ trackHeight: number;
4
+ svgHeight: number;
5
+ amplitude: number;
6
+ wavelength: number;
7
+ indeterminateWavelength: number;
8
+ activeColor: string;
9
+ trackColor: string;
10
+ value?: number;
11
+ isRtl: boolean;
12
+ gapSize: number;
13
+ waveSpeed: number;
14
+ crawlerSpeed: number;
15
+ determinateAnimation: "md3" | "continuous";
16
+ indeterminateAnimation: "md3" | "continuous";
17
+ trackShape: "flat" | "wavy";
18
+ }>;
@@ -0,0 +1,3 @@
1
+ import * as React from "react";
2
+ import type { LinearProgressProps } from "./types";
3
+ export declare const LinearProgress: React.ForwardRefExoticComponent<LinearProgressProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,151 @@
1
+ import type * as React from "react";
2
+ export interface ProgressBaseProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
3
+ /**
4
+ * Giá trị phần trăm tiến trình hiện tại (từ 0 đến 100).
5
+ * Nếu truyền giá trị này, tiến trình sẽ hiển thị ở trạng thái Determinate.
6
+ * Nếu bỏ trống (undefined), tiến trình sẽ tự động ở trạng thái Indeterminate.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * <ProgressIndicator value={45} aria-label="Loading profile..." />
11
+ * ```
12
+ */
13
+ value?: number;
14
+ /**
15
+ * Nhan đề mô tả mục đích của thanh tiến trình dành cho screen readers (bắt buộc).
16
+ */
17
+ "aria-label": string;
18
+ /**
19
+ * Độ dày của track (đường nền) của thanh tiến trình (đơn vị: px).
20
+ * - Với Linear: là chiều cao của thanh.
21
+ * - Với Circular: là độ dày của viền hình tròn.
22
+ *
23
+ * @example
24
+ * trackHeight={8} // Dày hơn bình thường
25
+ */
26
+ trackHeight?: number;
27
+ /**
28
+ * Màu sắc của phần tiến trình (phần đã hoàn thành).
29
+ * Mặc định mượn màu `currentColor` của thẻ wrap, để dễ dàng tuỳ biến qua utility class.
30
+ *
31
+ * @example
32
+ * color="var(--md-sys-color-primary)" // Sử dụng custom token
33
+ */
34
+ color?: string;
35
+ /**
36
+ * Màu sắc của thanh nền (phần chưa hoàn thành).
37
+ * Mặc định sử dụng màu được tính toán từ bề mặt hoặc transparency để tạo sự tinh tế.
38
+ */
39
+ trackColor?: string;
40
+ }
41
+ export interface LinearProgressProps extends ProgressBaseProps {
42
+ /** Phân loại component sang kiểu dáng Linear (đường thẳng ngang). */
43
+ variant: "linear";
44
+ /**
45
+ * Hình dáng của vạch tiến trình (phần đã hoàn thành).
46
+ * - `flat`: Đường nét liền phẳng (mặc định)
47
+ * - `wavy`: Đường lượn sóng động
48
+ */
49
+ shape?: "flat" | "wavy";
50
+ /**
51
+ * Hình dáng của thanh nền chờ (track).
52
+ * - `flat`: Đường nét liền phẳng (mặc định)
53
+ * - `wavy`: Đường lượn sóng cố định hoặc động
54
+ */
55
+ trackShape?: "flat" | "wavy";
56
+ /**
57
+ * Biên độ sóng (áp dụng khi `shape` hoặc `trackShape` là "wavy").
58
+ * Chỉ định độ lượn cao thấp của con sóng.
59
+ */
60
+ amplitude?: number;
61
+ /**
62
+ * Chiều dài một nhịp sóng (áp dụng khi `shape` là "wavy" theo determinate).
63
+ * Khoảng cách giữa 2 đỉnh sóng kề nhau.
64
+ */
65
+ wavelength?: number;
66
+ /**
67
+ * Nhịp sóng dành riêng cho trạng thái chạy liên tục (Indeterminate Wavy).
68
+ */
69
+ indeterminateWavelength?: number;
70
+ /**
71
+ * Khoảng hở kết dính giữa cụm vạch đang chạy và thanh nền.
72
+ * Cho phép truyền \`0\` để hai thanh chạm sát vào nhau liền mạch.
73
+ *
74
+ * @example
75
+ * ```tsx
76
+ * <ProgressIndicator variant="linear" shape="wavy" gapSize={0} /> // Sóng chạm track liền nét
77
+ * ```
78
+ */
79
+ gapSize?: number;
80
+ /**
81
+ * Tốc độ dao động vỗ của sóng (Multiplier). Mặc định là \`1\`.
82
+ * Tăng giá trị (VD: 1.5, 2) để sóng dao động nhanh hơn.
83
+ */
84
+ waveSpeed?: number;
85
+ /**
86
+ * Tốc độ lướt / cuộn dải màu dọc trên track (Crawler) đối với trạng thái Indeterminate.
87
+ * Mặc định là \`1\`.
88
+ */
89
+ crawlerSpeed?: number;
90
+ /**
91
+ * Cấu hình hiệu ứng gợn khi thanh determinate ở mức xấp xỉ mép viền (<= 10% hoặc >= 90%).
92
+ * - `md3`: Tự động ép phẳng biên độ sóng thành số 0 một cách mềm mại (Chuẩn Google MD3).
93
+ * - `continuous`: Bỏ qua các ràng buộc ép phẳng, con sóng vẫn gợn lấp lóa trên mọi giá trị phần trăm.
94
+ *
95
+ * @example
96
+ * ```tsx
97
+ * <ProgressIndicator variant="linear" shape="wavy" determinateAnimation="continuous" />
98
+ * ```
99
+ */
100
+ determinateAnimation?: "md3" | "continuous";
101
+ /**
102
+ * Kiểu kịch bản tịnh tiến cho thanh Indeterminate.
103
+ * - `md3`: Render 2 vạch song song vọt tới & co giãn mô phỏng vật lý (Chuẩn Google MD3).
104
+ * - `continuous`: Vạch không ngắt quãng mà tràn lướt vòng lặp mượt mà.
105
+ *
106
+ * @example
107
+ * ```tsx
108
+ * <ProgressIndicator variant="linear" indeterminateAnimation="continuous" />
109
+ * ```
110
+ */
111
+ indeterminateAnimation?: "md3" | "continuous";
112
+ /**
113
+ * Bật/tắt dấm chấm điểm báo kết thúc ở cuối track (Stop Indicator).
114
+ * - `true`: Luôn thấy một chấm tròn bé xíu ở cuối đường đi
115
+ * - `false`: Tắt hoàn toàn
116
+ * - `"auto"`: Chấm tròn chỉ hiển thị và hòa trộn khi progress đạt 100%
117
+ */
118
+ showStopIndicator?: boolean | "auto";
119
+ }
120
+ export interface CircularProgressProps extends ProgressBaseProps {
121
+ /** Phân loại component sang kiểu dáng Circular (hình tròn khép kín). */
122
+ variant: "circular";
123
+ /**
124
+ * Đường kính hiển thị của vòng biểu đồ, đơn vị px.
125
+ *
126
+ * @example
127
+ * ```tsx
128
+ * <ProgressIndicator variant="circular" size={48} aria-label="Loading..." />
129
+ * ```
130
+ */
131
+ size?: number;
132
+ /**
133
+ * Phong cách nét vẽ của đường viền trong trạng thái trượt.
134
+ * - `flat`: Đường nét cứng, vuốt tròn hai đầu stroke.
135
+ * - `wavy`: Vệt màu chuyển động rung tạo hình răng cưa/gợn sóng.
136
+ */
137
+ shape?: "flat" | "wavy";
138
+ /**
139
+ * Biên độ gợn của trạng thái `wavy` cho vòng tròn.
140
+ */
141
+ amplitude?: number;
142
+ /** Bước sóng bao trọn chu vi hình tròn. */
143
+ wavelength?: number;
144
+ /** Khoảng hở khe đứt ngang nối đỉnh nét vẽ. */
145
+ gapSize?: number;
146
+ /**
147
+ * Xoay nhanh hay chậm theo số nhân cho Crawler Circular quay Indeterminate.
148
+ */
149
+ crawlerSpeed?: number;
150
+ }
151
+ export type ProgressIndicatorProps = LinearProgressProps | CircularProgressProps;
@@ -0,0 +1,3 @@
1
+ export declare function easeInOutCubic(x: number): number;
2
+ export declare function generateWavyCircularPath(center: number, radius: number, amplitude: number, wavelength: number): string;
3
+ export declare function getSinePath(startX: number, endX: number, phase: number, wl: number, amp: number): string;
@@ -0,0 +1,106 @@
1
+ /**
2
+ * @file radio-button.tsx
3
+ * MD3 Expressive RadioButton — single-select with RadioGroup support.
4
+ * Spec: https://m3.material.io/components/radio-button/overview
5
+ */
6
+ import * as React from "react";
7
+ /**
8
+ * Color variant for `RadioButton`.
9
+ * - `"primary"` — standard selection (default)
10
+ * - `"error"` — error/invalid state
11
+ */
12
+ export type RadioButtonColors = "primary" | "error";
13
+ /** Props for `RadioButton`. */
14
+ export interface RadioButtonProps {
15
+ /** Whether this radio is selected. */
16
+ selected?: boolean;
17
+ /** Initial selected state (uncontrolled). @default false */
18
+ defaultSelected?: boolean;
19
+ /** Called when user clicks. Pass `null` to disable interaction. */
20
+ onClick?: (() => void) | null;
21
+ /** Disables the radio — visual disabled state + no interaction. @default false */
22
+ disabled?: boolean;
23
+ /** Color variant. @default "primary" */
24
+ color?: RadioButtonColors;
25
+ /** Error state — changes colors to `m3-error`. @default false */
26
+ error?: boolean;
27
+ /** Adjacent label text. Renders a `<label>` wrapper. */
28
+ label?: string;
29
+ /** Value used for form submission. */
30
+ value?: string;
31
+ /** Name for grouping (used in RadioGroup context). */
32
+ name?: string;
33
+ /** ID for the hidden `<input>`. Auto-generated when `label` is set. */
34
+ id?: string;
35
+ /** Extra class names on the outermost wrapper. */
36
+ className?: string;
37
+ /** ARIA label for the radio when no visible label exists. */
38
+ "aria-label"?: string;
39
+ "aria-labelledby"?: string;
40
+ "aria-describedby"?: string;
41
+ /** Whether the radio is required for form submission. */
42
+ required?: boolean;
43
+ /** Ref to the hidden `<input type="radio">`. */
44
+ ref?: React.Ref<HTMLInputElement>;
45
+ }
46
+ /** Props for `RadioGroup`. */
47
+ export interface RadioGroupProps {
48
+ /** The name attribute shared across all child RadioButtons. */
49
+ name: string;
50
+ /** The currently selected value (controlled). */
51
+ value?: string;
52
+ /** Default value (uncontrolled). */
53
+ defaultValue?: string;
54
+ /** Called when selection changes. */
55
+ onValueChange?: (value: string) => void;
56
+ /** Disables all radio buttons in the group. */
57
+ disabled?: boolean;
58
+ /** Error state for the entire group. */
59
+ error?: boolean;
60
+ /** Label for the group (renders as visually hidden or visible heading). */
61
+ label?: string;
62
+ /** ID of an external element that labels this group. */
63
+ "aria-labelledby"?: string;
64
+ /** Direction of layout. @default "vertical" */
65
+ orientation?: "horizontal" | "vertical";
66
+ /** Whether at least one radio in the group must be selected. */
67
+ required?: boolean;
68
+ children: React.ReactNode;
69
+ className?: string;
70
+ }
71
+ /**
72
+ * MD3 Expressive RadioButton component.
73
+ *
74
+ * Single-select control. Supports standalone (controlled/uncontrolled) and
75
+ * `RadioGroup` context. Animated per MD3 spec: inner dot radius morph,
76
+ * outer ring color transition, state layer, and ripple.
77
+ *
78
+ * @example
79
+ * ```tsx
80
+ * <RadioButton selected={isSelected} onClick={() => setSelected(true)} label="Option A" />
81
+ *
82
+ * <RadioGroup name="plan" value={plan} onValueChange={setPlan}>
83
+ * <RadioButton value="free" label="Free" />
84
+ * <RadioButton value="pro" label="Pro" />
85
+ * </RadioGroup>
86
+ * ```
87
+ * @see https://m3.material.io/components/radio-button/overview
88
+ */
89
+ export declare const RadioButton: React.NamedExoticComponent<Omit<RadioButtonProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
90
+ /**
91
+ * MD3 Expressive RadioGroup component.
92
+ *
93
+ * Groups multiple `RadioButton` components under a shared `name` with keyboard
94
+ * navigation (Arrow keys with wrapping) and ARIA `radiogroup` semantics.
95
+ *
96
+ * @example
97
+ * ```tsx
98
+ * <RadioGroup name="theme" value={theme} onValueChange={setTheme} label="Theme">
99
+ * <RadioButton value="light" label="Light" />
100
+ * <RadioButton value="dark" label="Dark" />
101
+ * <RadioButton value="system" label="System" />
102
+ * </RadioGroup>
103
+ * ```
104
+ * @see https://m3.material.io/components/radio-button/overview
105
+ */
106
+ export declare const RadioGroup: React.NamedExoticComponent<RadioGroupProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,126 @@
1
+ import * as React from "react";
2
+ /**
3
+ * Represents a single ripple wave instance with position and size metadata.
4
+ *
5
+ * @example
6
+ * ```tsx
7
+ * const ripple: RippleOrigin = { id: Date.now(), x: 50, y: 30, size: 200 };
8
+ * ```
9
+ */
10
+ export interface RippleOrigin {
11
+ /** Unique identifier used as React key and for removal. */
12
+ id: number;
13
+ /** X coordinate of the pointer event relative to the container's left edge (px). */
14
+ x: number;
15
+ /** Y coordinate of the pointer event relative to the container's top edge (px). */
16
+ y: number;
17
+ /**
18
+ * Diameter of the ripple circle (px).
19
+ * Typically `Math.hypot(width, height) * 2` to ensure it fills the container.
20
+ */
21
+ size: number;
22
+ }
23
+ /**
24
+ * Props for the `Ripple` presentation component.
25
+ */
26
+ export interface RippleProps {
27
+ /** Active ripple instances to render. Managed by the parent via `useRipple`. */
28
+ ripples: RippleOrigin[];
29
+ /** Called when a ripple's exit animation completes — remove it from state. */
30
+ onRippleDone: (id: number) => void;
31
+ /**
32
+ * Completely disables the ripple effect.
33
+ * Use this when the parent element is disabled or interaction is not desired.
34
+ * @default false
35
+ */
36
+ disabled?: boolean;
37
+ /**
38
+ * When `true`, the ripple respects the user's OS-level
39
+ * `prefers-reduced-motion` accessibility setting and renders nothing if active.
40
+ *
41
+ * Set to `false` to always show ripples regardless of system preference.
42
+ * @default true
43
+ */
44
+ respectSystemMotion?: boolean;
45
+ }
46
+ /**
47
+ * MD3 Expressive Ripple — animated touch-feedback wave layer.
48
+ *
49
+ * Renders absolutely-positioned ripple circles inside an `overflow-hidden`
50
+ * container. Must be placed as a direct child of the interactive element.
51
+ *
52
+ * @remarks
53
+ * - The parent element **must** have `overflow: hidden` and `position: relative`
54
+ * (or equivalent) for clipping to work correctly.
55
+ * - Set `disabled` to `true` on parent's disabled state to avoid stale ripples.
56
+ * - The ripple color is `currentColor` at 12% opacity — matching MD3 state layer spec.
57
+ *
58
+ * @example
59
+ * ```tsx
60
+ * const { ripples, onPointerDown, removeRipple } = useRippleState();
61
+ *
62
+ * <button onPointerDown={onPointerDown} className="relative overflow-hidden">
63
+ * <Ripple ripples={ripples} onRippleDone={removeRipple} />
64
+ * Click me
65
+ * </button>
66
+ * ```
67
+ *
68
+ * @see {@link useRippleState} for the state management hook
69
+ * @see https://m3.material.io/foundations/interaction/states/overview
70
+ */
71
+ export declare function Ripple({ ripples, onRippleDone, disabled, respectSystemMotion, }: RippleProps): import("react/jsx-runtime").JSX.Element | null;
72
+ /**
73
+ * Options for configuring `useRippleState` behaviour.
74
+ */
75
+ export interface UseRippleStateOptions {
76
+ /**
77
+ * When `true`, the ripple is suppressed — `onPointerDown` becomes a no-op.
78
+ * Use this to sync the ripple with the parent element's `disabled` state.
79
+ * @default false
80
+ */
81
+ disabled?: boolean;
82
+ }
83
+ /**
84
+ * `useRippleState` — state manager for MD3 Expressive ripple waves.
85
+ *
86
+ * Tracks active ripple instances and provides pointer event handlers.
87
+ * Pair with the `<Ripple>` component for rendering.
88
+ *
89
+ * @remarks
90
+ * This hook only manages ripple *state* (coordinates, size, lifecycle).
91
+ * The actual animation is handled by `<Ripple>` via Framer Motion.
92
+ * Respecting `prefers-reduced-motion` is handled by `<Ripple>` itself.
93
+ *
94
+ * @param options - Configuration options. See {@link UseRippleStateOptions}.
95
+ * @returns `{ ripples, onPointerDown, removeRipple }` — bind to the interactive element.
96
+ *
97
+ * @example
98
+ * ```tsx
99
+ * function MyButton({ disabled, children }) {
100
+ * const { ripples, onPointerDown, removeRipple } = useRippleState({ disabled });
101
+ *
102
+ * return (
103
+ * <button
104
+ * disabled={disabled}
105
+ * onPointerDown={onPointerDown}
106
+ * className="relative overflow-hidden"
107
+ * >
108
+ * <Ripple ripples={ripples} onRippleDone={removeRipple} disabled={disabled} />
109
+ * {children}
110
+ * </button>
111
+ * );
112
+ * }
113
+ * ```
114
+ *
115
+ * @see {@link Ripple} for the rendering component
116
+ */
117
+ export declare function useRippleState(options?: UseRippleStateOptions): {
118
+ ripples: RippleOrigin[];
119
+ onPointerDown: (e: React.PointerEvent<HTMLElement>) => void;
120
+ removeRipple: (id: number) => void;
121
+ };
122
+ /**
123
+ * @deprecated Use `useRippleState` instead. This alias will be removed in a future version.
124
+ * @see {@link useRippleState}
125
+ */
126
+ export declare const useRipple: typeof useRippleState;
@@ -0,0 +1,27 @@
1
+ import * as RadixScrollArea from "@radix-ui/react-scroll-area";
2
+ import * as React from "react";
3
+ /** Radix accepts hover/scroll/always/auto. We add 'none' as a UI-only hide. */
4
+ export type ScrollAreaType = "hover" | "scroll" | "always" | "none";
5
+ export type ScrollAreaOrientation = "vertical" | "horizontal" | "both";
6
+ export interface ScrollAreaProps extends Omit<React.ComponentPropsWithoutRef<typeof RadixScrollArea.Root>, "type"> {
7
+ /**
8
+ * Controls when the scrollbars are visible.
9
+ * - `hover`: Show on hover (default, recommended for desktop)
10
+ * - `scroll`: Show only while scrolling (recommended for mobile)
11
+ * - `always`: Always visible
12
+ * - `none`: Never visible
13
+ */
14
+ type?: ScrollAreaType;
15
+ /**
16
+ * The scrollbar orientation to render.
17
+ * @default "vertical"
18
+ */
19
+ orientation?: ScrollAreaOrientation;
20
+ /** Delay in ms before scrollbars hide when `type` is `hover` or `scroll`. */
21
+ scrollHideDelay?: number;
22
+ /** Extra classes applied to the inner viewport element. */
23
+ viewportClassName?: string;
24
+ }
25
+ declare const ScrollArea: React.ForwardRefExoticComponent<ScrollAreaProps & React.RefAttributes<HTMLDivElement>>;
26
+ declare const ScrollAreaScrollbar: React.ForwardRefExoticComponent<Omit<RadixScrollArea.ScrollAreaScrollbarProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
27
+ export { ScrollArea, ScrollAreaScrollbar };
@@ -0,0 +1,86 @@
1
+ /**
2
+ * @file shared/constants.ts
3
+ *
4
+ * Shared animation constants for MD3 Expressive UI components.
5
+ * Centralises spring transition configs and motion variant objects to avoid
6
+ * duplication across button, icon-button, FAB, and other interactive components.
7
+ *
8
+ * @see https://m3.material.io/foundations/animation/overview
9
+ */
10
+ import type { Target, TargetAndTransition, Transition } from "motion/react";
11
+ /**
12
+ * Fast critically-damped spring — used for border-radius morphing.
13
+ *
14
+ * - Duration: 200ms
15
+ * - Bounce: 0 (no overshoot → prevents negative radius jitter)
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * <m.button transition={{ borderRadius: SPRING_TRANSITION_FAST }}>...</m.button>
20
+ * ```
21
+ */
22
+ export declare const SPRING_TRANSITION_FAST: Transition;
23
+ /**
24
+ * Standard critically-damped spring — used for icon/content scale animations.
25
+ *
26
+ * - Duration: 300ms
27
+ * - Bounce: 0 (no overshoot)
28
+ *
29
+ * @example
30
+ * ```tsx
31
+ * <m.span transition={SPRING_TRANSITION}>...</m.span>
32
+ * ```
33
+ */
34
+ export declare const SPRING_TRANSITION: Transition;
35
+ /**
36
+ * Framer Motion variants for animating icon spans in/out.
37
+ *
38
+ * Scale from near-zero → 1 on enter; back to near-zero on exit.
39
+ * The near-zero value (0.01) avoids the SMIL freeze bug on Chromium
40
+ * that occurs when an element starts at exactly `scale(0)`.
41
+ *
42
+ * @example
43
+ * ```tsx
44
+ * <AnimatePresence mode="wait">
45
+ * {loading ? (
46
+ * <m.span key="loading" {...ICON_SPAN_VARIANTS} transition={SPRING_TRANSITION}>
47
+ * <LoadingIndicator />
48
+ * </m.span>
49
+ * ) : (
50
+ * <m.span key="icon" {...ICON_SPAN_VARIANTS} transition={SPRING_TRANSITION}>
51
+ * {icon}
52
+ * </m.span>
53
+ * )}
54
+ * </AnimatePresence>
55
+ * ```
56
+ */
57
+ export declare const ICON_SPAN_VARIANTS: {
58
+ initial: Target;
59
+ animate: TargetAndTransition;
60
+ exit: TargetAndTransition;
61
+ };
62
+ /**
63
+ * MD3 Standard easing curve — used for label float, active indicator expand.
64
+ * cubic-bezier(0.2, 0, 0, 1)
65
+ *
66
+ * @see https://m3.material.io/foundations/animation/easing-and-duration
67
+ */
68
+ export declare const MD3_STANDARD_EASING: [number, number, number, number];
69
+ /**
70
+ * Duration for floating label transition: 150ms.
71
+ * Used when label moves between inline position ↔ floated position.
72
+ */
73
+ export declare const MD3_LABEL_FLOAT_DURATION = 0.15;
74
+ /**
75
+ * Duration for active indicator expand/collapse: 200ms.
76
+ * Used for the bottom border (filled) and outline (outlined) on focus.
77
+ */
78
+ export declare const MD3_INDICATOR_DURATION = 0.2;
79
+ /**
80
+ * Duration for supporting text / error text appear/disappear: 120ms.
81
+ */
82
+ export declare const MD3_SUPPORTING_DURATION = 0.12;
83
+ /**
84
+ * Duration for trailing icon appear/disappear (clear button, password toggle): 100ms.
85
+ */
86
+ export declare const MD3_ICON_SWAP_DURATION = 0.1;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @file shared/touch-target.tsx
3
+ *
4
+ * Invisible 48×48dp touch-target expander for small interactive elements.
5
+ *
6
+ * WCAG 2.5.5 (Level AA) requires interactive targets to be at least 44×44 CSS pixels.
7
+ * MD3 specifies a minimum 48dp touch target for all interactive components.
8
+ * For button sizes XS (32dp) and SM (40dp) that are smaller than this minimum,
9
+ * an invisible absolutely-positioned `<span>` is overlaid to extend the
10
+ * effective tap area without affecting visual layout.
11
+ *
12
+ * @see https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html
13
+ * @see https://m3.material.io/foundations/accessible-design/accessibility-basics
14
+ */
15
+ /**
16
+ * Invisible 48×48dp touch area expander — satisfies WCAG 2.5.5 + MD3 spec.
17
+ *
18
+ * Place as a direct child of any interactive element whose visual size is
19
+ * smaller than 48dp (e.g. `xs`/`sm` buttons and icon buttons).
20
+ * The parent element **must** have `position: relative`.
21
+ *
22
+ * @remarks
23
+ * - Hidden from assistive technologies via `aria-hidden="true"`.
24
+ * - `pointer-events: none` ensures it does not intercept click events.
25
+ * - Centred with `left: 50% / top: 50%` + negative translate for symmetry.
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * // Used inside a 32dp XS button:
30
+ * <button className="relative h-8 w-auto ...">
31
+ * <TouchTarget />
32
+ * Label
33
+ * </button>
34
+ * ```
35
+ *
36
+ * @see https://m3.material.io/components/buttons/specs (Touch target section)
37
+ */
38
+ export declare function TouchTarget(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @file snackbar/index.ts
3
+ * Barrel re-export for the MD3 Expressive Snackbar component system.
4
+ */
5
+ export type { SnackbarData, SnackbarDuration, SnackbarHostProps, SnackbarProps, SnackbarResult, SnackbarVisuals, UseSnackbarStateReturn, } from "./snackbar";
6
+ export { Snackbar, SnackbarHost, SnackbarProvider, useSnackbar, useSnackbarState, } from "./snackbar";