/** * Pure sheet math in reveal-px space — release (snap/dismiss) decisions, * transition durations, and drag-ownership arbitration. * * `reveal` is the sheet's visible height in px (0 = fully hidden). Both * sheet frontends drive a reveal SharedValue; everything their worklets * decide is delegated here so the logic is unit-testable — the MT worklet * runtime isn't available under `@sigx/lynx-testing`. All functions are * worklet-safe: pure, no captures, ES5-level operations only. */ /** * How far ahead (seconds) a release projects the finger's velocity to pick * its landing position. On-device testing showed a raw velocity threshold * (the original 300 px/s, mirroring EdgeBackHandle) misfires for sheets: a * controlled ~360 px/s downward drag from the upper detent read as a * "dismiss fling" even though the finger clearly aimed at the lower * detent. Projecting position instead unifies both decisions: a genuine * fling projects past the dismiss line from anywhere; a controlled drag * projects near a detent and settles there. */ export declare const PROJECTION_SEC = 0.2; /** * Projected release reveal: where the sheet would land if the finger's * velocity (`velocityY` px/sec, positive = downward) carried it for * `PROJECTION_SEC`. Unclamped — callers compare against thresholds. */ export declare function projectReveal(revealPx: number, velocityY: number): number; /** * Whether a release should dismiss the sheet — true when the projected * landing position falls below the dismiss line under the floor detent. */ export declare function shouldDismiss(revealPx: number, velocityY: number, floorPx: number): boolean; /** * Pick the candidate index to settle at on release: the one nearest the * PROJECTED landing position. A fling naturally selects the next * candidate in its direction; a slow release picks the nearest one to the * finger. `candidatesPx` must be ascending. Returns `-1` when empty. */ export declare function nearestDetentIndex(revealPx: number, velocityY: number, candidatesPx: readonly number[]): number; /** * Floor for sheet transition durations — keeps very low detents from * snapping open/closed instantly under velocity matching. */ export declare const REVEAL_MIN_DURATION_SEC = 0.15; /** * Sheet transition duration, velocity-matched to the card/modal slide: * those travel the full screen in `fullSlideDurationSec`, while a sheet * only travels to its detent — a flat duration made the sheet move at a * fraction of the modal's speed and read as sluggish (#290). * `heightFraction` is the share of screen height traveled. */ export declare function revealDurationSec(heightFraction: number, fullSlideDurationSec: number): number; /** "At max detent" tolerance in reveal px. */ export declare const MAX_EPS_PX = 0.5; /** Height of the always-drags chrome zone at the sheet's top edge. */ export declare const GRABBER_HEIGHT = 28; /** Owner states — plain numbers so they worklet-capture as literals. */ export declare const OWNER_UNDECIDED = 0; export declare const OWNER_SHEET = 1; export declare const OWNER_CONTENT = 2; /** * Inputs to one arbitration step. Flags are `0 | 1` numbers, matching the * SharedValues they're typically read from in worklets. */ export interface DragOwnerInput { /** Finger travel since touch start (px). */ dx: number; dy: number; /** `1` when this frame moved downward (pageY > previous frame's). */ frameDown: number; /** Touch-start Y in page coords. */ startPageY: number; /** Current visible sheet height (px). */ combinedPx: number; /** Largest detent (px) — "at max" gates content scrolling. */ maxPx: number; /** * Page-coord Y of the sheet's bottom edge — `SCREEN_HEIGHT` for a * screen-anchored route sheet; an inline sheet passes its own * measured bottom edge. `sheetTop = bottomEdgePageY - combinedPx`. */ bottomEdgePageY: number; /** Height of the always-claims chrome zone; `0` disables it. */ grabberPx: number; /** `1` in grabber-only drag mode (the body never drags the sheet). */ grabberOnly: number; /** `1` when an adopted inner vertical scrollable exists. */ hasScroll: number; /** The adopted scrollable's live scroll offset (px). */ scrollOffsetY: number; /** Current owner (`OWNER_*`) — decisions are sticky except handoff. */ currentOwner: number; } /** * Resolve who owns this drag frame — the 8-step UNDECIDED → SHEET|CONTENT * arbitration (UX reference: iOS detents / gorhom's bottom-sheet): * * 1. touch in the top `grabberPx` of the surface → SHEET (chrome) * 2. mostly-horizontal drag → CONTENT (native `.axis('y')` never fires * these; the check is load-bearing on web, whose pan fallback * ignores the axis config) * 3. grabber-only mode → CONTENT (body never drags) * 4. no adopted vertical scrollable → SHEET (plain-content sheets) * 5. sheet below max detent → SHEET (content is rest-locked anyway) * 6. at max, dragging up → CONTENT * 7. at max, dragging down, scroll offset ≤ 0 → SHEET (`≤` covers iOS * bounce-negative offsets) * 8. at max, dragging down, scroll offset > 0 → CONTENT, watching for * the one allowed mid-gesture handoff: content reaches top under the * finger while still moving down → SHEET takes over. Never the * reverse (native can't re-deliver an in-flight touch to a * re-enabled scroll pan). * * A SHEET decision is final for the gesture; CONTENT re-evaluates only * the step-8 handoff conjunction (guarded off for grabber-only mode — the * other CONTENT parkings can't satisfy at-max + down + at-top without * genuinely becoming a collapse pull, at which point taking over is the * right feel). */ export declare function decideDragOwner(i: DragOwnerInput): number; //# sourceMappingURL=math.d.ts.map