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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | 353x 353x 353x 353x 353x 353x 6820x 353x 353x 87x 86x 86x 86x 53x 53x 87x 53x 353x 353x 353x 466x 463x 2x 2x 461x 461x 461x 461x 461x 463x 463x 463x 462x 364x 462x 462x 462x 462x 461x 461x 409x 409x 409x 462x 460x 1x 1x 1x 461x 461x 353x 353x 352x 352x 352x 352x | import type { ChangeDetails, ControllableValueOptions, Unsubscribe } from '../core/types';
/** Shared controlled/uncontrolled state cell with atomic, re-entrant-safe updates. @internal */
export interface ControllableValue<TValue, TReason extends string> {
/** Reads the consumer-owned value in controlled mode or the internal value otherwise. */
get(): TValue;
/** Requests a value change and returns whether the observable value changed. */
set(value: TValue, details: ChangeDetails<TReason>): boolean;
/** Releases an optional external subscription. */
destroy(): void;
/** Reports whether the value is owned by the consumer. */
readonly controlled: boolean;
}
/**
* Creates the single state ownership primitive used by every component controller.
*
* @param options - Value reader, default, callback, and optional external subscription.
* @param onExternalChange - Invalidates the owning controller when external state changes.
* @returns An idempotently disposable value cell.
* @internal
*/
export function createControllableValue<TValue, TReason extends string>(
options: ControllableValueOptions<TValue, TReason>,
onExternalChange: (value: TValue, details?: ChangeDetails<TReason>) => void,
): ControllableValue<TValue, TReason> {
let internal = options.defaultValue;
let destroyed = false;
let notifying = false;
let requesting = false;
let queued: { value: TValue; details: ChangeDetails<TReason> } | undefined;
let pendingRequest: { value: TValue; details: ChangeDetails<TReason> } | undefined;
const externalReader = options.getValue;
const controlled = externalReader !== undefined;
const get = (): TValue => (externalReader ? externalReader() : internal);
let lastObserved = get();
const notifyExternal = (): void => {
if (destroyed) return;
const value = get();
if (pendingRequest && !Object.is(pendingRequest.value, value)) pendingRequest = undefined;
if (Object.is(lastObserved, value)) return;
lastObserved = value;
const details =
pendingRequest && Object.is(pendingRequest.value, value) ? pendingRequest.details : undefined;
if (details) pendingRequest = undefined;
if (!requesting) onExternalChange(value, details);
};
let externalUnsubscribe: Unsubscribe = () => undefined;
externalUnsubscribe = options.subscribeValue?.(notifyExternal) ?? externalUnsubscribe;
const commit = (value: TValue, details: ChangeDetails<TReason>): boolean => {
if (destroyed || Object.is(get(), value)) return false;
if (notifying) {
queued = { value, details };
return true;
}
notifying = true;
try {
let next: { value: TValue; details: ChangeDetails<TReason> } | undefined = {
value,
details,
};
let changed = false;
while (next) {
queued = undefined;
const before = get();
if (!Object.is(before, next.value)) {
if (controlled) pendingRequest = next;
else internal = next.value;
requesting = true;
try {
options.onValueChange?.(next.value, next.details);
} finally {
requesting = false;
}
const observed = get();
if (!Object.is(before, observed)) {
lastObserved = observed;
if (controlled) pendingRequest = undefined;
changed = true;
}
}
next = queued;
}
return changed;
} catch (error) {
queued = undefined;
pendingRequest = undefined;
throw error;
} finally {
notifying = false;
requesting = false;
}
};
return {
get,
set: commit,
controlled,
destroy() {
if (destroyed) return;
destroyed = true;
queued = undefined;
pendingRequest = undefined;
externalUnsubscribe();
},
};
}
|