@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.
- package/README.md +215 -0
- package/dist/assets/fonts/GoogleSansFlex-VariableFont.woff2 +0 -0
- package/dist/assets/fonts/MaterialSymbolsOutlined-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
- package/dist/assets/fonts/MaterialSymbolsRounded-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
- package/dist/assets/fonts/MaterialSymbolsSharp-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
- package/dist/assets/loading-indicator.svg +19 -0
- package/dist/assets/material-symbols-cdn.css +65 -0
- package/dist/assets/material-symbols-self-hosted.css +109 -0
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/useMediaQuery.d.ts +11 -0
- package/dist/hooks/useRipple.d.ts +26 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.js +9059 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +8929 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lib/material-symbols-preconnect.d.ts +42 -0
- package/dist/lib/theme-utils.d.ts +63 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/material-symbols-cdn.css +65 -0
- package/dist/material-symbols-self-hosted.css +109 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/md3.d.ts +14 -0
- package/dist/typography.css +22 -0
- package/dist/ui/badge.d.ts +125 -0
- package/dist/ui/button-group.d.ts +59 -0
- package/dist/ui/button.d.ts +148 -0
- package/dist/ui/card.d.ts +62 -0
- package/dist/ui/checkbox.d.ts +82 -0
- package/dist/ui/chip.d.ts +110 -0
- package/dist/ui/code-block.d.ts +14 -0
- package/dist/ui/dialog.d.ts +111 -0
- package/dist/ui/divider.d.ts +164 -0
- package/dist/ui/drawer.d.ts +39 -0
- package/dist/ui/dropdown.d.ts +29 -0
- package/dist/ui/fab-menu.d.ts +204 -0
- package/dist/ui/fab.d.ts +162 -0
- package/dist/ui/icon-button.d.ts +131 -0
- package/dist/ui/icon.d.ts +88 -0
- package/dist/ui/loading-indicator.d.ts +42 -0
- package/dist/ui/navigation-rail.d.ts +29 -0
- package/dist/ui/progress-indicator/circular.d.ts +3 -0
- package/dist/ui/progress-indicator/hooks.d.ts +3 -0
- package/dist/ui/progress-indicator/index.d.ts +21 -0
- package/dist/ui/progress-indicator/linear-flat.d.ts +10 -0
- package/dist/ui/progress-indicator/linear-wavy.d.ts +18 -0
- package/dist/ui/progress-indicator/linear.d.ts +3 -0
- package/dist/ui/progress-indicator/types.d.ts +151 -0
- package/dist/ui/progress-indicator/utils.d.ts +3 -0
- package/dist/ui/radio-button.d.ts +106 -0
- package/dist/ui/ripple.d.ts +126 -0
- package/dist/ui/scroll-area.d.ts +27 -0
- package/dist/ui/shared/constants.d.ts +86 -0
- package/dist/ui/shared/touch-target.d.ts +38 -0
- package/dist/ui/snackbar/index.d.ts +6 -0
- package/dist/ui/snackbar/snackbar.d.ts +196 -0
- package/dist/ui/switch/index.d.ts +7 -0
- package/dist/ui/switch/switch.d.ts +30 -0
- package/dist/ui/switch/switch.stories.d.ts +48 -0
- package/dist/ui/switch/switch.tokens.d.ts +67 -0
- package/dist/ui/switch/switch.types.d.ts +59 -0
- package/dist/ui/tabs/index.d.ts +10 -0
- package/dist/ui/tabs/tab.d.ts +43 -0
- package/dist/ui/tabs/tabs-content.d.ts +36 -0
- package/dist/ui/tabs/tabs-list.d.ts +40 -0
- package/dist/ui/tabs/tabs.d.ts +60 -0
- package/dist/ui/tabs/tabs.tokens.d.ts +94 -0
- package/dist/ui/tabs/tabs.types.d.ts +172 -0
- package/dist/ui/text-field/index.d.ts +11 -0
- package/dist/ui/text-field/subcomponents/active-indicator.d.ts +24 -0
- package/dist/ui/text-field/subcomponents/floating-label.d.ts +43 -0
- package/dist/ui/text-field/subcomponents/leading-icon.d.ts +23 -0
- package/dist/ui/text-field/subcomponents/outline-container.d.ts +42 -0
- package/dist/ui/text-field/subcomponents/prefix-suffix.d.ts +24 -0
- package/dist/ui/text-field/subcomponents/supporting-text.d.ts +37 -0
- package/dist/ui/text-field/subcomponents/trailing-icon.d.ts +41 -0
- package/dist/ui/text-field/text-field.d.ts +49 -0
- package/dist/ui/text-field/text-field.tokens.d.ts +76 -0
- package/dist/ui/text-field/text-field.types.d.ts +126 -0
- package/dist/ui/theme-provider/index.d.ts +18 -0
- package/dist/ui/toc.d.ts +74 -0
- package/dist/ui/tooltip/index.d.ts +8 -0
- package/dist/ui/tooltip/plain-tooltip.d.ts +2 -0
- package/dist/ui/tooltip/rich-tooltip.d.ts +2 -0
- package/dist/ui/tooltip/tooltip-box.d.ts +2 -0
- package/dist/ui/tooltip/tooltip-caret-shape.d.ts +9 -0
- package/dist/ui/tooltip/tooltip.tokens.d.ts +26 -0
- package/dist/ui/tooltip/tooltip.types.d.ts +56 -0
- package/dist/ui/tooltip/use-tooltip-position.d.ts +8 -0
- package/dist/ui/tooltip/use-tooltip-state.d.ts +2 -0
- package/dist/ui/typography/index.d.ts +16 -0
- package/dist/ui/typography/type-scale-tokens.d.ts +162 -0
- package/dist/ui/typography/typography-key-tokens.d.ts +40 -0
- package/dist/ui/typography/typography-tokens.d.ts +220 -0
- package/dist/ui/typography/typography.d.ts +265 -0
- package/package.json +80 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file snackbar.tsx
|
|
3
|
+
*
|
|
4
|
+
* MD3 Expressive Snackbar component.
|
|
5
|
+
*
|
|
6
|
+
* Architecture:
|
|
7
|
+
* - `Snackbar` → Pure display component (motion.div, role="status", aria-live="polite")
|
|
8
|
+
* - `SnackbarHost` → AnimatePresence container + queue flush (place once in layout)
|
|
9
|
+
* - `SnackbarProvider` → Context provider that wires SnackbarHost + exposes `useSnackbar`
|
|
10
|
+
* - `useSnackbarState` → Low-level ref-based queue hook (mutex pattern)
|
|
11
|
+
* - `useSnackbar` → Consumer hook for imperative `showSnackbar(visuals)` calls
|
|
12
|
+
*
|
|
13
|
+
* Queue strategy:
|
|
14
|
+
* - One snackbar visible at a time (MD3 spec).
|
|
15
|
+
* - Subsequent `showSnackbar()` calls are enqueued, shown as soon as current one dismisses.
|
|
16
|
+
* - Cleanup: on unmount, all pending promises resolve as 'dismissed'.
|
|
17
|
+
*
|
|
18
|
+
* @see https://m3.material.io/components/snackbar/overview
|
|
19
|
+
*/
|
|
20
|
+
import * as React from "react";
|
|
21
|
+
/**
|
|
22
|
+
* Duration preset for the snackbar auto-dismiss timer.
|
|
23
|
+
* - `'short'` → 4 000 ms (default, MD3 spec)
|
|
24
|
+
* - `'long'` → 7 000 ms
|
|
25
|
+
* - `number` → custom milliseconds
|
|
26
|
+
*/
|
|
27
|
+
export type SnackbarDuration = "short" | "long" | number;
|
|
28
|
+
/**
|
|
29
|
+
* Resolution value returned by the `showSnackbar()` promise.
|
|
30
|
+
* - `'action-performed'` → user clicked the action button
|
|
31
|
+
* - `'dismissed'` → auto-dismissed or close button clicked
|
|
32
|
+
*/
|
|
33
|
+
export type SnackbarResult = "action-performed" | "dismissed";
|
|
34
|
+
/**
|
|
35
|
+
* Visual configuration for a single snackbar instance.
|
|
36
|
+
*/
|
|
37
|
+
export interface SnackbarVisuals {
|
|
38
|
+
/** Main message text. */
|
|
39
|
+
message: string;
|
|
40
|
+
/** Label for the optional action button. */
|
|
41
|
+
actionLabel?: string;
|
|
42
|
+
/** When `true`, renders a close (X) icon button. @default false */
|
|
43
|
+
withDismissAction?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* When `true`, renders the action button below the message (Column layout).
|
|
46
|
+
* Use when both message and actionLabel are long.
|
|
47
|
+
* @default false
|
|
48
|
+
*/
|
|
49
|
+
actionOnNewLine?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Auto-dismiss duration.
|
|
52
|
+
* @default 'short' (4 000 ms)
|
|
53
|
+
*/
|
|
54
|
+
duration?: SnackbarDuration;
|
|
55
|
+
/** Additional className applied to the snackbar container. */
|
|
56
|
+
className?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Internal runtime data for a currently-displayed snackbar.
|
|
60
|
+
* Includes the resolve callback to settle the caller's promise.
|
|
61
|
+
*/
|
|
62
|
+
export interface SnackbarData {
|
|
63
|
+
/** Unique key for AnimatePresence element diffing. */
|
|
64
|
+
id: string;
|
|
65
|
+
/** Visual configuration. */
|
|
66
|
+
visuals: SnackbarVisuals;
|
|
67
|
+
/** Settles the promise returned by `showSnackbar()`. */
|
|
68
|
+
resolve: (result: SnackbarResult) => void;
|
|
69
|
+
}
|
|
70
|
+
/** Props for the pure `Snackbar` display component. */
|
|
71
|
+
export interface SnackbarProps {
|
|
72
|
+
/** Runtime data including message, actions, and resolve callback. */
|
|
73
|
+
data: SnackbarData;
|
|
74
|
+
/** Additional className merged onto the snackbar container. */
|
|
75
|
+
className?: string;
|
|
76
|
+
}
|
|
77
|
+
/** Props for the `SnackbarHost` component. */
|
|
78
|
+
export interface SnackbarHostProps {
|
|
79
|
+
/** State returned by `useSnackbarState()`. */
|
|
80
|
+
state: UseSnackbarStateReturn;
|
|
81
|
+
/** Additional className applied to the fixed host wrapper. */
|
|
82
|
+
className?: string;
|
|
83
|
+
}
|
|
84
|
+
/** Return type of `useSnackbarState`. */
|
|
85
|
+
export interface UseSnackbarStateReturn {
|
|
86
|
+
/** Currently visible snackbar data, or `null` when idle. */
|
|
87
|
+
current: SnackbarData | null;
|
|
88
|
+
/**
|
|
89
|
+
* Show a snackbar with the given visuals.
|
|
90
|
+
* Returns a promise that resolves when the snackbar is dismissed or the action is triggered.
|
|
91
|
+
*/
|
|
92
|
+
showSnackbar: (visuals: SnackbarVisuals) => Promise<SnackbarResult>;
|
|
93
|
+
/** Internal dismiss handler — called by `SnackbarHost`. */
|
|
94
|
+
_dismiss: (result: SnackbarResult) => void;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Low-level hook that manages the snackbar queue and current state.
|
|
98
|
+
*
|
|
99
|
+
* Uses a `ref`-based queue (mutex pattern) so that enqueueing never
|
|
100
|
+
* triggers a re-render storm — only the state transition does.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```tsx
|
|
104
|
+
* // Used internally by SnackbarProvider
|
|
105
|
+
* const state = useSnackbarState();
|
|
106
|
+
* return <SnackbarHost state={state} />;
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export declare function useSnackbarState(): UseSnackbarStateReturn;
|
|
110
|
+
/**
|
|
111
|
+
* MD3 Expressive Snackbar — pure display component.
|
|
112
|
+
*
|
|
113
|
+
* Renders a single snackbar with message, optional action button, and
|
|
114
|
+
* optional dismiss icon button. Handles its own auto-dismiss timer.
|
|
115
|
+
*
|
|
116
|
+
* @remarks
|
|
117
|
+
* - Uses `role="status"` + `aria-live="polite"` for screen reader announcements.
|
|
118
|
+
* - All entrance/exit animation is handled by the parent `SnackbarHost` via
|
|
119
|
+
* `AnimatePresence` + `SNACKBAR_ANIM`.
|
|
120
|
+
* - Do NOT render this component directly — use `SnackbarHost`.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```tsx
|
|
124
|
+
* // Internal usage inside SnackbarHost — not for direct use
|
|
125
|
+
* <Snackbar data={currentSnackbarData} />
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export declare const Snackbar: React.NamedExoticComponent<SnackbarProps>;
|
|
129
|
+
/**
|
|
130
|
+
* MD3 SnackbarHost — renders the AnimatePresence container for snackbar queue.
|
|
131
|
+
*
|
|
132
|
+
* Place this once in your app layout. It will show snackbars one at a time,
|
|
133
|
+
* dequeuing the next one as each dismisses.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```tsx
|
|
137
|
+
* // Typically used inside SnackbarProvider — not directly
|
|
138
|
+
* const state = useSnackbarState();
|
|
139
|
+
* <SnackbarHost state={state} />
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
export declare function SnackbarHost({ state, className }: SnackbarHostProps): import("react/jsx-runtime").JSX.Element;
|
|
143
|
+
export declare namespace SnackbarHost {
|
|
144
|
+
var displayName: string;
|
|
145
|
+
}
|
|
146
|
+
interface SnackbarContextValue {
|
|
147
|
+
showSnackbar: (visuals: SnackbarVisuals) => Promise<SnackbarResult>;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* MD3 SnackbarProvider — context provider for imperative snackbar API.
|
|
151
|
+
*
|
|
152
|
+
* Wrap your application (or a section of it) with this provider.
|
|
153
|
+
* Then use `useSnackbar()` in any descendant to show snackbars.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```tsx
|
|
157
|
+
* // In your root layout:
|
|
158
|
+
* <SnackbarProvider>
|
|
159
|
+
* <App />
|
|
160
|
+
* </SnackbarProvider>
|
|
161
|
+
*
|
|
162
|
+
* // In any component:
|
|
163
|
+
* const { showSnackbar } = useSnackbar();
|
|
164
|
+
* await showSnackbar({ message: 'Saved!', actionLabel: 'Undo' });
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
export declare function SnackbarProvider({ children }: {
|
|
168
|
+
children: React.ReactNode;
|
|
169
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
170
|
+
export declare namespace SnackbarProvider {
|
|
171
|
+
var displayName: string;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Hook that returns the `showSnackbar` function from the nearest `SnackbarProvider`.
|
|
175
|
+
*
|
|
176
|
+
* @throws {Error} if used outside of a `SnackbarProvider`.
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```tsx
|
|
180
|
+
* function SaveButton() {
|
|
181
|
+
* const { showSnackbar } = useSnackbar();
|
|
182
|
+
*
|
|
183
|
+
* const handleSave = async () => {
|
|
184
|
+
* const result = await showSnackbar({
|
|
185
|
+
* message: 'Changes saved',
|
|
186
|
+
* actionLabel: 'Undo',
|
|
187
|
+
* });
|
|
188
|
+
* if (result === 'action-performed') undoSave();
|
|
189
|
+
* };
|
|
190
|
+
*
|
|
191
|
+
* return <button onClick={handleSave}>Save</button>;
|
|
192
|
+
* }
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
export declare function useSnackbar(): SnackbarContextValue;
|
|
196
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file switch.tsx
|
|
3
|
+
* MD3 Expressive Switch — ARIA switch pattern with Framer Motion animations.
|
|
4
|
+
* Spec: https://m3.material.io/components/switch/overview
|
|
5
|
+
*
|
|
6
|
+
* Key decisions:
|
|
7
|
+
* - Uses `<button role="switch">` (no <input>) per MD3 accessibility spec
|
|
8
|
+
* - Framer Motion for ALL animations (thumb x, size morph, state layer, icons)
|
|
9
|
+
* - Hover state via useState (required for Framer Motion color animate)
|
|
10
|
+
* - Disabled colors via rgba() literals (color-mix() not animatable by FM)
|
|
11
|
+
*/
|
|
12
|
+
import * as React from "react";
|
|
13
|
+
import type { SwitchProps } from "./switch.types";
|
|
14
|
+
/**
|
|
15
|
+
* MD3 Expressive Switch component.
|
|
16
|
+
*
|
|
17
|
+
* Toggles a single item on or off. Implements the ARIA switch pattern
|
|
18
|
+
* (`role="switch"`) without `<input>`. Fully animated per MD3 spec:
|
|
19
|
+
* thumb translation, size morph (16→24→28px), state layer, and icon cross-fade.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* <Switch checked={isOn} onCheckedChange={setIsOn} label="Wi-Fi" />
|
|
24
|
+
* <Switch checked={isOn} onCheckedChange={setIsOn} icons thumbContent={<CheckIcon />} />
|
|
25
|
+
* <Switch checked={isOn} onCheckedChange={setIsOn} disabled />
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @see https://m3.material.io/components/switch/overview
|
|
29
|
+
*/
|
|
30
|
+
export declare const Switch: React.NamedExoticComponent<SwitchProps & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file switch.stories.tsx
|
|
3
|
+
* MD3 Expressive Switch — all usage patterns and states.
|
|
4
|
+
*
|
|
5
|
+
* Can be used as:
|
|
6
|
+
* 1. Storybook stories (if Storybook is configured)
|
|
7
|
+
* 2. Standalone demo component for the docs app
|
|
8
|
+
*
|
|
9
|
+
* Covers all 7 patterns from the MD3 Switch specification:
|
|
10
|
+
* 1. Basic toggle
|
|
11
|
+
* 2. With label
|
|
12
|
+
* 3. With icons (both states)
|
|
13
|
+
* 4. With only selected icon
|
|
14
|
+
* 5. Disabled (checked)
|
|
15
|
+
* 6. Disabled (unchecked)
|
|
16
|
+
* 7. All states grid
|
|
17
|
+
*/
|
|
18
|
+
/** Basic stateful switch — no label, no icons. */
|
|
19
|
+
export declare function Basic(): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
/** Switch with a visible text label using htmlFor linkage. */
|
|
21
|
+
export declare function WithLabel(): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
/**
|
|
23
|
+
* Switch showing icons in both checked and unchecked states.
|
|
24
|
+
* Uses check icon when on, close icon when off.
|
|
25
|
+
*/
|
|
26
|
+
export declare function WithIcons(): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
/**
|
|
28
|
+
* Switch showing the icon only in the checked (selected) state.
|
|
29
|
+
* Unselected state has no icon.
|
|
30
|
+
*/
|
|
31
|
+
export declare function WithOnlySelectedIcon(): import("react/jsx-runtime").JSX.Element;
|
|
32
|
+
/** Disabled switch in the checked (on) state. */
|
|
33
|
+
export declare function DisabledChecked(): import("react/jsx-runtime").JSX.Element;
|
|
34
|
+
/** Disabled switch in the unchecked (off) state. */
|
|
35
|
+
export declare function DisabledUnchecked(): import("react/jsx-runtime").JSX.Element;
|
|
36
|
+
/**
|
|
37
|
+
* Grid displaying all Switch state combinations:
|
|
38
|
+
* - Enabled: checked / unchecked
|
|
39
|
+
* - Disabled: checked / unchecked
|
|
40
|
+
* - With icons: checked / unchecked
|
|
41
|
+
* - With only selected icon: checked / unchecked
|
|
42
|
+
*/
|
|
43
|
+
export declare function AllStatesGrid(): import("react/jsx-runtime").JSX.Element;
|
|
44
|
+
/**
|
|
45
|
+
* All switch stories in one scrollable demo page.
|
|
46
|
+
* Used by the docs app to showcase all variants.
|
|
47
|
+
*/
|
|
48
|
+
export declare function SwitchDemo(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file switch.tokens.ts
|
|
3
|
+
* MD3 Expressive Switch — Design tokens ported from SwitchTokens.kt.
|
|
4
|
+
* All dimensional values are in px (dp equivalent for web).
|
|
5
|
+
* @see docs/m3/switch/SwitchTokens.kt
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Design tokens for the MD3 Expressive Switch component.
|
|
9
|
+
*
|
|
10
|
+
* Maps directly from `SwitchTokens.kt` (v0_210) to CSS/JS values.
|
|
11
|
+
* Use these as the single source of truth for sizing and opacity.
|
|
12
|
+
*
|
|
13
|
+
* Color tokens are NOT included here — they reference CSS custom properties
|
|
14
|
+
* from the project's MD3 theme system (`--md-sys-color-*`).
|
|
15
|
+
*/
|
|
16
|
+
export declare const SwitchTokens: {
|
|
17
|
+
/** SwitchTokens.TrackWidth = 52dp */
|
|
18
|
+
readonly trackWidth: 52;
|
|
19
|
+
/** SwitchTokens.TrackHeight = 32dp */
|
|
20
|
+
readonly trackHeight: 32;
|
|
21
|
+
/** SwitchTokens.TrackOutlineWidth = 2dp */
|
|
22
|
+
readonly trackOutlineWidth: 2;
|
|
23
|
+
/** SwitchTokens.SelectedHandleWidth/Height = 24dp */
|
|
24
|
+
readonly selectedHandleSize: 24;
|
|
25
|
+
/** SwitchTokens.UnselectedHandleWidth/Height = 16dp */
|
|
26
|
+
readonly unselectedHandleSize: 16;
|
|
27
|
+
/** SwitchTokens.IconHandleWidth/Height = 24dp (when thumb has icon content) */
|
|
28
|
+
readonly iconHandleSize: 24;
|
|
29
|
+
/** SwitchTokens.PressedHandleWidth/Height = 28dp */
|
|
30
|
+
readonly pressedHandleSize: 28;
|
|
31
|
+
/** SwitchTokens.StateLayerSize = 40dp */
|
|
32
|
+
readonly stateLayerSize: 40;
|
|
33
|
+
/** SwitchTokens.SelectedIconSize / UnselectedIconSize = 16dp */
|
|
34
|
+
readonly iconSize: 16;
|
|
35
|
+
/** SwitchTokens.DisabledTrackOpacity = 0.12 */
|
|
36
|
+
readonly disabledTrackOpacity: 0.12;
|
|
37
|
+
/** SwitchTokens.DisabledSelectedHandleOpacity = 1.0 */
|
|
38
|
+
readonly disabledSelectedHandleOpacity: 1;
|
|
39
|
+
/** SwitchTokens.DisabledUnselectedHandleOpacity = 0.38 */
|
|
40
|
+
readonly disabledUnselectedHandleOpacity: 0.38;
|
|
41
|
+
/** SwitchTokens.DisabledSelectedIconOpacity = 0.38 */
|
|
42
|
+
readonly disabledSelectedIconOpacity: 0.38;
|
|
43
|
+
/** SwitchTokens.DisabledUnselectedIconOpacity = 0.38 */
|
|
44
|
+
readonly disabledUnselectedIconOpacity: 0.38;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* CSS custom property references for Switch colors.
|
|
48
|
+
* These map to the project's `--md-sys-color-*` tokens in `colors.css`.
|
|
49
|
+
*
|
|
50
|
+
* DO NOT hardcode hex values — always use these references so the
|
|
51
|
+
* component automatically adapts to light/dark theme switching.
|
|
52
|
+
*/
|
|
53
|
+
export declare const SwitchColors: {
|
|
54
|
+
readonly checkedTrack: "var(--md-sys-color-primary)";
|
|
55
|
+
readonly uncheckedTrack: "var(--md-sys-color-surface-container-highest)";
|
|
56
|
+
readonly uncheckedTrackOutline: "var(--md-sys-color-outline)";
|
|
57
|
+
readonly checkedThumb: "var(--md-sys-color-on-primary)";
|
|
58
|
+
readonly uncheckedThumb: "var(--md-sys-color-outline)";
|
|
59
|
+
readonly hoverCheckedThumb: "var(--md-sys-color-primary-container)";
|
|
60
|
+
readonly hoverUncheckedThumb: "var(--md-sys-color-on-surface-variant)";
|
|
61
|
+
readonly disabledCheckedThumb: "var(--md-sys-color-surface)";
|
|
62
|
+
readonly checkedIcon: "var(--md-sys-color-on-primary-container)";
|
|
63
|
+
readonly uncheckedIcon: "var(--md-sys-color-surface-container-highest)";
|
|
64
|
+
readonly checkedStateLayer: "var(--md-sys-color-primary)";
|
|
65
|
+
readonly uncheckedStateLayer: "var(--md-sys-color-on-surface)";
|
|
66
|
+
readonly focusIndicator: "var(--md-sys-color-secondary)";
|
|
67
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file switch.types.ts
|
|
3
|
+
* MD3 Expressive Switch — TypeScript prop definitions.
|
|
4
|
+
* Spec: https://m3.material.io/components/switch/overview
|
|
5
|
+
*/
|
|
6
|
+
import type * as React from "react";
|
|
7
|
+
/**
|
|
8
|
+
* Props for the `Switch` component.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* <Switch checked={isOn} onCheckedChange={setIsOn} label="Wi-Fi" />
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export interface SwitchProps {
|
|
16
|
+
/** Controlled checked (on) state. */
|
|
17
|
+
checked: boolean;
|
|
18
|
+
/** Called when the switch is toggled. Not called when disabled. */
|
|
19
|
+
onCheckedChange: (checked: boolean) => void;
|
|
20
|
+
/** Disables interaction and applies disabled visual state. @default false */
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Optional icon content rendered inside the thumb.
|
|
24
|
+
* Expected to measure 16dp (SwitchTokens.iconSize).
|
|
25
|
+
*/
|
|
26
|
+
thumbContent?: React.ReactNode;
|
|
27
|
+
/**
|
|
28
|
+
* When true, shows thumb icons in both selected and unselected states.
|
|
29
|
+
* Requires `thumbContent` to be provided.
|
|
30
|
+
* @default false
|
|
31
|
+
*/
|
|
32
|
+
icons?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* When true, shows the icon only in the selected/checked state.
|
|
35
|
+
* Requires `thumbContent` to be provided.
|
|
36
|
+
* @default false
|
|
37
|
+
*/
|
|
38
|
+
showOnlySelectedIcon?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Visible label text rendered adjacent to the switch.
|
|
41
|
+
* When provided, wraps the switch in a `<label>` for accessibility.
|
|
42
|
+
*/
|
|
43
|
+
label?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Overrides the accessible name. Used when no visible `label` is provided.
|
|
46
|
+
* Maps to the `aria-label` attribute.
|
|
47
|
+
*/
|
|
48
|
+
ariaLabel?: string;
|
|
49
|
+
/** Additional CSS class names applied to the outermost wrapper. */
|
|
50
|
+
className?: string;
|
|
51
|
+
/** Override track background color when checked. Defaults to MD3 primary. */
|
|
52
|
+
checkedTrackColor?: string;
|
|
53
|
+
/** Override track background color when unchecked. Defaults to MD3 surface-container-highest. */
|
|
54
|
+
uncheckedTrackColor?: string;
|
|
55
|
+
/** Override thumb color when checked. Defaults to MD3 on-primary. */
|
|
56
|
+
checkedThumbColor?: string;
|
|
57
|
+
/** Override thumb color when unchecked. Defaults to MD3 outline. */
|
|
58
|
+
uncheckedThumbColor?: string;
|
|
59
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file tabs/index.ts
|
|
3
|
+
* Public exports for the MD3 Expressive Tabs component system.
|
|
4
|
+
*/
|
|
5
|
+
export { Tab } from "./tab";
|
|
6
|
+
export { Tabs } from "./tabs";
|
|
7
|
+
export { TabsColors, TabsTokens } from "./tabs.tokens";
|
|
8
|
+
export type { TabProps, TabsContentProps, TabsListProps, TabsProps, TabsVariant, } from "./tabs.types";
|
|
9
|
+
export { TabsContent } from "./tabs-content";
|
|
10
|
+
export { TabsList } from "./tabs-list";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file tab.tsx
|
|
3
|
+
* MD3 Expressive Tab — Individual tab button with Framer Motion indicator.
|
|
4
|
+
*
|
|
5
|
+
* Design decisions:
|
|
6
|
+
* 1. PRIMARY indicator nested inside content wrapper → width = content width (not full button).
|
|
7
|
+
* 2. SECONDARY indicator outside content wrapper → `inset-x-0` = full button width.
|
|
8
|
+
* 3. ROVING TABINDEX (WAI-ARIA): only focused tab has tabIndex=0; ArrowKey moves focus, Enter/Space selects.
|
|
9
|
+
* 4. DISABLED tabs are skipped in ArrowKey navigation.
|
|
10
|
+
* 5. RTL: ArrowLeft/Right directions are swapped when `direction: rtl` is detected.
|
|
11
|
+
* 6. INLINE ICON: icon beside label, height stays 48dp (stacked = 64dp).
|
|
12
|
+
* 7. AUTO-ACTIVATE: when parent `<Tabs autoActivate>`, ArrowKey also selects.
|
|
13
|
+
*
|
|
14
|
+
* @see https://m3.material.io/components/tabs/overview
|
|
15
|
+
* @see https://www.w3.org/WAI/ARIA/apg/patterns/tabs/
|
|
16
|
+
*/
|
|
17
|
+
import * as React from "react";
|
|
18
|
+
import type { TabProps } from "./tabs.types";
|
|
19
|
+
/**
|
|
20
|
+
* MD3 Expressive Tab component — individual tab button.
|
|
21
|
+
*
|
|
22
|
+
* Must be a direct child of `<TabsList>`. Implements WAI-ARIA Tabs pattern
|
|
23
|
+
* with roving tabindex keyboard navigation.
|
|
24
|
+
*
|
|
25
|
+
* - **Primary variant**: indicator width = content (text + icon) width.
|
|
26
|
+
* - **Secondary variant**: indicator width = full button hit area.
|
|
27
|
+
* - **Disabled**: Skipped entirely in ArrowKey navigation (cannot be focused).
|
|
28
|
+
* - **inlineIcon**: Icon beside (not above) label; height stays 48dp.
|
|
29
|
+
* - Framer Motion `layoutId` animates indicator with spring physics.
|
|
30
|
+
* - ArrowLeft/Right respect RTL direction automatically.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* <Tab value="flights" icon={<Icon name="flight" />}>Flights</Tab>
|
|
35
|
+
* <Tab value="trips">Trips</Tab>
|
|
36
|
+
* <Tab value="explore" disabled>Explore</Tab>
|
|
37
|
+
* <Tab value="hotels" icon={<Icon name="hotel" />} inlineIcon>Hotels</Tab>
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @see https://m3.material.io/components/tabs/overview
|
|
41
|
+
* @see https://www.w3.org/WAI/ARIA/apg/patterns/tabs/
|
|
42
|
+
*/
|
|
43
|
+
export declare const Tab: React.NamedExoticComponent<TabProps & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file tabs-content.tsx
|
|
3
|
+
* MD3 Expressive TabsContent — Animated panel component.
|
|
4
|
+
*
|
|
5
|
+
* Implements WAI-ARIA tabpanel role with:
|
|
6
|
+
* - AnimatePresence for fade transition on tab switch
|
|
7
|
+
* - Proper aria-labelledby pointing to the associated <Tab>
|
|
8
|
+
* - tabIndex=0 so keyboard users can Tab from the tablist into the panel
|
|
9
|
+
* - Hidden panels are removed from the DOM (not just visually hidden)
|
|
10
|
+
* to prevent screen readers from reading inactive content
|
|
11
|
+
*/
|
|
12
|
+
import * as React from "react";
|
|
13
|
+
import type { TabsContentProps } from "./tabs.types";
|
|
14
|
+
/**
|
|
15
|
+
* MD3 Expressive TabsContent panel component.
|
|
16
|
+
*
|
|
17
|
+
* Each panel corresponds to a `<Tab>` with the same `value`.
|
|
18
|
+
* Only the active panel is rendered in the DOM — inactive panels
|
|
19
|
+
* are fully unmounted (not `display: none`) to prevent screen readers
|
|
20
|
+
* from reading hidden content.
|
|
21
|
+
*
|
|
22
|
+
* Fade animation is applied on both enter and exit via Framer Motion
|
|
23
|
+
* `AnimatePresence`. We use `mode="popLayout"` to prevent height layout shifting
|
|
24
|
+
* during tab transitions. Animation is automatically disabled when the user
|
|
25
|
+
* has enabled `prefers-reduced-motion`.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```tsx
|
|
29
|
+
* <TabsContent value="flights">
|
|
30
|
+
* <p>Available flights...</p>
|
|
31
|
+
* </TabsContent>
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @see https://www.w3.org/WAI/ARIA/apg/patterns/tabs/
|
|
35
|
+
*/
|
|
36
|
+
export declare const TabsContent: React.NamedExoticComponent<TabsContentProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file tabs-list.tsx
|
|
3
|
+
* MD3 Expressive TabsList — Container component for tab buttons.
|
|
4
|
+
*
|
|
5
|
+
* Responsibilities:
|
|
6
|
+
* - Applies variant (primary/secondary) layout and styling
|
|
7
|
+
* - Manages horizontal scroll for scrollable mode (52px edge padding per MD3)
|
|
8
|
+
* - Renders the bottom divider for secondary variant
|
|
9
|
+
* - Scopes Framer Motion LayoutGroup so indicators animate correctly
|
|
10
|
+
* when multiple <Tabs> instances are on the same page
|
|
11
|
+
* - Restores focus to activeTab when keyboard focus leaves the tablist
|
|
12
|
+
* (matches Google's `focusout` handler on <md-tabs>)
|
|
13
|
+
*/
|
|
14
|
+
import * as React from "react";
|
|
15
|
+
import type { TabsListProps } from "./tabs.types";
|
|
16
|
+
/**
|
|
17
|
+
* MD3 Expressive TabsList container component.
|
|
18
|
+
*
|
|
19
|
+
* Renders a horizontal row of `<Tab>` components with MD3-compliant
|
|
20
|
+
* layout (fixed or scrollable) and variant styling (primary or secondary).
|
|
21
|
+
*
|
|
22
|
+
* - **Primary**: Tabs divide available width equally, indicator width = content width.
|
|
23
|
+
* - **Secondary**: Tabs divide equally + full-width indicator + bottom divider line.
|
|
24
|
+
* - **Scrollable**: Tabs have min-width (90px), scroll horizontally with 52px edge padding.
|
|
25
|
+
* - **Focusout**: When focus leaves the tablist, roving focus resets to the active tab.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```tsx
|
|
29
|
+
* <TabsList variant="primary" scrollable={false}>
|
|
30
|
+
* <Tab value="tab1">Tab 1</Tab>
|
|
31
|
+
* <Tab value="tab2">Tab 2</Tab>
|
|
32
|
+
* </TabsList>
|
|
33
|
+
*
|
|
34
|
+
* <TabsList variant="secondary" scrollable={true} aria-label="Content sections">
|
|
35
|
+
* <Tab value="a">Alpha</Tab>
|
|
36
|
+
* <Tab value="b">Beta</Tab>
|
|
37
|
+
* </TabsList>
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare const TabsList: React.NamedExoticComponent<TabsListProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file tabs.tsx
|
|
3
|
+
* MD3 Expressive Tabs — Root context provider and state manager.
|
|
4
|
+
* Implements compound component pattern (similar to Radix UI).
|
|
5
|
+
* Spec: https://m3.material.io/components/tabs/overview
|
|
6
|
+
*/
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
import type { TabsContextValue, TabsProps } from "./tabs.types";
|
|
9
|
+
/**
|
|
10
|
+
* Hook to consume the Tabs context.
|
|
11
|
+
* Throws if used outside a `<Tabs>` root.
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
export declare function useTabsContext(): TabsContextValue;
|
|
15
|
+
/**
|
|
16
|
+
* MD3 Expressive Tabs root component.
|
|
17
|
+
*
|
|
18
|
+
* Manages tab selection state and provides context to all
|
|
19
|
+
* compound sub-components. Supports both controlled and
|
|
20
|
+
* uncontrolled usage.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* // Uncontrolled
|
|
25
|
+
* <Tabs defaultValue="flights">
|
|
26
|
+
* <TabsList variant="primary">
|
|
27
|
+
* <Tab value="flights">Flights</Tab>
|
|
28
|
+
* <Tab value="trips">Trips</Tab>
|
|
29
|
+
* </TabsList>
|
|
30
|
+
* <TabsContent value="flights">Flight content</TabsContent>
|
|
31
|
+
* <TabsContent value="trips">Trip content</TabsContent>
|
|
32
|
+
* </Tabs>
|
|
33
|
+
*
|
|
34
|
+
* // Controlled
|
|
35
|
+
* const [tab, setTab] = useState("flights");
|
|
36
|
+
* <Tabs value={tab} onValueChange={setTab}>...</Tabs>
|
|
37
|
+
*
|
|
38
|
+
* // Auto-activate mode (focus = select)
|
|
39
|
+
* <Tabs defaultValue="flights" autoActivate>...</Tabs>
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @see https://m3.material.io/components/tabs/overview
|
|
43
|
+
*/
|
|
44
|
+
export declare const Tabs: React.NamedExoticComponent<TabsProps & React.RefAttributes<HTMLDivElement>>;
|
|
45
|
+
/**
|
|
46
|
+
* Secondary context carrying variant + scrollable from <TabsList>.
|
|
47
|
+
* Separate from TabsContext so Tabs root doesn't need these props —
|
|
48
|
+
* they belong to the list, not the root.
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
export interface TabsListContextValue {
|
|
52
|
+
variant: "primary" | "secondary";
|
|
53
|
+
scrollable: boolean;
|
|
54
|
+
}
|
|
55
|
+
export declare const TabsListContext: React.Context<TabsListContextValue | null>;
|
|
56
|
+
/**
|
|
57
|
+
* Hook to consume TabsList-level context (variant, scrollable).
|
|
58
|
+
* @internal
|
|
59
|
+
*/
|
|
60
|
+
export declare function useTabsListContext(): TabsListContextValue;
|