From 5c038f0427f231d5305b32ce8be99fe35f84e82e Mon Sep 17 00:00:00 2001 From: Jan Andrle Date: Wed, 8 Nov 2023 18:53:22 +0100 Subject: [PATCH] :hammer: mainly types + :boom: ddePublicElementTagNameMap --- dist/dde-with-signals.js | 16 ++++----- dist/esm-with-signals.d.ts | 54 +++++++++++++++++++++++++++-- dist/esm-with-signals.js | 8 ++--- dist/esm.d.ts | 38 ++++++++++++++++++-- docs/elements.html | 14 ++++---- docs/index.html | 4 +-- docs_src/types.d.ts | 6 ++++ examples/components/webComponent.js | 37 ++++++++------------ examples/index.js | 2 +- index.d.ts | 38 ++++++++++++++++++-- signals.d.ts | 16 ++++++++- src/signals-lib.js | 16 +++++++-- 12 files changed, 195 insertions(+), 54 deletions(-) diff --git a/dist/dde-with-signals.js b/dist/dde-with-signals.js index fe68e7e..6b84714 100644 --- a/dist/dde-with-signals.js +++ b/dist/dde-with-signals.js @@ -1,22 +1,22 @@ //deka-dom-el library is available via global namespace `dde` (()=> { -var y={isSignal(t){return!1},processReactiveAttribute(t,e,n,r){return n}};function W(t,e=!0){return e?Object.assign(y,t):(Object.setPrototypeOf(t,y),t)}function R(t){return y.isPrototypeOf(t)&&t!==y?t:y}function _(t){return typeof t>"u"}function F(t){let e=typeof t;return e!=="object"?e:t===null?"null":Object.prototype.toString.call(t)}function C(t,e){if(!t||!(t instanceof AbortSignal))return!0;if(!t.aborted)return t.addEventListener("abort",e),function(){t.removeEventListener("abort",e)}}var U={setDeleteAttr:X};function X(t,e,n){if(Reflect.set(t,e,n),!!_(n)){if(Reflect.deleteProperty(t,e),t instanceof HTMLElement&&t.getAttribute(e)==="undefined")return t.removeAttribute(e);if(Reflect.get(t,e)==="undefined")return Reflect.set(t,e,"")}}var w=[{scope:document.body,host:t=>t?t(document.body):document.body,prevent:!0}],E={get current(){return w[w.length-1]},get host(){return this.current.host},preventDefault(){let{current:t}=this;return t.prevent=!0,t},get state(){return[...w]},push(t={}){return w.push(Object.assign({},this.current,{prevent:!1},t))},pop(){return w.pop()}},L;function m(t,e,...n){let r=R(this),o=0,c,s;switch((Object(e)!==e||r.isSignal(e))&&(e={textContent:e}),!0){case typeof t=="function":{o=1,E.push({scope:t,host:h=>h?(o===1?n.unshift(h):h(s),void 0):s}),c=t(e||void 0);let d=c instanceof DocumentFragment,a=m.mark({type:"component",name:t.name,host:d?"this":c.nodeName==="#comment"?"previousLater":"parentElement"});c.prepend(a),d&&(s=a);break}case t==="#text":c=N.call(this,document.createTextNode(""),e);break;case(t==="<>"||!t):c=N.call(this,document.createDocumentFragment(),e);break;case L:c=N.call(this,document.createElementNS(L,t),e);break;case!c:c=N.call(this,document.createElement(t),e)}return rt(c),s||(s=c),n.forEach(d=>d(s)),o&&E.pop(),o=2,c}m.mark=function(t,e=!1){t=Object.entries(t).map(([o,c])=>o+`="${c}"`).join(" ");let n=e?"":"/",r=document.createComment(``);return e||(r.end=document.createComment("")),r};m.later=function(){let t=m.mark({type:"later"});return t.append=t.prepend=function(...e){return t.after(...e),t},t};function bt(t){let e=this;return function(...r){L=t;let o=m.call(e,...r);return L=void 0,o}}var{setDeleteAttr:q}=U,j=new WeakMap;function N(t,...e){if(!e.length)return t;j.set(t,J(t,this));for(let[n,r]of Object.entries(Object.assign({},...e)))I.call(this,t,n,r);return j.delete(t),t}function I(t,e,n){let{setRemoveAttr:r,s:o}=J(t,this),c=this;n=o.processReactiveAttribute(t,e,n,(d,a)=>I.call(c,t,d,a));let[s]=e;if(s==="=")return r(e.slice(1),n);if(s===".")return B(t,e.slice(1),n);if(/(aria|data)([A-Z])/.test(e))return e=e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase(),r(e,n);switch(e==="className"&&(e="class"),e){case"xlink:href":return r(e,n,"http://www.w3.org/1999/xlink");case"textContent":return q(t,e,n);case"style":if(typeof n!="object")break;case"dataset":return M(o,n,B.bind(null,t[e]));case"ariaset":return M(o,n,(d,a)=>r("aria-"+d,a));case"classList":return Y.call(c,t,n)}return tt(t,e)?q(t,e,n):r(e,n)}function J(t,e){if(j.has(t))return j.get(t);let r=(t instanceof SVGElement?nt:et).bind(null,t,"Attribute"),o=R(e);return{setRemoveAttr:r,s:o}}function Y(t,e){let n=R(this);return M(n,e,(r,o)=>t.classList.toggle(r,o===-1?void 0:!!o)),t}function _t(t){return Array.from(t.children).forEach(e=>e.remove()),t}function tt(t,e){if(!Reflect.has(t,e))return!1;let n=Z(t,e);return!_(n.set)}function Z(t,e){if(t=Object.getPrototypeOf(t),!t)return{};let n=Object.getOwnPropertyDescriptor(t,e);return n||Z(t,e)}function M(t,e,n){if(!(typeof e!="object"||e===null))return Object.entries(e).forEach(function([o,c]){o&&(c=t.processReactiveAttribute(e,o,c,n),n(o,c))})}function G(t){return Array.isArray(t)?t.filter(Boolean).join(" "):t}function et(t,e,n,r){return t[(_(r)?"remove":"set")+e](n,G(r))}function nt(t,e,n,r,o=null){return t[(_(r)?"remove":"set")+e+"NS"](o,n,G(r))}function B(t,e,n){if(Reflect.set(t,e,n),!!_(n))return Reflect.deleteProperty(t,e)}function H(...t){return this.appendOrig(...t),this}function rt(t){return t.append===H||(t.appendOrig=t.append,t.append=H),t}function xt(t,e,...n){let r=n.length?new CustomEvent(e,{detail:n[0]}):new Event(e);return t.dispatchEvent(r)}function v(t,e,n){return function(o){return o.addEventListener(t,e,n),o}}var P=ct(),ot=new WeakSet;v.connected=function(t,e){let n="connected";return typeof e!="object"&&(e={}),e.once=!0,function(o){let c="dde:"+n;return o.addEventListener(c,t,e),o.__dde_lifecycleToEvents?o:o.isConnected?(o.dispatchEvent(new Event(c)),o):(C(e.signal,()=>P.offConnected(o,t))&&P.onConnected(o,t),o)}};v.disconnected=function(t,e){let n="disconnected";return typeof e!="object"&&(e={}),e.once=!0,function(o){let c="dde:"+n;return o.addEventListener(c,t,e),o.__dde_lifecycleToEvents||C(e.signal,()=>P.offDisconnected(o,t))&&P.onDisconnected(o,t),o}};v.attributeChanged=function(t,e){let n="attributeChanged";return typeof e!="object"&&(e={}),function(o){let c="dde:"+n;if(o.addEventListener(c,t,e),o.__dde_lifecycleToEvents||ot.has(o))return o;let s=new MutationObserver(function(a){for(let{attributeName:h,target:x}of a)x.dispatchEvent(new CustomEvent(c,{detail:[h,x.getAttribute(h)]}))});return C(e.signal,()=>s.disconnect())&&s.observe(o,{attributes:!0}),o}};function ct(){let t=new Map,e=!1,n=new MutationObserver(function(i){for(let u of i)if(u.type==="childList"){if(h(u.addedNodes,!0)){s();continue}x(u.removedNodes,!0)&&s()}});return{onConnected(i,u){c();let f=o(i);f.connected.has(u)||(f.connected.add(u),f.length_c+=1)},offConnected(i,u){if(!t.has(i))return;let f=t.get(i);f.connected.has(u)&&(f.connected.delete(u),f.length_c-=1,r(i,f))},onDisconnected(i,u){c();let f=o(i);f.disconnected.has(u)||(f.disconnected.add(u),f.length_d+=1)},offDisconnected(i,u){if(!t.has(i))return;let f=t.get(i);f.disconnected.has(u)&&(f.disconnected.delete(u),f.length_d-=1,r(i,f))}};function r(i,u){u.length_c||u.length_d||(t.delete(i),s())}function o(i){if(t.has(i))return t.get(i);let u={connected:new WeakSet,length_c:0,disconnected:new WeakSet,length_d:0};return t.set(i,u),u}function c(){e||(e=!0,n.observe(document.body,{childList:!0,subtree:!0}))}function s(){!e||t.size||(e=!1,n.disconnect())}function d(){return new Promise(function(i){(requestIdleCallback||requestAnimationFrame)(i)})}async function a(i){t.size>30&&await d();let u=[];if(!(i instanceof Node))return u;for(let f of t.keys())f===i||!(f instanceof Node)||i.contains(f)&&u.push(f);return u}function h(i,u){let f=!1;for(let b of i){if(u&&a(b).then(h),!t.has(b))continue;let S=t.get(b);S.length_c&&(b.dispatchEvent(new Event("dde:connected")),S.connected=new WeakSet,S.length_c=0,S.length_d||t.delete(b),f=!0)}return f}function x(i,u){let f=!1;for(let b of i)u&&a(b).then(x),!(!t.has(b)||!t.get(b).length_d)&&(b.dispatchEvent(new Event("dde:disconnected")),t.delete(b),f=!0);return f}}var p=Symbol.for("Signal");function D(t){try{return Reflect.has(t,p)}catch{return!1}}var T=[],l=new WeakMap;function g(t,e){if(typeof t!="function")return V(t,e);if(D(t))return t;let n=V(),r=function(){let[o,...c]=l.get(r);if(l.set(r,new Set([o])),T.push(r),n(t()),T.pop(),!c.length)return;let s=l.get(r);for(let d of c)s.has(d)||O(d,r)};return l.set(n[p],r),l.set(r,new Set([n])),r(),n}g.action=function(t,e,...n){let r=t[p],{actions:o}=r;if(!o||!Reflect.has(o,e))throw new Error(`'${t}' has no action with name '${e}'!`);if(o[e].apply(r,n),r.skip)return Reflect.deleteProperty(r,"skip");r.listeners.forEach(c=>c(r.value))};g.on=function t(e,n,r={}){let{signal:o}=r;if(!(o&&o.aborted)){if(Array.isArray(e))return e.forEach(c=>t(c,n,r));z(e,n),o&&o.addEventListener("abort",()=>O(e,n))}};g.symbols={signal:p,onclear:Symbol.for("Signal.onclear")};var A="__dde_attributes";g.attribute=function(t,e=void 0){let n=g(e);return E.host(r=>{if(r instanceof HTMLElement?r.hasAttribute(t)&&n(r.getAttribute(t)):r.hasAttributeNS(null,t)&&n(r.getAttributeNS(null,t)),r[A]){r[A][t]=n;return}return r[A]={[t]:n},v.attributeChanged(function({detail:c}){/*! This maps attributes to signals (`S.attribute`). -* Investigate `__dde_attributes` key of the element.*/let[s,d]=c,a=r[A][s];a&&a(d)})(r),v.disconnected(function(){/*! This removes all signals mapped to attributes (`S.attribute`). -* Investigate `__dde_attributes` key of the element.*/g.clear(...Object.values(r[A]))})(r),r}),n};g.clear=function(...t){for(let n of t){Reflect.deleteProperty(n,"toJSON");let r=n[p];r.onclear.forEach(o=>o.call(r)),e(n,r),Reflect.deleteProperty(n,p)}function e(n,r){r.listeners.forEach(o=>{if(r.listeners.delete(o),!l.has(o))return;let c=l.get(o);c.delete(n),!(c.size>1)&&(g.clear(...c),l.delete(o))})}};var k="__dde_reactive";g.el=function(t,e){let n=m.mark({type:"reactive"},!1),r=n.end,o=document.createDocumentFragment();o.append(n,r);let{current:c}=E,s=d=>{if(!n.parentNode||!r.parentNode)return O(t,s);E.push(c);let a=e(d);E.pop(),Array.isArray(a)||(a=[a]);let h=n;for(;(h=n.nextSibling)!==r;)h.remove();n.after(...a)};return z(t,s),Q(t,s,n,e),s(t()),o};var K={isSignal:D,processReactiveAttribute(t,e,n,r){if(!D(n))return n;let o=c=>r(e,c);return z(n,o),Q(n,o,t,e),n()}};function Q(t,e,...n){let{current:r}=E;r.prevent||r.host(function(o){o[k]||(o[k]=[],v.disconnected(()=>o[k].forEach(([[c,s]])=>O(c,s,c[p]?.host()===o)))(o)),o[k].push([[t,e],...n])})}function V(t,e){let n=(...r)=>r.length?at(n,...r):ft(n);return it(n,t,e)}var st=Object.assign(Object.create(null),{stopPropagation(){this.skip=!0}}),$=class extends Error{constructor(){super();let[e,...n]=this.stack.split(` -`),r=e.slice(e.indexOf("@"),e.indexOf(".js:")+4);this.stack=n.find(o=>!o.includes(r))}};function it(t,e,n){let r=[];F(n)!=="[object Object]"&&(n={});let{onclear:o}=g.symbols;n[o]&&(r.push(n[o]),Reflect.deleteProperty(n,o));let{host:c}=E;return Reflect.defineProperty(t,p,{value:{value:e,actions:n,onclear:r,host:c,listeners:new Set,defined:new $},enumerable:!1,writable:!1,configurable:!0}),t.toJSON=()=>t(),Object.setPrototypeOf(t[p],st),t}function ut(){return T[T.length-1]}function ft(t){if(!t[p])return;let{value:e,listeners:n}=t[p],r=ut();return r&&n.add(r),l.has(r)&&l.get(r).add(t),e}function at(t,e,n){if(!t[p])return;let r=t[p];if(!(!n&&r.value===e))return r.value=e,r.listeners.forEach(o=>o(e)),e}function z(t,e){if(t[p])return t[p].listeners.add(e)}function O(t,e,n){let r=t[p];if(!r)return;let o=r.listeners.delete(e);if(n&&!r.listeners.size){if(g.clear(t),!l.has(r))return o;let c=l.get(r);if(!l.has(c))return o;l.get(c).forEach(s=>O(s,c,!0))}return o}W(K); +var y={isSignal(t){return!1},processReactiveAttribute(t,e,n,o){return n}};function M(t,e=!0){return e?Object.assign(y,t):(Object.setPrototypeOf(t,y),t)}function R(t){return y.isPrototypeOf(t)&&t!==y?t:y}function _(t){return typeof t>"u"}function F(t){let e=typeof t;return e!=="object"?e:t===null?"null":Object.prototype.toString.call(t)}function C(t,e){if(!t||!(t instanceof AbortSignal))return!0;if(!t.aborted)return t.addEventListener("abort",e),function(){t.removeEventListener("abort",e)}}var U={setDeleteAttr:X};function X(t,e,n){if(Reflect.set(t,e,n),!!_(n)){if(Reflect.deleteProperty(t,e),t instanceof HTMLElement&&t.getAttribute(e)==="undefined")return t.removeAttribute(e);if(Reflect.get(t,e)==="undefined")return Reflect.set(t,e,"")}}var w=[{scope:document.body,host:t=>t?t(document.body):document.body,prevent:!0}],E={get current(){return w[w.length-1]},get host(){return this.current.host},preventDefault(){let{current:t}=this;return t.prevent=!0,t},get state(){return[...w]},push(t={}){return w.push(Object.assign({},this.current,{prevent:!1},t))},pop(){return w.pop()}},L;function v(t,e,...n){let o=R(this),r=0,c,s;switch((Object(e)!==e||o.isSignal(e))&&(e={textContent:e}),!0){case typeof t=="function":{r=1,E.push({scope:t,host:l=>l?(r===1?n.unshift(l):l(s),void 0):s}),c=t(e||void 0);let a=c instanceof DocumentFragment,d=v.mark({type:"component",name:t.name,host:a?"this":c.nodeName==="#comment"?"previousLater":"parentElement"});c.prepend(d),a&&(s=d);break}case t==="#text":c=N.call(this,document.createTextNode(""),e);break;case(t==="<>"||!t):c=N.call(this,document.createDocumentFragment(),e);break;case L:c=N.call(this,document.createElementNS(L,t),e);break;case!c:c=N.call(this,document.createElement(t),e)}return rt(c),s||(s=c),n.forEach(a=>a(s)),r&&E.pop(),r=2,c}v.mark=function(t,e=!1){t=Object.entries(t).map(([r,c])=>r+`="${c}"`).join(" ");let n=e?"":"/",o=document.createComment(``);return e||(o.end=document.createComment("")),o};v.later=function(){let t=v.mark({type:"later"});return t.append=t.prepend=function(...e){return t.after(...e),t},t};function bt(t){let e=this;return function(...o){L=t;let r=v.call(e,...o);return L=void 0,r}}var{setDeleteAttr:q}=U,j=new WeakMap;function N(t,...e){if(!e.length)return t;j.set(t,J(t,this));for(let[n,o]of Object.entries(Object.assign({},...e)))I.call(this,t,n,o);return j.delete(t),t}function I(t,e,n){let{setRemoveAttr:o,s:r}=J(t,this),c=this;n=r.processReactiveAttribute(t,e,n,(a,d)=>I.call(c,t,a,d));let[s]=e;if(s==="=")return o(e.slice(1),n);if(s===".")return H(t,e.slice(1),n);if(/(aria|data)([A-Z])/.test(e))return e=e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase(),o(e,n);switch(e==="className"&&(e="class"),e){case"xlink:href":return o(e,n,"http://www.w3.org/1999/xlink");case"textContent":return q(t,e,n);case"style":if(typeof n!="object")break;case"dataset":return W(r,n,H.bind(null,t[e]));case"ariaset":return W(r,n,(a,d)=>o("aria-"+a,d));case"classList":return Y.call(c,t,n)}return tt(t,e)?q(t,e,n):o(e,n)}function J(t,e){if(j.has(t))return j.get(t);let o=(t instanceof SVGElement?nt:et).bind(null,t,"Attribute"),r=R(e);return{setRemoveAttr:o,s:r}}function Y(t,e){let n=R(this);return W(n,e,(o,r)=>t.classList.toggle(o,r===-1?void 0:!!r)),t}function _t(t){return Array.from(t.children).forEach(e=>e.remove()),t}function tt(t,e){if(!Reflect.has(t,e))return!1;let n=Z(t,e);return!_(n.set)}function Z(t,e){if(t=Object.getPrototypeOf(t),!t)return{};let n=Object.getOwnPropertyDescriptor(t,e);return n||Z(t,e)}function W(t,e,n){if(!(typeof e!="object"||e===null))return Object.entries(e).forEach(function([r,c]){r&&(c=t.processReactiveAttribute(e,r,c,n),n(r,c))})}function G(t){return Array.isArray(t)?t.filter(Boolean).join(" "):t}function et(t,e,n,o){return t[(_(o)?"remove":"set")+e](n,G(o))}function nt(t,e,n,o,r=null){return t[(_(o)?"remove":"set")+e+"NS"](r,n,G(o))}function H(t,e,n){if(Reflect.set(t,e,n),!!_(n))return Reflect.deleteProperty(t,e)}function B(...t){return this.appendOrig(...t),this}function rt(t){return t.append===B||(t.appendOrig=t.append,t.append=B),t}function xt(t,e,...n){let o=n.length?new CustomEvent(e,{detail:n[0]}):new Event(e);return t.dispatchEvent(o)}function m(t,e,n){return function(r){return r.addEventListener(t,e,n),r}}var P=ct(),ot=new WeakSet;m.connected=function(t,e){let n="connected";return typeof e!="object"&&(e={}),e.once=!0,function(r){let c="dde:"+n;return r.addEventListener(c,t,e),r.__dde_lifecycleToEvents?r:r.isConnected?(r.dispatchEvent(new Event(c)),r):(C(e.signal,()=>P.offConnected(r,t))&&P.onConnected(r,t),r)}};m.disconnected=function(t,e){let n="disconnected";return typeof e!="object"&&(e={}),e.once=!0,function(r){let c="dde:"+n;return r.addEventListener(c,t,e),r.__dde_lifecycleToEvents||C(e.signal,()=>P.offDisconnected(r,t))&&P.onDisconnected(r,t),r}};m.attributeChanged=function(t,e){let n="attributeChanged";return typeof e!="object"&&(e={}),function(r){let c="dde:"+n;if(r.addEventListener(c,t,e),r.__dde_lifecycleToEvents||ot.has(r))return r;let s=new MutationObserver(function(d){for(let{attributeName:l,target:x}of d)x.dispatchEvent(new CustomEvent(c,{detail:[l,x.getAttribute(l)]}))});return C(e.signal,()=>s.disconnect())&&s.observe(r,{attributes:!0}),r}};function ct(){let t=new Map,e=!1,n=new MutationObserver(function(i){for(let u of i)if(u.type==="childList"){if(l(u.addedNodes,!0)){s();continue}x(u.removedNodes,!0)&&s()}});return{onConnected(i,u){c();let f=r(i);f.connected.has(u)||(f.connected.add(u),f.length_c+=1)},offConnected(i,u){if(!t.has(i))return;let f=t.get(i);f.connected.has(u)&&(f.connected.delete(u),f.length_c-=1,o(i,f))},onDisconnected(i,u){c();let f=r(i);f.disconnected.has(u)||(f.disconnected.add(u),f.length_d+=1)},offDisconnected(i,u){if(!t.has(i))return;let f=t.get(i);f.disconnected.has(u)&&(f.disconnected.delete(u),f.length_d-=1,o(i,f))}};function o(i,u){u.length_c||u.length_d||(t.delete(i),s())}function r(i){if(t.has(i))return t.get(i);let u={connected:new WeakSet,length_c:0,disconnected:new WeakSet,length_d:0};return t.set(i,u),u}function c(){e||(e=!0,n.observe(document.body,{childList:!0,subtree:!0}))}function s(){!e||t.size||(e=!1,n.disconnect())}function a(){return new Promise(function(i){(requestIdleCallback||requestAnimationFrame)(i)})}async function d(i){t.size>30&&await a();let u=[];if(!(i instanceof Node))return u;for(let f of t.keys())f===i||!(f instanceof Node)||i.contains(f)&&u.push(f);return u}function l(i,u){let f=!1;for(let b of i){if(u&&d(b).then(l),!t.has(b))continue;let S=t.get(b);S.length_c&&(b.dispatchEvent(new Event("dde:connected")),S.connected=new WeakSet,S.length_c=0,S.length_d||t.delete(b),f=!0)}return f}function x(i,u){let f=!1;for(let b of i)u&&d(b).then(x),!(!t.has(b)||!t.get(b).length_d)&&(b.dispatchEvent(new Event("dde:disconnected")),t.delete(b),f=!0);return f}}var p=Symbol.for("Signal");function D(t){try{return Reflect.has(t,p)}catch{return!1}}var T=[],h=new WeakMap;function g(t,e){if(typeof t!="function")return V(t,e);if(D(t))return t;let n=V(),o=function(){let[r,...c]=h.get(o);if(h.set(o,new Set([r])),T.push(o),n(t()),T.pop(),!c.length)return;let s=h.get(o);for(let a of c)s.has(a)||O(a,o)};return h.set(n[p],o),h.set(o,new Set([n])),o(),n}g.action=function(t,e,...n){let o=t[p],{actions:r}=o;if(!r||!Reflect.has(r,e))throw new Error(`'${t}' has no action with name '${e}'!`);if(r[e].apply(o,n),o.skip)return Reflect.deleteProperty(o,"skip");o.listeners.forEach(c=>c(o.value))};g.on=function t(e,n,o={}){let{signal:r}=o;if(!(r&&r.aborted)){if(Array.isArray(e))return e.forEach(c=>t(c,n,o));z(e,n),r&&r.addEventListener("abort",()=>O(e,n))}};g.symbols={signal:p,onclear:Symbol.for("Signal.onclear")};var A="__dde_attributes";g.attribute=function(t,e=void 0){let n=g(e),o;return E.host(r=>{if(o=r,r instanceof HTMLElement?r.hasAttribute(t)&&n(r.getAttribute(t)):r.hasAttributeNS(null,t)&&n(r.getAttributeNS(null,t)),r[A]){r[A][t]=n;return}return r[A]={[t]:n},m.attributeChanged(function({detail:s}){/*! This maps attributes to signals (`S.attribute`). +* Investigate `__dde_attributes` key of the element.*/let[a,d]=s,l=r[A][a];if(l)return l(d)})(r),m.disconnected(function(){/*! This removes all signals mapped to attributes (`S.attribute`). +* Investigate `__dde_attributes` key of the element.*/g.clear(...Object.values(r[A])),o=null})(r),r}),new Proxy(n,{apply(r,c,s){if(!s.length)return r();if(!o)return;let a=s[0];return o instanceof HTMLElement?o.setAttribute(t,a):o.setAttributeNS(null,t,a)}})};g.clear=function(...t){for(let n of t){Reflect.deleteProperty(n,"toJSON");let o=n[p];o.onclear.forEach(r=>r.call(o)),e(n,o),Reflect.deleteProperty(n,p)}function e(n,o){o.listeners.forEach(r=>{if(o.listeners.delete(r),!h.has(r))return;let c=h.get(r);c.delete(n),!(c.size>1)&&(g.clear(...c),h.delete(r))})}};var k="__dde_reactive";g.el=function(t,e){let n=v.mark({type:"reactive"},!1),o=n.end,r=document.createDocumentFragment();r.append(n,o);let{current:c}=E,s=a=>{if(!n.parentNode||!o.parentNode)return O(t,s);E.push(c);let d=e(a);E.pop(),Array.isArray(d)||(d=[d]);let l=n;for(;(l=n.nextSibling)!==o;)l.remove();n.after(...d)};return z(t,s),Q(t,s,n,e),s(t()),r};var K={isSignal:D,processReactiveAttribute(t,e,n,o){if(!D(n))return n;let r=c=>o(e,c);return z(n,r),Q(n,r,t,e),n()}};function Q(t,e,...n){let{current:o}=E;o.prevent||o.host(function(r){r[k]||(r[k]=[],m.disconnected(()=>r[k].forEach(([[c,s]])=>O(c,s,c[p]?.host()===r)))(r)),r[k].push([[t,e],...n])})}function V(t,e){let n=(...o)=>o.length?at(n,...o):ft(n);return it(n,t,e)}var st=Object.assign(Object.create(null),{stopPropagation(){this.skip=!0}}),$=class extends Error{constructor(){super();let[e,...n]=this.stack.split(` +`),o=e.slice(e.indexOf("@"),e.indexOf(".js:")+4);this.stack=n.find(r=>!r.includes(o))}};function it(t,e,n){let o=[];F(n)!=="[object Object]"&&(n={});let{onclear:r}=g.symbols;n[r]&&(o.push(n[r]),Reflect.deleteProperty(n,r));let{host:c}=E;return Reflect.defineProperty(t,p,{value:{value:e,actions:n,onclear:o,host:c,listeners:new Set,defined:new $},enumerable:!1,writable:!1,configurable:!0}),t.toJSON=()=>t(),Object.setPrototypeOf(t[p],st),t}function ut(){return T[T.length-1]}function ft(t){if(!t[p])return;let{value:e,listeners:n}=t[p],o=ut();return o&&n.add(o),h.has(o)&&h.get(o).add(t),e}function at(t,e,n){if(!t[p])return;let o=t[p];if(!(!n&&o.value===e))return o.value=e,o.listeners.forEach(r=>r(e)),e}function z(t,e){if(t[p])return t[p].listeners.add(e)}function O(t,e,n){let o=t[p];if(!o)return;let r=o.listeners.delete(e);if(n&&!o.listeners.size){if(g.clear(t),!h.has(o))return r;let c=h.get(o);if(!h.has(c))return r;h.get(c).forEach(s=>O(s,c,!0))}return r}M(K); globalThis.dde= {S: g, assign: N, assignAttribute: I, classListDeclarative: Y, -createElement: m, +createElement: v, createElementNS: bt, dispatchEvent: xt, -el: m, +el: v, elNS: bt, empty: _t, isSignal: D, -on: v, -registerReactivity: W, +on: m, +registerReactivity: M, scope: E }; diff --git a/dist/esm-with-signals.d.ts b/dist/esm-with-signals.d.ts index 1484306..250b8a3 100644 --- a/dist/esm-with-signals.d.ts +++ b/dist/esm-with-signals.d.ts @@ -1,9 +1,14 @@ type CustomElementTagNameMap= { '#text': Text, '#comment': Comment } +declare global { + interface ddePublicElementTagNameMap{ + } +} type SupportedElement= HTMLElementTagNameMap[keyof HTMLElementTagNameMap] | SVGElementTagNameMap[keyof SVGElementTagNameMap] | MathMLElementTagNameMap[keyof MathMLElementTagNameMap] - | CustomElementTagNameMap[keyof CustomElementTagNameMap]; + | CustomElementTagNameMap[keyof CustomElementTagNameMap] + | ddePublicElementTagNameMap[keyof ddePublicElementTagNameMap]; declare global { type ddeComponentAttributes= Record | undefined | string; type ddeElementModifier= (element: El)=> El; @@ -36,7 +41,7 @@ type AttrsModified= { */ type ElementAttributes= Omit & AttrsModified; export function assign(element: El, ...attrs_array: Partial>[]): El -type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagNameMap +type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagNameMap & ddePublicElementTagNameMap export function el( tag_name: TAG, attrs?: string | Partial>, @@ -81,6 +86,7 @@ export function elNS( export function dispatchEvent(element: SupportedElement, name: keyof DocumentEventMap): void; export function dispatchEvent(element: SupportedElement, name: string, data: any): void; interface On{ + /** Listens to the DOM event. See {@link Document.addEventListener} */ < EE extends ddeElementModifier, El extends ( EE extends ddeElementModifier ? El : never ), @@ -89,6 +95,7 @@ interface On{ listener: (this: El, ev: DocumentEventMap[Event]) => any, options?: AddEventListenerOptions ) : EE; + /** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ connected< EE extends ddeElementModifier, El extends ( EE extends ddeElementModifier ? El : never ) @@ -96,6 +103,7 @@ interface On{ listener: (el: El) => any, options?: AddEventListenerOptions ) : EE; + /** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ disconnected< EE extends ddeElementModifier, El extends ( EE extends ddeElementModifier ? El : never ) @@ -103,10 +111,36 @@ interface On{ listener: (el: El) => any, options?: AddEventListenerOptions ) : EE; + /** Listens to the element attribute changes. In case of custom elements uses [`attributeChangedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ + attributeChanged< + EE extends ddeElementModifier, + El extends ( EE extends ddeElementModifier ? El : never ) + >( + listener: (el: El) => any, + options?: AddEventListenerOptions + ) : EE; } export const on: On; +type Scope= { scope: Node | Function | Object, host: ddeElementModifier, prevent: boolean } +/** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */ export const scope: { + current: Scope, + /** Stops all automatizations. E. g. signals used as attributes in current scope + * registers removing these listeners (and clean signal if no other listeners are detected) + * on `disconnected` event. */ + preventDefault(prevent: T): T, + /** + * This represents reference to the current host element — `scope.host()`. + * It can be also used to register Modifier (function to be called when component is initized) + * — `scope.host(on.connected(console.log))`. + * */ host: ddeElementModifier, + + state: Scope[], + /** Adds new child scope. All attributes are inherited by default. */ + push(scope: Partial): ReturnType["push"]> + /** Removes last/current child scope. */ + pop(): ReturnType["pop"]> }; /* * TODO TypeScript HACK (better way?) @@ -430,6 +464,10 @@ interface S { * by `S.clear`. * */ >(value: V, actions?: A): Signal; + /** + * Computations signal. This creates a signal which is computed from other signals. + * */ + (computation: ()=> V): Signal action>, A extends (S extends Signal ? A : never), N extends keyof A>( signal: S, name: N, @@ -441,7 +479,17 @@ interface S { signal: SymbolSignal; onclear: SymbolOnclear; } - el(signal: Signal, el: (v: S)=> T): T; + /** + * Reactive element, which is rendered based on the given signal. + * ```js + * S.el(signal, value=> value ? el("b", "True") : el("i", "False")); + * S.el(listS, list=> list.map(li=> el("li", li))); + * ``` + * */ + el(signal: Signal, el: (v: S)=> Element | Element[]): DocumentFragment; + + /** Mirrors element attributes for current host (both way). */ + attribute(name: string, initial?: T): Signal } export const S: S; declare global { diff --git a/dist/esm-with-signals.js b/dist/esm-with-signals.js index d2ff843..9291246 100644 --- a/dist/esm-with-signals.js +++ b/dist/esm-with-signals.js @@ -1,4 +1,4 @@ -var y={isSignal(t){return!1},processReactiveAttribute(t,e,n,r){return n}};function W(t,e=!0){return e?Object.assign(y,t):(Object.setPrototypeOf(t,y),t)}function R(t){return y.isPrototypeOf(t)&&t!==y?t:y}function _(t){return typeof t>"u"}function F(t){let e=typeof t;return e!=="object"?e:t===null?"null":Object.prototype.toString.call(t)}function C(t,e){if(!t||!(t instanceof AbortSignal))return!0;if(!t.aborted)return t.addEventListener("abort",e),function(){t.removeEventListener("abort",e)}}var U={setDeleteAttr:X};function X(t,e,n){if(Reflect.set(t,e,n),!!_(n)){if(Reflect.deleteProperty(t,e),t instanceof HTMLElement&&t.getAttribute(e)==="undefined")return t.removeAttribute(e);if(Reflect.get(t,e)==="undefined")return Reflect.set(t,e,"")}}var w=[{scope:document.body,host:t=>t?t(document.body):document.body,prevent:!0}],E={get current(){return w[w.length-1]},get host(){return this.current.host},preventDefault(){let{current:t}=this;return t.prevent=!0,t},get state(){return[...w]},push(t={}){return w.push(Object.assign({},this.current,{prevent:!1},t))},pop(){return w.pop()}},L;function m(t,e,...n){let r=R(this),o=0,c,s;switch((Object(e)!==e||r.isSignal(e))&&(e={textContent:e}),!0){case typeof t=="function":{o=1,E.push({scope:t,host:h=>h?(o===1?n.unshift(h):h(s),void 0):s}),c=t(e||void 0);let d=c instanceof DocumentFragment,a=m.mark({type:"component",name:t.name,host:d?"this":c.nodeName==="#comment"?"previousLater":"parentElement"});c.prepend(a),d&&(s=a);break}case t==="#text":c=N.call(this,document.createTextNode(""),e);break;case(t==="<>"||!t):c=N.call(this,document.createDocumentFragment(),e);break;case L:c=N.call(this,document.createElementNS(L,t),e);break;case!c:c=N.call(this,document.createElement(t),e)}return rt(c),s||(s=c),n.forEach(d=>d(s)),o&&E.pop(),o=2,c}m.mark=function(t,e=!1){t=Object.entries(t).map(([o,c])=>o+`="${c}"`).join(" ");let n=e?"":"/",r=document.createComment(``);return e||(r.end=document.createComment("")),r};m.later=function(){let t=m.mark({type:"later"});return t.append=t.prepend=function(...e){return t.after(...e),t},t};function bt(t){let e=this;return function(...r){L=t;let o=m.call(e,...r);return L=void 0,o}}var{setDeleteAttr:q}=U,j=new WeakMap;function N(t,...e){if(!e.length)return t;j.set(t,J(t,this));for(let[n,r]of Object.entries(Object.assign({},...e)))I.call(this,t,n,r);return j.delete(t),t}function I(t,e,n){let{setRemoveAttr:r,s:o}=J(t,this),c=this;n=o.processReactiveAttribute(t,e,n,(d,a)=>I.call(c,t,d,a));let[s]=e;if(s==="=")return r(e.slice(1),n);if(s===".")return B(t,e.slice(1),n);if(/(aria|data)([A-Z])/.test(e))return e=e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase(),r(e,n);switch(e==="className"&&(e="class"),e){case"xlink:href":return r(e,n,"http://www.w3.org/1999/xlink");case"textContent":return q(t,e,n);case"style":if(typeof n!="object")break;case"dataset":return M(o,n,B.bind(null,t[e]));case"ariaset":return M(o,n,(d,a)=>r("aria-"+d,a));case"classList":return Y.call(c,t,n)}return tt(t,e)?q(t,e,n):r(e,n)}function J(t,e){if(j.has(t))return j.get(t);let r=(t instanceof SVGElement?nt:et).bind(null,t,"Attribute"),o=R(e);return{setRemoveAttr:r,s:o}}function Y(t,e){let n=R(this);return M(n,e,(r,o)=>t.classList.toggle(r,o===-1?void 0:!!o)),t}function _t(t){return Array.from(t.children).forEach(e=>e.remove()),t}function tt(t,e){if(!Reflect.has(t,e))return!1;let n=Z(t,e);return!_(n.set)}function Z(t,e){if(t=Object.getPrototypeOf(t),!t)return{};let n=Object.getOwnPropertyDescriptor(t,e);return n||Z(t,e)}function M(t,e,n){if(!(typeof e!="object"||e===null))return Object.entries(e).forEach(function([o,c]){o&&(c=t.processReactiveAttribute(e,o,c,n),n(o,c))})}function G(t){return Array.isArray(t)?t.filter(Boolean).join(" "):t}function et(t,e,n,r){return t[(_(r)?"remove":"set")+e](n,G(r))}function nt(t,e,n,r,o=null){return t[(_(r)?"remove":"set")+e+"NS"](o,n,G(r))}function B(t,e,n){if(Reflect.set(t,e,n),!!_(n))return Reflect.deleteProperty(t,e)}function H(...t){return this.appendOrig(...t),this}function rt(t){return t.append===H||(t.appendOrig=t.append,t.append=H),t}function xt(t,e,...n){let r=n.length?new CustomEvent(e,{detail:n[0]}):new Event(e);return t.dispatchEvent(r)}function v(t,e,n){return function(o){return o.addEventListener(t,e,n),o}}var P=ct(),ot=new WeakSet;v.connected=function(t,e){let n="connected";return typeof e!="object"&&(e={}),e.once=!0,function(o){let c="dde:"+n;return o.addEventListener(c,t,e),o.__dde_lifecycleToEvents?o:o.isConnected?(o.dispatchEvent(new Event(c)),o):(C(e.signal,()=>P.offConnected(o,t))&&P.onConnected(o,t),o)}};v.disconnected=function(t,e){let n="disconnected";return typeof e!="object"&&(e={}),e.once=!0,function(o){let c="dde:"+n;return o.addEventListener(c,t,e),o.__dde_lifecycleToEvents||C(e.signal,()=>P.offDisconnected(o,t))&&P.onDisconnected(o,t),o}};v.attributeChanged=function(t,e){let n="attributeChanged";return typeof e!="object"&&(e={}),function(o){let c="dde:"+n;if(o.addEventListener(c,t,e),o.__dde_lifecycleToEvents||ot.has(o))return o;let s=new MutationObserver(function(a){for(let{attributeName:h,target:x}of a)x.dispatchEvent(new CustomEvent(c,{detail:[h,x.getAttribute(h)]}))});return C(e.signal,()=>s.disconnect())&&s.observe(o,{attributes:!0}),o}};function ct(){let t=new Map,e=!1,n=new MutationObserver(function(i){for(let u of i)if(u.type==="childList"){if(h(u.addedNodes,!0)){s();continue}x(u.removedNodes,!0)&&s()}});return{onConnected(i,u){c();let f=o(i);f.connected.has(u)||(f.connected.add(u),f.length_c+=1)},offConnected(i,u){if(!t.has(i))return;let f=t.get(i);f.connected.has(u)&&(f.connected.delete(u),f.length_c-=1,r(i,f))},onDisconnected(i,u){c();let f=o(i);f.disconnected.has(u)||(f.disconnected.add(u),f.length_d+=1)},offDisconnected(i,u){if(!t.has(i))return;let f=t.get(i);f.disconnected.has(u)&&(f.disconnected.delete(u),f.length_d-=1,r(i,f))}};function r(i,u){u.length_c||u.length_d||(t.delete(i),s())}function o(i){if(t.has(i))return t.get(i);let u={connected:new WeakSet,length_c:0,disconnected:new WeakSet,length_d:0};return t.set(i,u),u}function c(){e||(e=!0,n.observe(document.body,{childList:!0,subtree:!0}))}function s(){!e||t.size||(e=!1,n.disconnect())}function d(){return new Promise(function(i){(requestIdleCallback||requestAnimationFrame)(i)})}async function a(i){t.size>30&&await d();let u=[];if(!(i instanceof Node))return u;for(let f of t.keys())f===i||!(f instanceof Node)||i.contains(f)&&u.push(f);return u}function h(i,u){let f=!1;for(let b of i){if(u&&a(b).then(h),!t.has(b))continue;let S=t.get(b);S.length_c&&(b.dispatchEvent(new Event("dde:connected")),S.connected=new WeakSet,S.length_c=0,S.length_d||t.delete(b),f=!0)}return f}function x(i,u){let f=!1;for(let b of i)u&&a(b).then(x),!(!t.has(b)||!t.get(b).length_d)&&(b.dispatchEvent(new Event("dde:disconnected")),t.delete(b),f=!0);return f}}var p=Symbol.for("Signal");function D(t){try{return Reflect.has(t,p)}catch{return!1}}var T=[],l=new WeakMap;function g(t,e){if(typeof t!="function")return V(t,e);if(D(t))return t;let n=V(),r=function(){let[o,...c]=l.get(r);if(l.set(r,new Set([o])),T.push(r),n(t()),T.pop(),!c.length)return;let s=l.get(r);for(let d of c)s.has(d)||O(d,r)};return l.set(n[p],r),l.set(r,new Set([n])),r(),n}g.action=function(t,e,...n){let r=t[p],{actions:o}=r;if(!o||!Reflect.has(o,e))throw new Error(`'${t}' has no action with name '${e}'!`);if(o[e].apply(r,n),r.skip)return Reflect.deleteProperty(r,"skip");r.listeners.forEach(c=>c(r.value))};g.on=function t(e,n,r={}){let{signal:o}=r;if(!(o&&o.aborted)){if(Array.isArray(e))return e.forEach(c=>t(c,n,r));z(e,n),o&&o.addEventListener("abort",()=>O(e,n))}};g.symbols={signal:p,onclear:Symbol.for("Signal.onclear")};var A="__dde_attributes";g.attribute=function(t,e=void 0){let n=g(e);return E.host(r=>{if(r instanceof HTMLElement?r.hasAttribute(t)&&n(r.getAttribute(t)):r.hasAttributeNS(null,t)&&n(r.getAttributeNS(null,t)),r[A]){r[A][t]=n;return}return r[A]={[t]:n},v.attributeChanged(function({detail:c}){/*! This maps attributes to signals (`S.attribute`). -* Investigate `__dde_attributes` key of the element.*/let[s,d]=c,a=r[A][s];a&&a(d)})(r),v.disconnected(function(){/*! This removes all signals mapped to attributes (`S.attribute`). -* Investigate `__dde_attributes` key of the element.*/g.clear(...Object.values(r[A]))})(r),r}),n};g.clear=function(...t){for(let n of t){Reflect.deleteProperty(n,"toJSON");let r=n[p];r.onclear.forEach(o=>o.call(r)),e(n,r),Reflect.deleteProperty(n,p)}function e(n,r){r.listeners.forEach(o=>{if(r.listeners.delete(o),!l.has(o))return;let c=l.get(o);c.delete(n),!(c.size>1)&&(g.clear(...c),l.delete(o))})}};var k="__dde_reactive";g.el=function(t,e){let n=m.mark({type:"reactive"},!1),r=n.end,o=document.createDocumentFragment();o.append(n,r);let{current:c}=E,s=d=>{if(!n.parentNode||!r.parentNode)return O(t,s);E.push(c);let a=e(d);E.pop(),Array.isArray(a)||(a=[a]);let h=n;for(;(h=n.nextSibling)!==r;)h.remove();n.after(...a)};return z(t,s),Q(t,s,n,e),s(t()),o};var K={isSignal:D,processReactiveAttribute(t,e,n,r){if(!D(n))return n;let o=c=>r(e,c);return z(n,o),Q(n,o,t,e),n()}};function Q(t,e,...n){let{current:r}=E;r.prevent||r.host(function(o){o[k]||(o[k]=[],v.disconnected(()=>o[k].forEach(([[c,s]])=>O(c,s,c[p]?.host()===o)))(o)),o[k].push([[t,e],...n])})}function V(t,e){let n=(...r)=>r.length?at(n,...r):ft(n);return it(n,t,e)}var st=Object.assign(Object.create(null),{stopPropagation(){this.skip=!0}}),$=class extends Error{constructor(){super();let[e,...n]=this.stack.split(` -`),r=e.slice(e.indexOf("@"),e.indexOf(".js:")+4);this.stack=n.find(o=>!o.includes(r))}};function it(t,e,n){let r=[];F(n)!=="[object Object]"&&(n={});let{onclear:o}=g.symbols;n[o]&&(r.push(n[o]),Reflect.deleteProperty(n,o));let{host:c}=E;return Reflect.defineProperty(t,p,{value:{value:e,actions:n,onclear:r,host:c,listeners:new Set,defined:new $},enumerable:!1,writable:!1,configurable:!0}),t.toJSON=()=>t(),Object.setPrototypeOf(t[p],st),t}function ut(){return T[T.length-1]}function ft(t){if(!t[p])return;let{value:e,listeners:n}=t[p],r=ut();return r&&n.add(r),l.has(r)&&l.get(r).add(t),e}function at(t,e,n){if(!t[p])return;let r=t[p];if(!(!n&&r.value===e))return r.value=e,r.listeners.forEach(o=>o(e)),e}function z(t,e){if(t[p])return t[p].listeners.add(e)}function O(t,e,n){let r=t[p];if(!r)return;let o=r.listeners.delete(e);if(n&&!r.listeners.size){if(g.clear(t),!l.has(r))return o;let c=l.get(r);if(!l.has(c))return o;l.get(c).forEach(s=>O(s,c,!0))}return o}W(K);export{g as S,N as assign,I as assignAttribute,Y as classListDeclarative,m as createElement,bt as createElementNS,xt as dispatchEvent,m as el,bt as elNS,_t as empty,D as isSignal,v as on,W as registerReactivity,E as scope}; +var y={isSignal(t){return!1},processReactiveAttribute(t,e,n,o){return n}};function M(t,e=!0){return e?Object.assign(y,t):(Object.setPrototypeOf(t,y),t)}function R(t){return y.isPrototypeOf(t)&&t!==y?t:y}function _(t){return typeof t>"u"}function F(t){let e=typeof t;return e!=="object"?e:t===null?"null":Object.prototype.toString.call(t)}function C(t,e){if(!t||!(t instanceof AbortSignal))return!0;if(!t.aborted)return t.addEventListener("abort",e),function(){t.removeEventListener("abort",e)}}var U={setDeleteAttr:X};function X(t,e,n){if(Reflect.set(t,e,n),!!_(n)){if(Reflect.deleteProperty(t,e),t instanceof HTMLElement&&t.getAttribute(e)==="undefined")return t.removeAttribute(e);if(Reflect.get(t,e)==="undefined")return Reflect.set(t,e,"")}}var w=[{scope:document.body,host:t=>t?t(document.body):document.body,prevent:!0}],E={get current(){return w[w.length-1]},get host(){return this.current.host},preventDefault(){let{current:t}=this;return t.prevent=!0,t},get state(){return[...w]},push(t={}){return w.push(Object.assign({},this.current,{prevent:!1},t))},pop(){return w.pop()}},L;function v(t,e,...n){let o=R(this),r=0,c,s;switch((Object(e)!==e||o.isSignal(e))&&(e={textContent:e}),!0){case typeof t=="function":{r=1,E.push({scope:t,host:l=>l?(r===1?n.unshift(l):l(s),void 0):s}),c=t(e||void 0);let a=c instanceof DocumentFragment,d=v.mark({type:"component",name:t.name,host:a?"this":c.nodeName==="#comment"?"previousLater":"parentElement"});c.prepend(d),a&&(s=d);break}case t==="#text":c=N.call(this,document.createTextNode(""),e);break;case(t==="<>"||!t):c=N.call(this,document.createDocumentFragment(),e);break;case L:c=N.call(this,document.createElementNS(L,t),e);break;case!c:c=N.call(this,document.createElement(t),e)}return rt(c),s||(s=c),n.forEach(a=>a(s)),r&&E.pop(),r=2,c}v.mark=function(t,e=!1){t=Object.entries(t).map(([r,c])=>r+`="${c}"`).join(" ");let n=e?"":"/",o=document.createComment(``);return e||(o.end=document.createComment("")),o};v.later=function(){let t=v.mark({type:"later"});return t.append=t.prepend=function(...e){return t.after(...e),t},t};function bt(t){let e=this;return function(...o){L=t;let r=v.call(e,...o);return L=void 0,r}}var{setDeleteAttr:q}=U,j=new WeakMap;function N(t,...e){if(!e.length)return t;j.set(t,J(t,this));for(let[n,o]of Object.entries(Object.assign({},...e)))I.call(this,t,n,o);return j.delete(t),t}function I(t,e,n){let{setRemoveAttr:o,s:r}=J(t,this),c=this;n=r.processReactiveAttribute(t,e,n,(a,d)=>I.call(c,t,a,d));let[s]=e;if(s==="=")return o(e.slice(1),n);if(s===".")return H(t,e.slice(1),n);if(/(aria|data)([A-Z])/.test(e))return e=e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase(),o(e,n);switch(e==="className"&&(e="class"),e){case"xlink:href":return o(e,n,"http://www.w3.org/1999/xlink");case"textContent":return q(t,e,n);case"style":if(typeof n!="object")break;case"dataset":return W(r,n,H.bind(null,t[e]));case"ariaset":return W(r,n,(a,d)=>o("aria-"+a,d));case"classList":return Y.call(c,t,n)}return tt(t,e)?q(t,e,n):o(e,n)}function J(t,e){if(j.has(t))return j.get(t);let o=(t instanceof SVGElement?nt:et).bind(null,t,"Attribute"),r=R(e);return{setRemoveAttr:o,s:r}}function Y(t,e){let n=R(this);return W(n,e,(o,r)=>t.classList.toggle(o,r===-1?void 0:!!r)),t}function _t(t){return Array.from(t.children).forEach(e=>e.remove()),t}function tt(t,e){if(!Reflect.has(t,e))return!1;let n=Z(t,e);return!_(n.set)}function Z(t,e){if(t=Object.getPrototypeOf(t),!t)return{};let n=Object.getOwnPropertyDescriptor(t,e);return n||Z(t,e)}function W(t,e,n){if(!(typeof e!="object"||e===null))return Object.entries(e).forEach(function([r,c]){r&&(c=t.processReactiveAttribute(e,r,c,n),n(r,c))})}function G(t){return Array.isArray(t)?t.filter(Boolean).join(" "):t}function et(t,e,n,o){return t[(_(o)?"remove":"set")+e](n,G(o))}function nt(t,e,n,o,r=null){return t[(_(o)?"remove":"set")+e+"NS"](r,n,G(o))}function H(t,e,n){if(Reflect.set(t,e,n),!!_(n))return Reflect.deleteProperty(t,e)}function B(...t){return this.appendOrig(...t),this}function rt(t){return t.append===B||(t.appendOrig=t.append,t.append=B),t}function xt(t,e,...n){let o=n.length?new CustomEvent(e,{detail:n[0]}):new Event(e);return t.dispatchEvent(o)}function m(t,e,n){return function(r){return r.addEventListener(t,e,n),r}}var P=ct(),ot=new WeakSet;m.connected=function(t,e){let n="connected";return typeof e!="object"&&(e={}),e.once=!0,function(r){let c="dde:"+n;return r.addEventListener(c,t,e),r.__dde_lifecycleToEvents?r:r.isConnected?(r.dispatchEvent(new Event(c)),r):(C(e.signal,()=>P.offConnected(r,t))&&P.onConnected(r,t),r)}};m.disconnected=function(t,e){let n="disconnected";return typeof e!="object"&&(e={}),e.once=!0,function(r){let c="dde:"+n;return r.addEventListener(c,t,e),r.__dde_lifecycleToEvents||C(e.signal,()=>P.offDisconnected(r,t))&&P.onDisconnected(r,t),r}};m.attributeChanged=function(t,e){let n="attributeChanged";return typeof e!="object"&&(e={}),function(r){let c="dde:"+n;if(r.addEventListener(c,t,e),r.__dde_lifecycleToEvents||ot.has(r))return r;let s=new MutationObserver(function(d){for(let{attributeName:l,target:x}of d)x.dispatchEvent(new CustomEvent(c,{detail:[l,x.getAttribute(l)]}))});return C(e.signal,()=>s.disconnect())&&s.observe(r,{attributes:!0}),r}};function ct(){let t=new Map,e=!1,n=new MutationObserver(function(i){for(let u of i)if(u.type==="childList"){if(l(u.addedNodes,!0)){s();continue}x(u.removedNodes,!0)&&s()}});return{onConnected(i,u){c();let f=r(i);f.connected.has(u)||(f.connected.add(u),f.length_c+=1)},offConnected(i,u){if(!t.has(i))return;let f=t.get(i);f.connected.has(u)&&(f.connected.delete(u),f.length_c-=1,o(i,f))},onDisconnected(i,u){c();let f=r(i);f.disconnected.has(u)||(f.disconnected.add(u),f.length_d+=1)},offDisconnected(i,u){if(!t.has(i))return;let f=t.get(i);f.disconnected.has(u)&&(f.disconnected.delete(u),f.length_d-=1,o(i,f))}};function o(i,u){u.length_c||u.length_d||(t.delete(i),s())}function r(i){if(t.has(i))return t.get(i);let u={connected:new WeakSet,length_c:0,disconnected:new WeakSet,length_d:0};return t.set(i,u),u}function c(){e||(e=!0,n.observe(document.body,{childList:!0,subtree:!0}))}function s(){!e||t.size||(e=!1,n.disconnect())}function a(){return new Promise(function(i){(requestIdleCallback||requestAnimationFrame)(i)})}async function d(i){t.size>30&&await a();let u=[];if(!(i instanceof Node))return u;for(let f of t.keys())f===i||!(f instanceof Node)||i.contains(f)&&u.push(f);return u}function l(i,u){let f=!1;for(let b of i){if(u&&d(b).then(l),!t.has(b))continue;let S=t.get(b);S.length_c&&(b.dispatchEvent(new Event("dde:connected")),S.connected=new WeakSet,S.length_c=0,S.length_d||t.delete(b),f=!0)}return f}function x(i,u){let f=!1;for(let b of i)u&&d(b).then(x),!(!t.has(b)||!t.get(b).length_d)&&(b.dispatchEvent(new Event("dde:disconnected")),t.delete(b),f=!0);return f}}var p=Symbol.for("Signal");function D(t){try{return Reflect.has(t,p)}catch{return!1}}var T=[],h=new WeakMap;function g(t,e){if(typeof t!="function")return V(t,e);if(D(t))return t;let n=V(),o=function(){let[r,...c]=h.get(o);if(h.set(o,new Set([r])),T.push(o),n(t()),T.pop(),!c.length)return;let s=h.get(o);for(let a of c)s.has(a)||O(a,o)};return h.set(n[p],o),h.set(o,new Set([n])),o(),n}g.action=function(t,e,...n){let o=t[p],{actions:r}=o;if(!r||!Reflect.has(r,e))throw new Error(`'${t}' has no action with name '${e}'!`);if(r[e].apply(o,n),o.skip)return Reflect.deleteProperty(o,"skip");o.listeners.forEach(c=>c(o.value))};g.on=function t(e,n,o={}){let{signal:r}=o;if(!(r&&r.aborted)){if(Array.isArray(e))return e.forEach(c=>t(c,n,o));z(e,n),r&&r.addEventListener("abort",()=>O(e,n))}};g.symbols={signal:p,onclear:Symbol.for("Signal.onclear")};var A="__dde_attributes";g.attribute=function(t,e=void 0){let n=g(e),o;return E.host(r=>{if(o=r,r instanceof HTMLElement?r.hasAttribute(t)&&n(r.getAttribute(t)):r.hasAttributeNS(null,t)&&n(r.getAttributeNS(null,t)),r[A]){r[A][t]=n;return}return r[A]={[t]:n},m.attributeChanged(function({detail:s}){/*! This maps attributes to signals (`S.attribute`). +* Investigate `__dde_attributes` key of the element.*/let[a,d]=s,l=r[A][a];if(l)return l(d)})(r),m.disconnected(function(){/*! This removes all signals mapped to attributes (`S.attribute`). +* Investigate `__dde_attributes` key of the element.*/g.clear(...Object.values(r[A])),o=null})(r),r}),new Proxy(n,{apply(r,c,s){if(!s.length)return r();if(!o)return;let a=s[0];return o instanceof HTMLElement?o.setAttribute(t,a):o.setAttributeNS(null,t,a)}})};g.clear=function(...t){for(let n of t){Reflect.deleteProperty(n,"toJSON");let o=n[p];o.onclear.forEach(r=>r.call(o)),e(n,o),Reflect.deleteProperty(n,p)}function e(n,o){o.listeners.forEach(r=>{if(o.listeners.delete(r),!h.has(r))return;let c=h.get(r);c.delete(n),!(c.size>1)&&(g.clear(...c),h.delete(r))})}};var k="__dde_reactive";g.el=function(t,e){let n=v.mark({type:"reactive"},!1),o=n.end,r=document.createDocumentFragment();r.append(n,o);let{current:c}=E,s=a=>{if(!n.parentNode||!o.parentNode)return O(t,s);E.push(c);let d=e(a);E.pop(),Array.isArray(d)||(d=[d]);let l=n;for(;(l=n.nextSibling)!==o;)l.remove();n.after(...d)};return z(t,s),Q(t,s,n,e),s(t()),r};var K={isSignal:D,processReactiveAttribute(t,e,n,o){if(!D(n))return n;let r=c=>o(e,c);return z(n,r),Q(n,r,t,e),n()}};function Q(t,e,...n){let{current:o}=E;o.prevent||o.host(function(r){r[k]||(r[k]=[],m.disconnected(()=>r[k].forEach(([[c,s]])=>O(c,s,c[p]?.host()===r)))(r)),r[k].push([[t,e],...n])})}function V(t,e){let n=(...o)=>o.length?at(n,...o):ft(n);return it(n,t,e)}var st=Object.assign(Object.create(null),{stopPropagation(){this.skip=!0}}),$=class extends Error{constructor(){super();let[e,...n]=this.stack.split(` +`),o=e.slice(e.indexOf("@"),e.indexOf(".js:")+4);this.stack=n.find(r=>!r.includes(o))}};function it(t,e,n){let o=[];F(n)!=="[object Object]"&&(n={});let{onclear:r}=g.symbols;n[r]&&(o.push(n[r]),Reflect.deleteProperty(n,r));let{host:c}=E;return Reflect.defineProperty(t,p,{value:{value:e,actions:n,onclear:o,host:c,listeners:new Set,defined:new $},enumerable:!1,writable:!1,configurable:!0}),t.toJSON=()=>t(),Object.setPrototypeOf(t[p],st),t}function ut(){return T[T.length-1]}function ft(t){if(!t[p])return;let{value:e,listeners:n}=t[p],o=ut();return o&&n.add(o),h.has(o)&&h.get(o).add(t),e}function at(t,e,n){if(!t[p])return;let o=t[p];if(!(!n&&o.value===e))return o.value=e,o.listeners.forEach(r=>r(e)),e}function z(t,e){if(t[p])return t[p].listeners.add(e)}function O(t,e,n){let o=t[p];if(!o)return;let r=o.listeners.delete(e);if(n&&!o.listeners.size){if(g.clear(t),!h.has(o))return r;let c=h.get(o);if(!h.has(c))return r;h.get(c).forEach(s=>O(s,c,!0))}return r}M(K);export{g as S,N as assign,I as assignAttribute,Y as classListDeclarative,v as createElement,bt as createElementNS,xt as dispatchEvent,v as el,bt as elNS,_t as empty,D as isSignal,m as on,M as registerReactivity,E as scope}; diff --git a/dist/esm.d.ts b/dist/esm.d.ts index fde91c8..ec6d7ee 100644 --- a/dist/esm.d.ts +++ b/dist/esm.d.ts @@ -1,9 +1,14 @@ type CustomElementTagNameMap= { '#text': Text, '#comment': Comment } +declare global { + interface ddePublicElementTagNameMap{ + } +} type SupportedElement= HTMLElementTagNameMap[keyof HTMLElementTagNameMap] | SVGElementTagNameMap[keyof SVGElementTagNameMap] | MathMLElementTagNameMap[keyof MathMLElementTagNameMap] - | CustomElementTagNameMap[keyof CustomElementTagNameMap]; + | CustomElementTagNameMap[keyof CustomElementTagNameMap] + | ddePublicElementTagNameMap[keyof ddePublicElementTagNameMap]; declare global { type ddeComponentAttributes= Record | undefined | string; type ddeElementModifier= (element: El)=> El; @@ -36,7 +41,7 @@ type AttrsModified= { */ type ElementAttributes= Omit & AttrsModified; export function assign(element: El, ...attrs_array: Partial>[]): El -type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagNameMap +type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagNameMap & ddePublicElementTagNameMap export function el( tag_name: TAG, attrs?: string | Partial>, @@ -81,6 +86,7 @@ export function elNS( export function dispatchEvent(element: SupportedElement, name: keyof DocumentEventMap): void; export function dispatchEvent(element: SupportedElement, name: string, data: any): void; interface On{ + /** Listens to the DOM event. See {@link Document.addEventListener} */ < EE extends ddeElementModifier, El extends ( EE extends ddeElementModifier ? El : never ), @@ -89,6 +95,7 @@ interface On{ listener: (this: El, ev: DocumentEventMap[Event]) => any, options?: AddEventListenerOptions ) : EE; + /** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ connected< EE extends ddeElementModifier, El extends ( EE extends ddeElementModifier ? El : never ) @@ -96,6 +103,7 @@ interface On{ listener: (el: El) => any, options?: AddEventListenerOptions ) : EE; + /** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ disconnected< EE extends ddeElementModifier, El extends ( EE extends ddeElementModifier ? El : never ) @@ -103,10 +111,36 @@ interface On{ listener: (el: El) => any, options?: AddEventListenerOptions ) : EE; + /** Listens to the element attribute changes. In case of custom elements uses [`attributeChangedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ + attributeChanged< + EE extends ddeElementModifier, + El extends ( EE extends ddeElementModifier ? El : never ) + >( + listener: (el: El) => any, + options?: AddEventListenerOptions + ) : EE; } export const on: On; +type Scope= { scope: Node | Function | Object, host: ddeElementModifier, prevent: boolean } +/** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */ export const scope: { + current: Scope, + /** Stops all automatizations. E. g. signals used as attributes in current scope + * registers removing these listeners (and clean signal if no other listeners are detected) + * on `disconnected` event. */ + preventDefault(prevent: T): T, + /** + * This represents reference to the current host element — `scope.host()`. + * It can be also used to register Modifier (function to be called when component is initized) + * — `scope.host(on.connected(console.log))`. + * */ host: ddeElementModifier, + + state: Scope[], + /** Adds new child scope. All attributes are inherited by default. */ + push(scope: Partial): ReturnType["push"]> + /** Removes last/current child scope. */ + pop(): ReturnType["pop"]> }; /* * TODO TypeScript HACK (better way?) diff --git a/docs/elements.html b/docs/elements.html index bf25588..9f6c143 100644 --- a/docs/elements.html +++ b/docs/elements.html @@ -1,4 +1,4 @@ -`deka-dom-el` — Elements

`deka-dom-el` — Elements

Basic concepts of elements modifications and creations.

Native JavaScript DOM elements creations

Let’s go through all patterns we would like to use and what needs to be improved for better experience.

Creating element(s) (with custom attributes)

You can create a native DOM element by using the document.createElement(). It is also possible to provide a some attribute(s) declaratively using Object.assign(). More precisely, this way you can sets some IDL.

document.body.append(
+`deka-dom-el` — Elements

`deka-dom-el` — Elements

Basic concepts of elements modifications and creations.

Native JavaScript DOM elements creations

Let’s go through all patterns we would like to use and what needs to be improved for better experience.

Creating element(s) (with custom attributes)

You can create a native DOM element by using the document.createElement(). It is also possible to provide a some attribute(s) declaratively using Object.assign(). More precisely, this way you can sets some IDL.

document.body.append(
 	document.createElement("div")
 );
 console.log(
@@ -12,7 +12,7 @@ document.body.append(
 		{ textContent: "Element’s text content.", style: "color: coral;" }
 	)
 );
-

To make this easier, you can use the el function. Internally in basic examples, it is wrapper around assign(document.createElement(…), { … }).

import { el, assign } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
+

To make this easier, you can use the el function. Internally in basic examples, it is wrapper around assign(document.createElement(…), { … }).

import { el, assign } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
 const color= "lightcoral";
 document.body.append(
 	el("p", { textContent: "Hello (first time)", style: { color } })
@@ -23,7 +23,7 @@ document.body.append(
 		{ textContent: "Hello (second time)", style: { color } }
 	)
 );
-

The assign function provides improved behaviour of Object.assign(). You can declaratively sets any IDL and attribute of the given element. Function prefers IDL and fallback to the element.setAttribute if there is no writable property in the element prototype.

You can study all JavaScript elements interfaces to the corresponding HTML elements. All HTML elements inherits from HTMLElement. To see all available IDLs for example for paragraphs, see HTMLParagraphElement. Moreover, the assign provides a way to sets declaratively some convenient properties:

  • It is possible to sets data-*/aria-* attributes using object notation.
  • In opposite, it is also possible to sets data-*/aria-* attribute using camelCase notation.
  • You can use string or object as a value for style property.
  • className (IDL – preffered)/class are ways to add CSS class to the element. You can use string (similarly to class="…" syntax in HTML) or array of strings. This is handy to concat conditional classes.
  • Use classList to toggle specific classes. This will be handy later when the reactivity via signals is beeing introduced.
  • The assign also accepts the undefined as a value for any property to remove it from the element declaratively. Also for some IDL the corresponding attribute is removed as it can be confusing. For example, natievly the element’s id is removed by setting the IDL to an empty string.

For processing, the assign internally uses assignAttribute and classListDeclarative.

import { assignAttribute, classListDeclarative } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
+

The assign function provides improved behaviour of Object.assign(). You can declaratively sets any IDL and attribute of the given element. Function prefers IDL and fallback to the element.setAttribute if there is no writable property in the element prototype.

You can study all JavaScript elements interfaces to the corresponding HTML elements. All HTML elements inherits from HTMLElement. To see all available IDLs for example for paragraphs, see HTMLParagraphElement. Moreover, the assign provides a way to sets declaratively some convenient properties:

  • It is possible to sets data-*/aria-* attributes using object notation.
  • In opposite, it is also possible to sets data-*/aria-* attribute using camelCase notation.
  • You can use string or object as a value for style property.
  • className (IDL – preffered)/class are ways to add CSS class to the element. You can use string (similarly to class="…" syntax in HTML) or array of strings. This is handy to concat conditional classes.
  • Use classList to toggle specific classes. This will be handy later when the reactivity via signals is beeing introduced.
  • The assign also accepts the undefined as a value for any property to remove it from the element declaratively. Also for some IDL the corresponding attribute is removed as it can be confusing. For example, natievly the element’s id is removed by setting the IDL to an empty string.

For processing, the assign internally uses assignAttribute and classListDeclarative.

import { assignAttribute, classListDeclarative } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
 const paragraph= document.createElement("p");
 
 assignAttribute(paragraph, "textContent", "Hello, world!");
@@ -48,7 +48,7 @@ console.log(paragraph.outerHTML);
 document.body.append(
 	paragraph
 );
-

Native JavaScript templating

By default, the native JS has no good way to define HTML template using DOM API:

document.body.append(
+

Native JavaScript templating

By default, the native JS has no good way to define HTML template using DOM API:

document.body.append(
 	document.createElement("div"),
 	document.createElement("span"),
 	document.createElement("main")
@@ -59,7 +59,7 @@ const template= document.createElement("main").append(
 	document.createElement("span"),
 );
 console.log(typeof template==="undefined");
-

This library therefore overwrites the append method of created elements to always return parent element.

import { el } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
+

This library therefore overwrites the append method of created elements to always return parent element.

import { el } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
 document.head.append(
 	el("style").append(
 		"tr, td{ border: 1px solid red; padding: 1em; }",
@@ -83,7 +83,7 @@ document.body.append(
 		)
 	)
 );
-

Creating advanced (reactive) templates and components

Basic components

You can use functions for encapsulation (repeating) logic. The el accepts function as first argument. In that case, the function should return dom elements and the second argument for el is argument for given element.

import { el } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
+

Creating advanced (reactive) templates and components

Basic components

You can use functions for encapsulation (repeating) logic. The el accepts function as first argument. In that case, the function should return dom elements and the second argument for el is argument for given element.

import { el } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
 document.head.append(
 	el("style").append(
 		".class1{ font-weight: bold; }",
@@ -100,4 +100,4 @@ function component({ className, textContent }){
 		el("p", textContent)
 	);
 }
-

It is nice to use similar naming convention as native DOM API.

\ No newline at end of file +

It is nice to use similar naming convention as native DOM API.

\ No newline at end of file diff --git a/docs/index.html b/docs/index.html index aa1353d..a6b813e 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,4 +1,4 @@ -`deka-dom-el` — Introduction

`deka-dom-el` — Introduction

Introducing a library and motivations.

The library tries to provide pure JavaScript tool(s) to create reactive interfaces.

import { el, S } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
+`deka-dom-el` — Introduction

`deka-dom-el` — Introduction

Introducing a library and motivations.

The library tries to provide pure JavaScript tool(s) to create reactive interfaces.

import { el, S } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
 const clicks= S(0);
 document.body.append(
 	el().append(
@@ -12,4 +12,4 @@ document.body.append(
 		})
 	)
 );
-
\ No newline at end of file +
\ No newline at end of file diff --git a/docs_src/types.d.ts b/docs_src/types.d.ts index 13168fb..2d831f0 100644 --- a/docs_src/types.d.ts +++ b/docs_src/types.d.ts @@ -19,3 +19,9 @@ export type PageAttrs= { registerClientFile: registerClientFile } +import type { CustomHTMLTestElement } from "../examples/components/webComponent.js"; +declare global{ + interface ddePublicElementTagNameMap{ + ["custom-test"]: CustomHTMLTestElement; + } +} diff --git a/examples/components/webComponent.js b/examples/components/webComponent.js index 5232a3d..d845970 100644 --- a/examples/components/webComponent.js +++ b/examples/components/webComponent.js @@ -1,11 +1,17 @@ -import { el, scope } from "../../index.js"; +import { el, on, scope } from "../../index.js"; import { S } from "../../signals.js"; const { hasOwnProperty }= Object.prototype; +/** + * @typedef CustomHTMLTestElementInterface + * @type {object} + * @property {string} name + * */ /** * Compatible with `npx-wca test/components/webComponent.js` * */ export class CustomHTMLTestElement extends HTMLElement{ + static tagName= "custom-test"; static get observedAttributes(){ return [ "name", "pre-name" ]; } @@ -28,16 +34,18 @@ export class CustomHTMLTestElement extends HTMLElement{ el("#text", { textContent: name }), el("#text", { textContent: test }), el("#text", { textContent: preName }), + el("button", { type: "button", textContent: "pre-name", onclick: ()=> preName("Ahoj") }) ); } + + get name(){ return this.getAttribute("name"); } + set name(value){ this.setAttribute("name", value); } + get preName(){ return this.getAttribute("pre-name"); } + set preName(value){ this.setAttribute("pre-name", value); } } // https://gist.github.com/WebReflection/ec9f6687842aa385477c4afca625bbf4 -customElementsAssign( - CustomHTMLTestElement, - reflectObservedAttributes, - lifecycleToEvents, -); -customElements.define("custom-test", CustomHTMLTestElement); +lifecycleToEvents(CustomHTMLTestElement) +customElements.define(CustomHTMLTestElement.tagName, CustomHTMLTestElement); function customElementRender(_this, render){ scope.push({ scope: _this, host: (...a)=> a.length ? a[0](_this) : _this }); @@ -45,21 +53,6 @@ function customElementRender(_this, render){ scope.pop(); return out; } -/** @returns {HTMLElement} */ -function customElementsAssign(class_base, ...automatize){ - automatize.forEach(a=> a(class_base)); -} -function reflectObservedAttributes(c){ - for(const name of c.observedAttributes){ - const name_camel= name.replace(/([a-z])-([a-z])/g, (_, l, r)=> l+r.toUpperCase()); - if(hasOwnProperty.call(c.prototype, name_camel)) - continue; - Reflect.defineProperty(c.prototype, name_camel, { - get(){ return this.getAttribute(name); }, - set(value){ this.setAttribute(name, value); } - }); - } -} function lifecycleToEvents(class_declaration){ for (const name of [ "connected", "disconnected" ]) wrapMethod(class_declaration.prototype, name+"Callback", function(target, thisArg, detail){ diff --git a/examples/index.js b/examples/index.js index 4bfe947..5ba917c 100644 --- a/examples/index.js +++ b/examples/index.js @@ -8,5 +8,5 @@ document.body.append( el("h1", "Experiments:"), el(fullNameComponent), el(todosComponent), - el(customElements.getName(CustomHTMLTestElement), { name: "attr" }) + el(CustomHTMLTestElement.tagName, { name: "attr" }) ); diff --git a/index.d.ts b/index.d.ts index c53d3dc..2e5e334 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,9 +1,14 @@ type CustomElementTagNameMap= { '#text': Text, '#comment': Comment } +declare global { + interface ddePublicElementTagNameMap{ + } +} type SupportedElement= HTMLElementTagNameMap[keyof HTMLElementTagNameMap] | SVGElementTagNameMap[keyof SVGElementTagNameMap] | MathMLElementTagNameMap[keyof MathMLElementTagNameMap] - | CustomElementTagNameMap[keyof CustomElementTagNameMap]; + | CustomElementTagNameMap[keyof CustomElementTagNameMap] + | ddePublicElementTagNameMap[keyof ddePublicElementTagNameMap]; declare global { type ddeComponentAttributes= Record | undefined | string; type ddeElementModifier= (element: El)=> El; @@ -37,7 +42,7 @@ type AttrsModified= { type ElementAttributes= Omit & AttrsModified; export function assign(element: El, ...attrs_array: Partial>[]): El -type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagNameMap +type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagNameMap & ddePublicElementTagNameMap export function el( tag_name: TAG, attrs?: string | Partial>, @@ -86,6 +91,7 @@ export function elNS( export function dispatchEvent(element: SupportedElement, name: keyof DocumentEventMap): void; export function dispatchEvent(element: SupportedElement, name: string, data: any): void; interface On{ + /** Listens to the DOM event. See {@link Document.addEventListener} */ < EE extends ddeElementModifier, El extends ( EE extends ddeElementModifier ? El : never ), @@ -94,6 +100,7 @@ interface On{ listener: (this: El, ev: DocumentEventMap[Event]) => any, options?: AddEventListenerOptions ) : EE; + /** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ connected< EE extends ddeElementModifier, El extends ( EE extends ddeElementModifier ? El : never ) @@ -101,6 +108,7 @@ interface On{ listener: (el: El) => any, options?: AddEventListenerOptions ) : EE; + /** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ disconnected< EE extends ddeElementModifier, El extends ( EE extends ddeElementModifier ? El : never ) @@ -108,11 +116,37 @@ interface On{ listener: (el: El) => any, options?: AddEventListenerOptions ) : EE; + /** Listens to the element attribute changes. In case of custom elements uses [`attributeChangedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ + attributeChanged< + EE extends ddeElementModifier, + El extends ( EE extends ddeElementModifier ? El : never ) + >( + listener: (el: El) => any, + options?: AddEventListenerOptions + ) : EE; } export const on: On; +type Scope= { scope: Node | Function | Object, host: ddeElementModifier, prevent: boolean } +/** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */ export const scope: { + current: Scope, + /** Stops all automatizations. E. g. signals used as attributes in current scope + * registers removing these listeners (and clean signal if no other listeners are detected) + * on `disconnected` event. */ + preventDefault(prevent: T): T, + /** + * This represents reference to the current host element — `scope.host()`. + * It can be also used to register Modifier (function to be called when component is initized) + * — `scope.host(on.connected(console.log))`. + * */ host: ddeElementModifier, + + state: Scope[], + /** Adds new child scope. All attributes are inherited by default. */ + push(scope: Partial): ReturnType["push"]> + /** Removes last/current child scope. */ + pop(): ReturnType["pop"]> }; /* diff --git a/signals.d.ts b/signals.d.ts index 27ca041..4d99776 100644 --- a/signals.d.ts +++ b/signals.d.ts @@ -30,6 +30,10 @@ interface S { * by `S.clear`. * */ >(value: V, actions?: A): Signal; + /** + * Computations signal. This creates a signal which is computed from other signals. + * */ + (computation: ()=> V): Signal action>, A extends (S extends Signal ? A : never), N extends keyof A>( signal: S, name: N, @@ -41,7 +45,17 @@ interface S { signal: SymbolSignal; onclear: SymbolOnclear; } - el(signal: Signal, el: (v: S)=> T): T; + /** + * Reactive element, which is rendered based on the given signal. + * ```js + * S.el(signal, value=> value ? el("b", "True") : el("i", "False")); + * S.el(listS, list=> list.map(li=> el("li", li))); + * ``` + * */ + el(signal: Signal, el: (v: S)=> Element | Element[]): DocumentFragment; + + /** Mirrors element attributes for current host (both way). */ + attribute(name: string, initial?: T): Signal } export const S: S; declare global { diff --git a/src/signals-lib.js b/src/signals-lib.js index 8598c71..920a627 100644 --- a/src/signals-lib.js +++ b/src/signals-lib.js @@ -67,7 +67,9 @@ import { scope } from "./dom.js"; const key_attributes= "__dde_attributes"; S.attribute= function(name, initial= undefined){ const out= S(initial); + let host; scope.host(element=> { + host= element; if(element instanceof HTMLElement){ if(element.hasAttribute(name)) out(element.getAttribute(name)); } else { @@ -83,16 +85,26 @@ S.attribute= function(name, initial= undefined){ * Investigate `__dde_attributes` key of the element.*/ const [ name, value ]= detail; const curr= element[key_attributes][name]; - if(curr) curr(value); + if(curr) return curr(value); })(element); on.disconnected(function(){ /*! This removes all signals mapped to attributes (`S.attribute`). * Investigate `__dde_attributes` key of the element.*/ S.clear(...Object.values(element[key_attributes])); + host= null; })(element); return element; }); - return out; + return new Proxy(out, { + apply(target, _, args){ + if(!args.length) return target(); + if(!host) return; + const value= args[0]; + if(host instanceof HTMLElement) + return host.setAttribute(name, value); + return host.setAttributeNS(null, name, value); + } + }); }; S.clear= function(...signals){ for(const signal of signals){