svelte
Version:
Cybernetically enhanced web apps
128 lines (125 loc) • 3.82 kB
JavaScript
import { noop, safe_not_equal, run_all, is_function } from '../internal';
/**
* Creates a `Readable` store that allows reading by subscription.
* @param value initial value
* @param {StartStopNotifier}start start and stop notifications for subscriptions
*/
function readable(value, start) {
return {
subscribe: writable(value, start).subscribe,
};
}
/**
* Create a `Writable` store that allows both updating and reading by subscription.
* @param {*=}value initial value
* @param {StartStopNotifier=}start start and stop notifications for subscriptions
*/
function writable(value, start = noop) {
let stop;
const subscribers = [];
function set(new_value) {
if (safe_not_equal(value, new_value)) {
value = new_value;
if (!stop) {
return; // not ready
}
subscribers.forEach((s) => s[1]());
subscribers.forEach((s) => s[0](value));
}
}
function update(fn) {
set(fn(value));
}
function subscribe(run, invalidate = noop) {
const subscriber = [run, invalidate];
subscribers.push(subscriber);
if (subscribers.length === 1) {
stop = start(set) || noop;
}
run(value);
return () => {
const index = subscribers.indexOf(subscriber);
if (index !== -1) {
subscribers.splice(index, 1);
}
if (subscribers.length === 0) {
stop();
stop = null;
}
};
}
return { set, update, subscribe };
}
/**
* Derived value store by synchronizing one or more readable stores and
* applying an aggregation function over its input values.
* @param {Stores} stores input stores
* @param {function(Stores=, function(*)=):*}fn function callback that aggregates the values
* @param {*=}initial_value when used asynchronously
*/
function derived(stores, fn, initial_value) {
const single = !Array.isArray(stores);
const stores_array = single
? [stores]
: stores;
const auto = fn.length < 2;
const invalidators = [];
const store = readable(initial_value, (set) => {
let inited = false;
const values = [];
let pending = 0;
let cleanup = noop;
const sync = () => {
if (pending) {
return;
}
cleanup();
const result = fn(single ? values[0] : values, set);
if (auto) {
set(result);
}
else {
cleanup = is_function(result) ? result : noop;
}
};
const unsubscribers = stores_array.map((store, i) => store.subscribe((value) => {
values[i] = value;
pending &= ~(1 << i);
if (inited) {
sync();
}
}, () => {
run_all(invalidators);
pending |= (1 << i);
}));
inited = true;
sync();
return function stop() {
run_all(unsubscribers);
cleanup();
};
});
return {
subscribe(run, invalidate = noop) {
invalidators.push(invalidate);
const unsubscribe = store.subscribe(run, invalidate);
return () => {
const index = invalidators.indexOf(invalidate);
if (index !== -1) {
invalidators.splice(index, 1);
}
unsubscribe();
};
}
};
}
/**
* Get the current value from a store by subscribing and immediately unsubscribing.
* @param store readable
*/
function get(store) {
let value;
store.subscribe((_) => value = _)();
return value;
}
export { readable, writable, derived, get };