{"version":3,"file":"index.mjs","sources":["../../../src/view/index.ts"],"sourcesContent":["import { noop } from \"motion-utils\"\nimport type { GroupAnimation } from \"../animation/GroupAnimation\"\nimport { AnimationOptions, DOMKeyframesDefinition } from \"../animation/types\"\nimport { addBuilderToScope } from \"../scope/active\"\nimport { addToQueue } from \"./queue\"\nimport {\n    ViewTransitionOptions,\n    ViewTransitionTarget,\n    ViewTransitionTargetDefinition,\n} from \"./types\"\nimport \"./types.global\"\n\n// Re-export the public view-transition types so they flow out through\n// motion-dom -> framer-motion -> motion alongside `animateView`.\nexport type * from \"./types\"\n\nexport class ViewTransitionBuilder {\n    private currentSubject: ViewTransitionTargetDefinition = \"root\"\n\n    targets = new Map<ViewTransitionTargetDefinition, ViewTransitionTarget>()\n\n    /**\n     * Definitions that must be resolved to elements (and assigned a\n     * `view-transition-name`) rather than treated as pre-named layers.\n     */\n    resolveDefs = new Set<ViewTransitionTargetDefinition>()\n\n    /**\n     * Per-subject crop override: `true` forces the crop (clip + object-fit:\n     * cover + animated corner radii) on, `false` forces it off. A subject with\n     * no entry uses the default - crop only a genuine morph (a layer present in\n     * both snapshots), so a fade-only enter/exit isn't clipped to nothing.\n     */\n    cropOverride = new Map<ViewTransitionTargetDefinition, boolean>()\n\n    /**\n     * Subjects paired with a different new-snapshot target (the second `.add()`\n     * argument), so two distinct elements share one name and morph into each\n     * other - a shared-element transition.\n     */\n    pairs = new Map<\n        ViewTransitionTargetDefinition,\n        ViewTransitionTargetDefinition\n    >()\n\n    /**\n     * A `view-transition-class` to apply to each subject's resolved elements,\n     * so authors can target the generated layers from CSS by class rather than\n     * the opaque generated name.\n     */\n    classNames = new Map<ViewTransitionTargetDefinition, string>()\n\n    /**\n     * Subjects opted out of automatic group nesting via `.group(false)`. Their\n     * layer stays a flat top-level group (`view-transition-group: none`) instead\n     * of nesting under its DOM-ancestor layer - so it animates independently and\n     * escapes an ancestor's clip/transform (e.g. an element that lifts out of a\n     * card and flies across, which nesting would clip to the card).\n     */\n    flatGroups = new Set<ViewTransitionTargetDefinition>()\n\n    update: () => void | Promise<void>\n\n    options: ViewTransitionOptions\n\n    notifyReady: (value: GroupAnimation) => void = noop\n\n    notifyReject: (error: unknown) => void = noop\n\n    private readyPromise = new Promise<GroupAnimation>((resolve, reject) => {\n        this.notifyReady = resolve\n        this.notifyReject = reject\n    })\n\n    constructor(\n        update: () => void | Promise<void>,\n        options: ViewTransitionOptions = {}\n    ) {\n        this.update = update\n        this.options = {\n            interrupt: \"wait\",\n            ...options,\n        }\n        // Avoid an unhandled rejection when a failed transition has no\n        // `.then(_, reject)` handler attached (e.g. fire-and-forget).\n        this.readyPromise.catch(noop)\n        addToQueue(this)\n    }\n\n    /**\n     * Target elements resolved from a selector or Element, each assigned a\n     * `view-transition-name` automatically.\n     *\n     * Passing a second target pairs them: the first is resolved in the old\n     * snapshot and the second in the new, sharing one name so two *different*\n     * elements morph into each other (e.g. `.add(card, \".modal\")`). Symmetric -\n     * pass them the other way round to morph back.\n     */\n    add(\n        subject: ViewTransitionTargetDefinition,\n        newSubject?: ViewTransitionTargetDefinition\n    ) {\n        this.currentSubject = subject\n        this.resolveDefs.add(subject)\n        if (newSubject !== undefined) this.pairs.set(subject, newSubject)\n        // Register the subject so it participates (and gets an automatic\n        // layout/morph animation) even without an explicit enter/exit/layout.\n        if (!this.targets.has(subject)) this.targets.set(subject, {})\n\n        return this\n    }\n\n    /**\n     * Control this subject's crop (clip + `object-fit: cover` + animated\n     * corners). By default a subject auto-crops only when it actually morphs -\n     * present in both snapshots (a survivor, or an `.add(a, b)` pair). A\n     * fade-only enter/exit has no second box to crop against, so it's left to\n     * the browser default; in particular the `overflow: clip` a crop adds would\n     * otherwise clip a mis-sized enter/exit layer to nothing.\n     *\n     * `.crop(false)` forces the crop off (e.g. a text morph, where\n     * `object-fit: cover` clips glyphs as the box grows); `.crop(true)` forces\n     * it on for a non-morph the default wouldn't otherwise crop.\n     */\n    crop(enabled = true) {\n        this.cropOverride.set(this.currentSubject, enabled)\n\n        return this\n    }\n\n    /**\n     * By default a subject's layer nests under its nearest DOM-ancestor layer\n     * (`view-transition-group: contain`), so the ancestor's clip/transform/opacity\n     * apply to it through the transition - mirroring how the DOM actually paints,\n     * and letting a wrapper crop its child for the whole morph rather than only\n     * once the live DOM takes back over. (Needs a browser that supports nested\n     * view-transition groups; elsewhere it degrades to the flat default.)\n     *\n     * Call `.group(false)` to opt out: the layer stays flat and top-level, so it\n     * animates independently and escapes an ancestor's clip - e.g. an element\n     * that should lift out of a card and fly across, which nesting would clip.\n     */\n    group(enabled = true) {\n        enabled\n            ? this.flatGroups.delete(this.currentSubject)\n            : this.flatGroups.add(this.currentSubject)\n\n        return this\n    }\n\n    /**\n     * Tag this subject's generated layers with a `view-transition-class`, so\n     * they can be targeted from CSS - `::view-transition-group(.name)`,\n     * `::view-transition-old/new(.name)`, `::view-transition-image-pair(.name)`\n     * - without the opaque generated `view-transition-name`. Because `.add()`\n     * can match many elements, a shared class targets them all at once (and,\n     * for a pair, both ends). The escape hatch for z-index / custom keyframes\n     * on a morph layer.\n     */\n    class(name: string) {\n        this.classNames.set(this.currentSubject, name)\n\n        return this\n    }\n\n    /**\n     * Set the transition for this subject's morph. The morph is enabled\n     * automatically by `.add()`; this just customises its timing (duration,\n     * easing, a `delay`/`stagger`, …). On the implicit `root` subject it also\n     * opts the page into the transition (the root crossfade).\n     */\n    layout(options: AnimationOptions = {}) {\n        this.updateTarget(\"layout\", {}, options)\n\n        return this\n    }\n\n    enter(keyframes: DOMKeyframesDefinition, options?: AnimationOptions) {\n        this.updateTarget(\"enter\", keyframes, options)\n\n        return this\n    }\n\n    exit(keyframes: DOMKeyframesDefinition, options?: AnimationOptions) {\n        this.updateTarget(\"exit\", keyframes, options)\n\n        return this\n    }\n\n    /**\n     * Animate the new view directly, whether the element is appearing or\n     * persisting (unlike `.enter()`, which only fires for a pure newcomer).\n     * Pair with `.old()` for a crossfade or slide-through.\n     */\n    new(keyframes: DOMKeyframesDefinition, options?: AnimationOptions) {\n        this.updateTarget(\"new\", keyframes, options)\n\n        return this\n    }\n\n    /**\n     * Animate the old view directly, whether the element is leaving or\n     * persisting (unlike `.exit()`, which only fires for a pure leaver).\n     */\n    old(keyframes: DOMKeyframesDefinition, options?: AnimationOptions) {\n        this.updateTarget(\"old\", keyframes, options)\n\n        return this\n    }\n\n    updateTarget(\n        target: \"enter\" | \"exit\" | \"layout\" | \"new\" | \"old\",\n        keyframes: DOMKeyframesDefinition,\n        options: AnimationOptions = {}\n    ) {\n        const { currentSubject, targets } = this\n\n        if (!targets.has(currentSubject)) {\n            targets.set(currentSubject, {})\n        }\n\n        const targetData = targets.get(currentSubject)!\n\n        targetData[target] = { keyframes, options }\n    }\n\n    then(resolve: () => void, reject?: () => void) {\n        return this.readyPromise.then(resolve, reject)\n    }\n}\n\nexport function animateView(\n    update: () => void | Promise<void>,\n    options: ViewTransitionOptions = {}\n) {\n    const builder = new ViewTransitionBuilder(update, options)\n    addBuilderToScope(builder)\n    return builder\n}\n"],"names":[],"mappings":"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"}