1
0
mirror of https://github.com/jaandrle/deka-dom-el synced 2025-07-01 12:22:15 +02:00

reorganize files

This commit is contained in:
2025-03-14 13:15:31 +01:00
parent 93b905e677
commit 36fab5276d
27 changed files with 712 additions and 597 deletions

View File

@ -21,7 +21,7 @@ export const enviroment= {
M: globalThis.MutationObserver,
q: p=> p || Promise.resolve(),
};
import { isInstance, isUndef } from './helpers.js';
import { isInstance, isUndef } from '../helpers.js';
/**
* Handles attribute setting with special undefined handling

View File

@ -1,6 +1,47 @@
import { keyLTE, evc, evd, eva } from "./dom-common.js";
import { scope } from "./dom.js";
import { keyLTE, evc, evd, eva } from "./common.js";
import { scope } from "./scopes.js";
import { c_ch_o } from "./events-observer.js";
import { elementAttribute } from "./helpers.js";
/**
* Simulates slot functionality for elements
*
* @param {HTMLElement} element - Parent element
* @param {HTMLElement} [root=element] - Root element containing slots
* @returns {HTMLElement} The root element
*/
export function simulateSlots(element, root= element){
const mark_e= "¹⁰", mark_s= "✓"; //NOTE: Markers to identify slots processed by this function. Also “prevents” native behavior as it is unlikely to use these in names. // editorconfig-checker-disable-line
const slots= Object.fromEntries(
Array.from(root.querySelectorAll("slot"))
.filter(s => !s.name.endsWith(mark_e))
.map(s => [(s.name += mark_e), s]));
element.append= new Proxy(element.append, {
apply(orig, _, els){
if(els[0]===root) return orig.apply(element, els);
for(const el of els){
const name= (el.slot||"")+mark_e;
try{ elementAttribute(el, "remove", "slot"); } catch(_error){}
const slot= slots[name];
if(!slot) return;
if(!slot.name.startsWith(mark_s)){
slot.childNodes.forEach(c=> c.remove());
slot.name= mark_s+name;
}
slot.append(el);
//TODO?: el.dispatchEvent(new CustomEvent("dde:slotchange", { detail: slot }));
}
element.append= orig; //TODO?: better memory management, but non-native behavior!
return element;
}
});
if(element!==root){
const els= Array.from(element.childNodes);
//TODO?: els.forEach(el=> el.remove());
element.append(...els);
}
return root;
}
/**
* Renders content into a custom element or shadow root

View File

@ -1,7 +1,6 @@
import { signals } from "./signals-lib/common.js";
import { enviroment as env } from './dom-common.js';
import { isInstance, isUndef, oAssign } from "./helpers.js";
import { on } from "./events.js";
import { signals } from "../signals-lib/common.js";
import { enviroment as env } from './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
@ -11,84 +10,6 @@ import { on } from "./events.js";
*/
export function queue(promise){ return env.q(promise); }
/**
* Array of scope contexts for tracking component hierarchies
* @type {{ scope: object, prevent: boolean, host: function }[]}
*/
const scopes= [ {
get scope(){ return env.D.body; },
host: c=> c ? c(env.D.body) : env.D.body,
prevent: true,
} ];
/** Store for disconnect abort controllers */
const store_abort= new WeakMap();
/**
* Scope management utility for tracking component hierarchies
*/
export const scope= {
/**
* Gets the current scope
* @returns {Object} Current scope context
*/
get current(){ return scopes[scopes.length-1]; },
/**
* Gets the host element of the current scope
* @returns {Function} Host accessor function
*/
get host(){ return this.current.host; },
/**
* Creates/gets an AbortController that triggers when the element disconnects
* */
get signal(){
const { host }= this;
if(store_abort.has(host)) return store_abort.get(host);
const a= new AbortController();
store_abort.set(host, a);
host(on.disconnected(()=> a.abort()));
return a.signal;
},
/**
* Prevents default behavior in the current scope
* @returns {Object} Current scope context
*/
preventDefault(){
const { current }= this;
current.prevent= true;
return current;
},
/**
* Gets a copy of the current scope stack
* @returns {Array} Copy of scope stack
*/
get state(){ 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= {}){ return scopes.push(oAssign({}, this.current, { prevent: false }, s)); },
/**
* Pushes the root scope to the stack
* @returns {number} New length of the scope stack
*/
pushRoot(){ 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(){
if(scopes.length===1) return;
return scopes.pop();
},
};
/**
* Chainable append function for elements
* @private
@ -107,6 +28,7 @@ export function chainableAppend(el){
/** Current namespace for element creation */
let namespace;
import { scope } from "./scopes.js";
/**
* Creates a DOM element with specified tag, attributes and addons
*
@ -191,46 +113,6 @@ export function createElementNS(ns){
/** Alias for createElementNS */
export { createElementNS as elNS };
/**
* Simulates slot functionality for elements
*
* @param {HTMLElement} element - Parent element
* @param {HTMLElement} [root=element] - Root element containing slots
* @returns {HTMLElement} The root element
*/
export function simulateSlots(element, root= element){
const mark_e= "¹⁰", mark_s= "✓"; //NOTE: Markers to identify slots processed by this function. Also “prevents” native behavior as it is unlikely to use these in names. // editorconfig-checker-disable-line
const slots= Object.fromEntries(
Array.from(root.querySelectorAll("slot"))
.filter(s => !s.name.endsWith(mark_e))
.map(s => [(s.name += mark_e), s]));
element.append= new Proxy(element.append, {
apply(orig, _, els){
if(els[0]===root) return orig.apply(element, els);
for(const el of els){
const name= (el.slot||"")+mark_e;
try{ elementAttribute(el, "remove", "slot"); } catch(_error){}
const slot= slots[name];
if(!slot) return;
if(!slot.name.startsWith(mark_s)){
slot.childNodes.forEach(c=> c.remove());
slot.name= mark_s+name;
}
slot.append(el);
//TODO?: el.dispatchEvent(new CustomEvent("dde:slotchange", { detail: slot }));
}
element.append= orig; //TODO?: better memory management, but non-native behavior!
return element;
}
});
if(element!==root){
const els= Array.from(element.childNodes);
//TODO?: els.forEach(el=> el.remove());
element.append(...els);
}
return root;
}
/** Store for element assignment contexts */
const assign_context= new WeakMap();
const { setDeleteAttr }= env;
@ -251,6 +133,7 @@ export function assign(element, ...attributes){
assign_context.delete(element);
return element;
}
import { setDelete } from "./helpers.js";
/**
* Assigns a single attribute to an element
*
@ -290,6 +173,7 @@ export function assignAttribute(element, key, value){
}
return isPropSetter(element, key) ? setDeleteAttr(element, key, value) : setRemoveAttr(key, value);
}
import { setRemove, setRemoveNS } from "./helpers.js";
/**
* Gets or creates assignment context for an element
*
@ -320,21 +204,6 @@ export function classListDeclarative(element, toggle){
return element;
}
/**
* Generic element attribute manipulation
*
* @param {Element} element - Element to manipulate
* @param {string} op - Operation ("set" or "remove")
* @param {string} key - Attribute name
* @param {any} [value] - Attribute value
* @returns {void}
*/
export function elementAttribute(element, op, key, value){
if(isInstance(element, env.H))
return element[op+"Attribute"](key, value);
return element[op+"AttributeNS"](null, key, value);
}
//TODO: add cache? `(Map/Set)<el.tagName+key,isUndef>`
/**
* Checks if a property can be set on an element
@ -385,45 +254,3 @@ function forEachEntries(s, target, element, obj, cb){
cb(key, val);
});
}
/**
* Sets or removes an attribute based on value
*
* @param {Element} obj - Element to modify
* @param {string} prop - Property suffix ("Attribute")
* @param {string} key - Attribute name
* @param {any} val - Attribute value
* @returns {void}
* @private
*/
function setRemove(obj, prop, key, val){
return obj[ (isUndef(val) ? "remove" : "set") + prop ](key, val);
}
/**
* Sets or removes a namespaced attribute based on value
*
* @param {Element} obj - Element to modify
* @param {string} prop - Property suffix ("Attribute")
* @param {string} key - Attribute name
* @param {any} val - Attribute value
* @param {string|null} [ns=null] - Namespace URI
* @returns {void}
* @private
*/
function setRemoveNS(obj, prop, key, val, ns= null){
return obj[ (isUndef(val) ? "remove" : "set") + prop + "NS" ](ns, key, val);
}
/**
* Sets or deletes a property based on value
*
* @param {Object} obj - Object to modify
* @param {string} key - Property name
* @param {any} val - Property value
* @returns {void}
* @private
*/
function setDelete(obj, key, val){
Reflect.set(obj, key, val); if(!isUndef(val)) return; return Reflect.deleteProperty(obj, key);
}

View File

@ -1,5 +1,5 @@
import { enviroment as env, evc, evd } from './dom-common.js';
import { isInstance } from "./helpers.js";
import { enviroment as env, evc, evd } from './common.js';
import { isInstance } from "../helpers.js";
/**
* Connection changes observer for tracking element connection/disconnection

View File

@ -1,5 +1,5 @@
import { keyLTE, evc, evd } from './dom-common.js';
import { oAssign, onAbort } from './helpers.js';
import { keyLTE, evc, evd } from './common.js';
import { oAssign, onAbort } from '../helpers.js';
/**
* Creates a function to dispatch events on elements

57
src/dom-lib/helpers.js Normal file
View File

@ -0,0 +1,57 @@
import { enviroment as env } from './common.js';
import { isInstance, isUndef } from "../helpers.js";
/**
* Sets or removes an attribute based on value
*
* @param {Element} obj - Element to modify
* @param {string} prop - Property suffix ("Attribute")
* @param {string} key - Attribute name
* @param {any} val - Attribute value
* @returns {void}
* @private
*/
export function setRemove(obj, prop, key, val){
return obj[ (isUndef(val) ? "remove" : "set") + prop ](key, val);
}
/**
* Sets or removes a namespaced attribute based on value
*
* @param {Element} obj - Element to modify
* @param {string} prop - Property suffix ("Attribute")
* @param {string} key - Attribute name
* @param {any} val - Attribute value
* @param {string|null} [ns=null] - Namespace URI
* @returns {void}
* @private
*/
export function setRemoveNS(obj, prop, key, val, ns= null){
return obj[ (isUndef(val) ? "remove" : "set") + prop + "NS" ](ns, key, val);
}
/**
* Sets or deletes a property based on value
*
* @param {Object} obj - Object to modify
* @param {string} key - Property name
* @param {any} val - Property value
* @returns {void}
* @private
*/
export function setDelete(obj, key, val){
Reflect.set(obj, key, val); if(!isUndef(val)) return; return Reflect.deleteProperty(obj, key);
}
/**
* Generic element attribute manipulation
*
* @param {Element} element - Element to manipulate
* @param {string} op - Operation ("set" or "remove")
* @param {string} key - Attribute name
* @param {any} [value] - Attribute value
* @returns {void}
*/
export function elementAttribute(element, op, key, value){
if(isInstance(element, env.H))
return element[op+"Attribute"](key, value);
return element[op+"AttributeNS"](null, key, value);
}

4
src/dom-lib/index.js Normal file
View File

@ -0,0 +1,4 @@
export * from "./scopes.js";
export * from "./el.js";
export * from "./events.js";
export * from "./customElement.js";

84
src/dom-lib/scopes.js Normal file
View File

@ -0,0 +1,84 @@
import { enviroment as env } from './common.js';
import { oAssign } from "../helpers.js";
import { on } from "./events.js";
/**
* Array of scope contexts for tracking component hierarchies
* @type {{ scope: object, prevent: boolean, host: function }[]}
*/
const scopes= [ {
get scope(){ return env.D.body; },
host: c=> c ? c(env.D.body) : env.D.body,
prevent: true,
} ];
/** Store for disconnect abort controllers */
const store_abort= new WeakMap();
/**
* Scope management utility for tracking component hierarchies
*/
export const scope= {
/**
* Gets the current scope
* @returns {Object} Current scope context
*/
get current(){ return scopes[scopes.length-1]; },
/**
* Gets the host element of the current scope
* @returns {Function} Host accessor function
*/
get host(){ return this.current.host; },
/**
* Creates/gets an AbortController that triggers when the element disconnects
* */
get signal(){
const { host }= this;
if(store_abort.has(host)) return store_abort.get(host);
const a= new AbortController();
store_abort.set(host, a);
host(on.disconnected(()=> a.abort()));
return a.signal;
},
/**
* Prevents default behavior in the current scope
* @returns {Object} Current scope context
*/
preventDefault(){
const { current }= this;
current.prevent= true;
return current;
},
/**
* Gets a copy of the current scope stack
* @returns {Array} Copy of scope stack
*/
get state(){ 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= {}){ return scopes.push(oAssign({}, this.current, { prevent: false }, s)); },
/**
* Pushes the root scope to the stack
* @returns {number} New length of the scope stack
*/
pushRoot(){ 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(){
if(scopes.length===1) return;
return scopes.pop();
},
};
// TODO: better place while no cross-import?
on.host= (fn, host= scope.host)=> el=> host(()=> fn(el));

View File

@ -159,10 +159,10 @@ signal.clear= function(...signals){
};
/** Property key for tracking reactive elements */
const key_reactive= "__dde_reactive";
import { enviroment as env, eva } from "../dom-common.js";
import { el } from "../dom.js";
import { scope } from "../dom.js";
import { on } from "../events.js";
import { enviroment as env, eva } from "../dom-lib/common.js";
import { el } from "../dom-lib/index.js";
import { scope } from "../dom-lib/scopes.js";
import { on } from "../dom-lib/events.js";
import { memo } from "../memo.js";
/**