@epa-wg/custom-element 0.0.31 → 0.0.33
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 +5 -5
- package/bin/xslDtd2Ide.mjs +1 -1
- package/custom-element.d.ts +4 -0
- package/custom-element.js +145 -65
- package/demo/a.html +73 -0
- package/demo/attributes.html +153 -0
- package/demo/data-slices.html +2 -2
- package/demo/external-template.html +1 -0
- package/demo/form.html +42 -0
- package/demo/hex-grid-dce.html +1 -1
- package/demo/hex-grid.html +1 -1
- package/demo/s.xml +66 -9
- package/demo/s.xslt +89 -16
- package/demo/ss.html +12 -22
- package/docs/attributes.md +86 -0
- package/ide/web-types-dce.json +1 -1
- package/ide/web-types-xsl.json +1 -1
- package/index.html +1 -1
- package/package.json +1 -1
- package/demo/parameters.html +0 -70
- /package/docs/{remdering.md → rendering.md} +0 -0
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ This project is a POC( Proof of Concept ) targeting to become a base for native
|
|
|
47
47
|
|
|
48
48
|
# use
|
|
49
49
|
|
|
50
|
-
Use the [bootstrap project]
|
|
50
|
+
[//]: # (Use the [bootstrap project](https://github.com/EPA-WG/custom-element-bootstrap) with all pre-configured or)
|
|
51
51
|
## install
|
|
52
52
|
use via CDN
|
|
53
53
|
```html
|
|
@@ -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.
|
|
344
|
+
[hex-grid-image]: https://unpkg.com/@epa-wg/custom-element@0.0.33/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.
|
|
352
|
-
[coverage-url]: https://unpkg.com/@epa-wg/custom-element-dist@0.0.
|
|
353
|
-
[storybook-url]: https://unpkg.com/@epa-wg/custom-element-dist@0.0.
|
|
351
|
+
[coverage-image]: https://unpkg.com/@epa-wg/custom-element-dist@0.0.33/coverage/src/custom-element/coverage.svg
|
|
352
|
+
[coverage-url]: https://unpkg.com/@epa-wg/custom-element-dist@0.0.33/coverage/src/custom-element/index.html
|
|
353
|
+
[storybook-url]: https://unpkg.com/@epa-wg/custom-element-dist@0.0.33/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
|
package/bin/xslDtd2Ide.mjs
CHANGED
|
@@ -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.
|
|
119
|
+
"version": "0.0.33",
|
|
120
120
|
"js-types-syntax": "typescript",
|
|
121
121
|
"description-markup": "markdown",
|
|
122
122
|
"contributions": {
|
package/custom-element.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export function log(x: any): void;
|
|
2
2
|
export function deepEqual(a: any, b:any): boolean|0;
|
|
3
|
+
export function cloneAs(sourceNode: HTMLElement, tag:string): HTMLElement;
|
|
4
|
+
export function mix(objTo: any, objFrom:any): any;
|
|
5
|
+
export function mergeAttr(fromEl: HTMLElement, toEL:HTMLElement): void;
|
|
6
|
+
|
|
3
7
|
export function xml2dom(xmlString:string): Document;
|
|
4
8
|
export function xmlString(doc:Node|Document): string;
|
|
5
9
|
export function obj2node(o:any, tag:string, doc:Document): HTMLElement;
|
package/custom-element.js
CHANGED
|
@@ -5,7 +5,7 @@ const XSL_NS_URL = 'http://www.w3.org/1999/XSL/Transform'
|
|
|
5
5
|
|
|
6
6
|
// const log = x => console.debug( new XMLSerializer().serializeToString( x ) );
|
|
7
7
|
|
|
8
|
-
const attr = (el, attr)=> el
|
|
8
|
+
const attr = (el, attr)=> el?.getAttribute?.(attr)
|
|
9
9
|
, isText = e => e.nodeType === 3
|
|
10
10
|
, isString = s => typeof s === 'string'
|
|
11
11
|
, isNode = e => e && typeof e.nodeType === 'number'
|
|
@@ -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
|
-
|
|
33
|
-
px.append(
|
|
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="
|
|
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,64 @@ createXsltFromDom( templateNode, S = 'xsl:stylesheet' )
|
|
|
285
293
|
[...fr.childNodes].forEach(n=>r.append(n));
|
|
286
294
|
fr.append(r)
|
|
287
295
|
}
|
|
288
|
-
|
|
296
|
+
|
|
297
|
+
[...fr.querySelectorAll('[test]')].forEach( n=>{
|
|
298
|
+
const t = attr(n,'test')
|
|
299
|
+
, r = t.replace(/hasBoolAttribute\((.*?)\)/g,
|
|
300
|
+
(match, p1, p2,p3,p4)=>
|
|
301
|
+
{ const a = p1.substring(1);
|
|
302
|
+
return `(not($${a} = \'false\') and ($${a} = '' or $${a} = '${a}' or $${a} = 'true' ))`
|
|
303
|
+
});
|
|
304
|
+
t!== r && n.setAttribute('test',r);
|
|
305
|
+
});
|
|
306
|
+
|
|
289
307
|
[...fr.querySelectorAll('dce-root>attribute')].forEach( a=>
|
|
290
308
|
{
|
|
291
309
|
keepAttributes(a,'namespace,name,select');
|
|
292
310
|
const p = cloneAs(a,'xsl:param')
|
|
293
311
|
, name = attr(a,'name');
|
|
312
|
+
|
|
313
|
+
declaredAttributes.push(name);
|
|
314
|
+
if( a.childNodes.length)
|
|
315
|
+
hardcodedAttributes[name] = a.textContent;
|
|
316
|
+
|
|
294
317
|
payload.append(p);
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
318
|
+
|
|
319
|
+
if( a.hasAttribute('select') )
|
|
320
|
+
{
|
|
321
|
+
exposedAttributes[ name ] = attr( a, 'select' );
|
|
322
|
+
keepAttributes( p, 'select,name' );
|
|
323
|
+
|
|
324
|
+
let select = attr(a,'select').split('??');
|
|
325
|
+
|
|
326
|
+
let val;
|
|
327
|
+
if( select?.length>1 )
|
|
328
|
+
{ p.removeAttribute('select');
|
|
329
|
+
const c = $( xslDom, 'template[match="ignore"]>choose').cloneNode(true);
|
|
330
|
+
emptyNode(c.firstElementChild).append( createText(c,'{'+select[0]+'}'));
|
|
331
|
+
c.firstElementChild.setAttribute('test',select[0]);
|
|
332
|
+
for( let i=1; i<select.length-1; i++)
|
|
333
|
+
{ const when = c.firstElementChild.cloneNode(true);
|
|
334
|
+
emptyNode(when).append( createText(c,'{'+select[i]+'}'));
|
|
335
|
+
when.setAttribute('test',select[i]);
|
|
336
|
+
c.insertBefore(when, c.lastElementChild);
|
|
337
|
+
}
|
|
338
|
+
emptyNode(c.lastElementChild ).append( createText(c,'{'+select[select.length-1]+'}'));
|
|
339
|
+
p.append(c);
|
|
340
|
+
val = c.cloneNode(true);
|
|
341
|
+
}else
|
|
342
|
+
val = cloneAs(a,'xsl:value-of');
|
|
343
|
+
val.removeAttribute('name');
|
|
344
|
+
a.append(val);
|
|
345
|
+
a.removeAttribute('select');
|
|
311
346
|
}else
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
347
|
+
{
|
|
348
|
+
keepAttributes( p, 'name' );
|
|
349
|
+
p.setAttribute('select','/datadom/attributes/'+name)
|
|
350
|
+
|
|
351
|
+
if( !hardcodedAttributes[name] )
|
|
352
|
+
a.remove();
|
|
353
|
+
}
|
|
317
354
|
});
|
|
318
355
|
[...fr.querySelectorAll('[value]')].filter(el=>el.getAttribute('value').match( /\{(.*)\?\?(.*)\}/g )).forEach(el=>
|
|
319
356
|
{ const v = attr(el,'value');
|
|
@@ -338,8 +375,8 @@ createXsltFromDom( templateNode, S = 'xsl:stylesheet' )
|
|
|
338
375
|
|
|
339
376
|
forEach$( payload,'slot', s => s.parentNode.replaceChild( slot2xsl(s), s ) )
|
|
340
377
|
|
|
341
|
-
const ret = tagUid(xslDom)
|
|
342
|
-
ret
|
|
378
|
+
const ret = tagUid(xslDom);
|
|
379
|
+
mix( ret, { declaredAttributes, hardcodedAttributes, exposedAttributes } );
|
|
343
380
|
return ret;
|
|
344
381
|
}
|
|
345
382
|
export async function
|
|
@@ -442,12 +479,15 @@ event2slice( x, sliceNames, ev, dce )
|
|
|
442
479
|
const v = notChecked? '' : el.value ?? attr( el, 'value' );
|
|
443
480
|
cleanSliceValue();
|
|
444
481
|
if( v === null || v === undefined )
|
|
482
|
+
{
|
|
445
483
|
[...s.childNodes].filter(n=>n.localName!=='event').map(n=>n.remove());
|
|
484
|
+
s.removeAttribute('value');
|
|
485
|
+
}
|
|
446
486
|
else
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
487
|
+
{ const ve = isString(v) ? createText( d, v) : obj2node(v,'value',s.ownerDocument);
|
|
488
|
+
s.append( ve );
|
|
489
|
+
s.setAttribute('value',v);
|
|
490
|
+
}
|
|
451
491
|
}
|
|
452
492
|
return s
|
|
453
493
|
})
|
|
@@ -494,10 +534,25 @@ const loadTemplateRoots = async ( src, dce )=>
|
|
|
494
534
|
}
|
|
495
535
|
export function mergeAttr( from, to )
|
|
496
536
|
{ for( let a of from.attributes)
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
537
|
+
try
|
|
538
|
+
{ const name = a.name;
|
|
539
|
+
if( a.namespaceURI )
|
|
540
|
+
{ if( !to.hasAttributeNS(a.namespaceURI, name) || to.getAttributeNS(a.namespaceURI, name) !== a.value )
|
|
541
|
+
to.setAttributeNS( a.namespaceURI, name, a.value )
|
|
542
|
+
}else
|
|
543
|
+
{ if( !to.hasAttribute(name) || to.getAttribute(name) !== a.value )
|
|
544
|
+
to.setAttribute( a.name, a.value )
|
|
545
|
+
}
|
|
546
|
+
if( a.name === 'value')
|
|
547
|
+
to.value = a.value
|
|
548
|
+
}catch(e)
|
|
549
|
+
{ console.warn('attribute assignment error',e?.message || e); }
|
|
550
|
+
const ea = to.dceExportedAttributes
|
|
551
|
+
, aa = to.getAttribute('dce-exported-attributes')
|
|
552
|
+
, em = aa ? new Set( aa.split(' ') ) : null;
|
|
553
|
+
for( let a of to.getAttributeNames() )
|
|
554
|
+
if( !from.hasAttribute(a) && !ea?.has(a) && !em?.has(a) )
|
|
555
|
+
to.removeAttribute(a)
|
|
501
556
|
}
|
|
502
557
|
export function assureUnique(n, id=0)
|
|
503
558
|
{
|
|
@@ -530,7 +585,7 @@ export function appendByDceId(parent,e,k)
|
|
|
530
585
|
}
|
|
531
586
|
export function merge( parent, fromArr )
|
|
532
587
|
{
|
|
533
|
-
if( 'dce-root' === parent.firstElementChild?.localName && 'dce-root' !== fromArr[0]
|
|
588
|
+
if( 'dce-root' === parent.firstElementChild?.localName && 'dce-root' !== fromArr[0]?.localName)
|
|
534
589
|
return;
|
|
535
590
|
if( !fromArr.length )
|
|
536
591
|
return 'dce-root' !== parent.firstElementChild?.localName && removeChildren(parent);
|
|
@@ -658,13 +713,16 @@ CustomElement extends HTMLElement
|
|
|
658
713
|
, sliceNames = sliceNodes.map(e=>attr(e,'slice'))
|
|
659
714
|
.filter(n=>!n.includes('/'))
|
|
660
715
|
.filter((v, i, a)=>a.indexOf(v) === i)
|
|
661
|
-
.map(splitSliceNames).flat()
|
|
662
|
-
|
|
716
|
+
.map(splitSliceNames).flat();
|
|
717
|
+
|
|
718
|
+
const { declaredAttributes, hardcodedAttributes, exposedAttributes } = templateDocs[0];
|
|
719
|
+
const dceExportedAttributes = new Set([...Object.keys(hardcodedAttributes), ...Object.keys(exposedAttributes)]);
|
|
663
720
|
|
|
664
721
|
class DceElement extends HTMLElement
|
|
665
722
|
{
|
|
666
|
-
static get observedAttributes(){ return declaredAttributes
|
|
723
|
+
static get observedAttributes(){ return declaredAttributes; }
|
|
667
724
|
#inTransform = 0;
|
|
725
|
+
get dceExportedAttributes(){ return dceExportedAttributes; }
|
|
668
726
|
connectedCallback()
|
|
669
727
|
{ let payload = sanitizeBlankText(this.childNodes);
|
|
670
728
|
if( this.firstElementChild?.tagName === 'TEMPLATE' )
|
|
@@ -696,7 +754,12 @@ CustomElement extends HTMLElement
|
|
|
696
754
|
xslNs(payloadNode);
|
|
697
755
|
xslHtmlNs(payloadNode);
|
|
698
756
|
this.innerHTML='';
|
|
699
|
-
const attrsRoot = injectData( x, 'attributes' , this.attributes, e => createXmlNode( e.nodeName, e.value ) )
|
|
757
|
+
const attrsRoot = injectData( x, 'attributes' , this.attributes, e => createXmlNode( e.nodeName, e.value ) )
|
|
758
|
+
, inAttrs = a=> this.hasAttribute(a) || [...attrsRoot.children].find(e=>e.localName === a);
|
|
759
|
+
mergeAttr( this, attrsRoot );
|
|
760
|
+
Object.keys(hardcodedAttributes).map(a=> inAttrs(a) || attrsRoot.append(createXmlNode(a,hardcodedAttributes[a])) );
|
|
761
|
+
Object.keys(exposedAttributes).map(a=> inAttrs(a) || attrsRoot.append(createXmlNode(a)) );
|
|
762
|
+
|
|
700
763
|
injectData( x, 'dataset', Object.keys( this.dataset ), k => createXmlNode( k, this.dataset[ k ] ) );
|
|
701
764
|
const sliceRoot = injectData( x, 'slice', sliceNames, k => createXmlNode( k, '' ) )
|
|
702
765
|
, sliceXPath = x => xPath(x, sliceRoot);
|
|
@@ -728,27 +791,42 @@ CustomElement extends HTMLElement
|
|
|
728
791
|
const transform = this.transform = ()=>
|
|
729
792
|
{ if(this.#inTransform){ debugger }
|
|
730
793
|
this.#inTransform = 1;
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
if( v !== attr(this,a) )
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
794
|
+
const renderModel = ()=>
|
|
795
|
+
{
|
|
796
|
+
const ff = xp.map( (p,i) =>
|
|
797
|
+
{ const f = p.transformToFragment(x.ownerDocument, document)
|
|
798
|
+
if( !f )
|
|
799
|
+
console.error( "XSLT transformation error. xsl:\n", xmlString(templateDocs[i]), '\nxml:\n', xmlString(x) );
|
|
800
|
+
return f
|
|
801
|
+
});
|
|
802
|
+
ff.map( f =>
|
|
803
|
+
{ if( !f )
|
|
804
|
+
return;
|
|
805
|
+
assureUnique(f);
|
|
806
|
+
merge( this, f.childNodes )
|
|
807
|
+
})
|
|
808
|
+
let attrChangedCount = 0;
|
|
809
|
+
Object.entries(hardcodedAttributes).map(( [a,v] )=>
|
|
810
|
+
{ if( !this.hasAttribute(a) && v !== attr(this,a) )
|
|
811
|
+
{ this.setAttribute( a, v );
|
|
812
|
+
this.#applyAttribute( a, v );
|
|
813
|
+
attrChangedCount++;
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
Object.keys(exposedAttributes).map( a =>
|
|
818
|
+
{ let v = attr(this.firstElementChild,a);
|
|
819
|
+
if( v !== attr(this,a) )
|
|
820
|
+
{ this.setAttribute( a, v );
|
|
821
|
+
this.#applyAttribute( a, v );
|
|
822
|
+
attrChangedCount++;
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
return attrChangedCount;
|
|
826
|
+
};
|
|
827
|
+
if( renderModel() )
|
|
828
|
+
if( renderModel() )
|
|
829
|
+
console.warn("model update should not be the result of transform more than once");
|
|
752
830
|
|
|
753
831
|
function getSliceTarget(el)
|
|
754
832
|
{ let r = el;
|
|
@@ -836,13 +914,15 @@ CustomElement extends HTMLElement
|
|
|
836
914
|
#applyAttribute(name, newValue)
|
|
837
915
|
{ if( 'value' === name )
|
|
838
916
|
this.value = newValue;
|
|
917
|
+
const attrs = this.xml.querySelector('attributes');
|
|
839
918
|
let a = this.xml.querySelector(`attributes>${name}`);
|
|
840
919
|
if( a )
|
|
841
920
|
emptyNode(a).append( createText(a,newValue) );
|
|
842
921
|
else
|
|
843
922
|
{ a = create( name, newValue, this.xml );
|
|
844
|
-
|
|
923
|
+
attrs.append( a );
|
|
845
924
|
}
|
|
925
|
+
this.#inTransform || attrs.setAttribute(name,newValue);
|
|
846
926
|
|
|
847
927
|
this.dispatchEvent(new CustomEvent('change', { bubbles: true,detail: { [name]: newValue }}))
|
|
848
928
|
}
|
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>
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
|
3
|
+
xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
|
4
|
+
<head>
|
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
|
6
|
+
<title>parameters - 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
|
+
<style>
|
|
11
|
+
@import "./demo.css";
|
|
12
|
+
|
|
13
|
+
button{ background: forestgreen; }
|
|
14
|
+
table{ min-width: 16rem; }
|
|
15
|
+
td{ border-bottom: 1px solid silver; }
|
|
16
|
+
tfoot td{ border-bottom: none; }
|
|
17
|
+
td,th{text-align: right; }
|
|
18
|
+
caption{ padding: 1rem; font-weight: bolder; font-family: sans-serif; }
|
|
19
|
+
</style>
|
|
20
|
+
</head>
|
|
21
|
+
<body>
|
|
22
|
+
<nav>
|
|
23
|
+
<a href="../index.html"><h3><code>custom-element</code> demo</h3></a>
|
|
24
|
+
</nav>
|
|
25
|
+
<main>
|
|
26
|
+
<p><code>attribute</code> is used for DCE attributes declaration and track the attributes changes. It also be used by IDE and validation.</p>
|
|
27
|
+
<p>The attribute can be changed by component itself and used as output to the container.
|
|
28
|
+
Usual case is when <code>value</code> attribute is updated from inside.</p>
|
|
29
|
+
<p>Initial value of attribute is available on the <code>attributes</code> node attribute as in <code>/datadom/attributes/@attr1</code></p>
|
|
30
|
+
<p>The current, i.e. including the changes by component itself, attribute value is a child node of <code>attributes</code> as in <code>/datadom/attributes/attr1</code>.</p>
|
|
31
|
+
<p>To define the attribute which is modified from within, the 3 parts are usually used as in <code>//s[//s/event] ?? //attributes/@v ?? 'def' </code>
|
|
32
|
+
<ol>
|
|
33
|
+
<li><code>//s[//s/event]</code> the slice <code>s</code> with event gives the slice value which was modified by user event like input</li>
|
|
34
|
+
<li><code>//attributes/@v</code> the attribute passed by container</li>
|
|
35
|
+
<li><code>'def' </code> the default value which used when no user input or attribute set by container</li>
|
|
36
|
+
</ol>
|
|
37
|
+
</p>
|
|
38
|
+
</main>
|
|
39
|
+
<html-demo-element legend="attributes definition" >
|
|
40
|
+
<p slot="description">
|
|
41
|
+
<code>attribute</code> is used for DCE attributes declaration and track the attributes changes. It also be used by IDE and validation.
|
|
42
|
+
|
|
43
|
+
</p>
|
|
44
|
+
<template>
|
|
45
|
+
<custom-element tag="dce-link" hidden>
|
|
46
|
+
<attribute name="p1" >default_P1 </attribute>
|
|
47
|
+
<attribute name="p2" select="'always_p2'" ></attribute>
|
|
48
|
+
<attribute name="p3" select="//attributes/@p3 ?? 'def_P3' " ></attribute>
|
|
49
|
+
p1:{$p1} <br/> p2: {$p2} <br/> p3: {$p3}
|
|
50
|
+
</custom-element>
|
|
51
|
+
<dce-link id="dce1" ></dce-link>
|
|
52
|
+
<section>
|
|
53
|
+
<dce-link id="dce2" p1="123" p2="override ignored as select is defined"></dce-link> <br/>
|
|
54
|
+
<div><input id="i1" value="p1" /> <button onclick="dce2.setAttribute('p1',i1.value)"> set p1</button> </div>
|
|
55
|
+
<div><input id="i2" value="p2" /> <button onclick="dce2.setAttribute('p2',i2.value)"> set p2</button> </div>
|
|
56
|
+
<div><input id="i3" value="p3" /> <button onclick="dce2.setAttribute('p3',i3.value)"> set p3</button> </div>
|
|
57
|
+
</section>
|
|
58
|
+
<dce-link id="dce3" p1="123" p3="qwe"></dce-link> |
|
|
59
|
+
|
|
60
|
+
</template>
|
|
61
|
+
</html-demo-element>
|
|
62
|
+
|
|
63
|
+
<html-demo-element legend="attribute from slice">
|
|
64
|
+
<p slot="description">
|
|
65
|
+
When slice value points to attribute, it would be populated on slice change.<br/>
|
|
66
|
+
Type in the input field to see the variable $title change. <br/>
|
|
67
|
+
Hover the mouse to see the title attribute text popup.<br/>
|
|
68
|
+
Inspect DCE node in dev tools to see `title` attribute updated while typing.
|
|
69
|
+
|
|
70
|
+
</p>
|
|
71
|
+
|
|
72
|
+
<template>
|
|
73
|
+
<custom-element>
|
|
74
|
+
<template>
|
|
75
|
+
<attribute name="title" select="//title ?? '😃'" ></attribute>
|
|
76
|
+
<input slice="/datadom/attributes/title" slice-event="keyup"/>
|
|
77
|
+
title attribute: {$title}
|
|
78
|
+
</template>
|
|
79
|
+
</custom-element>
|
|
80
|
+
</template>
|
|
81
|
+
</html-demo-element>
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
<html-demo-element legend="V attribute matches input value" description="
|
|
85
|
+
Type in the input field and observe in DevTools the V attribute changed.
|
|
86
|
+
">
|
|
87
|
+
<template>
|
|
88
|
+
<custom-element tag="x-input" >
|
|
89
|
+
<template>
|
|
90
|
+
<attribute name="is-changed" select="count(//s/event) > 0"></attribute>
|
|
91
|
+
<attribute name="v" select="//s[//s/event] ?? //attributes/@v ?? 'def' "></attribute>
|
|
92
|
+
/datadom/attributes/v='{/datadom/attributes/v}'<br/>
|
|
93
|
+
same as v='{v}'<br/>
|
|
94
|
+
same as $v='{$v}'<br/>
|
|
95
|
+
//attributes/@v='{//attributes/@v}'<br/>
|
|
96
|
+
//s='{//s}'<br/>
|
|
97
|
+
is-changed ={ is-changed }<br/>
|
|
98
|
+
<input slice="s" slice-event="input" value="{//attributes/v}"/>
|
|
99
|
+
</template>
|
|
100
|
+
</custom-element>
|
|
101
|
+
<x-input></x-input>
|
|
102
|
+
<x-input v="V1"></x-input>
|
|
103
|
+
</template>
|
|
104
|
+
</html-demo-element>
|
|
105
|
+
|
|
106
|
+
<html-demo-element legend="attribute defaults, from container, and from slice" description="
|
|
107
|
+
Type in the input field and observe in DevTools the V attribute changed.
|
|
108
|
+
">
|
|
109
|
+
<template>
|
|
110
|
+
<custom-element tag="attr-demo">
|
|
111
|
+
<template>
|
|
112
|
+
<variable name="has-input" select="count(//s/*) > 0"></variable>
|
|
113
|
+
<attribute name="v" select="//s[//s/event] ?? //attributes/@v ?? 'def' "></attribute>
|
|
114
|
+
//attributes/v='{//attributes/v}'<br/>
|
|
115
|
+
//attributes/@v='{//attributes/@v}'<br/>
|
|
116
|
+
$v='{$v}'<br/>
|
|
117
|
+
//s='{//s}'<br/>
|
|
118
|
+
A='{//s[//s/event] | //attributes/v[not(//s/event)]}'<br/>
|
|
119
|
+
has-input ={ $has-input }<br/>
|
|
120
|
+
<input slice="s" slice-event="input" />
|
|
121
|
+
</template>
|
|
122
|
+
</custom-element>
|
|
123
|
+
|
|
124
|
+
<attr-demo></attr-demo>
|
|
125
|
+
<attr-demo v="From Container"></attr-demo>
|
|
126
|
+
</template>
|
|
127
|
+
</html-demo-element>
|
|
128
|
+
|
|
129
|
+
<details>
|
|
130
|
+
<summary>Attributes processing</summary>
|
|
131
|
+
To be available in template, <code>custom-element</code> attributes should be
|
|
132
|
+
defined by <code>attribute</code> markup as shown above.
|
|
133
|
+
The value is taken from attribute text content or from <code>select</code> XPath expression<br/>
|
|
134
|
+
|
|
135
|
+
Declared in such way, attributes are exposed via <code><a
|
|
136
|
+
href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#responding_to_attribute_changes"
|
|
137
|
+
>observedAttributes</a></code>. <br/>
|
|
138
|
+
The template exposes those attributes as <code>xsl:param</code> which makes the attribute value available as
|
|
139
|
+
xsl variable (as attribute name prefixed with $). <br/>
|
|
140
|
+
After transformation the attributes values are read from CE root and copied into DCE component. <br/>
|
|
141
|
+
The DCE attribute change from outside invokes <code>attributeChangedCallback</code> which triggers DCE re-render.
|
|
142
|
+
<p>
|
|
143
|
+
The <code>value</code> attribute is usual case to be propagated from within of <code>custom-element</code>.
|
|
144
|
+
See the <a href="./form.html#sample-5">using custom-element as form input</a> example.
|
|
145
|
+
</p>
|
|
146
|
+
• <a href="https://github.com/EPA-WG/custom-element/blob/main/docs/attributes.md">Design docs</a>
|
|
147
|
+
</details>
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
<script type="module" src="https://unpkg.com/html-demo-element@1/html-demo-element.js"></script>
|
|
151
|
+
|
|
152
|
+
</body>
|
|
153
|
+
</html>
|
package/demo/data-slices.html
CHANGED
|
@@ -233,8 +233,8 @@
|
|
|
233
233
|
</template>
|
|
234
234
|
</html-demo-element>
|
|
235
235
|
|
|
236
|
-
<html-demo-element legend="13. Radio
|
|
237
|
-
description="The value
|
|
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>
|
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>
|
package/demo/hex-grid-dce.html
CHANGED
|
@@ -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://
|
|
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"/>
|
package/demo/hex-grid.html
CHANGED
|
@@ -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/
|
|
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"/>
|
package/demo/s.xml
CHANGED
|
@@ -1,18 +1,75 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
2
|
<datadom>
|
|
3
3
|
<payload xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
<div xmlns="http://www.w3.org/1999/xhtml" slot="leading" data-dce-id="4">
|
|
5
|
+
<cem-icon image="history_edu" class="input-focused" data-dce-id="5">
|
|
6
|
+
<dce-root xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml"
|
|
7
|
+
xmlns:dce="urn:schemas-epa-wg:dce" data-dce-id="1">
|
|
8
|
+
<span xmlns="" class="icon material-icons" data-dce-id="5">history_edu</span>
|
|
9
|
+
</dce-root>
|
|
10
|
+
</cem-icon>
|
|
11
|
+
<cem-icon image="edit" class="input-unfocused" data-dce-id="6">
|
|
12
|
+
<dce-root xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml"
|
|
13
|
+
xmlns:dce="urn:schemas-epa-wg:dce" data-dce-id="1">
|
|
14
|
+
<span xmlns="" class="icon material-icons" data-dce-id="5">edit</span>
|
|
15
|
+
</dce-root>
|
|
16
|
+
</cem-icon>
|
|
17
|
+
</div>
|
|
18
|
+
<div xmlns="http://www.w3.org/1999/xhtml" slot="trailing" data-dce-id="7">
|
|
19
|
+
<label class="eye-contailer" for="visible-cb" data-dce-id="8">
|
|
20
|
+
<cem-icon image="visibility" class="visible-ico" tabindex="0" data-dce-id="9">
|
|
21
|
+
<dce-root xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml"
|
|
22
|
+
xmlns:dce="urn:schemas-epa-wg:dce" data-dce-id="1">
|
|
23
|
+
<span xmlns="" class="icon material-icons" data-dce-id="5">visibility</span>
|
|
24
|
+
</dce-root>
|
|
25
|
+
</cem-icon>
|
|
26
|
+
<cem-icon image="visibility_off" class="invisible-ico" tabindex="0" data-dce-id="10">
|
|
27
|
+
<dce-root xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml"
|
|
28
|
+
xmlns:dce="urn:schemas-epa-wg:dce" data-dce-id="1">
|
|
29
|
+
<span xmlns="" class="icon material-icons" data-dce-id="5">visibility_off</span>
|
|
30
|
+
</dce-root>
|
|
31
|
+
</cem-icon>
|
|
32
|
+
</label>
|
|
33
|
+
<cem-icon image="close" class="clear" tabindex="0" slice="password" slice-event="click" slice-value="''"
|
|
34
|
+
data-dce-id="11">
|
|
35
|
+
<dce-root xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml"
|
|
36
|
+
xmlns:dce="urn:schemas-epa-wg:dce" data-dce-id="1">
|
|
37
|
+
<span xmlns="" class="icon material-icons" data-dce-id="5">close</span>
|
|
38
|
+
</dce-root>
|
|
39
|
+
</cem-icon>
|
|
40
|
+
<dce-text data-dce-id="12">️
|
|
41
|
+
</dce-text>
|
|
42
|
+
</div>
|
|
7
43
|
</payload>
|
|
8
|
-
<attributes>
|
|
44
|
+
<attributes xmlns="" label="password sample" value="secret word" slice="password" data-dce-id="3" type="">
|
|
9
45
|
<xmlns/>
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
<
|
|
46
|
+
<label>password sample</label>
|
|
47
|
+
<value>secret word</value>
|
|
48
|
+
<slice>password</slice>
|
|
49
|
+
<data-dce-id>3</data-dce-id>
|
|
50
|
+
<type></type>
|
|
13
51
|
</attributes>
|
|
14
52
|
<dataset>
|
|
15
|
-
<dceId>
|
|
53
|
+
<dceId>3</dceId>
|
|
16
54
|
</dataset>
|
|
17
|
-
<slice
|
|
55
|
+
<slice>
|
|
56
|
+
<selected/>
|
|
57
|
+
<password image="close" class="clear" tabindex="0" slice="password" slice-event="click" slice-value="''"
|
|
58
|
+
data-dce-id="11">
|
|
59
|
+
<event isTrusted="true" sliceProcessed="1" pointerId="1" width="1" height="1" pressure="0" tiltX="0"
|
|
60
|
+
tiltY="0" azimuthAngle="0" altitudeAngle="1.5707963267948966" tangentialPressure="0" twist="0"
|
|
61
|
+
pointerType="mouse" isPrimary="false" persistentDeviceId="0" screenX="345" screenY="682"
|
|
62
|
+
clientX="345" clientY="595" ctrlKey="false" shiftKey="false" altKey="false" metaKey="false"
|
|
63
|
+
button="0" buttons="0" pageX="345" pageY="4354" x="345" y="595" offsetX="14" offsetY="15"
|
|
64
|
+
movementX="0" movementY="0" layerX="248" layerY="27" detail="1" which="1" type="click" eventPhase="3"
|
|
65
|
+
bubbles="true" cancelable="true" defaultPrevented="false" composed="true"
|
|
66
|
+
timeStamp="1974517.2999999523" returnValue="true" cancelBubble="false" NONE="0" CAPTURING_PHASE="1"
|
|
67
|
+
AT_TARGET="2" BUBBLING_PHASE="3">
|
|
68
|
+
<relatedTarget/>
|
|
69
|
+
<fromElement/>
|
|
70
|
+
<toElement/>
|
|
71
|
+
<sourceCapabilities firesTouchEvents="false"/>
|
|
72
|
+
</event>
|
|
73
|
+
</password>
|
|
74
|
+
</slice>
|
|
18
75
|
</datadom>
|
package/demo/s.xslt
CHANGED
|
@@ -1,20 +1,93 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml"
|
|
2
|
+
xmlns:dce="urn:schemas-epa-wg:dce" xmlns:exsl="http://exslt.org/common" version="1.0"
|
|
3
|
+
exclude-result-prefixes="exsl">
|
|
3
4
|
<xsl:template match="ignore">
|
|
4
5
|
<xsl:choose>
|
|
5
|
-
<xsl:when test="//attr"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
</xsl:choose
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
<xsl:
|
|
17
|
-
|
|
6
|
+
<xsl:when test="//attr">
|
|
7
|
+
<xsl:value-of select="//attr"/>
|
|
8
|
+
</xsl:when>
|
|
9
|
+
<xsl:otherwise>
|
|
10
|
+
<xsl:value-of select="def"/>
|
|
11
|
+
</xsl:otherwise>
|
|
12
|
+
</xsl:choose>
|
|
13
|
+
<xsl:value-of select="."/>
|
|
14
|
+
</xsl:template>
|
|
15
|
+
<xsl:template mode="payload" match="attributes">
|
|
16
|
+
<xsl:param name="v">
|
|
17
|
+
<xsl:choose>
|
|
18
|
+
<xsl:when test="//s[//s/event] ">
|
|
19
|
+
<xsl:value-of select="//s[//s/event] "/>
|
|
20
|
+
</xsl:when>
|
|
21
|
+
<xsl:when test=" //attributes/@v ">
|
|
22
|
+
<xsl:value-of select=" //attributes/@v "/>
|
|
23
|
+
</xsl:when>
|
|
24
|
+
<xsl:otherwise>
|
|
25
|
+
<xsl:value-of select=" 'def' "/>
|
|
26
|
+
</xsl:otherwise>
|
|
27
|
+
</xsl:choose>
|
|
28
|
+
</xsl:param>
|
|
29
|
+
<dce-root xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml" data-dce-id="1">
|
|
30
|
+
<xsl:variable xmlns:xsl="http://www.w3.org/1999/XSL/Transform" name="has-input"
|
|
31
|
+
select="count(//s/*) > 0"/>
|
|
32
|
+
<xsl:attribute name="v">
|
|
33
|
+
<xsl:choose>
|
|
34
|
+
<xsl:when test="//s[//s/event] ">
|
|
35
|
+
<xsl:value-of select="//s[//s/event] "/>
|
|
36
|
+
</xsl:when>
|
|
37
|
+
<xsl:when test=" //attributes/@v ">
|
|
38
|
+
<xsl:value-of select=" //attributes/@v "/>
|
|
39
|
+
</xsl:when>
|
|
40
|
+
<xsl:otherwise>
|
|
41
|
+
<xsl:value-of select=" 'def' "/>
|
|
42
|
+
</xsl:otherwise>
|
|
43
|
+
</xsl:choose>
|
|
44
|
+
</xsl:attribute>
|
|
45
|
+
<dce-text xmlns="" data-dce-id="2">
|
|
46
|
+
|
|
47
|
+
//attributes/v='<xsl:value-of select="//attributes/v"/>'
|
|
48
|
+
</dce-text>
|
|
49
|
+
<br xmlns="" data-dce-id="3"/>
|
|
50
|
+
<dce-text xmlns="" data-dce-id="4">
|
|
51
|
+
//attributes/@v='<xsl:value-of select="//attributes/@v"/>'
|
|
52
|
+
</dce-text>
|
|
53
|
+
<br xmlns="" data-dce-id="5"/>
|
|
54
|
+
<dce-text xmlns="" data-dce-id="6">
|
|
55
|
+
$v='<xsl:value-of select="$v"/>'
|
|
56
|
+
</dce-text>
|
|
57
|
+
<br xmlns="" data-dce-id="7"/>
|
|
58
|
+
<dce-text xmlns="" data-dce-id="8">
|
|
59
|
+
//s='<xsl:value-of select="//s"/>'
|
|
60
|
+
</dce-text>
|
|
61
|
+
<br xmlns="" data-dce-id="9"/>
|
|
62
|
+
<dce-text xmlns="" data-dce-id="10">
|
|
63
|
+
A='<xsl:value-of select="//s[//s/event] | //attributes/v[not(//s/event)]"/>'
|
|
64
|
+
</dce-text>
|
|
65
|
+
<br xmlns="" data-dce-id="11"/>
|
|
66
|
+
<dce-text xmlns="" data-dce-id="12">
|
|
67
|
+
has-input =<xsl:value-of select=" $has-input "/>
|
|
68
|
+
</dce-text>
|
|
69
|
+
<br xmlns="" data-dce-id="13"/>
|
|
70
|
+
<xsl:variable xmlns:xsl="http://www.w3.org/1999/XSL/Transform" name="in-value">
|
|
71
|
+
<xsl:choose>
|
|
72
|
+
<xsl:when test="//s/event">
|
|
73
|
+
<xsl:value-of select="//s">
|
|
74
|
+
</xsl:value-of>
|
|
75
|
+
</xsl:when>
|
|
76
|
+
<xsl:when test="//attributes/@v">
|
|
77
|
+
<xsl:value-of select="//attributes/@v">
|
|
78
|
+
</xsl:value-of>
|
|
79
|
+
</xsl:when>
|
|
80
|
+
<xsl:otherwise>def</xsl:otherwise>
|
|
81
|
+
</xsl:choose>
|
|
82
|
+
</xsl:variable>
|
|
83
|
+
<xsl:variable xmlns:xsl="http://www.w3.org/1999/XSL/Transform" name="xx"
|
|
84
|
+
select="//s[//s/event] ?? //attributes/@v ?? 'def' "/>
|
|
85
|
+
<input xmlns="" slice="s" slice-event="input" value="{$in-value}" data-dce-id="14"/>
|
|
86
|
+
<dce-text xmlns="" data-dce-id="15">$in-value:<xsl:value-of select="$in-value"/> | $xx:<xsl:value-of
|
|
87
|
+
select="$xx"/>
|
|
88
|
+
</dce-text>
|
|
89
|
+
</dce-root>
|
|
90
|
+
</xsl:template>
|
|
18
91
|
<xsl:template match="/">
|
|
19
92
|
<xsl:apply-templates mode="payload" select="/datadom/attributes"/>
|
|
20
93
|
</xsl:template>
|
|
@@ -23,7 +96,7 @@
|
|
|
23
96
|
<xsl:param name="defaultvalue"/>
|
|
24
97
|
<xsl:choose>
|
|
25
98
|
<xsl:when test="//payload/*[@slot=$slotname]">
|
|
26
|
-
|
|
99
|
+
<xsl:copy-of select="//payload/*[@slot=$slotname]"/>
|
|
27
100
|
</xsl:when>
|
|
28
101
|
<xsl:otherwise>
|
|
29
102
|
<xsl:copy-of select="$defaultvalue"/>
|
package/demo/ss.html
CHANGED
|
@@ -1,24 +1,14 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<dce-root data-dce-id="1"
|
|
3
|
-
xmlns="http://www.w3.org/1999/xhtml"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
<
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
</
|
|
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>
|
|
2
|
+
<dce-root data-dce-id="1" xmlns="http://www.w3.org/1999/xhtml" xmlns:dce="urn:schemas-epa-wg:dce"
|
|
3
|
+
xmlns:xhtml="http://www.w3.org/1999/xhtml"><label data-dce-id="2" xmlns=""><input data-dce-id="3"
|
|
4
|
+
slice="disabled-slice"
|
|
5
|
+
type="checkbox"
|
|
6
|
+
value="disabled"/>
|
|
7
|
+
<dce-text data-dce-id="4">disabled</dce-text>
|
|
8
|
+
</label>
|
|
9
|
+
<cem-input data-dce-id="6" value="123"/>
|
|
10
|
+
<button data-dce-id="7">Next</button>
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
</p></form>
|
|
24
14
|
</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.
|
package/ide/web-types-dce.json
CHANGED
package/ide/web-types-xsl.json
CHANGED
package/index.html
CHANGED
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
<a href="./demo/module-url.html" >importmap </a> <br/>
|
|
52
52
|
<a href="./demo/hex-grid.html" >hex grid lib </a> |
|
|
53
53
|
<a href="./demo/scoped-css.html" >scoped CSS </a> |
|
|
54
|
-
<a href="./demo/
|
|
54
|
+
<a href="./demo/attributes.html" >attributes </a> |
|
|
55
55
|
<a href="./demo/data-slices.html" >data slices/events </a> |
|
|
56
56
|
<a href="./demo/form.html" >Form validation </a> |
|
|
57
57
|
<a href="./demo/dom-merge.html" >DOM merge on dynamic update </a><br/>
|
package/package.json
CHANGED
package/demo/parameters.html
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
|
3
|
-
xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
|
4
|
-
<head>
|
|
5
|
-
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
|
6
|
-
<title>parameters - 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
|
-
<style>
|
|
11
|
-
@import "./demo.css";
|
|
12
|
-
|
|
13
|
-
button{ background: forestgreen; }
|
|
14
|
-
table{ min-width: 16rem; }
|
|
15
|
-
td{ border-bottom: 1px solid silver; }
|
|
16
|
-
tfoot td{ border-bottom: none; }
|
|
17
|
-
td,th{text-align: right; }
|
|
18
|
-
caption{ padding: 1rem; font-weight: bolder; font-family: sans-serif; }
|
|
19
|
-
</style>
|
|
20
|
-
</head>
|
|
21
|
-
<body>
|
|
22
|
-
<nav>
|
|
23
|
-
<a href="../index.html"><h3><code>custom-element</code> demo</h3></a>
|
|
24
|
-
</nav>
|
|
25
|
-
<html-demo-element legend="param as attributes definition" description="
|
|
26
|
-
params needed to declare DCE attributes and track the attributes changes. It also be used by IDE and validation.
|
|
27
|
-
">
|
|
28
|
-
<template>
|
|
29
|
-
<custom-element tag="dce-link" hidden>
|
|
30
|
-
<attribute name="p1" >default_P1 </attribute>
|
|
31
|
-
<attribute name="p2" select="'always_p2'" ></attribute>
|
|
32
|
-
<attribute name="p3" select="//p3 ?? 'def_P3' " ></attribute>
|
|
33
|
-
p1:{$p1} <br/> p2: {$p2} <br/> p3: {$p3}
|
|
34
|
-
</custom-element>
|
|
35
|
-
<dce-link id="dce1" ></dce-link>
|
|
36
|
-
<section>
|
|
37
|
-
<dce-link id="dce2" p1="123" p2="override ignored as select is defined"></dce-link> <br/>
|
|
38
|
-
<div><input id="i1" value="p1" /> <button onclick="dce2.setAttribute('p1',i1.value)"> set p1</button> </div>
|
|
39
|
-
<div><input id="i2" value="p2" /> <button onclick="dce2.setAttribute('p2',i2.value)"> set p2</button> </div>
|
|
40
|
-
<div><input id="i3" value="p3" /> <button onclick="dce2.setAttribute('p3',i3.value)"> set p3</button> </div>
|
|
41
|
-
</section>
|
|
42
|
-
<dce-link id="dce3" p1="123" p3="qwe"></dce-link> |
|
|
43
|
-
|
|
44
|
-
</template>
|
|
45
|
-
</html-demo-element>
|
|
46
|
-
|
|
47
|
-
<html-demo-element legend="slice propagates attribute" description="
|
|
48
|
-
when slice value points to attribute, it would be populated on slice change
|
|
49
|
-
">
|
|
50
|
-
Type in the input field to see the variable $title change. <br/>
|
|
51
|
-
Hover the mouse to see the title attribute text popup.<br/>
|
|
52
|
-
Inspect DCE node in dev tools to see `title` attribute updated while typing.
|
|
53
|
-
|
|
54
|
-
<template>
|
|
55
|
-
<custom-element>
|
|
56
|
-
<template>
|
|
57
|
-
<attribute name="title" select="//title ?? '😃'" ></attribute>
|
|
58
|
-
<input slice="/datadom/attributes/title" slice-event="keyup"/>
|
|
59
|
-
title attribute: {$title}
|
|
60
|
-
</template>
|
|
61
|
-
</custom-element>
|
|
62
|
-
</template>
|
|
63
|
-
</html-demo-element>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
<script type="module" src="https://unpkg.com/html-demo-element@1/html-demo-element.js"></script>
|
|
68
|
-
|
|
69
|
-
</body>
|
|
70
|
-
</html>
|
|
File without changes
|