sweetpea
Version:
Signal and Web Component Enhanced Web Apps
101 lines (78 loc) • 2.62 kB
JavaScript
import Signal from 'signal';
export default function main (el){
const dataStore = new DataStore(el);
return new Proxy(dataStore, {
get(dataStore, key) {
if (key === 'destroy') return ()=>dataStore.destroy();
if (key === 'parameters') return dataStore.parameters();
return dataStore.getSignal(key);
},
set(dataStore, key, value) {
dataStore.updateSignal(key, value);
},
});
} // main
class DataStore {
signals = {};
#el;
#observer;
constructor(el) {
this.#el = el;
const callback = (mutationList, observer) => {
// console.log(mutationList);
for (const mutation of mutationList) {
if (mutation.type === "attributes") {
// console.log(`mutation.attributeName: ${mutation.attributeName}`)
if (mutation.attributeName.startsWith('data-')) {
const [,key] = mutation.attributeName.split('-');
const incomingValue = mutation.target.getAttribute(mutation.attributeName);
const signalObject = Object.entries(this.signals).find(([k, v]) => key == k.toLowerCase())[1];
const signalValue = signalObject.value;
// console.log(`The ${mutation.attributeName} attribute was modified`);
if (incomingValue !== signalValue){
console.info('External attribute modification', {incomingValue, signalValue})
signalObject.value = incomingValue;
}
}
}
}
};
this.#observer = new MutationObserver(callback);
this.#observer.observe(this.#el, { attributes: true });
}
subscriptions = [];
collectGarbage(){
this.subscriptions.map(s=>s.subscription())
}
set gc(subscription){ // shorthand for component level garbage collection
this.subscriptions.push( {type:'gc-standard', id:'gc-'+this.subscriptions.length, subscription} );
}
destroy() {
this.#observer.disconnect();
this.collectGarbage();
}
ensureSignal(key) {
const missing = !this.signals[key];
if (missing) {
this.signals[key] = new Signal(this.#el.getAttribute(`data-${key}`));
}
this.gc = this.signals[key].subscribe(v => {
if (v !== this.#el.getAttribute(`data-${key}`)) this.#el.setAttribute(`data-${key}`, v);
})
return this.signals[key];
}
getSignal(key) {
return this.ensureSignal(key);
}
updateSignal(key, value) {
this.ensureSignal(key).value = value;
return null;
}
parameters() {
const response = {};
for (const key in this.signals) {
response[key] = this.signals[key].value;
}
return response;
}
}