/** * SectionProgress — a floating pill saying how far through a screen you are, * and which part of it you are in. * * A ring filled to the scroll position, and beside it the title of the section * being read. Pressed, it opens into the list of sections and jumps to any of * them. * * ```tsx * const sections = useScrollSections({ ids: SECTIONS.map((s) => s.id) }); * * * Introduction * Setup * * ``` * * ## Two readings, one control * * The ring is continuous and the label is not, and that is the point: a * percentage says how much is left, a section name says what is being read. * Either on its own leaves the other question open — a bar at 60% of an * unfamiliar page means nothing in particular, and a heading with no sense of * depth is a position without a scale. * * ## It arrives, and then it stays * * Nothing is drawn on the first screen. Past `revealAt` the pill fades in and * remains for the rest of the scroll — it does not hide again on the way back * up. A label that comes and goes with the scroll direction is one the reader * has to catch rather than read. * * ## One surface, not a card above a button * * Open, the list and the pill are a single bordered box: the pill's row is the * end of the card rather than a control sitting under a panel of its own. Two * boxes would draw two outlines a few points apart, and the pill would read as * something the list had landed on top of rather than as the thing it grew * out of. * * The card is the only thing carrying a border, a background and a shadow. * Everything inside it is a row. * * ## The section, and the colour it brings * * An `Item` may carry a `color`, and the active one's colour is taken by the * ring, the label and a wash across the pill, crossfading as the reader moves * between sections. It turns the pill into a second, peripheral signal — the * part of the page you are in, readable without the words. */ import { type ReactNode } from 'react'; import { type ViewProps } from 'react-native'; import { type SharedValue } from 'react-native-reanimated'; export type SectionProgressPlacement = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'; /** The colour an `Item` can bring with it. */ export type SectionProgressColor = 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'foreground'; /** * Where the scroller is. * * Three values rather than one fraction, because the fraction is not the only * thing that is wanted: the reveal threshold is a distance in points, and a * distance cannot be recovered from a percentage. * * Both `useScrollSections().scroll` and the `ScrollProgress` primitive's * context satisfy this shape. */ export interface SectionProgressScroll { /** Distance scrolled, in points. */ offset: SharedValue; /** Height of the visible area. */ viewport: SharedValue; /** Total height of the content. */ content: SharedValue; } export interface SectionProgressProps extends Omit { className?: string; /** * The scroll position the ring is filled from. `useScrollSections` returns * one as `scroll`; without it the component falls back to the nearest * `ScrollProgress`, and with neither the ring stays empty. */ scroll?: SectionProgressScroll; /** * Fill the ring from a value of your own, between 0 and 1. Nothing is * derived when this is passed. */ progress?: SharedValue | number; /** Active section id. Controlled — usually driven by a scroll handler. */ value?: string; /** Starting section when uncontrolled. */ defaultValue?: string; /** Fires when a section is chosen from the panel. Scroll there. */ onValueChange?: (value: string) => void; /** Controlled expansion of the panel. */ open?: boolean; /** Whether the panel starts open when uncontrolled. */ defaultOpen?: boolean; /** Fires when the panel opens or closes, however it was done. */ onOpenChange?: (open: boolean) => void; /** Which corner or edge the pill floats in. */ placement?: SectionProgressPlacement; /** Gap between the pill and the edge of the safe area. */ offset?: number; /** * How far the reader must scroll, in points, before the pill appears. `0` * shows it from the first frame. It never hides again. */ revealAt?: number; /** * Tick under the finger on every change of section, however it was made. * Nothing between a tap in the panel and its arrival counts as a change. * Needs the optional `expo-haptics` package; without it this does nothing. */ haptics?: boolean; /** * What the pill is called to a screen reader. The section being read and * the percentage are announced after it, so this names the control rather * than describing the state. */ label?: string; /** One `SectionProgress.Item` per section, in the order they appear. */ children: ReactNode; } declare function SectionProgressRoot({ className, scroll, progress, value: valueProp, defaultValue, onValueChange, open: openProp, defaultOpen, onOpenChange, placement, offset, revealAt, haptics, label, children, ...props }: SectionProgressProps): import("react").JSX.Element; declare namespace SectionProgressRoot { var displayName: string; } export interface SectionProgressItemProps { className?: string; /** Section this row jumps to. Matches the root's `value`. */ value: string; /** * The colour this section brings to the pill. Left out, the section takes * the foreground colour like every other. */ color?: SectionProgressColor; /** The section's title. It is what the collapsed pill shows. */ children: ReactNode; } /** * One section: a row in the panel, and the label the pill shows while that * section is the one being read. */ declare function SectionProgressItem({ className, value, children }: SectionProgressItemProps): import("react").JSX.Element; declare namespace SectionProgressItem { var displayName: string; } export declare const SectionProgress: typeof SectionProgressRoot & { Item: typeof SectionProgressItem; }; export {}; //# sourceMappingURL=index.d.ts.map