@epa-wg/custom-element 0.0.6 → 0.0.8

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
@@ -8,7 +8,11 @@ It allows to define custom HTML tag with template filled from slots and attribut
8
8
  | Live demo: [custom-element][demo-url]
9
9
  | Try in [Sandbox][sandbox-url]
10
10
  | [tests project][git-test-url]
11
- [![NPM version][npm-image]][npm-url] [![coverage][coverage-image]][coverage-url]
11
+ | [Chrome devtools pugin][plugin-url]
12
+
13
+ [![NPM version][npm-image]][npm-url]
14
+ [![coverage][coverage-image]][coverage-url]
15
+ [![Published on webcomponents.org][webcomponents-img]][webcomponents-url]
12
16
 
13
17
  # use
14
18
  ## install
@@ -16,16 +20,13 @@ use via CDN
16
20
  ```html
17
21
  <script type="module" src="https://unpkg.com/@epa-wg/custom-element@0.0/custom-element.js"></script>
18
22
  ```
19
- NPM
23
+ NPM, yarn
20
24
  ```shell
21
25
  npm i -P @epa-wg/custom-element
22
- ```
23
- yarn
24
- ```shell
25
26
  yarn add @epa-wg/custom-element
26
27
  ```
27
28
 
28
- ## [Live demo][demo-url]
29
+ ## [Live demo 🔗][demo-url]
29
30
  ```html
30
31
  <custom-element tag="pokemon-tile" hidden>
31
32
  <h3><xsl:value-of select="title"/></h3> <!-- title is an attribute in instance
@@ -127,7 +128,50 @@ is available in `{}` in attributes, in `xsl:for-each`, `xsl:if`, `xsl:value-of`,
127
128
 
128
129
  XPath is a selector language to navigate over custom element instance data, attributes, and payload.
129
130
 
131
+ ## XSLT 1.0
132
+ The in-browser native implementation as of now supports [XSLT 1.0](https://www.w3.org/TR/xslt-10/).
133
+ File the [change request](https://github.com/EPA-WG/custom-element/issues) for support of another XSLT version or
134
+ template engine.
135
+
130
136
  # troubleshooting
137
+ ## HTML parser is not compatible with templates
138
+ On many tags like `table`, or link `a` the attempt to use XSLT operations could lead to DOM order missmatch to given
139
+ in template. In such cases the `html:` prefix in front of troubled tag would solve the parsing.
140
+
141
+ ```html
142
+ <custom-element tag="dce-2" hidden>
143
+ <local-storage key="basket" slice="basket"></local-storage>
144
+ <html:table>
145
+ <xsl:for-each select="//slice/basket/@*">
146
+ <html:tr>
147
+ <html:th><xsl:value-of select="name()"/></html:th>
148
+ <html:td><xsl:value-of select="."/></html:td>
149
+ </html:tr>
150
+ </xsl:for-each>
151
+ </html:table>
152
+ count:<xsl:value-of select="count(//slice/basket/@*)"/>
153
+ </custom-element>
154
+ ```
155
+ See [demo source](demo/local-storage.html) for detailed sample.
156
+
157
+ ## Chrome devtools plugin
158
+ [@epa-wg/custom-element plugin][plugin-url] gives the view into
159
+
160
+ * `current` selected in DOM inspector node
161
+ * Parent `customElement`
162
+ * Declarative Custom Element `dce` for custom element ^^
163
+
164
+ * `datadom` for easier inspection
165
+ * `xml` as a string
166
+ * `xslt` as a string
167
+
168
+ ## template debugging
169
+ `xml` and `xslt` can be saved to file via for "_copy string contents_" into clipboard.
170
+
171
+ The XSLT debugger from your favorite IDE can set the breakpoints withing those files and
172
+ run transformation under debugger.
173
+
174
+
131
175
  ## `{}` does not give a value
132
176
  * try to add as attribute you could observe and put the value of node name or text to identify the current location in data
133
177
  within template
@@ -141,7 +185,10 @@ within template
141
185
  [github-image]: https://cdnjs.cloudflare.com/ajax/libs/octicons/8.5.0/svg/mark-github.svg
142
186
  [npm-image]: https://img.shields.io/npm/v/@epa-wg/custom-element.svg
143
187
  [npm-url]: https://npmjs.org/package/@epa-wg/custom-element
144
- [coverage-image]: https://unpkg.com/@epa-wg/custom-element-test@0.0.6/coverage/coverage.svg
145
- [coverage-url]: https://unpkg.com/@epa-wg/custom-element-test@0.0.6/coverage/lcov-report/index.html
146
- [storybook-url]: https://unpkg.com/@epa-wg/custom-element-test@0.0.6/storybook-static/index.html?path=/story/welcome--introduction
188
+ [coverage-image]: https://unpkg.com/@epa-wg/custom-element-test@0.0.8/coverage/coverage.svg
189
+ [coverage-url]: https://unpkg.com/@epa-wg/custom-element-test@0.0.8/coverage/lcov-report/index.html
190
+ [storybook-url]: https://unpkg.com/@epa-wg/custom-element-test@0.0.8/storybook-static/index.html?path=/story/welcome--introduction
147
191
  [sandbox-url]: https://stackblitz.com/github/EPA-WG/custom-element?file=index.html
192
+ [webcomponents-url]: https://www.webcomponents.org/element/@epa-wg/custom-element
193
+ [webcomponents-img]: https://img.shields.io/badge/webcomponents.org-published-blue.svg
194
+ [plugin-url]: https://chrome.google.com/webstore/detail/epa-wgcustom-element/hiofgpmmkdembdogjpagmbbbmefefhbl
@@ -1,4 +1,4 @@
1
- export function log(x: any): void;
2
- export class CustomElement extends HTMLElement {
3
- }
4
- export default CustomElement;
1
+ export function log(x: any): void;
2
+ export class CustomElement extends HTMLElement {
3
+ }
4
+ export default CustomElement;
package/custom-element.js CHANGED
@@ -1,81 +1,195 @@
1
- const XML_DECLARATION = '<?xml version="1.0" encoding="UTF-8"?>'
2
- , XSL_NS_URL = 'http://www.w3.org/1999/XSL/Transform';
3
-
4
- // const log = x => console.debug( new XMLSerializer().serializeToString( x ) );
5
-
6
- const create = ( tag, t='' ) =>
7
- {
8
- const e = document.createElement( tag );
9
- if(t) e.innerText = t;
10
- return e;
11
- }
12
- function xml2dom( xmlString )
13
- {
14
- return new DOMParser().parseFromString( XML_DECLARATION + xmlString, "application/xml" )
15
- }
16
- function bodyXml( dce )
17
- { const s = new XMLSerializer().serializeToString( dce );
18
- return s.substring( s.indexOf( '>' ) + 1, s.lastIndexOf( '<' ) );
19
- }
20
- function slot2xsl( s )
21
- { const v = document.createElementNS( XSL_NS_URL, 'value-of' );
22
- v.setAttribute( 'select', `//*[@slot="${ s.name }"]` );
23
- s.parentNode.replaceChild( v, s );
24
- }
25
- function injectData( root, sectionName, arr, cb )
26
- {
27
- const inject = ( tag, parent, s ) =>
28
- {
29
- parent.append( s = create( tag ) );
30
- return s;
31
- };
32
- const l = inject( sectionName, root );
33
- [ ...arr ].forEach( e => l.append( cb(e) ) );
34
- }
35
- function assureSlot( e )
36
- { if( !e.slot )
37
- { if( !e.setAttribute )
38
- e = create( 'span', e.textContent.replaceAll('\n','') );
39
- e.setAttribute( 'slot', '' )
40
- }
41
- return e;
42
- }
43
- export class CustomElement extends HTMLElement
44
- {
45
- constructor()
46
- { super();
47
-
48
- [ ...this.getElementsByTagName( 'slot' ) ].forEach( slot2xsl );
49
- const p = new XSLTProcessor();
50
- p.importStylesheet( xml2dom( `<xsl:stylesheet version="1.0"
51
- xmlns:xsl="${XSL_NS_URL}">
52
- <xsl:output method="html" />
53
-
54
- <xsl:template match="/">
55
- <xsl:apply-templates select="//attributes"/>
56
- </xsl:template>
57
- <xsl:template match="attributes">
58
- ${ bodyXml(this) }
59
- </xsl:template>
60
-
61
- </xsl:stylesheet>` ) );
62
- const tag = this.getAttribute( 'tag' );
63
- tag && window.customElements.define( tag, class extends HTMLElement
64
- {
65
- constructor()
66
- {
67
- super();
68
- const x = create( 'div' );
69
- injectData( x, 'payload' , this.childNodes, assureSlot );
70
- injectData( x, 'attributes' , this.attributes, e=>create( e.nodeName, e.value ) );
71
- injectData( x, 'dataset' , Object.keys( this.dataset ), k=>create( k, this.dataset[ k ] ) );
72
- const f = p.transformToFragment( x, document );
73
- this.innerHTML = '';
74
- [...f.childNodes].forEach(e=>this.appendChild(e));
75
- }
76
- });
77
- }
78
- }
79
-
80
- window.customElements.define( 'custom-element', CustomElement );
81
- export default CustomElement;
1
+ const XML_DECLARATION = '<?xml version="1.0" encoding="UTF-8"?>'
2
+ , XSL_NS_URL = 'http://www.w3.org/1999/XSL/Transform';
3
+
4
+ // const log = x => console.debug( new XMLSerializer().serializeToString( x ) );
5
+
6
+ const attr = (el, attr)=> el.getAttribute(attr)
7
+ , create = ( tag, t = '' ) => ( e => ((e.innerText = t||''),e) )(document.createElement( tag ));
8
+
9
+ function
10
+ xml2dom( xmlString )
11
+ {
12
+ return new DOMParser().parseFromString( XML_DECLARATION + xmlString, "application/xml" )
13
+ }
14
+
15
+ function
16
+ bodyXml( dce )
17
+ {
18
+ const t = dce.firstElementChild
19
+ , sanitize = s => s.replaceAll("<html:","<")
20
+ .replaceAll("</html:","</");
21
+ if( t?.tagName === 'TEMPLATE')
22
+ return sanitize( new XMLSerializer().serializeToString( t.content ) );
23
+
24
+ const s = new XMLSerializer().serializeToString( dce );
25
+ return sanitize( s.substring( s.indexOf( '>' ) + 1, s.lastIndexOf( '<' ) ) );
26
+ }
27
+
28
+ function
29
+ slot2xsl( s )
30
+ {
31
+ const v = document.createElementNS( XSL_NS_URL, 'value-of' );
32
+ v.setAttribute( 'select', `//*[@slot="${ s.name }"]` );
33
+ s.parentNode.replaceChild( v, s );
34
+ }
35
+
36
+ function
37
+ injectData( root, sectionName, arr, cb )
38
+ {
39
+ const inject = ( tag, parent, s ) =>
40
+ {
41
+ parent.append( s = create( tag ) );
42
+ return s;
43
+ };
44
+ const l = inject( sectionName, root );
45
+ [ ...arr ].forEach( e => l.append( cb( e ) ) );
46
+ return l;
47
+ }
48
+
49
+ function
50
+ assureSlot( e )
51
+ {
52
+ if( !e.slot )
53
+ {
54
+ if( !e.setAttribute )
55
+ e = create( 'span', e.textContent.replaceAll( '\n', '' ) );
56
+ e.setAttribute( 'slot', '' )
57
+ }
58
+ return e;
59
+ }
60
+
61
+ export function
62
+ Json2Xml( o, tag )
63
+ {
64
+ if( typeof o === 'string' )
65
+ return o;
66
+
67
+ const noTag = "string" != typeof tag;
68
+
69
+ if( o instanceof Array )
70
+ { noTag && (tag = 'array');
71
+ return "<"+tag+">"+o.map(function(el){ return Json2Xml(el,tag); }).join()+"</"+tag+">";
72
+ }
73
+ noTag && (tag = 'r');
74
+ tag=tag.replace( /[^a-z0-9]/gi,'_' );
75
+ var oo = {}
76
+ , ret = [ "<"+tag+" "];
77
+ for( var k in o )
78
+ if( typeof o[k] == "object" )
79
+ oo[k] = o[k];
80
+ else
81
+ ret.push( k.replace( /[^a-z0-9]/gi,'_' ) + '="'+o[k].toString().replace(/&/gi,'&#38;')+'"');
82
+ if( oo )
83
+ { ret.push(">");
84
+ for( var k in oo )
85
+ ret.push( Json2Xml( oo[k], k ) );
86
+ ret.push("</"+tag+">");
87
+ }else
88
+ ret.push("/>");
89
+ return ret.join('\n');
90
+ }
91
+
92
+ function
93
+ injectSlice( x, s, data )
94
+ {
95
+ const el = create(s)
96
+ , isString = typeof data === 'string' ;
97
+ el.innerHTML = isString? data : Json2Xml( data, s );
98
+ const slice = isString? el : el.firstChild;
99
+ [...x.children].filter( e=>e.localName === s ).map( el=>el.remove() );
100
+ x.append(slice);
101
+ }
102
+
103
+ export class
104
+ CustomElement extends HTMLElement
105
+ {
106
+ constructor()
107
+ {
108
+ super();
109
+
110
+ [ ...this.templateNode.querySelectorAll('slot') ].forEach( slot2xsl );
111
+ const p = new XSLTProcessor();
112
+ p.importStylesheet( this.xslt );
113
+ const tag = attr( this, 'tag' );
114
+ const dce = this;
115
+ const sliceNames = [...this.templateNode.querySelectorAll('[slice]')].map(e=>attr(e,'slice'));
116
+ tag && window.customElements.define( tag, class extends HTMLElement
117
+ {
118
+ constructor()
119
+ {
120
+ super();
121
+ const x = create( 'div' );
122
+ injectData( x, 'payload', this.childNodes, assureSlot );
123
+ injectData( x, 'attributes', this.attributes, e => create( e.nodeName, e.value ) );
124
+ injectData( x, 'dataset', Object.keys( this.dataset ), k => create( k, this.dataset[ k ] ) );
125
+ const sliceRoot = injectData( x, 'slice', sliceNames, k => create( k, '' ) );
126
+ this.xml = x;
127
+ const slices = {};
128
+
129
+
130
+ const sliceEvents=[];
131
+ const applySlices = ()=>
132
+ { const processed = {}
133
+
134
+ for(let ev; ev = sliceEvents.pop(); )
135
+ { const s = attr( ev.target, 'slice');
136
+ if( processed[s] )
137
+ continue;
138
+ injectSlice( sliceRoot, s, ev.detail );
139
+ processed[s] = ev;
140
+ }
141
+ Object.keys(processed).length !== 0 && transform();
142
+ }
143
+ let timeoutID;
144
+
145
+ const onSlice = ev=>
146
+ { ev.stopPropagation?.();
147
+ sliceEvents.push(ev);
148
+ if( !timeoutID )
149
+ timeoutID = setTimeout(()=>
150
+ { applySlices();
151
+ timeoutID =0;
152
+ },10);
153
+ };
154
+ this.onSlice = onSlice;
155
+ const transform = ()=>
156
+ {
157
+ const f = p.transformToFragment( x, document );
158
+ this.innerHTML = '';
159
+ [ ...f.childNodes ].forEach( e => this.appendChild( e ) );
160
+
161
+ for( let el of this.querySelectorAll('[slice]') )
162
+ if( 'function' === typeof el.sliceInit )
163
+ { const s = attr(el,'slice');
164
+ slices[s] = el.sliceInit( slices[s] );
165
+ }
166
+ };
167
+ transform();
168
+ applySlices();
169
+ }
170
+ get dce(){ return dce;}
171
+ } );
172
+ }
173
+ get templateNode(){ return this.firstElementChild?.tagName === 'TEMPLATE'? this.firstElementChild.content : this }
174
+ get dce(){ return this;}
175
+ get xsltString()
176
+ {
177
+ return (
178
+ `<xsl:stylesheet version="1.0"
179
+ xmlns:xsl="${ XSL_NS_URL }">
180
+ <xsl:output method="html" />
181
+
182
+ <xsl:template match="/">
183
+ <xsl:apply-templates select="//attributes"/>
184
+ </xsl:template>
185
+ <xsl:template match="attributes">
186
+ ${ bodyXml( this ) }
187
+ </xsl:template>
188
+
189
+ </xsl:stylesheet>` );
190
+ }
191
+ get xslt(){ return xml2dom( this.xsltString ); }
192
+ }
193
+
194
+ window.customElements.define( 'custom-element', CustomElement );
195
+ export default CustomElement;
package/datasource.md ADDED
@@ -0,0 +1,64 @@
1
+ <h1>DRAFT</h1>
2
+
3
+ # Data Sourse (DS) and transformation pipeline
4
+
5
+ The DS is the data provider for template and DCE. It defines the way to retrieve the data, does the data fetch and
6
+ notifies the template owner on data availability.
7
+ # DS types
8
+ The data samples which are needed for Declarative Web Application would include
9
+ * remote data available over HTTP from URL, request parameters, and HTTP headers
10
+ * embedded into page data island(s) available over `#` anchors
11
+ * variety of storages including localStorage/sesionStorage
12
+ * web application properties like URI, app settings, import maps, etc.
13
+
14
+ # DS Life cycle
15
+ ## 1. Declaration
16
+ resides within template with the parameters populated by expression from template owner data.
17
+ ```html
18
+ <custom-element>
19
+ <local-storage key="{app/url/host}/key1" name="slice1"></local-storage>
20
+ </custom-element>
21
+ ```
22
+
23
+ ## 2. DataRequest Rendering iterations
24
+ DS initiated as DataRequest(**DR**) by [load](https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event)
25
+ event. It starts its lifecycle as async process which emitted to itself as "process" event which bubbles uo to template holder.
26
+
27
+ The process event passes 3 parameters:
28
+ * DS name
29
+ * data
30
+ * state
31
+
32
+ Initially the state is "initiated" with default or no data, then "progress" with partial data, and in the end "completed" with final data.
33
+ Process owner (template renderer) would populate the passed data slices into dataset and mark itself as "dirty" , i.e candidate for re-render upon finalizing the "pre-render" phase.
34
+
35
+ ## 3. final transformation
36
+ The DS can be instant routine which immediately return the final state. Sample of such is app settings.
37
+
38
+ DS with finite steps is any remote call.
39
+
40
+ DS which is never ending samples are localStorage, application URL, clipboard, etc.
41
+
42
+ The transformation owner (DCE, include, etc.) would track the Data Request state change and keep re-rendering on each
43
+ state change. This DR state change notification is the custom event which bubbles up to the transformation owner.
44
+
45
+ # Browser events vs API
46
+ In environments where the data sources custom elements and lifecycle events are not available, the transformation 'owner'
47
+ would be able to track data sources and generate new stream for each data state change. The following sequence of
48
+ data loading and transformations would work even for PDF rendering.
49
+
50
+ 1. transformation process (TP) would start the XSLT processing
51
+ 2. on DS branch it would
52
+ * check the DS state by unique id(XPath?) in the template state.
53
+ * if DS not yet initialized,
54
+ * create the data request(DR) object
55
+ * DR is a process which continue to live in own thread and starts data retrieval.
56
+ * DR, provides its state and data to transformation via data slice
57
+ * DS data slice is set on the TP by the name with initial default value
58
+ * notify the TR to mark the DR `incomplete`
59
+ * finish transformation with blank data
60
+ 3. when DR receives data or state change, it would notify the TR to mark the DR `incomplete`
61
+ 4. TP would re-trigger the transformation on each DR state change.
62
+ 5. the transformation with all DRs in final state(error or completed) is counted as last.
63
+ It is up to implementation to break the current transformation when the any of DR status changes.
64
+ Depend of application, either final or the sequence of rendered transforms could be used.
package/demo/demo.css ADDED
@@ -0,0 +1,19 @@
1
+ html
2
+ { font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;
3
+ font-weight: 400; font-style: normal; -webkit-font-smoothing: antialiased;
4
+ }
5
+ body,nav{ display: flex; flex-wrap: wrap; align-content: stretch; gap: 1rem; }
6
+ body>*{flex: auto;}
7
+ nav{ flex-direction: column;}
8
+ dce-link,dce-1-slot,dce-2-slot,dce-3-slot,dce-4-slot,dce-2-slots,greet-element,pokemon-tile,
9
+ dce-1,dce-2,dce-3,dce-4
10
+ { box-shadow: 0 0 0.5rem lime; padding: 1rem; display: inline-block; flex:1; }
11
+ dd{ padding: 1rem;}
12
+ p{ margin: 0;}
13
+
14
+
15
+ html-demo-element h3
16
+ { text-shadow: 0 0 0.25em white;
17
+ letter-spacing: 0.1rem;
18
+ }
19
+ *[slot="demo"]{ display: flex; gap: 1rem; flex-wrap: wrap; }
@@ -0,0 +1,61 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:html="http://www.w3.org/1999/xhtml">
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
+ <script type="module" src="../http-request.js"></script>
7
+ <script type="module" src="../custom-element.js"></script>
8
+ <style>
9
+ @import "./demo.css";
10
+
11
+ button
12
+ { display: inline-flex; flex-direction: column; align-items: center; flex: auto;
13
+ box-shadow: inset silver 0px 0px 1rem; min-width: 12rem; padding: 1rem;
14
+ color: coral; text-shadow: 1px 1px silver; font-weight: bolder;
15
+ }
16
+ button img{ max-height: 10vw; min-height: 4rem;}
17
+ table{ min-width: 16rem; }
18
+ td{ border-bottom: 1px solid silver; }
19
+ tfoot td{ border-bottom: none; }
20
+ td,th{text-align: right; }
21
+ caption{ padding: 1rem; font-weight: bolder; font-family: sans-serif; }
22
+ dce-1{ padding: 0; display: flex; flex-wrap: wrap;}
23
+ code{ text-align: right; min-width: 3rem;}
24
+ </style>
25
+ </head>
26
+ <body>
27
+
28
+
29
+ <html-demo-element legend="1. http-request simplest"
30
+ description="load the list of pokemons">
31
+ <p>Should display 6 image buttons with pokemon name </p>
32
+ <template>
33
+ <custom-element tag="dce-1" hidden>
34
+ <template><!-- wrapping into template to prevent images loading within DCE declaration -->
35
+ <http-request
36
+ url="https://pokeapi.co/api/v2/pokemon?limit=6&offset=0"
37
+ slice="page"
38
+ ></http-request>
39
+ <xsl:for-each select="//slice/page/data/results/*">
40
+ <xsl:variable name="slides-url"
41
+ >https://unpkg.com/pokeapi-sprites@2.0.2/sprites/pokemon/other/dream-world</xsl:variable>
42
+ <xsl:variable name="pokeid"
43
+ select="substring-before( substring-after( @url, 'https://pokeapi.co/api/v2/pokemon/'),'/')"
44
+ ></xsl:variable>
45
+ <button>
46
+ <img src="{$slides-url}/{$pokeid}.svg"
47
+ alt="{@name}"/>
48
+ <xsl:value-of select='@name'/>
49
+ </button>
50
+ </xsl:for-each>
51
+ </template>
52
+ </custom-element>
53
+ <dce-1></dce-1>
54
+ </template>
55
+ </html-demo-element>
56
+
57
+
58
+ <script type="module" src="https://unpkg.com/html-demo-element@1/html-demo-element.js"></script>
59
+
60
+ </body>
61
+ </html>
@@ -0,0 +1,103 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:html="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
5
+ <title>custom-element Declarative Custom Element implementation demo</title>
6
+ <script type="module" src="../local-storage.js"></script>
7
+ <script type="module" src="../custom-element.js"></script>
8
+ <style>
9
+ @import "./demo.css";
10
+
11
+ button{ background: forestgreen; }
12
+ table{ min-width: 16rem; }
13
+ td{ border-bottom: 1px solid silver; }
14
+ tfoot td{ border-bottom: none; }
15
+ td,th{text-align: right; }
16
+ caption{ padding: 1rem; font-weight: bolder; font-family: sans-serif; }
17
+ </style>
18
+ </head>
19
+ <body>
20
+
21
+
22
+ <html-demo-element legend="1. localStorage simplest"
23
+ description="local-storage read only during initial and only render, does not track the changes.">
24
+ <p>Has to produce 12🍒</p>
25
+ <template>
26
+ <custom-element tag="dce-1" hidden>
27
+ <xsl:value-of select="//slice/fruits/text()"></xsl:value-of>
28
+ <slot>🤔</slot>
29
+ <local-storage key="cherries" slice="fruits"></local-storage>
30
+ </custom-element>
31
+ <dce-1>🍒</dce-1>
32
+ </template>
33
+ </html-demo-element>
34
+
35
+ <html-demo-element legend="2. localStorage basket JSON "
36
+ description="local-storage tracks changes">
37
+ <p>Click the fruits button to add into cart </p>
38
+ <template>
39
+ <custom-element tag="dce-2" hidden>
40
+ <local-storage key="basket" slice="basket" live type="json"></local-storage>
41
+ <html:table>
42
+ <xsl:for-each select="//slice/basket/@*">
43
+ <html:tr>
44
+ <html:th><xsl:value-of select="name()"/></html:th>
45
+ <html:td><xsl:value-of select="."/></html:td>
46
+ </html:tr>
47
+ </xsl:for-each>
48
+ <html:tfoot>
49
+ <html:tr>
50
+ <html:td><slot>🤔</slot></html:td>
51
+ <html:th><xsl:value-of select="sum(//slice/basket/@*)"/></html:th>
52
+ </html:tr>
53
+ </html:tfoot>
54
+ </html:table>
55
+ </custom-element>
56
+ <dce-2>🛒total</dce-2>
57
+ </template>
58
+ </html-demo-element>
59
+
60
+ <fieldset>
61
+ <legend>localStorage content</legend>
62
+ <p>The demo should display count 1🍋 and 12🍒 initially.
63
+ The value in <code>localStorage</code> is incremented
64
+ when clicked on matching button
65
+ </p>
66
+ <button name="lemons" value="1" >🍋</button>
67
+ <button name="cherries" value="12" >🍒</button>
68
+ <button name="apple" >🍏</button>
69
+ <button name="banana" >🍌</button>
70
+ <table>
71
+ <caption> Click to add the localStorage value </caption>
72
+ <thead><tr><th>key</th><th>value</th></tr></thead>
73
+ <tbody id="local-storage-values"></tbody>
74
+ </table>
75
+ </fieldset>
76
+ <script type="module">
77
+ import $ from 'https://unpkg.com/css-chain@1/CssChain.js';
78
+
79
+ const basket = {cherries: 12, lemons:1 };
80
+ localStorage.setItem( 'basket', JSON.stringify(basket) );
81
+
82
+ $('button[name]')
83
+ .forEach( b=> localStorage.setItem( b.name, b.value ) )
84
+ .addEventListener( 'click', e =>
85
+ { const k = e.target.name;
86
+ basket[k] || (basket[k] = 1);
87
+ localStorage.setItem( k, basket[k] = 1+1*localStorage[k] )
88
+ localStorage.setItem( 'basket', JSON.stringify(basket) );
89
+ } );
90
+
91
+ const renderStorage = () =>
92
+ window[ 'local-storage-values' ].innerHTML = [...Array(localStorage.length).keys()]
93
+ .map( k => `<tr><th>${ localStorage.key(k) }</th><td>${ localStorage.getItem( localStorage.key(k) ) }</td>` ).join( '\n' );
94
+
95
+ window.addEventListener( 'storage', renderStorage );
96
+ window.addEventListener( 'local-storage', renderStorage );
97
+ renderStorage();
98
+ </script>
99
+
100
+ <script type="module" src="https://unpkg.com/html-demo-element@1/html-demo-element.js"></script>
101
+
102
+ </body>
103
+ </html>
@@ -0,0 +1,44 @@
1
+ const attr = (el, attr)=> el.getAttribute(attr);
2
+
3
+ export class HttpRequestElement extends HTMLElement
4
+ {
5
+ // @attribute url
6
+ constructor() {
7
+ super();
8
+ }
9
+ sliceInit( s )
10
+ { if( !s )
11
+ s = {};
12
+ s.element = this;
13
+ if( s.destroy )
14
+ return s;
15
+ const controller = new AbortController();
16
+ s.destroy = ()=>
17
+ { // todo destroy slices in custom-element
18
+ controller.abort();
19
+ };
20
+ const url = attr(this, 'url') || ''
21
+ , request = { url }
22
+ , slice = { detail: { request }, target: this }
23
+ , updateSlice = slice =>
24
+ { for( let parent = s.element.parentElement; parent; parent = parent.parentElement )
25
+ if ( parent.onSlice )
26
+ return parent.onSlice(slice);
27
+ console.error(`${this.localName} used outside of custom-element`)
28
+ debugger;
29
+ };
30
+
31
+ setTimeout( async ()=>
32
+ { updateSlice( slice );
33
+ slice.detail.response = await fetch(url,{ signal: controller.signal });
34
+ updateSlice( slice );
35
+ slice.detail.data = await slice.detail.response.json();
36
+ updateSlice( slice );
37
+ },0 );
38
+
39
+ return s;
40
+ }
41
+ }
42
+
43
+ window.customElements.define( 'http-request', HttpRequestElement );
44
+ export default HttpRequestElement;
package/index.html CHANGED
@@ -1,150 +1,152 @@
1
- <!DOCTYPE html>
2
- <html lang="en" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3
- <head>
4
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
5
- <title>custom-element Declarative Custom Element implementation demo</title>
6
- <script type="module" src="custom-element.js"></script>
7
- <style>
8
- body,nav{ display: flex; flex-wrap: wrap; align-content: stretch; gap: 1rem; }
9
- nav{ flex-direction: column;}
10
- dce-link,dce-1-slot,dce-2-slot,dce-3-slot,dce-4-slot,dce-2-slots,greet-element,pokemon-tile
11
- { box-shadow: 0 0 0.5rem lime; padding: 1rem; display: inline-block;}
12
- dd{ padding: 1rem;}
13
- p{ margin: 0;}
14
- </style>
15
- </head>
16
- <body>
17
- <nav>
18
- <h3><code>custom-element</code> demo</h3>
19
- <div><a href="https://github.com/EPA-WG/custom-element"
20
- ><img src="https://cdnjs.cloudflare.com/ajax/libs/octicons/8.5.0/svg/mark-github.svg" alt="icon">GIT</a>
21
- | <a href="https://stackblitz.com/github/EPA-WG/custom-element?file=index.html">Sandbox</a>
22
- </div>
23
- <p>
24
- This <em>Declarative Custom Element</em> allows to define<br/>
25
- custom HTML tag with template filled from slots, attributes, dataset. </p>
26
- <p>The template is fully loaded with variables, conditions, loops, etc. <br/>
27
- The data query is powered by XPath. </p>
28
- <p>Try in <a href="https://stackblitz.com/github/EPA-WG/custom-element?file=index.html" >Sandbox</a> </p>
29
- </nav>
30
- <html-demo-element legend="1. simple payload"
31
- description="payload is ignored as in DCE definition there is no default slot">
32
- <template>
33
- <custom-element tag="dce-link" hidden>
34
- <a href="#">link 😃</a>
35
- </custom-element>
36
- <dce-link><i>🍋</i></dce-link>
37
- </template>
38
- </html-demo-element>
39
-
40
- <html-demo-element legend="2. payload with slot definition and slot value"
41
- description="slots are filled as in template+shadow root">
42
- <template>
43
- <custom-element tag="dce-1-slot" hidden>
44
- <slot name="slot1"> 😃</slot>
45
- </custom-element>
46
- <dce-1-slot><i slot="slot1">🥕</i></dce-1-slot>
47
- </template>
48
- </html-demo-element>
49
-
50
- <html-demo-element legend="2a. payload with slot definition and slot value"
51
- description="same slot can be used multiple times unlike in TEMPLATE">
52
- <template>
53
- <custom-element tag="dce-2-slots" hidden>
54
- <slot name="slot2"> 😃</slot> and again:
55
- <slot name="slot2"> 😃</slot>
56
- </custom-element>
57
- <dce-2-slots><i slot="slot2">🥕</i></dce-2-slots>
58
- </template>
59
- </html-demo-element>
60
-
61
- <html-demo-element legend="2b. named default slot"
62
- description="slot without `name` attribute or with blank value `name=''` use whole payload">
63
- <template>
64
- <custom-element tag="dce-3-slot" hidden>
65
- #1
66
- <slot name=""> 😃</slot>
67
- and
68
- <slot> 😃</slot>
69
- </custom-element>
70
- <dce-3-slot><i slot="">🥕</i></dce-3-slot>
71
- </template>
72
- </html-demo-element>
73
-
74
- <html-demo-element legend="2c. named default slot"
75
- description="slot without `name` attribute or with blank value `name=''` use whole payload">
76
- <template>
77
- <custom-element tag="dce-4-slot" hidden>
78
- #2
79
- <slot name=""> 😃</slot>
80
- and
81
- <slot> 😃</slot>
82
- </custom-element>
83
- <dce-4-slot>🥕</dce-4-slot>
84
- </template>
85
- </html-demo-element>
86
- <html-demo-element legend="2d. default slot"
87
- description="slot without `name` attribute use whole payload">
88
- <template>
89
-
90
- <custom-element tag="greet-element" hidden>
91
- <slot> Hello </slot> World!
92
- </custom-element>
93
- <greet-element>👋</greet-element>
94
- </template>
95
- </html-demo-element>
96
-
97
- <html-demo-element legend="3. 💪 DCE template "
98
- description="Complex case with slots, attributes, dataset, conditional render">
99
- <template>
100
-
101
- <custom-element tag="pokemon-tile" hidden>
102
- <h3><xsl:value-of select="title"/></h3> <!-- title is an attribute in instance
103
- mapped into /*/attributes/title -->
104
- <xsl:if test="//smile"> <!-- data-smile DCE instance attribute,
105
- mapped into /*/dataset/smile
106
- used in condition -->
107
- <!-- data-smile DCE instance attribute, used as HTML -->
108
- <div>Smile as: <xsl:value-of select='//smile'/></div>
109
- </xsl:if>
110
- <!-- image would not be visible in sandbox, see live demo -->
111
- <img src="https://unpkg.com/pokeapi-sprites@2.0.2/sprites/pokemon/other/dream-world/{pokemon-id}.svg"
112
- alt="{title} image"/>
113
- <!-- image-src and title are DCE instance attributes,
114
- mapped into /*/attributes/
115
- used within output attribute via curly brackets -->
116
-
117
- <!-- `slot name=xxx` replaced with elements with `slot=xxx` attribute -->
118
- <p><slot name="description"><i>description is not available</i></slot></p>
119
- <xsl:for-each select="//*[@pokemon-id]">
120
- <!-- loop over payload elements with `pokemon-id` attribute -->
121
- <button>
122
- <img height="32"
123
- src="https://unpkg.com/pokeapi-sprites@2.0.2/sprites/pokemon/other/dream-world/{@pokemon-id}.svg"
124
- alt="{text()}"/>
125
- <br/>
126
- <xsl:value-of select='text()'/>
127
- </button>
128
-
129
- </xsl:for-each>
130
- </custom-element>
131
-
132
- <pokemon-tile title="bulbasaur" data-smile="👼" pokemon-id="1" >
133
- <p slot="description">Bulbasaur is a cute Pokémon born with a large seed firmly affixed to its back;
134
- the seed grows in size as the Pokémon does.</p>
135
- <ul>
136
- <li pokemon-id="2">ivysaur</li>
137
- <li pokemon-id="3">venusaur</li>
138
- </ul>
139
- </pokemon-tile>
140
-
141
- <pokemon-tile title="ninetales" pokemon-id="38" >
142
- <li pokemon-id="37">vulpix</li>
143
- </pokemon-tile>
144
- </template>
145
- </html-demo-element>
146
-
147
- <script type="module" src="https://unpkg.com/html-demo-element@1.0/html-demo-element.js"></script>
148
-
149
- </body>
150
- </html>
1
+ <!DOCTYPE html>
2
+ <html lang="en" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
5
+ <title>custom-element Declarative Custom Element implementation demo</title>
6
+ <script type="module" src="custom-element.js"></script>
7
+ <style>
8
+ @import "demo/demo.css";
9
+ </style>
10
+ </head>
11
+ <body>
12
+ <nav>
13
+ <h3><code>custom-element</code> demo</h3>
14
+ <div><a href="https://github.com/EPA-WG/custom-element"
15
+ ><img src="https://cdnjs.cloudflare.com/ajax/libs/octicons/8.5.0/svg/mark-github.svg" alt="icon">GIT</a>
16
+ | <a href="https://stackblitz.com/github/EPA-WG/custom-element?file=index.html">Sandbox</a>
17
+ | <a href="https://chrome.google.com/webstore/detail/epa-wgcustom-element/hiofgpmmkdembdogjpagmbbbmefefhbl"
18
+ >Chrome devtools plugin</a>
19
+ </div>
20
+ <p>
21
+ This <em>Declarative Custom Element</em> allows to define<br/>
22
+ custom HTML tag with template filled from slots, attributes, dataset. </p>
23
+ <p>The template is fully loaded with variables, conditions, loops, etc. <br/>
24
+ The data query is powered by XPath. </p>
25
+ <p>Try in <a href="https://stackblitz.com/github/EPA-WG/custom-element?file=index.html" >Sandbox</a> </p>
26
+ <section>
27
+ <b>Data layer demo</b>
28
+ <a href="./demo/local-storage.html">local-storage</a> |
29
+ <a href="./demo/http-request.html">http-request</a>
30
+ </section>
31
+ </nav>
32
+ <html-demo-element legend="1. simple payload"
33
+ description="payload is ignored as in DCE definition there is no default slot">
34
+ <template>
35
+ <custom-element tag="dce-link" hidden>
36
+ <a href="#">link 😃</a>
37
+ </custom-element>
38
+ <dce-link><i>🍋</i></dce-link>
39
+ </template>
40
+ </html-demo-element>
41
+
42
+ <html-demo-element legend="2. payload with slot definition and slot value"
43
+ description="slots are filled as in template+shadow root">
44
+ <template>
45
+ <custom-element tag="dce-1-slot" hidden>
46
+ <slot name="slot1"> 😃</slot>
47
+ </custom-element>
48
+ <dce-1-slot><i slot="slot1">🥕</i></dce-1-slot>
49
+ </template>
50
+ </html-demo-element>
51
+
52
+ <html-demo-element legend="2a. payload with slot definition and slot value"
53
+ description="same slot can be used multiple times unlike in TEMPLATE">
54
+ <template>
55
+ <custom-element tag="dce-2-slots" hidden>
56
+ <slot name="slot2"> 😃</slot> and again:
57
+ <slot name="slot2"> 😃</slot>
58
+ </custom-element>
59
+ <dce-2-slots><i slot="slot2">🥕</i></dce-2-slots>
60
+ </template>
61
+ </html-demo-element>
62
+
63
+ <html-demo-element legend="2b. named default slot"
64
+ description="slot without `name` attribute or with blank value `name=''` use whole payload">
65
+ <template>
66
+ <custom-element tag="dce-3-slot" hidden>
67
+ #1
68
+ <slot name=""> 😃</slot>
69
+ and
70
+ <slot> 😃</slot>
71
+ </custom-element>
72
+ <dce-3-slot><i slot="">🥕</i></dce-3-slot>
73
+ </template>
74
+ </html-demo-element>
75
+
76
+ <html-demo-element legend="2c. named default slot"
77
+ description="slot without `name` attribute or with blank value `name=''` use whole payload">
78
+ <template>
79
+ <custom-element tag="dce-4-slot" hidden>
80
+ #2
81
+ <slot name=""> 😃</slot>
82
+ and
83
+ <slot> 😃</slot>
84
+ </custom-element>
85
+ <dce-4-slot>🥕</dce-4-slot>
86
+ </template>
87
+ </html-demo-element>
88
+ <html-demo-element legend="2d. default slot"
89
+ description="slot without `name` attribute use whole payload">
90
+ <template>
91
+
92
+ <custom-element tag="greet-element" hidden>
93
+ <slot> Hello </slot> World!
94
+ </custom-element>
95
+ <greet-element>👋</greet-element>
96
+ </template>
97
+ </html-demo-element>
98
+
99
+ <html-demo-element legend="3. 💪 DCE template "
100
+ description="Complex case with slots, attributes, dataset, conditional render">
101
+ <template>
102
+
103
+ <custom-element tag="pokemon-tile" hidden>
104
+ <h3><xsl:value-of select="title"/></h3> <!-- title is an attribute in instance
105
+ mapped into /*/attributes/title -->
106
+ <xsl:if test="//smile"> <!-- data-smile DCE instance attribute,
107
+ mapped into /*/dataset/smile
108
+ used in condition -->
109
+ <!-- data-smile DCE instance attribute, used as HTML -->
110
+ <div>Smile as: <xsl:value-of select='//smile'/></div>
111
+ </xsl:if>
112
+ <!-- image would not be visible in sandbox, see live demo -->
113
+ <img src="https://unpkg.com/pokeapi-sprites@2.0.2/sprites/pokemon/other/dream-world/{pokemon-id}.svg"
114
+ alt="{title} image"/>
115
+ <!-- image-src and title are DCE instance attributes,
116
+ mapped into /*/attributes/
117
+ used within output attribute via curly brackets -->
118
+
119
+ <!-- `slot name=xxx` replaced with elements with `slot=xxx` attribute -->
120
+ <p><slot name="description"><i>description is not available</i></slot></p>
121
+ <xsl:for-each select="//*[@pokemon-id]">
122
+ <!-- loop over payload elements with `pokemon-id` attribute -->
123
+ <button>
124
+ <img height="32"
125
+ src="https://unpkg.com/pokeapi-sprites@2.0.2/sprites/pokemon/other/dream-world/{@pokemon-id}.svg"
126
+ alt="{text()}"/>
127
+ <br/>
128
+ <xsl:value-of select='text()'/>
129
+ </button>
130
+
131
+ </xsl:for-each>
132
+ </custom-element>
133
+
134
+ <pokemon-tile title="bulbasaur" data-smile="👼" pokemon-id="1" >
135
+ <p slot="description">Bulbasaur is a cute Pokémon born with a large seed firmly affixed to its back;
136
+ the seed grows in size as the Pokémon does.</p>
137
+ <ul>
138
+ <li pokemon-id="2">ivysaur</li>
139
+ <li pokemon-id="3">venusaur</li>
140
+ </ul>
141
+ </pokemon-tile>
142
+
143
+ <pokemon-tile title="ninetales" pokemon-id="38" >
144
+ <li pokemon-id="37">vulpix</li>
145
+ </pokemon-tile>
146
+ </template>
147
+ </html-demo-element>
148
+
149
+ <script type="module" src="https://unpkg.com/html-demo-element@1.0/html-demo-element.js"></script>
150
+
151
+ </body>
152
+ </html>
@@ -0,0 +1,66 @@
1
+ const attr = (el, attr)=> el.getAttribute(attr)
2
+ , string2value = (type, v) =>
3
+ { if( type === 'text')
4
+ return v;
5
+ if( type === 'json')
6
+ return JSON.parse( v );
7
+ const el = document.createElement('input');
8
+ el.setAttribute('type',type);
9
+ el.setAttribute('value', v );
10
+ return type==='number'? el.valueAsNumber : 'date|time|dateTimeLocal'.includes(type)? el.valueAsDate: el.value;
11
+ };
12
+
13
+ let originalSetItem;
14
+
15
+ function ensureTrackLocalStorage()
16
+ { if( originalSetItem )
17
+ return;
18
+ originalSetItem = localStorage.setItem;
19
+ localStorage.setItem = function( key, value, ...rest )
20
+ { originalSetItem.apply(this, [ key, value, ...rest ]);
21
+ window.dispatchEvent( new CustomEvent('local-storage',{detail:{key,value}}) );
22
+ };
23
+ }
24
+
25
+ export class LocalStorageElement extends HTMLElement
26
+ {
27
+ // @attribute live - monitors localStorage change
28
+ // @attribute type - `text|json`, defaults to text, other types are compatible with INPUT field
29
+ constructor()
30
+ {
31
+ super();
32
+ const state = {}
33
+ , type = attr(this, 'type') || 'text'
34
+ , listener = e=> e.detail.key === attr( this,'key' ) && propagateSlice()
35
+ , propagateSlice = ()=>
36
+ { for( let parent = this.parentElement; parent; parent = parent.parentElement)
37
+ if( parent.onSlice )
38
+ return parent.onSlice(
39
+ { detail: string2value( type, localStorage.getItem( attr( this, 'key' ) ) )
40
+ , target: this
41
+ } );
42
+ console.error(`${this.localName} used outside of custom-element`)
43
+ debugger;
44
+ };
45
+ this.sliceInit = s =>
46
+ { if( !state.listener && this.hasAttribute('live') )
47
+ { state.listener = 1;
48
+ window.addEventListener( 'local-storage', listener );
49
+ ensureTrackLocalStorage();
50
+ }
51
+ propagateSlice();
52
+ return s || {}
53
+ }
54
+ this._destroy = ()=>
55
+ {
56
+ if( !state.listener )
57
+ return;
58
+ state.listener && window.removeEventListener('local-storage', listener );
59
+ delete state.listener;
60
+ };
61
+ }
62
+ disconnectedCallback(){ this._destroy(); }
63
+ }
64
+
65
+ window.customElements.define( 'local-storage', LocalStorageElement );
66
+ export default LocalStorageElement;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epa-wg/custom-element",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
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",
@@ -15,6 +15,7 @@
15
15
  "type": "module",
16
16
  "types": "./custom-element.d.ts",
17
17
  "scripts": {
18
+ "dev:help": "echo \"needed for sandbox demo\"",
18
19
  "dev": "bash bin/stackblitz.sh",
19
20
  "start": "npm i --no-save @web/dev-server && web-dev-server --node-resolve",
20
21
  "test": "echo \"test would reside in https://github.com/EPA-WG/custom-element-test\" && exit 0",
package/request.html ADDED
@@ -0,0 +1,53 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
5
+ <title>custom-element Declarative Custom Element implementation demo</title>
6
+ <script type="module" src="custom-element.js"></script>
7
+ <style>
8
+ body,nav{ display: flex; flex-wrap: wrap; align-content: stretch; gap: 1rem; }
9
+ nav{ flex-direction: column;}
10
+ dce-link,dce-1-slot,dce-2-slot,dce-3-slot,dce-4-slot,dce-2-slots,greet-element,pokemon-tile
11
+ { box-shadow: 0 0 0.5rem lime; padding: 1rem; display: inline-block;}
12
+ dd{ padding: 1rem;}
13
+ p{ margin: 0;}
14
+ </style>
15
+ </head>
16
+ <body>
17
+
18
+ <html-demo-element legend="1. simple payload"
19
+ description="payload is ignored as in DCE definition there is no default slot">
20
+ <template>
21
+ <custom-element tag="poke-image-request" hidden>
22
+ <img src="https://unpkg.com/pokeapi-sprites@2.0.2/sprites/pokemon/other/dream-world/{//poke-id}.svg"/>
23
+ </custom-element>
24
+ <poke-image-request poke-id="1"></poke-image-request>
25
+
26
+ <custom-element tag="poke-list-request" hidden>
27
+ <a href="https://pokeapi.co/api/v2/pokemon?offset={//offset}&limit=10">list json</a>
28
+ </custom-element>
29
+
30
+ <custom-element tag="poke-list-page" hidden>
31
+ <a href="https://pokeapi.co/api/v2/pokemon?offset={//offset}&limit=10">list json</a>
32
+ <http-request-json
33
+ target="poke-list"
34
+ src="https://pokeapi.co/api/v2/pokemon?offset={//offset}&limit=10"
35
+ ></http-request-json>
36
+ <ol>
37
+ <xsl:for-each select="">
38
+
39
+ </xsl:for-each>
40
+ </ol>
41
+ </custom-element>
42
+ <poke-list-page>
43
+ <poke-list-request offset="0"></poke-list-request>
44
+ </poke-list-page>
45
+
46
+ </template>
47
+ </html-demo-element>
48
+
49
+
50
+ <script type="module" src="https://unpkg.com/html-demo-element@1.0/html-demo-element.js"></script>
51
+
52
+ </body>
53
+ </html>
@@ -1,28 +0,0 @@
1
- <component name="InspectionProjectProfileManager">
2
- <profile version="1.0">
3
- <option name="myName" value="Project Default" />
4
- <inspection_tool class="HtmlUnknownTag" enabled="true" level="WARNING" enabled_by_default="true">
5
- <option name="myValues">
6
- <value>
7
- <list size="14">
8
- <item index="0" class="java.lang.String" itemvalue="nobr" />
9
- <item index="1" class="java.lang.String" itemvalue="noembed" />
10
- <item index="2" class="java.lang.String" itemvalue="comment" />
11
- <item index="3" class="java.lang.String" itemvalue="noscript" />
12
- <item index="4" class="java.lang.String" itemvalue="embed" />
13
- <item index="5" class="java.lang.String" itemvalue="script" />
14
- <item index="6" class="java.lang.String" itemvalue="dce-link" />
15
- <item index="7" class="java.lang.String" itemvalue="dce-1-slot" />
16
- <item index="8" class="java.lang.String" itemvalue="dce-2-slots" />
17
- <item index="9" class="java.lang.String" itemvalue="greet-element" />
18
- <item index="10" class="java.lang.String" itemvalue="pokemon-tile" />
19
- <item index="11" class="java.lang.String" itemvalue="html-demo-element" />
20
- <item index="12" class="java.lang.String" itemvalue="custom-element" />
21
- <item index="13" class="java.lang.String" itemvalue="slot" />
22
- </list>
23
- </value>
24
- </option>
25
- <option name="myCustomValuesEnabled" value="true" />
26
- </inspection_tool>
27
- </profile>
28
- </component>
package/.idea/php.xml DELETED
@@ -1,12 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="MessDetectorOptionsConfiguration">
4
- <option name="transferred" value="true" />
5
- </component>
6
- <component name="PHPCSFixerOptionsConfiguration">
7
- <option name="transferred" value="true" />
8
- </component>
9
- <component name="PHPCodeSnifferOptionsConfiguration">
10
- <option name="transferred" value="true" />
11
- </component>
12
- </project>
package/0/1.xml DELETED
@@ -1,23 +0,0 @@
1
- <div>
2
- <payload>
3
- <span slot="">
4
- <br>
5
- <br>
6
- </span>
7
- <p slot="description">Bulbasaur is a cute Pokémon born with a large seed firmly affixed to its back;\n the seed
8
- grows in size as the Pokémon does.
9
- </p>
10
- <span slot="">
11
- <br>
12
- <br>
13
- </span>
14
- </payload>
15
- <attributes>
16
- <title>bulbasaur</title>
17
- <data-smile>👼</data-smile>
18
- <image-src>https://unpkg.com/pokeapi-sprites@2.0.2/sprites/pokemon/other/dream-world/1.svg</image-src>
19
- </attributes>
20
- <dataset>
21
- <smile>👼</smile>
22
- </dataset>
23
- </div>
package/0/1.xsl DELETED
@@ -1,26 +0,0 @@
1
- <xsl:stylesheet version="1.0"
2
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3
- <xsl:output method="html" />
4
-
5
- <xsl:template match="/">
6
- <xsl:apply-templates select="//attributes"/>
7
- </xsl:template>
8
- <xsl:template match="attributes">
9
-
10
- <h3><xsl:value-of select="title"></xsl:value-of></h3> <!-- title is an attribute in instance
11
- mapped into /*/attributes/title -->
12
- <xsl:if test="//smile"> <!-- data-smile DCE instance attribute,
13
- mapped into /*/dataset/smile
14
- used in condition -->
15
- <!-- data-smile DCE instance attribute, used as HTML -->
16
- <div>Smile as: <xsl:value-of select="//smile"></xsl:value-of></div>
17
- </xsl:if>
18
- <img src="{image-src}" alt="{title}" /> <!-- image-src and title are DCE instance attributes,
19
- mapped into /*/attributes/
20
- used within output attribute via curly brackets -->
21
- <!-- `slot name=xxx` replaced with elements with `slot=xxx` attribute -->
22
- <p><value-of xmlns="http://www.w3.org/1999/XSL/Transform" select="//*[@slot=&quot;description&quot;]"/></p>
23
-
24
- </xsl:template>
25
-
26
- </xsl:stylesheet>
package/0/a.html DELETED
@@ -1,42 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3
- <head>
4
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
5
- <title>custom-element Declarative Custom Element implementation demo</title>
6
- <script type="module" src="../custom-element.js"></script>
7
- <style>
8
- body{ display: flex; flex-wrap: wrap; align-content: stretch; gap: 1rem; }
9
- dce-link,dce-1-slot,dce-2-slots,greet-element,pokemon-tile
10
- { box-shadow: 0 0 0.5rem lime; padding: 1rem; display: inline-block;}
11
- </style>
12
- </head>
13
- <body>
14
- <custom-element tag="pokemon-tile" hidden>
15
- <h3><xsl:value-of select="title"/></h3> <!-- title is an attribute in instance
16
- mapped into /*/attributes/title -->
17
- <xsl:if test="//smile"> <!-- data-smile DCE instance attribute,
18
- mapped into /*/dataset/smile
19
- used in condition -->
20
- <!-- data-smile DCE instance attribute, used as HTML -->
21
- <div>Smile as: <xsl:value-of select='//smile'/></div>
22
- </xsl:if>
23
- <img src="{image-src}" alt="{title}"/> <!-- image-src and title are DCE instance attributes,
24
- mapped into /*/attributes/
25
- used within output attribute via curly brackets -->
26
- <!-- `slot name=xxx` replaced with elements with `slot=xxx` attribute -->
27
- <p><slot name="description"><i>description is not available</i></slot></p>
28
- </custom-element>
29
- <pokemon-tile title="bulbasaur"
30
- data-smile="👼"
31
- image-src="https://unpkg.com/pokeapi-sprites@2.0.2/sprites/pokemon/other/dream-world/1.svg">
32
-
33
- <p slot="description">Bulbasaur is a cute Pokémon born with a large seed firmly affixed to its back;
34
- the seed grows in size as the Pokémon does.</p>
35
-
36
- </pokemon-tile>
37
-
38
- <pokemon-tile title="ninetales"
39
- image-src="https://unpkg.com/pokeapi-sprites@2.0.2/sprites/pokemon/other/dream-world/38.svg">
40
- </pokemon-tile>
41
- </body>
42
- </html>