ripple
Version:
Ripple is an elegant TypeScript UI framework
75 lines (68 loc) • 1.9 kB
JavaScript
/** @import { Block, Tracked } from '#client' */
import { render } from './blocks.js';
import { UNINITIALIZED } from './constants.js';
import { get_tracked, selector_tracked, set } from './runtime.js';
/**
* @typedef {{ v: any; m: Map<any, Tracked>; b: Block; a: import('./runtime.js').SelectorAccessors }} Selector
*/
/**
* Shares one reactive source across many `source === key` comparisons: each
* key subscribes to its own match flag, so a change of the source only
* notifies the blocks for the previous and next key rather than every block
* that compares against it. Used by the compiler for comparisons between a
* loop item and outer state inside `@for` templates.
* @param {() => any} get_source
* @returns {Selector}
*/
export function selector(get_source) {
var m = new Map();
/** @type {Selector} */
var s = {
v: UNINITIALIZED,
m,
b: /** @type {Block} */ (/** @type {unknown} */ (null)),
a: { get: undefined, set: undefined, m },
};
s.b = render(() => {
var next = get_source();
var prev = s.v;
if (next === prev) {
return;
}
s.v = next;
var m = s.m;
if (m.size === 0) {
return;
}
// NaN never matches with ===, so it never toggles a flag.
if (prev === prev) {
var previous_match = m.get(prev);
if (previous_match !== undefined) {
set(previous_match, false);
}
}
if (next === next) {
var next_match = m.get(next);
if (next_match !== undefined) {
set(next_match, true);
}
}
});
return s;
}
/**
* `source === key`, read inside a render block so the block re-runs only when
* the match for this key changes.
* @param {Selector} s
* @param {any} key
* @returns {boolean}
*/
export function selector_match(s, key) {
var m = s.m;
var match = m.get(key);
if (match === undefined) {
match = selector_tracked(key === s.v, s.b, s.a, key);
m.set(key, match);
}
return get_tracked(match);
}