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:
@ -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
|
@ -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
|
@ -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);
|
||||
}
|
@ -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
|
@ -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
57
src/dom-lib/helpers.js
Normal 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
4
src/dom-lib/index.js
Normal 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
84
src/dom-lib/scopes.js
Normal 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));
|
@ -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";
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user