@dark-engine/core
Version:
The lightweight and powerful UI rendering engine without dependencies and written in TypeScript (Browser, Node.js, Android, iOS, Windows, Linux, macOS)
219 lines (218 loc) • 6.62 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.useStore =
exports.useComputed =
exports.useAtom =
exports.computed =
exports.atom =
exports.detectIsReadableAtom =
exports.detectIsWritableAtom =
exports.detectIsAtom =
exports.ReadableAtom =
exports.WritableAtom =
void 0;
const utils_1 = require('../utils');
const use_update_1 = require('../use-update');
const use_layout_effect_1 = require('../use-layout-effect');
const constants_1 = require('../constants');
const scope_1 = require('../scope');
const use_state_1 = require('../use-state');
const emitter_1 = require('../emitter');
const use_memo_1 = require('../use-memo');
const batch_1 = require('../batch');
class Atom {
value;
connections1;
connections2;
subjects;
emitter;
constructor(value) {
this.value = value;
}
val(fn, key) {
try {
this.__connect(fn, key);
} catch (err) {
if (process.env.NODE_ENV !== 'production') {
(0, utils_1.logError)((0, utils_1.formatErrorMsg)(`Illegal invocation atom.val() outside render process!`));
}
}
return this.value;
}
get() {
return this.value;
}
on(fn) {
!this.emitter && (this.emitter = new emitter_1.EventEmitter());
return this.emitter.on('data', fn);
}
kill() {
if (this.connections1) {
for (const [hook, [_, __, ___, key]] of this.connections1) {
this.off(hook, key);
}
}
if (this.connections2) {
for (const [key, [_, hook]] of this.connections2) {
this.off(hook, key);
}
}
this.connections1 = null;
this.connections2 = null;
this.emitter = null;
this.subjects = null;
}
toString() {
return String(this.value);
}
toJSON() {
return this.value;
}
valueOf() {
return this.value;
}
__connect(fn, key) {
const rootId = (0, scope_1.getRootId)();
const cursor = (0, scope_1.$$scope)().getCursor();
const { hook } = cursor;
const disconnect = () => this.off(hook, key);
hook.setAtom(this, disconnect);
cursor.markHost(constants_1.ATOM_HOST_MASK);
if ((0, utils_1.detectIsEmpty)(key)) {
!this.connections1 && (this.connections1 = new Map());
this.connections1.set(hook, [rootId, hook, fn, key]);
} else {
!this.connections2 && (this.connections2 = new Map());
this.connections2.set(key, [rootId, hook, fn, key]);
}
return disconnect;
}
__addSubject(atom$) {
!this.subjects && (this.subjects = new Set());
this.subjects.add(atom$);
}
__removeSubject(atom$) {
return this.subjects && this.subjects.delete(atom$);
}
__getSize() {
const size1 = this.connections1 ? this.connections1.size : 0;
const size2 = this.connections2 ? this.connections2.size : 0;
const size3 = this.subjects ? this.subjects.size : 0;
const size4 = this.emitter ? this.emitter.__getSize() : 0;
return size1 + size2 + size3 + size4;
}
setValue(value) {
const prev = this.value;
const next = (0, utils_1.detectIsFunction)(value) ? value(this.value) : value;
const data = { prev, next };
const make = (tuple, prev, next) => {
const [rootId, hook, shouldUpdate, key] = tuple;
const fn = shouldUpdate || utils_1.trueFn;
if (fn(prev, next, key)) {
const update = (0, use_update_1.createUpdate)(rootId, hook);
if (this.__getSize() === 1) {
const tools = (0, use_state_1.createTools)({
next,
get: () => prev,
set: () => (this.value = next),
reset: () => (this.value = prev),
});
update(tools);
} else {
update();
}
}
};
this.value = next;
if (this.connections1) {
for (const [_, tuple] of this.connections1) {
make(tuple, prev, next);
}
}
if (this.connections2) {
if (this.connections2.has(next)) {
make(this.connections2.get(next), prev, next);
this.connections2.has(prev) && make(this.connections2.get(prev), prev, next);
}
}
this.emitter && this.emitter.emit('data', data);
this.subjects && this.subjects.forEach(x => x.__notify());
}
off(hook, key) {
hook.removeAtom(this);
this.connections1 && this.connections1.delete(hook);
this.connections2 && this.connections2.delete(key);
}
}
class WritableAtom extends Atom {
set(value) {
super.setValue(value);
}
}
exports.WritableAtom = WritableAtom;
class ReadableAtom extends Atom {
deps$ = [];
fn = null;
values = [];
constructor(deps$, fn) {
const values = ReadableAtom.values(deps$);
super(ReadableAtom.compute(fn, values));
this.deps$ = deps$;
this.fn = fn;
this.values = values;
deps$.forEach(x => x.__addSubject(this));
}
__notify() {
const values = ReadableAtom.values(this.deps$);
if ((0, utils_1.detectAreDepsDifferent)(this.values, values)) {
super.setValue(ReadableAtom.compute(this.fn, values));
}
this.values = values;
}
kill() {
super.kill();
this.deps$.forEach(x => x.__removeSubject(this));
this.deps$ = [];
this.fn = null;
}
static compute(fn, values) {
return fn(...values);
}
static values(deps$) {
return deps$.map(x => x.get());
}
}
exports.ReadableAtom = ReadableAtom;
const detectIsAtom = x => x instanceof Atom;
exports.detectIsAtom = detectIsAtom;
const detectIsWritableAtom = x => x instanceof WritableAtom;
exports.detectIsWritableAtom = detectIsWritableAtom;
const detectIsReadableAtom = x => x instanceof ReadableAtom;
exports.detectIsReadableAtom = detectIsReadableAtom;
const atom = value => new WritableAtom(value);
exports.atom = atom;
const computed = (deps$, fn) => new ReadableAtom(deps$, fn);
exports.computed = computed;
function useAtom(value) {
const atom$ = (0, use_memo_1.useMemo)(() => atom(value), []);
(0, use_layout_effect_1.useLayoutEffect)(() => () => atom$.kill(), []);
return atom$;
}
exports.useAtom = useAtom;
function useComputed(deps$, fn) {
const atom$ = (0, use_memo_1.useMemo)(() => computed(deps$, fn), []);
(0, use_layout_effect_1.useLayoutEffect)(() => () => atom$.kill(), []);
return atom$;
}
exports.useComputed = useComputed;
function useStore(atoms$) {
const forceUpdate = (0, use_update_1.useUpdate)();
const update = () => (0, batch_1.batch)(forceUpdate);
(0, use_layout_effect_1.useLayoutEffect)(() => {
const offs = atoms$.map(x => x.on(update));
return () => offs.forEach(x => x());
}, [...atoms$]);
return atoms$.map(x => x.get());
}
exports.useStore = useStore;
//# sourceMappingURL=atom.js.map