From 4a277188883270b68e7cb20095c91be597a38c32 Mon Sep 17 00:00:00 2001 From: Sasha Firsov Date: Mon, 1 Apr 2024 23:35:55 -0700 Subject: [PATCH 01/15] `observedAttributes` fix --- README.md | 3 ++- custom-element.js | 15 +++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3892cb7..350d960 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,8 @@ npm i -P @epa-wg/custom-element yarn add @epa-wg/custom-element ``` -## [enable IDE support](ide/IDE.md) +## Enable IDE support +[IDE.md](ide/IDE.md) diff --git a/custom-element.js b/custom-element.js index 799e180..9f08f70 100644 --- a/custom-element.js +++ b/custom-element.js @@ -436,16 +436,15 @@ CustomElement extends HTMLElement Object.defineProperty( this, "xsltString", { get: ()=>templateDocs.map( td => xmlString(td) ).join('\n') }); - const dce = this; - const sliceNames = [...this.templateNode.querySelectorAll('[slice]')].map(e=>attr(e,'slice')); + const dce = this + , sliceNodes = [...this.templateNode.querySelectorAll('[slice]')] + , sliceNames = sliceNodes.map(e=>attr(e,'slice')).filter(n=>!n.includes('/')) + , declaredAttributes = templateDocs.reduce( (ret,t) => { if( t.params ) ret.push( ...t.params ); return ret; }, [] ); + class DceElement extends HTMLElement { - static get observedAttributes() - { return templateDocs.reduce( (ret,t) => - { if( t.params ) ret.push( ...t.params.map(e=>attr(e,'name')) ); - return ret; - }, [] ); - } + static get observedAttributes(){ return declaredAttributes.map( a=>attr(a,'name')); } + connectedCallback() { if( this.firstElementChild?.tagName === 'TEMPLATE' ) { const t = this.firstElementChild; From 15a9852b586f46957e1feebc170e79712964359d Mon Sep 17 00:00:00 2001 From: Sasha Firsov Date: Tue, 2 Apr 2024 14:02:42 -0700 Subject: [PATCH 02/15] #44 DCE attributes missing,observedAttributes fix --- custom-element.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/custom-element.js b/custom-element.js index 9f08f70..d644dd9 100644 --- a/custom-element.js +++ b/custom-element.js @@ -212,8 +212,9 @@ createXsltFromDom( templateNode, S = 'xsl:stylesheet' ) if( !fr ) return console.error("transformation error",{ xml:tc.outerHTML, xsl: xmlString( sanitizeXsl ) }); const params = []; - [...fr.querySelectorAll('dce-root>attribute')].forEach(p=> - { p = cloneAs(p,'xsl:param'); + [...fr.querySelectorAll('dce-root>attribute')].forEach( a=> + { + const p = cloneAs(a,'xsl:param'); payload.append(p); let select = attr(p,'select')?.split('??') if( !select) @@ -226,8 +227,11 @@ createXsltFromDom( templateNode, S = 'xsl:stylesheet' ) c.firstElementChild.setAttribute('test',select[0]); emptyNode(c.firstElementChild).append( createText(c,'{'+select[0]+'}')); emptyNode(c.lastElementChild ).append( createText(c,'{'+select[1]+'}')); - p.append(c) - } + p.append(c); + a.append(c.cloneNode(true)); + }else + a.append(cloneAs(a,'xsl:value-of')); + a.removeAttribute('select'); params.push(p) }); @@ -527,6 +531,12 @@ CustomElement extends HTMLElement if( hasInitValue(el) ) changeCb(el) } + }); + // merge attributes from dce-root to `this` + [...this.firstElementChild.attributes].map(a=> { + if( a.value !== this.getAttribute(a.name)) + this.setAttribute(a.name, a.value); + // this.firstElementChild.removeAttributeNode(a); }) }; transform(); From b70fcb427a2cd7456a3663ac80526ec67e8fa3ba Mon Sep 17 00:00:00 2001 From: Sasha Firsov Date: Tue, 2 Apr 2024 20:25:38 -0700 Subject: [PATCH 03/15] attributes update fix --- custom-element.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/custom-element.js b/custom-element.js index d644dd9..7e5ebba 100644 --- a/custom-element.js +++ b/custom-element.js @@ -131,7 +131,7 @@ createXsltFromDom( templateNode, S = 'xsl:stylesheet' ) { if( templateNode.tagName === S || templateNode.documentElement?.tagName === S ) return tagUid(templateNode) - const sanitizeXsl = xml2dom(` + const sanitizeXsl = xml2dom(` @@ -221,6 +221,7 @@ createXsltFromDom( templateNode, S = 'xsl:stylesheet' ) { select = ['//'+attr(p, 'name'), `'${p.textContent}'`]; emptyNode(p); } + let val; if( select?.length>1 ){ p.removeAttribute('select'); const c = $( xslDom, 'template[match="ignore"]>choose').cloneNode(true); @@ -228,9 +229,11 @@ createXsltFromDom( templateNode, S = 'xsl:stylesheet' ) emptyNode(c.firstElementChild).append( createText(c,'{'+select[0]+'}')); emptyNode(c.lastElementChild ).append( createText(c,'{'+select[1]+'}')); p.append(c); - a.append(c.cloneNode(true)); + val = c.cloneNode(true); }else - a.append(cloneAs(a,'xsl:value-of')); + val=cloneAs(a,'xsl:value-of'); + val.removeAttribute('name'); + a.append(val); a.removeAttribute('select'); params.push(p) }); @@ -385,7 +388,7 @@ export function merge( parent, fromArr ) { if( o.nodeValue !== e.nodeValue ) o.nodeValue = e.nodeValue; }else - { mergeAttr(o,e) + { mergeAttr(e,o) if( o.childNodes.length || e.childNodes.length ) merge(o, e.childNodes) } @@ -532,11 +535,9 @@ CustomElement extends HTMLElement changeCb(el) } }); - // merge attributes from dce-root to `this` - [...this.firstElementChild.attributes].map(a=> { - if( a.value !== this.getAttribute(a.name)) - this.setAttribute(a.name, a.value); - // this.firstElementChild.removeAttributeNode(a); + DceElement.observedAttributes.map( a => { + if( attr(this.firstElementChild,a) !== attr(this,a) ) + this.setAttribute( a, attr(this.firstElementChild,a) ); }) }; transform(); From a73a36a4db77430b2d6526ca44db11be3582d253 Mon Sep 17 00:00:00 2001 From: Sasha Firsov Date: Tue, 2 Apr 2024 22:55:52 -0700 Subject: [PATCH 04/15] slice sets attribute --- custom-element.js | 33 ++++++++++++++++++++++++++++----- demo/external-template.html | 1 + demo/parameters.html | 15 +++++++++++++++ 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/custom-element.js b/custom-element.js index 7e5ebba..5786747 100644 --- a/custom-element.js +++ b/custom-element.js @@ -296,16 +296,24 @@ deepEqual(a, b, O=false) } export function -injectSlice( x, s, data ) +injectSlice( x, s, data, dce ) { + if( s.includes('/') ) + { const it = x.ownerDocument.evaluate(s,x) + , n = it.iterateNext(); + if( n.parentNode.localName ==='attributes') + dce.setAttribute( n.localName, data ); + n.textContent = data; + return + } const isString = typeof data === 'string' ; const createXmlNode = ( tag, t = '' ) => ( e => ((e.append( createText(x, t||''))),e) )(x.ownerDocument.createElement( tag )) const el = isString ? createXmlNode(s, data) : document.adoptNode( xml2dom( Json2Xml( data, s ) ).documentElement); [...x.children].filter( e=>e.localName === s ).map( el=>el.remove() ); - el.data = data - x.append(el); + el.data = data; + x.append(el); } function forEach$( el, css, cb){ @@ -401,6 +409,18 @@ export function assureUID(n,attr) n.setAttribute(attr, crypto.randomUUID()); return n.getAttribute(attr) } +export const xPath = (x,root)=>{ + let ret = ''; + const it = root.ownerDocument.evaluate(x, root); + let thisNode = it.iterateNext(); + + while (thisNode) { + console.log(thisNode.textContent); + ret+= thisNode.textContent; + thisNode = it.iterateNext(); + } + return ret +} export const xslTags = 'stylesheet,transform,import,include,strip-space,preserve-space,output,key,decimal-format,namespace-alias,template,value-of,copy-of,number,apply-templates,apply-imports,for-each,sort,if,choose,when,otherwise,attribute-set,call-template,with-param,variable,param,text,processing-instruction,element,attribute,comment,copy,message,fallback'.split(','); export const toXsl = (el, defParent) => { const x = create('xsl:'+el.localName); @@ -479,7 +499,8 @@ CustomElement extends HTMLElement this.innerHTML=''; injectData( x, 'attributes' , this.attributes, e => createXmlNode( e.nodeName, e.value ) ); injectData( x, 'dataset', Object.keys( this.dataset ), k => createXmlNode( k, this.dataset[ k ] ) ); - const sliceRoot = injectData( x, 'slice', sliceNames, k => createXmlNode( k, '' ) ); + const sliceRoot = injectData( x, 'slice', sliceNames, k => createXmlNode( k, '' ) ) + , slicePath = x => xPath(x, sliceRoot); this.xml = x; const sliceEvents=[]; @@ -490,7 +511,7 @@ CustomElement extends HTMLElement { const s = attr( ev.target, 'slice'); if( processed[s] ) continue; - injectSlice( sliceRoot, s, 'object' === typeof ev.detail ? {...ev.detail}: ev.detail ); + injectSlice( sliceRoot, s, 'object' === typeof ev.detail ? {...ev.detail}: ev.detail, this ); processed[s] = ev; } Object.keys(processed).length !== 0 && transform(); @@ -533,6 +554,8 @@ CustomElement extends HTMLElement el.addEventListener( attr(el,'slice-update')|| 'change', ()=>changeCb(el) ) if( hasInitValue(el) ) changeCb(el) + else + el.value = slicePath( attr(el,'slice') ) } }); DceElement.observedAttributes.map( a => { diff --git a/demo/external-template.html b/demo/external-template.html index bc8d7d2..0f829f1 100644 --- a/demo/external-template.html +++ b/demo/external-template.html @@ -78,6 +78,7 @@

Loading DCE template by SRC attribute

+ tree.xsl + + Type in the input field to see the variable $title change. Hover the mouse to see the title attribute text popup. + + + From 10feffb6bc682cb836db40f755a7b546a059df29 Mon Sep 17 00:00:00 2001 From: Sasha Firsov Date: Sat, 13 Apr 2024 14:39:33 -0700 Subject: [PATCH 05/15] * slice default value * slice sets attribute * slice/name/value for object in value --- custom-element.js | 198 ++++++++++++++++++++++++++++++---------- demo/data-slices.html | 87 ++++++++++++++++++ demo/demo.css | 1 + demo/dom-merge.html | 2 +- demo/local-storage.html | 4 +- demo/parameters.html | 7 +- ide/customData-dce.json | 17 +++- ide/web-types-dce.json | 9 +- index.html | 3 +- 9 files changed, 268 insertions(+), 60 deletions(-) create mode 100644 demo/data-slices.html diff --git a/custom-element.js b/custom-element.js index 5786747..435c2a5 100644 --- a/custom-element.js +++ b/custom-element.js @@ -7,9 +7,11 @@ const XSL_NS_URL = 'http://www.w3.org/1999/XSL/Transform' const attr = (el, attr)=> el.getAttribute?.(attr) , isText = e => e.nodeType === 3 +, isString = s => typeof s === 'string' +, isNode = e => e && typeof e.nodeType === 'number' , create = ( tag, t = '', d=document ) => ( e => ((e.innerText = t||''),e) )((d.ownerDocument || d ).createElement( tag )) , createText = ( d, t) => (d.ownerDocument || d ).createTextNode( t ) -, emptyNode = n=> { while(n.firstChild) n.firstChild.remove(); return n; } +, emptyNode = n => { while(n.firstChild) n.firstChild.remove(); n.getAttributeNames().map( a => n.removeAttribute(a) ); return n; } , createNS = ( ns, tag, t = '' ) => ( e => ((e.innerText = t||''),e) )(document.createElementNS( ns, tag )) , xslNs = x => ( x?.setAttribute('xmlns:xsl', XSL_NS_URL ), x ) , xslHtmlNs = x => ( x?.setAttribute('xmlns:xhtml', HTML_NS_URL ), xslNs(x) ) @@ -90,6 +92,31 @@ Json2Xml( o, tag ) ret.push("/>"); return ret.join('\n'); } + + export function +obj2node( o, tag, doc ) +{ + if( typeof o === 'function') + {debugger} + // tag = tag.replace( /[^a-z0-9\-]/gi,'_' ).toLowerCase(); + if( typeof o === 'string' ) + return create(tag,o,doc); + + if( o instanceof Array ) + { const ret = create('array'); + o.map( ae => ret.append( obj2node(ae,tag,doc)) ); + } + const ret = create(tag,'',doc); + for( let k in o ) + if( isNode(o[k]) || typeof o[k] ==='function' || o[k] instanceof Window ) + continue + else + if( typeof o[k] !== "object" ) + ret.setAttribute(k, o[k] ); + else + ret.append(obj2node(o[k], k, doc)) + return ret; +} export function tagUid( node ) { // {} to xsl:value-of @@ -214,20 +241,22 @@ createXsltFromDom( templateNode, S = 'xsl:stylesheet' ) const params = []; [...fr.querySelectorAll('dce-root>attribute')].forEach( a=> { - const p = cloneAs(a,'xsl:param'); + const p = cloneAs(a,'xsl:param') + , name = attr(a,'name'); payload.append(p); let select = attr(p,'select')?.split('??') if( !select) - { select = ['//'+attr(p, 'name'), `'${p.textContent}'`]; + { select = ['//'+name, `'${p.textContent}'`]; emptyNode(p); + p.setAttribute('name',name); } - let val; + let val; if( select?.length>1 ){ p.removeAttribute('select'); const c = $( xslDom, 'template[match="ignore"]>choose').cloneNode(true); - c.firstElementChild.setAttribute('test',select[0]); emptyNode(c.firstElementChild).append( createText(c,'{'+select[0]+'}')); emptyNode(c.lastElementChild ).append( createText(c,'{'+select[1]+'}')); + c.firstElementChild.setAttribute('test',select[0]); p.append(c); val = c.cloneNode(true); }else @@ -237,7 +266,11 @@ createXsltFromDom( templateNode, S = 'xsl:stylesheet' ) a.removeAttribute('select'); params.push(p) }); - + [...fr.querySelectorAll('[value]')].filter(el=>el.getAttribute('value').match( /\{(.*)\?\?(.*)\}/g )).forEach(el=> + { const v = attr(el,'value'); + if(v) + el.setAttribute('value', evalCurly(v)); + }); for( const c of fr.childNodes ) payload.append(xslDom.importNode(c,true)) @@ -294,26 +327,73 @@ deepEqual(a, b, O=false) return O return true; } - + export const +assureSlices = ( root, names) => + names.split('|').map(n=>n.trim()).map( xp => + { if(xp.includes('/')) + { const ret = [], r = root.ownerDocument.evaluate( xp, root ); + for( let n; n = r.iterateNext(); ) + ret.push( n ) + return ret + } + return [...root.childNodes].find(n=>n.localName === xp) || create(xp); + }).flat(); + +/** + * + * @param x slice node + * @param sliceNames slice name, xPath in /datadom/slice/ + * @param ev Event obj + * @param dce + */ export function -injectSlice( x, s, data, dce ) +event2slice( x, sliceNames, ev, dce ) { - if( s.includes('/') ) - { const it = x.ownerDocument.evaluate(s,x) - , n = it.iterateNext(); - if( n.parentNode.localName ==='attributes') - dce.setAttribute( n.localName, data ); - n.textContent = data; - return - } - const isString = typeof data === 'string' ; - const createXmlNode = ( tag, t = '' ) => ( e => ((e.append( createText(x, t||''))),e) )(x.ownerDocument.createElement( tag )) - const el = isString - ? createXmlNode(s, data) - : document.adoptNode( xml2dom( Json2Xml( data, s ) ).documentElement); - [...x.children].filter( e=>e.localName === s ).map( el=>el.remove() ); - el.data = data; - x.append(el); + // evaluate slices[] + // inject @attributes + // inject event + // evaluate slice-value + // slice[i] = slice-value + assureSlices(x,sliceNames).map( s => + { + const d = x.ownerDocument + , el = ev.target + , cleanSliceValue = ()=>[...s.childNodes].filter(n=>n.nodeType===3 || n.localName==='value').map(n=>n.remove()); + el.getAttributeNames().map( a => s.setAttribute( a, attr(el,a) ) ); + [...s.childNodes].filter(n=>n.localName==='event').map(n=>n.remove()); + ev.type==='init' && cleanSliceValue(); + if( el.hasAttribute('slice-value') ) + { const v = xPath( attr( el, 'slice-value'),s ); + cleanSliceValue(); + s.append( createText( d, v ) ); + }else + { const v = el.value || attr( el, 'value' ) ; + cleanSliceValue(); + if( isString(v) ) + s.append( createText( d, v) ); + else + s.append( obj2node(v,'value',s.ownerDocument) ) + } + s.append( obj2node( ev, 'event', d ) ); + }) + // if( s.includes('/') ) + // { const it = x.ownerDocument.evaluate(s,x) + // , n = it.iterateNext(); + // if( n.parentNode.localName ==='attributes' ) + // dce.setAttribute( n.localName, data ); + // n.textContent = data; + // return + // } + // const isString = typeof data === 'string' ; + // if( isString ) + // data = ''+xPath(data, x); + // const createXmlNode = ( tag, t = '' ) => ( e => ((e.append( createText(x, t||''))),e) )(x.ownerDocument.createElement( tag )) + // const el = isString + // ? createXmlNode(s, data) + // : document.adoptNode( xml2dom( Json2Xml( data, s ) ).documentElement); + // [...x.children].filter( e=>e.localName === s ).map( el=>el.remove() ); + // el.data = data; + // x.append(el); } function forEach$( el, css, cb){ @@ -409,17 +489,33 @@ export function assureUID(n,attr) n.setAttribute(attr, crypto.randomUUID()); return n.getAttribute(attr) } -export const xPath = (x,root)=>{ - let ret = ''; +export const evalCurly = s => +{ const exp = [...s?.matchAll( /([^{}]*)(\{)([^}]+)}([^{}]*)/g ) ].map(l=>`${l[1]}{${ xPathDefaults(l[3] )}}${l[4]}`); + return exp.join(''); +} +export const xPathDefaults = x=> +{ if(!x.trim()) + return x; + const xx = x.split('??') + , a = xx.shift() + , b = xPathDefaults(xx.join('??')); + + return xx.length ? `concat( ${a} , substring( ${b} , (1+string-length( ${b} )) * string-length( ${a} ) ) )`: x + // return xx.length ? `${a}|(${xPathDefaults(xx.join('??'))})[not(${a})]`: a +} +export const xPath = (x,root)=> +{ x = xPathDefaults(x); + const it = root.ownerDocument.evaluate(x, root); - let thisNode = it.iterateNext(); - - while (thisNode) { - console.log(thisNode.textContent); - ret+= thisNode.textContent; - thisNode = it.iterateNext(); - } - return ret + switch( it.resultType ) + { case XPathResult.NUMBER_TYPE: return it.numberValue; + case XPathResult.STRING_TYPE: return it.stringValue; + } + + let ret = ''; + for( let n ;n=it.iterateNext(); ) + ret += n.textContent; + return ret } export const xslTags = 'stylesheet,transform,import,include,strip-space,preserve-space,output,key,decimal-format,namespace-alias,template,value-of,copy-of,number,apply-templates,apply-imports,for-each,sort,if,choose,when,otherwise,attribute-set,call-template,with-param,variable,param,text,processing-instruction,element,attribute,comment,copy,message,fallback'.split(','); export const toXsl = (el, defParent) => { @@ -465,7 +561,7 @@ CustomElement extends HTMLElement const dce = this , sliceNodes = [...this.templateNode.querySelectorAll('[slice]')] - , sliceNames = sliceNodes.map(e=>attr(e,'slice')).filter(n=>!n.includes('/')) + , sliceNames = sliceNodes.map(e=>attr(e,'slice')).filter(n=>!n.includes('/')).filter((v, i, a)=>a.indexOf(v) === i) , declaredAttributes = templateDocs.reduce( (ret,t) => { if( t.params ) ret.push( ...t.params ); return ret; }, [] ); class DceElement extends HTMLElement @@ -500,7 +596,7 @@ CustomElement extends HTMLElement injectData( x, 'attributes' , this.attributes, e => createXmlNode( e.nodeName, e.value ) ); injectData( x, 'dataset', Object.keys( this.dataset ), k => createXmlNode( k, this.dataset[ k ] ) ); const sliceRoot = injectData( x, 'slice', sliceNames, k => createXmlNode( k, '' ) ) - , slicePath = x => xPath(x, sliceRoot); + , sliceXPath = x => xPath(x, sliceRoot); this.xml = x; const sliceEvents=[]; @@ -511,7 +607,7 @@ CustomElement extends HTMLElement { const s = attr( ev.target, 'slice'); if( processed[s] ) continue; - injectSlice( sliceRoot, s, 'object' === typeof ev.detail ? {...ev.detail}: ev.detail, this ); + event2slice( sliceRoot, s, ev, this ); processed[s] = ev; } Object.keys(processed).length !== 0 && transform(); @@ -521,8 +617,8 @@ CustomElement extends HTMLElement this.onSlice = ev=> { ev.stopPropagation?.(); const s = attr( ev.target, 'slice') - if( deepEqual( ev.detail, [...sliceRoot.children].find( e=>e.localName === s )?.data ) ) - return + // if( deepEqual( ev.detail, [...sliceRoot.children].find( e=>e.localName === s )?.data ) ) + // return sliceEvents.push(ev); if( !timeoutID ) @@ -545,23 +641,25 @@ CustomElement extends HTMLElement assureUnique(f); merge( this, f.childNodes ) }) - const changeCb = el=>this.onSlice({ detail: el[attr(el,'slice-prop') || 'value'], target: el }) - , hasInitValue = el => el.hasAttribute('slice-prop') || el.hasAttribute('value') || el.value; + + DceElement.observedAttributes.map( a => { + if( attr(this.firstElementChild,a) !== attr(this,a) ) + this.setAttribute( a, attr(this.firstElementChild,a) ); + }) forEach$( this,'[slice]', el => { if( !el.dceInitialized ) { el.dceInitialized = 1; - el.addEventListener( attr(el,'slice-update')|| 'change', ()=>changeCb(el) ) - if( hasInitValue(el) ) - changeCb(el) - else - el.value = slicePath( attr(el,'slice') ) + const evs = attr(el,'slice-event'); + el.addEventListener( evs || 'change', ev=>this.onSlice(ev) ); + if( !evs || evs.includes('init') ) + { if( el.hasAttribute('slice-value') || el.hasAttribute('value') || el.value ) + this.onSlice({type:'init', target: el }) + else + el.value = sliceXPath( attr(el,'slice') ) + } } }); - DceElement.observedAttributes.map( a => { - if( attr(this.firstElementChild,a) !== attr(this,a) ) - this.setAttribute( a, attr(this.firstElementChild,a) ); - }) }; transform(); applySlices(); diff --git a/demo/data-slices.html b/demo/data-slices.html new file mode 100644 index 0000000..d07f904 --- /dev/null +++ b/demo/data-slices.html @@ -0,0 +1,87 @@ + + + + + Data slices - Declarative Custom Element implementation demo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/demo.css b/demo/demo.css index 9410d19..e851ab3 100644 --- a/demo/demo.css +++ b/demo/demo.css @@ -6,6 +6,7 @@ body,nav{ display: flex; flex-wrap: wrap; align-content: stretch; gap: 1rem; } body>*{flex: auto;} nav{ flex-direction: column;} custom-element+*, +custom-element+*+*, custom-element:not([tag]), dce-link,dce-1-slot,dce-2-slot,dce-3-slot,dce-4-slot,dce-2-slots,greet-element,pokemon-tile, dce-1,dce-2,dce-3,dce-4,dce-internal,dce-hash diff --git a/demo/dom-merge.html b/demo/dom-merge.html index 31145b7..ff96ab0 100644 --- a/demo/dom-merge.html +++ b/demo/dom-merge.html @@ -96,7 +96,7 @@

DOM merge. DCE dynamic update with focus preservation.

+ slice-event="keyup"/> Character count: {string-length(//slice/txt)} diff --git a/demo/local-storage.html b/demo/local-storage.html index 8c37a91..09ac3a8 100644 --- a/demo/local-storage.html +++ b/demo/local-storage.html @@ -45,7 +45,7 @@ - + {name()} {.} @@ -55,7 +55,7 @@ 🤔 - {sum(//slice/basket/@*)} + {sum(//slice/basket/value/@*)} diff --git a/demo/parameters.html b/demo/parameters.html index 3fe0640..675b9b5 100644 --- a/demo/parameters.html +++ b/demo/parameters.html @@ -47,12 +47,15 @@ - Type in the input field to see the variable $title change. Hover the mouse to see the title attribute text popup. + Type in the input field to see the variable $title change.
+ Hover the mouse to see the title attribute text popup.
+ Inspect DCE node in dev tools to see `title` attribute updated while typing. +
+ + + + + + + + diff --git a/ide/web-types-dce.json b/ide/web-types-dce.json index 231c713..31ae2cb 100644 --- a/ide/web-types-dce.json +++ b/ide/web-types-dce.json @@ -1,7 +1,7 @@ { "$schema": "http://json.schemastore.org/web-types", "name": "@epa-wg/custom-element", - "version": "0.0.18", + "version": "0.0.19", "js-types-syntax": "typescript", "description-markup": "markdown", "contributions": { diff --git a/ide/web-types-xsl.json b/ide/web-types-xsl.json index 3680165..1b0df69 100644 --- a/ide/web-types-xsl.json +++ b/ide/web-types-xsl.json @@ -1,7 +1,7 @@ { "$schema": "http://json.schemastore.org/web-types", "name": "@epa-wg/custom-element", - "version": "0.0.18", + "version": "0.0.19", "js-types-syntax": "typescript", "description-markup": "markdown", "contributions": { diff --git a/package-lock.json b/package-lock.json index cd0eb91..8b9c174 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@epa-wg/custom-element", - "version": "0.0.18", + "version": "0.0.19", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@epa-wg/custom-element", - "version": "0.0.18", + "version": "0.0.19", "license": "Apache-2.0", "funding": { "type": "patreon", diff --git a/package.json b/package.json index 56c1f34..ab8f1e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@epa-wg/custom-element", - "version": "0.0.18", + "version": "0.0.19", "description": "Declarative Custom Element as W3C proposal PoC with native(XSLT) based templating", "browser": "custom-element.js", "module": "custom-element.js",