/**
* ScrollHeader — a screen header that hands over to a compact bar as the page
* scrolls.
*
* A screen with a title has two states and every app draws both: the title
* large, at rest, with room around it, and the title small, pinned, once you
* are reading. Hand-rolling the change between them is where screens stop
* matching each other — one snaps, one crossfades at a different point, one
* forgets the bar is over a safe area — so the two states and the transit
* between them are one component here.
*
* ```tsx
*
*
* Library
*
*
*
*
*
* Library
* 128 components
*
* {rows}
*
* ```
*
* ## The band collapses; nothing inside it is animated up
*
* The header is one absolutely positioned band over the scroller, and the only
* thing driven by the scroll is its height: `bar + max(0, large - offset)`.
* The bar is anchored to its top and the large block to its bottom, so the
* band shrinking is what carries the large block up and behind the bar, and
* `overflow-hidden` is what cuts it off there.
*
* Driving the height rather than a `translateY` is what makes the rest fall
* out for free. Over-scrolling makes `large - offset` larger than `large`, so
* the band grows and a cover filling it stretches by being laid out bigger —
* no scale transform, so a photograph stretches without going soft. And the
* band's height is the inset the content needs, so there is one number rather
* than two that have to agree.
*
* ## The two titles cross-fade; neither one morphs
*
* The large title and the bar title are separate elements that fade past each
* other. A single title scaled and translated between the two positions tracks
* beautifully until the text is long enough to truncate, at which point it is
* animating between two different strings.
*
* Both are therefore in the tree at once, which is a problem for a screen
* reader — one of them is invisible and would still be read. So the crossing
* point is also published to React as `collapsed`, and whichever title is not
* being shown is hidden from accessibility. That is one re-render per crossing
* and no more: everything that runs per frame stays in shared values.
*
* ## The bar's surface is a fill, or a frost
*
* `surface` says what the bar is drawn on once it has taken over: a token
* fill, nothing at all over a cover, or `blur` — a real material, so the rows
* passing under the bar stay legible as shape and colour while losing the
* detail that would compete with the title on top.
*
* The frost needs a native view, and there are two ways it cannot be drawn:
* `expo-blur` is optional and may not be installed, and Reduce Transparency is
* a preference that outranks the design. Both fall back to the plain
* background token rather than to nothing, because a bar you cannot read is a
* worse answer than a bar that is not frosted.
*
* ## What it needs
*
* A height to fill, and exactly one scrollable child. The child is cloned with
* the scroll handler and the content inset composed onto it, the same way
* `ScrollFade` wraps one, so a `ScrollView`, a `FlatList` or a `SectionList`
* all work unchanged.
*/
import { type ReactNode } from 'react';
import { View, type ImageSourcePropType, type Text as RNText, type ViewProps } from 'react-native';
import { type SharedValue } from 'react-native-reanimated';
import { type VariantProps } from 'tailwind-variants';
import { type TextProps } from '../../primitives/text.js';
/**
* The scrollables Reanimated already animates. Its animated components are
* ordinary function components carrying the *inner* component's name, so there
* is nothing on one to test — but these two are module-level constants, and
* identity is exact.
*/
/** Which way the frost tints. `default` follows the app's theme. */
export type ScrollHeaderMaterial = 'light' | 'dark' | 'default';
declare const scrollHeaderVariants: import("tailwind-variants").TVReturnType<{
/** What the bar is drawn on once it has taken over. */
surface: {
plain: {
barSurface: string;
};
muted: {
barSurface: string;
};
none: {
barSurface: string;
};
blur: {
barSurface: string;
};
};
divider: {
true: {
barSurface: string;
};
false: {};
};
}, {
root: string;
band: string;
cover: string;
bar: string;
barSurface: string;
large: string;
actions: string;
}, undefined, {
/** What the bar is drawn on once it has taken over. */
surface: {
plain: {
barSurface: string;
};
muted: {
barSurface: string;
};
none: {
barSurface: string;
};
blur: {
barSurface: string;
};
};
divider: {
true: {
barSurface: string;
};
false: {};
};
}, {
root: string;
band: string;
cover: string;
bar: string;
barSurface: string;
large: string;
actions: string;
}, import("tailwind-variants").TVReturnType<{
/** What the bar is drawn on once it has taken over. */
surface: {
plain: {
barSurface: string;
};
muted: {
barSurface: string;
};
none: {
barSurface: string;
};
blur: {
barSurface: string;
};
};
divider: {
true: {
barSurface: string;
};
false: {};
};
}, {
root: string;
band: string;
cover: string;
bar: string;
barSurface: string;
large: string;
actions: string;
}, undefined, unknown, unknown, undefined>>;
type ScrollHeaderVariantProps = VariantProps;
/** What the bar is drawn on once the large block has gone. */
export type ScrollHeaderSurface = NonNullable;
/** Which half of the header a part is standing in. */
export type ScrollHeaderSlot = 'bar' | 'large';
export interface ScrollHeaderProps extends ViewProps {
className?: string;
/**
* Height of the pinned bar in points, before the device's top inset. The
* inset is added on top of this rather than taken out of it, so the bar's
* contents keep this much room on every device.
*/
barHeight?: number;
/**
* How much of the large block has to leave before the bar has fully taken
* over, as a fraction of its height. Below 1 the crossing happens early,
* which suits a tall block whose last few points are not worth waiting for.
*/
threshold?: number;
/**
* Settle a part-scrolled band open or closed when the finger lifts, rather
* than leaving the header half-collapsed.
*/
snap?: boolean;
/**
* Let the band grow past its resting height when the scroller is pulled
* down. A cover fills the band, so this is what stretches it. Off under
* Reduce Motion.
*/
stretch?: boolean;
/** Add the device's top inset above the bar. Off inside a screen that already has one. */
inset?: boolean;
/**
* Called as the bar takes over, and again when the large block comes back.
* Fires on the crossing, not on every frame.
*/
onCollapsedChange?: (collapsed: boolean) => void;
/**
* A shared value to mirror the collapse into, 0 to 1, for animating
* something outside the header against the same transition.
*/
progress?: SharedValue;
/** The parts, and exactly one scrollable. */
children?: ReactNode;
}
export interface ScrollHeaderBarProps extends ViewProps {
className?: string;
/**
* What the bar is drawn on once it has taken over. `none` leaves it clear,
* for a bar over a cover that should stay visible. `blur` frosts it, so the
* content passing under the bar stays legible as shape and colour.
*
* `blur` needs `expo-blur`, which is optional, and it is replaced by an
* opaque bar under Reduce Transparency. Both fall back to `plain` — a bar
* whose title cannot be read is a worse answer than one that is not frosted.
*/
surface?: ScrollHeaderSurface;
/** A hairline under the bar, drawn with its surface. */
divider?: boolean;
/**
* Depth of the frost, on `expo-blur`'s 0–100 scale. Defaults to 40 — heavier
* than a scrim's, because a scrim covers a whole screen and this is a thin
* band read against content moving under it. Ignored unless `surface` is
* `blur`.
*/
intensity?: number;
/**
* Which way the frost tints. Defaults to the app's theme rather than the
* device's, so an app running light inside a dark OS frosts light. Ignored
* unless `surface` is `blur`.
*/
material?: ScrollHeaderMaterial;
children?: ReactNode;
}
export interface ScrollHeaderLargeProps extends ViewProps {
className?: string;
children?: ReactNode;
}
export interface ScrollHeaderActionsProps extends ViewProps {
className?: string;
children?: ReactNode;
}
export interface ScrollHeaderCoverProps extends ViewProps {
className?: string;
/** A picture behind the header. Laid out to fill the band, so it stretches with it. */
source?: ImageSourcePropType;
/**
* The gradient drawn when there is no picture, or under one that has not
* loaded. Defaults to two of the theme's series tokens, so an app that puts
* its charts on brand puts this on brand with them.
*/
colors?: [string, string, ...string[]];
/** A wash over the cover, so a title stays legible on a bright picture. */
scrim?: boolean;
children?: ReactNode;
}
export declare const ScrollHeader: import("react").ForwardRefExoticComponent> & {
Bar: import("react").ForwardRefExoticComponent>;
Large: import("react").ForwardRefExoticComponent>;
Title: import("react").ForwardRefExoticComponent>;
Description: import("react").ForwardRefExoticComponent>;
Actions: import("react").ForwardRefExoticComponent>;
Cover: import("react").ForwardRefExoticComponent>;
};
export {};
//# sourceMappingURL=index.d.ts.map