import { computed, inject, type ComputedRef, type InjectionKey } from "vue"; import type { OriginAction } from "../actions/types"; import type { OriginContext, OriginOperationHandle, OriginScene, } from "../scene/types"; import type { OriginView } from "../view/types"; /** * Injection payload provided by each stable scene-node host. * * @internal */ export interface OriginNodeScope { /** Scene containing the node. */ readonly scene: OriginScene; /** Unique key of the mounted node. */ readonly nodeKey: string; } /** Node-scoped controls returned by {@link useOrigin}. */ export interface UseOrigin { /** Key of the mounted node containing the calling component. */ readonly nodeKey: string; /** Scene containing the calling component. */ readonly scene: OriginScene; /** Reactive context for the calling component's scene node. */ readonly context: ComputedRef; /** Reactive shorthand for `context.value.view`. */ readonly view: ComputedRef; /** Reactive shorthand for `context.value.previous`. */ readonly previous: ComputedRef; /** Reactive shorthand for `context.value.canGoBack`. */ readonly canGoBack: ComputedRef; /** Begin an interactively controlled operation from this component. */ begin(action: OriginAction): Promise; /** Programmatically create and commit a target from this component. */ perform(action: OriginAction): Promise; } /** * Injection key used by the internal scene-node host to establish origin * ownership for descendant components. * * Application code normally calls {@link useOrigin} instead of injecting this * key directly. * * @internal */ export const originNodeScopeKey: InjectionKey = Symbol("origin-node-scope"); /** * Access the scene from the component that owns an interaction declaration. * * There is deliberately no `activeView`: the injected node is the origin * because this component is where the event or application action occurred. * * @returns Node-scoped scene state and operation controls. * @throws If called outside a component rendered by `OriginScene`. * * @example * ```ts * const origin = useOrigin(); * * function openProfile() { * return origin.perform( * forward(originView(ProfileView), slideLeft), * ); * } * ``` */ export function useOrigin(): UseOrigin { const scope = inject(originNodeScopeKey); if (!scope) throw new Error("useOrigin() must be called inside an view."); const { scene, nodeKey } = scope; const context = computed(() => scene.contextFor(nodeKey)); return { nodeKey, scene, context, view: computed(() => context.value.view), previous: computed(() => context.value.previous), canGoBack: computed(() => context.value.canGoBack), begin: (action) => scene.begin(nodeKey, action), perform: (action) => scene.perform(nodeKey, action), }; }