Skip to content

Commit

Permalink
Merge pull request #4 from sashafirsov/develop
Browse files Browse the repository at this point in the history
rev 1.0.2
  • Loading branch information
sashafirsov authored Mar 15, 2021
2 parents 4fb1d8b + de7b8ca commit a1aa1a7
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 40 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.idea
package-lock.json

# Logs
logs
Expand Down
34 changes: 24 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ are covering the typical UI tasks:
3. control UI parts(slots) depending on fetch state.

[![git](https://cdnjs.cloudflare.com/ajax/libs/octicons/8.5.0/svg/mark-github.svg) GitHub](https://github.com/sashafirsov/slotted-element)
| Demo: [slotted-element](https://unpkg.com/[email protected].1/demo/index.html)
, [fetch-element JSON as table](https://unpkg.com/[email protected].1/demo/render-from-json.html)
| Demo: [slotted-element](https://unpkg.com/[email protected].2/demo/index.html)
, [fetch-element JSON as table](https://unpkg.com/[email protected].2/demo/render-from-json.html)
| [tests project](https://github.com/sashafirsov/slotted-element-test)

[![NPM version][npm-image]][npm-url]
Expand All @@ -23,7 +23,7 @@ the `slotted-element` is derived from `fetch-element`.
2. Import into page/module either by ES6 import or simple SCRIPT tag
3. In page body add ```<fetch-element src="url/to/some.html"></fetch-element>``` or

```
```html
<slotted-element src="url/to/some.json">
<i slot="loading"> Loading... please wait. </i>
<i slot="errror"> System error, please try again. </i>
Expand All @@ -36,19 +36,33 @@ the `slotted-element` is derived from `fetch-element`.
![screenshot][screenshot]

# slotted-element
Gives ability to use slots **without shadowDOM** and exposes API to work with slots programmatically by adding and
removing slots by slot name.
Gives ability to use slots
**without [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM)**
and exposes API to work with slots programmatically by adding and removing slots by slot name.

Coupled with `fetch-element` it provides UI management for each stage of data fetch and transforming to UI.
Coupled with `fetch-element`, it provides UI management for each stage of data fetch and UI transformation.

The slots concept is described in
[using slots in MDN](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slots#adding_flexibility_with_slots)

Originally it works in conjunction with shadowDOM when slots defined in content of element and referenced in
rendered shadowDOM by name. I.e. rendered DOM defines which slot and where will be displayed inside of web component.
Originally slots are designed to work in conjunction with template and shadowDOM when slot values are defined in content of
element and referenced in rendered shadowDOM by name. I.e. template DOM defines which slot and where will be displayed
inside of web component.

## Template vs inline HTML
If the `template` is defined as getter method, property, or attribute then

slotted-element simulates the usual ShadowDOM slots handling by template cloning into local DOM and
placing the slots from inner DOM into template clone. Unlike in ShadowDOM this is less efficient as template DOM
is not reused and inner DOM has to be re-build.

Without `template` property defined the inner content is uses as template:

inner DOM is shown except of elements with slot attribute. It is up to application code to trigger the visibility
of particular slots. `embed-page` activates slots for fetch-element handling when `src` attribute is set.

`slotted-element` gives ability to manipulate slots programmatically without engaging
[Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
Using inline HTML is handy in CMS or publishing systems when content is authored by editors rather than developers.
It is more performant than separate template as there is no content cloning involved.

## API
* `slotsInit()` read slots from internal DOM to be cloned later by
Expand Down
16 changes: 6 additions & 10 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@

<h1> slotted-element demo </h1>
<a href="https://github.com/sashafirsov/slotted-element"><img src="https://cdnjs.cloudflare.com/ajax/libs/octicons/8.5.0/svg/mark-github.svg">git</a> |
<a href="render-from-json.html"> fetch-element demo </a>
<a href="render-from-json.html"> fetch-element demo </a> |
<a href="template-demo.html"> template demo </a>
<p>
<var>slotted-element</var> is web component implementing slots without shadow DOM.
It is based on <var>fetch-element</var> component to fetch data and render from retrieved JSON or HTML.
Expand All @@ -26,10 +27,10 @@ <h1> slotted-element demo </h1>
</slotted-element>
</html-demo-element>

<html-demo-element>
<html-demo-element title="Content would be placed inside of 'loaded' slot ">
<slotted-element src="embedded.html">
<fieldset>
<legend> Content would be placed inside </legend>
<legend> Slots located in wrapper </legend>
<p slot="loading" hidden> Loading... ⏳ </p>
<p slot="error" hidden> What could be wrong? </p>
<p slot="loaded" hidden> Replaced with content of <b>embedded.html</b> </p>
Expand All @@ -46,13 +47,8 @@ <h1> slotted-element demo </h1>
</slotted-element>
</html-demo-element>

<html-demo-element>
<slotted-element src="dwarfs.json">
<fieldset>
<legend> array <b>dwarfs.json</b> as table </legend>
<p slot="loaded" hidden> </p>
</fieldset>
</slotted-element>
<html-demo-element title="array dwarfs.json as table">
<slotted-element src="dwarfs.json"></slotted-element>
</html-demo-element>

<script type="module" src="https://unpkg.com/[email protected]/html-demo-element.js"></script>
Expand Down
11 changes: 7 additions & 4 deletions demo/render-from-json.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<title>render from json - FetchElement</title>
<title>render table from json - FetchElement</title>
<script type="module" src="https://unpkg.com/[email protected]/html-demo-element.js"></script>
</head>
<body>
<script type="module" src="https://unpkg.com/[email protected]/html-demo-element.js"></script>
<h1> fetch-element render from JSON demo </h1>
<a href="https://github.com/sashafirsov/slotted-element"><img src="https://cdnjs.cloudflare.com/ajax/libs/octicons/8.5.0/svg/mark-github.svg">git</a> |
<a href="index.html"> slotted-element demo </a>

<html-demo-element title="Custom renderer">
<json-render-element src="doc.json"></json-render-element>
Expand All @@ -17,8 +20,8 @@
render(json)
{
return `<h1>${ json.name }</h1>
<img src="${ json.portrait }" alt="" />
`;
<img src="${ json.portrait }" alt="" />
`;
}
});
</script>
Expand Down
90 changes: 90 additions & 0 deletions demo/template-demo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<!doctype html>
<html lang="en-GB">
<head>
<meta charset="utf-8">
<style>

</style>
<script type="module" src="https://unpkg.com/[email protected]/html-demo-element.js"></script>
<script type="module" src="../slotted-element.js"></script>

</head>
<body>

<h1> slotted-element with template demo </h1>
<a href="https://github.com/sashafirsov/slotted-element"><img src="https://cdnjs.cloudflare.com/ajax/libs/octicons/8.5.0/svg/mark-github.svg">git</a> |
<a href="index.html"> slotted-element demo </a>
<p>
<var>slotted-element</var> is web component implementing slots without shadow DOM. There are 3 ways of template use:
</p>
<html-demo-element title="1. Inline HTML">
<slotted-element>
<h6>inline HTML with slots 🎉</h6>
<p slot="loading" > Not triggered as "src" attribute is not set. </p>
</slotted-element>
</html-demo-element>

<html-demo-element title="2. Template defined by ID">
<template id="template-with-slots">
<h6>template HTML with slots 🥳</h6>
<slot name="slot0" hidden>
Slots are hidden in template when "hidden" attribute is set.
Could be shown by slotsAdd() method.
</slot>
<slot name="slot1"> slot1 is visible, hide by setting "hidden" attribute </slot>
<slot name="slot3"></slot>
</template>
<slotted-element template="template-with-slots"></slotted-element>
</html-demo-element>


<html-demo-element title="2a. Template defined by ID, slots redefined in body.">
<template id="template-with-slots2">
<h6>template by ID, inline HTML redefines slots 🥳</h6>
<slot name="slot0">slot0 in template would be overridden. </slot>
<slot name="slot1">slot1 is defined in template. </slot>
</template>
<slotted-element template="template-with-slots2">
<p slot="slot0">slot0 is overridden in body!</p>
</slotted-element>
</html-demo-element>

<html-demo-element title="3. template defined as property getter">
<demo3-element>
<p slot="slot0">slot0 is overridden in body!</p>
</demo3-element>
<script type="module">
import SlottedElement from '../slotted-element.js';
window.customElements.define('demo3-element',
class Demo3Element extends SlottedElement
{
get template()
{
return `<h6>${this.nodeName}, inline HTML redefines slots 🥳</h6>
<slot name="slot0">slot0 in template would be overridden. </slot>
<slot name="slot1">slot1 is defined in template. </slot>
`;
}
});
</script>
</html-demo-element>


<html-demo-element title="3a. template defined as instance set property">
<slotted-element id="demo3a">
<p slot="slot0">slot0 is overridden in body!</p>
</slotted-element>
<script type="module">
window.addEventListener('load', () =>
{
demo3a.template = `<h6>template set by external JS on instance 🥳</h6>
<slot name="slot0">slot0 in template would be overridden. </slot>
<slot name="slot1">slot1 is defined in template. </slot>
`;
demo3a.slotsInit();
});
</script>
</html-demo-element>

</body>
</html>
8 changes: 7 additions & 1 deletion fetch-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,20 @@ FetchElement extends HTMLElement
} ) );
}

connectedCallback()
{
this.src && this.fetch( this.src );
this.initialized = !0;
}

attributeChangedCallback( name, oldValue, newValue )
{
switch( name )
{ case 'headers':
this[ name ] = eval( newValue );
break;
case 'src':
this.fetch( newValue );
this.initialized && this.fetch( newValue );
break;
default:
if( this[ name ] !== newValue )
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "slotted-element",
"version": "1.0.1",
"version": "1.0.2",
"description": "Web component fetch-element for ajax and render JSON as table and slotted-element without shadow DOM",
"author":
{ "name": "Sasha Firsov",
Expand Down
58 changes: 44 additions & 14 deletions slotted-element.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import FetchElement from './fetch-element.js';
function createNode( tag, prop, val ){ const el = document.createElement(tag); el[prop]=val; return el; }

export class
SlottedElement extends FetchElement
{
static get template()
{ return `
<div slot="loading">loading...</div>
<div slot="error">System error</div>
<div slot="loaded"></div>
` }
static get observedAttributes(){ return [ 'template', ...FetchElement.observedAttributes ]; }

constructor()
{ super();
this.slotsInit();
connectedCallback()
{ this.slotsInit();
super.connectedCallback();
}
attributeChangedCallback( name, oldValue, newValue )
{
if( name !== 'template')
return super.attributeChangedCallback( name, oldValue, newValue );
this.template = newValue;
this.initialized && this.slotsInit();
}

fetch( url, options )
Expand All @@ -36,9 +40,31 @@ SlottedElement extends FetchElement

slotsInit()
{
this.slots = {};
for( let slot of this.querySelectorAll( '[slot]' ) )
this.slots[ slot.slot ] = slot;
if( !this.slots )
{
this.slots = {};
for( let node of this.querySelectorAll( '[slot]' ) )
{
this.slots[ node.slot ] = node;
node.parentNode.replaceChild( createNode('slot', 'name', node.slot ), node );
}
}

if( this.template )
{ const nodeContent = n => n && (n.content || n);
let t = nodeContent(this.template.content) || nodeContent( document.getElementById( this.template ) );
if( !t )
t = createNode('template',"innerHTML", this.template).content;
this.innerHTML='';
this.appendChild( t.cloneNode(true))
for( let s of this.querySelectorAll( 'slot' ) )
{ let slot = this.slots[ s.name ];
if( slot )
{ s.hidden = !0;
s.parentNode.insertBefore( slot.cloneNode( true ), s );
}
}
}
}

slotOnly( name )
Expand Down Expand Up @@ -71,10 +97,14 @@ SlottedElement extends FetchElement
slotAdd( node ) // name or node created by slotClone(name)
{
const slot = node.slot ? node: this.slotClone( node )
, ref = this.slots[ node.slot || node ];
return ref && ref.parentElement.insertBefore( slot, ref );
, ref = this.querySelectorAll(`slot[name="${node.slot || node}"]`);
let added;
for( let r of ref)
added = r.parentElement.insertBefore( slot, r );
return added;
}

}
export default SlottedElement;

window.customElements.define('slotted-element', SlottedElement);

0 comments on commit a1aa1a7

Please sign in to comment.