@epa-wg/custom-element 0.0.10 → 0.0.12

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/custom-element.js CHANGED
@@ -1,46 +1,37 @@
1
- const XML_DECLARATION = '<?xml version="1.0" encoding="UTF-8"?>'
2
- , XSL_NS_URL = 'http://www.w3.org/1999/XSL/Transform';
1
+ const XSL_NS_URL = 'http://www.w3.org/1999/XSL/Transform'
2
+ , HTML_NS_URL = 'http://www.w3.org/1999/xhtml'
3
+ , EXSL_NS_URL = 'http://exslt.org/common'
4
+ , DCE_NS_URL ="urn:schemas-epa-wg:dce";
3
5
 
4
6
  // const log = x => console.debug( new XMLSerializer().serializeToString( x ) );
5
7
 
6
- const attr = (el, attr)=> el.getAttribute(attr)
7
- , create = ( tag, t = '' ) => ( e => ((e.innerText = t||''),e) )(document.createElement( tag ));
8
+ const attr = (el, attr)=> el.getAttribute?.(attr)
9
+ , isText = e => e.nodeType === 3
10
+ , create = ( tag, t = '' ) => ( e => ((e.innerText = t||''),e) )(document.createElement( tag ))
11
+ , createText = ( d, t) => (d.ownerDocument || d ).createTextNode( t )
12
+ , createNS = ( ns, tag, t = '' ) => ( e => ((e.innerText = t||''),e) )(document.createElementNS( ns, tag ))
13
+ , xslNs = x => ( x?.setAttribute('xmlns:xsl', XSL_NS_URL ), x )
14
+ , xslHtmlNs = x => ( x?.setAttribute('xmlns:xhtml', HTML_NS_URL ), xslNs(x) );
8
15
 
9
16
  function
10
- xml2dom( xmlString )
17
+ ASSERT(x)
11
18
  {
12
- return new DOMParser().parseFromString( XML_DECLARATION + xmlString, "application/xml" )
19
+ // if(!x)
20
+ // debugger
13
21
  }
14
-
15
22
  function
16
- bodyXml( dce )
23
+ xml2dom( xmlString )
17
24
  {
18
- const t = dce.firstElementChild
19
- , sanitize = s => s.replaceAll("<html:","<")
20
- .replaceAll("</html:","</")
21
- .replaceAll( />\s*<\/xsl:value-of>/g ,"/>")
22
- .replaceAll( />\s*<\/(br|hr|img|area|base|col|embed|input|link|meta|param|source|track|wbr)>/g ,"/>");
23
- if( t?.tagName === 'TEMPLATE')
24
- return sanitize( new XMLSerializer().serializeToString( t.content ) );
25
-
26
- const s = new XMLSerializer().serializeToString( dce );
27
- return sanitize( s.substring( s.indexOf( '>' ) + 1, s.lastIndexOf( '<' ) ) );
25
+ return new DOMParser().parseFromString( xmlString, "application/xml" )
28
26
  }
29
-
30
27
  function
31
- slot2xsl( s )
32
- {
33
- const v = document.createElementNS( XSL_NS_URL, 'value-of' );
34
- v.setAttribute( 'select', `//*[@slot="${ s.name }"]` );
35
- s.parentNode.replaceChild( v, s );
36
- }
28
+ xmlString(doc){ return new XMLSerializer().serializeToString( doc ) }
37
29
 
38
30
  function
39
31
  injectData( root, sectionName, arr, cb )
40
- {
32
+ { const create = ( tag ) => root.ownerDocument.createElement( tag );
41
33
  const inject = ( tag, parent, s ) =>
42
- {
43
- parent.append( s = create( tag ) );
34
+ { parent.append( s = create( tag ) );
44
35
  return s;
45
36
  };
46
37
  const l = inject( sectionName, root );
@@ -90,45 +81,300 @@ Json2Xml( o, tag )
90
81
  ret.push("/>");
91
82
  return ret.join('\n');
92
83
  }
84
+ export function
85
+ tagUid( node )
86
+ { // {} to xsl:value-of
87
+ forEach$(node,'*',d => [...d.childNodes].filter( e=>e.nodeType === 3 ).forEach( e=>
88
+ { if( e.parentNode.localName === 'style' )
89
+ return;
90
+ const m = e.data.matchAll( /{([^}]*)}/g );
91
+ if(m)
92
+ { let l = 0
93
+ , txt = t => createText(e,t||'')
94
+ , tt = [];
95
+ [...m].forEach(t=>
96
+ { if( t.index > l )
97
+ tt.push( txt( t.input.substring( l, t.index ) ))
98
+ const v = e.ownerDocument.createElement('xsl:value-of');
99
+ v.setAttribute('select', t[1] );
100
+ tt.push(v);
101
+ l = t.index+t[0].length;
102
+ })
103
+ if( l < e.data.length)
104
+ tt.push( txt( e.data.substring(l,e.data.length) ));
105
+ if( tt.length )
106
+ { for( let t of tt )
107
+ d.insertBefore(t,e);
108
+ d.removeChild(e);
109
+ }
110
+ }
111
+ }));
93
112
 
94
- function
113
+ if( 'all' in node ) {
114
+ let i= 1;
115
+ for( let e of node.all )
116
+ e.setAttribute && !e.tagName.startsWith('xsl:') && e.setAttribute('data-dce-id', '' + i++)
117
+ }
118
+ return node
119
+ }
120
+ export function
121
+ createXsltFromDom( templateNode, S = 'xsl:stylesheet' )
122
+ {
123
+ if( templateNode.tagName === S || templateNode.documentElement?.tagName === S )
124
+ return tagUid(templateNode)
125
+ 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" >
126
+ <xsl:output method="xml" />
127
+ <xsl:template match="/"><dce-root><xsl:apply-templates select="*"/></dce-root></xsl:template>
128
+ <xsl:template match="*[name()='template']"><xsl:apply-templates mode="sanitize" select="*|text()"/></xsl:template>
129
+ <xsl:template match="*"><xsl:apply-templates mode="sanitize" select="*|text()"/></xsl:template>
130
+ <xsl:template match="*[name()='svg']|*[name()='math']"><xsl:apply-templates mode="sanitize" select="."/></xsl:template>
131
+ <xsl:template mode="sanitize" match="*[count(text())=1 and count(*)=0]"><xsl:copy><xsl:apply-templates mode="sanitize" select="@*"/><xsl:value-of select="text()"/></xsl:copy></xsl:template>
132
+ <xsl:template mode="sanitize" match="xhtml:*[count(text())=1 and count(*)=0]"><xsl:element name="{local-name()}"><xsl:apply-templates mode="sanitize" select="@*"/><xsl:value-of select="text()"/></xsl:element></xsl:template>
133
+ <xsl:template mode="sanitize" match="*|@*"><xsl:copy><xsl:apply-templates mode="sanitize" select="*|@*|text()"/></xsl:copy></xsl:template>
134
+ <xsl:template mode="sanitize" match="text()[normalize-space(.) = '']"/>
135
+ <xsl:template mode="sanitize" match="text()"><dce-text><xsl:copy/></dce-text></xsl:template>
136
+ <xsl:template mode="sanitize" match="xsl:value-of|*[name()='slot']"><dce-text><xsl:copy><xsl:apply-templates mode="sanitize" select="*|@*|text()"/></xsl:copy></dce-text></xsl:template>
137
+ <xsl:template mode="sanitize" match="xhtml:*"><xsl:element name="{local-name()}"><xsl:apply-templates mode="sanitize" select="*|@*|text()"/></xsl:element></xsl:template>
138
+ </xsl:stylesheet>`)
139
+ const sanitizeProcessor = new XSLTProcessor()
140
+ , tc = (n =>
141
+ {
142
+ forEach$(n,'script', s=> s.remove() );
143
+ const e = n.firstElementChild?.content || n.content
144
+ , asXmlNode = r => xslHtmlNs(xml2dom( '<xhtml/>' ).importNode(r, true));
145
+ if( e )
146
+ { const t = create('div');
147
+ [ ...e.childNodes ].map( c => t.append(c.cloneNode(true)) )
148
+ return asXmlNode(t)
149
+ }
150
+ return asXmlNode(n.documentElement || n.body || n)
151
+ })(templateNode)
152
+ , xslDom = xml2dom(
153
+ `<xsl:stylesheet version="1.0"
154
+ xmlns:xsl="${ XSL_NS_URL }"
155
+ xmlns:dce="urn:schemas-epa-wg:dce"
156
+ xmlns:exsl="http://exslt.org/common"
157
+ exclude-result-prefixes="exsl"
158
+ >
159
+ <xsl:template mode="payload" match="attributes"></xsl:template>
160
+ <xsl:template match="/">
161
+ <xsl:apply-templates mode="payload" select="/datadom/attributes"/>
162
+ </xsl:template>
163
+ <xsl:template name="slot" >
164
+ <xsl:param name="slotname" />
165
+ <xsl:param name="defaultvalue" />
166
+ <xsl:choose>
167
+ <xsl:when test="//payload/*[@slot=$slotname]">
168
+ <xsl:copy-of select="//payload/*[@slot=$slotname]"/>
169
+ </xsl:when>
170
+ <xsl:otherwise>
171
+ <xsl:copy-of select="$defaultvalue"/>
172
+ </xsl:otherwise>
173
+ </xsl:choose>
174
+ </xsl:template>
175
+ <xsl:variable name="js-injected-body">
176
+ <xsl:call-template name="slot" >
177
+ <xsl:with-param name="slotname" select="''"/>
178
+ <xsl:with-param name="defaultvalue"/>
179
+ </xsl:call-template>
180
+ </xsl:variable>
181
+ </xsl:stylesheet>`
182
+ );
183
+
184
+ sanitizeProcessor.importStylesheet( sanitizeXsl );
185
+
186
+ const fr = sanitizeProcessor.transformToFragment(tc, document)
187
+ , $ = (e,css) => e.querySelector(css)
188
+ , payload = $( xslDom, 'template[mode="payload"]');
189
+ if( !fr )
190
+ return console.error("transformation error",{ xml:tc.outerHTML, xsl: xmlString( sanitizeXsl ) });
191
+
192
+ for( const c of fr.childNodes )
193
+ payload.append(xslDom.importNode(c,true))
194
+
195
+ const embeddedTemplates = [...payload.querySelectorAll('template')];
196
+ embeddedTemplates.forEach(t=>payload.ownerDocument.documentElement.append(t));
197
+
198
+ const slotCall = $(xslDom,'call-template[name="slot"]')
199
+ , slot2xsl = s =>
200
+ { const v = slotCall.cloneNode(true)
201
+ , name = attr(s,'name') || '';
202
+ name && v.firstElementChild.setAttribute('select',`'${ name }'`)
203
+ for( let c of s.childNodes)
204
+ v.lastElementChild.append(c)
205
+ return v
206
+ }
207
+
208
+ forEach$( payload,'slot', s => s.parentNode.replaceChild( slot2xsl(s), s ) )
209
+
210
+ return tagUid(xslDom)
211
+ }
212
+ export async function
213
+ xhrTemplate(src)
214
+ {
215
+ const dom = await new Promise((resolve,reject)=>
216
+ { const xhr = new XMLHttpRequest();
217
+ xhr.open("GET", src);
218
+ xhr.responseType = "document";
219
+ // xhr.overrideMimeType("text/xml");
220
+ xhr.onload = () =>
221
+ { if( xhr.readyState === xhr.DONE && xhr.status === 200 )
222
+ resolve( xhr.responseXML || create('div', xhr.responseText ) )
223
+ reject(xhr.statusText)
224
+ };
225
+ xhr.addEventListener("error", ev=>reject(ev) );
226
+
227
+ xhr.send();
228
+ })
229
+ return dom
230
+ }
231
+ export function
232
+ deepEqual(a, b, O=false)
233
+ {
234
+ if( a === b )
235
+ return true;
236
+
237
+ if( (typeof a !== "object" || a === null) || (typeof b !== "object" || b === null)
238
+ || Object.keys(a).length !== Object.keys(b).length )
239
+ return O;
240
+
241
+ for( let k in a )
242
+ if( !(k in b) || !deepEqual( a[k], b[k] ) )
243
+ return O
244
+ return true;
245
+ }
246
+
247
+ export function
95
248
  injectSlice( x, s, data )
96
249
  {
97
250
  const isString = typeof data === 'string' ;
98
-
251
+ const createXmlNode = ( tag, t = '' ) => ( e => ((e.append( createText(x, t||''))),e) )(x.ownerDocument.createElement( tag ))
99
252
  const el = isString
100
- ? create(s, data)
253
+ ? createXmlNode(s, data)
101
254
  : document.adoptNode( xml2dom( Json2Xml( data, s ) ).documentElement);
102
255
  [...x.children].filter( e=>e.localName === s ).map( el=>el.remove() );
256
+ el.data = data
103
257
  x.append(el);
104
258
  }
105
259
 
260
+ function forEach$( el, css, cb){
261
+ if( el.querySelectorAll )
262
+ [...el.querySelectorAll(css)].forEach(cb)
263
+ }
264
+ const getByHashId = ( n, id )=> ( p => n===p? null: (p && ( p.querySelector(id) || getByHashId(p,id) ) ))( n.getRootNode() )
265
+ const loadTemplateRoots = async ( src, dce )=>
266
+ {
267
+ if( !src || !src.trim() )
268
+ return [dce]
269
+ if( src.startsWith('#') )
270
+ return ( n =>
271
+ { if(!n) return []
272
+ const a = n.querySelectorAll(src)
273
+ if( a.length )
274
+ return [...a]
275
+ const r = n.getRootNode();
276
+ return r===n ? []: getByHashId(r)
277
+ })(dce.parentElement)
278
+ try
279
+ { // todo cache
280
+ const dom = await xhrTemplate(src)
281
+ const hash = new URL(src, location).hash
282
+ if( hash )
283
+ { const ret = dom.querySelectorAll(hash);
284
+ if( ret.length )
285
+ return [...ret]
286
+ return [dce]
287
+ }
288
+ return [dom]
289
+ }catch (error){ return [dce]}
290
+ }
291
+ export function mergeAttr( from, to )
292
+ { if( isText(from) )
293
+ {
294
+ if( !isText(to) ){ debugger }
295
+ return
296
+ }
297
+ for( let a of from.attributes)
298
+ a.namespaceURI? to.setAttributeNS( a.namespaceURI, a.name, a.value ) : to.setAttribute( a.name, a.value )
299
+ }
300
+ export function assureUnique(n, id=0)
301
+ {
302
+ const m = {}
303
+ for( const e of n.childNodes )
304
+ {
305
+ const a = attr(e,'data-dce-id') || e.dceId || 0;
306
+ if( !m[a] )
307
+ { if( !a )
308
+ { m[a] = e.dceId = ++id;
309
+ if( e.setAttribute )
310
+ e.setAttribute('data-dce-id', e.dceId )
311
+ }else
312
+ m[a] = 1;
313
+ }else
314
+ { const v = e.dceId = a + '-' + m[a]++;
315
+ if( e.setAttribute )
316
+ e.setAttribute('data-dce-id', v )
317
+ }
318
+ e.childNodes.length && assureUnique(e)
319
+ }
320
+ }
321
+ export function merge( parent, fromArr )
322
+ {
323
+ const id2old = {};
324
+ for( let c of parent.childNodes)
325
+ { ASSERT( !id2old[c.dceId] );
326
+ if( isText(c) )
327
+ { ASSERT( c.data.trim() );
328
+ id2old[c.dceId || 0] = c;
329
+ } else
330
+ id2old[attr(c, 'data-dce-id') || 0] = c;
331
+ }
332
+ for( let e of [...fromArr] )
333
+ {
334
+ const o = id2old[ attr(e, 'data-dce-id') || e.dceId ];
335
+ if( o )
336
+ { if( isText(e) )
337
+ { if( o.nodeValue !== e.nodeValue )
338
+ o.nodeValue = e.nodeValue;
339
+ }else
340
+ { mergeAttr(o,e)
341
+ if( o.childNodes.length || e.childNodes.length )
342
+ merge(o, e.childNodes)
343
+ }
344
+ }else
345
+ parent.append( e )
346
+ }
347
+ }
348
+
106
349
  export class
107
350
  CustomElement extends HTMLElement
108
351
  {
109
- constructor()
352
+ async connectedCallback()
110
353
  {
111
- super();
354
+ const templateRoots = await loadTemplateRoots( attr( this, 'src' ), this )
355
+ , templateDocs = templateRoots.map( n => createXsltFromDom( n ) )
356
+ , xp = templateDocs.map( (td, p) =>{ p = new XSLTProcessor(); p.importStylesheet( td ); return p })
357
+
358
+ Object.defineProperty( this, "xsltString", { get: ()=>templateDocs.map( td => xmlString(td) ).join('\n') });
112
359
 
113
- [ ...this.templateNode.querySelectorAll('slot') ].forEach( slot2xsl );
114
- const p = new XSLTProcessor();
115
- p.importStylesheet( this.xslt );
116
360
  const tag = attr( this, 'tag' );
117
361
  const dce = this;
118
362
  const sliceNames = [...this.templateNode.querySelectorAll('[slice]')].map(e=>attr(e,'slice'));
119
- tag && window.customElements.define( tag, class extends HTMLElement
363
+ class DceElement extends HTMLElement
120
364
  {
121
- constructor()
122
- {
123
- super();
124
- const x = create( 'div' );
125
- injectData( x, 'payload', this.childNodes, assureSlot );
126
- injectData( x, 'attributes', this.attributes, e => create( e.nodeName, e.value ) );
127
- injectData( x, 'dataset', Object.keys( this.dataset ), k => create( k, this.dataset[ k ] ) );
128
- const sliceRoot = injectData( x, 'slice', sliceNames, k => create( k, '' ) );
365
+ connectedCallback()
366
+ { const x = xml2dom( '<datadom/>' ).documentElement;
367
+ const createXmlNode = ( tag, t = '' ) => ( e =>
368
+ { if( t )
369
+ e.append( createText( x, t ))
370
+ return e;
371
+ })(x.ownerDocument.createElement( tag ))
372
+ injectData( x, 'payload' , this.childNodes, assureSlot );
373
+ this.innerHTML='';
374
+ injectData( x, 'attributes' , this.attributes, e => createXmlNode( e.nodeName, e.value ) );
375
+ injectData( x, 'dataset', Object.keys( this.dataset ), k => createXmlNode( k, this.dataset[ k ] ) );
376
+ const sliceRoot = injectData( x, 'slice', sliceNames, k => createXmlNode( k, '' ) );
129
377
  this.xml = x;
130
- const slices = {};
131
-
132
378
 
133
379
  const sliceEvents=[];
134
380
  const applySlices = ()=>
@@ -138,7 +384,7 @@ CustomElement extends HTMLElement
138
384
  { const s = attr( ev.target, 'slice');
139
385
  if( processed[s] )
140
386
  continue;
141
- injectSlice( sliceRoot, s, ev.detail );
387
+ injectSlice( sliceRoot, s, 'object' === typeof ev.detail ? {...ev.detail}: ev.detail );
142
388
  processed[s] = ev;
143
389
  }
144
390
  Object.keys(processed).length !== 0 && transform();
@@ -147,6 +393,10 @@ CustomElement extends HTMLElement
147
393
 
148
394
  this.onSlice = ev=>
149
395
  { ev.stopPropagation?.();
396
+ const s = attr( ev.target, 'slice')
397
+ if( deepEqual( ev.detail, [...sliceRoot.children].find( e=>e.localName === s )?.data ) )
398
+ return
399
+
150
400
  sliceEvents.push(ev);
151
401
  if( !timeoutID )
152
402
  timeoutID = setTimeout(()=>
@@ -156,41 +406,50 @@ CustomElement extends HTMLElement
156
406
  };
157
407
  const transform = ()=>
158
408
  {
159
- const f = p.transformToFragment( x, document );
160
- this.innerHTML = '';
161
- [ ...f.childNodes ].forEach( e => this.appendChild( e ) );
162
-
163
- for( let el of this.querySelectorAll('[slice]') )
164
- if( 'function' === typeof el.sliceInit )
165
- { const s = attr(el,'slice');
166
- slices[s] = el.sliceInit( slices[s] );
409
+ const ff = xp.map( (p,i) =>
410
+ { const f = p.transformToFragment(x, document)
411
+ if( !f )
412
+ console.error( "XSLT transformation error. xsl:\n", xmlString(templateDocs[i]), '\nxml:\n', xmlString(x) );
413
+ return f
414
+ });
415
+ ff.map( f =>
416
+ { if( !f )
417
+ return;
418
+ assureUnique(f)
419
+ merge( this, f.childNodes )
420
+ })
421
+ const changeCb = el=>this.onSlice({ detail: el[attr(el,'slice-prop') || 'value'], target: el })
422
+ , hasInitValue = el => el.hasAttribute('slice-prop') || el.hasAttribute('value') || el.value;
423
+
424
+ forEach$( this,'[slice]', el =>
425
+ { if( !el.dceInitialized )
426
+ { el.dceInitialized = 1;
427
+ el.addEventListener( attr(el,'slice-update')|| 'change', ()=>changeCb(el) )
428
+ if( hasInitValue(el) )
429
+ changeCb(el)
167
430
  }
431
+ })
168
432
  };
169
433
  transform();
170
434
  applySlices();
171
435
  }
172
- get dce(){ return dce;}
173
- } );
436
+ get dce(){ return dce }
437
+ }
438
+ if(tag)
439
+ window.customElements.define( tag, DceElement);
440
+ else
441
+ { const t = 'dce-'+crypto.randomUUID()
442
+ window.customElements.define( t, DceElement);
443
+ const el = document.createElement(t);
444
+ this.getAttributeNames().forEach(a=>el.setAttribute(a,this.getAttribute(a)));
445
+ el.append(...this.childNodes)
446
+ this.append(el);
447
+ }
174
448
  }
175
449
  get templateNode(){ return this.firstElementChild?.tagName === 'TEMPLATE'? this.firstElementChild.content : this }
176
- get dce(){ return this;}
177
- get xsltString()
178
- {
179
- return (
180
- `<xsl:stylesheet version="1.0"
181
- xmlns:xsl="${ XSL_NS_URL }">
182
- <xsl:output method="html" />
183
-
184
- <xsl:template match="/">
185
- <xsl:apply-templates select="//attributes"/>
186
- </xsl:template>
187
- <xsl:template match="attributes">
188
- ${ bodyXml( this ) }
189
- </xsl:template>
190
-
191
- </xsl:stylesheet>` );
192
- }
193
- get xslt(){ return xml2dom( this.xsltString ); }
450
+ get dce(){ return this }
451
+
452
+ get xslt(){ return xml2dom( this.xsltString ) }
194
453
  }
195
454
 
196
455
  window.customElements.define( 'custom-element', CustomElement );
package/demo/a.html ADDED
@@ -0,0 +1,38 @@
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
+ xmlns:html="http://www.w3.org/1999/xhtml">
4
+ <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
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>
9
+ <script type="module" src="../custom-element.js"></script>
10
+
11
+ <style>
12
+ @import "./demo.css";
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; }
20
+ </style>
21
+ </head>
22
+ <body>
23
+
24
+ <custom-element >
25
+ <local-storage key="basket" slice="basket"></local-storage>
26
+ <html:table>
27
+ <xsl:for-each select="//slice/basket/@*">
28
+ <html:tr>
29
+ <html:th> {name()} </html:th>
30
+ <html:td> {.} </html:td>
31
+ </html:tr>
32
+ </xsl:for-each>
33
+ </html:table>
34
+ count:<xsl:value-of select="count(//slice/basket/@*)"/>
35
+ </custom-element>
36
+
37
+ </body>
38
+ </html>
@@ -0,0 +1,37 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48" version="1.0">
3
+ <defs>
4
+ <linearGradient id="d">
5
+ <stop offset="0"/>
6
+ <stop offset=".5"/>
7
+ <stop offset=".80000001" stop-opacity=".46666667"/>
8
+ <stop offset="1" stop-opacity="0"/>
9
+ </linearGradient>
10
+ <linearGradient id="a">
11
+ <stop offset="0" stop-color="#fb0"/>
12
+ <stop offset=".5" stop-color="#e29d00"/>
13
+ <stop offset="1" stop-color="#fb0"/>
14
+ </linearGradient>
15
+ <linearGradient id="c">
16
+ <stop offset="0"/>
17
+ <stop offset="1" stop-opacity="0"/>
18
+ </linearGradient>
19
+ <linearGradient id="b">
20
+ <stop offset="0" stop-color="#ffc"/>
21
+ <stop offset=".5" stop-color="#fff965"/>
22
+ <stop offset="1" stop-color="#fc3"/>
23
+ </linearGradient>
24
+ <linearGradient xlink:href="#a" id="i" x1="8.0350637" x2="42.788235" y1="32.372219" y2="32.372219" gradientUnits="userSpaceOnUse" spreadMethod="pad"/>
25
+ <radialGradient xlink:href="#b" id="f" cx="17.986637" cy="16.545853" r="23.978155" fx="17.986637" fy="16.545853" gradientUnits="userSpaceOnUse"/>
26
+ <radialGradient xlink:href="#c" id="e" cx="53.309223" cy="94.956306" r="63.252911" fx="53.309223" fy="94.956306" gradientTransform="matrix(1 0 0 .34935 0 61.7838)" gradientUnits="userSpaceOnUse"/>
27
+ <radialGradient xlink:href="#d" id="g" cx="18.71347" cy="21.759708" r="1.8644418" fx="18.71347" fy="21.759708" gradientTransform="matrix(1 0 0 1.77778 0 -16.92422)" gradientUnits="userSpaceOnUse"/>
28
+ </defs>
29
+ <path fill="url(#e)" d="M116.56213 94.956306a63.252911 22.097088 0 1 1-126.5058174 0 63.252911 22.097088 0 1 1 126.5058174 0z" opacity=".53200001" transform="matrix(.3162 0 0 .33941 6.936944 8.132618)"/>
30
+ <path fill="url(#f)" stroke="#fb0" stroke-width="1.43869453" d="M47.094418 23.83131a23.478155 23.478155 0 1 1-46.9563107 0 23.478155 23.478155 0 1 1 46.9563107 0z" transform="translate(4.30185 4.122792) scale(.83409)"/>
31
+ <path id="h" fill="#fff" fill-opacity="1" fill-rule="nonzero" stroke="#fc0" stroke-dasharray="none" stroke-dashoffset="0" stroke-linejoin="miter" stroke-miterlimit="4" stroke-opacity="1" stroke-width="1" d="M21.682767 18.5142a3.9360437 6.9743929 0 1 1-7.872088 0 3.9360437 6.9743929 0 1 1 7.872088 0z" opacity="1" transform="matrix(1.01507 0 0 1.00354 -.0090285 .916405)"/>
32
+ <path id="j" fill="url(#g)" fill-opacity="1" fill-rule="nonzero" stroke="none" stroke-dasharray="none" stroke-dashoffset="0" stroke-linejoin="miter" stroke-miterlimit="4" stroke-opacity="1" stroke-width="1" d="M20.577912 21.759708a1.8644418 3.314563 0 1 1-3.728883 0 1.8644418 3.314563 0 1 1 3.728883 0z" opacity="1" transform="translate(-.138107 .535104)"/>
33
+ <use xlink:href="#h" width="48" height="48" transform="translate(12.50001 -4.4e-7)"/>
34
+ <path fill="none" stroke="url(#i)" stroke-linecap="round" stroke-width="1.97319973" d="M9.0216636 35.899178c4.7689724-7.457767 10.9544424-9.489956 17.3095664-3.728884 5.404329 4.899155 11.190398 4.350365 15.470406-.656007"/>
35
+ <path fill="none" stroke="#e2ac00" stroke-linecap="round" stroke-width="1.17813516" d="M15.504748 34.21319c3.012147-3.243177 6.693658.87012 6.693658.87012" opacity=".8"/>
36
+ <use xlink:href="#j" width="48" height="48" transform="translate(10.78418 -5)"/>
37
+ </svg>
package/demo/demo.css CHANGED
@@ -5,8 +5,10 @@ html
5
5
  body,nav{ display: flex; flex-wrap: wrap; align-content: stretch; gap: 1rem; }
6
6
  body>*{flex: auto;}
7
7
  nav{ flex-direction: column;}
8
+ custom-element+*,
9
+ custom-element:not([tag]),
8
10
  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
11
+ dce-1,dce-2,dce-3,dce-4,dce-internal,dce-hash
10
12
  { box-shadow: 0 0 0.5rem lime; padding: 1rem; display: inline-block; flex:1; }
11
13
  dd{ padding: 1rem;}
12
14
  p{ margin: 0;}