mirror of
https://github.com/jaandrle/deka-dom-el
synced 2025-04-04 12:45:54 +02:00
⚡ wip
This commit is contained in:
parent
8f2fd5a68c
commit
b53f3926b3
131
dist/dde-with-signals.js
vendored
131
dist/dde-with-signals.js
vendored
@ -2,9 +2,22 @@
|
|||||||
(()=> {
|
(()=> {
|
||||||
// src/signals-lib/common.js
|
// src/signals-lib/common.js
|
||||||
var signals_global = {
|
var signals_global = {
|
||||||
|
/**
|
||||||
|
* Checks if a value is a signal
|
||||||
|
* @param {any} attributes - Value to check
|
||||||
|
* @returns {boolean} Whether the value is a signal
|
||||||
|
*/
|
||||||
isSignal(attributes) {
|
isSignal(attributes) {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Processes an attribute that might be reactive
|
||||||
|
* @param {Element} obj - Element that owns the attribute
|
||||||
|
* @param {string} key - Attribute name
|
||||||
|
* @param {any} attr - Attribute value
|
||||||
|
* @param {Function} set - Function to set the attribute
|
||||||
|
* @returns {any} Processed attribute value
|
||||||
|
*/
|
||||||
processReactiveAttribute(obj, key, attr, set) {
|
processReactiveAttribute(obj, key, attr, set) {
|
||||||
return attr;
|
return attr;
|
||||||
}
|
}
|
||||||
@ -49,6 +62,19 @@ function observedAttributes(instance, observedAttribute2) {
|
|||||||
function kebabToCamel(name) {
|
function kebabToCamel(name) {
|
||||||
return name.replace(/-./g, (x) => x[1].toUpperCase());
|
return name.replace(/-./g, (x) => x[1].toUpperCase());
|
||||||
}
|
}
|
||||||
|
var Defined = class extends Error {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
const [curr, ...rest] = this.stack.split("\n");
|
||||||
|
const curr_file = curr.slice(curr.indexOf("@"), curr.indexOf(".js:") + 4);
|
||||||
|
const curr_lib = curr_file.includes("src/dom-common.js") ? "src/" : curr_file;
|
||||||
|
this.stack = rest.find((l) => !l.includes(curr_lib)) || curr;
|
||||||
|
}
|
||||||
|
get compact() {
|
||||||
|
const { stack } = this;
|
||||||
|
return stack.slice(0, stack.indexOf("@") + 1) + "\u2026" + stack.slice(stack.lastIndexOf("/"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// src/dom-common.js
|
// src/dom-common.js
|
||||||
var enviroment = {
|
var enviroment = {
|
||||||
@ -87,26 +113,55 @@ var scopes = [{
|
|||||||
prevent: true
|
prevent: true
|
||||||
}];
|
}];
|
||||||
var scope = {
|
var scope = {
|
||||||
|
/**
|
||||||
|
* Gets the current scope
|
||||||
|
* @returns {Object} Current scope context
|
||||||
|
*/
|
||||||
get current() {
|
get current() {
|
||||||
return scopes[scopes.length - 1];
|
return scopes[scopes.length - 1];
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Gets the host element of the current scope
|
||||||
|
* @returns {Function} Host accessor function
|
||||||
|
*/
|
||||||
get host() {
|
get host() {
|
||||||
return this.current.host;
|
return this.current.host;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Prevents default behavior in the current scope
|
||||||
|
* @returns {Object} Current scope context
|
||||||
|
*/
|
||||||
preventDefault() {
|
preventDefault() {
|
||||||
const { current } = this;
|
const { current } = this;
|
||||||
current.prevent = true;
|
current.prevent = true;
|
||||||
return current;
|
return current;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Gets a copy of the current scope stack
|
||||||
|
* @returns {Array} Copy of scope stack
|
||||||
|
*/
|
||||||
get state() {
|
get state() {
|
||||||
return [...scopes];
|
return [...scopes];
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Pushes a new scope to the stack
|
||||||
|
* @param {Object} [s={}] - Scope object to push
|
||||||
|
* @returns {number} New length of the scope stack
|
||||||
|
*/
|
||||||
push(s = {}) {
|
push(s = {}) {
|
||||||
return scopes.push(Object.assign({}, this.current, { prevent: false }, s));
|
return scopes.push(Object.assign({}, this.current, { prevent: false }, s));
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Pushes the root scope to the stack
|
||||||
|
* @returns {number} New length of the scope stack
|
||||||
|
*/
|
||||||
pushRoot() {
|
pushRoot() {
|
||||||
return scopes.push(scopes[0]);
|
return scopes.push(scopes[0]);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Pops the current scope from the stack
|
||||||
|
* @returns {Object|undefined} Popped scope or undefined if only one scope remains
|
||||||
|
*/
|
||||||
pop() {
|
pop() {
|
||||||
if (scopes.length === 1) return;
|
if (scopes.length === 1) return;
|
||||||
return scopes.pop();
|
return scopes.pop();
|
||||||
@ -338,12 +393,22 @@ function connectionsChangesObserverConstructor() {
|
|||||||
};
|
};
|
||||||
const observer = new enviroment.M(observerListener(stop));
|
const observer = new enviroment.M(observerListener(stop));
|
||||||
return {
|
return {
|
||||||
|
/**
|
||||||
|
* Creates an observer for a specific element
|
||||||
|
* @param {Element} element - Element to observe
|
||||||
|
* @returns {Function} Cleanup function
|
||||||
|
*/
|
||||||
observe(element) {
|
observe(element) {
|
||||||
const o = new enviroment.M(observerListener(() => {
|
const o = new enviroment.M(observerListener(() => {
|
||||||
}));
|
}));
|
||||||
o.observe(element, { childList: true, subtree: true });
|
o.observe(element, { childList: true, subtree: true });
|
||||||
return () => o.disconnect();
|
return () => o.disconnect();
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Register a connection listener for an element
|
||||||
|
* @param {Element} element - Element to watch
|
||||||
|
* @param {Function} listener - Callback for connection event
|
||||||
|
*/
|
||||||
onConnected(element, listener) {
|
onConnected(element, listener) {
|
||||||
start();
|
start();
|
||||||
const listeners = getElementStore(element);
|
const listeners = getElementStore(element);
|
||||||
@ -351,6 +416,11 @@ function connectionsChangesObserverConstructor() {
|
|||||||
listeners.connected.add(listener);
|
listeners.connected.add(listener);
|
||||||
listeners.length_c += 1;
|
listeners.length_c += 1;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Unregister a connection listener
|
||||||
|
* @param {Element} element - Element being watched
|
||||||
|
* @param {Function} listener - Callback to remove
|
||||||
|
*/
|
||||||
offConnected(element, listener) {
|
offConnected(element, listener) {
|
||||||
if (!store.has(element)) return;
|
if (!store.has(element)) return;
|
||||||
const ls = store.get(element);
|
const ls = store.get(element);
|
||||||
@ -359,6 +429,11 @@ function connectionsChangesObserverConstructor() {
|
|||||||
ls.length_c -= 1;
|
ls.length_c -= 1;
|
||||||
cleanWhenOff(element, ls);
|
cleanWhenOff(element, ls);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Register a disconnection listener for an element
|
||||||
|
* @param {Element} element - Element to watch
|
||||||
|
* @param {Function} listener - Callback for disconnection event
|
||||||
|
*/
|
||||||
onDisconnected(element, listener) {
|
onDisconnected(element, listener) {
|
||||||
start();
|
start();
|
||||||
const listeners = getElementStore(element);
|
const listeners = getElementStore(element);
|
||||||
@ -366,6 +441,11 @@ function connectionsChangesObserverConstructor() {
|
|||||||
listeners.disconnected.add(listener);
|
listeners.disconnected.add(listener);
|
||||||
listeners.length_d += 1;
|
listeners.length_d += 1;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Unregister a disconnection listener
|
||||||
|
* @param {Element} element - Element being watched
|
||||||
|
* @param {Function} listener - Callback to remove
|
||||||
|
*/
|
||||||
offDisconnected(element, listener) {
|
offDisconnected(element, listener) {
|
||||||
if (!store.has(element)) return;
|
if (!store.has(element)) return;
|
||||||
const ls = store.get(element);
|
const ls = store.get(element);
|
||||||
@ -572,20 +652,13 @@ on.attributeChanged = function(listener, options) {
|
|||||||
|
|
||||||
// src/signals-lib/helpers.js
|
// src/signals-lib/helpers.js
|
||||||
var mark = "__dde_signal";
|
var mark = "__dde_signal";
|
||||||
var SignalDefined = class 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));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var queueSignalWrite = /* @__PURE__ */ (() => {
|
var queueSignalWrite = /* @__PURE__ */ (() => {
|
||||||
let pendingSignals = /* @__PURE__ */ new Set();
|
let pendingSignals = /* @__PURE__ */ new Set();
|
||||||
let scheduled = false;
|
let scheduled = false;
|
||||||
function flushSignals() {
|
function flushSignals() {
|
||||||
scheduled = false;
|
scheduled = false;
|
||||||
for (const signal2 of pendingSignals) {
|
for (const signal2 of pendingSignals) {
|
||||||
|
pendingSignals.delete(signal2);
|
||||||
const M = signal2[mark];
|
const M = signal2[mark];
|
||||||
if (M) M.listeners.forEach((l) => l(M.value));
|
if (M) M.listeners.forEach((l) => l(M.value));
|
||||||
}
|
}
|
||||||
@ -623,7 +696,6 @@ function signal(value, actions) {
|
|||||||
removeSignalListener(dep_signal, contextReWatch);
|
removeSignalListener(dep_signal, contextReWatch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
;
|
|
||||||
deps.set(out[mark], contextReWatch);
|
deps.set(out[mark], contextReWatch);
|
||||||
deps.set(contextReWatch, /* @__PURE__ */ new Set([out]));
|
deps.set(contextReWatch, /* @__PURE__ */ new Set([out]));
|
||||||
contextReWatch();
|
contextReWatch();
|
||||||
@ -673,36 +745,27 @@ signal.clear = function(...signals2) {
|
|||||||
};
|
};
|
||||||
var key_reactive = "__dde_reactive";
|
var key_reactive = "__dde_reactive";
|
||||||
var storeMemo = /* @__PURE__ */ new WeakMap();
|
var storeMemo = /* @__PURE__ */ new WeakMap();
|
||||||
function memo(key, fun, cache) {
|
function memo(key, fun, host = fun) {
|
||||||
if (typeof key !== "string") key = JSON.stringify(key);
|
if (typeof key !== "string") key = JSON.stringify(key);
|
||||||
if (!cache) {
|
if (!storeMemo.has(host)) storeMemo.set(host, {});
|
||||||
const keyStore = scope.host();
|
const cache = storeMemo.get(host);
|
||||||
if (storeMemo.has(keyStore))
|
|
||||||
cache = storeMemo.get(keyStore);
|
|
||||||
else {
|
|
||||||
cache = {};
|
|
||||||
storeMemo.set(keyStore, cache);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hasOwn(cache, key) ? cache[key] : cache[key] = fun();
|
return hasOwn(cache, key) ? cache[key] : cache[key] = fun();
|
||||||
}
|
}
|
||||||
signal.el = function(s, map) {
|
signal.el = function(s, map) {
|
||||||
const mark_start = createElement.mark({ type: "reactive" }, true);
|
const mark_start = createElement.mark({ type: "reactive", source: new Defined().compact }, true);
|
||||||
const mark_end = mark_start.end;
|
const mark_end = mark_start.end;
|
||||||
const out = enviroment.D.createDocumentFragment();
|
const out = enviroment.D.createDocumentFragment();
|
||||||
out.append(mark_start, mark_end);
|
out.append(mark_start, mark_end);
|
||||||
const { current } = scope;
|
const { current } = scope;
|
||||||
let cache = {};
|
|
||||||
const reRenderReactiveElement = (v) => {
|
const reRenderReactiveElement = (v) => {
|
||||||
if (!mark_start.parentNode || !mark_end.parentNode)
|
if (!mark_start.parentNode || !mark_end.parentNode)
|
||||||
return removeSignalListener(s, reRenderReactiveElement);
|
return removeSignalListener(s, reRenderReactiveElement);
|
||||||
let cache_tmp = cache;
|
const cache = {};
|
||||||
cache = {};
|
|
||||||
scope.push(current);
|
scope.push(current);
|
||||||
let els = map(v, function useCache(key, fun) {
|
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();
|
scope.pop();
|
||||||
if (!Array.isArray(els))
|
if (!Array.isArray(els))
|
||||||
els = [els];
|
els = [els];
|
||||||
@ -719,10 +782,6 @@ signal.el = function(s, map) {
|
|||||||
addSignalListener(s, reRenderReactiveElement);
|
addSignalListener(s, reRenderReactiveElement);
|
||||||
removeSignalsFromElements(s, reRenderReactiveElement, mark_start, map);
|
removeSignalsFromElements(s, reRenderReactiveElement, mark_start, map);
|
||||||
reRenderReactiveElement(s());
|
reRenderReactiveElement(s());
|
||||||
current.host(on.disconnected(() => (
|
|
||||||
/*! This clears memoized elements in S.el when the host is disconnected */
|
|
||||||
cache = {}
|
|
||||||
)));
|
|
||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
function requestCleanUpReactives(host) {
|
function requestCleanUpReactives(host) {
|
||||||
@ -764,6 +823,15 @@ signal.observedAttributes = function(element) {
|
|||||||
};
|
};
|
||||||
var signals_config = {
|
var signals_config = {
|
||||||
isSignal,
|
isSignal,
|
||||||
|
/**
|
||||||
|
* Processes attributes that might be signals
|
||||||
|
*
|
||||||
|
* @param {Element} element - Element with the attribute
|
||||||
|
* @param {string} key - Attribute name
|
||||||
|
* @param {any} attrs - Attribute value (possibly a signal)
|
||||||
|
* @param {Function} set - Function to set attribute value
|
||||||
|
* @returns {any} Processed attribute value
|
||||||
|
*/
|
||||||
processReactiveAttribute(element, key, attrs, set) {
|
processReactiveAttribute(element, key, attrs, set) {
|
||||||
if (!isSignal(attrs)) return attrs;
|
if (!isSignal(attrs)) return attrs;
|
||||||
const l = (attr) => {
|
const l = (attr) => {
|
||||||
@ -802,6 +870,9 @@ function create(is_readonly, value, actions) {
|
|||||||
return SI;
|
return SI;
|
||||||
}
|
}
|
||||||
var protoSigal = Object.assign(/* @__PURE__ */ Object.create(null), {
|
var protoSigal = Object.assign(/* @__PURE__ */ Object.create(null), {
|
||||||
|
/**
|
||||||
|
* Prevents signal propagation
|
||||||
|
*/
|
||||||
stopPropagation() {
|
stopPropagation() {
|
||||||
this.skip = true;
|
this.skip = true;
|
||||||
}
|
}
|
||||||
@ -823,7 +894,7 @@ function toSignal(s, value, actions, readonly = false) {
|
|||||||
onclear,
|
onclear,
|
||||||
host,
|
host,
|
||||||
listeners: /* @__PURE__ */ new Set(),
|
listeners: /* @__PURE__ */ new Set(),
|
||||||
defined: new SignalDefined().stack,
|
defined: new Defined().stack,
|
||||||
readonly
|
readonly
|
||||||
},
|
},
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
|
67
dist/dde.js
vendored
67
dist/dde.js
vendored
@ -2,9 +2,22 @@
|
|||||||
(()=> {
|
(()=> {
|
||||||
// src/signals-lib/common.js
|
// src/signals-lib/common.js
|
||||||
var signals_global = {
|
var signals_global = {
|
||||||
|
/**
|
||||||
|
* Checks if a value is a signal
|
||||||
|
* @param {any} attributes - Value to check
|
||||||
|
* @returns {boolean} Whether the value is a signal
|
||||||
|
*/
|
||||||
isSignal(attributes) {
|
isSignal(attributes) {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Processes an attribute that might be reactive
|
||||||
|
* @param {Element} obj - Element that owns the attribute
|
||||||
|
* @param {string} key - Attribute name
|
||||||
|
* @param {any} attr - Attribute value
|
||||||
|
* @param {Function} set - Function to set the attribute
|
||||||
|
* @returns {any} Processed attribute value
|
||||||
|
*/
|
||||||
processReactiveAttribute(obj, key, attr, set) {
|
processReactiveAttribute(obj, key, attr, set) {
|
||||||
return attr;
|
return attr;
|
||||||
}
|
}
|
||||||
@ -80,26 +93,55 @@ var scopes = [{
|
|||||||
prevent: true
|
prevent: true
|
||||||
}];
|
}];
|
||||||
var scope = {
|
var scope = {
|
||||||
|
/**
|
||||||
|
* Gets the current scope
|
||||||
|
* @returns {Object} Current scope context
|
||||||
|
*/
|
||||||
get current() {
|
get current() {
|
||||||
return scopes[scopes.length - 1];
|
return scopes[scopes.length - 1];
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Gets the host element of the current scope
|
||||||
|
* @returns {Function} Host accessor function
|
||||||
|
*/
|
||||||
get host() {
|
get host() {
|
||||||
return this.current.host;
|
return this.current.host;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Prevents default behavior in the current scope
|
||||||
|
* @returns {Object} Current scope context
|
||||||
|
*/
|
||||||
preventDefault() {
|
preventDefault() {
|
||||||
const { current } = this;
|
const { current } = this;
|
||||||
current.prevent = true;
|
current.prevent = true;
|
||||||
return current;
|
return current;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Gets a copy of the current scope stack
|
||||||
|
* @returns {Array} Copy of scope stack
|
||||||
|
*/
|
||||||
get state() {
|
get state() {
|
||||||
return [...scopes];
|
return [...scopes];
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Pushes a new scope to the stack
|
||||||
|
* @param {Object} [s={}] - Scope object to push
|
||||||
|
* @returns {number} New length of the scope stack
|
||||||
|
*/
|
||||||
push(s = {}) {
|
push(s = {}) {
|
||||||
return scopes.push(Object.assign({}, this.current, { prevent: false }, s));
|
return scopes.push(Object.assign({}, this.current, { prevent: false }, s));
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Pushes the root scope to the stack
|
||||||
|
* @returns {number} New length of the scope stack
|
||||||
|
*/
|
||||||
pushRoot() {
|
pushRoot() {
|
||||||
return scopes.push(scopes[0]);
|
return scopes.push(scopes[0]);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Pops the current scope from the stack
|
||||||
|
* @returns {Object|undefined} Popped scope or undefined if only one scope remains
|
||||||
|
*/
|
||||||
pop() {
|
pop() {
|
||||||
if (scopes.length === 1) return;
|
if (scopes.length === 1) return;
|
||||||
return scopes.pop();
|
return scopes.pop();
|
||||||
@ -331,12 +373,22 @@ function connectionsChangesObserverConstructor() {
|
|||||||
};
|
};
|
||||||
const observer = new enviroment.M(observerListener(stop));
|
const observer = new enviroment.M(observerListener(stop));
|
||||||
return {
|
return {
|
||||||
|
/**
|
||||||
|
* Creates an observer for a specific element
|
||||||
|
* @param {Element} element - Element to observe
|
||||||
|
* @returns {Function} Cleanup function
|
||||||
|
*/
|
||||||
observe(element) {
|
observe(element) {
|
||||||
const o = new enviroment.M(observerListener(() => {
|
const o = new enviroment.M(observerListener(() => {
|
||||||
}));
|
}));
|
||||||
o.observe(element, { childList: true, subtree: true });
|
o.observe(element, { childList: true, subtree: true });
|
||||||
return () => o.disconnect();
|
return () => o.disconnect();
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Register a connection listener for an element
|
||||||
|
* @param {Element} element - Element to watch
|
||||||
|
* @param {Function} listener - Callback for connection event
|
||||||
|
*/
|
||||||
onConnected(element, listener) {
|
onConnected(element, listener) {
|
||||||
start();
|
start();
|
||||||
const listeners = getElementStore(element);
|
const listeners = getElementStore(element);
|
||||||
@ -344,6 +396,11 @@ function connectionsChangesObserverConstructor() {
|
|||||||
listeners.connected.add(listener);
|
listeners.connected.add(listener);
|
||||||
listeners.length_c += 1;
|
listeners.length_c += 1;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Unregister a connection listener
|
||||||
|
* @param {Element} element - Element being watched
|
||||||
|
* @param {Function} listener - Callback to remove
|
||||||
|
*/
|
||||||
offConnected(element, listener) {
|
offConnected(element, listener) {
|
||||||
if (!store.has(element)) return;
|
if (!store.has(element)) return;
|
||||||
const ls = store.get(element);
|
const ls = store.get(element);
|
||||||
@ -352,6 +409,11 @@ function connectionsChangesObserverConstructor() {
|
|||||||
ls.length_c -= 1;
|
ls.length_c -= 1;
|
||||||
cleanWhenOff(element, ls);
|
cleanWhenOff(element, ls);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Register a disconnection listener for an element
|
||||||
|
* @param {Element} element - Element to watch
|
||||||
|
* @param {Function} listener - Callback for disconnection event
|
||||||
|
*/
|
||||||
onDisconnected(element, listener) {
|
onDisconnected(element, listener) {
|
||||||
start();
|
start();
|
||||||
const listeners = getElementStore(element);
|
const listeners = getElementStore(element);
|
||||||
@ -359,6 +421,11 @@ function connectionsChangesObserverConstructor() {
|
|||||||
listeners.disconnected.add(listener);
|
listeners.disconnected.add(listener);
|
||||||
listeners.length_d += 1;
|
listeners.length_d += 1;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Unregister a disconnection listener
|
||||||
|
* @param {Element} element - Element being watched
|
||||||
|
* @param {Function} listener - Callback to remove
|
||||||
|
*/
|
||||||
offDisconnected(element, listener) {
|
offDisconnected(element, listener) {
|
||||||
if (!store.has(element)) return;
|
if (!store.has(element)) return;
|
||||||
const ls = store.get(element);
|
const ls = store.get(element);
|
||||||
|
131
dist/esm-with-signals.js
vendored
131
dist/esm-with-signals.js
vendored
@ -1,8 +1,21 @@
|
|||||||
// src/signals-lib/common.js
|
// src/signals-lib/common.js
|
||||||
var signals_global = {
|
var signals_global = {
|
||||||
|
/**
|
||||||
|
* Checks if a value is a signal
|
||||||
|
* @param {any} attributes - Value to check
|
||||||
|
* @returns {boolean} Whether the value is a signal
|
||||||
|
*/
|
||||||
isSignal(attributes) {
|
isSignal(attributes) {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Processes an attribute that might be reactive
|
||||||
|
* @param {Element} obj - Element that owns the attribute
|
||||||
|
* @param {string} key - Attribute name
|
||||||
|
* @param {any} attr - Attribute value
|
||||||
|
* @param {Function} set - Function to set the attribute
|
||||||
|
* @returns {any} Processed attribute value
|
||||||
|
*/
|
||||||
processReactiveAttribute(obj, key, attr, set) {
|
processReactiveAttribute(obj, key, attr, set) {
|
||||||
return attr;
|
return attr;
|
||||||
}
|
}
|
||||||
@ -47,6 +60,19 @@ function observedAttributes(instance, observedAttribute2) {
|
|||||||
function kebabToCamel(name) {
|
function kebabToCamel(name) {
|
||||||
return name.replace(/-./g, (x) => x[1].toUpperCase());
|
return name.replace(/-./g, (x) => x[1].toUpperCase());
|
||||||
}
|
}
|
||||||
|
var Defined = class extends Error {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
const [curr, ...rest] = this.stack.split("\n");
|
||||||
|
const curr_file = curr.slice(curr.indexOf("@"), curr.indexOf(".js:") + 4);
|
||||||
|
const curr_lib = curr_file.includes("src/dom-common.js") ? "src/" : curr_file;
|
||||||
|
this.stack = rest.find((l) => !l.includes(curr_lib)) || curr;
|
||||||
|
}
|
||||||
|
get compact() {
|
||||||
|
const { stack } = this;
|
||||||
|
return stack.slice(0, stack.indexOf("@") + 1) + "\u2026" + stack.slice(stack.lastIndexOf("/"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// src/dom-common.js
|
// src/dom-common.js
|
||||||
var enviroment = {
|
var enviroment = {
|
||||||
@ -85,26 +111,55 @@ var scopes = [{
|
|||||||
prevent: true
|
prevent: true
|
||||||
}];
|
}];
|
||||||
var scope = {
|
var scope = {
|
||||||
|
/**
|
||||||
|
* Gets the current scope
|
||||||
|
* @returns {Object} Current scope context
|
||||||
|
*/
|
||||||
get current() {
|
get current() {
|
||||||
return scopes[scopes.length - 1];
|
return scopes[scopes.length - 1];
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Gets the host element of the current scope
|
||||||
|
* @returns {Function} Host accessor function
|
||||||
|
*/
|
||||||
get host() {
|
get host() {
|
||||||
return this.current.host;
|
return this.current.host;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Prevents default behavior in the current scope
|
||||||
|
* @returns {Object} Current scope context
|
||||||
|
*/
|
||||||
preventDefault() {
|
preventDefault() {
|
||||||
const { current } = this;
|
const { current } = this;
|
||||||
current.prevent = true;
|
current.prevent = true;
|
||||||
return current;
|
return current;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Gets a copy of the current scope stack
|
||||||
|
* @returns {Array} Copy of scope stack
|
||||||
|
*/
|
||||||
get state() {
|
get state() {
|
||||||
return [...scopes];
|
return [...scopes];
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Pushes a new scope to the stack
|
||||||
|
* @param {Object} [s={}] - Scope object to push
|
||||||
|
* @returns {number} New length of the scope stack
|
||||||
|
*/
|
||||||
push(s = {}) {
|
push(s = {}) {
|
||||||
return scopes.push(Object.assign({}, this.current, { prevent: false }, s));
|
return scopes.push(Object.assign({}, this.current, { prevent: false }, s));
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Pushes the root scope to the stack
|
||||||
|
* @returns {number} New length of the scope stack
|
||||||
|
*/
|
||||||
pushRoot() {
|
pushRoot() {
|
||||||
return scopes.push(scopes[0]);
|
return scopes.push(scopes[0]);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Pops the current scope from the stack
|
||||||
|
* @returns {Object|undefined} Popped scope or undefined if only one scope remains
|
||||||
|
*/
|
||||||
pop() {
|
pop() {
|
||||||
if (scopes.length === 1) return;
|
if (scopes.length === 1) return;
|
||||||
return scopes.pop();
|
return scopes.pop();
|
||||||
@ -336,12 +391,22 @@ function connectionsChangesObserverConstructor() {
|
|||||||
};
|
};
|
||||||
const observer = new enviroment.M(observerListener(stop));
|
const observer = new enviroment.M(observerListener(stop));
|
||||||
return {
|
return {
|
||||||
|
/**
|
||||||
|
* Creates an observer for a specific element
|
||||||
|
* @param {Element} element - Element to observe
|
||||||
|
* @returns {Function} Cleanup function
|
||||||
|
*/
|
||||||
observe(element) {
|
observe(element) {
|
||||||
const o = new enviroment.M(observerListener(() => {
|
const o = new enviroment.M(observerListener(() => {
|
||||||
}));
|
}));
|
||||||
o.observe(element, { childList: true, subtree: true });
|
o.observe(element, { childList: true, subtree: true });
|
||||||
return () => o.disconnect();
|
return () => o.disconnect();
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Register a connection listener for an element
|
||||||
|
* @param {Element} element - Element to watch
|
||||||
|
* @param {Function} listener - Callback for connection event
|
||||||
|
*/
|
||||||
onConnected(element, listener) {
|
onConnected(element, listener) {
|
||||||
start();
|
start();
|
||||||
const listeners = getElementStore(element);
|
const listeners = getElementStore(element);
|
||||||
@ -349,6 +414,11 @@ function connectionsChangesObserverConstructor() {
|
|||||||
listeners.connected.add(listener);
|
listeners.connected.add(listener);
|
||||||
listeners.length_c += 1;
|
listeners.length_c += 1;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Unregister a connection listener
|
||||||
|
* @param {Element} element - Element being watched
|
||||||
|
* @param {Function} listener - Callback to remove
|
||||||
|
*/
|
||||||
offConnected(element, listener) {
|
offConnected(element, listener) {
|
||||||
if (!store.has(element)) return;
|
if (!store.has(element)) return;
|
||||||
const ls = store.get(element);
|
const ls = store.get(element);
|
||||||
@ -357,6 +427,11 @@ function connectionsChangesObserverConstructor() {
|
|||||||
ls.length_c -= 1;
|
ls.length_c -= 1;
|
||||||
cleanWhenOff(element, ls);
|
cleanWhenOff(element, ls);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Register a disconnection listener for an element
|
||||||
|
* @param {Element} element - Element to watch
|
||||||
|
* @param {Function} listener - Callback for disconnection event
|
||||||
|
*/
|
||||||
onDisconnected(element, listener) {
|
onDisconnected(element, listener) {
|
||||||
start();
|
start();
|
||||||
const listeners = getElementStore(element);
|
const listeners = getElementStore(element);
|
||||||
@ -364,6 +439,11 @@ function connectionsChangesObserverConstructor() {
|
|||||||
listeners.disconnected.add(listener);
|
listeners.disconnected.add(listener);
|
||||||
listeners.length_d += 1;
|
listeners.length_d += 1;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Unregister a disconnection listener
|
||||||
|
* @param {Element} element - Element being watched
|
||||||
|
* @param {Function} listener - Callback to remove
|
||||||
|
*/
|
||||||
offDisconnected(element, listener) {
|
offDisconnected(element, listener) {
|
||||||
if (!store.has(element)) return;
|
if (!store.has(element)) return;
|
||||||
const ls = store.get(element);
|
const ls = store.get(element);
|
||||||
@ -570,20 +650,13 @@ on.attributeChanged = function(listener, options) {
|
|||||||
|
|
||||||
// src/signals-lib/helpers.js
|
// src/signals-lib/helpers.js
|
||||||
var mark = "__dde_signal";
|
var mark = "__dde_signal";
|
||||||
var SignalDefined = class 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));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var queueSignalWrite = /* @__PURE__ */ (() => {
|
var queueSignalWrite = /* @__PURE__ */ (() => {
|
||||||
let pendingSignals = /* @__PURE__ */ new Set();
|
let pendingSignals = /* @__PURE__ */ new Set();
|
||||||
let scheduled = false;
|
let scheduled = false;
|
||||||
function flushSignals() {
|
function flushSignals() {
|
||||||
scheduled = false;
|
scheduled = false;
|
||||||
for (const signal2 of pendingSignals) {
|
for (const signal2 of pendingSignals) {
|
||||||
|
pendingSignals.delete(signal2);
|
||||||
const M = signal2[mark];
|
const M = signal2[mark];
|
||||||
if (M) M.listeners.forEach((l) => l(M.value));
|
if (M) M.listeners.forEach((l) => l(M.value));
|
||||||
}
|
}
|
||||||
@ -621,7 +694,6 @@ function signal(value, actions) {
|
|||||||
removeSignalListener(dep_signal, contextReWatch);
|
removeSignalListener(dep_signal, contextReWatch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
;
|
|
||||||
deps.set(out[mark], contextReWatch);
|
deps.set(out[mark], contextReWatch);
|
||||||
deps.set(contextReWatch, /* @__PURE__ */ new Set([out]));
|
deps.set(contextReWatch, /* @__PURE__ */ new Set([out]));
|
||||||
contextReWatch();
|
contextReWatch();
|
||||||
@ -671,36 +743,27 @@ signal.clear = function(...signals2) {
|
|||||||
};
|
};
|
||||||
var key_reactive = "__dde_reactive";
|
var key_reactive = "__dde_reactive";
|
||||||
var storeMemo = /* @__PURE__ */ new WeakMap();
|
var storeMemo = /* @__PURE__ */ new WeakMap();
|
||||||
function memo(key, fun, cache) {
|
function memo(key, fun, host = fun) {
|
||||||
if (typeof key !== "string") key = JSON.stringify(key);
|
if (typeof key !== "string") key = JSON.stringify(key);
|
||||||
if (!cache) {
|
if (!storeMemo.has(host)) storeMemo.set(host, {});
|
||||||
const keyStore = scope.host();
|
const cache = storeMemo.get(host);
|
||||||
if (storeMemo.has(keyStore))
|
|
||||||
cache = storeMemo.get(keyStore);
|
|
||||||
else {
|
|
||||||
cache = {};
|
|
||||||
storeMemo.set(keyStore, cache);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hasOwn(cache, key) ? cache[key] : cache[key] = fun();
|
return hasOwn(cache, key) ? cache[key] : cache[key] = fun();
|
||||||
}
|
}
|
||||||
signal.el = function(s, map) {
|
signal.el = function(s, map) {
|
||||||
const mark_start = createElement.mark({ type: "reactive" }, true);
|
const mark_start = createElement.mark({ type: "reactive", source: new Defined().compact }, true);
|
||||||
const mark_end = mark_start.end;
|
const mark_end = mark_start.end;
|
||||||
const out = enviroment.D.createDocumentFragment();
|
const out = enviroment.D.createDocumentFragment();
|
||||||
out.append(mark_start, mark_end);
|
out.append(mark_start, mark_end);
|
||||||
const { current } = scope;
|
const { current } = scope;
|
||||||
let cache = {};
|
|
||||||
const reRenderReactiveElement = (v) => {
|
const reRenderReactiveElement = (v) => {
|
||||||
if (!mark_start.parentNode || !mark_end.parentNode)
|
if (!mark_start.parentNode || !mark_end.parentNode)
|
||||||
return removeSignalListener(s, reRenderReactiveElement);
|
return removeSignalListener(s, reRenderReactiveElement);
|
||||||
let cache_tmp = cache;
|
const cache = {};
|
||||||
cache = {};
|
|
||||||
scope.push(current);
|
scope.push(current);
|
||||||
let els = map(v, function useCache(key, fun) {
|
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();
|
scope.pop();
|
||||||
if (!Array.isArray(els))
|
if (!Array.isArray(els))
|
||||||
els = [els];
|
els = [els];
|
||||||
@ -717,10 +780,6 @@ signal.el = function(s, map) {
|
|||||||
addSignalListener(s, reRenderReactiveElement);
|
addSignalListener(s, reRenderReactiveElement);
|
||||||
removeSignalsFromElements(s, reRenderReactiveElement, mark_start, map);
|
removeSignalsFromElements(s, reRenderReactiveElement, mark_start, map);
|
||||||
reRenderReactiveElement(s());
|
reRenderReactiveElement(s());
|
||||||
current.host(on.disconnected(() => (
|
|
||||||
/*! This clears memoized elements in S.el when the host is disconnected */
|
|
||||||
cache = {}
|
|
||||||
)));
|
|
||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
function requestCleanUpReactives(host) {
|
function requestCleanUpReactives(host) {
|
||||||
@ -762,6 +821,15 @@ signal.observedAttributes = function(element) {
|
|||||||
};
|
};
|
||||||
var signals_config = {
|
var signals_config = {
|
||||||
isSignal,
|
isSignal,
|
||||||
|
/**
|
||||||
|
* Processes attributes that might be signals
|
||||||
|
*
|
||||||
|
* @param {Element} element - Element with the attribute
|
||||||
|
* @param {string} key - Attribute name
|
||||||
|
* @param {any} attrs - Attribute value (possibly a signal)
|
||||||
|
* @param {Function} set - Function to set attribute value
|
||||||
|
* @returns {any} Processed attribute value
|
||||||
|
*/
|
||||||
processReactiveAttribute(element, key, attrs, set) {
|
processReactiveAttribute(element, key, attrs, set) {
|
||||||
if (!isSignal(attrs)) return attrs;
|
if (!isSignal(attrs)) return attrs;
|
||||||
const l = (attr) => {
|
const l = (attr) => {
|
||||||
@ -800,6 +868,9 @@ function create(is_readonly, value, actions) {
|
|||||||
return SI;
|
return SI;
|
||||||
}
|
}
|
||||||
var protoSigal = Object.assign(/* @__PURE__ */ Object.create(null), {
|
var protoSigal = Object.assign(/* @__PURE__ */ Object.create(null), {
|
||||||
|
/**
|
||||||
|
* Prevents signal propagation
|
||||||
|
*/
|
||||||
stopPropagation() {
|
stopPropagation() {
|
||||||
this.skip = true;
|
this.skip = true;
|
||||||
}
|
}
|
||||||
@ -821,7 +892,7 @@ function toSignal(s, value, actions, readonly = false) {
|
|||||||
onclear,
|
onclear,
|
||||||
host,
|
host,
|
||||||
listeners: /* @__PURE__ */ new Set(),
|
listeners: /* @__PURE__ */ new Set(),
|
||||||
defined: new SignalDefined().stack,
|
defined: new Defined().stack,
|
||||||
readonly
|
readonly
|
||||||
},
|
},
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
|
67
dist/esm.js
vendored
67
dist/esm.js
vendored
@ -1,8 +1,21 @@
|
|||||||
// src/signals-lib/common.js
|
// src/signals-lib/common.js
|
||||||
var signals_global = {
|
var signals_global = {
|
||||||
|
/**
|
||||||
|
* Checks if a value is a signal
|
||||||
|
* @param {any} attributes - Value to check
|
||||||
|
* @returns {boolean} Whether the value is a signal
|
||||||
|
*/
|
||||||
isSignal(attributes) {
|
isSignal(attributes) {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Processes an attribute that might be reactive
|
||||||
|
* @param {Element} obj - Element that owns the attribute
|
||||||
|
* @param {string} key - Attribute name
|
||||||
|
* @param {any} attr - Attribute value
|
||||||
|
* @param {Function} set - Function to set the attribute
|
||||||
|
* @returns {any} Processed attribute value
|
||||||
|
*/
|
||||||
processReactiveAttribute(obj, key, attr, set) {
|
processReactiveAttribute(obj, key, attr, set) {
|
||||||
return attr;
|
return attr;
|
||||||
}
|
}
|
||||||
@ -78,26 +91,55 @@ var scopes = [{
|
|||||||
prevent: true
|
prevent: true
|
||||||
}];
|
}];
|
||||||
var scope = {
|
var scope = {
|
||||||
|
/**
|
||||||
|
* Gets the current scope
|
||||||
|
* @returns {Object} Current scope context
|
||||||
|
*/
|
||||||
get current() {
|
get current() {
|
||||||
return scopes[scopes.length - 1];
|
return scopes[scopes.length - 1];
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Gets the host element of the current scope
|
||||||
|
* @returns {Function} Host accessor function
|
||||||
|
*/
|
||||||
get host() {
|
get host() {
|
||||||
return this.current.host;
|
return this.current.host;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Prevents default behavior in the current scope
|
||||||
|
* @returns {Object} Current scope context
|
||||||
|
*/
|
||||||
preventDefault() {
|
preventDefault() {
|
||||||
const { current } = this;
|
const { current } = this;
|
||||||
current.prevent = true;
|
current.prevent = true;
|
||||||
return current;
|
return current;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Gets a copy of the current scope stack
|
||||||
|
* @returns {Array} Copy of scope stack
|
||||||
|
*/
|
||||||
get state() {
|
get state() {
|
||||||
return [...scopes];
|
return [...scopes];
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Pushes a new scope to the stack
|
||||||
|
* @param {Object} [s={}] - Scope object to push
|
||||||
|
* @returns {number} New length of the scope stack
|
||||||
|
*/
|
||||||
push(s = {}) {
|
push(s = {}) {
|
||||||
return scopes.push(Object.assign({}, this.current, { prevent: false }, s));
|
return scopes.push(Object.assign({}, this.current, { prevent: false }, s));
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Pushes the root scope to the stack
|
||||||
|
* @returns {number} New length of the scope stack
|
||||||
|
*/
|
||||||
pushRoot() {
|
pushRoot() {
|
||||||
return scopes.push(scopes[0]);
|
return scopes.push(scopes[0]);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Pops the current scope from the stack
|
||||||
|
* @returns {Object|undefined} Popped scope or undefined if only one scope remains
|
||||||
|
*/
|
||||||
pop() {
|
pop() {
|
||||||
if (scopes.length === 1) return;
|
if (scopes.length === 1) return;
|
||||||
return scopes.pop();
|
return scopes.pop();
|
||||||
@ -329,12 +371,22 @@ function connectionsChangesObserverConstructor() {
|
|||||||
};
|
};
|
||||||
const observer = new enviroment.M(observerListener(stop));
|
const observer = new enviroment.M(observerListener(stop));
|
||||||
return {
|
return {
|
||||||
|
/**
|
||||||
|
* Creates an observer for a specific element
|
||||||
|
* @param {Element} element - Element to observe
|
||||||
|
* @returns {Function} Cleanup function
|
||||||
|
*/
|
||||||
observe(element) {
|
observe(element) {
|
||||||
const o = new enviroment.M(observerListener(() => {
|
const o = new enviroment.M(observerListener(() => {
|
||||||
}));
|
}));
|
||||||
o.observe(element, { childList: true, subtree: true });
|
o.observe(element, { childList: true, subtree: true });
|
||||||
return () => o.disconnect();
|
return () => o.disconnect();
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Register a connection listener for an element
|
||||||
|
* @param {Element} element - Element to watch
|
||||||
|
* @param {Function} listener - Callback for connection event
|
||||||
|
*/
|
||||||
onConnected(element, listener) {
|
onConnected(element, listener) {
|
||||||
start();
|
start();
|
||||||
const listeners = getElementStore(element);
|
const listeners = getElementStore(element);
|
||||||
@ -342,6 +394,11 @@ function connectionsChangesObserverConstructor() {
|
|||||||
listeners.connected.add(listener);
|
listeners.connected.add(listener);
|
||||||
listeners.length_c += 1;
|
listeners.length_c += 1;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Unregister a connection listener
|
||||||
|
* @param {Element} element - Element being watched
|
||||||
|
* @param {Function} listener - Callback to remove
|
||||||
|
*/
|
||||||
offConnected(element, listener) {
|
offConnected(element, listener) {
|
||||||
if (!store.has(element)) return;
|
if (!store.has(element)) return;
|
||||||
const ls = store.get(element);
|
const ls = store.get(element);
|
||||||
@ -350,6 +407,11 @@ function connectionsChangesObserverConstructor() {
|
|||||||
ls.length_c -= 1;
|
ls.length_c -= 1;
|
||||||
cleanWhenOff(element, ls);
|
cleanWhenOff(element, ls);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Register a disconnection listener for an element
|
||||||
|
* @param {Element} element - Element to watch
|
||||||
|
* @param {Function} listener - Callback for disconnection event
|
||||||
|
*/
|
||||||
onDisconnected(element, listener) {
|
onDisconnected(element, listener) {
|
||||||
start();
|
start();
|
||||||
const listeners = getElementStore(element);
|
const listeners = getElementStore(element);
|
||||||
@ -357,6 +419,11 @@ function connectionsChangesObserverConstructor() {
|
|||||||
listeners.disconnected.add(listener);
|
listeners.disconnected.add(listener);
|
||||||
listeners.length_d += 1;
|
listeners.length_d += 1;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Unregister a disconnection listener
|
||||||
|
* @param {Element} element - Element being watched
|
||||||
|
* @param {Function} listener - Callback to remove
|
||||||
|
*/
|
||||||
offDisconnected(element, listener) {
|
offDisconnected(element, listener) {
|
||||||
if (!store.has(element)) return;
|
if (!store.has(element)) return;
|
||||||
const ls = store.get(element);
|
const ls = store.get(element);
|
||||||
|
@ -53,7 +53,8 @@
|
|||||||
"globals": {
|
"globals": {
|
||||||
"requestIdleCallback": false,
|
"requestIdleCallback": false,
|
||||||
"AbortController": false,
|
"AbortController": false,
|
||||||
"AbortSignal": false
|
"AbortSignal": false,
|
||||||
|
"FinalizationRegistry": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"size-limit": [
|
"size-limit": [
|
||||||
@ -65,7 +66,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "./signals.js",
|
"path": "./signals.js",
|
||||||
"limit": "12 kB",
|
"limit": "12.5 kB",
|
||||||
"gzip": false,
|
"gzip": false,
|
||||||
"brotli": false
|
"brotli": false
|
||||||
},
|
},
|
||||||
|
@ -62,3 +62,21 @@ export function observedAttributes(instance, observedAttribute){
|
|||||||
* @returns {string} The camelCase string
|
* @returns {string} The camelCase string
|
||||||
*/
|
*/
|
||||||
function kebabToCamel(name){ return name.replace(/-./g, x=> x[1].toUpperCase()); }
|
function kebabToCamel(name){ return name.replace(/-./g, x=> x[1].toUpperCase()); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error class for definition tracking
|
||||||
|
* Shows the correct stack trace for debugging (signal) creation
|
||||||
|
*/
|
||||||
|
export class Defined extends Error{
|
||||||
|
constructor(){
|
||||||
|
super();
|
||||||
|
const [ curr, ...rest ]= this.stack.split("\n");
|
||||||
|
const curr_file= curr.slice(curr.indexOf("@"), curr.indexOf(".js:")+4);
|
||||||
|
const curr_lib= curr_file.includes("src/helpers.js") ? "src/" : curr_file;
|
||||||
|
this.stack= rest.find(l=> !l.includes(curr_lib)) || curr;
|
||||||
|
}
|
||||||
|
get compact(){
|
||||||
|
const { stack }= this;
|
||||||
|
return stack.slice(0, stack.indexOf("@")+1)+"…"+stack.slice(stack.lastIndexOf("/"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,19 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
export const mark= "__dde_signal";
|
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
|
* Batches signal updates to improve performance
|
||||||
* @type {Function}
|
* @type {Function}
|
||||||
@ -32,6 +19,7 @@ export const queueSignalWrite= (()=> {
|
|||||||
function flushSignals() {
|
function flushSignals() {
|
||||||
scheduled = false;
|
scheduled = false;
|
||||||
for(const signal of pendingSignals){
|
for(const signal of pendingSignals){
|
||||||
|
pendingSignals.delete(signal);
|
||||||
const M = signal[mark];
|
const M = signal[mark];
|
||||||
if(M) M.listeners.forEach(l => l(M.value));
|
if(M) M.listeners.forEach(l => l(M.value));
|
||||||
}
|
}
|
||||||
@ -47,5 +35,5 @@ export const queueSignalWrite= (()=> {
|
|||||||
if(scheduled) return;
|
if(scheduled) return;
|
||||||
scheduled = true;
|
scheduled = true;
|
||||||
queueMicrotask(flushSignals);
|
queueMicrotask(flushSignals);
|
||||||
}
|
};
|
||||||
})();
|
})();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { SignalDefined, queueSignalWrite, mark } from "./helpers.js";
|
import { queueSignalWrite, mark } from "./helpers.js";
|
||||||
export { mark };
|
export { mark };
|
||||||
import { hasOwn } from "../helpers.js";
|
import { hasOwn, Defined } from "../helpers.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a value is a signal
|
* Checks if a value is a signal
|
||||||
@ -62,7 +62,7 @@ export function signal(value, actions){
|
|||||||
if(deps_curr.has(dep_signal)) continue;
|
if(deps_curr.has(dep_signal)) continue;
|
||||||
removeSignalListener(dep_signal, contextReWatch);
|
removeSignalListener(dep_signal, contextReWatch);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
deps.set(out[mark], contextReWatch);
|
deps.set(out[mark], contextReWatch);
|
||||||
deps.set(contextReWatch, new Set([ out ]));
|
deps.set(contextReWatch, new Set([ out ]));
|
||||||
contextReWatch();
|
contextReWatch();
|
||||||
@ -159,51 +159,42 @@ import { on } from "../events.js";
|
|||||||
const storeMemo= new WeakMap();
|
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 {Function} fun - Function to compute value
|
||||||
* @param {Object} [cache] - Optional explicit cache object
|
* @param {keyof storeMemo} [host= fun]
|
||||||
* @returns {any} Cached or computed result
|
* @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(typeof key!=="string") key= JSON.stringify(key);
|
||||||
if(!cache) {
|
if (!storeMemo.has(host)) storeMemo.set(host, {});
|
||||||
const keyStore= scope.host();
|
const cache= storeMemo.get(host);
|
||||||
if(storeMemo.has(keyStore))
|
|
||||||
cache= storeMemo.get(keyStore);
|
|
||||||
else {
|
|
||||||
cache= {};
|
|
||||||
storeMemo.set(keyStore, cache);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hasOwn(cache, key) ? cache[key] : (cache[key]= fun());
|
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
|
* 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} s - Signal to watch
|
||||||
* @param {Function} map - Function mapping signal value to DOM elements
|
* @param {Function} map - Function mapping signal value to DOM elements
|
||||||
* @returns {DocumentFragment} Fragment containing reactive elements
|
* @returns {DocumentFragment} Fragment containing reactive elements
|
||||||
*/
|
*/
|
||||||
signal.el= function(s, map){
|
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 mark_end= mark_start.end;
|
||||||
const out= env.D.createDocumentFragment();
|
const out= env.D.createDocumentFragment();
|
||||||
out.append(mark_start, mark_end);
|
out.append(mark_start, mark_end);
|
||||||
const { current }= scope;
|
const { current }= scope;
|
||||||
let cache= {};
|
|
||||||
const reRenderReactiveElement= v=> {
|
const reRenderReactiveElement= v=> {
|
||||||
if(!mark_start.parentNode || !mark_end.parentNode) // === `isConnected` or wasn’t yet rendered
|
if(!mark_start.parentNode || !mark_end.parentNode) // === `isConnected` or wasn’t yet rendered
|
||||||
return removeSignalListener(s, reRenderReactiveElement);
|
return removeSignalListener(s, reRenderReactiveElement);
|
||||||
let cache_tmp= cache; // will be reused in the useCache or removed in the while loop on the end
|
const cache= {}; // remove unused els from cache
|
||||||
cache= {};
|
|
||||||
scope.push(current);
|
scope.push(current);
|
||||||
let els= map(v, function useCache(key, fun){
|
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();
|
scope.pop();
|
||||||
if(!Array.isArray(els))
|
if(!Array.isArray(els))
|
||||||
els= [ els ];
|
els= [ els ];
|
||||||
@ -220,9 +211,6 @@ signal.el= function(s, map){
|
|||||||
addSignalListener(s, reRenderReactiveElement);
|
addSignalListener(s, reRenderReactiveElement);
|
||||||
removeSignalsFromElements(s, reRenderReactiveElement, mark_start, map);
|
removeSignalsFromElements(s, reRenderReactiveElement, mark_start, map);
|
||||||
reRenderReactiveElement(s());
|
reRenderReactiveElement(s());
|
||||||
current.host(on.disconnected(()=>
|
|
||||||
/*! This clears memoized elements in S.el when the host is disconnected */
|
|
||||||
cache= {}));
|
|
||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
@ -407,7 +395,7 @@ function toSignal(s, value, actions, readonly= false){
|
|||||||
value: {
|
value: {
|
||||||
value, actions, onclear, host,
|
value, actions, onclear, host,
|
||||||
listeners: new Set(),
|
listeners: new Set(),
|
||||||
defined: (new SignalDefined()).stack,
|
defined: new Defined().stack,
|
||||||
readonly
|
readonly
|
||||||
},
|
},
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user