Skip to content

Commit

Permalink
Merge pull request #26 from EPA-WG/develop
Browse files Browse the repository at this point in the history
0.0.13
  • Loading branch information
sashafirsov authored Jan 20, 2024
2 parents 42d2b2a + 8df1a19 commit 94e7d44
Show file tree
Hide file tree
Showing 10 changed files with 513 additions and 15 deletions.
51 changes: 48 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ generates HTML
</pokemon-tile>
```

[![responsive hex grid demo][hex-grid-image] responsive hex-grid demo][hex-grid-url]
, look into sources for samples of CSS encapsulation and external template use.


# Implementation notes
## Life cycle
### `custom-element` declaration
Expand Down Expand Up @@ -126,6 +130,44 @@ allows to refer the template withing external document


# template syntax
[Scoped CSS][css-demo-url] live demo
## styles encapsulation
DCE can have the own styles which would be scoped to the instances.
In order to prevent the style leaking, it has to be defined withing `template` tag:
```html
<custom-element>
<template>
<style>
color:green;
button{ color: blue; }
</style>
<label> green <button>blue</button> </label>
</template>
</custom-element>
```
<fieldset>
<label style="color: green"> green <button style="color: blue">blue</button> </label>
</fieldset>

### override style for instance
In same way as in DCE itself:
```html
<custom-element tag="dce-2">
<template><!-- template needed to avoid styles leaking into global HTML -->
<style>
button{ border: 0.2rem dashed blue; }
</style>
<button><slot>Blue borders</slot></button>
</template>
</custom-element>
<dce-2>dashed blue</dce-2>
<dce-2>
<template> <!-- template needed to avoid styles leaking into global HTML -->
<style>button{border-color:red;}</style>
Red border
</template>
</dce-2>
```
## Attributes
curly braces `{}` in attributes implemented as [attribute value template](https://www.w3.org/TR/xslt20/#attribute-value-templates)

Expand Down Expand Up @@ -219,15 +261,18 @@ within template
[git-url]: https://github.com/EPA-WG/custom-element
[git-test-url]: https://github.com/EPA-WG/custom-element-test
[demo-url]: https://unpkg.com/@epa-wg/custom-element@0.0/index.html
[css-demo-url]: https://unpkg.com/@epa-wg/custom-element@0.0/demo/scoped-css.html
[hex-grid-url]: https://unpkg.com/@epa-wg/custom-element@0.0/demo/hex-grid.html
[hex-grid-image]: demo/hex-grid-transform.png
[local-storage-demo]: https://unpkg.com/@epa-wg/custom-element@0.0/demo/local-storage.html
[http-request-demo]: https://unpkg.com/@epa-wg/custom-element@0.0/demo/http-request.html
[location-demo]: https://unpkg.com/@epa-wg/custom-element@0.0/demo/location.html
[github-image]: https://cdnjs.cloudflare.com/ajax/libs/octicons/8.5.0/svg/mark-github.svg
[npm-image]: https://img.shields.io/npm/v/@epa-wg/custom-element.svg
[npm-url]: https://npmjs.org/package/@epa-wg/custom-element
[coverage-image]: https://unpkg.com/@epa-wg/custom-element-test@0.0.12/coverage/coverage.svg
[coverage-url]: https://unpkg.com/@epa-wg/custom-element-test@0.0.12/coverage/lcov-report/index.html
[storybook-url]: https://unpkg.com/@epa-wg/custom-element-test@0.0.12/storybook-static/index.html?path=/story/welcome--introduction
[coverage-image]: https://unpkg.com/@epa-wg/custom-element-test@0.0.13/coverage/coverage.svg
[coverage-url]: https://unpkg.com/@epa-wg/custom-element-test@0.0.13/coverage/lcov-report/index.html
[storybook-url]: https://unpkg.com/@epa-wg/custom-element-test@0.0.13/storybook-static/index.html?path=/story/welcome--introduction
[sandbox-url]: https://stackblitz.com/github/EPA-WG/custom-element?file=index.html
[webcomponents-url]: https://www.webcomponents.org/element/@epa-wg/custom-element
[webcomponents-img]: https://img.shields.io/badge/webcomponents.org-published-blue.svg
Expand Down
43 changes: 36 additions & 7 deletions custom-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -345,25 +345,54 @@ export function merge( parent, fromArr )
parent.append( e )
}
}

export function assureUID(n,attr)
{ if( !n.hasAttribute(attr) )
n.setAttribute(attr, crypto.randomUUID());
return n.getAttribute(attr)
}
export class
CustomElement extends HTMLElement
{
async connectedCallback()
{
const templateRoots = await loadTemplateRoots( attr( this, 'src' ), this )
, templateDocs = templateRoots.map( n => createXsltFromDom( n ) )
, tag = attr( this, 'tag' )
, tagName = tag ? tag : 'dce-'+crypto.randomUUID();

for( const t of templateRoots )
forEach$(t.templateNode||t.content||t, 'style',s=>{
const slot = s.closest('slot');
const sName = slot ? `slot[name="${slot.name}"]`:'';
s.innerHTML = `${tagName} ${sName}{${s.innerHTML}}`;
this.append(s);
})
const templateDocs = templateRoots.map( n => createXsltFromDom( n ) )
, xp = templateDocs.map( (td, p) =>{ p = new XSLTProcessor(); p.importStylesheet( td ); return p })

Object.defineProperty( this, "xsltString", { get: ()=>templateDocs.map( td => xmlString(td) ).join('\n') });

const tag = attr( this, 'tag' );
const dce = this;
const sliceNames = [...this.templateNode.querySelectorAll('[slice]')].map(e=>attr(e,'slice'));
class DceElement extends HTMLElement
{
connectedCallback()
{ const x = xml2dom( '<datadom/>' ).documentElement;
{ if( this.firstElementChild?.tagName === 'TEMPLATE' )
{ const t = this.firstElementChild;
for( const n of [...t.content.childNodes] )
if( n.localName === 'style' ){
const id = assureUID(this,'data-dce-style')
n.innerHTML= `${tagName}[data-dce-style="${id}"]{${n.innerHTML}}`;
t.insertAdjacentElement('beforebegin',n);
}else
if(n.nodeType===1)
t.insertAdjacentElement('beforebegin',n);
else if(n.nodeType===3)
t.insertAdjacentText('beforebegin',n.data);

t.remove();

}
const x = xml2dom( '<datadom/>' ).documentElement;
const createXmlNode = ( tag, t = '' ) => ( e =>
{ if( t )
e.append( createText( x, t ))
Expand Down Expand Up @@ -415,7 +444,7 @@ CustomElement extends HTMLElement
ff.map( f =>
{ if( !f )
return;
assureUnique(f)
assureUnique(f);
merge( this, f.childNodes )
})
const changeCb = el=>this.onSlice({ detail: el[attr(el,'slice-prop') || 'value'], target: el })
Expand All @@ -438,11 +467,11 @@ CustomElement extends HTMLElement
if(tag)
window.customElements.define( tag, DceElement);
else
{ const t = 'dce-'+crypto.randomUUID()
{ const t = tagName;
window.customElements.define( t, DceElement);
const el = document.createElement(t);
this.getAttributeNames().forEach(a=>el.setAttribute(a,this.getAttribute(a)));
el.append(...this.childNodes)
el.append(...[...this.childNodes].filter(e=>e.localName!=='style'))
this.append(el);
}
}
Expand Down
5 changes: 4 additions & 1 deletion demo/dom-merge.html
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,10 @@ <h3>DOM merge. DCE dynamic update with focus preservation.</h3>
<custom-element>
<form>
<label>
<input type="text" value="Type time update" slice="txt" slice-update="keyup"/>
<input type="text"
value="Type time update"
slice="txt"
slice-update="keyup"/>

<span> Character count:
<b> {string-length(//slice/txt)} </b>
Expand Down
183 changes: 183 additions & 0 deletions demo/hex-grid-dce.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
<!DOCTYPE html>
<html lang="en" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<script type="module" src="../custom-element.js"></script>

<style>
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@500&display=swap');
button{ font-family: 'Roboto', sans-serif; color: darkcyan;}
body{ background-color: #DDE2F1;}
</style>
</head>
<body>

<custom-element tag="hex-grid">
<template id="hex-grid-template">
<style>
section{ filter: drop-shadow(0px 2px 3px rgba(50, 50, 0, 0.5)) }
:root {
--hex-grid-size: 8rem;
}

nav {
--hex-grid-size: 8rem;
display: flex;
flex-wrap: wrap;
flex-direction: row;
justify-content: left;
margin-right: calc(var(--hex-grid-size)/2);
padding-bottom: calc(var(--hex-grid-size,8rem));
gap: 0.1rem;
section{
width: var(--hex-grid-size);
height: calc(1.53 *var(--hex-grid-size))
}
button {
display: flex;
flex-direction: column; align-items: center;
justify-content: center;
border: none;
width: var(--hex-grid-size,8rem);
min-width: var(--hex-grid-size,8rem);
height: var(--hex-grid-size,8rem);
padding: calc( var(--hex-grid-size,8rem)/8);
clip-path: polygon(50% 0, 100% 25%,100% 75%, 50% 100%, 0 75%, 0 25%);
&:hover,&:focus,&.focus{
transform: scale3d(0.9,0.9,0.9) ;
background-color: mediumaquamarine;
color: white;
}

img{ height: calc( var(--hex-grid-size,8rem)/2);}
}


section:nth-child(even){
width:0;
}
section:nth-child(even){
position: relative;
top: 50%;
button{
position: absolute;

top: calc(0.77*var(--hex-grid-size));
right: calc( -1* var(--hex-grid-size,8rem)/2);
}
}
}
</style>
<nav>
<xsl:for-each select="/datadom/payload/xhtml:*">
<xsl:if test="local-name(.) = 'style'">
<xsl:copy-of select="." />
</xsl:if>
<!-- sanitize blank spans -->
<xsl:if test="local-name(.) != 'style' and (local-name(.) != 'span' or normalize-space(.) != '')">
<section>
<button class="action">
{@alt}
<xsl:copy-of select="."/>
</button>
</section>
</xsl:if>
</xsl:for-each>
</nav>
</template>
</custom-element>

<!--<hex-grid>-->
<!-- <img src="wc-square.svg" alt="web component"/>-->
<!-- <img src="wc-square.svg" alt="123"/>-->
<!-- <span>Hey!</span>-->
<!--</hex-grid>-->
<hex-grid class="grid2x">
<img src="wc-square.svg" alt="DCE" href="https://github.com/EPA-WG/custom-element"/>
<img src="https://upload.wikimedia.org/wikipedia/commons/a/a7/React-icon.svg" alt="React" href="https://react.dev/"/>
<img src="https://angularjs.org/favicon.ico" alt="Angular" href="https://angularjs.org/"/>
<img src="https://semantic-ui.com/images/logo.png" alt="Semantic UI" href="https://semantic-ui.com/"/>
<img src="https://open-wc.org/35ded306.svg" alt="Open WC" href="https://open-wc.org/"/>
<img src="https://storage.googleapis.com/cms-storage-bucket/4fd0db61df0567c0f352.png" alt="Flutter" href="https://flutter.dev/"/>
<img src="https://refine.dev/img/refine_favicon.svg" alt="Refine" href="https://refine.dev/"/>
<img src="https://getbootstrap.com/docs/5.3/assets/brand/bootstrap-logo-shadow.png" alt="Bootstrap" href="https://getbootstrap.com/"/>
<img src="https://upload.wikimedia.org/wikipedia/commons/9/95/Vue.js_Logo_2.svg" alt="Vue.JS" href="https://vuejs.org/"/>
<img src="https://lit.dev/images/logo.svg#flame" alt="Lit"/>
<img src="https://redux.js.org/img/redux.svg" alt="Redux"/>
<img src="https://upload.wikimedia.org/wikipedia/commons/1/1b/Svelte_Logo.svg" alt="Svelte"/>
<img src="https://www.solidjs.com/img/logo/without-wordmark/logo.svg" alt="SolidJS"/>
<img src="https://www.svgrepo.com/show/354113/nextjs-icon.svg" alt="NextJS"/>
<img src="https://global.discourse-cdn.com/standard17/uploads/threejs/original/2X/b/be2f75f72751c11cbe1593c69a99a52900bf12cb.svg" alt="ThreeJS" href="https://threejs.org/"/>
<img src="https://www.blazejs.org/logo/icon.png" alt="BlazeJS"/>
<img src="https://dmtgy0px4zdqn.cloudfront.net/images/integrations/tailwindcss.webp" alt="Tailwind CSS"/>
<img src="https://dmtgy0px4zdqn.cloudfront.net/images/integrations/flowbite.webp" alt="Flowbite"/>
<img src="https://dmtgy0px4zdqn.cloudfront.net/images/integrations/chakra-ui.webp" alt="Chakra-UI"/>
<img src="https://dmtgy0px4zdqn.cloudfront.net/images/integrations/graphql.webp" alt="GraphQL"/>
<img src="https://dmtgy0px4zdqn.cloudfront.net/images/cloud-icon.png" alt="Meteor" href="https://www.meteor.com/"/>
<img src="https://www.svgrepo.com/show/508923/jquery.svg" alt="jQuery"/>
<img src="https://www.svgrepo.com/show/330899/materialui.svg" alt="Material UI"/>
<img src="https://mithril.js.org/logo.svg" alt="Mithril.js" href="https://mithril.js.org/"/>
<img src="https://seeklogo.com/images/M/materialize-logo-0FCAD8A6F8-seeklogo.com.png" alt="Materialize"/>
<img src="https://seeklogo.com/images/P/preact-logo-64E4BF9ABC-seeklogo.com.png" alt="Preact"/>
<img src="https://seeklogo.com/images/D/dojo-logo-E24E2CA700-seeklogo.com.png" alt="Dojo"/>
<img src="https://uxwing.com/wp-content/themes/uxwing/download/brands-and-social-media/backbone-js-icon.svg" alt="Backbone.js"/>
<img src="https://d3js.org/logo.svg" alt="D3.js"/>
<img src="https://lodash.com/assets/img/lodash.svg" alt="Lodash"/>
<img src="https://www.chartjs.org/img/chartjs-logo.svg" alt="Chart.JS" href="https://www.chartjs.org/"/>
<img src="https://glimmerjs.com/images/favicon.png" alt="Glimmer" href="https://glimmerjs.com/"/>
<img src="https://sarcadass.github.io/granim.js/favicon.ico" alt="Granim.js" href="https://sarcadass.github.io/granim.js/"/>
<img src="https://leafletjs.com/docs/images/favicon.ico" alt="leaflet.js" href="https://leafletjs.com/"/>
<img src="https://animejs.com/documentation/assets/img/favicon.png" alt="Anime.JS"/>
<img src="https://www.algolia.com/algoliaweb-static-favicons/light-mode/apple-touch-icon.png" alt="Algolia"/>
<img src="https://momentjs.com/static/img/moment-favicon.png" alt="Moment" href="https://momentjs.com"/>
<img src="https://omniscientjs.github.io/assets/favicon.ico" alt="Omniscient" href="https://omniscientjs.github.io"/>
<img src="https://aurelia.io/styles/images/aurelia-icon.svg" alt="Aurelia" href="https://aurelia.io"/>
<img src="https://emberjs.com/favicons/icon.svg" alt="EmberJS" href="https://emberjs.com"/>
<img src="https://ionicframework.com/apple-icon-57x57.png" alt="Ionic" href="https://ionicframework.com/"/>
<img src="https://webix.com/assets/favicons/favicon-96x96.png" alt="Webix" href="https://webix.com/"/>
<img src="https://images.ctfassets.net/vkdbses00qqt/3Id5VwofmvaFbjuSumaOmM/592c2d2e523187bd054a16b358d5a7ec/framework.svg" alt="Gatsby" href="https://www.gatsbyjs.com/"/>
</hex-grid>

<custom-element tag="hex-row">
<template>
<style>
hex-grid {
nav {
--hex-grid-size:6rem;
display: inline-flex;
padding-bottom: 0;
margin-right: 0;
section{ margin-right: 0.2rem; height: var(--hex-grid-size);}
button:has([selected]){ background-color: #2d4554; color: burlywood; }
}
}
</style>
<xsl:for-each select="/datadom/payload/xhtml:*">
<!-- sanitize blank spans -->
<xsl:if test="local-name(.) != 'span' or normalize-space(.) != ''">
<hex-grid>
<xsl:copy-of select="."/>
</hex-grid>
</xsl:if>
</xsl:for-each>
</template>
</custom-element>
<hex-row class="grid2x">
<img src="https://dmtgy0px4zdqn.cloudfront.net/images/integrations/chakra-ui.webp" alt="Chakra-UI"/>
<img src="https://dmtgy0px4zdqn.cloudfront.net/images/integrations/graphql.webp" alt="GraphQL"/>
<img src="https://dmtgy0px4zdqn.cloudfront.net/images/cloud-icon.png" alt="Meteor"/>
<img src="https://www.svgrepo.com/show/508923/jquery.svg" alt="jQuery" selected/>
<img src="https://www.svgrepo.com/show/330899/materialui.svg" alt="Material UI"/>
<img src="https://mithril.js.org/logo.svg" alt="Mithril.js"/>
<img src="https://uxwing.com/wp-content/themes/uxwing/download/brands-and-social-media/backbone-js-icon.svg" alt="Backbone.js" href="https://backbonejs.org/"/>
<img src="https://ionicframework.com/apple-icon-57x57.png" alt="Ionic" href="https://ionicframework.com/"/>
</hex-row>



<style>
.grid2x{--hex-grid-size:16rem; margin-top: 8rem; }
</style>

</body>
</html>
Binary file added demo/hex-grid-transform.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 94e7d44

Please sign in to comment.