UNPKG

@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
'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