mirror of
https://github.com/jaandrle/deka-dom-el
synced 2024-11-24 01:29:36 +01:00
local
This commit is contained in:
parent
13c75fede1
commit
b326c0e050
@ -12,9 +12,7 @@ export class CustomHTMLTestElement extends HTMLElement{
|
|||||||
}
|
}
|
||||||
connectedCallback(){
|
connectedCallback(){
|
||||||
if(!this.hasAttribute("pre-name")) this.setAttribute("pre-name", "default");
|
if(!this.hasAttribute("pre-name")) this.setAttribute("pre-name", "default");
|
||||||
this.attachShadow({ mode: "open" }).append(
|
customElementRender(this, this.attachShadow({ mode: "open" }), this.render, this.attributes)
|
||||||
customElementRender(this, this.render, this.attributes)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes(element){
|
attributes(element){
|
||||||
@ -23,6 +21,7 @@ export class CustomHTMLTestElement extends HTMLElement{
|
|||||||
}
|
}
|
||||||
render({ name, preName, test }){
|
render({ name, preName, test }){
|
||||||
console.log(scope.state);
|
console.log(scope.state);
|
||||||
|
console.log({ name, preName, test });
|
||||||
scope.host(
|
scope.host(
|
||||||
on.connected(()=> console.log(CustomHTMLTestElement)),
|
on.connected(()=> console.log(CustomHTMLTestElement)),
|
||||||
on.attributeChanged(e=> console.log(e)),
|
on.attributeChanged(e=> console.log(e)),
|
||||||
@ -63,7 +62,7 @@ export class CustomSlottingHTMLElement extends HTMLElement{
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
connectedCallback(){
|
connectedCallback(){
|
||||||
this.append(customElementRender(this, this.render));
|
customElementRender(this, this, this.render);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElementWithDDE(CustomSlottingHTMLElement);
|
customElementWithDDE(CustomSlottingHTMLElement);
|
||||||
|
1
index.d.ts
vendored
1
index.d.ts
vendored
@ -180,6 +180,7 @@ export function customElementRender<
|
|||||||
P extends any = Record<string, any>
|
P extends any = Record<string, any>
|
||||||
>(
|
>(
|
||||||
custom_element: EL,
|
custom_element: EL,
|
||||||
|
target: ShadowRoot | EL,
|
||||||
render: (props: P)=> SupportedElement,
|
render: (props: P)=> SupportedElement,
|
||||||
props?: P | ((...args: any[])=> P)
|
props?: P | ((...args: any[])=> P)
|
||||||
): EL
|
): EL
|
||||||
|
3
observables.d.ts
vendored
3
observables.d.ts
vendored
@ -3,6 +3,7 @@ type Action<V>= (this: { value: V, stopPropagation(): void }, ...a: any[])=> typ
|
|||||||
//type SymbolObservable= Symbol;
|
//type SymbolObservable= Symbol;
|
||||||
type SymbolOnclear= symbol;
|
type SymbolOnclear= symbol;
|
||||||
type Actions<V>= Record<string | SymbolOnclear, Action<V>>;
|
type Actions<V>= Record<string | SymbolOnclear, Action<V>>;
|
||||||
|
type OnListenerOptions= Pick<AddEventListenerOptions, "signal"> & { first_time?: boolean };
|
||||||
interface observable{
|
interface observable{
|
||||||
_: Symbol
|
_: Symbol
|
||||||
/**
|
/**
|
||||||
@ -40,7 +41,7 @@ interface observable{
|
|||||||
...params: A[N] extends (...args: infer P)=> any ? P : never
|
...params: A[N] extends (...args: infer P)=> any ? P : never
|
||||||
): void;
|
): void;
|
||||||
clear(...observables: Observable<any, any>[]): void;
|
clear(...observables: Observable<any, any>[]): void;
|
||||||
on<T>(observable: Observable<T, any>, onchange: (a: T)=> void, options?: AddEventListenerOptions): void;
|
on<T>(observable: Observable<T, any>, onchange: (a: T)=> void, options?: OnListenerOptions): void;
|
||||||
symbols: {
|
symbols: {
|
||||||
//observable: SymbolObservable;
|
//observable: SymbolObservable;
|
||||||
onclear: SymbolOnclear;
|
onclear: SymbolOnclear;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "deka-dom-el",
|
"name": "deka-dom-el",
|
||||||
"version": "0.7.7",
|
"version": "0.7.8",
|
||||||
"description": "A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks.",
|
"description": "A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks.",
|
||||||
"author": "Jan Andrle <andrle.jan@centrum.cz>",
|
"author": "Jan Andrle <andrle.jan@centrum.cz>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -59,14 +59,14 @@
|
|||||||
"size-limit": [
|
"size-limit": [
|
||||||
{
|
{
|
||||||
"path": "./index.js",
|
"path": "./index.js",
|
||||||
"limit": "10 kB",
|
"limit": "10.5 kB",
|
||||||
"gzip": false,
|
"gzip": false,
|
||||||
"brotli": false
|
"brotli": false
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "./observables.js",
|
"path": "./observables.js",
|
||||||
"limit": "11.5 kB",
|
"limit": "12 kB",
|
||||||
"gzip": false,
|
"gzip": false,
|
||||||
"brotli": false
|
"brotli": false
|
||||||
|
|
||||||
|
@ -1,30 +1,40 @@
|
|||||||
|
import { keyLTE } from "./dom-common.js";
|
||||||
import { scope } from "./dom.js";
|
import { scope } from "./dom.js";
|
||||||
export function customElementRender(custom_element, render, props= observedAttributes){
|
import { c_ch_o } from "./events-observer.js";
|
||||||
|
export function customElementRender(custom_element, target, render, props= observedAttributes){
|
||||||
scope.push({
|
scope.push({
|
||||||
scope: custom_element,
|
scope: custom_element,
|
||||||
host: (...c)=> c.length ? c.forEach(c=> c(custom_element)) : custom_element,
|
host: (...c)=> c.length ? c.forEach(c=> c(custom_element)) : custom_element
|
||||||
custom_element
|
|
||||||
});
|
});
|
||||||
if(typeof props==="function") props= props.call(custom_element, custom_element);
|
if(typeof props==="function") props= props.call(custom_element, custom_element);
|
||||||
|
const is_lte= custom_element[keyLTE];
|
||||||
|
if(!is_lte) lifecycleToEvents(custom_element);
|
||||||
const out= render.call(custom_element, props);
|
const out= render.call(custom_element, props);
|
||||||
|
if(!is_lte) custom_element.dispatchEvent(new Event("dde:connected"));
|
||||||
|
if(target.nodeType===11 && typeof target.mode==="string") // is ShadowRoot
|
||||||
|
custom_element.addEventListener("dde:disconnected", c_ch_o.observe(target), { once: true });
|
||||||
scope.pop();
|
scope.pop();
|
||||||
return out;
|
return target.append(out);
|
||||||
}
|
}
|
||||||
export function lifecycleToEvents(class_declaration){
|
export function lifecycleToEvents(class_declaration){
|
||||||
for (const name of [ "connected", "disconnected" ])
|
wrapMethod(class_declaration.prototype, "connectedCallback", function(target, thisArg, detail){
|
||||||
wrapMethod(class_declaration.prototype, name+"Callback", function(target, thisArg, detail){
|
target.apply(thisArg, detail);
|
||||||
target.apply(thisArg, detail);
|
thisArg.dispatchEvent(new Event("dde:connected"));
|
||||||
thisArg.dispatchEvent(new Event("dde:"+name));
|
});
|
||||||
});
|
wrapMethod(class_declaration.prototype, "disconnectedCallback", function(target, thisArg, detail){
|
||||||
const name= "attributeChanged";
|
target.apply(thisArg, detail);
|
||||||
wrapMethod(class_declaration.prototype, name+"Callback", function(target, thisArg, detail){
|
(queueMicrotask || setTimeout)(
|
||||||
|
()=> !thisArg.isConnected && thisArg.dispatchEvent(new Event("dde:disconnected"))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
wrapMethod(class_declaration.prototype, "attributeChangedCallback", function(target, thisArg, detail){
|
||||||
const [ attribute, , value ]= detail;
|
const [ attribute, , value ]= detail;
|
||||||
thisArg.dispatchEvent(new CustomEvent("dde:"+name, {
|
thisArg.dispatchEvent(new CustomEvent("dde:attributeChanged", {
|
||||||
detail: [ attribute, value ]
|
detail: [ attribute, value ]
|
||||||
}));
|
}));
|
||||||
target.apply(thisArg, detail);
|
target.apply(thisArg, detail);
|
||||||
});
|
});
|
||||||
class_declaration.prototype.__dde_lifecycleToEvents= true;
|
class_declaration.prototype[keyLTE]= true;
|
||||||
return class_declaration;
|
return class_declaration;
|
||||||
}
|
}
|
||||||
export { lifecycleToEvents as customElementWithDDE };
|
export { lifecycleToEvents as customElementWithDDE };
|
||||||
|
@ -26,3 +26,4 @@ function setDeleteAttr(obj, prop, val){
|
|||||||
if(Reflect.get(obj, prop)==="undefined")
|
if(Reflect.get(obj, prop)==="undefined")
|
||||||
return Reflect.set(obj, prop, "");
|
return Reflect.set(obj, prop, "");
|
||||||
}
|
}
|
||||||
|
export const keyLTE= "__dde_lifecycleToEvents"; //boolean
|
@ -5,7 +5,6 @@ import { enviroment as env } from './dom-common.js';
|
|||||||
const scopes= [ {
|
const scopes= [ {
|
||||||
get scope(){ return env.D.body; },
|
get scope(){ return env.D.body; },
|
||||||
host: c=> c ? c(env.D.body) : env.D.body,
|
host: c=> c ? c(env.D.body) : env.D.body,
|
||||||
custom_element: false,
|
|
||||||
prevent: true,
|
prevent: true,
|
||||||
} ];
|
} ];
|
||||||
export const scope= {
|
export const scope= {
|
||||||
|
138
src/events-observer.js
Normal file
138
src/events-observer.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
import { enviroment as env } from './dom-common.js';
|
||||||
|
export const c_ch_o= env.M ? connectionsChangesObserverConstructor() : new Proxy({}, {
|
||||||
|
get(){ return ()=> {}; }
|
||||||
|
});
|
||||||
|
|
||||||
|
function connectionsChangesObserverConstructor(){
|
||||||
|
const store= new Map();
|
||||||
|
let is_observing= false;
|
||||||
|
const observerListener= stop=> function(mutations){
|
||||||
|
for(const mutation of mutations){
|
||||||
|
if(mutation.type!=="childList") continue;
|
||||||
|
if(observerAdded(mutation.addedNodes, true)){
|
||||||
|
stop();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(observerRemoved(mutation.removedNodes, true))
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const observer= new env.M(observerListener(stop));
|
||||||
|
return {
|
||||||
|
observe(element){
|
||||||
|
const o= new env.M(observerListener(()=> {}));
|
||||||
|
o.observe(element, { childList: true, subtree: true });
|
||||||
|
return ()=> o.disconnect();
|
||||||
|
},
|
||||||
|
onConnected(element, listener){
|
||||||
|
start();
|
||||||
|
const listeners= getElementStore(element);
|
||||||
|
if(listeners.connected.has(listener)) return;
|
||||||
|
listeners.connected.add(listener);
|
||||||
|
listeners.length_c+= 1;
|
||||||
|
},
|
||||||
|
offConnected(element, listener){
|
||||||
|
if(!store.has(element)) return;
|
||||||
|
const ls= store.get(element);
|
||||||
|
if(!ls.connected.has(listener)) return;
|
||||||
|
ls.connected.delete(listener);
|
||||||
|
ls.length_c-= 1;
|
||||||
|
cleanWhenOff(element, ls);
|
||||||
|
},
|
||||||
|
onDisconnected(element, listener){
|
||||||
|
start();
|
||||||
|
const listeners= getElementStore(element);
|
||||||
|
if(listeners.disconnected.has(listener)) return;
|
||||||
|
listeners.disconnected.add(listener);
|
||||||
|
listeners.length_d+= 1;
|
||||||
|
},
|
||||||
|
offDisconnected(element, listener){
|
||||||
|
if(!store.has(element)) return;
|
||||||
|
const ls= store.get(element);
|
||||||
|
if(!ls.disconnected.has(listener)) return;
|
||||||
|
ls.disconnected.delete(listener);
|
||||||
|
ls.length_d-= 1;
|
||||||
|
cleanWhenOff(element, ls);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function cleanWhenOff(element, ls){
|
||||||
|
if(ls.length_c || ls.length_d)
|
||||||
|
return;
|
||||||
|
store.delete(element);
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
function getElementStore(element){
|
||||||
|
if(store.has(element)) return store.get(element);
|
||||||
|
const out= {
|
||||||
|
connected: new WeakSet(),
|
||||||
|
length_c: 0,
|
||||||
|
disconnected: new WeakSet(),
|
||||||
|
length_d: 0
|
||||||
|
};
|
||||||
|
store.set(element, out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
function start(){
|
||||||
|
if(is_observing) return;
|
||||||
|
is_observing= true;
|
||||||
|
observer.observe(env.D.body, { childList: true, subtree: true });
|
||||||
|
}
|
||||||
|
function stop(){
|
||||||
|
if(!is_observing || store.size) return;
|
||||||
|
is_observing= false;
|
||||||
|
observer.disconnect();
|
||||||
|
}
|
||||||
|
//TODO remount support?
|
||||||
|
function requestIdle(){ return new Promise(function(resolve){
|
||||||
|
(requestIdleCallback || requestAnimationFrame)(resolve);
|
||||||
|
}); }
|
||||||
|
async function collectChildren(element){
|
||||||
|
if(store.size > 30)//TODO limit?
|
||||||
|
await requestIdle();
|
||||||
|
const out= [];
|
||||||
|
if(!(element instanceof Node)) return out;
|
||||||
|
for(const el of store.keys()){
|
||||||
|
if(el===element || !(el instanceof Node)) continue;
|
||||||
|
if(element.contains(el))
|
||||||
|
out.push(el);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
function observerAdded(addedNodes, is_root){
|
||||||
|
let out= false;
|
||||||
|
for(const element of addedNodes){
|
||||||
|
if(is_root) collectChildren(element).then(observerAdded);
|
||||||
|
if(!store.has(element)) continue;
|
||||||
|
|
||||||
|
const ls= store.get(element);
|
||||||
|
if(!ls.length_c) continue;
|
||||||
|
|
||||||
|
element.dispatchEvent(new Event("dde:connected"));
|
||||||
|
ls.connected= new WeakSet();
|
||||||
|
ls.length_c= 0;
|
||||||
|
if(!ls.length_d) store.delete(element);
|
||||||
|
out= true;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
function observerRemoved(removedNodes, is_root){
|
||||||
|
let out= false;
|
||||||
|
for(const element of removedNodes){
|
||||||
|
if(is_root) collectChildren(element).then(observerRemoved);
|
||||||
|
if(!store.has(element)) continue;
|
||||||
|
|
||||||
|
const ls= store.get(element);
|
||||||
|
if(!ls.length_d) continue;
|
||||||
|
(queueMicrotask || setTimeout)(dispatchRemove(element));
|
||||||
|
out= true;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
function dispatchRemove(element){
|
||||||
|
return ()=> {
|
||||||
|
if(element.isConnected) return;
|
||||||
|
element.dispatchEvent(new Event("dde:disconnected"));
|
||||||
|
store.delete(element);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
137
src/events.js
137
src/events.js
@ -1,5 +1,5 @@
|
|||||||
export { registerReactivity } from './observables-common.js';
|
export { registerReactivity } from './observables-common.js';
|
||||||
import { enviroment as env } from './dom-common.js';
|
import { enviroment as env, keyLTE } from './dom-common.js';
|
||||||
|
|
||||||
export function dispatchEvent(name, options, host){
|
export function dispatchEvent(name, options, host){
|
||||||
if(!options) options= {};
|
if(!options) options= {};
|
||||||
@ -19,13 +19,12 @@ export function on(event, listener, options){
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const c_ch_o= env.M ? connectionsChangesObserverConstructor() : new Proxy({}, {
|
import { c_ch_o } from "./events-observer.js";
|
||||||
get(){ return ()=> {}; }
|
|
||||||
});
|
|
||||||
const els_attribute_store= new WeakSet();
|
const els_attribute_store= new WeakSet();
|
||||||
import { scope } from "./dom.js";
|
import { scope } from "./dom.js";
|
||||||
import { onAbort } from './helpers.js';
|
import { onAbort } from './helpers.js';
|
||||||
//TODO: cleanUp when event before abort?
|
//TODO: cleanUp when event before abort?
|
||||||
|
//TODO: docs (e.g.) https://nolanlawson.com/2024/01/13/web-component-gotcha-constructor-vs-connectedcallback/
|
||||||
on.connected= function(listener, options){
|
on.connected= function(listener, options){
|
||||||
const { custom_element }= scope.current;
|
const { custom_element }= scope.current;
|
||||||
const name= "connected";
|
const name= "connected";
|
||||||
@ -36,7 +35,7 @@ on.connected= function(listener, options){
|
|||||||
if(custom_element) element= custom_element;
|
if(custom_element) element= custom_element;
|
||||||
const event= "dde:"+name;
|
const event= "dde:"+name;
|
||||||
element.addEventListener(event, listener, options);
|
element.addEventListener(event, listener, options);
|
||||||
if(element.__dde_lifecycleToEvents) return element;
|
if(element[keyLTE]) return element;
|
||||||
if(element.isConnected) return ( element.dispatchEvent(new Event(event)), element );
|
if(element.isConnected) return ( element.dispatchEvent(new Event(event)), element );
|
||||||
|
|
||||||
const c= onAbort(options.signal, ()=> c_ch_o.offConnected(element, listener));
|
const c= onAbort(options.signal, ()=> c_ch_o.offConnected(element, listener));
|
||||||
@ -54,7 +53,7 @@ on.disconnected= function(listener, options){
|
|||||||
if(custom_element) element= custom_element;
|
if(custom_element) element= custom_element;
|
||||||
const event= "dde:"+name;
|
const event= "dde:"+name;
|
||||||
element.addEventListener(event, listener, options);
|
element.addEventListener(event, listener, options);
|
||||||
if(element.__dde_lifecycleToEvents) return element;
|
if(element[keyLTE]) return element;
|
||||||
|
|
||||||
const c= onAbort(options.signal, ()=> c_ch_o.offDisconnected(element, listener));
|
const c= onAbort(options.signal, ()=> c_ch_o.offDisconnected(element, listener));
|
||||||
if(c) c_ch_o.onDisconnected(element, listener);
|
if(c) c_ch_o.onDisconnected(element, listener);
|
||||||
@ -77,7 +76,7 @@ on.attributeChanged= function(listener, options){
|
|||||||
return function registerElement(element){
|
return function registerElement(element){
|
||||||
const event= "dde:"+name;
|
const event= "dde:"+name;
|
||||||
element.addEventListener(event, listener, options);
|
element.addEventListener(event, listener, options);
|
||||||
if(element.__dde_lifecycleToEvents || els_attribute_store.has(element))
|
if(element[keyLTE] || els_attribute_store.has(element))
|
||||||
return element;
|
return element;
|
||||||
|
|
||||||
if(!env.M) return element;
|
if(!env.M) return element;
|
||||||
@ -93,127 +92,3 @@ on.attributeChanged= function(listener, options){
|
|||||||
return element;
|
return element;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
function connectionsChangesObserverConstructor(){
|
|
||||||
const store= new Map();
|
|
||||||
let is_observing= false;
|
|
||||||
const observer= new env.M(function(mutations){
|
|
||||||
for(const mutation of mutations){
|
|
||||||
if(mutation.type!=="childList") continue;
|
|
||||||
if(observerAdded(mutation.addedNodes, true)){
|
|
||||||
stop();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(observerRemoved(mutation.removedNodes, true))
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
onConnected(element, listener){
|
|
||||||
start();
|
|
||||||
const listeners= getElementStore(element);
|
|
||||||
if(listeners.connected.has(listener)) return;
|
|
||||||
listeners.connected.add(listener);
|
|
||||||
listeners.length_c+= 1;
|
|
||||||
},
|
|
||||||
offConnected(element, listener){
|
|
||||||
if(!store.has(element)) return;
|
|
||||||
const ls= store.get(element);
|
|
||||||
if(!ls.connected.has(listener)) return;
|
|
||||||
ls.connected.delete(listener);
|
|
||||||
ls.length_c-= 1;
|
|
||||||
cleanWhenOff(element, ls);
|
|
||||||
},
|
|
||||||
onDisconnected(element, listener){
|
|
||||||
start();
|
|
||||||
const listeners= getElementStore(element);
|
|
||||||
if(listeners.disconnected.has(listener)) return;
|
|
||||||
listeners.disconnected.add(listener);
|
|
||||||
listeners.length_d+= 1;
|
|
||||||
},
|
|
||||||
offDisconnected(element, listener){
|
|
||||||
if(!store.has(element)) return;
|
|
||||||
const ls= store.get(element);
|
|
||||||
if(!ls.disconnected.has(listener)) return;
|
|
||||||
ls.disconnected.delete(listener);
|
|
||||||
ls.length_d-= 1;
|
|
||||||
cleanWhenOff(element, ls);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
function cleanWhenOff(element, ls){
|
|
||||||
if(ls.length_c || ls.length_d)
|
|
||||||
return;
|
|
||||||
store.delete(element);
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
function getElementStore(element){
|
|
||||||
if(store.has(element)) return store.get(element);
|
|
||||||
const out= {
|
|
||||||
connected: new WeakSet(),
|
|
||||||
length_c: 0,
|
|
||||||
disconnected: new WeakSet(),
|
|
||||||
length_d: 0
|
|
||||||
};
|
|
||||||
store.set(element, out);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
function start(){
|
|
||||||
if(is_observing) return;
|
|
||||||
is_observing= true;
|
|
||||||
observer.observe(env.D.body, { childList: true, subtree: true });
|
|
||||||
}
|
|
||||||
function stop(){
|
|
||||||
if(!is_observing || store.size) return;
|
|
||||||
is_observing= false;
|
|
||||||
observer.disconnect();
|
|
||||||
}
|
|
||||||
//TODO remount support?
|
|
||||||
function requestIdle(){ return new Promise(function(resolve){
|
|
||||||
(requestIdleCallback || requestAnimationFrame)(resolve);
|
|
||||||
}); }
|
|
||||||
async function collectChildren(element, filter){
|
|
||||||
if(store.size > 30)//TODO limit?
|
|
||||||
await requestIdle();
|
|
||||||
const out= [];
|
|
||||||
if(!(element instanceof Node)) return out;
|
|
||||||
for(const el of store.keys()){
|
|
||||||
if(el===element || !(el instanceof Node) || filter(el)) continue;
|
|
||||||
if(element.contains(el))
|
|
||||||
out.push(el);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
function observerAdded(addedNodes, is_root){
|
|
||||||
let out= false;
|
|
||||||
for(const element of addedNodes){
|
|
||||||
if(is_root) collectChildren(element, el=> !el.isConnectedd).then(observerAdded);
|
|
||||||
if(!store.has(element)) continue;
|
|
||||||
|
|
||||||
const ls= store.get(element);
|
|
||||||
if(!ls.length_c) continue;
|
|
||||||
|
|
||||||
element.dispatchEvent(new Event("dde:connected"));
|
|
||||||
ls.connected= new WeakSet();
|
|
||||||
ls.length_c= 0;
|
|
||||||
if(!ls.length_d) store.delete(element);
|
|
||||||
out= true;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
function observerRemoved(removedNodes, is_root){
|
|
||||||
let out= false;
|
|
||||||
for(const element of removedNodes){
|
|
||||||
if(is_root) collectChildren(element, el=> el.isConnectedd).then(observerRemoved);
|
|
||||||
if(!store.has(element)) continue;
|
|
||||||
|
|
||||||
const ls= store.get(element);
|
|
||||||
if(!ls.length_d) continue;
|
|
||||||
|
|
||||||
element.dispatchEvent(new Event("dde:disconnected"));
|
|
||||||
|
|
||||||
store.delete(element);
|
|
||||||
out= true;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -96,7 +96,7 @@ observable.el= function(o, map){
|
|||||||
out.append(mark_start, mark_end);
|
out.append(mark_start, mark_end);
|
||||||
const { current }= scope;
|
const { current }= scope;
|
||||||
const reRenderReactiveElement= v=> {
|
const reRenderReactiveElement= v=> {
|
||||||
if(!mark_start.parentNode || !mark_end.parentNode)
|
if(!mark_start.parentNode || !mark_end.parentNode) // isConnected or wasn’t yet rendered
|
||||||
return removeObservableListener(o, reRenderReactiveElement);
|
return removeObservableListener(o, reRenderReactiveElement);
|
||||||
scope.push(current);
|
scope.push(current);
|
||||||
let els= map(v);
|
let els= map(v);
|
||||||
@ -107,12 +107,21 @@ observable.el= function(o, map){
|
|||||||
while(( el_r= mark_start.nextSibling ) !== mark_end)
|
while(( el_r= mark_start.nextSibling ) !== mark_end)
|
||||||
el_r.remove();
|
el_r.remove();
|
||||||
mark_start.after(...els);
|
mark_start.after(...els);
|
||||||
|
if(mark_start.isConnected)
|
||||||
|
requestCleanUpReactives(current.host());
|
||||||
};
|
};
|
||||||
addObservableListener(o, reRenderReactiveElement);
|
addObservableListener(o, reRenderReactiveElement);
|
||||||
removeObservablesFromElements(o, reRenderReactiveElement, mark_start, map);
|
removeObservablesFromElements(o, reRenderReactiveElement, mark_start, map);
|
||||||
reRenderReactiveElement(o());
|
reRenderReactiveElement(o());
|
||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
|
function requestCleanUpReactives(host){
|
||||||
|
if(!host || !host[key_reactive]) return;
|
||||||
|
(requestIdleCallback || setTimeout)(function(){
|
||||||
|
host[key_reactive]= host[key_reactive]
|
||||||
|
.filter(([ o, el ])=> el.isConnected ? true : (removeObservableListener(...o), false));
|
||||||
|
});
|
||||||
|
}
|
||||||
import { on } from "./events.js";
|
import { on } from "./events.js";
|
||||||
import { observedAttributes } from "./helpers.js";
|
import { observedAttributes } from "./helpers.js";
|
||||||
const observedAttributeActions= {
|
const observedAttributeActions= {
|
||||||
@ -152,7 +161,11 @@ export const observables_config= {
|
|||||||
isObservable,
|
isObservable,
|
||||||
processReactiveAttribute(element, key, attrs, set){
|
processReactiveAttribute(element, key, attrs, set){
|
||||||
if(!isObservable(attrs)) return attrs;
|
if(!isObservable(attrs)) return attrs;
|
||||||
const l= attr=> set(key, attr);
|
const l= attr=> {
|
||||||
|
if(!element.isConnected)
|
||||||
|
return removeObservableListener(attrs, l);
|
||||||
|
set(key, attr);
|
||||||
|
};
|
||||||
addObservableListener(attrs, l);
|
addObservableListener(attrs, l);
|
||||||
removeObservablesFromElements(attrs, l, element, key);
|
removeObservablesFromElements(attrs, l, element, key);
|
||||||
return attrs();
|
return attrs();
|
||||||
|
Loading…
Reference in New Issue
Block a user