/**
* SelectionMode — turning a list into one you can pick several things out of.
*
* ```tsx
*
*
* (
* open(item)}>
* - …
*
* )}
* />
*
* } destructive onPress={remove}>
* Delete
*
*
*
* ```
*
* ## Two ways to present it
*
* On a screen it is a *mode*: the list is there to be read, and a long press
* turns it into one you can pick from. In a sheet it is a *picker*:
* `SelectionMode.Sheet` was opened in order to choose something, so it is
* choosing from the moment it appears, with the actions in the sheet's footer.
*
* ## The items stay yours
*
* `SelectionMode.Item` wraps whatever you put in it rather than replacing it.
* It adds the circle and takes over what a press means; what the item looks
* like is yours. That is what lets one component hold a row of people, a grid
* of colours, a run of slides and a list of files without growing a prop for
* each of them.
*
* ## A mode has to be obvious
*
* There are two states and the list behaves differently in each: normally a tap
* opens a row, and in selection a tap picks it. That is only safe if leaving is
* always available and never hidden — hence a cancel in the header, the Android
* back button, and the count in front of the reader the whole time.
*
* Entering is a long press on a row, which is the gesture the platform has used
* for this for fifteen years, and the row you pressed is the first one picked.
* Entering with nothing selected leaves the reader in a changed list with no
* explanation of what changed.
*
* ## Selection is a set of values, not of rows
*
* The component holds ids, never indices or elements. A list that reorders,
* pages in more rows or drops one underneath the reader would invalidate
* anything positional; a set of ids survives all three, and is also the shape
* the action at the end needs — deleting takes ids.
*/
import { type ReactNode } from 'react';
import { type ViewProps } from 'react-native';
import { type VariantProps } from 'tailwind-variants';
declare const selectionVariants: import("tailwind-variants").TVReturnType<{
selected: {
true: {
circle: string;
ring: string;
};
};
/**
* The action laid out along its label instead of above it.
*
* A screen's bar carries three or four actions side by side, so each one is
* a narrow column and the label belongs under the glyph. A sheet's footer
* usually carries one, full width — stacked there it is a tall block that
* costs the list a row of its own for no gain.
*/
compact: {
true: {
action: string;
actionLabel: string;
};
};
destructive: {
true: {
actionLabel: string;
};
};
disabled: {
true: {
action: string;
};
};
/**
* The group's surface, which depends on what is behind it.
*
* `card` is a step *down* from the background on a screen, which is what a
* grouped list wants there. A sheet is already `popover`, and popover sits
* above card in every dark theme — so the same card inside a sheet is
* darker than the sheet around it and reads as a hole rather than a raised
* set of rows.
*
* `muted` is an alpha tint, so it steps up from whatever it is drawn on and
* is correct against either. It is only used in the sheet because the
* screen's card is right where it is.
*/
surface: {
screen: {
group: string;
};
sheet: {
group: string;
};
};
/**
* Flush to the bottom edge, or lifted off it.
*
* `bar` is the platform shape — full width against the edge, a hairline
* along the top, and the same background as the screen's own chrome. It is
* the default because it is what a list with a selection in it does on both
* platforms, and because it does not take width away from the list.
*/
placement: {
bar: {
bar: string;
};
floating: {
bar: string;
};
};
}, {
circle: string;
fill: string;
header: string;
title: string;
close: string;
group: string;
ring: string;
bar: string;
action: string;
actionLabel: string;
groupLabel: string;
}, undefined, {
selected: {
true: {
circle: string;
ring: string;
};
};
/**
* The action laid out along its label instead of above it.
*
* A screen's bar carries three or four actions side by side, so each one is
* a narrow column and the label belongs under the glyph. A sheet's footer
* usually carries one, full width — stacked there it is a tall block that
* costs the list a row of its own for no gain.
*/
compact: {
true: {
action: string;
actionLabel: string;
};
};
destructive: {
true: {
actionLabel: string;
};
};
disabled: {
true: {
action: string;
};
};
/**
* The group's surface, which depends on what is behind it.
*
* `card` is a step *down* from the background on a screen, which is what a
* grouped list wants there. A sheet is already `popover`, and popover sits
* above card in every dark theme — so the same card inside a sheet is
* darker than the sheet around it and reads as a hole rather than a raised
* set of rows.
*
* `muted` is an alpha tint, so it steps up from whatever it is drawn on and
* is correct against either. It is only used in the sheet because the
* screen's card is right where it is.
*/
surface: {
screen: {
group: string;
};
sheet: {
group: string;
};
};
/**
* Flush to the bottom edge, or lifted off it.
*
* `bar` is the platform shape — full width against the edge, a hairline
* along the top, and the same background as the screen's own chrome. It is
* the default because it is what a list with a selection in it does on both
* platforms, and because it does not take width away from the list.
*/
placement: {
bar: {
bar: string;
};
floating: {
bar: string;
};
};
}, {
circle: string;
fill: string;
header: string;
title: string;
close: string;
group: string;
ring: string;
bar: string;
action: string;
actionLabel: string;
groupLabel: string;
}, import("tailwind-variants").TVReturnType<{
selected: {
true: {
circle: string;
ring: string;
};
};
/**
* The action laid out along its label instead of above it.
*
* A screen's bar carries three or four actions side by side, so each one is
* a narrow column and the label belongs under the glyph. A sheet's footer
* usually carries one, full width — stacked there it is a tall block that
* costs the list a row of its own for no gain.
*/
compact: {
true: {
action: string;
actionLabel: string;
};
};
destructive: {
true: {
actionLabel: string;
};
};
disabled: {
true: {
action: string;
};
};
/**
* The group's surface, which depends on what is behind it.
*
* `card` is a step *down* from the background on a screen, which is what a
* grouped list wants there. A sheet is already `popover`, and popover sits
* above card in every dark theme — so the same card inside a sheet is
* darker than the sheet around it and reads as a hole rather than a raised
* set of rows.
*
* `muted` is an alpha tint, so it steps up from whatever it is drawn on and
* is correct against either. It is only used in the sheet because the
* screen's card is right where it is.
*/
surface: {
screen: {
group: string;
};
sheet: {
group: string;
};
};
/**
* Flush to the bottom edge, or lifted off it.
*
* `bar` is the platform shape — full width against the edge, a hairline
* along the top, and the same background as the screen's own chrome. It is
* the default because it is what a list with a selection in it does on both
* platforms, and because it does not take width away from the list.
*/
placement: {
bar: {
bar: string;
};
floating: {
bar: string;
};
};
}, {
circle: string;
fill: string;
header: string;
title: string;
close: string;
group: string;
ring: string;
bar: string;
action: string;
actionLabel: string;
groupLabel: string;
}, undefined, unknown, unknown, undefined>>;
type SelectionVariantProps = VariantProps;
interface SelectionModeContextValue {
active: boolean;
enter: (value?: string) => void;
exit: () => void;
selected: string[];
isSelected: (value: string) => boolean;
toggle: (value: string) => void;
selectAll: () => void;
clear: () => void;
/** True when everything selectable is picked, and there is something to pick. */
allSelected: boolean;
count: number;
/** How many rows `values` says there are, or 0 when it was not given. */
total: number;
max?: number;
haptics: boolean;
/**
* Whether the selection is being presented in a sheet.
*
* A sheet is opened *in order to* pick something, so there is no mode to
* enter and nothing to long-press for — and the action bar belongs to the
* sheet's footer rather than floating over the screen.
*/
sheet: boolean;
}
/**
* Read the selection from anywhere inside a `SelectionMode` — for a header of
* your own, a count somewhere else on the screen, or an action that has to know
* what is picked.
*/
export declare function useSelectionMode(): SelectionModeContextValue;
export interface SelectionModeProps extends ViewProps {
className?: string;
/**
* Every value that can be picked, in list order.
*
* Only "select all" and the "n of m" in the header need it — picking rows one
* at a time works without it. Give it the same ids you give the list.
*/
values?: string[];
/** Controlled selection mode. Leave it out and a long press turns it on. */
active?: boolean;
/** Whether selection mode starts on. */
defaultActive?: boolean;
onActiveChange?: (active: boolean) => void;
/** Controlled selection. */
selected?: string[];
defaultSelected?: string[];
onSelectedChange?: (selected: string[]) => void;
/**
* The most that can be picked at once.
*
* A row that would go over it does not toggle on, and "select all" stops at
* the limit rather than refusing. Leave it out for no limit.
*/
max?: number;
/**
* A tick when a row is picked and when the mode is entered. Off by default —
* needs the optional `expo-haptics`, and is silent without it.
*/
haptics?: boolean;
children: ReactNode;
}
declare function SelectionModeRoot({ className, values, active: activeProp, defaultActive, onActiveChange, selected: selectedProp, defaultSelected, onSelectedChange, max, haptics, children, ...props }: SelectionModeProps): import("react").JSX.Element;
declare namespace SelectionModeRoot {
var displayName: string;
}
export interface SelectionModeIndicatorProps {
className?: string;
/** Which row this stands for. Defaults to the row it is inside. */
value?: string;
}
/**
* The circle at the left of a row.
*
* Round rather than square, and that is the convention doing real work: a
* square box is a form control the reader is filling in, a round one is a thing
* they are picking out of a list. `Checkbox` is the former and stays that way.
*
* `Item` draws one for you. This is exported for a row that wants it somewhere
* else — over a photo's corner, at the end instead of the start.
*/
declare function SelectionModeIndicator({ className, value }: SelectionModeIndicatorProps): import("react").JSX.Element;
declare namespace SelectionModeIndicator {
var displayName: string;
}
export interface SelectionModeItemProps extends Omit {
className?: string;
/** This row's id. What ends up in `selected`. */
value: string;
/** What the row does whenever selection does not own its press. */
onPress?: () => void;
/**
* Stop this row entering selection mode, and being picked once in it. Its
* ordinary `onPress` still runs, including while selection is active. For a
* header row, an advert, a "load more" — anything in the list that is not one
* of the things being chosen between.
*/
disabled?: boolean;
/** Draw the circle without waiting for the mode. */
alwaysShowIndicator?: boolean;
/**
* How being picked is drawn.
*
* `leading` puts the circle in front of the item, which is what a row wants.
* `ring` draws a ring around whatever you gave it instead — for a swatch, a
* thumbnail or a photo, where a circle beside it would be a second thing to
* look at and the item itself can carry the state. `none` draws nothing and
* leaves it to you; read `useSelectionMode().isSelected`.
*/
indicator?: 'leading' | 'ring' | 'none';
children: ReactNode;
}
/**
* One row, with the circle in front of it.
*
* The press behaviour is the whole component: off mode, a press is the row's
* own and a long press turns the mode on with this row picked; in it, a press
* picks and unpicks. A row excluded from selection keeps its ordinary press in
* both states. Two meanings for one gesture is exactly why the mode has to be
* visible from the header.
*/
declare function SelectionModeItem({ className, value, onPress, disabled, alwaysShowIndicator, indicator, children, ...props }: SelectionModeItemProps): import("react").JSX.Element;
declare namespace SelectionModeItem {
var displayName: string;
}
export interface SelectionModeGroupProps extends ViewProps {
className?: string;
/**
* Lay the items out in a grid this many across instead of stacking them.
*
* For things recognised by sight rather than read — swatches, thumbnails,
* slides. A grid of six colours is one glance; the same six as rows is a
* scroll.
*
* Ignored when `horizontal` is set.
*/
columns?: number;
/**
* Lay the items out in one row that scrolls sideways.
*
* For a strip of small things next to other controls — swatches above a
* slider, filters above a list. A grid of the same items claims as many rows
* as it needs and pushes everything below it off the sheet; a strip costs one
* row whatever the count.
*
* Wins over `columns`, which asks for the opposite arrangement.
*/
horizontal?: boolean;
/** How wide each item is in a horizontal strip, in points. */
itemWidth?: number;
/** Space between items in a grid or a strip, in points. */
gap?: number;
/**
* A caption above the items, on the leading edge.
*
* Worth setting on anything picked by sight. A strip of colours with nothing
* in front of it is a row of circles the reader has to work out the purpose
* of, and a screen reader has nothing at all to announce it by — so this is
* also the group's accessibility label.
*/
label?: string;
/** Extra classes for that caption. */
labelClassName?: string;
/** Hairlines between stacked items. On by default; off in a grid or a strip. */
separators?: boolean;
children: ReactNode;
}
/**
* A rounded card holding a run of items.
*
* Grouping is what makes a sheet of choices readable: one card of options with
* hairlines between them reads as a set, and the same rows loose on the sheet's
* background read as a list that has not finished loading. It is also what the
* platform's own sheets do.
*
* Stacked by default, with a rule between each item. Pass `columns` for a grid.
*/
declare function SelectionModeGroup({ className, columns, horizontal, itemWidth, gap, label, labelClassName, separators, children, style, ...props }: SelectionModeGroupProps): string | number | bigint | boolean | Iterable | Promise> | Iterable | null | undefined> | import("react").JSX.Element | null | undefined;
declare namespace SelectionModeGroup {
var displayName: string;
}
export interface SelectionModeHeaderProps extends ViewProps {
className?: string;
/** The word in front of the count. */
title?: string;
/** Hide the select-all control, for a list where picking everything is wrong. */
hideSelectAll?: boolean;
/** Replaces the whole header's contents, keeping only its layout. */
children?: ReactNode;
}
/**
* The bar that says the mode is on: a way out, how many are picked, and all
* of them at once.
*
* Rendered only while the mode is on, and it is the thing that makes the mode
* legible — a list whose rows have quietly changed what a tap does, with no
* banner saying so, is a list that loses somebody's work.
*/
declare function SelectionModeHeader({ className, title, hideSelectAll, children, ...props }: SelectionModeHeaderProps): import("react").JSX.Element;
declare namespace SelectionModeHeader {
var displayName: string;
}
export interface SelectionModeBarProps extends ViewProps, Pick {
className?: string;
/**
* Room under the actions, in points — your safe-area inset.
*
* A bar against the bottom edge sits over the home indicator on a phone that
* has one, and an action under a home indicator is an action that takes two
* tries. `floating` uses it as the gap on all four sides instead.
*/
inset?: number;
/**
* Keep the bar up with nothing picked.
*
* Off by default: every action on it needs something to act on, and a row of
* buttons that all refuse is worse than a row that is not there yet.
*/
showWhenEmpty?: boolean;
children: ReactNode;
}
/**
* The actions, across the bottom of the list.
*
* Over the list rather than under it, because the list is as long as it is and
* a bar in the flow would be somewhere off the end of it. **Pad the bottom of
* your list so the last row can clear this** — nothing here can work out how
* tall the list is.
*
* Flush to the edge by default. A bar inset from the sides is a card floating
* over a list, which reads as something that arrived rather than as the mode
* the screen is in — and it takes width away from the actions, which are the
* one row of controls on screen that must not be cramped.
*/
declare function SelectionModeBar({ className, placement, inset, showWhenEmpty, children, style, ...props }: SelectionModeBarProps): import("react").JSX.Element | null;
declare namespace SelectionModeBar {
var displayName: string;
}
export interface SelectionModeActionProps extends Omit, Pick {
className?: string;
/** The glyph above the label. */
icon?: ReactNode;
/**
* What it does. Handed the selection, so the common case needs no other
* wiring — and leaving the mode afterwards is up to you, because whether the
* list still makes sense depends on what you did to it.
*/
onPress?: (selected: string[]) => void;
/** Leave selection mode after the action runs. */
exitOnPress?: boolean;
disabled?: boolean;
/** Extra classes for the label. */
labelClassName?: string;
children?: ReactNode;
}
/**
* One action in the bar: a glyph with its name under it.
*
* Labelled, always. A row of bare glyphs at the bottom of a screen is a row of
* guesses, and one of them usually deletes something.
*/
declare function SelectionModeAction({ className, icon, onPress, exitOnPress, disabled, destructive, labelClassName, children, ...props }: SelectionModeActionProps): import("react").JSX.Element;
declare namespace SelectionModeAction {
var displayName: string;
}
export interface SelectionModeSheetProps {
className?: string;
/** Controlled open state of the sheet. */
open?: boolean;
defaultOpen?: boolean;
onOpenChange?: (open: boolean) => void;
/** The word in front of the count. */
title?: string;
/** Hide the select-all control. */
hideSelectAll?: boolean;
/**
* How tall the sheet opens.
*
* `full` by default, and deliberately not `auto`. A sheet that sizes to its
* content gives its scrolling body no height to fill, and a list inside a box
* of no height draws nothing — which looks like an empty sheet rather than
* like a missing style.
*
* Full rather than half because a picker spends a header and a footer before
* it draws a single row. At half a screen that leaves four or five rows for
* the thing the sheet was opened to do, and the reader scrolls a list that
* would have fitted. Pass `half` for a sheet of two or three choices.
*/
size?: 'auto' | 'half' | 'full';
/**
* The things to pick between, and optionally a `SelectionMode.Bar` of
* actions. The bar is lifted into the sheet's footer wherever it is written.
*/
children: ReactNode;
}
/**
* The whole selection, presented in a bottom sheet.
*
* A picker rather than a mode. The list on a screen has to be *turned into* one
* you can pick from — hence the long press, the cancel and the count — but a
* sheet was opened in order to pick something, so it is picking from the moment
* it appears and there is nothing to enter or leave.
*
* What goes in it is anything: a column of friends, a grid of colours, a run of
* slides. `SelectionMode.Item` wraps whatever you give it, so the sheet does
* not need to know what it is holding.
*
* ```tsx
*
*
* {people.map((person) => (
*
* - …
*
* ))}
*
* } onPress={share}>Send
*
*
*
* ```
*/
declare function SelectionModeSheet({ className, open, defaultOpen, onOpenChange, title, hideSelectAll, size, children, }: SelectionModeSheetProps): import("react").JSX.Element;
declare namespace SelectionModeSheet {
var displayName: string;
}
export declare const SelectionMode: typeof SelectionModeRoot & {
Sheet: typeof SelectionModeSheet;
Group: typeof SelectionModeGroup;
Item: typeof SelectionModeItem;
Indicator: typeof SelectionModeIndicator;
Header: typeof SelectionModeHeader;
Bar: typeof SelectionModeBar;
Action: typeof SelectionModeAction;
};
export {};
//# sourceMappingURL=index.d.ts.map