packages/core/src/lib/transitions/binding.ts
This is the source snapshot used to build these API details. View this revision on GitHub.
1 import type { PibblVisibilityTransition } from '../hooks/use-visibility-transition.js';
2 import { GLOBAL_STATE } from '../global-state.js';
3 import { requireHookContext } from '../hooks/hook-slot.js';
4 import type { PibblComponentRefs, PibblEventRoot, PibblInstance } from '../types.js';
5 import type { PibblFilter } from '../style/filter-types.js';
6
7 declare const bindingBrand: unique symbol;
8 /**
9 * An opaque connection between one transition controller and its painted subject.
10 *
11 * @see {@link PibblVisibilityTransition}
12 */
13 export interface PibblTransitionBinding { readonly [bindingBrand]: true }
14 interface BindingRecord {
15 readonly resolve: () => readonly PibblFilter[];
16 readonly root: PibblEventRoot;
17 readonly owner: PibblComponentRefs<any>;
18 receiver?: PibblComponentRefs<any>;
19 receiverInstance?: PibblInstance;
20 }
21 const bindings = new WeakMap<PibblTransitionBinding, BindingRecord>();
22 export function createTransitionBinding(resolve: () => readonly PibblFilter[]): PibblTransitionBinding {
23 const binding = Object.freeze({}) as PibblTransitionBinding;
24 const { componentRefs, pibblInstance } = requireHookContext();
25 bindings.set(binding, { resolve, owner: componentRefs, root: pibblInstance.eventRoot });
26 return binding;
27 }
28 export function resolveTransitionBinding(binding: PibblTransitionBinding): readonly PibblFilter[] {
29 const record = bindings.get(binding);
30 if (!record) throw new TypeError('style.transition requires a binding from useVisibilityTransition in the same Pibbl runtime.');
31 const receiver = GLOBAL_STATE.componentRefs;
32 if (GLOBAL_STATE.currentPibblInstance?.eventRoot !== record.root) throw new Error('A transition binding must stay within its owning Pibbl root.');
33 if (record.owner.teardowns.closed) throw new Error('Cannot use a disposed transition binding.');
34 if (!receiver) throw new Error('Transition bindings require a mounted receiver.');
35 const instance = GLOBAL_STATE.currentPibblInstance!;
36 // Keyed replacements are visited before the old receiver is torn down. Only
37 // retire an unseen receiver in this same render instance; cached Layers own
38 // independent generations and must not silently lose their binding.
39 if (record.receiver && record.receiver !== receiver && record.receiverInstance === instance && record.receiver.renderGeneration !== instance.renderGeneration) {
40 record.receiver = undefined;
41 }
42 if (record.receiver && record.receiver !== receiver) throw new Error('A transition binding supports one mounted receiver. Wrap shared content in a Group.');
43 if (!record.receiver) {
44 record.receiver = receiver;
45 record.receiverInstance = instance;
46 receiver.teardowns.add(() => { if (record.receiver === receiver) record.receiver = undefined; });
47 }
48 return record.resolve();
49 }
50
Documentation version
Section titled “Documentation version”Documentation built with @pibbl/core 0.0.2, revision 272a94a. ALPHA — NOT FOR PRODUCTION USE.