// A hold-only wrapper — the smallest interactive primitive in the library.
//
// `useHoldable` is the whole behaviour; this is that hook plus the elements it
// cannot render: a host `View` and the `GestureDetector` a hook cannot mount.
// Reach for the hook directly when the host must be something other than a
// plain `View`.
//
// The child may be a plain node or a render-prop receiving the current press
// state, which is how a child drives its own pressed or selected look without
// lifting state to the parent:
//
// select(id)}>
// {({ isPressed, isHeld }) => (
//
// )}
//
import type { ReactNode, Ref } from 'react';
import { View, type ViewProps } from 'react-native';
import { GestureDetector } from 'react-native-gesture-handler';
import type { PressPhase } from '../press-timeline';
import { type UseHoldableOptions, useHoldable } from './use-holdable';
/** State passed to a render-prop child. */
export type HoldableState = {
/** The hold has fired and the finger has not let go. */
isHeld: boolean;
/**
* The press has committed — true from `armDelay` through the hold.
* Use this for a pressed style rather than `phase === 'active'` directly, to
* avoid dropping the pressed look the instant the hold fires.
*/
isPressed: boolean;
/** Raw phase, for consumers that need finer-grained branching. */
phase: PressPhase;
};
export type HoldableProps = Omit &
UseHoldableOptions & {
children?: ReactNode | ((state: HoldableState) => ReactNode);
ref?: Ref;
};
/**
* Makes its child respond to a hold press, with a clean separation between the
* scroll window (before `armDelay`), the pressed state (after it), and the hold
* action (at `holdDelay`).
*
* ```tsx
* select(row.id)}>
* {({ isPressed }) =>
}
*
* ```
*
* **No drag.** For a component that holds *and* drags, use ``.
*
* **Thresholds.** The defaults hold on every platform (unlike ``,
* which has no hold on web by default). Override with `behavior` — same shape as
* ``.
*
* **Accessibility.** Carries none: no role, no announcement. A hold is
* touch-only and pointer-only — add a second path to the same outcome, such as
* an `accessibilityActions` entry on the host.
*/
export function Holdable({
behavior,
children,
className,
cursorMode = false,
disabled,
onActive,
onHold,
onHoldEscape,
onPhaseChange,
ref,
style,
testID,
...viewProps
}: HoldableProps) {
const hold = useHoldable({ behavior, cursorMode, disabled, onActive, onHold, onHoldEscape, onPhaseChange });
const root = hold.getRootProps();
const state: HoldableState = { isHeld: hold.isHeld, isPressed: hold.isPressed, phase: hold.phase };
const resolvedChildren = typeof children === 'function' ? children(state) : children;
const host = (
{resolvedChildren}
);
return hold.gesture === null ? host : {host};
}