data-tier-list
Advanced tools
Comparing version 1.2.3 to 2.0.0
@@ -5,3 +5,2 @@ import { ties, Observable } from 'data-tier'; | ||
DATA_TIER_LIST = 'data-tier-list', | ||
SELF_TEMPLATE = `<slot id="template"></slot>`, | ||
ITEM_KEY = Symbol('item.key'), | ||
@@ -23,5 +22,2 @@ ITEMS_KEY = Symbol('items.key'), | ||
this[OBSERVER_KEY] = this[OBSERVER_KEY].bind(this); | ||
this.attachShadow({ mode: 'open' }).innerHTML = SELF_TEMPLATE; | ||
// TODO: replace the below one with MutationObserver | ||
this.shadowRoot.querySelector('#template').addEventListener('slotchange', () => this[TEMPLATE_PROCESSOR_KEY]()); | ||
} | ||
@@ -31,7 +27,17 @@ | ||
this.hidden = true; | ||
this.templateObserver = new MutationObserver(() => { | ||
this.templateObserver.takeRecords(); | ||
this[TEMPLATE_PROCESSOR_KEY](); | ||
}); | ||
this.templateObserver.observe(this, { | ||
subtree: true, | ||
childList: true, | ||
attributes: true, | ||
characterData: true | ||
}); | ||
this[TEMPLATE_PROCESSOR_KEY](); | ||
} | ||
get defaultTieTarget() { | ||
return 'items'; | ||
disconnectedCallback() { | ||
this.templateObserver.disconnect(); | ||
} | ||
@@ -82,25 +88,31 @@ | ||
[TEMPLATE_PROCESSOR_KEY]() { | ||
let newTemplate = null; | ||
const templateNodes = this.children; | ||
let newTemplate = null; | ||
if (templateNodes.length === 1) { | ||
newTemplate = templateNodes[0].outerHTML; | ||
} else { | ||
console.error(`list item template MAY have 1 root element only, got ${templateNodes.length}`); | ||
return; | ||
} | ||
if (templateNodes[0] === this.__currentTemplate) { | ||
return; | ||
if (templateNodes.length > 1) { | ||
console.error(`list item template MAY have 1 root element only, got ${templateNodes.length}; working with the first one`); | ||
} | ||
if (templateNodes.length > 0) { | ||
newTemplate = templateNodes[0]; | ||
} | ||
// scoping the template self to prevent shadowing | ||
if (this.__currentTemplate) { | ||
ties.remove(this.__currentTemplate); | ||
let processedTemplate = null; | ||
if (newTemplate) { | ||
const matcher = /(?<= data-tie=["']{1}.*\b)item(?=\b)/g; | ||
const origin = newTemplate.outerHTML; | ||
let r; | ||
let li = 0; | ||
processedTemplate = ''; | ||
do { | ||
r = matcher.exec(origin); | ||
if (r) { | ||
processedTemplate += origin.substring(li, r.index) + 'scope'; | ||
li = r.index + 4; | ||
} else { | ||
processedTemplate += origin.substring(li); | ||
} | ||
} while (r); | ||
} | ||
ties.create(templateNodes[0]); | ||
this.__currentTemplate = templateNodes[0]; | ||
// TODO: any preprocessing/optimisations go here | ||
this[TEMPLATE_KEY] = newTemplate; | ||
this[FULL_UPDATER_KEY](); | ||
this[TEMPLATE_KEY] = processedTemplate; | ||
this[FULL_UPDATER_KEY](true); | ||
} | ||
@@ -118,3 +130,3 @@ | ||
[FULL_UPDATER_KEY]() { | ||
[FULL_UPDATER_KEY](forceReplaceAll = false) { | ||
if (!this[TEMPLATE_KEY] || !this.items) { | ||
@@ -136,5 +148,7 @@ return; | ||
while (llc > desiredListLength) { | ||
while (llc > (forceReplaceAll ? 0 : desiredListLength)) { | ||
lastElementChild = targetContainer.lastElementChild; | ||
if (lastElementChild !== this) { | ||
ties.remove(lastElementChild); | ||
lastElementChild.remove(); | ||
@@ -180,3 +194,3 @@ } | ||
t.innerHTML = this[TEMPLATE_KEY]; | ||
ties.create(t.content.firstElementChild, change.value); | ||
ties.update(t.content.firstElementChild, change.value); | ||
targetContainer.insertBefore(t.content, targetContainer.children[affectedIndex + inParentAdjust]); | ||
@@ -199,3 +213,3 @@ } else if (change.type === 'update') { | ||
t.innerHTML = this[TEMPLATE_KEY]; | ||
ties.create(t.content.firstElementChild, change.value); | ||
ties.update(t.content.firstElementChild, change.value); | ||
// TODO: add test that breaks and fix it for object | ||
@@ -202,0 +216,0 @@ targetContainer.insertBefore(t.content, targetContainer.children[change.path[0] + inParentAdjust]); |
@@ -1,1 +0,1 @@ | ||
import{ties,Observable}from"data-tier";const DATA_TIER_LIST="data-tier-list",SELF_TEMPLATE='<slot id="template"></slot>',ITEM_KEY=Symbol("item.key"),ITEMS_KEY=Symbol("items.key"),TEMPLATE_KEY=Symbol("template"),OBSERVER_KEY=Symbol("observer"),FULL_UPDATER_KEY=Symbol("full.updater"),PART_UPDATER_KEY=Symbol("part.updater"),CONTAINER_RESOLVER_KEY=Symbol("container.resolver"),TEMPLATE_PROCESSOR_KEY=Symbol("template.processor");class DataTierList extends HTMLElement{constructor(){super(),this[ITEMS_KEY]=null,this[TEMPLATE_KEY]=null,this[OBSERVER_KEY]=this[OBSERVER_KEY].bind(this),this.attachShadow({mode:"open"}).innerHTML=SELF_TEMPLATE,this.shadowRoot.querySelector("#template").addEventListener("slotchange",()=>this[TEMPLATE_PROCESSOR_KEY]())}connectedCallback(){this.hidden=!0,this[TEMPLATE_PROCESSOR_KEY]()}get defaultTieTarget(){return"items"}set items(e){this[ITEMS_KEY]!==e&&(null===e||""===e?this[ITEMS_KEY]&&(this[ITEMS_KEY].unobserve(this[OBSERVER_KEY]),this[ITEMS_KEY]=null,this[FULL_UPDATER_KEY]()):"object"==typeof e?(this[ITEMS_KEY]&&this[ITEMS_KEY].unobserve(this[OBSERVER_KEY]),this[ITEMS_KEY]=Observable.from(e),this[ITEMS_KEY].observe(this[OBSERVER_KEY],{pathsOf:""}),this[FULL_UPDATER_KEY]()):console.error(`items MAY ONLY be set to an object, got '${e}'`))}get items(){return this[ITEMS_KEY]}[CONTAINER_RESOLVER_KEY](){let e;const t=this.getAttribute("data-list-target");if(t){if(!(e=this.getRootNode().querySelector(t)))throw new Error(`failed to resolve target container by the given query '${t}'`)}else e=this.parentNode;return e}[TEMPLATE_PROCESSOR_KEY](){const e=this.children;let t=null;1===e.length?(t=e[0].outerHTML,e[0]!==this.__currentTemplate&&(this.__currentTemplate&&ties.remove(this.__currentTemplate),ties.create(e[0]),this.__currentTemplate=e[0],this[TEMPLATE_KEY]=t,this[FULL_UPDATER_KEY]())):console.error(`list item template MAY have 1 root element only, got ${e.length}`)}[OBSERVER_KEY](e){for(const t of e)"shuffle"===t.type||"reverse"===t.type?this[FULL_UPDATER_KEY]():this[PART_UPDATER_KEY](t)}[FULL_UPDATER_KEY](){if(!this[TEMPLATE_KEY]||!this.items)return;const e=this[CONTAINER_RESOLVER_KEY](),t=e.contains(this)?1:0,i=Object.keys(this.items),s=i.length;let E,r=e.childElementCount-t;for(;r>s;)(E=e.lastElementChild)!==this&&E.remove(),r--;let n="";for(;r<s;)n+=this[TEMPLATE_KEY],r++;if(n){const t=document.createElement("template");t.innerHTML=n,e.appendChild(t.content)}for(let s=t,E=e.children.length;s<E;s++){const E=e.children[s];E[ITEM_KEY]=i[s-t],ties.update(E,this.items[i[s-t]])}}[PART_UPDATER_KEY](e){if(!this[TEMPLATE_KEY]||!this.items)return;const t=this[CONTAINER_RESOLVER_KEY](),i=t.contains(this)?1:0,s=document.createElement("template");if(!(e.path.length>1))if(Array.isArray(this.items)){const n=parseInt(e.path[0],10);if("insert"===e.type)s.innerHTML=this[TEMPLATE_KEY],ties.create(s.content.firstElementChild,e.value),t.insertBefore(s.content,t.children[n+i]);else if("update"===e.type)ties.update(t.children[n+i],e.value);else if("delete"===e.type)t.removeChild(t.children[n+i]);else if("reverse"===e.type)for(var E=i+1,r=t.children.length-i/2;E<r;E++)t.insertBefore(t.children[E],t.children[E-1]);else console.warn(`unsupported change type ${e.type}`)}else{const E=Object.keys(this.items).indexOf(e.path[0]);if("insert"===e.type)s.innerHTML=this[TEMPLATE_KEY],ties.create(s.content.firstElementChild,e.value),t.insertBefore(s.content,t.children[e.path[0]+i]);else if("update"===e.type)ties.update(t.children[E+i],e.value);else if("delete"===e.type){for(const i of t.children)if(i[ITEM_KEY]===e.path[0]){i.remove();break}}else console.warn(`unsupported change type ${e.type}`)}}}customElements.get(DATA_TIER_LIST)?console.warn("'data-tier-list' is already defined in this environment, won't redefine"):customElements.define(DATA_TIER_LIST,DataTierList); | ||
import{ties,Observable}from"data-tier";const DATA_TIER_LIST="data-tier-list",ITEM_KEY=Symbol("item.key"),ITEMS_KEY=Symbol("items.key"),TEMPLATE_KEY=Symbol("template"),OBSERVER_KEY=Symbol("observer"),FULL_UPDATER_KEY=Symbol("full.updater"),PART_UPDATER_KEY=Symbol("part.updater"),CONTAINER_RESOLVER_KEY=Symbol("container.resolver"),TEMPLATE_PROCESSOR_KEY=Symbol("template.processor");class DataTierList extends HTMLElement{constructor(){super(),this[ITEMS_KEY]=null,this[TEMPLATE_KEY]=null,this[OBSERVER_KEY]=this[OBSERVER_KEY].bind(this)}connectedCallback(){this.hidden=!0,this.templateObserver=new MutationObserver(()=>{this.templateObserver.takeRecords(),this[TEMPLATE_PROCESSOR_KEY]()}),this.templateObserver.observe(this,{subtree:!0,childList:!0,attributes:!0,characterData:!0}),this[TEMPLATE_PROCESSOR_KEY]()}disconnectedCallback(){this.templateObserver.disconnect()}set items(e){this[ITEMS_KEY]!==e&&(null===e||""===e?this[ITEMS_KEY]&&(this[ITEMS_KEY].unobserve(this[OBSERVER_KEY]),this[ITEMS_KEY]=null,this[FULL_UPDATER_KEY]()):"object"==typeof e?(this[ITEMS_KEY]&&this[ITEMS_KEY].unobserve(this[OBSERVER_KEY]),this[ITEMS_KEY]=Observable.from(e),this[ITEMS_KEY].observe(this[OBSERVER_KEY],{pathsOf:""}),this[FULL_UPDATER_KEY]()):console.error(`items MAY ONLY be set to an object, got '${e}'`))}get items(){return this[ITEMS_KEY]}[CONTAINER_RESOLVER_KEY](){let e;const t=this.getAttribute("data-list-target");if(t){if(!(e=this.getRootNode().querySelector(t)))throw new Error(`failed to resolve target container by the given query '${t}'`)}else e=this.parentNode;return e}[TEMPLATE_PROCESSOR_KEY](){let e=null;const t=this.children;t.length>1&&console.error(`list item template MAY have 1 root element only, got ${t.length}; working with the first one`),t.length>0&&(e=t[0]);let s=null;if(e){const t=/(?<= data-tie=["']{1}.*\b)item(?=\b)/g,i=e.outerHTML;let E,n=0;s="";do{(E=t.exec(i))?(s+=i.substring(n,E.index)+"scope",n=E.index+4):s+=i.substring(n)}while(E)}this[TEMPLATE_KEY]=s,this[FULL_UPDATER_KEY](!0)}[OBSERVER_KEY](e){for(const t of e)"shuffle"===t.type||"reverse"===t.type?this[FULL_UPDATER_KEY]():this[PART_UPDATER_KEY](t)}[FULL_UPDATER_KEY](e=!1){if(!this[TEMPLATE_KEY]||!this.items)return;const t=this[CONTAINER_RESOLVER_KEY](),s=t.contains(this)?1:0,i=Object.keys(this.items),E=i.length;let n,r=t.childElementCount-s;for(;r>(e?0:E);)(n=t.lastElementChild)!==this&&(ties.remove(n),n.remove()),r--;let l="";for(;r<E;)l+=this[TEMPLATE_KEY],r++;if(l){const e=document.createElement("template");e.innerHTML=l,t.appendChild(e.content)}for(let e=s,E=t.children.length;e<E;e++){const E=t.children[e];E[ITEM_KEY]=i[e-s],ties.update(E,this.items[i[e-s]])}}[PART_UPDATER_KEY](e){if(!this[TEMPLATE_KEY]||!this.items)return;const t=this[CONTAINER_RESOLVER_KEY](),s=t.contains(this)?1:0,i=document.createElement("template");if(!(e.path.length>1))if(Array.isArray(this.items)){const r=parseInt(e.path[0],10);if("insert"===e.type)i.innerHTML=this[TEMPLATE_KEY],ties.update(i.content.firstElementChild,e.value),t.insertBefore(i.content,t.children[r+s]);else if("update"===e.type)ties.update(t.children[r+s],e.value);else if("delete"===e.type)t.removeChild(t.children[r+s]);else if("reverse"===e.type)for(var E=s+1,n=t.children.length-s/2;E<n;E++)t.insertBefore(t.children[E],t.children[E-1]);else console.warn(`unsupported change type ${e.type}`)}else{const E=Object.keys(this.items).indexOf(e.path[0]);if("insert"===e.type)i.innerHTML=this[TEMPLATE_KEY],ties.update(i.content.firstElementChild,e.value),t.insertBefore(i.content,t.children[e.path[0]+s]);else if("update"===e.type)ties.update(t.children[E+s],e.value);else if("delete"===e.type){for(const s of t.children)if(s[ITEM_KEY]===e.path[0]){s.remove();break}}else console.warn(`unsupported change type ${e.type}`)}}}customElements.get(DATA_TIER_LIST)?console.warn("'data-tier-list' is already defined in this environment, won't redefine"):customElements.define(DATA_TIER_LIST,DataTierList); |
{ | ||
"name": "data-tier-list", | ||
"version": "1.2.3", | ||
"version": "2.0.0", | ||
"description": "List component based on DataTier binding engine", | ||
@@ -53,11 +53,11 @@ "keywords": [ | ||
"dependencies": { | ||
"data-tier": "^3.1.6" | ||
"data-tier": "^3.2.0" | ||
}, | ||
"devDependencies": { | ||
"eslint": "^7.25.0", | ||
"fs-extra": "^9.1.0", | ||
"eslint": "^7.26.0", | ||
"fs-extra": "^10.0.0", | ||
"just-test": "2.3.2", | ||
"puppeteer": "^9.0.0", | ||
"puppeteer": "^9.1.1", | ||
"uglify-es": "^3.3.9" | ||
} | ||
} |
@@ -18,3 +18,3 @@ [![NPM](https://img.shields.io/npm/v/data-tier-list.svg?label=npm%20data-tier-list)](https://www.npmjs.com/package/data-tier-list) | ||
- template mutations are observed and reflected | ||
- uniformity of a data items **is not** validated/enforced | ||
- uniformity of a data items __is not__ validated/enforced | ||
@@ -51,7 +51,8 @@ `data-tier-list` relies on a [data-tier](https://github.com/gullerya/data-tier) library for the model-view tying part. | ||
- `data-tier-list` element self and it's light DOM __are not__ displayed | ||
- `data-tier-list` element self and its light DOM __are not__ displayed | ||
- light DOM of the `data-tier-list` is taken as a __template__ for a single item | ||
- within a template use binding declaration syntax as that of the `data-tier`, with `item` as scope key (see examples above) | ||
- template is observed for any changes; replacement of it or a change of its child/ren triggers a __full (re)render__ of the list | ||
- template may have __at most__ one top level element (any nested elements tree allowed) | ||
- template removal cleans the list | ||
- template error (eg more than one child) throws, list remains untouched | ||
- template error (eg more than one child) throws, list remains untouched |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
15363
203
Updateddata-tier@^3.2.0