mirror of
https://github.com/jaandrle/deka-dom-el
synced 2025-07-01 12:22:15 +02:00
⚡ Refatc signals to .get/.set syntax #26
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
import { signals } from "./signals-lib/common.js";
|
||||
import { enviroment as env } from './dom-common.js';
|
||||
import { isInstance, isUndef, oAssign } from "./helpers.js";
|
||||
|
||||
/**
|
||||
* Queues a promise, this is helpful for crossplatform components (on server side we can wait for all registered
|
||||
@ -55,7 +56,7 @@ export const scope= {
|
||||
* @param {Object} [s={}] - Scope object to push
|
||||
* @returns {number} New length of the scope stack
|
||||
*/
|
||||
push(s= {}){ return scopes.push(Object.assign({}, this.current, { prevent: false }, s)); },
|
||||
push(s= {}){ return scopes.push(oAssign({}, this.current, { prevent: false }, s)); },
|
||||
|
||||
/**
|
||||
* Pushes the root scope to the stack
|
||||
@ -90,7 +91,6 @@ export function chainableAppend(el){
|
||||
/** Current namespace for element creation */
|
||||
let namespace;
|
||||
|
||||
import { isInstance, isUndef } from "./helpers.js";
|
||||
/**
|
||||
* Creates a DOM element with specified tag, attributes and addons
|
||||
*
|
||||
@ -230,7 +230,7 @@ export function assign(element, ...attributes){
|
||||
if(!attributes.length) return element;
|
||||
assign_context.set(element, assignContext(element, this));
|
||||
|
||||
for(const [ key, value ] of Object.entries(Object.assign({}, ...attributes)))
|
||||
for(const [ key, value ] of Object.entries(oAssign({}, ...attributes)))
|
||||
assignAttribute.call(this, element, key, value);
|
||||
assign_context.delete(element);
|
||||
return element;
|
||||
|
@ -1,5 +1,6 @@
|
||||
export { registerReactivity } from './signals-lib/common.js';
|
||||
import { enviroment as env, keyLTE, evc, evd, eva } from './dom-common.js';
|
||||
import { oAssign, onAbort } from './helpers.js';
|
||||
|
||||
/**
|
||||
* Creates a function to dispatch events on elements
|
||||
@ -17,7 +18,7 @@ export function dispatchEvent(name, options, host){
|
||||
element= typeof host==="function"? host() : host;
|
||||
}
|
||||
//TODO: what about re-emmitting?
|
||||
const event= d.length ? new CustomEvent(name, Object.assign({ detail: d[0] }, options)) : new Event(name, options);
|
||||
const event= d.length ? new CustomEvent(name, oAssign({ detail: d[0] }, options)) : new Event(name, options);
|
||||
return element.dispatchEvent(event);
|
||||
};
|
||||
}
|
||||
@ -38,13 +39,12 @@ export function on(event, listener, options){
|
||||
}
|
||||
|
||||
import { c_ch_o } from "./events-observer.js";
|
||||
import { onAbort } from './helpers.js';
|
||||
|
||||
/**
|
||||
* Prepares lifecycle event options with once:true default
|
||||
* @private
|
||||
*/
|
||||
const lifeOptions= obj=> Object.assign({}, typeof obj==="object" ? obj : null, { once: true });
|
||||
const lifeOptions= obj=> oAssign({}, typeof obj==="object" ? obj : null, { once: true });
|
||||
|
||||
//TODO: cleanUp when event before abort?
|
||||
//TODO: docs (e.g.) https://nolanlawson.com/2024/01/13/web-component-gotcha-constructor-vs-connectedcallback/
|
||||
|
@ -27,7 +27,8 @@ export function typeOf(v){
|
||||
export function isInstance(obj, cls){ return obj instanceof cls; }
|
||||
/** @type {typeof Object.prototype.isPrototypeOf.call} */
|
||||
export function isProtoFrom(obj, cls){ return Object.prototype.isPrototypeOf.call(cls, obj); }
|
||||
export function oCreate(proto= null){ return Object.create(proto); }
|
||||
export function oCreate(proto= null, p= {}){ return Object.create(proto, p); }
|
||||
export function oAssign(...o){ return Object.assign(...o); }
|
||||
|
||||
/**
|
||||
* Handles AbortSignal registration and cleanup
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { isProtoFrom } from "../helpers.js";
|
||||
import { isProtoFrom, oAssign } from "../helpers.js";
|
||||
/**
|
||||
* Global signals object with default implementation
|
||||
* @type {Object}
|
||||
@ -29,7 +29,7 @@ export const signals_global= {
|
||||
* @returns {Object} The registered reactivity implementation
|
||||
*/
|
||||
export function registerReactivity(def, global= true){
|
||||
if(global) return Object.assign(signals_global, def);
|
||||
if(global) return oAssign(signals_global, def);
|
||||
Object.setPrototypeOf(def, signals_global);
|
||||
return def;
|
||||
}
|
||||
|
@ -18,12 +18,12 @@ export const queueSignalWrite= (()=> {
|
||||
*/
|
||||
function flushSignals() {
|
||||
scheduled = false;
|
||||
for(const signal of pendingSignals){
|
||||
pendingSignals.delete(signal);
|
||||
const todo= pendingSignals;
|
||||
pendingSignals= new Set();
|
||||
for(const signal of todo){
|
||||
const M = signal[mark];
|
||||
if(M) M.listeners.forEach(l => l(M.value));
|
||||
}
|
||||
pendingSignals.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,16 @@
|
||||
import { queueSignalWrite, mark } from "./helpers.js";
|
||||
export { mark };
|
||||
import { hasOwn, Defined, oCreate, isProtoFrom } from "../helpers.js";
|
||||
import { hasOwn, Defined, oCreate, isProtoFrom, oAssign } from "../helpers.js";
|
||||
|
||||
const Signal = oCreate(null, {
|
||||
get: { value(){ return read(this); } },
|
||||
set: { value(...v){ return write(this, ...v); } },
|
||||
toJSON: { value(){ return read(this); } },
|
||||
valueOf: { value(){ return this[mark] && this[mark].value; } }
|
||||
});
|
||||
const SignalReadOnly= oCreate(Signal, {
|
||||
set: { value(){ return; } },
|
||||
});
|
||||
/**
|
||||
* Checks if a value is a signal
|
||||
*
|
||||
@ -9,7 +18,7 @@ import { hasOwn, Defined, oCreate, isProtoFrom } from "../helpers.js";
|
||||
* @returns {boolean} True if the value is a signal
|
||||
*/
|
||||
export function isSignal(candidate){
|
||||
return typeof candidate === "function" && hasOwn(candidate, mark);
|
||||
return isProtoFrom(candidate, Signal);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,7 +44,7 @@ const deps= new WeakMap();
|
||||
*
|
||||
* @param {any|function} value - Initial value or function that computes the value
|
||||
* @param {Object} [actions] - Custom actions for the signal
|
||||
* @returns {function} Signal function
|
||||
* @returns {Object} Signal object with get() and set() methods
|
||||
*/
|
||||
export function signal(value, actions){
|
||||
if(typeof value!=="function")
|
||||
@ -53,7 +62,7 @@ export function signal(value, actions){
|
||||
deps.set(contextReWatch, new Set([ origin ]));
|
||||
|
||||
stack_watch.push(contextReWatch);
|
||||
write(out, value());
|
||||
write(out, value.get());
|
||||
stack_watch.pop();
|
||||
|
||||
if(!deps_old.length) return;
|
||||
@ -74,7 +83,7 @@ export { signal as S };
|
||||
/**
|
||||
* Calls a custom action on a signal
|
||||
*
|
||||
* @param {function} s - Signal to call action on
|
||||
* @param {Object} s - Signal object to call action on
|
||||
* @param {string} name - Action name
|
||||
* @param {...any} a - Arguments to pass to the action
|
||||
*/
|
||||
@ -92,7 +101,7 @@ signal.action= function(s, name, ...a){
|
||||
/**
|
||||
* Subscribes a listener to signal changes
|
||||
*
|
||||
* @param {function|function[]} s - Signal or array of signals to subscribe to
|
||||
* @param {Object|Object[]} s - Signal object or array of signal objects to subscribe to
|
||||
* @param {function} listener - Callback function receiving signal value
|
||||
* @param {Object} [options={}] - Subscription options
|
||||
* @param {AbortSignal} [options.signal] - Signal to abort subscription
|
||||
@ -116,7 +125,7 @@ signal.symbols= {
|
||||
/**
|
||||
* Cleans up signals and their dependencies
|
||||
*
|
||||
* @param {...function} signals - Signals to clean up
|
||||
* @param {...Object} signals - Signal objects to clean up
|
||||
*/
|
||||
signal.clear= function(...signals){
|
||||
for(const s of signals){
|
||||
@ -162,7 +171,7 @@ export function cache(store= oCreate()){
|
||||
* 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 {Object} s - Signal object to watch
|
||||
* @param {Function} map - Function mapping signal value to DOM elements
|
||||
* @returns {DocumentFragment} Fragment containing reactive elements
|
||||
*/
|
||||
@ -197,7 +206,7 @@ signal.el= function(s, map){
|
||||
};
|
||||
addSignalListener(s, reRenderReactiveElement);
|
||||
removeSignalsFromElements(s, reRenderReactiveElement, mark_start, map);
|
||||
reRenderReactiveElement(s());
|
||||
reRenderReactiveElement(s.get());
|
||||
current.host(on.disconnected(()=>
|
||||
/*! Clears cached elements for reactive element `S.el` */
|
||||
cache_shared= {}
|
||||
@ -236,9 +245,9 @@ const observedAttributeActions= {
|
||||
*/
|
||||
function observedAttribute(store){
|
||||
return function(instance, name){
|
||||
const varS= (...args)=> !args.length
|
||||
? read(varS)
|
||||
: instance.setAttribute(name, ...args);
|
||||
const varS= oCreate(Signal, {
|
||||
set: { value(...v){ return instance.setAttribute(name, ...v); } }
|
||||
});
|
||||
const out= toSignal(varS, instance.getAttribute(name), observedAttributeActions);
|
||||
store[name]= out;
|
||||
return out;
|
||||
@ -298,13 +307,13 @@ export const signals_config= {
|
||||
};
|
||||
addSignalListener(attrs, l);
|
||||
removeSignalsFromElements(attrs, l, element, key);
|
||||
return attrs();
|
||||
return attrs.get();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Registers signal listener for cleanup when element is removed
|
||||
*
|
||||
* @param {function} s - Signal to track
|
||||
* @param {Object} s - Signal object to track
|
||||
* @param {Function} listener - Signal listener
|
||||
* @param {...any} notes - Additional context information
|
||||
* @private
|
||||
@ -333,18 +342,16 @@ const cleanUpRegistry = new FinalizationRegistry(function(s){
|
||||
signal.clear({ [mark]: s });
|
||||
});
|
||||
/**
|
||||
* Creates a new signal function
|
||||
* Creates a new signal object
|
||||
*
|
||||
* @param {boolean} is_readonly - Whether the signal is readonly
|
||||
* @param {any} value - Initial signal value
|
||||
* @param {Object} actions - Custom actions for the signal
|
||||
* @returns {function} Signal function
|
||||
* @returns {Object} Signal object with get() and set() methods
|
||||
* @private
|
||||
*/
|
||||
function create(is_readonly, value, actions){
|
||||
const varS= is_readonly
|
||||
? ()=> read(varS)
|
||||
: (...value)=> value.length ? write(varS, ...value) : read(varS);
|
||||
const varS = oCreate(is_readonly ? SignalReadOnly : Signal);
|
||||
const SI= toSignal(varS, value, actions, is_readonly);
|
||||
cleanUpRegistry.register(SI, SI[mark]);
|
||||
return SI;
|
||||
@ -354,7 +361,7 @@ function create(is_readonly, value, actions){
|
||||
* Prototype for signal internal objects
|
||||
* @private
|
||||
*/
|
||||
const protoSigal= Object.assign(oCreate(), {
|
||||
const protoSigal= oAssign(oCreate(), {
|
||||
/**
|
||||
* Prevents signal propagation
|
||||
*/
|
||||
@ -363,13 +370,13 @@ const protoSigal= Object.assign(oCreate(), {
|
||||
}
|
||||
});
|
||||
/**
|
||||
* Transforms a function into a signal
|
||||
* Transforms an object into a signal
|
||||
*
|
||||
* @param {function} s - Function to transform
|
||||
* @param {Object} s - Object to transform
|
||||
* @param {any} value - Initial value
|
||||
* @param {Object} actions - Custom actions
|
||||
* @param {boolean} [readonly=false] - Whether the signal is readonly
|
||||
* @returns {function} Signal function
|
||||
* @returns {Object} Signal object with get() and set() methods
|
||||
* @private
|
||||
*/
|
||||
function toSignal(s, value, actions, readonly= false){
|
||||
@ -383,19 +390,16 @@ function toSignal(s, value, actions, readonly= false){
|
||||
}
|
||||
const { host }= scope;
|
||||
Reflect.defineProperty(s, mark, {
|
||||
value: {
|
||||
value: oAssign(oCreate(protoSigal), {
|
||||
value, actions, onclear, host,
|
||||
listeners: new Set(),
|
||||
defined: new Defined().stack,
|
||||
readonly
|
||||
},
|
||||
}),
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
configurable: true
|
||||
});
|
||||
s.toJSON= ()=> s();
|
||||
s.valueOf= ()=> s[mark] && s[mark].value;
|
||||
Object.setPrototypeOf(s[mark], protoSigal);
|
||||
return s;
|
||||
}
|
||||
/**
|
||||
@ -410,7 +414,7 @@ function currentContext(){
|
||||
/**
|
||||
* Reads a signal's value and tracks dependencies
|
||||
*
|
||||
* @param {function} s - Signal to read
|
||||
* @param {Object} s - Signal object to read
|
||||
* @returns {any} Signal value
|
||||
* @private
|
||||
*/
|
||||
@ -426,7 +430,7 @@ function read(s){
|
||||
/**
|
||||
* Writes a new value to a signal
|
||||
*
|
||||
* @param {function} s - Signal to update
|
||||
* @param {Object} s - Signal object to update
|
||||
* @param {any} value - New value
|
||||
* @param {boolean} [force=false] - Force update even if value is unchanged
|
||||
* @returns {any} The new value
|
||||
@ -444,7 +448,7 @@ function write(s, value, force){
|
||||
/**
|
||||
* Adds a listener to a signal
|
||||
*
|
||||
* @param {function} s - Signal to listen to
|
||||
* @param {Object} s - Signal object to listen to
|
||||
* @param {Function} listener - Callback function
|
||||
* @returns {Set} Listener set
|
||||
* @private
|
||||
@ -457,7 +461,7 @@ function addSignalListener(s, listener){
|
||||
/**
|
||||
* Removes a listener from a signal
|
||||
*
|
||||
* @param {function} s - Signal to modify
|
||||
* @param {Object} s - Signal object to modify
|
||||
* @param {Function} listener - Listener to remove
|
||||
* @param {boolean} [clear_when_empty] - Whether to clear the signal when no listeners remain
|
||||
* @returns {boolean} Whether the listener was found and removed
|
||||
|
Reference in New Issue
Block a user