mirror of
https://github.com/jaandrle/deka-dom-el
synced 2025-07-01 20:32:13 +02:00
🐛 Process primitives & signals correctly in createElement
/assign
This commit is contained in:
@ -1,5 +1,3 @@
|
||||
/** @type {Map<string, boolean>} */
|
||||
export const prop_cache= new Map(JSON.parse('[["#text,textContent",true],["HTMLElement,textContent",true],["HTMLElement,className",true]]'));
|
||||
export const prop_process= { setDeleteAttr };
|
||||
import { isUndef } from './helpers.js';
|
||||
function setDeleteAttr(obj, prop, val){
|
||||
@ -13,6 +11,9 @@ function setDeleteAttr(obj, prop, val){
|
||||
4. Point 2. with checks for coincidence (e.g. use special string)
|
||||
*/
|
||||
Reflect.set(obj, prop, val);
|
||||
if(isUndef(val) && obj.getAttribute(prop)==="undefined")
|
||||
obj.removeAttribute(prop);
|
||||
if(!isUndef(val)) return;
|
||||
if(obj instanceof HTMLElement && obj.getAttribute(prop)==="undefined")
|
||||
return obj.removeAttribute(prop);
|
||||
if(Reflect.get(obj, prop)==="undefined")
|
||||
return Reflect.set(obj, prop, "");
|
||||
}
|
||||
|
60
src/dom.js
60
src/dom.js
@ -17,7 +17,7 @@ export function createElement(tag, attributes, ...connect){
|
||||
const s= signals(this);
|
||||
let el;
|
||||
//TODO Array.isArray(tag) ⇒ set key (cache els)
|
||||
if(s.isTextContent(attributes))
|
||||
if(Object(attributes)!==attributes || s.isSignal(attributes))
|
||||
attributes= { textContent: attributes };
|
||||
switch(true){
|
||||
case typeof tag==="function": {
|
||||
@ -35,40 +35,39 @@ export function createElement(tag, attributes, ...connect){
|
||||
}
|
||||
export { createElement as el };
|
||||
|
||||
import { prop_cache, prop_process } from './dom-common.js';
|
||||
import { prop_process } from './dom-common.js';
|
||||
const { setDeleteAttr }= prop_process;
|
||||
export function assign(element, ...attributes){
|
||||
const _this= this;
|
||||
const s= signals(this);
|
||||
if(!attributes.length) return element;
|
||||
const is_svg= element instanceof SVGElement;
|
||||
const setRemoveAttr= (is_svg ? setRemoveNS : setRemove).bind(null, element, "Attribute");
|
||||
|
||||
/* jshint maxcomplexity:17 */
|
||||
/* jshint maxcomplexity:20 */
|
||||
Object.entries(Object.assign({}, ...attributes)).forEach(function assignNth([ key, attr ]){
|
||||
attr= s.processReactiveAttribute(element, key, attr, assignNth);
|
||||
const [ k ]= key;
|
||||
if("="===k) return setRemoveAttr(key.slice(1), attr);
|
||||
if("."===k) return setDelete(element, key.slice(1), attr);
|
||||
if(typeof attr === "object" && attr!==null){
|
||||
if(typeof attr === "object" && attr!==null && !Array.isArray(attr)){
|
||||
switch(key){
|
||||
case "style": return forEachEntries(attr, setRemove.bind(null, element.style, "Property"));
|
||||
case "dataset": return forEachEntries(attr, setDelete.bind(null, element.dataset));
|
||||
case "ariaset": return forEachEntries(attr, (key, val)=> setRemoveAttr("aria-"+key, val));
|
||||
case "classList": return classListDeclarative(element, attr);
|
||||
default: return Reflect.set(element, key, attr);
|
||||
case "style" || "dataset": return forEachEntries(s, attr, setDelete.bind(null, element[key]));
|
||||
case "ariaset": return forEachEntries(s, attr, (key, val)=> setRemoveAttr("aria-"+key, val));
|
||||
case "classList": return classListDeclarative.call(_this, element, attr);
|
||||
default: return Reflect.set(element, key, attr);
|
||||
}
|
||||
}
|
||||
if(/(aria|data)([A-Z])/.test(key)){
|
||||
key= key.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
||||
return setRemoveAttr(key, attr);
|
||||
}
|
||||
if("className"===key) key= "class";
|
||||
switch(key){
|
||||
case "href" || "src":
|
||||
return setRemoveAttr(key, attr);
|
||||
case "xlink:href":
|
||||
case "href" || "src" || "class" || "xlink:href":
|
||||
return setRemoveAttr(key, attr, "http://www.w3.org/1999/xlink");
|
||||
case "textContent" || "innerText":
|
||||
if(!is_svg) break;
|
||||
if(!is_svg) return setDeleteAttr(element, key, attr);
|
||||
return element.appendChild(document.createTextNode(attr));
|
||||
}
|
||||
return isPropSetter(element, key) ? setDeleteAttr(element, key, attr) : setRemoveAttr(key, attr);
|
||||
@ -76,9 +75,10 @@ export function assign(element, ...attributes){
|
||||
return element;
|
||||
}
|
||||
export function classListDeclarative(element, toggle){
|
||||
const s= signals(this);
|
||||
if(typeof toggle !== "object") return element;
|
||||
|
||||
forEachEntries(toggle,
|
||||
forEachEntries(s, toggle,
|
||||
(class_name, val)=>
|
||||
element.classList.toggle(class_name, val===-1 ? undefined : Boolean(val)));
|
||||
return element;
|
||||
@ -89,28 +89,28 @@ export function empty(el){
|
||||
}
|
||||
import { isUndef } from "./helpers.js";
|
||||
function isPropSetter(el, key){
|
||||
const cache_key_he= "HTMLElement,"+key;
|
||||
if(el instanceof HTMLElement && prop_cache.has(cache_key_he))
|
||||
return prop_cache.get(cache_key_he);
|
||||
const cache_key= el.nodeName+","+key;
|
||||
if(prop_cache.has(cache_key)) return prop_cache.get(cache_key);
|
||||
const [ des, level, p ]= getPropDescriptor(el, key);
|
||||
const is_set= !isUndef(des.set);
|
||||
if(!is_set || level)
|
||||
prop_cache.set(p===HTMLElement.prototype ? cache_key_he : cache_key, is_set);
|
||||
return is_set;
|
||||
if(!Reflect.has(el, key)) return false;
|
||||
const des= getPropDescriptor(el, key);
|
||||
return !isUndef(des.set);
|
||||
}
|
||||
function getPropDescriptor(p, key, level= 0){
|
||||
p= Object.getPrototypeOf(p);
|
||||
if(!p) return [ {}, level, p ];
|
||||
if(!p) return {};
|
||||
const des= Object.getOwnPropertyDescriptor(p, key);
|
||||
if(!des) return getPropDescriptor(p, key, level+1);
|
||||
return [ des, level, p ];
|
||||
return des;
|
||||
}
|
||||
|
||||
/** @template {Record<any, any>} T @param {T} obj @param {(param: [ keyof T, T[keyof T] ])=> void} cb */
|
||||
function forEachEntries(obj, cb){ return Object.entries(obj).forEach(([ key, val ])=> key && cb(key, val)); }
|
||||
/** @template {Record<any, any>} T @param {object} s @param {T} obj @param {(param: [ keyof T, T[keyof T] ])=> void} cb */
|
||||
function forEachEntries(s, obj, cb){
|
||||
return Object.entries(obj).forEach(function process([ key, val ]){
|
||||
if(!key) return;
|
||||
val= s.processReactiveAttribute(obj, key, val, a=> cb(...a));
|
||||
cb(key, val);
|
||||
});
|
||||
}
|
||||
|
||||
function setRemove(obj, prop, key, val){ return obj[ (isUndef(val) ? "remove" : "set") + prop ](key, val); }
|
||||
function setRemoveNS(obj, prop, key, val, ns= null){ return obj[ (isUndef(val) ? "remove" : "set") + prop + "NS" ](ns, key, val); }
|
||||
function attrArrToStr(attr){ return Array.isArray(attr) ? attr.filter(Boolean).join(" ") : attr; }
|
||||
function setRemove(obj, prop, key, val){ return obj[ (isUndef(val) ? "remove" : "set") + prop ](key, attrArrToStr(val)); }
|
||||
function setRemoveNS(obj, prop, key, val, ns= null){ return obj[ (isUndef(val) ? "remove" : "set") + prop + "NS" ](ns, key, attrArrToStr(val)); }
|
||||
function setDelete(obj, key, val){ Reflect.set(obj, key, val); if(!isUndef(val)) return; return Reflect.deleteProperty(obj, key); }
|
||||
|
@ -1,6 +1,6 @@
|
||||
export const signals_global= {
|
||||
isTextContent(attributes){ return typeof attributes==="string"; },
|
||||
processReactiveAttribute(el, key, attr, assignNth){ return attr; },
|
||||
isSignal(attributes){ return false; },
|
||||
processReactiveAttribute(obj, key, attr, assignNth){ return attr; },
|
||||
};
|
||||
export function registerReactivity(def, global= true){
|
||||
if(global) return Object.assign(signals_global, def);
|
||||
|
@ -84,9 +84,7 @@ S.el= function(signal, map){
|
||||
|
||||
import { typeOf } from './helpers.js';
|
||||
export const signals_config= {
|
||||
isTextContent(attributes){
|
||||
return typeOf(attributes)==="string" || ( isSignal(attributes) && typeOf(valueOfSignal(attributes))==="string" );
|
||||
},
|
||||
isSignal,
|
||||
processReactiveAttribute(_, key, attrs, assignNth){
|
||||
//TODO DOC: once the signal is used as attribute, there is no reason to use assign again (if for some reason needed, use imperative listeners clear with `S.clear`)
|
||||
if(!isSignal(attrs)) return attrs;
|
||||
|
Reference in New Issue
Block a user