/** * CameraShutter — dual-mode shutter button for the SDK's panorama UX. * * ┌──────────────────────────────────────────────────┐ * │ TAP → take a single photo (existing flow) │ * │ HOLD → start recording video │ * │ RELEASE → stop recording → return video file │ * └──────────────────────────────────────────────────┘ * * The button is "pure" UI — it owns gesture detection + visual * feedback (idle / pressing / recording / processing rings) but * NOT the recording / stitching pipeline itself. Host apps wire * the resulting `onTap` / `onHoldComplete` callbacks to whatever * `useCapture` / `useVideoCapture` instance they've configured. * * Why expose just the button (not the full surface)? * Different audit screens want different layouts (thumbnails, * quality badges, mode chips); pinning them all to one * "PanoramaCaptureSurface" would overfit one customer's UX. The * button is the only piece every screen needs identical, so it * ships in the SDK. The orchestration helpers ship as * `` next door — host apps that want full * plug-and-play use that; the rest stitch this button into their * own layout. * * Gesture detection * onPressIn fires immediately on touch down; we start a * ``HOLD_THRESHOLD_MS`` timer. Two outcomes: * * - onPressOut fires before the timer → it was a tap. * - timer fires before onPressOut → transition to recording * state, fire ``onHoldStart``. When onPressOut eventually * fires we call ``onHoldComplete``. * * We deliberately do NOT use react-native-gesture-handler — the * Pressable + setTimeout pattern stays in the SDK's existing dep * surface (RN core only). Adds zero new peer deps for host apps. */ import React from 'react'; import { type ViewStyle } from 'react-native'; export interface CameraShutterProps { /** Called when the user taps (press-and-release before the threshold). */ onTap: () => void; /** Called when the press crosses the threshold and recording should start. */ onHoldStart: () => void; /** Called on release while in the hold state — recording should stop. */ onHoldComplete: () => void; /** * Maximum hold duration in milliseconds. When the timer fires * we auto-fire `onHoldComplete` — same behaviour as the user * releasing the button. Default 8000 ms; keeps recording * within the stitcher's adjacent-frame-overlap budget * (16 frames × 2 fps = 8 s upper bound). Pass 0 / undefined * to disable the auto-stop. * * Pair with `` so the user * sees how much hold time is left. */ maxHoldMs?: number; /** * Optional state-driven visual override. When the host has its own * processing indicator (e.g. "Stitching... 70%") set this to true to * paint the button in the disabled-while-processing visual. */ isProcessing?: boolean; /** Disable the whole button (e.g. while permissions are loading). */ disabled?: boolean; /** * Whether the HOLD gesture is live (default `true`). Set `false` for a * tap-only shutter: the press never transitions to the holding phase, so * `onHoldStart`/`onHoldComplete` never fire, the red recording ring never * paints, and the a11y label drops the "hold for panorama" half. * * WHY IT EXISTS: without it, a host that wires `onHoldStart` to a no-op (a * capture mode with no panorama) still gets the full press→hold transition — * the button paints a red "Recording — release to stitch panorama" ring while * NOTHING records. This is the tap-only shutter Photo mode needs once Pano is * its own mode. Default `true` keeps every existing consumer byte-identical. */ holdEnabled?: boolean; /** Optional style applied to the outer touch target. */ style?: ViewStyle; } /** * Imperative handle so a parent can force-release (e.g. on unmount * during a long press). Exposed via forwardRef. */ export interface CameraShutterHandle { /** Cancel any in-flight hold without calling onHoldComplete. */ cancelHold: () => void; } export declare const CameraShutter: React.ForwardRefExoticComponent>; //# sourceMappingURL=CameraShutter.d.ts.map