@bug-on/md3-react 2.0.1 → 2.0.3
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/hooks/index.d.ts +1 -0
- package/dist/hooks/useClickOutside.d.ts +8 -0
- package/dist/index.css +23 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +11316 -9210
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +11262 -9187
- 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/search/animated-placeholder.d.ts +54 -0
- package/dist/ui/search/hooks/use-search-keyboard.d.ts +32 -0
- package/dist/ui/search/hooks/use-search-view-focus.d.ts +6 -0
- package/dist/ui/search/index.d.ts +27 -0
- package/dist/ui/search/search-bar.d.ts +32 -0
- package/dist/ui/search/search-context.d.ts +24 -0
- package/dist/ui/search/search-view-docked.d.ts +25 -0
- package/dist/ui/search/search-view-fullscreen.d.ts +36 -0
- package/dist/ui/search/search.d.ts +50 -0
- package/dist/ui/search/search.tokens.d.ts +112 -0
- package/dist/ui/search/search.types.d.ts +131 -0
- package/dist/ui/search/trailing-action.d.ts +9 -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 +2 -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,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file animated-placeholder.tsx
|
|
3
|
+
* MD3 Expressive Search — Animated placeholder overlay.
|
|
4
|
+
*
|
|
5
|
+
* Replaces the native `::placeholder` with a GPU-accelerated `translateX`
|
|
6
|
+
* animation so the placeholder text can smoothly slide from its alignment
|
|
7
|
+
* position (center / right) to left when the search input is focused.
|
|
8
|
+
*
|
|
9
|
+
* Implementation notes:
|
|
10
|
+
* - Only `transform: translateX` is animated → no layout triggers, no paint.
|
|
11
|
+
* - Container width is measured once via `useLayoutEffect` (before paint) to
|
|
12
|
+
* avoid a first-render flash, then kept fresh via `ResizeObserver`.
|
|
13
|
+
* - xOffset is stored in `useState` so Framer Motion picks up changes and
|
|
14
|
+
* re-animates smoothly on container resize.
|
|
15
|
+
* - The span is never unmounted — only opacity-toggled — to preserve the
|
|
16
|
+
* measurement ref between renders.
|
|
17
|
+
*/
|
|
18
|
+
import * as React from "react";
|
|
19
|
+
interface AnimatedPlaceholderProps {
|
|
20
|
+
/** Placeholder text to display. */
|
|
21
|
+
text: string;
|
|
22
|
+
/** Alignment of the placeholder when idle (not focused). @default "left" */
|
|
23
|
+
textAlign: "left" | "center" | "right";
|
|
24
|
+
/**
|
|
25
|
+
* Whether the placeholder should be visible.
|
|
26
|
+
* Pass `!query` — hide when the user has typed something.
|
|
27
|
+
*/
|
|
28
|
+
visible: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Whether the search is in an active/focused state.
|
|
31
|
+
* When `true`, the placeholder snaps to `left` regardless of `textAlign`.
|
|
32
|
+
*/
|
|
33
|
+
focused: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The `<input>` element that this component wraps.
|
|
36
|
+
* It should have `w-full` instead of `flex-1` since this wrapper
|
|
37
|
+
* takes over the `flex-1` role in the parent flex layout.
|
|
38
|
+
*/
|
|
39
|
+
children: React.ReactNode;
|
|
40
|
+
/** Extra className forwarded to the wrapper div. */
|
|
41
|
+
className?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Wraps a search `<input>` with an animated placeholder overlay.
|
|
45
|
+
*
|
|
46
|
+
* The wrapper div occupies `flex-1` so it fits seamlessly in the
|
|
47
|
+
* horizontal flex layout used by search bar headers. The children
|
|
48
|
+
* (the `<input>`) should use `w-full` to fill the wrapper.
|
|
49
|
+
*
|
|
50
|
+
* Accessibility: `aria-label` on the `<input>` carries the placeholder
|
|
51
|
+
* text for screen readers; this span is `aria-hidden="true"`.
|
|
52
|
+
*/
|
|
53
|
+
export declare function AnimatedPlaceholder({ text, textAlign, visible, focused, children, className, }: AnimatedPlaceholderProps): import("react/jsx-runtime").JSX.Element;
|
|
54
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file use-search-keyboard.ts
|
|
3
|
+
* Keyboard navigation hook for the MD3 Search component.
|
|
4
|
+
*
|
|
5
|
+
* Handles:
|
|
6
|
+
* - ArrowDown / ArrowUp → navigate through suggestions (role="option")
|
|
7
|
+
* - Enter → submit search or select active suggestion
|
|
8
|
+
* - Escape → close the SearchView
|
|
9
|
+
*/
|
|
10
|
+
import type { UseSearchKeyboardReturn } from "../search.types";
|
|
11
|
+
interface UseSearchKeyboardOptions {
|
|
12
|
+
/** Whether the SearchView is currently open. */
|
|
13
|
+
active: boolean;
|
|
14
|
+
/** Callback to close the SearchView. */
|
|
15
|
+
onActiveChange: (active: boolean) => void;
|
|
16
|
+
/** Callback for search submission. */
|
|
17
|
+
onSearch: (query: string) => void;
|
|
18
|
+
/** Current search query. */
|
|
19
|
+
query: string;
|
|
20
|
+
/** Total number of suggestion items in the listbox. */
|
|
21
|
+
itemCount: number;
|
|
22
|
+
/** Called when user selects a specific suggestion by index. */
|
|
23
|
+
onSelectSuggestion?: (index: number) => void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Manages keyboard navigation for the Search component.
|
|
27
|
+
*
|
|
28
|
+
* Complies with WAI-ARIA Combobox pattern:
|
|
29
|
+
* @see https://www.w3.org/WAI/ARIA/apg/patterns/combobox/
|
|
30
|
+
*/
|
|
31
|
+
export declare function useSearchKeyboard({ active, onActiveChange, onSearch, query, itemCount, onSelectSuggestion, }: UseSearchKeyboardOptions): UseSearchKeyboardReturn;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Focuses `inputRef` when `active` becomes true, using a double-rAF
|
|
4
|
+
* to wait for Framer Motion's layout animation to finish painting.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useSearchViewFocus(inputRef: React.RefObject<HTMLInputElement | null>, active: boolean): void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file index.ts
|
|
3
|
+
* MD3 Expressive Search — Public API exports.
|
|
4
|
+
*
|
|
5
|
+
* Components:
|
|
6
|
+
* - Search: Orchestrator (SearchBar + SearchView)
|
|
7
|
+
* - SearchBar: Collapsed pill state (standalone use)
|
|
8
|
+
* - SearchViewDocked: Expanded docked popup (standalone use)
|
|
9
|
+
* - SearchViewFullScreen: Expanded full-screen overlay (standalone use)
|
|
10
|
+
*
|
|
11
|
+
* Hook:
|
|
12
|
+
* - useSearchKeyboard: WAI-ARIA combobox keyboard navigation
|
|
13
|
+
*
|
|
14
|
+
* Tokens:
|
|
15
|
+
* - SearchTokens: Dimensional tokens (heights, sizes)
|
|
16
|
+
* - SEARCH_COLORS: CSS custom property color references
|
|
17
|
+
* - SEARCH_TYPOGRAPHY: Typography class strings
|
|
18
|
+
* - Animation constants
|
|
19
|
+
*/
|
|
20
|
+
export { useSearchKeyboard } from "./hooks/use-search-keyboard";
|
|
21
|
+
export { Search } from "./search";
|
|
22
|
+
export { SEARCH_BAR_EXIT_SPRING, SEARCH_BAR_EXPAND_SPRING, SEARCH_COLORS, SEARCH_DOCKED_REVEAL_SPRING, SEARCH_FULLSCREEN_SPRING, SEARCH_TYPOGRAPHY, SearchTokens, } from "./search.tokens";
|
|
23
|
+
export type { SearchProps, SearchStyleType, SearchVariant, } from "./search.types";
|
|
24
|
+
export { SearchBar } from "./search-bar";
|
|
25
|
+
export { useSearch } from "./search-context";
|
|
26
|
+
export { SearchViewDocked } from "./search-view-docked";
|
|
27
|
+
export { SearchViewFullScreen } from "./search-view-fullscreen";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file search-bar.tsx
|
|
3
|
+
* MD3 Expressive SearchBar — Collapsed state.
|
|
4
|
+
*
|
|
5
|
+
* Renders a pill-shaped search bar (CornerFull, h-56px).
|
|
6
|
+
* When focused/clicked → calls onActiveChange(true) to open SearchView.
|
|
7
|
+
*
|
|
8
|
+
* Option B (MD3 morphing): SearchBar is wrapped in its own AnimatePresence
|
|
9
|
+
* with mode="popLayout". When SearchView opens, SearchBar plays an exit
|
|
10
|
+
* animation (opacity → 0, scale → 0.95) before unmounting, releasing the
|
|
11
|
+
* shared layoutId so SearchView can claim it and morph from the same origin.
|
|
12
|
+
*
|
|
13
|
+
* Role: combobox (WAI-ARIA Search Combobox pattern).
|
|
14
|
+
* @see https://www.w3.org/WAI/ARIA/apg/patterns/combobox/
|
|
15
|
+
*/
|
|
16
|
+
import * as React from "react";
|
|
17
|
+
import type { SearchInternalProps, SearchProps } from "./search.types";
|
|
18
|
+
type SearchBarProps = Pick<SearchProps, "query" | "onQueryChange" | "onSearch" | "active" | "onActiveChange" | "leadingIcon" | "trailingIcon" | "placeholder" | "textAlign" | "className" | "aria-label"> & SearchInternalProps & {
|
|
19
|
+
/** KeyDown handler from useSearchKeyboard. */
|
|
20
|
+
onKeyDown: (e: React.KeyboardEvent) => void;
|
|
21
|
+
/** Currently highlighted suggestion index (-1 = none). */
|
|
22
|
+
activeIndex: number;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* SearchBar — collapsed state of the MD3 Search component.
|
|
26
|
+
*
|
|
27
|
+
* Uses Framer Motion `layout` + shared `layoutId` to morph into
|
|
28
|
+
* SearchView when active. Wrapped in AnimatePresence with mode="popLayout"
|
|
29
|
+
* so it exits (fades/scales out) before SearchView claims the layoutId.
|
|
30
|
+
*/
|
|
31
|
+
export declare function SearchBar({ query, onQueryChange, onSearch, active, onActiveChange, leadingIcon, trailingIcon, placeholder, textAlign, className, "aria-label": ariaLabel, searchId, listboxId, onKeyDown, activeIndex, }: SearchBarProps): import("react/jsx-runtime").JSX.Element;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* MD3 Expressive Search Context
|
|
4
|
+
* Shared state for the Search orchestrator and its children.
|
|
5
|
+
*/
|
|
6
|
+
interface SearchContextValue {
|
|
7
|
+
/** Unique ID for the results listbox, used for aria-controls. */
|
|
8
|
+
listboxId: string;
|
|
9
|
+
/** Currently highlighted suggestion index. -1 = none. */
|
|
10
|
+
activeIndex: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Provider for Search component state.
|
|
14
|
+
* Internal use only within the library.
|
|
15
|
+
*/
|
|
16
|
+
export declare function SearchProvider({ children, value, }: {
|
|
17
|
+
children: React.ReactNode;
|
|
18
|
+
value: SearchContextValue;
|
|
19
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
/**
|
|
21
|
+
* Hook to access Search state from children (e.g., search items).
|
|
22
|
+
*/
|
|
23
|
+
export declare function useSearch(): SearchContextValue;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file search-view-docked.tsx
|
|
3
|
+
* MD3 Expressive SearchView — Docked variant.
|
|
4
|
+
*
|
|
5
|
+
* Displays as a popup dropdown below the SearchBar.
|
|
6
|
+
* Recommended for medium and large screens (tablets, desktops).
|
|
7
|
+
*
|
|
8
|
+
* Shape: CornerExtraLarge (rounded-[28px]) per SearchViewTokens.DockedContainerShape.
|
|
9
|
+
* Header height: 56dp per SearchViewTokens.DockedHeaderContainerHeight.
|
|
10
|
+
*
|
|
11
|
+
* Animation (Option B — MD3 morphing):
|
|
12
|
+
* - Shares `layoutId` with SearchBar. After SearchBar exits via its own
|
|
13
|
+
* AnimatePresence (mode="popLayout"), this view claims the layoutId and
|
|
14
|
+
* Framer Motion morphs the pill shape → rounded-[28px] container.
|
|
15
|
+
* - Uses mode="popLayout" so SearchBar can re-enter after this exits.
|
|
16
|
+
* - Focus: double-rAF pattern syncs focus with layout animation frame.
|
|
17
|
+
*/
|
|
18
|
+
import * as React from "react";
|
|
19
|
+
import type { SearchInternalProps, SearchProps } from "./search.types";
|
|
20
|
+
type SearchViewDockedProps = Pick<SearchProps, "query" | "onQueryChange" | "onSearch" | "active" | "onActiveChange" | "leadingIcon" | "trailingIcon" | "placeholder" | "textAlign" | "styleType" | "hasGap" | "children" | "viewClassName" | "aria-label"> & SearchInternalProps & {
|
|
21
|
+
onKeyDown: (e: React.KeyboardEvent) => void;
|
|
22
|
+
activeIndex: number;
|
|
23
|
+
};
|
|
24
|
+
export declare function SearchViewDocked({ query, onQueryChange, onSearch, active, onActiveChange, leadingIcon, trailingIcon, placeholder, textAlign, styleType, hasGap, children, viewClassName, "aria-label": ariaLabel, searchId, listboxId, onKeyDown, activeIndex, }: SearchViewDockedProps): import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file search-view-fullscreen.tsx
|
|
3
|
+
* MD3 Expressive SearchView — FullScreen variant.
|
|
4
|
+
*
|
|
5
|
+
* Renders a full-screen overlay via React Portal.
|
|
6
|
+
* Using Portal avoids z-index stacking context issues and ensures
|
|
7
|
+
* the overlay always covers the entire viewport correctly.
|
|
8
|
+
*
|
|
9
|
+
* Animation (Option B — MD3 morphing):
|
|
10
|
+
* - Shares `layoutId` with SearchBar. After SearchBar exits via its own
|
|
11
|
+
* AnimatePresence (mode="popLayout"), this view claims the layoutId and
|
|
12
|
+
* Framer Motion morphs the pill shape → full-screen rect (CornerFull → CornerNone).
|
|
13
|
+
* - mode="popLayout" on this AnimatePresence enables SearchBar to re-enter
|
|
14
|
+
* after this view exits.
|
|
15
|
+
* - Focus: double-rAF pattern syncs focus with layout animation frame.
|
|
16
|
+
*
|
|
17
|
+
* Header height: 72dp per SearchViewTokens.FullScreenHeaderContainerHeight.
|
|
18
|
+
* ESC key closes the view (handled by useSearchKeyboard).
|
|
19
|
+
*/
|
|
20
|
+
import * as React from "react";
|
|
21
|
+
import type { SearchInternalProps, SearchProps } from "./search.types";
|
|
22
|
+
type SearchViewFullScreenProps = Pick<SearchProps, "query" | "onQueryChange" | "onSearch" | "active" | "onActiveChange" | "leadingIcon" | "trailingIcon" | "placeholder" | "textAlign" | "styleType" | "children" | "viewClassName" | "aria-label"> & SearchInternalProps & {
|
|
23
|
+
onKeyDown: (e: React.KeyboardEvent) => void;
|
|
24
|
+
activeIndex: number;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* SearchView FullScreen — full-screen overlay via React Portal.
|
|
28
|
+
*
|
|
29
|
+
* The `layoutId` shared with SearchBar enables Framer Motion to animate
|
|
30
|
+
* the shape morphing from the pill (rounded-full) to a full-screen rect.
|
|
31
|
+
*
|
|
32
|
+
* Contained style: no divider, background preserved.
|
|
33
|
+
* Divided style: HorizontalDivider between header and results.
|
|
34
|
+
*/
|
|
35
|
+
export declare function SearchViewFullScreen({ query, onQueryChange, onSearch, active, onActiveChange, leadingIcon, trailingIcon, placeholder, textAlign, styleType, children, viewClassName, "aria-label": ariaLabel, searchId, listboxId, onKeyDown, activeIndex, }: SearchViewFullScreenProps): React.ReactPortal | null;
|
|
36
|
+
export {};
|