@bug-on/md3-react 2.0.0 → 2.0.2
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/dist/index.d.ts +4 -0
- package/dist/index.js +10563 -7543
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +10495 -7499
- package/dist/index.mjs.map +1 -1
- package/dist/ui/app-bar/app-bar-column.d.ts +28 -0
- package/dist/ui/app-bar/app-bar-item-button.d.ts +16 -0
- package/dist/ui/app-bar/app-bar-overflow-indicator.d.ts +18 -0
- package/dist/ui/app-bar/app-bar-row.d.ts +36 -0
- package/dist/ui/app-bar/app-bar.tokens.d.ts +184 -0
- package/dist/ui/app-bar/app-bar.types.d.ts +392 -0
- package/dist/ui/app-bar/bottom-app-bar.d.ts +31 -0
- package/dist/ui/app-bar/docked-toolbar.d.ts +25 -0
- package/dist/ui/app-bar/hooks/use-app-bar-scroll.d.ts +42 -0
- package/dist/ui/app-bar/hooks/use-flexible-app-bar.d.ts +37 -0
- package/dist/ui/app-bar/index.d.ts +39 -0
- package/dist/ui/app-bar/large-flexible-app-bar.d.ts +26 -0
- package/dist/ui/app-bar/medium-flexible-app-bar.d.ts +28 -0
- package/dist/ui/app-bar/search-app-bar.d.ts +43 -0
- package/dist/ui/app-bar/search-view.d.ts +54 -0
- package/dist/ui/app-bar/small-app-bar.d.ts +37 -0
- package/dist/ui/slider/hooks/useSliderMath.d.ts +101 -0
- package/dist/ui/slider/index.d.ts +9 -0
- package/dist/ui/slider/range-slider.d.ts +47 -0
- package/dist/ui/slider/slider-thumb.d.ts +33 -0
- package/dist/ui/slider/slider-track.d.ts +25 -0
- package/dist/ui/slider/slider.d.ts +60 -0
- package/dist/ui/slider/slider.tokens.d.ts +151 -0
- package/dist/ui/slider/slider.types.d.ts +259 -0
- package/dist/ui/snackbar/snackbar.d.ts +1 -0
- package/dist/ui/theme-provider/index.d.ts +31 -1
- package/dist/ui/toc.d.ts +7 -1
- package/package.json +1 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file bottom-app-bar.tsx
|
|
3
|
+
* MD3 Expressive Bottom App Bar.
|
|
4
|
+
*
|
|
5
|
+
* Fixed to bottom of screen. Contains action icons and optional FAB.
|
|
6
|
+
* Height: 80px | Background: surface-container | Elevation: Level2 (always)
|
|
7
|
+
*
|
|
8
|
+
* @see docs/m3/app-bars/BottomAppBarTokens.kt
|
|
9
|
+
*/
|
|
10
|
+
import type { BottomAppBarProps } from "./app-bar.types";
|
|
11
|
+
/**
|
|
12
|
+
* MD3 Expressive Bottom App Bar.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* // With FAB
|
|
17
|
+
* <BottomAppBar
|
|
18
|
+
* actions={
|
|
19
|
+
* <>
|
|
20
|
+
* <IconButton aria-label="Check"><Icon>check_box</Icon></IconButton>
|
|
21
|
+
* <IconButton aria-label="Brush"><Icon>brush</Icon></IconButton>
|
|
22
|
+
* </>
|
|
23
|
+
* }
|
|
24
|
+
* floatingActionButton={<FAB aria-label="Compose">...</FAB>}
|
|
25
|
+
* />
|
|
26
|
+
*
|
|
27
|
+
* // Auto-hide on scroll
|
|
28
|
+
* <BottomAppBar scrollBehavior="hidden" actions={...} />
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function BottomAppBar({ actions, floatingActionButton, scrollBehavior, scrollElement, className, }: BottomAppBarProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file docked-toolbar.tsx
|
|
3
|
+
* MD3 Expressive Docked Toolbar.
|
|
4
|
+
*
|
|
5
|
+
* Secondary navigation component. NOT an App Bar.
|
|
6
|
+
* Usually appears directly below the main App Bar.
|
|
7
|
+
* Height: 64px | Background: surface-container
|
|
8
|
+
* Typical content: chips, segmented buttons, filter actions.
|
|
9
|
+
*
|
|
10
|
+
* @see docs/m3/app-bars/DockedToolbarTokens.kt
|
|
11
|
+
*/
|
|
12
|
+
import type { DockedToolbarProps } from "./app-bar.types";
|
|
13
|
+
/**
|
|
14
|
+
* MD3 Expressive Docked Toolbar.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* <DockedToolbar aria-label="Filter options">
|
|
19
|
+
* <Chip label="All" selected onClick={() => setFilter('all')} />
|
|
20
|
+
* <Chip label="Unread" onClick={() => setFilter('unread')} />
|
|
21
|
+
* <Chip label="Starred" onClick={() => setFilter('starred')} />
|
|
22
|
+
* </DockedToolbar>
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare function DockedToolbar({ children, "aria-label": ariaLabel, className, }: DockedToolbarProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file use-app-bar-scroll.ts
|
|
3
|
+
* MD3 Expressive App Bar — Scroll behavior hook.
|
|
4
|
+
*
|
|
5
|
+
* Tracks scroll state for App Bar behaviors:
|
|
6
|
+
* - `pinned`: background color change only
|
|
7
|
+
* - `enterAlways`: hide/show based on scroll direction
|
|
8
|
+
* - `exitUntilCollapsed`: drives collapse fraction (0 = expanded, 1 = collapsed)
|
|
9
|
+
*/
|
|
10
|
+
import * as React from "react";
|
|
11
|
+
import type { AppBarScrollBehavior, UseAppBarScrollReturn } from "../app-bar.types";
|
|
12
|
+
interface UseAppBarScrollOptions {
|
|
13
|
+
/** Ref to the scrollable container. Defaults to `window`. */
|
|
14
|
+
scrollElement?: React.RefObject<HTMLElement | null>;
|
|
15
|
+
/** Scroll behavior mode. @default "pinned" */
|
|
16
|
+
behavior?: AppBarScrollBehavior;
|
|
17
|
+
/** Collapsed height in px — used for `exitUntilCollapsed`. @default 64 */
|
|
18
|
+
collapsedHeight?: number;
|
|
19
|
+
/** Expanded height in px — used for `exitUntilCollapsed`. @default 112 */
|
|
20
|
+
expandedHeight?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Tracks scroll position and derives App Bar state for all three behaviors.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* // pinned (color change only)
|
|
28
|
+
* const { isScrolled } = useAppBarScroll({ behavior: 'pinned' });
|
|
29
|
+
*
|
|
30
|
+
* // enterAlways (hide/show)
|
|
31
|
+
* const { isHidden } = useAppBarScroll({ behavior: 'enterAlways' });
|
|
32
|
+
*
|
|
33
|
+
* // exitUntilCollapsed (collapse animation)
|
|
34
|
+
* const { collapsedFraction } = useAppBarScroll({
|
|
35
|
+
* behavior: 'exitUntilCollapsed',
|
|
36
|
+
* collapsedHeight: 64,
|
|
37
|
+
* expandedHeight: 112,
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function useAppBarScroll({ scrollElement, behavior, collapsedHeight, expandedHeight, }?: UseAppBarScrollOptions): UseAppBarScrollReturn;
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file use-flexible-app-bar.ts
|
|
3
|
+
* Shared animation hook for MediumFlexibleAppBar and LargeFlexibleAppBar.
|
|
4
|
+
*
|
|
5
|
+
* Extracts common scroll-driven animation state to avoid duplication.
|
|
6
|
+
*/
|
|
7
|
+
import type { MotionValue } from "motion/react";
|
|
8
|
+
import type { RefObject } from "react";
|
|
9
|
+
import type { AppBarColors } from "../app-bar.types";
|
|
10
|
+
interface UseFlexibleAppBarOptions {
|
|
11
|
+
collapsedHeight: number;
|
|
12
|
+
expandedHeight: number;
|
|
13
|
+
scrollElement?: RefObject<HTMLElement | null>;
|
|
14
|
+
colors?: AppBarColors;
|
|
15
|
+
/** [start, end] progress range where large title fades from 1 → 0 */
|
|
16
|
+
largeTitleFadeRange: [number, number];
|
|
17
|
+
/** [start, end] progress range where small title fades from 0 → 1 */
|
|
18
|
+
smallTitleFadeRange: [number, number];
|
|
19
|
+
/** [start, end] progress range where subtitle fades from 1 → 0 */
|
|
20
|
+
subtitleFadeRange: [number, number];
|
|
21
|
+
/** [start, end] progress range where headerContent fades from 1 → 0 */
|
|
22
|
+
headerContentFadeRange: [number, number];
|
|
23
|
+
/** [expanded, collapsed] Y offset for large title */
|
|
24
|
+
largeTitleYRange: [number, number];
|
|
25
|
+
}
|
|
26
|
+
export interface FlexibleAppBarAnimationState {
|
|
27
|
+
height: MotionValue<number>;
|
|
28
|
+
currentBg: string;
|
|
29
|
+
cssTransition: string | undefined;
|
|
30
|
+
largeTitleOpacity: MotionValue<number>;
|
|
31
|
+
largeTitleY: MotionValue<number>;
|
|
32
|
+
smallTitleOpacity: MotionValue<number>;
|
|
33
|
+
subtitleOpacity: MotionValue<number>;
|
|
34
|
+
headerContentOpacity: MotionValue<number>;
|
|
35
|
+
}
|
|
36
|
+
export declare function useFlexibleAppBar({ collapsedHeight, expandedHeight, scrollElement, colors, largeTitleFadeRange, smallTitleFadeRange, subtitleFadeRange, headerContentFadeRange, largeTitleYRange, }: UseFlexibleAppBarOptions): FlexibleAppBarAnimationState;
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file index.ts
|
|
3
|
+
* MD3 Expressive App Bar — Public API exports.
|
|
4
|
+
*
|
|
5
|
+
* Components:
|
|
6
|
+
* - SmallAppBar: Single-row, 64px height, TitleLarge
|
|
7
|
+
* - MediumFlexibleAppBar: Collapsible, HeadlineMedium → TitleLarge
|
|
8
|
+
* - LargeFlexibleAppBar: Collapsible, DisplaySmall → TitleLarge
|
|
9
|
+
* - SearchAppBar: Pill search bar with layoutId for SearchView transition
|
|
10
|
+
* - SearchView: Full-screen search overlay
|
|
11
|
+
* - SearchViewContainer: Wraps SearchView with AnimatePresence
|
|
12
|
+
* - BottomAppBar: Fixed bottom nav with optional FAB
|
|
13
|
+
* - DockedToolbar: Secondary navigation bar
|
|
14
|
+
* - AppBarRow: Horizontal actions with overflow
|
|
15
|
+
* - AppBarColumn: Vertical actions with overflow
|
|
16
|
+
* - AppBarOverflowIndicator: More-vert dropdown trigger
|
|
17
|
+
*
|
|
18
|
+
* Hook:
|
|
19
|
+
* - useAppBarScroll: Drives scroll-based behavior (pinned / enterAlways / exitUntilCollapsed)
|
|
20
|
+
*
|
|
21
|
+
* Tokens:
|
|
22
|
+
* - AppBarTokens: Dimensional tokens (heights, spacing)
|
|
23
|
+
* - appBarTypography: Typography class strings
|
|
24
|
+
* - APP_BAR_COLORS: CSS custom property color references
|
|
25
|
+
* - Animation constants (APP_BAR_COLOR_TRANSITION, etc.)
|
|
26
|
+
*/
|
|
27
|
+
export { APP_BAR_BOTTOM_SPRING, APP_BAR_COLOR_TRANSITION, APP_BAR_COLORS, APP_BAR_ENTER_ALWAYS_SPRING, APP_BAR_TITLE_FADE, AppBarTokens, appBarTypography, SEARCH_VIEW_SPRING, } from "./app-bar.tokens";
|
|
28
|
+
export type { AppBarColors, AppBarColumnProps, AppBarItem, AppBarItemType, AppBarMenuState, AppBarOverflowIndicatorProps, AppBarRowProps, AppBarScrollBehavior, BaseAppBarProps, BottomAppBarProps, DockedToolbarProps, FlexibleAppBarProps, SearchAppBarProps, SearchBarVariant, SearchViewProps, SmallAppBarProps, TitleAlignment, UseAppBarScrollReturn, } from "./app-bar.types";
|
|
29
|
+
export { AppBarColumn } from "./app-bar-column";
|
|
30
|
+
export { AppBarOverflowIndicator } from "./app-bar-overflow-indicator";
|
|
31
|
+
export { AppBarRow } from "./app-bar-row";
|
|
32
|
+
export { BottomAppBar } from "./bottom-app-bar";
|
|
33
|
+
export { DockedToolbar } from "./docked-toolbar";
|
|
34
|
+
export { useAppBarScroll } from "./hooks/use-app-bar-scroll";
|
|
35
|
+
export { LargeFlexibleAppBar } from "./large-flexible-app-bar";
|
|
36
|
+
export { MediumFlexibleAppBar } from "./medium-flexible-app-bar";
|
|
37
|
+
export { SearchAppBar } from "./search-app-bar";
|
|
38
|
+
export { SearchView, SearchViewContainer } from "./search-view";
|
|
39
|
+
export { SmallAppBar } from "./small-app-bar";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file large-flexible-app-bar.tsx
|
|
3
|
+
* MD3 Expressive Large Flexible App Bar.
|
|
4
|
+
*
|
|
5
|
+
* Like MediumFlexibleAppBar but with larger typography.
|
|
6
|
+
* Expanded height: 120px (no subtitle) / 152px (with subtitle)
|
|
7
|
+
* Collapsed height: 64px
|
|
8
|
+
* Title crossfade: DisplaySmall (expanded) ↔ TitleLarge (collapsed)
|
|
9
|
+
*
|
|
10
|
+
* @see docs/m3/app-bars/AppBarLargeFlexibleTokens.kt
|
|
11
|
+
*/
|
|
12
|
+
import type { FlexibleAppBarProps } from "./app-bar.types";
|
|
13
|
+
/**
|
|
14
|
+
* MD3 Expressive Large Flexible App Bar.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* <LargeFlexibleAppBar
|
|
19
|
+
* title="Discover"
|
|
20
|
+
* subtitle="Trending today"
|
|
21
|
+
* navigationIcon={<IconButton aria-label="Open menu"><Icon>menu</Icon></IconButton>}
|
|
22
|
+
* headerContent={<img src="/banner.jpg" alt="" className="rounded-xl h-20 w-full object-cover" />}
|
|
23
|
+
* />
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function LargeFlexibleAppBar({ title, subtitle, titleAlignment, navigationIcon, actions, colors, scrollElement, headerContent, collapsedHeight, expandedHeight, className, }: FlexibleAppBarProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file medium-flexible-app-bar.tsx
|
|
3
|
+
* MD3 Expressive Medium Flexible App Bar.
|
|
4
|
+
*
|
|
5
|
+
* Two-row layout in expanded state, collapses to single row on scroll.
|
|
6
|
+
* Expanded height: 112px (no subtitle) / 136px (with subtitle)
|
|
7
|
+
* Collapsed height: 64px
|
|
8
|
+
* Title crossfade: HeadlineMedium (expanded) ↔ TitleLarge (collapsed)
|
|
9
|
+
*
|
|
10
|
+
* Supports `exitUntilCollapsed` scroll behavior only.
|
|
11
|
+
*
|
|
12
|
+
* @see docs/m3/app-bars/AppBarMediumFlexibleTokens.kt
|
|
13
|
+
*/
|
|
14
|
+
import type { FlexibleAppBarProps } from "./app-bar.types";
|
|
15
|
+
/**
|
|
16
|
+
* MD3 Expressive Medium Flexible App Bar.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* <MediumFlexibleAppBar
|
|
21
|
+
* title="Settings"
|
|
22
|
+
* subtitle="Manage your preferences"
|
|
23
|
+
* navigationIcon={<IconButton aria-label="Go back"><Icon>arrow_back</Icon></IconButton>}
|
|
24
|
+
* actions={<IconButton aria-label="More options"><Icon>more_vert</Icon></IconButton>}
|
|
25
|
+
* />
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function MediumFlexibleAppBar({ title, subtitle, titleAlignment, navigationIcon, actions, colors, scrollElement, headerContent, collapsedHeight, expandedHeight, className, }: FlexibleAppBarProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file search-app-bar.tsx
|
|
3
|
+
* MD3 Expressive Search App Bar.
|
|
4
|
+
*
|
|
5
|
+
* New variant in MD3 Expressive (May 2025).
|
|
6
|
+
* Replaces the title area with a pill-shaped search bar.
|
|
7
|
+
* Uses Framer Motion layoutId to enable shared element transition with <SearchView>.
|
|
8
|
+
*
|
|
9
|
+
* @see docs/m3/app-bars/AppBar.kt — SearchAppBar
|
|
10
|
+
*/
|
|
11
|
+
import type { SearchAppBarProps } from "./app-bar.types";
|
|
12
|
+
/**
|
|
13
|
+
* MD3 Expressive Search App Bar.
|
|
14
|
+
*
|
|
15
|
+
* When the search bar is clicked, callers should open a `<SearchView>` overlay.
|
|
16
|
+
* Uses Framer Motion `layoutId` (via `searchBarId`) for a smooth shared-element
|
|
17
|
+
* transition between this bar and the search view.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* const [searchOpen, setSearchOpen] = useState(false);
|
|
22
|
+
*
|
|
23
|
+
* <SearchAppBar
|
|
24
|
+
* searchBarId="main-search"
|
|
25
|
+
* searchPlaceholder="Search messages..."
|
|
26
|
+
* onSearchFocus={() => setSearchOpen(true)}
|
|
27
|
+
* trailingSearchActions={
|
|
28
|
+
* <IconButton aria-label="Voice search"><Icon>mic</Icon></IconButton>
|
|
29
|
+
* }
|
|
30
|
+
* externalActions={<Avatar src={user.avatar} />}
|
|
31
|
+
* />
|
|
32
|
+
*
|
|
33
|
+
* <AnimatePresence>
|
|
34
|
+
* {searchOpen && (
|
|
35
|
+
* <SearchView
|
|
36
|
+
* searchBarId="main-search"
|
|
37
|
+
* onClose={() => setSearchOpen(false)}
|
|
38
|
+
* />
|
|
39
|
+
* )}
|
|
40
|
+
* </AnimatePresence>
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare function SearchAppBar({ searchPlaceholder, searchValue, onSearchFocus, leadingSearchIcon, trailingSearchActions, externalActions, navigationIcon, colors, scrollBehavior, scrollElement, searchBarId, className, }: SearchAppBarProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file search-view.tsx
|
|
3
|
+
* MD3 Expressive Search View.
|
|
4
|
+
*
|
|
5
|
+
* Full-screen overlay activated when a SearchAppBar's search bar is clicked.
|
|
6
|
+
* Shares a Framer Motion `layoutId` with the SearchAppBar search bar for a
|
|
7
|
+
* smooth shared element transition.
|
|
8
|
+
*
|
|
9
|
+
* Usage pattern:
|
|
10
|
+
* ```tsx
|
|
11
|
+
* const [open, setOpen] = useState(false);
|
|
12
|
+
*
|
|
13
|
+
* // In render:
|
|
14
|
+
* <SearchAppBar searchBarId="main" onSearchFocus={() => setOpen(true)} />
|
|
15
|
+
* <AnimatePresence>
|
|
16
|
+
* {open && <SearchView searchBarId="main" onClose={() => setOpen(false)} />}
|
|
17
|
+
* </AnimatePresence>
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* Design notes:
|
|
21
|
+
* - The SearchView is intentionally separate from SearchAppBar to allow consumers
|
|
22
|
+
* to customize the results/suggestions content without coupling.
|
|
23
|
+
* - The `searchBarId` prop must match between SearchAppBar and SearchView to
|
|
24
|
+
* enable the shared element transition.
|
|
25
|
+
* - Focus is moved to the search input when the view opens.
|
|
26
|
+
* - Escape key closes the view and returns focus to the search bar.
|
|
27
|
+
*/
|
|
28
|
+
import type { SearchViewProps } from "./app-bar.types";
|
|
29
|
+
/**
|
|
30
|
+
* MD3 Expressive Search View.
|
|
31
|
+
*
|
|
32
|
+
* Renders a full-screen search overlay with a shared element transition
|
|
33
|
+
* from the triggering `<SearchAppBar>` search bar.
|
|
34
|
+
*
|
|
35
|
+
* Mount/unmount this component via `<AnimatePresence>` in the consumer.
|
|
36
|
+
*/
|
|
37
|
+
export declare function SearchView({ searchBarId, value, onChange, onClose, placeholder, children, leadingIcon, trailingAction, className, }: SearchViewProps): import("react/jsx-runtime").JSX.Element;
|
|
38
|
+
/**
|
|
39
|
+
* Convenience wrapper that handles the AnimatePresence + open state.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* <SearchBar
|
|
44
|
+
* isOpen={searchOpen}
|
|
45
|
+
* onClose={() => setSearchOpen(false)}
|
|
46
|
+
* searchBarId="main-search"
|
|
47
|
+
* >
|
|
48
|
+
* <SearchResultsList results={results} />
|
|
49
|
+
* </SearchBar>
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare function SearchViewContainer({ isOpen, ...props }: SearchViewProps & {
|
|
53
|
+
isOpen: boolean;
|
|
54
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file small-app-bar.tsx
|
|
3
|
+
* MD3 Expressive Small App Bar.
|
|
4
|
+
*
|
|
5
|
+
* Single-row layout: [navigationIcon][title + subtitle][actions]
|
|
6
|
+
* Height: 64px | Title: TitleLarge (22sp) | Subtitle: LabelMedium (12sp)
|
|
7
|
+
*
|
|
8
|
+
* Scroll behaviors:
|
|
9
|
+
* - pinned: changes background color surface → surface-container
|
|
10
|
+
* - enterAlways: slides up when scrolling down, slides down when scrolling up
|
|
11
|
+
*
|
|
12
|
+
* @see docs/m3/app-bars/AppBarSmallTokens.kt
|
|
13
|
+
*/
|
|
14
|
+
import type { SmallAppBarProps } from "./app-bar.types";
|
|
15
|
+
/**
|
|
16
|
+
* MD3 Expressive Small App Bar.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* // Left-aligned (default)
|
|
21
|
+
* <SmallAppBar
|
|
22
|
+
* title="Inbox"
|
|
23
|
+
* navigationIcon={<IconButton aria-label="Go back"><Icon>arrow_back</Icon></IconButton>}
|
|
24
|
+
* actions={<IconButton aria-label="Search"><Icon>search</Icon></IconButton>}
|
|
25
|
+
* scrollBehavior="pinned"
|
|
26
|
+
* />
|
|
27
|
+
*
|
|
28
|
+
* // Center-aligned with subtitle
|
|
29
|
+
* <SmallAppBar
|
|
30
|
+
* title="Profile"
|
|
31
|
+
* subtitle="@username"
|
|
32
|
+
* titleAlignment="center"
|
|
33
|
+
* scrollBehavior="enterAlways"
|
|
34
|
+
* />
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function SmallAppBar({ title, subtitle, titleAlignment, navigationIcon, actions, colors, scrollBehavior, scrollElement, className, }: SmallAppBarProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file useSliderMath.ts
|
|
3
|
+
* MD3 Expressive Slider — Math utility hook.
|
|
4
|
+
*
|
|
5
|
+
* Handles all slider math in one place:
|
|
6
|
+
* - value coercion to [min, max]
|
|
7
|
+
* - step snapping for discrete mode
|
|
8
|
+
* - value ↔ percent conversion
|
|
9
|
+
* - keyboard delta calculation
|
|
10
|
+
* - tick position generation
|
|
11
|
+
*
|
|
12
|
+
* Exported as a pure hook for testability and reuse in both Slider and RangeSlider.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Clamps `value` to the closed interval `[min, max]`.
|
|
16
|
+
*
|
|
17
|
+
* @example coerceValue(150, 0, 100) → 100
|
|
18
|
+
*/
|
|
19
|
+
export declare function coerceValue(value: number, min: number, max: number): number;
|
|
20
|
+
/**
|
|
21
|
+
* Rounds `value` to the nearest multiple of `step` relative to `min`.
|
|
22
|
+
* When `step === 0`, returns `value` unchanged (continuous mode).
|
|
23
|
+
*
|
|
24
|
+
* @example snapToStep(23, 0, 10) → 20
|
|
25
|
+
* @example snapToStep(27, 0, 10) → 30
|
|
26
|
+
*/
|
|
27
|
+
export declare function snapToStep(value: number, min: number, step: number): number;
|
|
28
|
+
/**
|
|
29
|
+
* Converts a raw value to a 0–1 fraction of the [min, max] range.
|
|
30
|
+
* Returns 0 when max === min (degenerate range).
|
|
31
|
+
*
|
|
32
|
+
* @example valueToPercent(50, 0, 100) → 0.5
|
|
33
|
+
*/
|
|
34
|
+
export declare function valueToPercent(value: number, min: number, max: number): number;
|
|
35
|
+
/**
|
|
36
|
+
* Converts a 0–1 fraction to a value within [min, max], then snaps to step.
|
|
37
|
+
* The result is also coerced to [min, max].
|
|
38
|
+
*
|
|
39
|
+
* @example percentToValue(0.5, 0, 100, 10) → 50
|
|
40
|
+
* @example percentToValue(0.23, 0, 100, 10) → 20
|
|
41
|
+
*/
|
|
42
|
+
export declare function percentToValue(percent: number, min: number, max: number, step: number): number;
|
|
43
|
+
/**
|
|
44
|
+
* Computes the keyboard increment for a given key.
|
|
45
|
+
*
|
|
46
|
+
* Key mappings per WAI-ARIA Slider pattern:
|
|
47
|
+
* - ArrowRight/Up → +step (or +1% of range if continuous)
|
|
48
|
+
* - ArrowLeft/Down → -step (or -1% of range)
|
|
49
|
+
* - PageUp → +10% of range (snapped to step)
|
|
50
|
+
* - PageDown → -10% of range (snapped to step)
|
|
51
|
+
* - Home / End → handled by the caller (jump to min/max)
|
|
52
|
+
*
|
|
53
|
+
* @returns the signed delta to add to current value, or `null` for Home/End.
|
|
54
|
+
*/
|
|
55
|
+
export declare function getKeyboardDelta(key: string, step: number, min: number, max: number): number | null;
|
|
56
|
+
/**
|
|
57
|
+
* Generates the list of value positions for tick marks.
|
|
58
|
+
* Returns an empty array when `step === 0` (continuous mode).
|
|
59
|
+
*
|
|
60
|
+
* @example generateTicks(0, 100, 10) → [0, 10, 20, ..., 100]
|
|
61
|
+
*/
|
|
62
|
+
export declare function generateTicks(min: number, max: number, step: number): number[];
|
|
63
|
+
export interface UseSliderMathOptions {
|
|
64
|
+
min: number;
|
|
65
|
+
max: number;
|
|
66
|
+
step: number;
|
|
67
|
+
}
|
|
68
|
+
export interface UseSliderMathReturn {
|
|
69
|
+
/** Clamp value to [min, max]. */
|
|
70
|
+
coerce: (v: number) => number;
|
|
71
|
+
/** Snap value to nearest step (no-op if step=0). */
|
|
72
|
+
snap: (v: number) => number;
|
|
73
|
+
/** Value → percent [0, 1]. */
|
|
74
|
+
toPercent: (v: number) => number;
|
|
75
|
+
/** Percent [0, 1] → snapped value. */
|
|
76
|
+
fromPercent: (pct: number) => number;
|
|
77
|
+
/**
|
|
78
|
+
* Signed delta for a keyboard key.
|
|
79
|
+
* Returns `null` for Home/End (jump to min/max, handled by caller).
|
|
80
|
+
*/
|
|
81
|
+
getKeyDelta: (key: string) => number | null;
|
|
82
|
+
/** Tick positions. Empty array in continuous mode. */
|
|
83
|
+
ticks: number[];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Math utility hook for MD3 Slider components.
|
|
87
|
+
*
|
|
88
|
+
* Memoizes all math functions against `min`, `max`, `step` changes.
|
|
89
|
+
* Use this in both `<Slider>` and `<RangeSlider>` to share logic.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts
|
|
93
|
+
* const { coerce, snap, toPercent, fromPercent, getKeyDelta, ticks } =
|
|
94
|
+
* useSliderMath({ min: 0, max: 100, step: 10 });
|
|
95
|
+
*
|
|
96
|
+
* const percent = toPercent(value); // → 0.5 for value=50
|
|
97
|
+
* const newValue = fromPercent(dragPercent); // → 50
|
|
98
|
+
* const delta = getKeyDelta("ArrowRight"); // → 10
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export declare function useSliderMath({ min, max, step, }: UseSliderMathOptions): UseSliderMathReturn;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file index.ts
|
|
3
|
+
* MD3 Expressive Slider — Public API exports.
|
|
4
|
+
*/
|
|
5
|
+
export { RangeSlider } from "./range-slider";
|
|
6
|
+
export { Slider } from "./slider";
|
|
7
|
+
export { SliderColors, SliderTokens } from "./slider.tokens";
|
|
8
|
+
export type { RangeSliderProps, SliderOrientation, SliderProps, SliderTrackSize, SliderVariant, } from "./slider.types";
|
|
9
|
+
export { SliderThumb } from "./slider-thumb";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file range-slider.tsx
|
|
3
|
+
* MD3 Expressive RangeSlider — Two-thumb slider with crossover prevention.
|
|
4
|
+
*
|
|
5
|
+
* Extends the core Slider architecture with:
|
|
6
|
+
* - Two SliderThumb instances (start + end)
|
|
7
|
+
* - Crossover prevention: start cannot exceed end and vice versa
|
|
8
|
+
* - Z-index management: last-dragged thumb always on top
|
|
9
|
+
* - Active track spans from startPercent to endPercent
|
|
10
|
+
*
|
|
11
|
+
* Design decisions:
|
|
12
|
+
* - The track renders a custom "range" segment between the two thumbs.
|
|
13
|
+
* - Each thumb has independent onValueChange callbacks + crossover clamping.
|
|
14
|
+
* - Last-active thumb gets zIndex=2 to appear on top when thumbs are at same position.
|
|
15
|
+
*
|
|
16
|
+
* @see https://m3.material.io/components/sliders/overview
|
|
17
|
+
* @see docs/m3/sliders/Slider.kt#RangeSlider
|
|
18
|
+
*/
|
|
19
|
+
import * as React from "react";
|
|
20
|
+
import type { RangeSliderProps } from "./slider.types";
|
|
21
|
+
/**
|
|
22
|
+
* MD3 Expressive RangeSlider component.
|
|
23
|
+
*
|
|
24
|
+
* Two-thumb slider where the active track spans between the two thumbs.
|
|
25
|
+
* Thumbs cannot cross each other (crossover prevention built in).
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```tsx
|
|
29
|
+
* // Controlled
|
|
30
|
+
* <RangeSlider
|
|
31
|
+
* value={[20, 80]}
|
|
32
|
+
* onValueChange={([start, end]) => setRange([start, end])}
|
|
33
|
+
* aria-label="Price range"
|
|
34
|
+
* />
|
|
35
|
+
*
|
|
36
|
+
* // Discrete
|
|
37
|
+
* <RangeSlider defaultValue={[0, 100]} step={10} />
|
|
38
|
+
*
|
|
39
|
+
* // Vertical
|
|
40
|
+
* <div className="h-64">
|
|
41
|
+
* <RangeSlider defaultValue={[25, 75]} orientation="vertical" />
|
|
42
|
+
* </div>
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @see https://m3.material.io/components/sliders/overview
|
|
46
|
+
*/
|
|
47
|
+
export declare const RangeSlider: React.NamedExoticComponent<RangeSliderProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file slider-thumb.tsx
|
|
3
|
+
* MD3 Expressive Slider — Animated pill-shaped thumb (handle).
|
|
4
|
+
*
|
|
5
|
+
* Design decisions:
|
|
6
|
+
* 1. POINTER EVENTS (not Framer Motion drag): We use React pointer events
|
|
7
|
+
* + useMotionValue for real-time position tracking without re-render lag.
|
|
8
|
+
* Framer Motion's `drag` prop adds momentum/inertia that conflicts with MD3
|
|
9
|
+
* precise positioning; direct pointer handling gives us full control.
|
|
10
|
+
* 2. SQUEEZE ANIMATION: `whileTap` shrinks width from 4px → 2px via spring.
|
|
11
|
+
* 3. INVERTED Y-AXIS: Vertical slider maps bottom=min, top=max by inverting
|
|
12
|
+
* the pointer delta direction.
|
|
13
|
+
* 4. VALUE INDICATOR: AnimatePresence pill tooltip with teardrop origin point.
|
|
14
|
+
* 5. ACCESSIBILITY: role=slider, full ARIA attributes, keyboard nav.
|
|
15
|
+
* 6. prefers-reduced-motion: Disables all animations for accessibility.
|
|
16
|
+
*
|
|
17
|
+
* @see docs/m3/sliders/Slider.kt#SliderDefaults.Thumb
|
|
18
|
+
*/
|
|
19
|
+
import * as React from "react";
|
|
20
|
+
import type { SliderThumbProps } from "./slider.types";
|
|
21
|
+
/**
|
|
22
|
+
* MD3 Expressive Slider Thumb (handle).
|
|
23
|
+
*
|
|
24
|
+
* - Pill shape: 4px wide × 44px tall by default.
|
|
25
|
+
* - Squeezes to 2px wide on press/drag (Framer Motion spring).
|
|
26
|
+
* - Floats above track via absolute positioning at `percent` along the track.
|
|
27
|
+
* - Handles pointer drag via React PointerEvents + useMotionValue.
|
|
28
|
+
* - Full keyboard navigation per WAI-ARIA Slider pattern.
|
|
29
|
+
* - Value indicator tooltip with AnimatePresence.
|
|
30
|
+
*
|
|
31
|
+
* @internal — consumed by `<Slider>` and `<RangeSlider>`
|
|
32
|
+
*/
|
|
33
|
+
export declare const SliderThumb: React.NamedExoticComponent<SliderThumbProps>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file slider-track.tsx
|
|
3
|
+
* MD3 Expressive Slider — Track with asymmetric corner radii, 6px thumb gaps,
|
|
4
|
+
* discrete tick marks, and centered-mode support.
|
|
5
|
+
*
|
|
6
|
+
* Design decisions:
|
|
7
|
+
* 1. GAP MATH: The 6px gap between track and thumb is calculated mathematically
|
|
8
|
+
* using CSS calc() — NOT using margin/padding which would break layout.
|
|
9
|
+
* 2. ASYMMETRIC RADII: Inner corners (facing thumb) = 2px; outer ends = size/2 (pill cap).
|
|
10
|
+
* 3. CENTERED MODE: Active segment spans from 50% outward to thumb, not from min.
|
|
11
|
+
* 4. TICKS: 4×4px dots positioned absolutely along track center axis.
|
|
12
|
+
* Color differs on active vs inactive portions of the track.
|
|
13
|
+
* 5. VERTICAL: Uses height/top instead of width/left, with inverted axis
|
|
14
|
+
* (bottom=0%, top=100%) per MD3 vertical spec.
|
|
15
|
+
*
|
|
16
|
+
* @see docs/m3/sliders/Slider.kt#SliderDefaults.Track
|
|
17
|
+
*/
|
|
18
|
+
import * as React from "react";
|
|
19
|
+
import type { SliderTrackProps } from "./slider.types";
|
|
20
|
+
/**
|
|
21
|
+
* MD3 Expressive Slider Track.
|
|
22
|
+
*/
|
|
23
|
+
export declare const SliderTrack: React.NamedExoticComponent<Omit<SliderTrackProps, "step"> & {
|
|
24
|
+
ticks?: number[];
|
|
25
|
+
}>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file slider.tsx
|
|
3
|
+
* MD3 Expressive Slider — Main composition component.
|
|
4
|
+
*
|
|
5
|
+
* Supports:
|
|
6
|
+
* - Controlled and uncontrolled usage patterns
|
|
7
|
+
* - Horizontal and vertical orientations
|
|
8
|
+
* - Discrete mode (step snapping + tick marks)
|
|
9
|
+
* - Centered active track (isCentered)
|
|
10
|
+
* - Value indicator tooltip
|
|
11
|
+
* - Full keyboard navigation (ARIA Slider pattern)
|
|
12
|
+
* - Click-to-jump on track
|
|
13
|
+
*
|
|
14
|
+
* Architecture: Flat (no Context needed for single thumb variant).
|
|
15
|
+
* Track + Thumb are internal sub-components composed here.
|
|
16
|
+
*
|
|
17
|
+
* @see https://m3.material.io/components/sliders/overview
|
|
18
|
+
* @see docs/m3/sliders/Slider.kt
|
|
19
|
+
*/
|
|
20
|
+
import * as React from "react";
|
|
21
|
+
import type { SliderProps } from "./slider.types";
|
|
22
|
+
/**
|
|
23
|
+
* MD3 Expressive Slider component.
|
|
24
|
+
*
|
|
25
|
+
* A pill-shaped vertical handle that slides along a rounded track.
|
|
26
|
+
* Supports continuous and discrete (step) modes, horizontal/vertical
|
|
27
|
+
* orientations, and centered active track.
|
|
28
|
+
*
|
|
29
|
+
* Features:
|
|
30
|
+
* - M3 Expressive handle: 4px pill that squeezes to 2px on press
|
|
31
|
+
* - 6px transparent gap between track and thumb
|
|
32
|
+
* - Asymmetric corner radii: outer ends=9999px, inner ends=2px
|
|
33
|
+
* - Tick marks for discrete mode
|
|
34
|
+
* - Optional floating value indicator tooltip
|
|
35
|
+
* - Full keyboard navigation per WAI-ARIA Slider pattern
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```tsx
|
|
39
|
+
* // Basic controlled
|
|
40
|
+
* <Slider value={volume} onValueChange={setVolume} aria-label="Volume" />
|
|
41
|
+
*
|
|
42
|
+
* // Discrete with ticks
|
|
43
|
+
* <Slider defaultValue={0} step={10} min={0} max={100} />
|
|
44
|
+
*
|
|
45
|
+
* // Vertical orientation
|
|
46
|
+
* <div className="h-64">
|
|
47
|
+
* <Slider defaultValue={50} orientation="vertical" aria-label="Brightness" />
|
|
48
|
+
* </div>
|
|
49
|
+
*
|
|
50
|
+
* // Centered active track
|
|
51
|
+
* <Slider defaultValue={0} isCentered showValueIndicator />
|
|
52
|
+
*
|
|
53
|
+
* // Large track with value tooltip
|
|
54
|
+
* <Slider defaultValue={50} trackSize="l" showValueIndicator
|
|
55
|
+
* formatValue={(v) => `${v}%`} />
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @see https://m3.material.io/components/sliders/overview
|
|
59
|
+
*/
|
|
60
|
+
export declare const Slider: React.NamedExoticComponent<SliderProps & React.RefAttributes<HTMLDivElement>>;
|