/**
* Fab — the floating action button: one screen, one thing it is mostly for.
*
* ```tsx
* } accessibilityLabel="New note" onPress={compose} />
* ```
*
* ## It floats, which is the whole problem with it
*
* A button pinned over the content is a button covering some of it, and the
* bottom-right corner of a scrolling list is exactly where the last row goes. So
* a Fab is right when a screen has *one* action worth that trade and wrong when
* it has three — a corner with three buttons in it is a toolbar that has been
* put in the wrong place. Where a screen has several, the honest shapes are a
* `ButtonGroup` in a bar or, if one of them really does lead, this with a
* `Fab.Group` behind it.
*
* It positions itself absolutely against its nearest positioned ancestor, which
* on a screen means the screen. Give the content below it enough bottom padding
* to scroll clear — this cannot know how tall your list is, and a Fab sitting on
* the last row forever is the failure people actually hit.
*
* ## Extended, and when to bother
*
* ```tsx
* }>Write
* ```
*
* A lone glyph is a guess unless the glyph is a plus. `extended` spells the
* action out, costs the width, and is worth it for anything a plus would not
* have said.
*
* ## The speed dial
*
* ```tsx
* } accessibilityLabel="Add">
* } label="Photo" onPress={addPhoto} />
* } label="File" onPress={addFile} />
*
* ```
*
* The actions come out of the button one after another rather than together —
* a stagger of a few frames each, which is enough to read as a list unfolding
* instead of a menu appearing. Every action carries its label beside it, because
* a column of unlabelled circles is a quiz.
*
* ## The menu
*
* ```tsx
* } accessibilityLabel="Add">
* } label="Photo" onPress={addPhoto} />
* } label="File" onPress={addFile} />
*
* ```
*
* `layout="menu"` unfolds one panel of rows out of the button instead of a
* column of buttons — the shape the platform's own menus take. Each row is a
* label with its glyph, on the side the appearance puts it or `iconPlacement`
* says, and the panel springs out of the corner the
* trigger sits in, so it reads as the button opening rather than a sheet
* arriving. Drawn in glass, the panel is one piece of the material; the rows
* are content on it.
*
* ## Glass
*
* ```tsx
* } accessibilityLabel="New note" />
* ```
*
* `glass` draws the button in the material iOS 26 uses for its own floating
* controls, in place of the variant's fill. A control floating over content is
* exactly what that material is for: it refracts what scrolls under it rather
* than covering it, and lifts its own edge, so the shadow goes too.
*
* Every variant takes the plain material, because a tint is a dimmer: measured
* over content, a colour laid on the glass turns it back into a fill and a
* monochrome one only greys it. The glyph reads in the ordinary foreground
* colour, and in red on `destructive` — the colour that carries meaning goes
* on the glyph, where it stays legible. Pressed, it answers the way the platform's
* own glass controls do: the material swells and brightens under the finger,
* in place of this component's press scale. The material exists on iOS 26 and
* above with the optional `expo-glass-effect` installed. Everywhere else — older iOS,
* Android, web, Reduce Transparency on — the flag is inert and the button keeps
* its ordinary fill, so nothing has to be written twice.
*
* Opening also drops a scrim over the screen. Not for looks: an open dial is
* modal — the next tap either picks something or closes it — and a scrim is what
* says so, as well as what catches the tap that closes it.
*
* ## Where a group has to be written
*
* A `Fab.Group` draws its scrim and its buttons as two absolutely positioned
* siblings in whatever it is written inside, so put it in the screen's root
* container. That is where its `offset` is measured from, and it is what the
* scrim covers.
*
* It stays in the screen's own view tree rather than being lifted out of it,
* which is the part that matters after navigation: pushing a screen over this
* one hides the dial with everything else on it. A group lifted to the top of
* the app would still be drawn over the screen that replaced it.
*/
import { type ReactNode } from 'react';
import { View, type ViewProps } from 'react-native';
import { type VariantProps } from 'tailwind-variants';
import { type AnimatedPressableProps } from '../../primitives/animated-pressable.js';
/** Which corner the button sits in. */
export type FabPlacement = 'bottom-right' | 'bottom-center' | 'bottom-left';
/** How the menu is drawn: the platform's own shape, or rows with a glyph well. */
export type FabMenuAppearance = 'platform' | 'wells';
/** Which side of a menu row the glyph sits on. */
export type FabMenuIconPlacement = 'leading' | 'trailing';
declare const fabVariants: import("tailwind-variants").TVReturnType<{
size: {
sm: {
root: string;
label: string;
};
md: {
root: string;
label: string;
};
lg: {
root: string;
label: string;
};
};
extended: {
true: {};
false: {};
};
variant: {
primary: {
root: string;
label: string;
};
secondary: {
root: string;
label: string;
};
surface: {
root: string;
label: string;
};
destructive: {
root: string;
label: string;
};
};
disabled: {
true: {
root: string;
};
};
glass: {
true: {
root: string;
};
};
}, {
root: string;
content: string;
label: string;
}, undefined, {
size: {
sm: {
root: string;
label: string;
};
md: {
root: string;
label: string;
};
lg: {
root: string;
label: string;
};
};
extended: {
true: {};
false: {};
};
variant: {
primary: {
root: string;
label: string;
};
secondary: {
root: string;
label: string;
};
surface: {
root: string;
label: string;
};
destructive: {
root: string;
label: string;
};
};
disabled: {
true: {
root: string;
};
};
glass: {
true: {
root: string;
};
};
}, {
root: string;
content: string;
label: string;
}, import("tailwind-variants").TVReturnType<{
size: {
sm: {
root: string;
label: string;
};
md: {
root: string;
label: string;
};
lg: {
root: string;
label: string;
};
};
extended: {
true: {};
false: {};
};
variant: {
primary: {
root: string;
label: string;
};
secondary: {
root: string;
label: string;
};
surface: {
root: string;
label: string;
};
destructive: {
root: string;
label: string;
};
};
disabled: {
true: {
root: string;
};
};
glass: {
true: {
root: string;
};
};
}, {
root: string;
content: string;
label: string;
}, undefined, unknown, unknown, undefined>>;
type FabVariantProps = VariantProps;
/** How big the button is. */
export type FabSize = NonNullable;
/** What the button is drawn in. */
export type FabVariant = NonNullable;
export interface FabProps extends Omit, Omit {
className?: string;
/** The glyph. Sized by you — this is the one thing that should not guess. */
icon?: ReactNode;
/**
* The label, which turns the circle into a stadium. Needs `extended`; a
* label with nowhere to go is a label that gets clipped by the circle.
*/
children?: ReactNode;
/** Spell the action out beside the glyph. */
extended?: boolean;
/**
* Pin it over the content, in a corner.
*
* Left out, it is an ordinary button in the flow — which is what you want
* inside a `Fab.Group`, or when the screen already has somewhere for it to
* sit.
*/
placement?: FabPlacement;
/** Distance from the edges when `placement` is set, in points. */
offset?: number;
/** Placement-aware view style. Press-state styling belongs in `className`. */
style?: ViewProps['style'];
disabled?: boolean;
/**
* A tick on press. Off by default — needs the optional `expo-haptics`, and
* is silent without it.
*/
haptics?: boolean;
/**
* Draw it in Liquid Glass — the material iOS 26 uses for its own floating
* controls — instead of the variant's fill. Every variant takes the plain
* material, with its glyph in the foreground colour and in red on
* `destructive`. Pressed, the material swells and brightens the way the
* platform's own glass controls do.
*
* Needs iOS 26 and the optional `expo-glass-effect`. Below that, on Android,
* on web, or with Reduce Transparency on, it does nothing and the button
* keeps its ordinary fill.
*/
glass?: boolean;
/** Required for an icon-only button. A lone glyph reads out as nothing. */
accessibilityLabel?: string;
}
/**
* What opens out of the trigger: a column of buttons, one panel of rows, or
* the platform's own menu.
*/
export type FabGroupLayout = 'dial' | 'menu' | 'native';
export interface FabGroupProps extends Omit {
className?: string;
/** The glyph on the trigger. */
icon?: ReactNode;
/** The trigger's label, if it should be extended while closed. */
label?: string;
/** `Fab.Action` children, in the order they should unfold. */
children?: ReactNode;
/** Controlled open state. */
open?: boolean;
onOpenChange?: (open: boolean) => void;
/** Which corner of the *screen* the whole dial parks in. */
placement?: FabPlacement;
/** Distance from the screen's edges, in points. Add your safe-area inset. */
offset?: number;
/**
* What opens out of the trigger. `dial`, the default, is a column of round
* buttons with their labels beside them. `menu` is one panel of rows — a
* label with its glyph, on the side the appearance puts it — that springs
* out of the trigger's corner, the way the platform's own menus do.
* `native` hands the menu to the platform: SwiftUI on iOS, Jetpack Compose
* on Android.
*
* A native menu is drawn by the platform, so `className` and the theme
* tokens do not reach it, and the rows take `label` and `systemImage` rather
* than an `icon` element. `blur` still applies: the scrim behind the menu is
* ours, so the page recedes the way it does behind the dial and the panel.
*
* On iOS the platform owns the menu's open state, so `open` and
* `onOpenChange` do nothing there. Android's menu is controlled and honours
* both.
*
* Where the platform menu cannot be drawn — on the web, or without
* `@expo/ui` installed — this falls back to `menu`.
*/
layout?: FabGroupLayout;
/**
* How a menu is drawn. `platform`, the default, is the shape the platform's
* own menus take: a hairline between rows and the glyph after the label.
* `wells` is tighter, with the glyph leading in a tinted well and each row
* its own pill — a menu the app designed rather than the system.
*
* Menu layout only.
*/
appearance?: FabMenuAppearance;
/** Which side of a menu row the glyph sits on. Each appearance has its own default. */
iconPlacement?: FabMenuIconPlacement;
/** The menu panel's width in points. Each appearance has its own default. */
menuWidth?: number;
/** The menu panel's corner radius in points. Each appearance has its own default. */
menuRadius?: number;
/** Extra classes for the menu panel. */
menuClassName?: string;
/** Extra classes for every menu row. A row's own `className` comes after. */
rowClassName?: string;
size?: FabSize;
variant?: FabVariant;
disabled?: boolean;
haptics?: boolean;
/**
* Draw the trigger and its actions in Liquid Glass. The same flag as on
* `Fab`, with the same floor: iOS 26 and `expo-glass-effect`, inert
* elsewhere.
*/
glass?: boolean;
/** Frost the screen behind the open dial instead of dimming it. */
blur?: boolean;
/** Required — the trigger is a lone glyph until it is opened. */
accessibilityLabel?: string;
/**
* Turn the trigger's glyph a quarter circle while the dial is open.
*
* On by default, and it is doing real work when the glyph is a plus: the
* same mark becomes a cross, which says "this closes now" without a second
* icon that has to be swapped in. Turn it off for a glyph that means
* something at one angle only.
*/
rotateOnOpen?: boolean;
}
export interface FabActionProps extends Omit {
className?: string;
/** The glyph. */
icon?: ReactNode;
/**
* The glyph for a native menu row, as an SF Symbol name.
*
* `layout="native"` only, and iOS only — SwiftUI names its symbols rather
* than taking a view for them, so `icon` cannot cross over. Ignored
* everywhere else, so a group can carry both and be right on either path.
*/
systemImage?: string;
/** What it does, beside the glyph. A column of unlabelled circles is a quiz. */
label?: string;
onPress?: () => void;
disabled?: boolean;
/** Draws it in the destructive colour, for the one that removes something. */
destructive?: boolean;
/** Extra classes for the label — the chip in a dial, the row's text in a menu. */
labelClassName?: string;
}
export declare const Fab: import("react").ForwardRefExoticComponent> & {
Group: import("react").ForwardRefExoticComponent>;
Action: import("react").ForwardRefExoticComponent>;
};
export {};
//# sourceMappingURL=index.d.ts.map