@epa-wg/custom-element 0.0.31 → 0.0.32

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
@@ -341,16 +341,16 @@ within template
341
341
  [css-demo-url]: https://unpkg.com/@epa-wg/custom-element@0.0/demo/scoped-css.html
342
342
  [slice-demo-url]: https://unpkg.com/@epa-wg/custom-element@0.0/demo/data-slices.html
343
343
  [hex-grid-url]: https://unpkg.com/@epa-wg/custom-element@0.0/demo/hex-grid.html
344
- [hex-grid-image]: https://unpkg.com/@epa-wg/custom-element@0.0.31/demo/hex-grid-transform.png
344
+ [hex-grid-image]: https://unpkg.com/@epa-wg/custom-element@0.0.32/demo/hex-grid-transform.png
345
345
  [local-storage-demo]: https://unpkg.com/@epa-wg/custom-element@0.0/demo/local-storage.html
346
346
  [http-request-demo]: https://unpkg.com/@epa-wg/custom-element@0.0/demo/http-request.html
347
347
  [location-demo]: https://unpkg.com/@epa-wg/custom-element@0.0/demo/location.html
348
348
  [github-image]: https://cdnjs.cloudflare.com/ajax/libs/octicons/8.5.0/svg/mark-github.svg
349
349
  [npm-image]: https://img.shields.io/npm/v/@epa-wg/custom-element.svg
350
350
  [npm-url]: https://npmjs.org/package/@epa-wg/custom-element
351
- [coverage-image]: https://unpkg.com/@epa-wg/custom-element-dist@0.0.31/coverage/src/custom-element/coverage.svg
352
- [coverage-url]: https://unpkg.com/@epa-wg/custom-element-dist@0.0.31/coverage/src/custom-element/index.html
353
- [storybook-url]: https://unpkg.com/@epa-wg/custom-element-dist@0.0.31/storybook-static/index.html?path=/story/welcome--introduction
351
+ [coverage-image]: https://unpkg.com/@epa-wg/custom-element-dist@0.0.32/coverage/src/custom-element/coverage.svg
352
+ [coverage-url]: https://unpkg.com/@epa-wg/custom-element-dist@0.0.32/coverage/src/custom-element/index.html
353
+ [storybook-url]: https://unpkg.com/@epa-wg/custom-element-dist@0.0.32/storybook-static/index.html?path=/story/welcome--introduction
354
354
  [sandbox-url]: https://stackblitz.com/github/EPA-WG/custom-element?file=index.html
355
355
  [webcomponents-url]: https://www.webcomponents.org/element/@epa-wg/custom-element
356
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.31",
119
+ "version": "0.0.32",
120
120
  "js-types-syntax": "typescript",
121
121
  "description-markup": "markdown",
122
122
  "contributions": {
package/custom-element.js CHANGED
@@ -15,6 +15,7 @@ const attr = (el, attr)=> el.getAttribute?.(attr)
15
15
  , xslNs = x => ( x?.setAttribute('xmlns:xsl', XSL_NS_URL ), x )
16
16
  , xslHtmlNs = x => ( x?.setAttribute('xmlns:xhtml', HTML_NS_URL ), xslNs(x) )
17
17
  , isValidTagName = tag=> ( /^[_a-zA-Z][-_:a-zA-Z0-9]*$/ .test(tag) )
18
+ , mix = (o,kv) => { Object.keys(kv).map(k=> o[k] = kv[k] ) ; return o}
18
19
  , create = ( tag, t = '', d=document ) =>
19
20
  {
20
21
  const create = tag => ( e => ((t && e.append(createText(d.ownerDocument||d, t))),e) )((d.ownerDocument || d ).createElement( tag ))
@@ -29,11 +30,13 @@ const attr = (el, attr)=> el.getAttribute?.(attr)
29
30
  { const px = p.ownerDocument.createElementNS(p.namespaceURI,tag);
30
31
  for( let a of p.attributes)
31
32
  px.setAttribute(a.name, a.value);
32
- while( p.firstChild )
33
- px.append(p.firstChild);
33
+ for( let c of p.childNodes )
34
+ px.append(c.cloneNode(true));
34
35
  return px;
35
36
  };
36
37
 
38
+ export {cloneAs,mix};
39
+
37
40
  function
38
41
  ASSERT(x)
39
42
  {
@@ -149,8 +152,13 @@ tagUid( node )
149
152
  export function
150
153
  createXsltFromDom( templateNode, S = 'xsl:stylesheet' )
151
154
  {
155
+ const declaredAttributes = []
156
+ , hardcodedAttributes = {}
157
+ , exposedAttributes={};
158
+
152
159
  if( templateNode.tagName === S || templateNode.documentElement?.tagName === S )
153
- return tagUid(templateNode)
160
+ return tagUid(mix( templateNode, { declaredAttributes, hardcodedAttributes, exposedAttributes } ));
161
+
154
162
  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" >
155
163
  <xsl:output method="xml"/>
156
164
  <xsl:template match="/"><dce-root xmlns="${ HTML_NS_URL }"><xsl:apply-templates select="*" /></dce-root></xsl:template>
@@ -198,9 +206,9 @@ createXsltFromDom( templateNode, S = 'xsl:stylesheet' )
198
206
  </xsl:template>
199
207
  <xsl:template mode="sanitize" match="xhtml:input">
200
208
  <xsl:element name="{local-name()}">
201
- <xsl:apply-templates mode="sanitize" select="*|@*|text()"/>
209
+ <xsl:apply-templates mode="sanitize" select="*[not(name()='slice')]|@*|text()"/>
202
210
  </xsl:element>
203
- <xsl:for-each select="*">
211
+ <xsl:for-each select="slice">
204
212
  <xsl:copy>
205
213
  <xsl:attribute name="for" >^</xsl:attribute>
206
214
  <xsl:apply-templates mode="sanitize" select="*|@*|text()"/>
@@ -285,35 +293,45 @@ createXsltFromDom( templateNode, S = 'xsl:stylesheet' )
285
293
  [...fr.childNodes].forEach(n=>r.append(n));
286
294
  fr.append(r)
287
295
  }
288
- const params = [];
296
+
289
297
  [...fr.querySelectorAll('dce-root>attribute')].forEach( a=>
290
298
  {
291
299
  keepAttributes(a,'namespace,name,select');
292
300
  const p = cloneAs(a,'xsl:param')
293
301
  , name = attr(a,'name');
302
+
303
+ declaredAttributes.push(name);
304
+ if( a.childNodes.length)
305
+ hardcodedAttributes[name] = a.textContent;
306
+
294
307
  payload.append(p);
295
- keepAttributes(p,'select,name');
296
- let select = attr(p,'select')?.split('??')
297
- if( !select)
298
- { select = ['//'+name, `'${p.textContent}'`];
299
- emptyNode(p);
300
- p.setAttribute('name',name);
301
- }
302
- let val;
303
- if( select?.length>1 )
304
- { p.removeAttribute('select');
305
- const c = $( xslDom, 'template[match="ignore"]>choose').cloneNode(true);
306
- emptyNode(c.firstElementChild).append( createText(c,'{'+select[0]+'}'));
307
- emptyNode(c.lastElementChild ).append( createText(c,'{'+select[1]+'}'));
308
- c.firstElementChild.setAttribute('test',select[0]);
309
- p.append(c);
310
- val = c.cloneNode(true);
308
+
309
+ if( a.hasAttribute('select') )
310
+ {
311
+ exposedAttributes[ name ] = attr( a, 'select' );
312
+ keepAttributes( p, 'select,name' );
313
+
314
+ let select = attr(a,'select').split('??');
315
+
316
+ let val;
317
+ if( select?.length>1 )
318
+ { p.removeAttribute('select');
319
+ const c = $( xslDom, 'template[match="ignore"]>choose').cloneNode(true);
320
+ // todo multiple ?? operators
321
+ emptyNode(c.firstElementChild).append( createText(c,'{'+select[0]+'}'));
322
+ emptyNode(c.lastElementChild ).append( createText(c,'{'+select[1]+'}'));
323
+ c.firstElementChild.setAttribute('test','string-length('+select[0]+')');
324
+ p.append(c);
325
+ val = c.cloneNode(true);
326
+ }else
327
+ val = cloneAs(a,'xsl:value-of');
328
+ val.removeAttribute('name');
329
+ a.append(val);
330
+ a.removeAttribute('select');
311
331
  }else
312
- val=cloneAs(a,'xsl:value-of');
313
- val.removeAttribute('name');
314
- a.append(val);
315
- a.removeAttribute('select');
316
- params.push(p)
332
+ { keepAttributes( p, 'name' );
333
+ p.setAttribute('select','/datadom/attributes/'+name)
334
+ }
317
335
  });
318
336
  [...fr.querySelectorAll('[value]')].filter(el=>el.getAttribute('value').match( /\{(.*)\?\?(.*)\}/g )).forEach(el=>
319
337
  { const v = attr(el,'value');
@@ -338,8 +356,8 @@ createXsltFromDom( templateNode, S = 'xsl:stylesheet' )
338
356
 
339
357
  forEach$( payload,'slot', s => s.parentNode.replaceChild( slot2xsl(s), s ) )
340
358
 
341
- const ret = tagUid(xslDom)
342
- ret.params = params;
359
+ const ret = tagUid(xslDom);
360
+ mix( ret, { declaredAttributes, hardcodedAttributes, exposedAttributes } );
343
361
  return ret;
344
362
  }
345
363
  export async function
@@ -494,10 +512,12 @@ const loadTemplateRoots = async ( src, dce )=>
494
512
  }
495
513
  export function mergeAttr( from, to )
496
514
  { for( let a of from.attributes)
497
- { a.namespaceURI? to.setAttributeNS( a.namespaceURI, a.name, a.value ) : to.setAttribute( a.name, a.value )
498
- if( a.name === 'value')
499
- to.value = a.value
500
- }
515
+ try
516
+ { a.namespaceURI? to.setAttributeNS( a.namespaceURI, a.name, a.value ) : to.setAttribute( a.name, a.value )
517
+ if( a.name === 'value')
518
+ to.value = a.value
519
+ }catch(e)
520
+ { console.warn('attribute assignment error',e?.message || e); }
501
521
  }
502
522
  export function assureUnique(n, id=0)
503
523
  {
@@ -530,7 +550,7 @@ export function appendByDceId(parent,e,k)
530
550
  }
531
551
  export function merge( parent, fromArr )
532
552
  {
533
- if( 'dce-root' === parent.firstElementChild?.localName && 'dce-root' !== fromArr[0].localName)
553
+ if( 'dce-root' === parent.firstElementChild?.localName && 'dce-root' !== fromArr[0]?.localName)
534
554
  return;
535
555
  if( !fromArr.length )
536
556
  return 'dce-root' !== parent.firstElementChild?.localName && removeChildren(parent);
@@ -658,12 +678,13 @@ CustomElement extends HTMLElement
658
678
  , sliceNames = sliceNodes.map(e=>attr(e,'slice'))
659
679
  .filter(n=>!n.includes('/'))
660
680
  .filter((v, i, a)=>a.indexOf(v) === i)
661
- .map(splitSliceNames).flat()
662
- , declaredAttributes = templateDocs.reduce( (ret,t) => { if( t.params ) ret.push( ...t.params ); return ret; }, [] );
681
+ .map(splitSliceNames).flat();
682
+
683
+ const { declaredAttributes, hardcodedAttributes, exposedAttributes } = templateDocs[0];
663
684
 
664
685
  class DceElement extends HTMLElement
665
686
  {
666
- static get observedAttributes(){ return declaredAttributes.map( a=>attr(a,'name')); }
687
+ static get observedAttributes(){ return declaredAttributes; }
667
688
  #inTransform = 0;
668
689
  connectedCallback()
669
690
  { let payload = sanitizeBlankText(this.childNodes);
@@ -696,7 +717,11 @@ CustomElement extends HTMLElement
696
717
  xslNs(payloadNode);
697
718
  xslHtmlNs(payloadNode);
698
719
  this.innerHTML='';
699
- const attrsRoot = injectData( x, 'attributes' , this.attributes, e => createXmlNode( e.nodeName, e.value ) );
720
+ const attrsRoot = injectData( x, 'attributes' , this.attributes, e => createXmlNode( e.nodeName, e.value ) )
721
+ , inAttrs = a=> this.hasAttribute(a) || [...attrsRoot.children].find(e=>e.localName === a);
722
+ Object.keys(hardcodedAttributes).map(a=> inAttrs(a) || attrsRoot.append(createXmlNode(a,hardcodedAttributes[a])) );
723
+ declaredAttributes.map(a=> inAttrs(a) || attrsRoot.append(createXmlNode(a)) );
724
+
700
725
  injectData( x, 'dataset', Object.keys( this.dataset ), k => createXmlNode( k, this.dataset[ k ] ) );
701
726
  const sliceRoot = injectData( x, 'slice', sliceNames, k => createXmlNode( k, '' ) )
702
727
  , sliceXPath = x => xPath(x, sliceRoot);
@@ -742,13 +767,20 @@ CustomElement extends HTMLElement
742
767
  merge( this, f.childNodes )
743
768
  })
744
769
 
745
- DceElement.observedAttributes.map( a =>
770
+ Object.entries(hardcodedAttributes).map(( [a,v] )=>
771
+ { if( !this.hasAttribute(a) && v !== attr(this,a) )
772
+ { this.setAttribute( a, v );
773
+ this.#applyAttribute( a, v );
774
+ }
775
+ });
776
+
777
+ Object.keys(exposedAttributes).map( a =>
746
778
  { let v = attr(this.firstElementChild,a);
747
779
  if( v !== attr(this,a) )
748
780
  { this.setAttribute( a, v );
749
781
  this.#applyAttribute( a, v );
750
782
  }
751
- })
783
+ });
752
784
 
753
785
  function getSliceTarget(el)
754
786
  { let r = el;
package/demo/a.html ADDED
@@ -0,0 +1,73 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
5
+ <title>http-request 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="../custom-element.js"></script>
10
+ <style>
11
+ @import "./demo.css";
12
+
13
+ button {
14
+ display: inline-flex;
15
+ flex-direction: column;
16
+ align-items: center;
17
+ flex: auto;
18
+ box-shadow: inset silver 0 0 1rem;
19
+ min-width: 12rem;
20
+ padding: 1rem;
21
+ color: coral;
22
+ text-shadow: 1px 1px silver;
23
+ font-weight: bolder;
24
+ }
25
+
26
+ button img {
27
+ max-height: 10vw;
28
+ min-height: 4rem;
29
+ }
30
+
31
+ table {
32
+ min-width: 16rem;
33
+ }
34
+
35
+ td {
36
+ border-bottom: 1px solid silver;
37
+ }
38
+
39
+ tfoot td {
40
+ border-bottom: none;
41
+ }
42
+
43
+ td, th {
44
+ text-align: right;
45
+ }
46
+
47
+ caption {
48
+ padding: 1rem;
49
+ font-weight: bolder;
50
+ font-family: sans-serif;
51
+ }
52
+
53
+ code {
54
+ text-align: right;
55
+ min-width: 3rem;
56
+ }
57
+
58
+ svg {
59
+ max-height: 3rem;
60
+ }
61
+ </style>
62
+ </head>
63
+ <body>
64
+
65
+
66
+
67
+ <custom-element src="./html-template.html#dwc-logo">
68
+ <template><i>loading SVG from templates file ...</i></template>
69
+ </custom-element>
70
+
71
+
72
+ </body>
73
+ </html>
@@ -233,8 +233,8 @@
233
233
  </template>
234
234
  </html-demo-element>
235
235
 
236
- <html-demo-element legend="13. Radio buttons use"
237
- description="The value is empty when unchecked. Otherwise taken either from slice-value or value attribute.">
236
+ <html-demo-element legend="13. Radio group"
237
+ description="The value propagated into slice from the last checked radiobutton.">
238
238
  <p>Check the radiobutton to see the value in slice</p>
239
239
  <template>
240
240
  <custom-element>
@@ -57,6 +57,7 @@
57
57
 
58
58
  svg {
59
59
  max-height: 3rem;
60
+ min-height: 2rem;
60
61
  }
61
62
  </style>
62
63
  </head>
package/demo/form.html CHANGED
@@ -233,6 +233,48 @@
233
233
  </template>
234
234
  </html-demo-element>
235
235
 
236
+ <html-demo-element legend="5. using custom-element as form input"
237
+ description="bypass value to container form "
238
+ id="sample-5"
239
+ >
240
+ <ol>
241
+ <li> select radio in top group</li>
242
+ <li> observe the fruit selected on the left</li>
243
+ <li> observe the warning message bellow button</li>
244
+ <li> next button does not submit the form</li>
245
+ <li> select radio in bottom group</li>
246
+ <li> observe the fruit selected on the right</li>
247
+ <li> same fruit selection would eliminate the warning and
248
+ allow the form submission</li>
249
+ <li> after submit, observe the URL parameters 'inp-1=🍏&inp-2=🍏' </li>
250
+ </ol>
251
+ <template>
252
+ <custom-element tag="sample-input">
253
+ <template>
254
+ <attribute name="name" ></attribute>
255
+ <attribute name="value" select="//val"></attribute>
256
+ {$name}
257
+ <label><input type="radio" name="{$name}" slice="val" value="🍏">Apple</label>
258
+ <label><input type="radio" name="{$name}" slice="val" value="🍌">Banana</label>
259
+ </template>
260
+ </custom-element>
261
+ <custom-element>
262
+ <template>
263
+ <form slice="cart-form" action="#sample-5"
264
+ custom-validity="(//inp1 = //inp2) ?? 'pick same fruit'"
265
+ >
266
+ <sample-input slice="inp1" name="inp-1"></sample-input>
267
+ <sample-input slice="inp2" name="inp-2"></sample-input>
268
+ <button>Next</button>
269
+ Picked :{//inp1} and {//inp2}
270
+
271
+ <p>{//cart-form/@validation-message}</p>
272
+ </form>
273
+ </template>
274
+ </custom-element>
275
+ </template>
276
+ </html-demo-element>
277
+
236
278
  <script type="module" src="https://unpkg.com/html-demo-element@1/html-demo-element.js"></script>
237
279
 
238
280
  </body>
@@ -107,7 +107,7 @@
107
107
  <img src="https://upload.wikimedia.org/wikipedia/commons/1/1b/Svelte_Logo.svg" alt="Svelte"/>
108
108
  <img src="https://www.solidjs.com/img/logo/without-wordmark/logo.svg" alt="SolidJS"/>
109
109
  <img src="https://www.svgrepo.com/show/354113/nextjs-icon.svg" alt="NextJS"/>
110
- <img src="https://global.discourse-cdn.com/standard17/uploads/threejs/original/2X/b/be2f75f72751c11cbe1593c69a99a52900bf12cb.svg" alt="ThreeJS" href="https://threejs.org/"/>
110
+ <img src="https://threejs.org/files/favicon.ico" alt="ThreeJS" href="https://threejs.org/"/>
111
111
  <img src="https://www.blazejs.org/logo/icon.png" alt="BlazeJS"/>
112
112
  <img src="https://dmtgy0px4zdqn.cloudfront.net/images/integrations/tailwindcss.webp" alt="Tailwind CSS"/>
113
113
  <img src="https://dmtgy0px4zdqn.cloudfront.net/images/integrations/flowbite.webp" alt="Flowbite"/>
@@ -52,7 +52,7 @@
52
52
  <img src="https://semantic-ui.com/images/logo.png" alt="Semantic UI" href="https://semantic-ui.com/"/>
53
53
  <img src="https://open-wc.org/35ded306.svg" alt="Open WC" href="https://open-wc.org/"/>
54
54
  <img src="https://storage.googleapis.com/cms-storage-bucket/4fd0db61df0567c0f352.png" alt="Flutter" href="https://flutter.dev/"/>
55
- <img src="https://refine.dev/img/refine_favicon.svg" alt="Refine" href="https://refine.dev/"/>
55
+ <img src="https://refine.dev/img/favicon.ico" alt="Refine" href="https://refine.dev/"/>
56
56
  <img src="https://getbootstrap.com/docs/5.3/assets/brand/bootstrap-logo-shadow.png" alt="Bootstrap" href="https://getbootstrap.com/"/>
57
57
  <img src="https://upload.wikimedia.org/wikipedia/commons/9/95/Vue.js_Logo_2.svg" alt="Vue.JS" href="https://vuejs.org/"/>
58
58
  <img src="https://lit.dev/images/logo.svg#flame" alt="Lit"/>
@@ -44,7 +44,7 @@ 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="
47
+ <html-demo-element legend="attribute from slice" description="
48
48
  when slice value points to attribute, it would be populated on slice change
49
49
  ">
50
50
  Type in the input field to see the variable $title change. <br/>
@@ -62,6 +62,25 @@ when slice value points to attribute, it would be populated on slice change
62
62
  </template>
63
63
  </html-demo-element>
64
64
 
65
+ <details>
66
+ <summary>Attributes processing</summary>
67
+ To be available in template, <code>custom-element</code> attributes should be
68
+ defined by <code>attribute</code> markup as shown above.
69
+ The value is taken from attribute text content or from <code>select</code> XPath expression<br/>
70
+
71
+ Declared in such way, attributes are exposed via <code><a
72
+ href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#responding_to_attribute_changes"
73
+ >observedAttributes</a></code>. <br/>
74
+ The template exposes those attributes as <code>xsl:param</code> which makes the attribute value available as
75
+ xsl variable (as attribute name prefixed with $). <br/>
76
+ After transformation the attributes values are read from CE root and copied into DCE component. <br/>
77
+ The DCE attribute change from outside invokes <code>attributeChangedCallback</code> which triggers DCE re-render.
78
+ <p>
79
+ The <code>value</code> attribute is usual case to be propagated from within of <code>custom-element</code>.
80
+ See the <a href="./form.html#sample-5">using custom-element as form input</a> example.
81
+ </p>
82
+ &bull; <a href="https://github.com/EPA-WG/custom-element/blob/main/docs/attributes.md">Design docs</a>
83
+ </details>
65
84
 
66
85
 
67
86
  <script type="module" src="https://unpkg.com/html-demo-element@1/html-demo-element.js"></script>
package/demo/s.xml CHANGED
@@ -1,18 +1,12 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <datadom>
3
- <payload xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml">
4
- <a xmlns="http://www.w3.org/1999/xhtml" href="#" data-dce-id="4" slot="">link 1</a>
5
- <a xmlns="http://www.w3.org/1999/xhtml" href="#" data-dce-id="5" slot="">link 2</a>
6
- <a xmlns="http://www.w3.org/1999/xhtml" href="#" data-dce-id="6" slot="">link 3</a>
7
- </payload>
3
+ <payload xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml"/>
8
4
  <attributes>
9
- <xmlns/>
10
- <justify>end</justify>
11
- <data-dce-id>2</data-dce-id>
12
- <direction>row</direction>
5
+ <id>dce1</id>
6
+ <p1>default_P1</p1>
7
+ <p2>always_p2</p2>
8
+ <p3></p3>
13
9
  </attributes>
14
- <dataset>
15
- <dceId>2</dceId>
16
- </dataset>
10
+ <dataset/>
17
11
  <slice/>
18
12
  </datadom>
package/demo/s.xslt CHANGED
@@ -1,20 +1,69 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
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
+ <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml"
3
+ xmlns:dce="urn:schemas-epa-wg:dce" xmlns:exsl="http://exslt.org/common" version="1.0"
4
+ exclude-result-prefixes="exsl">
3
5
  <xsl:template match="ignore">
4
6
  <xsl:choose>
5
- <xsl:when test="//attr"><xsl:value-of select="//attr"/></xsl:when>
6
- <xsl:otherwise><xsl:value-of select="def"/></xsl:otherwise>
7
- </xsl:choose><xsl:value-of select="."/></xsl:template>
8
- <xsl:template mode="payload" match="attributes"><xsl:param name="direction"><xsl:choose>
9
- <xsl:when test="//direction"><xsl:value-of select="//direction"/></xsl:when>
10
- <xsl:otherwise><xsl:value-of select="'row'"/></xsl:otherwise>
11
- </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 name="direction"><xsl:choose>
12
- <xsl:when test="//direction"><xsl:value-of select="//direction"/></xsl:when>
13
- <xsl:otherwise><xsl:value-of select="'row'"/></xsl:otherwise>
14
- </xsl:choose></xsl:attribute><xsl:call-template name="slot">
15
- <xsl:with-param name="slotname" select="''"/>
16
- <xsl:with-param name="defaultvalue"/>
17
- </xsl:call-template></dce-root></xsl:template>
7
+ <xsl:when test="//attr">
8
+ <xsl:value-of select="//attr"/>
9
+ </xsl:when>
10
+ <xsl:otherwise>
11
+ <xsl:value-of select="def"/>
12
+ </xsl:otherwise>
13
+ </xsl:choose>
14
+ <xsl:value-of select="."/>
15
+ </xsl:template>
16
+ <xsl:template mode="payload" match="attributes">
17
+ <xsl:param name="p1" select="/datadom/attributes/p1">default_P1</xsl:param>
18
+ <xsl:param name="p2" select="'always_p2'"/>
19
+ <xsl:param name="p3">
20
+ <xsl:choose>
21
+ <xsl:when test="string-length(//p3)>0 ">RRRR
22
+ <xsl:value-of select="count(//p3)"/>
23
+ +<xsl:value-of select="//p3 "/>=
24
+ </xsl:when>
25
+ <xsl:otherwise>OOO
26
+ <xsl:value-of select=" 'def_P3' "/>
27
+ </xsl:otherwise>
28
+ </xsl:choose>
29
+ </xsl:param>
30
+ <dce-root xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml" data-dce-id="1">
31
+ <xsl:attribute name="p1">default_P1</xsl:attribute>
32
+ <xsl:attribute name="p2">
33
+ <xsl:value-of select="'always_p2'"/>
34
+ </xsl:attribute>
35
+ <xsl:attribute name="p3">
36
+ <xsl:choose>
37
+ <xsl:when test="//p3 ">
38
+ <xsl:value-of select="//p3 "/>
39
+ </xsl:when>
40
+ <xsl:otherwise>
41
+ <xsl:value-of select=" 'def_P3' "/>
42
+ </xsl:otherwise>
43
+ </xsl:choose>
44
+ </xsl:attribute>
45
+ <dce-text xmlns="" data-dce-id="2">
46
+ p1:
47
+ </dce-text>
48
+ <code xmlns="" data-testid="p1" data-dce-id="3">
49
+ <xsl:value-of select="$p1"/>
50
+ </code>
51
+ <br xmlns="" data-dce-id="4"/>
52
+ <dce-text xmlns="" data-dce-id="5">
53
+ p2:
54
+ </dce-text>
55
+ <code xmlns="" data-testid="p2" data-dce-id="6">
56
+ <xsl:value-of select="$p2"/>
57
+ </code>
58
+ <br xmlns="" data-dce-id="7"/>
59
+ <dce-text xmlns="" data-dce-id="8">
60
+ p3:
61
+ </dce-text>
62
+ <code xmlns="" data-testid="p3" data-dce-id="9">
63
+ <xsl:value-of select="$p3"/>
64
+ </code>
65
+ </dce-root>
66
+ </xsl:template>
18
67
  <xsl:template match="/">
19
68
  <xsl:apply-templates mode="payload" select="/datadom/attributes"/>
20
69
  </xsl:template>
@@ -23,7 +72,7 @@
23
72
  <xsl:param name="defaultvalue"/>
24
73
  <xsl:choose>
25
74
  <xsl:when test="//payload/*[@slot=$slotname]">
26
- <xsl:copy-of select="//payload/*[@slot=$slotname]"/>
75
+ <xsl:copy-of select="//payload/*[@slot=$slotname]"/>
27
76
  </xsl:when>
28
77
  <xsl:otherwise>
29
78
  <xsl:copy-of select="$defaultvalue"/>
package/demo/ss.html CHANGED
@@ -1,24 +1,5 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <dce-root data-dce-id="1" label="Number" name="" placeholder="Enter the numeric value" type="text" value=""
3
- xmlns="http://www.w3.org/1999/xhtml" xmlns:dce="urn:schemas-epa-wg:dce"
4
- xmlns:xhtml="http://www.w3.org/1999/xhtml"><pre data-dce-id="2" xmlns=""> //focused
5
- //suggest
6
- //selected
7
- $selected-value </pre>
8
- <label data-dce-id="3">
9
- <dce-text data-dce-id="4">Number</dce-text>
10
- <input data-dce-id="5" name="" placeholder="Enter the numeric value" type="text" value="">
11
- <slice data-dce-id="6" name="focused" slice-event="focus" slice-value="1"/>
12
- <slice data-dce-id="7" name="selected" slice-event="input"/>
13
- </input></label>
14
- <fieldset data-dce-id="8" form="cem-autocomplete-form" xmlns=""><label data-dce-id="9" value="one"><input
15
- data-dce-id="10" name="cem-autocomplete" slice="suggest" slice-event="input" type="radio" value="one"/>
16
- <dce-text data-dce-id="11">One</dce-text>
17
- </label><label data-dce-id="9" value="two"><input data-dce-id="10" name="cem-autocomplete" slice="suggest" slice-event="input"
18
- type="radio" value="two"/>
19
- <dce-text data-dce-id="11">Two</dce-text>
20
- </label><label data-dce-id="9" value="three"><input data-dce-id="10" name="cem-autocomplete" slice="suggest"
21
- slice-event="input" type="radio" value="three"/>
22
- <dce-text data-dce-id="11">Three</dce-text>
23
- </label></fieldset>
24
- </dce-root>
2
+ <dce-root data-dce-id="1" value="123"
3
+ xmlns="http://www.w3.org/1999/xhtml" xmlns:dce="urn:schemas-epa-wg:dce" xmlns:xhtml="http://www.w3.org/1999/xhtml"><label data-dce-id="2">
4
+ <dce-text data-dce-id="3"/>
5
+ <input data-dce-id="4" slice="selected" slice-event="input" value="123"/></label></dce-root>
@@ -0,0 +1,86 @@
1
+ # Attributes in `custom-element`
2
+
3
+ # Declaring
4
+ In order to have access to attributes and expose them back as `custom-element` attributes in runtime, they have to be
5
+ declared as list of direct children in CE `template` body:
6
+ ```html
7
+ <custom-element tag="sample-el">
8
+ <template>
9
+ <attribute name="p1"></attribute>
10
+ <attribute name="p2">DEFAULT VALUE</attribute>
11
+ <attribute name="p3" select="//from-input"></attribute>
12
+ <input slot="from-input" />
13
+ { $p1 } { $p2 } { $p3 }
14
+ </template>
15
+ </custom-element>
16
+ ```
17
+ ## Unassigned attribute value
18
+ The `p1` attribute meant to reflect the value of attribute passed from `sample-1` use.
19
+ ```html
20
+ <sample-el p1="abc"></sample-el>
21
+ ```
22
+ The value inside of CE rendered by `{ $p1 }` would be `abc` string.
23
+ It does not change as there is no dynamic selector is set.
24
+
25
+ ## Fixed value
26
+ ```html
27
+ <sample-el></sample-el>
28
+ <sample-el p2="DEFAULT VALUE"></sample-el>
29
+ ```
30
+ In given example both instances would have same value for `p2` attribute.
31
+ The missing attribute on 1st `sample-element` would be filled in runtime by CE.
32
+
33
+ ## `select` expression
34
+ used for dynamic attribute propagation from within CE. For `sample-el` `p3` would be in sync with input value.
35
+
36
+ # Implementation details
37
+ The source of truth for internal attributes collection is `/datadom/attributes`, an internal instance xml dom model.
38
+ As a "state" object, any change to it would be reflected in
39
+ * CE instance attributes
40
+ * rendered by template DOM, the attributed in template are referenced as
41
+ * XSLT variables, a name prefixed with `$`, like `$p1`. Recommended syntax.
42
+ * as `/datadom/attributes/*` XPath in template.
43
+ * as name, i.e. `p1` in XPath. The current data scope for XPath in template matches `/datadom/attributes` node.
44
+
45
+ ## Declaring
46
+ `custom-element` resolves template body and reads all its direct `attribute` children.
47
+ * All attributes are stored in template-associated `declaredAttributes` collection.
48
+ * The attributes with text value (p2), are stored in template-associated `hardcodedAttributes` key:value collection.
49
+ * The attributes with text value or `select` (as p2,p3), are stored in template-associated `exposedAttributes` collection.
50
+ * Template is translated into XSL where all attributes are replaced by
51
+ ```html
52
+ <xsl:param name="p1" select="/datadom/attributes/p1"></xsl:param>
53
+ <xsl:param name="p2" select="/datadom/attributes/p2"></xsl:param>
54
+ <xsl:param name="p3" select="//from-input"></xsl:param>
55
+ ```
56
+ The `select` is directly copied from `attribute` (p3), otherwise when `select` is missing (p2,p3),
57
+ the values are taken from model.
58
+
59
+ `declaredAttributes` is used as a value for `observedAttributes`.
60
+
61
+ ## Instance initialization
62
+ * keys from `declaredAttributes` saved into `/datadom/attributes`
63
+ * value from `hardcodedAttributes` saved into model
64
+ * all attributes with value from instance populated into model
65
+
66
+ ## 1st and all renders
67
+ happen initially `onConnectedCallback` and on each model change ( triggered by slot events, etc. ).
68
+
69
+ Before render, `exposedAttributes` collection is synced with DCE attributes by assigning the values from attribute
70
+ `select` XPath. Same value is propagated into matching `/datadom/attributes`.
71
+
72
+ The `attributeChangedCallback` would be triggered and invoke the render on each changed attribute.
73
+ `this.#inTransform` flag prevents the recursive render calls.
74
+
75
+ ## Attribute changes from outside
76
+ * Would trigger the `attributeChangedCallback`
77
+ * which would set the `/datadom/attributes`
78
+ * and trigger render cycle
79
+
80
+ Recursive attribute cycle from render is prevented by comparing the value.
81
+
82
+ WARNING: the infinite loop is possible by
83
+ * making one attribute depend on another which depends on first, possibly over multiple indirect dependencies.
84
+ * changing the attribute by `select` and forced to change again from outside by overriding attribute value on CE instance.
85
+
86
+ Such patterns are unlikely. Detection mechanism TBD.
@@ -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.31",
4
+ "version": "0.0.32",
5
5
  "js-types-syntax": "typescript",
6
6
  "description-markup": "markdown",
7
7
  "contributions": {
@@ -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.31",
4
+ "version": "0.0.32",
5
5
  "js-types-syntax": "typescript",
6
6
  "description-markup": "markdown",
7
7
  "contributions": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epa-wg/custom-element",
3
- "version": "0.0.31",
3
+ "version": "0.0.32",
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",
File without changes