/**
* MessageScroller — the scroll behaviour a chat transcript needs.
*
* A transcript is the one list where the interesting end is the bottom, the
* content grows while you are reading it, and history is added to the top. A
* plain scroll view gets all three wrong: it opens at the top, it stays put
* while a reply streams in below the fold, and it jumps a screen when older
* messages load. This owns those three behaviours so a screen does not have to
* rebuild them.
*
* ```tsx
*
*
*
* {messages.map((m) => (
*
* …
*
* ))}
*
*
*
*
* ```
*
* The rule behind every part of it: **the reader's position is theirs**. New
* content follows the bottom only while they are already at the bottom, and
* scrolling away hands control back to them until they ask for it again.
* Content added above them never moves what they are looking at.
*
* Needs a bounded height — from `flex-1` in a column, or an explicit one.
* Given an unbounded parent it grows to fit its content and never scrolls.
*/
import { type ReactElement, type ReactNode } from 'react';
import { type FlatListProps, type ListRenderItemInfo, type ViewProps } from 'react-native';
import { type AnimatedScrollViewProps } from 'react-native-reanimated';
export type MessageScrollerPosition = 'start' | 'end' | 'last-anchor';
/** Scroll commands, for a control that lives outside the viewport. */
export declare function useMessageScroller(): {
scrollToEnd: (animated?: boolean) => void;
scrollToStart: (animated?: boolean) => void;
scrollToMessage: (id: string, animated?: boolean) => void;
};
/**
* Where the reader is: the turn they are inside, and whether they are at the
* live edge. Both settle after a scroll rather than updating per frame — this
* is for a header that names the current turn, not for anything animated.
*/
export declare function useMessageScrollerVisibility(): {
currentAnchorId: string | null;
atEnd: boolean;
};
export interface MessageScrollerProps extends ViewProps {
className?: string;
/**
* Follow new content down as it arrives — but only while the reader is
* already at the bottom. Scrolling up disengages it until they come back or
* press the button.
*/
autoScroll?: boolean;
/**
* Keep the reader on the same message when older ones are added above. Without
* it, loading history throws them a screen backwards.
*/
preserveScrollOnPrepend?: boolean;
/**
* Where a freshly mounted transcript opens. `last-anchor` is the one to want
* for a saved thread: it lands on the last turn that started something,
* rather than at the very bottom of whatever the reply happened to be.
*/
defaultScrollPosition?: MessageScrollerPosition;
children?: ReactNode;
}
declare function MessageScrollerRoot({ className, autoScroll, preserveScrollOnPrepend, defaultScrollPosition, children, ...props }: MessageScrollerProps): import("react").JSX.Element;
declare namespace MessageScrollerRoot {
var displayName: string;
}
/**
* The four scroll events are owned by the component and taken off the props
* type rather than merged with a consumer's. They are not decoration on top of
* a scroll view — they *are* the behaviour, and a call site that quietly
* replaced one would look like a bug in the scrolling.
*/
export interface MessageScrollerViewportProps extends Omit {
className?: string;
children?: ReactNode;
}
/**
* The scrollable itself. Owns the three behaviours, because all three are
* reactions to scroll and content-size events and this is where they arrive.
*/
declare function MessageScrollerViewport({ className, children, ...props }: MessageScrollerViewportProps): import("react").JSX.Element;
declare namespace MessageScrollerViewport {
var displayName: string;
}
export interface MessageScrollerListItem {
/** Stable id used by `scrollToMessage` and as the default React key. */
messageId: string;
/** Marks the start of a turn for `last-anchor` and visibility reporting. */
scrollAnchor?: boolean;
}
export interface MessageScrollerListProps extends Omit, 'data' | 'renderItem' | 'keyExtractor' | 'onScroll' | 'onContentSizeChange' | 'onViewableItemsChanged' | 'maintainVisibleContentPosition' | 'onScrollToIndexFailed' | 'onScrollEndDrag' | 'onMomentumScrollEnd' | 'CellRendererComponent'> {
className?: string;
/** Classes on the virtualized list's padded transcript column. */
contentContainerClassName?: string;
/**
* The complete transcript. Rows outside the native render window stay
* unmounted; each item therefore carries its stable navigation metadata.
*/
data: readonly T[];
/** Draw one turn from the native list window. */
renderItem: (info: ListRenderItemInfo) => ReactElement | null;
}
/**
* The real virtualized transcript path. Only its render window mounts; native
* visible-content maintenance keeps prepends still without measuring offscreen
* rows in JavaScript.
*/
declare function MessageScrollerList({ className, contentContainerClassName, data, renderItem, initialNumToRender, maxToRenderPerBatch, windowSize, ...props }: MessageScrollerListProps): import("react").JSX.Element;
declare namespace MessageScrollerList {
var displayName: string;
}
export interface MessageScrollerContentProps extends ViewProps {
className?: string;
children?: ReactNode;
}
/** The transcript column. Announces additions rather than the whole list. */
declare function MessageScrollerContent({ className, children, ...props }: MessageScrollerContentProps): import("react").JSX.Element;
declare namespace MessageScrollerContent {
var displayName: string;
}
export interface MessageScrollerItemProps extends ViewProps {
className?: string;
/** Stable id for this turn. `scrollToMessage` takes the same value. */
messageId: string;
/**
* Marks this row as the start of a turn. `defaultScrollPosition="last-anchor"`
* opens on the last one, and it is what a saved thread should land on — the
* question, not the tail of the answer to it.
*/
scrollAnchor?: boolean;
children?: ReactNode;
}
/**
* One row of the transcript. It exists to be measured: without a boundary per
* turn there is nothing to scroll *to*, and nothing to measure the prepend
* shift against.
*/
declare function MessageScrollerItem({ className, messageId, scrollAnchor, children, ...props }: MessageScrollerItemProps): import("react").JSX.Element;
declare namespace MessageScrollerItem {
var displayName: string;
}
export interface MessageScrollerButtonProps extends ViewProps {
className?: string;
/** `end` jumps to the newest message, `start` to the beginning of the thread. */
target?: 'start' | 'end';
accessibilityLabel?: string;
children?: ReactNode;
}
/**
* The way back to the live edge, shown only when there is one to go back to.
*
* Its visibility is driven from the same shared value the scroll handler
* writes, so it fades on the UI thread while the transcript is still moving.
*/
declare function MessageScrollerButton({ className, target, accessibilityLabel, children, ...props }: MessageScrollerButtonProps): import("react").JSX.Element;
declare namespace MessageScrollerButton {
var displayName: string;
}
export declare const MessageScroller: typeof MessageScrollerRoot & {
Viewport: typeof MessageScrollerViewport;
List: typeof MessageScrollerList;
Content: typeof MessageScrollerContent;
Item: typeof MessageScrollerItem;
Button: typeof MessageScrollerButton;
};
export {};
//# sourceMappingURL=index.d.ts.map