Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | 1x 1x 1x 1x 7x 7x 7x 7x 7x 7x 7x 1x 1x 1x 1x 7x 1x 1x 1x 1x 5x 5x 5x 5x 5x 5x 5x 14x 11x 9x 9x 9x 14x 5x 5x 5x 5x 5x 14x 3x 3x 3x 14x 5x 5x 5x 5x 5x 5x 5x | import { anchor } from './anchor.js';
import { captureStack } from './exception.js';
import { subscribe } from './subscription.js';
import type { ObjLike, StateBinding, StateUnsubscribe } from './types.js';
/**
* Creates a two-way binding between a source and target reactive object properties.
*
* This function establishes a bidirectional data flow where changes to either the source
* or target property will automatically update the other. Both objects must be reactive
* (created with the anchor system) for the binding to work properly.
*
* Note: This binding is only for a single property and is non-recursive. For object
* properties, only the reference is bound, not the object's internal properties.
*
* @template T - The type of the source value
* @template B - The type of the source binding key (defaults to 'value')
* @template S extends ObjLike - The type of the target object
*
* @param source - The source binding which can be either:
* - A reactive object (defaulting to bind to its 'value' property)
* - A tuple of [reactiveObject, propertyName] to specify a custom property
* @param target - The target reactive object
* @param targetKey - The property name in the target object to bind to
*
* @returns A function that can be called to unsubscribe and remove the binding
* @throws Will throw an error if either source or target objects are not reactive
*/
export function binding<T, B, S extends ObjLike = ObjLike>(
source: StateBinding<T, B>,
target: S,
targetKey: keyof S
): StateUnsubscribe {
const _source = (Array.isArray(source) ? source[0] : source) as ObjLike;
const sourceKey = (Array.isArray(source) ? source[1] : 'value') as string;
if (!anchor.has(_source)) {
const error = new Error('State is not reactive.');
captureStack.violation.derivation('Attempted to bind state from a non-reactive state.', error);
return () => {};
}
if (!anchor.has(target)) {
const error = new Error('State is not reactive.');
captureStack.violation.derivation('Attempted to bind state to a non-reactive state.', error);
return () => {};
}
const rawSource = anchor.get(_source);
const rawTarget = anchor.get(target);
let updatingSource = false;
let updatingTarget = false;
const leaveSource = subscribe(
_source,
() => {
if (updatingSource) return;
if (rawTarget[targetKey] === rawSource[sourceKey]) return;
updatingTarget = true;
target[targetKey] = rawSource[sourceKey] as never;
updatingTarget = false;
},
false
);
const leaveTarget = subscribe(
target,
(_, event) => {
if (updatingTarget || event.type === 'init') return;
updatingSource = true;
_source[sourceKey] = rawTarget[targetKey] as never;
updatingSource = false;
},
false
);
return () => {
leaveSource();
leaveTarget();
};
}
|