@epa-wg/custom-element 0.0.18 → 0.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,16 +22,17 @@ UI is re-rendered on each data slice change triggered by initialization or DOM e
22
22
  <details>
23
23
  <summary> What is DCE? </summary>
24
24
  DCE provides the next level of abstraction in HTML - native composition. With native implementation which is
25
- streaming parser, streaming transformation, multithreading. native assumes the C/Rust compiled code. There is no place for JavaScript except of polyfill and ability to extend DCE, which otherwise has to be native.
25
+ streaming parser, streaming transformation, multithreading. native assumes the C/Rust compiled code.
26
+ There is no place for JavaScript except of polyfill and ability to extend DCE, which otherwise has to be native.
26
27
 
27
28
  The composition assumes the fully functional template and ability to call the template with parameters( custom tag + attributes) .
28
29
 
29
- As the next to HTML abstraction layer - composition, it needs and provide:
30
+ As the next to HTML abstraction layer - **composition**, it provides:
30
31
  * ability to use dependencies as from withing the page as from external file/lib via src attribute and # in URL
31
32
  * ability to treat external content via content-type like html, SVG, images, video with own template rendering
32
33
  * provide styles and embedded DCE declarations in own and named(lib) scope, sharing the scoped registry.
33
34
 
34
- As the next to composition layer of **functional component** it provides
35
+ After composition the layer of **functional component** provides
35
36
  * data layer with access to attributes/payload(+slots), dataset, data bound slice
36
37
  * means in template to use the data selector for condition/enumeration/text injection into attributes and DOM
37
38
  * Set of native primitives to support browser APIs declaratively: location,storage, http request which bonded to slice and as result to reactive UI.
@@ -58,12 +59,35 @@ npm i -P @epa-wg/custom-element
58
59
  yarn add @epa-wg/custom-element
59
60
  ```
60
61
 
61
- ## [enable IDE support](ide/IDE.md)
62
+ ## Enable IDE support
63
+ [IDE.md](ide/IDE.md)
62
64
 
63
65
 
64
66
 
65
67
  ## [Live demo 🔗][demo-url]
68
+
69
+ ### Interactivity via data `slice` triggered by events
70
+ ```html
71
+ <custom-element>
72
+ <input slice="typed"> //slice/typed : {//slice/typed}
73
+ </custom-element>
74
+
75
+ <custom-element>
76
+ <template>
77
+ <button slice="clickcount"
78
+ slice-event="click"
79
+ slice-value="//clickcount + 1" > + </button>
80
+ <input slice="clickcount" type="number" value="{//clickcount ?? 0}">
81
+ Click count: { //clickcount }
82
+ </template>
83
+ </custom-element>
84
+ ```
85
+ More on `slice` concept in [slice and events demo page][slice-demo-url]
86
+
87
+ ### Templating power
88
+ comes from XSLT and XPath. Which is natively implemented in all current browsers, globally tested and well documented.
66
89
  ```html
90
+
67
91
  <custom-element tag="pokemon-tile" hidden>
68
92
  <h3>{title}</h3> <!-- title is an attribute in instance
69
93
  mapped into /*/attributes/title -->
@@ -315,6 +339,7 @@ within template
315
339
  [git-test-url]: https://github.com/EPA-WG/custom-element-test
316
340
  [demo-url]: https://unpkg.com/@epa-wg/custom-element@0.0/index.html
317
341
  [css-demo-url]: https://unpkg.com/@epa-wg/custom-element@0.0/demo/scoped-css.html
342
+ [slice-demo-url]: https://unpkg.com/@epa-wg/custom-element@0.0/demo/data-slices.html
318
343
  [hex-grid-url]: https://unpkg.com/@epa-wg/custom-element@0.0/demo/hex-grid.html
319
344
  [hex-grid-image]: demo/hex-grid-transform.png
320
345
  [local-storage-demo]: https://unpkg.com/@epa-wg/custom-element@0.0/demo/local-storage.html
@@ -323,9 +348,9 @@ within template
323
348
  [github-image]: https://cdnjs.cloudflare.com/ajax/libs/octicons/8.5.0/svg/mark-github.svg
324
349
  [npm-image]: https://img.shields.io/npm/v/@epa-wg/custom-element.svg
325
350
  [npm-url]: https://npmjs.org/package/@epa-wg/custom-element
326
- [coverage-image]: https://unpkg.com/@epa-wg/custom-element-test@0.0.18/coverage/coverage.svg
327
- [coverage-url]: https://unpkg.com/@epa-wg/custom-element-test@0.0.18/coverage/lcov-report/index.html
328
- [storybook-url]: https://unpkg.com/@epa-wg/custom-element-test@0.0.18/storybook-static/index.html?path=/story/welcome--introduction
351
+ [coverage-image]: https://unpkg.com/@epa-wg/custom-element-test@0.0.19/coverage/coverage.svg
352
+ [coverage-url]: https://unpkg.com/@epa-wg/custom-element-test@0.0.19/coverage/lcov-report/index.html
353
+ [storybook-url]: https://unpkg.com/@epa-wg/custom-element-test@0.0.19/storybook-static/index.html?path=/story/welcome--introduction
329
354
  [sandbox-url]: https://stackblitz.com/github/EPA-WG/custom-element?file=index.html
330
355
  [webcomponents-url]: https://www.webcomponents.org/element/@epa-wg/custom-element
331
356
  [webcomponents-img]: https://img.shields.io/badge/webcomponents.org-published-blue.svg
@@ -116,7 +116,7 @@ writeFileSync( '.././ide/customData-xsl.json', JSON.stringify( vsCode, undefined
116
116
  const intelliJ = {
117
117
  "$schema": "http://json.schemastore.org/web-types",
118
118
  "name": "@epa-wg/custom-element",
119
- "version": "0.0.18",
119
+ "version": "0.0.19",
120
120
  "js-types-syntax": "typescript",
121
121
  "description-markup": "markdown",
122
122
  "contributions": {
package/custom-element.js CHANGED
@@ -7,9 +7,11 @@ const XSL_NS_URL = 'http://www.w3.org/1999/XSL/Transform'
7
7
 
8
8
  const attr = (el, attr)=> el.getAttribute?.(attr)
9
9
  , isText = e => e.nodeType === 3
10
- , create = ( tag, t = '', d=document ) => ( e => ((e.innerText = t||''),e) )((d.ownerDocument || d ).createElement( tag ))
10
+ , isString = s => typeof s === 'string'
11
+ , isNode = e => e && typeof e.nodeType === 'number'
12
+ , create = ( tag, t = '', d=document ) => ( e => ((t && e.append(createText(d.ownerDocument||d, t))),e) )((d.ownerDocument || d ).createElement( tag ))
11
13
  , createText = ( d, t) => (d.ownerDocument || d ).createTextNode( t )
12
- , emptyNode = n=> { while(n.firstChild) n.firstChild.remove(); return n; }
14
+ , emptyNode = n => { while(n.firstChild) n.firstChild.remove(); n.getAttributeNames().map( a => n.removeAttribute(a) ); return n; }
13
15
  , createNS = ( ns, tag, t = '' ) => ( e => ((e.innerText = t||''),e) )(document.createElementNS( ns, tag ))
14
16
  , xslNs = x => ( x?.setAttribute('xmlns:xsl', XSL_NS_URL ), x )
15
17
  , xslHtmlNs = x => ( x?.setAttribute('xmlns:xhtml', HTML_NS_URL ), xslNs(x) )
@@ -90,6 +92,30 @@ Json2Xml( o, tag )
90
92
  ret.push("/>");
91
93
  return ret.join('\n');
92
94
  }
95
+
96
+ export function
97
+ obj2node( o, tag, doc )
98
+ {
99
+ if( typeof o === 'function'){debugger}
100
+ if( typeof o === 'string' )
101
+ return create(tag,o,doc);
102
+
103
+ if( o instanceof Array )
104
+ { const ret = create('array');
105
+ o.map( ae => ret.append( obj2node(ae,tag,doc)) );
106
+ return ret
107
+ }
108
+ const ret = create(tag,'',doc);
109
+ for( let k in o )
110
+ if( isNode(o[k]) || typeof o[k] ==='function' || o[k] instanceof Window )
111
+ continue
112
+ else
113
+ if( typeof o[k] !== "object" )
114
+ ret.setAttribute(k, o[k] );
115
+ else
116
+ ret.append(obj2node(o[k], k, doc))
117
+ return ret;
118
+ }
93
119
  export function
94
120
  tagUid( node )
95
121
  { // {} to xsl:value-of
@@ -131,7 +157,7 @@ createXsltFromDom( templateNode, S = 'xsl:stylesheet' )
131
157
  {
132
158
  if( templateNode.tagName === S || templateNode.documentElement?.tagName === S )
133
159
  return tagUid(templateNode)
134
- const sanitizeXsl = xml2dom(`<xsl:stylesheet version="1.0" xmlns:xsl="${ XSL_NS_URL }" xmlns:xhtml="${ HTML_NS_URL }" xmlns:exsl="${EXSL_NS_URL}" exclude-result-prefixes="exsl" >
160
+ const sanitizeXsl = xml2dom(`<xsl:stylesheet version="1.0" xmlns:xsl="${ XSL_NS_URL }" xmlns:xhtml="${ HTML_NS_URL }" xmlns:exsl="${EXSL_NS_URL}" exclude-result-prefixes="exsl" >
135
161
  <xsl:output method="xml" />
136
162
  <xsl:template match="/"><dce-root xmlns="${ HTML_NS_URL }"><xsl:apply-templates select="*"/></dce-root></xsl:template>
137
163
  <xsl:template match="*[name()='template']"><xsl:apply-templates mode="sanitize" select="*|text()"/></xsl:template>
@@ -212,25 +238,38 @@ createXsltFromDom( templateNode, S = 'xsl:stylesheet' )
212
238
  if( !fr )
213
239
  return console.error("transformation error",{ xml:tc.outerHTML, xsl: xmlString( sanitizeXsl ) });
214
240
  const params = [];
215
- [...fr.querySelectorAll('dce-root>attribute')].forEach(p=>
216
- { p = cloneAs(p,'xsl:param');
241
+ [...fr.querySelectorAll('dce-root>attribute')].forEach( a=>
242
+ {
243
+ const p = cloneAs(a,'xsl:param')
244
+ , name = attr(a,'name');
217
245
  payload.append(p);
218
246
  let select = attr(p,'select')?.split('??')
219
247
  if( !select)
220
- { select = ['//'+attr(p, 'name'), `'${p.textContent}'`];
248
+ { select = ['//'+name, `'${p.textContent}'`];
221
249
  emptyNode(p);
250
+ p.setAttribute('name',name);
222
251
  }
252
+ let val;
223
253
  if( select?.length>1 ){
224
254
  p.removeAttribute('select');
225
255
  const c = $( xslDom, 'template[match="ignore"]>choose').cloneNode(true);
226
- c.firstElementChild.setAttribute('test',select[0]);
227
256
  emptyNode(c.firstElementChild).append( createText(c,'{'+select[0]+'}'));
228
257
  emptyNode(c.lastElementChild ).append( createText(c,'{'+select[1]+'}'));
229
- p.append(c)
230
- }
258
+ c.firstElementChild.setAttribute('test',select[0]);
259
+ p.append(c);
260
+ val = c.cloneNode(true);
261
+ }else
262
+ val=cloneAs(a,'xsl:value-of');
263
+ val.removeAttribute('name');
264
+ a.append(val);
265
+ a.removeAttribute('select');
231
266
  params.push(p)
232
267
  });
233
-
268
+ [...fr.querySelectorAll('[value]')].filter(el=>el.getAttribute('value').match( /\{(.*)\?\?(.*)\}/g )).forEach(el=>
269
+ { const v = attr(el,'value');
270
+ if(v)
271
+ el.setAttribute('value', evalCurly(v));
272
+ });
234
273
  for( const c of fr.childNodes )
235
274
  payload.append(xslDom.importNode(c,true))
236
275
 
@@ -287,18 +326,57 @@ deepEqual(a, b, O=false)
287
326
  return O
288
327
  return true;
289
328
  }
290
-
329
+ export const
330
+ assureSlices = ( root, names) =>
331
+ names.split('|').map(n=>n.trim()).map( xp =>
332
+ { if(xp.includes('/'))
333
+ { const ret = [], r = root.ownerDocument.evaluate( xp, root );
334
+ for( let n; n = r.iterateNext(); )
335
+ ret.push( n )
336
+ return ret
337
+ }
338
+ return [...root.childNodes].find(n=>n.localName === xp) || create(xp);
339
+ }).flat();
340
+
341
+ /**
342
+ *
343
+ * @param x slice node
344
+ * @param sliceNames slice name, xPath in /datadom/slice/
345
+ * @param ev Event obj
346
+ * @param dce
347
+ */
291
348
  export function
292
- injectSlice( x, s, data )
349
+ event2slice( x, sliceNames, ev, dce )
293
350
  {
294
- const isString = typeof data === 'string' ;
295
- const createXmlNode = ( tag, t = '' ) => ( e => ((e.append( createText(x, t||''))),e) )(x.ownerDocument.createElement( tag ))
296
- const el = isString
297
- ? createXmlNode(s, data)
298
- : document.adoptNode( xml2dom( Json2Xml( data, s ) ).documentElement);
299
- [...x.children].filter( e=>e.localName === s ).map( el=>el.remove() );
300
- el.data = data
301
- x.append(el);
351
+ // evaluate slices[]
352
+ // inject @attributes
353
+ // inject event
354
+ // evaluate slice-value
355
+ // slice[i] = slice-value
356
+ assureSlices(x,sliceNames).map( s =>
357
+ {
358
+ const d = x.ownerDocument
359
+ , el = ev.sliceEventSource
360
+ , sel = ev.sliceElement
361
+ , cleanSliceValue = ()=>[...s.childNodes].filter(n=>n.nodeType===3 || n.localName==='value').map(n=>n.remove());
362
+ el.getAttributeNames().map( a => s.setAttribute( a, attr(el,a) ) );
363
+ [...s.childNodes].filter(n=>n.localName==='event').map(n=>n.remove());
364
+ ev.type==='init' && cleanSliceValue();
365
+ s.append( obj2node( ev, 'event', d ) );
366
+ if( sel.hasAttribute('slice-value') )
367
+ { s.setAttribute('value', el.value );
368
+ const v = xPath( attr( sel, 'slice-value'),s );
369
+ cleanSliceValue();
370
+ s.append( createText( d, v ) );
371
+ }else
372
+ { const v = el.value || attr( sel, 'value' ) ;
373
+ cleanSliceValue();
374
+ if( isString(v) )
375
+ s.append( createText( d, v) );
376
+ else
377
+ s.append( obj2node(v,'value',s.ownerDocument) )
378
+ }
379
+ })
302
380
  }
303
381
 
304
382
  function forEach$( el, css, cb){
@@ -339,7 +417,10 @@ export function mergeAttr( from, to )
339
417
  return
340
418
  }
341
419
  for( let a of from.attributes)
342
- a.namespaceURI? to.setAttributeNS( a.namespaceURI, a.name, a.value ) : to.setAttribute( a.name, a.value )
420
+ { a.namespaceURI? to.setAttributeNS( a.namespaceURI, a.name, a.value ) : to.setAttribute( a.name, a.value )
421
+ if( a.name === 'value')
422
+ to.value = a.value
423
+ }
343
424
  }
344
425
  export function assureUnique(n, id=0)
345
426
  {
@@ -381,7 +462,7 @@ export function merge( parent, fromArr )
381
462
  { if( o.nodeValue !== e.nodeValue )
382
463
  o.nodeValue = e.nodeValue;
383
464
  }else
384
- { mergeAttr(o,e)
465
+ { mergeAttr(e,o)
385
466
  if( o.childNodes.length || e.childNodes.length )
386
467
  merge(o, e.childNodes)
387
468
  }
@@ -394,6 +475,34 @@ export function assureUID(n,attr)
394
475
  n.setAttribute(attr, crypto.randomUUID());
395
476
  return n.getAttribute(attr)
396
477
  }
478
+ export const evalCurly = s =>
479
+ { const exp = [...s?.matchAll( /([^{}]*)(\{)([^}]+)}([^{}]*)/g ) ].map(l=>`${l[1]}{${ xPathDefaults(l[3] )}}${l[4]}`);
480
+ return exp.join('');
481
+ }
482
+ export const xPathDefaults = x=>
483
+ { if(!x.trim())
484
+ return x;
485
+ const xx = x.split('??')
486
+ , a = xx.shift()
487
+ , b = xPathDefaults(xx.join('??'));
488
+
489
+ return xx.length ? `concat( ${a} , substring( ${b} , (1+string-length( ${b} )) * string-length( ${a} ) ) )`: x
490
+ // return xx.length ? `${a}|(${xPathDefaults(xx.join('??'))})[not(${a})]`: a
491
+ }
492
+ export const xPath = (x,root)=>
493
+ { x = xPathDefaults(x);
494
+
495
+ const it = root.ownerDocument.evaluate(x, root);
496
+ switch( it.resultType )
497
+ { case XPathResult.NUMBER_TYPE: return it.numberValue;
498
+ case XPathResult.STRING_TYPE: return it.stringValue;
499
+ }
500
+
501
+ let ret = '';
502
+ for( let n ;n=it.iterateNext(); )
503
+ ret += n.textContent;
504
+ return ret
505
+ }
397
506
  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(',');
398
507
  export const toXsl = (el, defParent) => {
399
508
  const x = create('xsl:'+el.localName);
@@ -436,19 +545,23 @@ CustomElement extends HTMLElement
436
545
 
437
546
  Object.defineProperty( this, "xsltString", { get: ()=>templateDocs.map( td => xmlString(td) ).join('\n') });
438
547
 
439
- const dce = this;
440
- const sliceNames = [...this.templateNode.querySelectorAll('[slice]')].map(e=>attr(e,'slice'));
548
+ const dce = this
549
+ , sliceNodes = [...this.templateNode.querySelectorAll('[slice]')]
550
+ , sliceNames = sliceNodes.map(e=>attr(e,'slice')).filter(n=>!n.includes('/')).filter((v, i, a)=>a.indexOf(v) === i)
551
+ , declaredAttributes = templateDocs.reduce( (ret,t) => { if( t.params ) ret.push( ...t.params ); return ret; }, [] );
552
+
441
553
  class DceElement extends HTMLElement
442
554
  {
443
- static get observedAttributes()
444
- { return templateDocs.reduce( (ret,t) =>
445
- { if( t.params ) ret.push( ...t.params.map(e=>attr(e,'name')) );
446
- return ret;
447
- }, [] );
448
- }
555
+ static get observedAttributes(){ return declaredAttributes.map( a=>attr(a,'name')); }
556
+ #inTransform = 0;
449
557
  connectedCallback()
450
- { if( this.firstElementChild?.tagName === 'TEMPLATE' )
451
- { const t = this.firstElementChild;
558
+ { let payload = this.childNodes;
559
+ if( this.firstElementChild?.tagName === 'TEMPLATE' )
560
+ {
561
+ const t = this.firstElementChild;
562
+ t.remove();
563
+ payload = t.content.childNodes;
564
+
452
565
  for( const n of [...t.content.childNodes] )
453
566
  if( n.localName === 'style' ){
454
567
  const id = assureUID(this,'data-dce-style')
@@ -459,9 +572,6 @@ CustomElement extends HTMLElement
459
572
  t.insertAdjacentElement('beforebegin',n);
460
573
  else if(n.nodeType===3)
461
574
  t.insertAdjacentText('beforebegin',n.data);
462
-
463
- t.remove();
464
-
465
575
  }
466
576
  const x = xml2dom( '<datadom/>' ).documentElement;
467
577
  const createXmlNode = ( tag, t = '' ) => ( e =>
@@ -469,11 +579,12 @@ CustomElement extends HTMLElement
469
579
  e.append( createText( x, t ))
470
580
  return e;
471
581
  })(x.ownerDocument.createElement( tag ))
472
- injectData( x, 'payload' , this.childNodes, assureSlot );
582
+ injectData( x, 'payload' , payload , assureSlot );
473
583
  this.innerHTML='';
474
584
  injectData( x, 'attributes' , this.attributes, e => createXmlNode( e.nodeName, e.value ) );
475
585
  injectData( x, 'dataset', Object.keys( this.dataset ), k => createXmlNode( k, this.dataset[ k ] ) );
476
- const sliceRoot = injectData( x, 'slice', sliceNames, k => createXmlNode( k, '' ) );
586
+ const sliceRoot = injectData( x, 'slice', sliceNames, k => createXmlNode( k, '' ) )
587
+ , sliceXPath = x => xPath(x, sliceRoot);
477
588
  this.xml = x;
478
589
 
479
590
  const sliceEvents=[];
@@ -481,10 +592,10 @@ CustomElement extends HTMLElement
481
592
  { const processed = {}
482
593
 
483
594
  for(let ev; ev = sliceEvents.pop(); )
484
- { const s = attr( ev.target, 'slice');
595
+ { const s = attr( ev.sliceElement, 'slice');
485
596
  if( processed[s] )
486
597
  continue;
487
- injectSlice( sliceRoot, s, 'object' === typeof ev.detail ? {...ev.detail}: ev.detail );
598
+ event2slice( sliceRoot, s, ev, this );
488
599
  processed[s] = ev;
489
600
  }
490
601
  Object.keys(processed).length !== 0 && transform();
@@ -493,10 +604,7 @@ CustomElement extends HTMLElement
493
604
 
494
605
  this.onSlice = ev=>
495
606
  { ev.stopPropagation?.();
496
- const s = attr( ev.target, 'slice')
497
- if( deepEqual( ev.detail, [...sliceRoot.children].find( e=>e.localName === s )?.data ) )
498
- return
499
-
607
+ ev.sliceEventSource = ev.currentTarget || ev.target;
500
608
  sliceEvents.push(ev);
501
609
  if( !timeoutID )
502
610
  timeoutID = setTimeout(()=>
@@ -505,7 +613,9 @@ CustomElement extends HTMLElement
505
613
  },10);
506
614
  };
507
615
  const transform = this.transform = ()=>
508
- {
616
+ { if(this.#inTransform){ debugger }
617
+ this.#inTransform = 1;
618
+
509
619
  const ff = xp.map( (p,i) =>
510
620
  { const f = p.transformToFragment(x.ownerDocument, document)
511
621
  if( !f )
@@ -518,35 +628,55 @@ CustomElement extends HTMLElement
518
628
  assureUnique(f);
519
629
  merge( this, f.childNodes )
520
630
  })
521
- const changeCb = el=>this.onSlice({ detail: el[attr(el,'slice-prop') || 'value'], target: el })
522
- , hasInitValue = el => el.hasAttribute('slice-prop') || el.hasAttribute('value') || el.value;
631
+
632
+ DceElement.observedAttributes.map( a =>
633
+ { let v = attr(this.firstElementChild,a);
634
+ if( v !== attr(this,a) )
635
+ { this.setAttribute( a, v );
636
+ this.#applyAttribute( a, v );
637
+ }
638
+ })
523
639
 
524
640
  forEach$( this,'[slice]', el =>
525
641
  { if( !el.dceInitialized )
526
642
  { el.dceInitialized = 1;
527
- el.addEventListener( attr(el,'slice-update')|| 'change', ()=>changeCb(el) )
528
- if( hasInitValue(el) )
529
- changeCb(el)
643
+ const evs = attr(el,'slice-event');
644
+ (evs || 'change')
645
+ .split(' ')
646
+ .forEach( t=> (el.localName==='slice'? el.parentElement : el)
647
+ .addEventListener( t, ev=>
648
+ { ev.sliceElement = el;
649
+ this.onSlice(ev)
650
+ } ));
651
+ if( !evs || evs.includes('init') )
652
+ { if( el.hasAttribute('slice-value') || el.hasAttribute('value') || el.value )
653
+ this.onSlice({type:'init', target: el, sliceElement:el })
654
+ else
655
+ el.value = sliceXPath( attr(el,'slice') )
656
+ }
530
657
  }
531
- })
658
+ });
659
+ this.#inTransform = 0;
532
660
  };
533
661
  transform();
534
662
  applySlices();
535
663
  }
536
- attributeChangedCallback(name, oldValue, newValue)
537
- { if( !this.xml )
538
- return;
539
- let a = this.xml.querySelector(`attributes>${name}`);
664
+ #applyAttribute(name, newValue)
665
+ { let a = this.xml.querySelector(`attributes>${name}`);
540
666
  if( a )
541
- emptyNode(a).append( createText(a,newValue));
667
+ emptyNode(a).append( createText(a,newValue) );
542
668
  else
543
669
  { a = create( name, newValue, this.xml );
544
- a.append( createText(a,newValue) );
545
670
  this.xml.querySelector('attributes').append( a );
546
671
  }
547
-
672
+ }
673
+ attributeChangedCallback(name, oldValue, newValue)
674
+ { if( !this.xml || this.#inTransform )
675
+ return;
676
+ this.#applyAttribute(name, newValue);
548
677
  this.transform(); // needs throttling
549
678
  }
679
+
550
680
  get dce(){ return dce }
551
681
  }
552
682
  if(tag)
package/demo/a.html CHANGED
@@ -4,30 +4,58 @@
4
4
  <head>
5
5
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
6
6
  <title>custom-element Declarative Custom Element implementation demo</title>
7
- <link rel="icon" href="./wc-square.svg" />
8
- <script type="module" src="../location-element.js"></script>
7
+ <link rel="icon" href="./wc-square.svg"/>
8
+ <script type="module" src="../http-request.js"></script>
9
9
  <script type="module" src="../custom-element.js"></script>
10
10
 
11
11
  <style>
12
12
  @import "./demo.css";
13
13
 
14
- button{ background: forestgreen; }
15
- table{ min-width: 16rem; }
16
- td{ border-bottom: 1px solid silver; }
17
- tfoot td{ border-bottom: none; }
18
- td,th{text-align: right; }
19
- caption{ padding: 1rem; font-weight: bolder; font-family: sans-serif; }
14
+ button {
15
+ background: forestgreen;
16
+ }
17
+
18
+ table {
19
+ min-width: 16rem;
20
+ }
21
+
22
+ td {
23
+ border-bottom: 1px solid silver;
24
+ }
25
+
26
+ tfoot td {
27
+ border-bottom: none;
28
+ }
29
+
30
+ td, th {
31
+ text-align: right;
32
+ }
33
+
34
+ caption {
35
+ padding: 1rem;
36
+ font-weight: bolder;
37
+ font-family: sans-serif;
38
+ }
20
39
  </style>
21
40
  </head>
22
41
  <body>
23
42
 
24
- <custom-element tag="dce-link" hidden>
25
- <param name="p1" >default_P1 </param>
26
- <param name="p2" select="'always_p2'" ></param>
27
- <param name="p3" select="//p3 ?? 'def_P3' " ></param>
28
- p1:{$p1} <br/> p2: {$p2} <br/> p3: {$p3}
29
- </custom-element>
30
- <dce-link id="dce1" ></dce-link>
43
+ <custom-element>
44
+ <template>
45
+ <button slice="clickcount"
46
+ slice-event="click"
47
+ slice-value="//clickcount + 1" >
48
+ +
49
+ </button>
50
+ <button slice="clickcount"
51
+ slice-event="click"
52
+ slice-value="//clickcount - 1" >
53
+ -
54
+ </button>
55
+ <input slice="clickcount" type="number" value="{//clickcount ?? 0}" />
56
+ {//clickcount}
57
+ </template>
58
+ </custom-element>
31
59
 
32
60
  </body>
33
61
  </html>
@@ -0,0 +1,185 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
5
+ <title>Data slices - Declarative Custom Element implementation demo</title>
6
+ <link rel="icon" href="./wc-square.svg"/>
7
+
8
+ <script type="module" src="../http-request.js"></script>
9
+ <script type="module" src="../input-text.js"></script>
10
+ <script type="module" src="../custom-element.js"></script>
11
+ <style>
12
+ @import "./demo.css";
13
+ </style>
14
+ </head>
15
+ <body>
16
+
17
+ <nav>
18
+ <a href="../index.html"><h3><code>custom-element</code> demo</h3></a>
19
+ <h3>Data slices propagation by events.</h3>
20
+ </nav>
21
+
22
+ <html-demo-element legend="A. slice initialization, change on event"
23
+ description="initial value should be 0; + and - should change the number in input field">
24
+ <template>
25
+ <custom-element>
26
+ <template>
27
+ <button slice="clickcount"
28
+ slice-event="click"
29
+ slice-value="//clickcount + 1" >
30
+ +
31
+ </button>
32
+ <button slice="clickcount"
33
+ slice-event="click"
34
+ slice-value="//clickcount - 1" >
35
+ -
36
+ </button>
37
+ <input slice="clickcount" type="number" value="{//clickcount ?? 0}" />
38
+ {//clickcount}
39
+ </template>
40
+ </custom-element>
41
+ </template>
42
+ </html-demo-element>
43
+
44
+ <html-demo-element legend="B. slice event data."
45
+ description="move the mouse over TEXTAREA and click to see slice and slice event changed">
46
+ <template>
47
+ <custom-element>
48
+ <template>
49
+ <textarea slice="s" slice-value="concat('x:', //@pageX)"
50
+ slice-event="mousemove click"
51
+ style="width:16rem;height:16rem;box-shadow: inset {//@offsetX}px {//@offsetY}px gold;" ></textarea><br/>
52
+ //slice/s : {//slice/s} <br/>
53
+ //slice/s/event/@offsetY: {//slice/s/event/@offsetY} <br/>
54
+ event type:{//slice/s/event/@type}
55
+ </template>
56
+ </custom-element>
57
+ </template>
58
+ </html-demo-element>
59
+
60
+ <html-demo-element legend="1. slice change on event. 1:1 slice⮂value"
61
+ description="initial value blank; type and unfocus to see slice changed">
62
+ <template>
63
+
64
+ <custom-element>
65
+ <input slice="typed" /> //slice/typed : {//slice/typed}
66
+ </custom-element>
67
+ </template>
68
+ </html-demo-element>
69
+
70
+
71
+ <html-demo-element legend="2. initial slice value, slice change on event. slice⮂value, w/ initial"
72
+ description="initial value from input; type and unfocus to see slice changed">
73
+ <template>
74
+ <custom-element>
75
+ <input slice="s" value="{//s ?? 'B'}" /> //slice/s : {//slice/s}
76
+ </custom-element>
77
+ </template>
78
+ </html-demo-element>
79
+
80
+ <html-demo-element legend="3. initial slice value, slice change on event. slice⮂value, w/ initial"
81
+ description="initial value from input; type to see slice changed">
82
+ <template>
83
+ <custom-element>
84
+ <input slice="s" value="{//s ?? 'B'}" slice-event="input"/> //slice/s : {//slice/s}
85
+ </custom-element>
86
+ </template>
87
+ </html-demo-element>
88
+
89
+ <html-demo-element legend="4. initial slice value from attribute, slice change on event."
90
+ description="initial value from input; type to see slice changed">
91
+ <template>
92
+ <custom-element tag="dce-1">
93
+ <template>
94
+ <attribute name="a" >😁</attribute>
95
+ <input slice="s" value="{//s ?? $a}" slice-event="keyup" />
96
+ attribute 'a' : {$a}
97
+ //slice/s : {//slice/s}
98
+ </template>
99
+ </custom-element>
100
+ <dce-1></dce-1>
101
+ <dce-1 a="🤗"></dce-1>
102
+ </template>
103
+ </html-demo-element>
104
+
105
+ <html-demo-element legend="5. initial slice value from attribute, slice change on event."
106
+ description="initial value from input as 'xB'; type and unfocus to see slice changed">
107
+ <template>
108
+ <custom-element>
109
+ <template>
110
+ <input slice="s" value="{substring(//s, 2) ?? 'B'}" slice-value="concat('x', @value )" />
111
+ //slice/s : {//slice/s}
112
+ </template>
113
+ </custom-element>
114
+ </template>
115
+ </html-demo-element>
116
+
117
+
118
+ <html-demo-element legend="6. initial slice value from input, button ignored till change on click."
119
+ description="initial value from input as 'anonymous'; on button click change to 'broccoli'">
120
+ <template>
121
+ <custom-element>
122
+ <template>
123
+ <input slice="nickname" value="anonymous" />
124
+ <button slice="nickname" slice-value="'broccoli'" slice-event="click">🥦</button>
125
+ {//nickname}
126
+ </template>
127
+ </custom-element>
128
+ </template>
129
+ </html-demo-element>
130
+
131
+ <html-demo-element legend="7. initial slice value from SLICE element, button ignored till change on click."
132
+ description="synthetic SLICE element serves as initial value holder">
133
+ <template>
134
+ <custom-element>
135
+ <template>
136
+ <button slice="clickcount" slice-event="click tap" slice-value="//clickcount + 1">
137
+ <slice slice="clickcount" value="0" ></slice>
138
+ click/tap
139
+ </button>
140
+ //clickcount : {//clickcount}
141
+ </template>
142
+ </custom-element>
143
+ </template>
144
+ </html-demo-element>
145
+
146
+ <html-demo-element legend="8. multiple slices by SLICE element, button ignored till change on click."
147
+ description="synthetic SLICE elements serve as initial value holder">
148
+ <template>
149
+ <custom-element>
150
+ <template>
151
+ <button>
152
+ <slice slice="clicked" value="{0}" ></slice>
153
+ <slice slice="focused" value="{0}" ></slice>
154
+ <slice slice-event="click tap" slice="clicked" slice-value="//clicked+1" ></slice>
155
+ <slice slice-event="focus" slice="focused" slice-value="1" ></slice>
156
+ <slice slice-event="blur" slice="focused" slice-value="0" ></slice>
157
+ click/tap, focus/blur
158
+ </button> <br/>
159
+ //clicked : {//clicked} <br/>
160
+ //focused : {//focused}
161
+ </template>
162
+ </custom-element>
163
+ </template>
164
+ </html-demo-element>
165
+
166
+
167
+ <html-demo-element legend="9. slice in attribute"
168
+ description="initial attribute value should be smile as emoji and :) on blur from input it should be updated from value">
169
+ <template>
170
+ <custom-element tag="emotional-element">
171
+ <template>
172
+ <attribute name="emotion" select="//emotion ?? '😃'"></attribute>
173
+ <input slice="/datadom/attributes/emotion"/>
174
+ Type and unfocus to update emotion attribute: {emotion}
175
+ </template>
176
+ </custom-element>
177
+ <emotional-element emotion=":)"></emotional-element>
178
+ <emotional-element></emotional-element>
179
+ </template>
180
+ </html-demo-element>
181
+
182
+ <script type="module" src="https://unpkg.com/html-demo-element@1/html-demo-element.js"></script>
183
+
184
+ </body>
185
+ </html>
package/demo/demo.css CHANGED
@@ -6,6 +6,7 @@ body,nav{ display: flex; flex-wrap: wrap; align-content: stretch; gap: 1rem; }
6
6
  body>*{flex: auto;}
7
7
  nav{ flex-direction: column;}
8
8
  custom-element+*,
9
+ custom-element+*+*,
9
10
  custom-element:not([tag]),
10
11
  dce-link,dce-1-slot,dce-2-slot,dce-3-slot,dce-4-slot,dce-2-slots,greet-element,pokemon-tile,
11
12
  dce-1,dce-2,dce-3,dce-4,dce-internal,dce-hash
@@ -96,7 +96,7 @@
96
96
  <input type="text"
97
97
  value="Type time update"
98
98
  slice="txt"
99
- slice-update="keyup"/>
99
+ slice-event="keyup"/>
100
100
 
101
101
  <span> Character count:
102
102
  <b> {string-length(//slice/txt)} </b>
@@ -78,6 +78,7 @@
78
78
  <html-demo-element legend="4. external XSLT file"
79
79
  description="This external templates generated the tree for DCE data set"
80
80
  >
81
+ <a href="tree.xsl">tree.xsl</a>
81
82
  <template>
82
83
  <custom-element tag="dce-external-4" src="tree.xsl" >
83
84
  <template><i>loading from XSLT ...</i></template>
@@ -37,6 +37,7 @@
37
37
  <template>
38
38
  <custom-element>
39
39
  <template><!-- wrapping into template to prevent images loading within DCE declaration -->
40
+ <p>Pokemon buttons from API</p>
40
41
  <http-request
41
42
  url="https://pokeapi.co/api/v2/pokemon?limit=6&offset=0"
42
43
  slice="page"
@@ -45,7 +46,7 @@
45
46
  ></http-request>
46
47
  <variable name="slides-url"
47
48
  >https://unpkg.com/pokeapi-sprites@2.0.2/sprites/pokemon/other/dream-world</variable>
48
- <for-each select="//slice/page/data/results/*">
49
+ <for-each select="//results">
49
50
  <variable name="pokeid"
50
51
  select="substring-before( substring-after( @url, 'https://pokeapi.co/api/v2/pokemon/'),'/')"
51
52
  ></variable>
@@ -67,6 +68,7 @@
67
68
  <custom-element url="https://pokeapi.co/api/v2/pokemon?offset=6&limit=6">
68
69
  <template> <!-- IMPORTANT! to wrap DCE payload into template to avoid
69
70
  http-request initializing out of instance -->
71
+ <attribute name="url"></attribute>
70
72
  <http-request
71
73
  url="{url}"
72
74
  slice="request_slice"
@@ -79,15 +81,15 @@
79
81
 
80
82
  <h3>Samples</h3>
81
83
  <table>
82
- <tr><th> //slice/request_slice/request/@mode </th>
83
- <td>{ //slice/request_slice/request/@mode }</td></tr>
84
- <tr><th> //slice/request_slice/response/headers/@content-type </th>
85
- <td>{ //slice/request_slice/response/headers/@content-type }</td></tr>
86
- <tr><th> //slice/request_slice/response/@status </th>
87
- <td>{ //slice/request_slice/response/@status }</td></tr>
84
+ <tr><th> //slice/request_slice/value/request/@mode </th>
85
+ <td>{ //slice/request_slice/value/request/@mode }</td></tr>
86
+ <tr><th> //slice/request_slice/value/response/headers/@content-type </th>
87
+ <td>{ //slice/request_slice/value/response/headers/@content-type }</td></tr>
88
+ <tr><th> //slice/request_slice/value/response/@status </th>
89
+ <td>{ //slice/request_slice/value/response/@status }</td></tr>
88
90
  </table>
89
- <apply-templates mode="display" select="//slice/request_slice/*"></apply-templates>
90
- <template mode="display" match="*">
91
+ <apply-templates mode="display" select="//slice/request_slice/value/*"></apply-templates>
92
+ <xsl:template mode="display" match="*">
91
93
  <fieldset>
92
94
  <legend> {local-name(.)} </legend>
93
95
  <ul>
@@ -101,7 +103,7 @@
101
103
  </ul>
102
104
  <apply-templates mode="display" select="*"></apply-templates>
103
105
  </fieldset>
104
- </template>
106
+ </xsl:template>
105
107
  </template>
106
108
  </custom-element>
107
109
  </template>
@@ -45,7 +45,7 @@
45
45
  <local-storage key="basket" slice="basket" live type="json"></local-storage>
46
46
  <xhtml:table xmlns:xhtml="http://www.w3.org/1999/xhtml" >
47
47
  <xhtml:tbody>
48
- <for-each select="//basket/@*">
48
+ <for-each select="//basket/value/@*">
49
49
  <xhtml:tr>
50
50
  <xhtml:th> {name()} </xhtml:th>
51
51
  <xhtml:td> {.} </xhtml:td>
@@ -55,7 +55,7 @@
55
55
  <xhtml:tfoot>
56
56
  <xhtml:tr>
57
57
  <xhtml:td><slot>🤔</slot></xhtml:td>
58
- <xhtml:th> {sum(//slice/basket/@*)} </xhtml:th>
58
+ <xhtml:th> {sum(//slice/basket/value/@*)} </xhtml:th>
59
59
  </xhtml:tr>
60
60
  </xhtml:tfoot>
61
61
  </xhtml:table>
@@ -51,19 +51,24 @@
51
51
  <location-element slice="window-url" live></location-element>
52
52
 
53
53
  <xhtml:table>
54
- <tr><th><h3> URL properties </h3></th></tr>
55
- <apply-templates mode="attrs" select="//slice/window-url/@*"></apply-templates>
54
+ <xhtml:tbody>
55
+ <xhtml:tr>
56
+ <xhtml:th><h3> URL properties </h3></xhtml:th>
57
+ <xhtml:td>{count(//value/@*)}</xhtml:td>
58
+ </xhtml:tr>
59
+ <apply-templates mode="attrs" select="//value/@*"></apply-templates>
60
+ </xhtml:tbody>
56
61
  </xhtml:table>
57
62
  <xhtml:table>
58
63
  <tr><th><h3> URL parameters </h3></th></tr>
59
- <apply-templates mode="attrs" select="//slice/window-url/params/*"></apply-templates>
64
+ <apply-templates mode="attrs" select="//params/*/*"></apply-templates>
60
65
  </xhtml:table>
61
- <template mode="attrs" match="*|@*">
66
+ <xsl:template mode="attrs" match="*|@*">
62
67
  <xhtml:tr>
63
68
  <xhtml:th>{name()}</xhtml:th>
64
69
  <xhtml:td>{.}</xhtml:td>
65
70
  </xhtml:tr>
66
- </template>
71
+ </xsl:template>
67
72
  </template>
68
73
  </custom-element>
69
74
  <dce-2>?</dce-2>
@@ -84,7 +89,7 @@
84
89
  <xhtml:table>
85
90
  <xhtml:tbody>
86
91
  <xhtml:tr><xhtml:th><h3>URL properties</h3></xhtml:th></xhtml:tr>
87
- <for-each select="//slice/window-url/@*">
92
+ <for-each select="//slice/window-url/value/@*">
88
93
  <xhtml:tr>
89
94
  <xhtml:th>{name()}</xhtml:th>
90
95
  <xhtml:td>{.}</xhtml:td>
@@ -93,7 +98,7 @@
93
98
  </xhtml:tbody>
94
99
  <xhtml:tbody>
95
100
  <xhtml:tr><xhtml:th><h3>URL parameters</h3></xhtml:th></xhtml:tr>
96
- <for-each select="//slice/window-url/params/*">
101
+ <for-each select="//slice/window-url/value/params/*">
97
102
  <xhtml:tr>
98
103
  <xhtml:th>{name()}</xhtml:th>
99
104
  <xhtml:td>{.}</xhtml:td>
@@ -121,7 +126,7 @@
121
126
  <xhtml:table>
122
127
  <xhtml:tbody>
123
128
  <xhtml:tr><xhtml:th><h3>URL properties</h3></xhtml:th></xhtml:tr>
124
- <for-each select="//slice/src-url/@*">
129
+ <for-each select="//slice/src-url/value/@*">
125
130
  <xhtml:tr>
126
131
  <xhtml:th>{name()}</xhtml:th>
127
132
  <xhtml:td>{.}</xhtml:td>
@@ -130,7 +135,7 @@
130
135
  </xhtml:tbody>
131
136
  <xhtml:tbody>
132
137
  <xhtml:tr><xhtml:th><h3>URL parameters</h3></xhtml:th></xhtml:tr>
133
- <for-each select="//slice/src-url/params/*">
138
+ <for-each select="//slice/src-url/value/params/*">
134
139
  <xhtml:tr>
135
140
  <xhtml:th>{name()}</xhtml:th>
136
141
  <xhtml:td>{.}</xhtml:td>
@@ -44,6 +44,24 @@ params needed to declare DCE attributes and track the attributes changes. It als
44
44
  </template>
45
45
  </html-demo-element>
46
46
 
47
+ <html-demo-element legend="slice propagates attribute" description="
48
+ when slice value points to attribute, it would be populated on slice change
49
+ ">
50
+ Type in the input field to see the variable $title change. <br/>
51
+ Hover the mouse to see the title attribute text popup.<br/>
52
+ Inspect DCE node in dev tools to see `title` attribute updated while typing.
53
+
54
+ <template>
55
+ <custom-element>
56
+ <template>
57
+ <attribute name="title" select="//title ?? '😃'" ></attribute>
58
+ <input slice="/datadom/attributes/title" slice-event="keyup"/>
59
+ title attribute: {$title}
60
+ </template>
61
+ </custom-element>
62
+ </template>
63
+ </html-demo-element>
64
+
47
65
 
48
66
 
49
67
  <script type="module" src="https://unpkg.com/html-demo-element@1/html-demo-element.js"></script>
package/demo/s.xml CHANGED
@@ -1 +1,6 @@
1
- <datadom><payload><i xmlns="http://www.w3.org/1999/xhtml" slot="">🍋</i></payload><attributes/><dataset/><slice/></datadom>
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <datadom><payload><span xmlns="http://www.w3.org/1999/xhtml" slot=""> </span><button xmlns="http://www.w3.org/1999/xhtml" slice="clickcount" slice-event="click" slice-value="//clickcount + 1" slot="">
3
+ +
4
+ </button><span xmlns="http://www.w3.org/1999/xhtml" slot=""> </span><button xmlns="http://www.w3.org/1999/xhtml" slice="clickcount" slice-event="click" slice-value="//clickcount - 1" slot="">
5
+ -
6
+ </button><span xmlns="http://www.w3.org/1999/xhtml" slot=""> </span><input xmlns="http://www.w3.org/1999/xhtml" slice="clickcount" type="number" value="{//clickcount ?? 0}" slot="" /><span xmlns="http://www.w3.org/1999/xhtml" slot=""> {//clickcount} </span></payload><attributes><tag>dce-72e0d208-b3b4-4b0a-8798-a094beaf5fe1</tag></attributes><dataset/><slice><clickcount xmlns="" slice="clickcount" type="number" value="" data-dce-id="2" slice-event="click" slice-value="//clickcount + 1"><event isTrusted="true" pointerId="1" width="1" height="1" pressure="0" tiltX="0" tiltY="0" azimuthAngle="0" altitudeAngle="1.5707963267948966" tangentialPressure="0" twist="0" pointerType="mouse" isPrimary="false" screenX="19" screenY="99" clientX="19" clientY="12" ctrlKey="false" shiftKey="false" altKey="false" metaKey="false" button="0" buttons="0" pageX="19" pageY="12" x="19" y="12" offsetX="10" offsetY="0" movementX="0" movementY="0" layerX="19" layerY="12" detail="1" which="1" type="click" eventPhase="0" bubbles="true" cancelable="true" defaultPrevented="false" composed="true" timeStamp="5460750.8000000715" returnValue="true" cancelBubble="false" NONE="0" CAPTURING_PHASE="1" AT_TARGET="2" BUBBLING_PHASE="3"><relatedTarget/><fromElement/><toElement/><sourceCapabilities firesTouchEvents="false"/><currentTarget/></event>46</clickcount></slice></datadom>
package/demo/s.xslt CHANGED
@@ -1,3 +1,4 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
1
2
  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:dce="urn:schemas-epa-wg:dce" xmlns:exsl="http://exslt.org/common" version="1.0" exclude-result-prefixes="exsl">
2
3
  <xsl:template match="ignore">
3
4
  <xsl:choose>
@@ -5,14 +6,13 @@
5
6
  <xsl:otherwise><xsl:value-of select="def"/></xsl:otherwise>
6
7
  </xsl:choose>
7
8
  <xsl:value-of select="."/></xsl:template>
8
- <xsl:template mode="payload" match="attributes"><xsl:param xmlns:xsl="http://www.w3.org/1999/XSL/Transform" name="p1"><xsl:choose>
9
- <xsl:when test="//p1 "><xsl:value-of select="//p1 "/></xsl:when>
10
- <xsl:otherwise><xsl:value-of select=" 'def_p1' "/></xsl:otherwise>
11
- </xsl:choose></xsl:param><xsl:param xmlns:xsl="http://www.w3.org/1999/XSL/Transform" name="p2" select="'always_p2'"/><xsl:param xmlns:xsl="http://www.w3.org/1999/XSL/Transform" name="p3"><xsl:choose>
12
- <xsl:when test="//p3"><xsl:value-of select="//p3"/></xsl:when>
13
- <xsl:otherwise><xsl:value-of select="'default_P3 &#10; p1:{$p1"/> p2:<xsl:value-of select="$p2"/> p3:<xsl:value-of select="$p3"/>
14
- '}</xsl:otherwise>
15
- </xsl:choose></xsl:param><dce-root xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml" data-dce-id="1"><xsl:attribute xmlns:xsl="http://www.w3.org/1999/XSL/Transform" name="p1" select="//p1 ?? 'def_p1' "/><xsl:attribute xmlns:xsl="http://www.w3.org/1999/XSL/Transform" name="p2" select="'always_p2'"/><xsl:attribute xmlns:xsl="http://www.w3.org/1999/XSL/Transform" name="p3"/></dce-root></xsl:template>
9
+ <xsl:template mode="payload" match="attributes"><dce-root xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml" data-dce-id="1"><button xmlns="" slice="clickcount" slice-event="click" slice-value="//clickcount + 1" data-dce-id="2">
10
+ +
11
+ </button><button xmlns="" slice="clickcount" slice-event="click" slice-value="//clickcount - 1" data-dce-id="3">
12
+ -
13
+ </button><input xmlns="" slice="clickcount" type="number" value="{concat( //clickcount , substring( 0 , (1+string-length( 0 )) * string-length( //clickcount ) ) )}" data-dce-id="4"/><dce-text xmlns="" data-dce-id="5">
14
+ <xsl:value-of select="//clickcount"/>
15
+ </dce-text></dce-root></xsl:template>
16
16
  <xsl:template match="/">
17
17
  <xsl:apply-templates mode="payload" select="/datadom/attributes"/>
18
18
  </xsl:template>
@@ -21,7 +21,7 @@
21
21
  <xsl:param name="defaultvalue"/>
22
22
  <xsl:choose>
23
23
  <xsl:when test="//payload/*[@slot=$slotname]">
24
- <xsl:copy-of select="//payload/*[@slot=$slotname]"/>
24
+ <xsl:copy-of select="//payload/*[@slot=$slotname]"/>
25
25
  </xsl:when>
26
26
  <xsl:otherwise>
27
27
  <xsl:copy-of select="$defaultvalue"/>
package/demo/z.html CHANGED
@@ -1,48 +1,62 @@
1
- <!DOCTYPE html>
2
- <html lang="en" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml">
3
- <head>
4
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
5
- <title>CSS scoping - Declarative Custom Element implementation demo</title>
6
- <link rel="icon" href="./wc-square.svg"/>
7
-
8
- <script type="module" src="../http-request.js"></script>
9
- <script type="module" src="../input-text.js"></script>
10
- <script type="module" src="../custom-element.js"></script>
11
- <style>
12
- @import "./demo.css";
13
-
14
- button {
15
- display: inline-flex;
16
- flex-direction: column;
17
- align-items: center;
18
- flex: auto;
19
- box-shadow: inset silver 0 0 1rem;
20
- min-width: 12rem;
21
- padding: 1rem;
22
- color: coral;
23
- text-shadow: 1px 1px silver;
24
- font-weight: bolder;
25
- }
26
-
27
- caption {
28
- padding: 1rem;
29
- font-weight: bolder;
30
- font-family: sans-serif;
31
- }
32
-
33
- code {
34
- text-align: right;
35
- min-width: 3rem;
36
- }
37
-
38
- </style>
39
- </head>
40
- <body>
41
-
42
- <custom-element src="embed-1.html">
43
- loading from embed-1.html ...
44
- </custom-element>
45
-
46
-
47
- </body>
48
- </html>
1
+ <dce-root xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml"
2
+ xmlns:dce="urn:schemas-epa-wg:dce" data-dce-id="1">
3
+ <location-element xmlns="" slice="window-url" live="" data-dce-id="2"></location-element>
4
+ <table xmlns="" data-dce-id="3">
5
+ <tbody data-dce-id="0-1">
6
+ <tr data-dce-id="4">
7
+ <th data-dce-id="5"><h3 data-dce-id="6"> URL properties </h3></th>
8
+ <td data-dce-id="7">9</td>
9
+ </tr>
10
+ <tr data-dce-id="10">
11
+ <th data-dce-id="11">href</th>
12
+ <td data-dce-id="12">http://localhost:63342/custom-element/demo/a.html?_ijt=dmv0p4go000q47lg48i5im92f7&amp;_ij_reload=RELOAD_ON_SAVE</td>
13
+ </tr>
14
+ <tr data-dce-id="10-1">
15
+ <th data-dce-id="11">origin</th>
16
+ <td data-dce-id="12">http://localhost:63342</td>
17
+ </tr>
18
+ <tr data-dce-id="10-2">
19
+ <th data-dce-id="11">protocol</th>
20
+ <td data-dce-id="12">http:</td>
21
+ </tr>
22
+ <tr data-dce-id="10-3">
23
+ <th data-dce-id="11">host</th>
24
+ <td data-dce-id="12">localhost:63342</td>
25
+ </tr>
26
+ <tr data-dce-id="10-4">
27
+ <th data-dce-id="11">hostname</th>
28
+ <td data-dce-id="12">localhost</td>
29
+ </tr>
30
+ <tr data-dce-id="10-5">
31
+ <th data-dce-id="11">port</th>
32
+ <td data-dce-id="12">63342</td>
33
+ </tr>
34
+ <tr data-dce-id="10-6">
35
+ <th data-dce-id="11">pathname</th>
36
+ <td data-dce-id="12">/custom-element/demo/a.html</td>
37
+ </tr>
38
+ <tr data-dce-id="10-7">
39
+ <th data-dce-id="11">search</th>
40
+ <td data-dce-id="12">?_ijt=dmv0p4go000q47lg48i5im92f7&amp;_ij_reload=RELOAD_ON_SAVE</td>
41
+ </tr>
42
+ <tr data-dce-id="10-8">
43
+ <th data-dce-id="11">hash</th>
44
+ <td data-dce-id="12"></td>
45
+ </tr>
46
+ </tbody>
47
+ </table>
48
+ <h3 data-dce-id="9"> URL parameters </h3>
49
+ <table xmlns="" data-dce-id="8">
50
+
51
+ <tbody data-dce-id="0-1">
52
+ <tr data-dce-id="10">
53
+ <th data-dce-id="11">_ijt</th>
54
+ <td data-dce-id="12">dmv0p4go000q47lg48i5im92f7</td>
55
+ </tr>
56
+ <tr data-dce-id="10-1">
57
+ <th data-dce-id="11">_ij_reload</th>
58
+ <td data-dce-id="12">RELOAD_ON_SAVE</td>
59
+ </tr>
60
+ </tbody>
61
+ </table>
62
+ </dce-root>
package/http-request.js CHANGED
@@ -18,7 +18,8 @@ export class HttpRequestElement extends HTMLElement
18
18
  }
19
19
  get requestProps()
20
20
  { const ret = {};
21
- [...this.attributes].filter(a=>!a.name.startsWith('header-')).map( a => ret[a.name] = a.value );
21
+ [...this.attributes].filter(a=>!a.name.startsWith('header-'))
22
+ .filter(a=>!a.name.startsWith('slice')).map( a => ret[a.name] = a.value );
22
23
  return ret
23
24
  }
24
25
 
@@ -5,7 +5,7 @@
5
5
  "name": "slice",
6
6
  "description": {
7
7
  "kind": "markdown",
8
- "value": "Defines the name of data slice in DCE where the data from `value` will be propagated on `change` or by `slice-update` event\n\nOn: any component with `value` and associated change event"
8
+ "value": "Defines the name of data slice in DCE where the data from `value` will be propagated on `change` or by `slice-event` event\n\nOn: any component with `value` and associated change event"
9
9
  },
10
10
  "references": [
11
11
  {
@@ -15,7 +15,7 @@
15
15
  ]
16
16
  },
17
17
  {
18
- "name": "slice-update",
18
+ "name": "slice-event",
19
19
  "description": {
20
20
  "kind": "markdown",
21
21
  "value": "Defines the event name on which `value` would be synchronized with DCE slice\n\nOn: any component with `value` and associated change event"
@@ -26,6 +26,19 @@
26
26
  "url": "https://unpkg.com/@epa-wg/custom-element/demo/dom-merge.html"
27
27
  }
28
28
  ]
29
+ },
30
+ {
31
+ "name": "slice-value",
32
+ "description": {
33
+ "kind": "markdown",
34
+ "value": "XPath expression to populate into the slice"
35
+ },
36
+ "references": [
37
+ {
38
+ "name": "Demo",
39
+ "url": "https://unpkg.com/@epa-wg/custom-element/demo/data-slices.html"
40
+ }
41
+ ]
29
42
  }
30
43
  ],
31
44
  "tags": [
@@ -84,6 +97,16 @@
84
97
  "url": "https://developer.mozilla.org/en-US/docs/Web/XSLT/Element/for-each"
85
98
  }
86
99
  ]
100
+ },
101
+ {
102
+ "name": "slice",
103
+ "description": "Synthetic element for defining the slice-attributed when more then one slice/event/value associated with parent element",
104
+ "references": [
105
+ {
106
+ "name": "README",
107
+ "url": "https://github.com/EPA-WG/custom-element/tree/develop?tab=readme-ov-file#interactivity-via-data-slice-triggered-by-events"
108
+ }
109
+ ]
87
110
  }
88
111
  ]
89
112
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "http://json.schemastore.org/web-types",
3
3
  "name": "@epa-wg/custom-element",
4
- "version": "0.0.18",
4
+ "version": "0.0.19",
5
5
  "js-types-syntax": "typescript",
6
6
  "description-markup": "markdown",
7
7
  "contributions": {
@@ -9,13 +9,18 @@
9
9
  "attributes": [
10
10
  {
11
11
  "name": "slice",
12
- "description": "Defines the name of data slice in DCE where the data from `value` will be propagated on `change` or by `slice-update` event\n\nOn: any component with `value` and associated change event",
12
+ "description": "Defines the name of data slice in DCE where the data from `value` will be propagated on `change` or by `slice-event` event\n\nOn: any component with `value` and associated change event",
13
13
  "doc-url": "https://unpkg.com/@epa-wg/custom-element/demo/dom-merge.html"
14
14
  },
15
15
  {
16
- "name": "slice-update",
16
+ "name": "slice-event",
17
17
  "description": "Defines the event name on which `value` would be synchronized with DCE slice\n\nOn: any component with `value` and associated change event",
18
18
  "doc-url": "https://unpkg.com/@epa-wg/custom-element/demo/dom-merge.html"
19
+ },
20
+ {
21
+ "name": "slice-value",
22
+ "description": "XPath expression to populate into the slice",
23
+ "doc-url": "https://unpkg.com/@epa-wg/custom-element/demo/data-slices.html"
19
24
  }
20
25
  ],
21
26
  "elements": [
@@ -91,6 +96,14 @@
91
96
  }
92
97
  }
93
98
  ]
99
+ },
100
+ {
101
+ "name": "slice",
102
+ "description": "Synthetic element for defining the slice-attributed when more then one slice/event/value associated with parent element",
103
+ "doc-url": "https://github.com/EPA-WG/custom-element/tree/develop?tab=readme-ov-file#interactivity-via-data-slice-triggered-by-events",
104
+ "attributes": [
105
+
106
+ ]
94
107
  }
95
108
  ]
96
109
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "http://json.schemastore.org/web-types",
3
3
  "name": "@epa-wg/custom-element",
4
- "version": "0.0.18",
4
+ "version": "0.0.19",
5
5
  "js-types-syntax": "typescript",
6
6
  "description-markup": "markdown",
7
7
  "contributions": {
package/index.html CHANGED
@@ -9,7 +9,7 @@
9
9
  @import "demo/demo.css";
10
10
  </style>
11
11
  </head>
12
- <body>
12
+ <body xmlns:xhtml="http://www.w3.org/1999/xhtml">
13
13
  <nav>
14
14
  <h3><code>custom-element</code> demo</h3>
15
15
  <div><a href="https://github.com/EPA-WG/custom-element"
@@ -34,6 +34,7 @@
34
34
  <a href="./demo/hex-grid.html" >hex grid lib </a> |
35
35
  <a href="./demo/scoped-css.html" >scoped CSS </a> |
36
36
  <a href="./demo/parameters.html" >attributes </a> |
37
+ <a href="./demo/data-slices.html" >data slices/events </a> |
37
38
  <a href="./demo/dom-merge.html" >DOM merge on dynamic update </a>
38
39
  </section>
39
40
  </nav>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epa-wg/custom-element",
3
- "version": "0.0.18",
3
+ "version": "0.0.19",
4
4
  "description": "Declarative Custom Element as W3C proposal PoC with native(XSLT) based templating",
5
5
  "browser": "custom-element.js",
6
6
  "module": "custom-element.js",