mirror of
https://github.com/jaandrle/deka-dom-el
synced 2024-11-24 01:29:36 +01:00
Memo/reuse in O.el
(#18)
* 🎉 * ✨ Add hasOwn helper and replace `Reflect.*` * Update package.json
This commit is contained in:
parent
a5d43e6925
commit
b740b8e020
52
dist/dde-with-observables.js
vendored
52
dist/dde-with-observables.js
vendored
File diff suppressed because one or more lines are too long
32
dist/dde.js
vendored
32
dist/dde.js
vendored
File diff suppressed because one or more lines are too long
8
dist/esm-with-observables.js
vendored
8
dist/esm-with-observables.js
vendored
File diff suppressed because one or more lines are too long
2
dist/esm.js
vendored
2
dist/esm.js
vendored
File diff suppressed because one or more lines are too long
@ -16,10 +16,12 @@ const className= style.host(todosComponent).css`
|
||||
`;
|
||||
/** @param {{ todos: string[] }} */
|
||||
export function todosComponent({ todos= [ "Task A" ] }= {}){
|
||||
const todosO= O(todos.map(t=> O(t)), {
|
||||
add(v){ this.value.push(O(v)); },
|
||||
remove(i){ O.clear(this.value.splice(i, 1)[0]); }
|
||||
let key= 0;
|
||||
const todosO= O(new Map(), {
|
||||
add(v){ this.value.set(key++, O(v)); },
|
||||
remove(key){ O.clear(this.value.get(key)); this.value.delete(key); }
|
||||
});
|
||||
todos.forEach(text=> O.action(todosO, "add", text));
|
||||
|
||||
const name= "todoName";
|
||||
const onsubmitAdd= on("submit", event=> {
|
||||
@ -31,24 +33,16 @@ export function todosComponent({ todos= [ "Task A" ] }= {}){
|
||||
const onremove= on("remove", event=>
|
||||
O.action(todosO, "remove", event.detail));
|
||||
|
||||
const ul_todos_version= 1; // ul_todos_v1/ul_todos_v2
|
||||
const ul_todos_v1= el("ul").append(
|
||||
O.el(todosO, ts=> ts
|
||||
.map((textContent, value)=>
|
||||
el(todoComponent, { textContent, value, className }, onremove))
|
||||
));
|
||||
const ul_todos_v2= ts=> el("ul").append(
|
||||
...ts.map((textContent, value)=>
|
||||
el(todoComponent, { textContent, value, className }, onremove))
|
||||
);
|
||||
|
||||
return el("div", { className }).append(
|
||||
el("div").append(
|
||||
el("h2", "Todos:"),
|
||||
el("h3", "List of todos:"),
|
||||
O.el(todosO, ts=> !ts.length
|
||||
O.el(todosO, (ts, memo)=> !ts.size
|
||||
? el("p", "No todos yet")
|
||||
: ( !(ul_todos_version-1) ? ul_todos_v1 : ul_todos_v2(ts) )
|
||||
: el("ul").append(
|
||||
...Array.from(ts).map(([ value, textContent ])=>
|
||||
memo(value, ()=> el(todoComponent, { textContent, value, className }, onremove)))
|
||||
)
|
||||
),
|
||||
el("p", "Click to the text to edit it.")
|
||||
),
|
||||
@ -61,7 +55,7 @@ export function todosComponent({ todos= [ "Task A" ] }= {}){
|
||||
),
|
||||
el("div").append(
|
||||
el("h3", "Output (JSON):"),
|
||||
el("output", O(()=> JSON.stringify(todosO, null, "\t")))
|
||||
el("output", O(()=> JSON.stringify(Array.from(todosO()), null, "\t")))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -79,7 +79,7 @@
|
||||
},
|
||||
{
|
||||
"path": "./index-with-observables.js",
|
||||
"limit": "5 kB"
|
||||
"limit": "5.25 kB"
|
||||
}
|
||||
],
|
||||
"modifyEsbuildConfig": {
|
||||
|
@ -65,12 +65,13 @@ export function createElement(tag, attributes, ...addons){
|
||||
scoped= 2;
|
||||
return el;
|
||||
}
|
||||
import { hasOwn } from "./helpers.js";
|
||||
/** @param {HTMLElement} element @param {HTMLElement} [root] */
|
||||
export function simulateSlots(element, root= element, mapper= undefined){
|
||||
const _default= Symbol.for("default");
|
||||
const slots= Array.from(root.querySelectorAll("slot"))
|
||||
.reduce((out, curr)=> Reflect.set(out, curr.name || _default, curr) && out, {});
|
||||
const has_d= Reflect.has(slots, _default);
|
||||
const has_d= hasOwn(slots, _default);
|
||||
element.append= new Proxy(element.append, {
|
||||
apply(orig, _, els){
|
||||
if(!els.length) return element;
|
||||
@ -198,7 +199,7 @@ export function elementAttribute(element, op, key, value){
|
||||
import { isUndef } from "./helpers.js";
|
||||
//TODO add cache? `(Map/Set)<el.tagName+key,isUndef>`
|
||||
function isPropSetter(el, key){
|
||||
if(!Reflect.has(el, key)) return false;
|
||||
if(!(key in el)) return false;
|
||||
const des= getPropDescriptor(el, key);
|
||||
return !isUndef(des.set);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
export const hasOwn= (...a)=> Object.prototype.hasOwnProperty.call(...a);
|
||||
export function isUndef(value){ return typeof value==="undefined"; }
|
||||
export function typeOf(v){
|
||||
const t= typeof v;
|
||||
@ -19,7 +20,7 @@ export function observedAttributes(instance, observedAttribute){
|
||||
const { observedAttributes= [] }= instance.constructor;
|
||||
return observedAttributes
|
||||
.reduce(function(out, name){
|
||||
Reflect.set(out, kebabToCamel(name), observedAttribute(instance, name));
|
||||
out[kebabToCamel(name)]= observedAttribute(instance, name);
|
||||
return out;
|
||||
}, {});
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
export const mark= "__dde_observable";
|
||||
import { hasOwn } from "./helpers.js";
|
||||
|
||||
export function isObservable(candidate){
|
||||
try{ return Reflect.has(candidate, mark); }
|
||||
try{ return hasOwn(candidate, mark); }
|
||||
catch(e){ return false; }
|
||||
}
|
||||
/** @type {function[]} */
|
||||
@ -45,10 +46,10 @@ export function observable(value, actions){
|
||||
export { observable as O };
|
||||
observable.action= function(o, name, ...a){
|
||||
const s= o[mark], { actions }= s;
|
||||
if(!actions || !Reflect.has(actions, name))
|
||||
if(!actions || !(name in actions))
|
||||
throw new Error(`'${o}' has no action with name '${name}'!`);
|
||||
actions[name].apply(s, a);
|
||||
if(s.skip) return Reflect.deleteProperty(s, "skip");
|
||||
if(s.skip) return (delete s.skip);
|
||||
s.listeners.forEach(l=> l(s.value));
|
||||
};
|
||||
observable.on= function on(o, listener, options= {}){
|
||||
@ -65,11 +66,12 @@ observable.symbols= {
|
||||
};
|
||||
observable.clear= function(...observables){
|
||||
for(const o of observables){
|
||||
Reflect.deleteProperty(o, "toJSON");
|
||||
const s= o[mark];
|
||||
if(!s) continue;
|
||||
delete o.toJSON;
|
||||
s.onclear.forEach(f=> f.call(s));
|
||||
clearListDeps(o, s);
|
||||
Reflect.deleteProperty(o, mark);
|
||||
delete o[mark];
|
||||
}
|
||||
function clearListDeps(o, s){
|
||||
s.listeners.forEach(l=> {
|
||||
@ -89,24 +91,38 @@ const key_reactive= "__dde_reactive";
|
||||
import { enviroment as env } from "./dom-common.js";
|
||||
import { el } from "./dom.js";
|
||||
import { scope } from "./dom.js";
|
||||
// TODO: third argument for handle `cache_tmp` in re-render
|
||||
observable.el= function(o, map){
|
||||
const mark_start= el.mark({ type: "reactive" }, true);
|
||||
const mark_end= mark_start.end;
|
||||
const out= env.D.createDocumentFragment();
|
||||
out.append(mark_start, mark_end);
|
||||
const { current }= scope;
|
||||
let cache= {};
|
||||
const reRenderReactiveElement= v=> {
|
||||
if(!mark_start.parentNode || !mark_end.parentNode) // isConnected or wasn’t yet rendered
|
||||
if(!mark_start.parentNode || !mark_end.parentNode) // === `isConnected` or wasn’t yet rendered
|
||||
return removeObservableListener(o, reRenderReactiveElement);
|
||||
const cache_tmp= cache; // will be reused in the useCache or removed in the while loop on the end
|
||||
cache= {};
|
||||
scope.push(current);
|
||||
let els= map(v);
|
||||
let els= map(v, function useCache(key, fun){
|
||||
let value;
|
||||
if(hasOwn(cache_tmp, key)){
|
||||
value= cache_tmp[key];
|
||||
delete cache_tmp[key];
|
||||
} else
|
||||
value= fun();
|
||||
cache[key]= value;
|
||||
return value;
|
||||
});
|
||||
scope.pop();
|
||||
if(!Array.isArray(els))
|
||||
els= [ els ];
|
||||
let el_r= mark_start;
|
||||
while(( el_r= mark_start.nextSibling ) !== mark_end)
|
||||
el_r.remove();
|
||||
mark_start.after(...els);
|
||||
const el_last_keep= els[els.length-1];
|
||||
let el_r;
|
||||
while(( el_r= el_last_keep.nextSibling ) !== mark_end)
|
||||
el_r.remove();
|
||||
if(mark_start.isConnected)
|
||||
requestCleanUpReactives(current.host());
|
||||
};
|
||||
@ -183,7 +199,7 @@ function removeObservablesFromElements(o, listener, ...notes){
|
||||
* You can investigate the `__dde_reactive` key of the element.
|
||||
* */
|
||||
element[key_reactive].forEach(([ [ o, listener ] ])=>
|
||||
removeObservableListener(o, listener, o[mark]?.host() === element))
|
||||
removeObservableListener(o, listener, o[mark] && o[mark].host && o[mark].host() === element))
|
||||
)(element);
|
||||
}
|
||||
element[key_reactive].push([ [ o, listener ], ...notes ]);
|
||||
@ -216,7 +232,7 @@ function toObservable(o, value, actions, readonly= false){
|
||||
const { onclear: ocs }= observable.symbols;
|
||||
if(actions[ocs]){
|
||||
onclear.push(actions[ocs]);
|
||||
Reflect.deleteProperty(actions, ocs);
|
||||
delete actions[ocs];
|
||||
}
|
||||
const { host }= scope;
|
||||
Reflect.defineProperty(o, mark, {
|
||||
@ -270,4 +286,4 @@ function removeObservableListener(o, listener, clear_when_empty){
|
||||
deps.get(c).forEach(sig=> removeObservableListener(sig, c, true));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user