/** * 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