import { type ReactNode } from "react";
import { type TextInput as RNTextInput } from "react-native";
import { type ColorTokens, type ViewStyle, type TextStyle, type LayoutStyle } from "../../style/index.js";
export type Size = "small" | "base" | "large";
export interface StepperProps {
/** Controlled numeric value (clamped to [min, max] for display); omit for uncontrolled use. */
value?: number;
/** Initial value for uncontrolled use (a bare steps out of the box). Default `min`. */
defaultValue?: number;
/** Fired with the next clamped value when buttons, adjustment keys, or direct entry change it (both modes). */
onChange?: (next: number) => void;
/** E2E hook forwarded to the group container. */
testID?: string;
/** Lower bound. Default 0. The − button disables when value <= min. */
min?: number;
/** Upper bound. Default Number.MAX_SAFE_INTEGER. The + button disables when value >= max. */
max?: number;
/** Increment/decrement amount for the ± buttons and adjustment keys. Default 1. */
step?: number;
/**
* The control's persistent, component-owned label. When set it renders as a
* VISIBLE title ABOVE the ± control (mirroring Input's above-field placement,
* per-OS type from the skin) and becomes the group's programmatic name (wired via
* accessibilityLabel + aria-labelledby). When omitted, the control keeps the
* invisible "Number" accessible-name fallback and renders no visible label, so a
* bare `` looks exactly as before.
*/
label?: string;
/** Optional muted secondary line rendered under the label (parity with Input). Takes effect only alongside `label`. */
description?: ReactNode;
/**
* Marks the control required: appends a destructive "*" to the label (decorative,
* never in the accessible name) and announces the control as required,
*
* through
* aria-required on the web's numeric field and with the word in its name ("Quantity,
* required") on iOS and Android.
*
*
* with the word in its name ("Quantity, required").
*
* The star takes effect only alongside `label`.
*/
required?: boolean;
/**
* A unit or note beside the control, 16 px after it on the same line ("× 4px",
* "items"): a string is set in the 400 13 px subtle ink, a node as it is. Unlike
* `description`, it reads as part of the value's line.
*/
suffix?: ReactNode;
small?: boolean;
large?: boolean;
disabled?: boolean;
/** Composition within a parent only, never a restyle hook and never a width: the parent layout container provides the bounds. */
style?: LayoutStyle;
}
export interface StepperSkin {
/** Clear Liquid Glass text entry on the web appearance. */
liquid?: boolean;
/**
* The outer group container (the [ − | value | + ] shell shape). `focused` is the
* value field holding focus: a skin whose group has a border paints its focus state
* there (the web box turns its border `ring`) and sets `groupShowsFocus`.
*/
group: (t: ColorTokens, size: Size, disabled: boolean, focused: boolean) => ViewStyle;
/**
* The group's border is the value field's keyboard focus indicator, so the field
* suppresses the browser's ring. A skin whose field sits bare (no box of its own to
* paint) leaves it unset, and the field keeps the kit's themed ring.
*/
groupShowsFocus?: boolean;
/** One ± button cell; `side` lets the skin round the matching outer corner. */
button: (t: ColorTokens, size: Size, side: "left" | "right", disabled: boolean, pressed: boolean) => ViewStyle;
/** A ± button's wash under a hovering pointer; omit for none. */
buttonHover?: (t: ColorTokens) => ViewStyle;
/** The editable center field surface (and its type scale). */
field: (t: ColorTokens, size: Size, disabled: boolean) => TextStyle;
/** Divider line between a button and the value (null = no separator on this skin). */
divider: ((t: ColorTokens, disabled: boolean) => ViewStyle) | null;
/** The ± glyph color and px size; `off` is a button at its bound (or the whole control disabled). */
glyph: (t: ColorTokens, size: Size, off: boolean) => {
color: string;
size: number;
};
/**
* Web dims a ± button on press. null on Android (ripple carries the feedback)
* and on iOS (the skin's `button` paints a highlight fill on the pressed half
* instead — the iOS 27 stepper never dims its glyph).
*/
pressedOpacity: number | null;
/** Android ripple over a ± button; null on iOS/web. */
ripple: ((t: ColorTokens) => {
color: string;
borderless: boolean;
}) | null;
/**
* Uniform hitSlop (px per edge) padding a ± button's touch target out to the
* platform minimum (Android M3 48dp); null = no padding (web, and iOS where
* Apple's own 32pt UIStepper sets the precedent).
*/
hitSlop: ((size: Size) => number) | null;
/** Layout order: the iOS HIG puts the value field to the LEFT of the [ − | + ] pill. */
fieldOnLeft: boolean;
/** The control's opacity when disabled (solid mode). */
disabledOpacity: number;
/**
* The persistent label rendered ABOVE the control (the canonical field-label
* type, matching Input's label scale): iOS the SF field-label (semibold 600,
* -0.15 tracking), web the 500-weight title, Android the M3 label (medium, M3
* tracking). The floating M3 in-container label Input uses does not apply — a ±
* button row has no filled field box to float a label into — so Android keeps the
* component-owned title above, at the M3 label type.
*/
labelAbove: (t: ColorTokens, size: Size) => TextStyle;
/** The secondary description line under the control (per-OS supporting-text type). */
description: (t: ColorTokens, size: Size) => TextStyle;
/** A string `suffix`'s type beside the control. */
suffix: (t: ColorTokens) => TextStyle;
}
export declare function sizeOf(p: StepperProps): Size;
export declare function createStepper(skin: StepperSkin): import("react").ForwardRefExoticComponent>;
//# sourceMappingURL=stepper.shared.d.ts.map