Add .onCommit() to the gesture builder
`.onCommit(handler)` is available at every builder stage after the direction, returns the same stage, and accumulates handlers in order. Handlers run once, synchronously at release, when the operation commits and before the settling animation, with the same context `.complete()` receives. A throwing handler is reported without stopping later handlers or the operation. Intended for haptic feedback such as `navigator.vibrate`. Named onCommit rather than then so builder stages never become thenables. The builder now threads one frozen draft through its stages. OriginOperationHandle gains a read-only `intent`, which the recognizer reads right after finish() to know whether the release committed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Faarh2Ts6StJPcz2mRiHiR
This commit is contained in:
@@ -168,8 +168,22 @@ export interface OriginGestureDefinition {
|
||||
readonly navigation: OriginGestureNavigationFactory;
|
||||
/** Visual routine paired with the navigation intent. */
|
||||
readonly choreography: OriginChoreography;
|
||||
/** Handlers run, in order, when a release commits. See `.onCommit()`. */
|
||||
readonly commitHandlers?: readonly OriginGestureCommitHandler[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Side effect run when a released gesture commits, such as haptic feedback.
|
||||
*
|
||||
* It runs once, synchronously at release, after the commit decision and
|
||||
* before the settling animation. It receives the same context as a
|
||||
* `.complete()` predicate. Errors are reported without interrupting the
|
||||
* operation.
|
||||
*/
|
||||
export type OriginGestureCommitHandler = (
|
||||
context: OriginGestureCompletionContext,
|
||||
) => void;
|
||||
|
||||
/** Direction-selection stage shared by `gesture.to` and `.from.*().to`. */
|
||||
export interface OriginGestureToBuilder {
|
||||
/** Recognize leftward pointer movement. */
|
||||
@@ -214,6 +228,8 @@ export interface OriginGestureDirectedBuilder {
|
||||
navigate(
|
||||
factory: OriginGestureNavigationFactory,
|
||||
): OriginGestureNavigationBuilder;
|
||||
/** Run `handler` when a release commits. Handlers accumulate in order. */
|
||||
onCommit(handler: OriginGestureCommitHandler): OriginGestureDirectedBuilder;
|
||||
}
|
||||
|
||||
/** Builder stage after a custom completion policy has been selected. */
|
||||
@@ -222,12 +238,16 @@ export interface OriginGestureCompletedBuilder {
|
||||
navigate(
|
||||
factory: OriginGestureNavigationFactory,
|
||||
): OriginGestureNavigationBuilder;
|
||||
/** Run `handler` when a release commits. Handlers accumulate in order. */
|
||||
onCommit(handler: OriginGestureCommitHandler): OriginGestureCompletedBuilder;
|
||||
}
|
||||
|
||||
/** Final builder stage waiting for visual choreography. */
|
||||
export interface OriginGestureNavigationBuilder {
|
||||
/** Attach visual choreography and produce an executable definition. */
|
||||
animate(choreography: OriginChoreography): OriginGestureDefinition;
|
||||
/** Run `handler` when a release commits. Handlers accumulate in order. */
|
||||
onCommit(handler: OriginGestureCommitHandler): OriginGestureNavigationBuilder;
|
||||
}
|
||||
|
||||
/** Root of the immutable fluent gesture-definition API. */
|
||||
@@ -368,55 +388,86 @@ export interface OriginGestureSurfaceProps {
|
||||
// Builder ------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Immutable fluent builder. Each stage is a frozen object closing over the
|
||||
* choices made so far; the final `.animate()` produces a frozen definition.
|
||||
* Immutable fluent builder. Each stage is a frozen object holding the choices
|
||||
* made so far in a frozen draft; the final `.animate()` produces a frozen
|
||||
* definition. `.onCommit()` is available at every stage after the direction
|
||||
* and returns the same stage with the handler appended.
|
||||
*
|
||||
* gesture[.from.<start>].to.<direction>()[.complete()].navigate().animate()
|
||||
*/
|
||||
|
||||
function navigationStage(
|
||||
start: OriginGestureStart,
|
||||
direction: OriginGestureDirection,
|
||||
recognition: Readonly<OriginGestureDirectionOptions>,
|
||||
completion: OriginGestureCompletionPredicate | undefined,
|
||||
) {
|
||||
return (
|
||||
navigation: OriginGestureNavigationFactory,
|
||||
): OriginGestureNavigationBuilder =>
|
||||
Object.freeze({
|
||||
animate: (choreography: OriginChoreography): OriginGestureDefinition =>
|
||||
Object.freeze({
|
||||
kind: "origin-gesture-definition",
|
||||
start,
|
||||
direction,
|
||||
recognition,
|
||||
completion,
|
||||
navigation,
|
||||
choreography,
|
||||
}),
|
||||
});
|
||||
/** Choices accumulated by the builder before `.animate()`. */
|
||||
interface GestureDraft {
|
||||
readonly start: OriginGestureStart;
|
||||
readonly direction: OriginGestureDirection;
|
||||
readonly recognition: Readonly<OriginGestureDirectionOptions>;
|
||||
readonly completion?: OriginGestureCompletionPredicate;
|
||||
readonly commitHandlers: readonly OriginGestureCommitHandler[];
|
||||
}
|
||||
|
||||
function directedStage(
|
||||
start: OriginGestureStart,
|
||||
direction: OriginGestureDirection,
|
||||
options: OriginGestureDirectionOptions = {},
|
||||
): OriginGestureDirectedBuilder {
|
||||
const recognition = Object.freeze({ ...options });
|
||||
const withCommitHandler = <Draft extends GestureDraft>(
|
||||
draft: Draft,
|
||||
handler: OriginGestureCommitHandler,
|
||||
): Draft =>
|
||||
Object.freeze({
|
||||
...draft,
|
||||
commitHandlers: Object.freeze([...draft.commitHandlers, handler]),
|
||||
});
|
||||
|
||||
function navigationStage(
|
||||
draft: GestureDraft & { readonly navigation: OriginGestureNavigationFactory },
|
||||
): OriginGestureNavigationBuilder {
|
||||
return Object.freeze({
|
||||
animate: (choreography: OriginChoreography): OriginGestureDefinition =>
|
||||
Object.freeze({
|
||||
kind: "origin-gesture-definition",
|
||||
start: draft.start,
|
||||
direction: draft.direction,
|
||||
recognition: draft.recognition,
|
||||
completion: draft.completion,
|
||||
navigation: draft.navigation,
|
||||
choreography,
|
||||
commitHandlers: draft.commitHandlers,
|
||||
}),
|
||||
onCommit: (handler: OriginGestureCommitHandler) =>
|
||||
navigationStage(withCommitHandler(draft, handler)),
|
||||
});
|
||||
}
|
||||
|
||||
const navigateFrom =
|
||||
(draft: GestureDraft) => (navigation: OriginGestureNavigationFactory) =>
|
||||
navigationStage(Object.freeze({ ...draft, navigation }));
|
||||
|
||||
function completedStage(draft: GestureDraft): OriginGestureCompletedBuilder {
|
||||
return Object.freeze({
|
||||
navigate: navigateFrom(draft),
|
||||
onCommit: (handler: OriginGestureCommitHandler) =>
|
||||
completedStage(withCommitHandler(draft, handler)),
|
||||
});
|
||||
}
|
||||
|
||||
function directedStage(draft: GestureDraft): OriginGestureDirectedBuilder {
|
||||
return Object.freeze({
|
||||
complete: (completion: OriginGestureCompletionPredicate) =>
|
||||
Object.freeze({
|
||||
navigate: navigationStage(start, direction, recognition, completion),
|
||||
}),
|
||||
navigate: navigationStage(start, direction, recognition, undefined),
|
||||
completedStage(Object.freeze({ ...draft, completion })),
|
||||
navigate: navigateFrom(draft),
|
||||
onCommit: (handler: OriginGestureCommitHandler) =>
|
||||
directedStage(withCommitHandler(draft, handler)),
|
||||
});
|
||||
}
|
||||
|
||||
function toStage(start: OriginGestureStart): OriginGestureToBuilder {
|
||||
const direct =
|
||||
(direction: OriginGestureDirection) =>
|
||||
(options?: OriginGestureDirectionOptions) =>
|
||||
directedStage(start, direction, options);
|
||||
(options: OriginGestureDirectionOptions = {}) =>
|
||||
directedStage(
|
||||
Object.freeze({
|
||||
start,
|
||||
direction,
|
||||
recognition: Object.freeze({ ...options }),
|
||||
commitHandlers: Object.freeze([]),
|
||||
}),
|
||||
);
|
||||
return Object.freeze({
|
||||
left: direct("left"),
|
||||
right: direct("right"),
|
||||
|
||||
Reference in New Issue
Block a user