1
0
mirror of https://github.com/jaandrle/deka-dom-el synced 2025-07-02 04:32:14 +02:00
This commit is contained in:
2025-02-28 17:12:40 +01:00
parent 8f2fd5a68c
commit b53f3926b3
8 changed files with 375 additions and 104 deletions

View File

@ -4,19 +4,6 @@
*/
export const mark= "__dde_signal";
/**
* Error class for signal definition tracking
* Shows the correct stack trace for debugging signal creation
*/
export class SignalDefined extends Error{
constructor(){
super();
const [ curr, ...rest ]= this.stack.split("\n");
const curr_file= curr.slice(curr.indexOf("@"), curr.indexOf(".js:")+4);
this.stack= rest.find(l=> !l.includes(curr_file));
}
}
/**
* Batches signal updates to improve performance
* @type {Function}
@ -32,6 +19,7 @@ export const queueSignalWrite= (()=> {
function flushSignals() {
scheduled = false;
for(const signal of pendingSignals){
pendingSignals.delete(signal);
const M = signal[mark];
if(M) M.listeners.forEach(l => l(M.value));
}
@ -47,5 +35,5 @@ export const queueSignalWrite= (()=> {
if(scheduled) return;
scheduled = true;
queueMicrotask(flushSignals);
}
};
})();

View File

@ -1,6 +1,6 @@
import { SignalDefined, queueSignalWrite, mark } from "./helpers.js";
import { queueSignalWrite, mark } from "./helpers.js";
export { mark };
import { hasOwn } from "../helpers.js";
import { hasOwn, Defined } from "../helpers.js";
/**
* Checks if a value is a signal
@ -62,7 +62,7 @@ export function signal(value, actions){
if(deps_curr.has(dep_signal)) continue;
removeSignalListener(dep_signal, contextReWatch);
}
};
}
deps.set(out[mark], contextReWatch);
deps.set(contextReWatch, new Set([ out ]));
contextReWatch();
@ -159,51 +159,42 @@ import { on } from "../events.js";
const storeMemo= new WeakMap();
/**
* Memoizes a function result by key
* Memoizes a function result
*
* @param {string|any} key - Cache key (non-strings will be stringified)
* @param {string|unknown} key - Cache key (non-strings will be stringified)
* @param {Function} fun - Function to compute value
* @param {Object} [cache] - Optional explicit cache object
* @returns {any} Cached or computed result
* @param {keyof storeMemo} [host= fun]
* @returns {unknown} Cached or computed result
*/
export function memo(key, fun, cache){
export function memo(key, fun, host= fun){
if(typeof key!=="string") key= JSON.stringify(key);
if(!cache) {
const keyStore= scope.host();
if(storeMemo.has(keyStore))
cache= storeMemo.get(keyStore);
else {
cache= {};
storeMemo.set(keyStore, cache);
}
}
if (!storeMemo.has(host)) storeMemo.set(host, {});
const cache= storeMemo.get(host);
return hasOwn(cache, key) ? cache[key] : (cache[key]= fun());
}
// TODO: third argument for handle `cache_tmp` in re-render
/**
* Creates a reactive DOM element that re-renders when signal changes
*
* @TODO Third argument for handle `cache_tmp` in re-render
* @param {function} s - Signal to watch
* @param {Function} map - Function mapping signal value to DOM elements
* @returns {DocumentFragment} Fragment containing reactive elements
*/
signal.el= function(s, map){
const mark_start= el.mark({ type: "reactive" }, true);
const mark_start= el.mark({ type: "reactive", source: new Defined().compact }, true);
const mark_end= mark_start.end;
const out= env.D.createDocumentFragment();
out.append(mark_start, mark_end);
const { current }= scope;
let cache= {};
const reRenderReactiveElement= v=> {
if(!mark_start.parentNode || !mark_end.parentNode) // === `isConnected` or wasnt yet rendered
return removeSignalListener(s, reRenderReactiveElement);
let cache_tmp= cache; // will be reused in the useCache or removed in the while loop on the end
cache= {};
const cache= {}; // remove unused els from cache
scope.push(current);
let els= map(v, function useCache(key, fun){
return cache[key]= memo(key, fun, cache_tmp);
return (cache[key]= memo(key, fun, reRenderReactiveElement));
});
cache_tmp= {};
storeMemo.set(reRenderReactiveElement, cache);
scope.pop();
if(!Array.isArray(els))
els= [ els ];
@ -220,9 +211,6 @@ signal.el= function(s, map){
addSignalListener(s, reRenderReactiveElement);
removeSignalsFromElements(s, reRenderReactiveElement, mark_start, map);
reRenderReactiveElement(s());
current.host(on.disconnected(()=>
/*! This clears memoized elements in S.el when the host is disconnected */
cache= {}));
return out;
};
/**
@ -407,7 +395,7 @@ function toSignal(s, value, actions, readonly= false){
value: {
value, actions, onclear, host,
listeners: new Set(),
defined: (new SignalDefined()).stack,
defined: new Defined().stack,
readonly
},
enumerable: false,