/** * Tabs — segmented navigation between panels. * * The active tab is marked by one indicator that slides between measured * trigger positions rather than by a style on each trigger. That is what makes * the movement continuous: there is a single thing travelling, so a switch two * tabs away reads as one gesture instead of two states swapping. * * ```tsx * * * Account * 3}>Team * * … * * ``` * * **Swiping puts the panels in a row.** With `swipeable`, the panels are laid * out side by side in a strip as wide as all of them, inside a viewport that * shows one at a time, and moving between tabs is that strip translating. The * neighbours are therefore already built and already the right size before the * finger arrives at them, which is the whole point: a panel that has to be * mounted and measured at the moment it becomes visible is a panel that stalls * there, and it stalls for exactly as long as it takes to build. * * One shared value carries the strip's position, in panels rather than points, * and it is the only thing that decides where the strip is. A press springs it, * a drag sets it, and neither waits for React: the value the tab set reports is * updated alongside the movement, not ahead of it. * * A swipeable tab set therefore needs a height to fill, the same as any pager. * Give it one — `flex-1` on the tab set, or a fixed height — or the strip has * nothing to lay its panels out in. */ import { type ReactNode } from 'react'; import { type ViewProps } from 'react-native'; export type TabsVariant = 'segmented' | 'underline' | 'pill' | 'expanding'; /** * How much of an inactive panel survives a switch away from it. * * `false` unmounts it. `true` keeps it mounted. * * `'measured'` meant "keep it mounted *and* laid out at a real size", which was * a distinction only a tab set of separately hidden panels had to make. In a * swipeable tab set every panel in the strip is laid out at a real size * already, so it is the same as `true` there and is kept only so that passing * it does not break. * * @see TabsProps.keepMounted */ export type TabsKeepMounted = boolean | 'measured'; /** * `'disable-all'` turns off every animation in the tab set — the indicator, the * strip, and an expanding tab's reveal — including the ones its parts run * themselves. */ export type TabsAnimation = 'disable-all'; export interface TabsProps extends ViewProps { className?: string; value?: string; onValueChange?: (value: string) => void; defaultValue: string; /** * `segmented` is a chip travelling inside a recessed track, `underline` is a * rule under the active tab, `pill` is a filled chip on the page. * * `expanding` is a row of icon pills where only the selected one is open: * it widens to let its label out and closes again behind it. For a short row * of destinations that are recognisable by their icons, where the labels * would otherwise take the whole width to say things nobody rereads. Give * every trigger an `icon` — a closed tab has nothing else. */ variant?: TabsVariant; /** * Mount every panel up front instead of only the ones that have been * reached, so a scroll position or a half-filled form is there from the * start rather than from the first visit. * * Usually unnecessary. A panel that has been shown once stays mounted for * the life of the tab set either way, and with `swipeable` the panels on * each side of the active one are mounted before you get to them. What this * adds is the panels you have *not* been near — the fourth tab of four — * which costs their render at startup and buys nothing until somebody opens * them. * * Turn it on when a panel has to be live while it is off screen: a form that * must validate as another tab is edited, a chart that has to be ready to * print, a subscription that must not miss a message. */ keepMounted?: TabsKeepMounted; /** * Move between tabs by dragging sideways on the panels, as well as by * pressing the triggers. * * Off by default, because a panel is allowed to contain something that * already wants a horizontal drag — a carousel, a slider, a row that swipes * open — and the two cannot both have it. Turn it on for panels of ordinary * scrolling content, where it is the gesture people try first. * * **It changes how the panels are laid out.** They go side by side in a strip * that is as wide as all of them, and the tab set shows one panel of it at a * time. So the panel on each side of the active one is built and sized before * you swipe to it, which is what stops a heavy panel — a virtualised list, a * chart — from stalling on the frame it becomes visible. * * **It needs a height to fill**, the same as any pager: `flex-1` on the tab * set, or a fixed height. Without one the strip has no room to lay its panels * out in, and a list inside a panel of no height renders no rows. In * development the tab set says so rather than rendering nothing. */ swipeable?: boolean; /** * Turn the tab set's animations off — the indicator, the strip, and an * expanding tab's reveal. * * For a screen that is already animating something more important, and as a * blunt instrument on a device that cannot afford them. The system's own * reduce-motion setting is honoured without this. */ animation?: TabsAnimation; children: ReactNode; } declare function TabsRoot({ className, value, onValueChange, defaultValue, variant, keepMounted, swipeable, animation, children, ...props }: TabsProps): import("react").JSX.Element; export interface TabsListProps extends ViewProps { className?: string; /** * Lay the triggers out at their natural widths inside a horizontal scroller * instead of splitting the row between them. For more tabs than fit — which * a fixed row answers by crushing every label. */ scrollable?: boolean; children: ReactNode; } declare function TabsList({ className, scrollable, children, ...props }: TabsListProps): import("react").JSX.Element; export interface TabsTriggerProps { className?: string; value: string; /** * Rendered before the label. Required by `variant="expanding"`, where it is * the only thing a closed tab has left to identify it by. */ icon?: ReactNode; /** Rendered after the label — a count, a dot, a status. */ badge?: ReactNode; /** Unselectable, dimmed, and announced as disabled. */ disabled?: boolean; children: ReactNode; } declare function TabsTrigger({ className, value, icon, badge, disabled, children, }: TabsTriggerProps): import("react").JSX.Element; export interface TabsContentProps extends ViewProps { className?: string; value: string; children: ReactNode; } declare function TabsContent({ className, value, children, style, ...props }: TabsContentProps): import("react").JSX.Element | null; export declare const Tabs: typeof TabsRoot & { List: typeof TabsList; Trigger: typeof TabsTrigger; Content: typeof TabsContent; }; export {}; //# sourceMappingURL=index.d.ts.map