Skip to content

Commit 437d70f

Browse files
committed
work on conditional template, dce elements
1 parent 49a7800 commit 437d70f

5 files changed

Lines changed: 81 additions & 5 deletions

File tree

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,21 @@ class:className="[[boolExpression]]" to set/remove a css class
6363

6464
$attribute="[[expression]]" to bind to Attributes instead of properties.
6565

66+
?booleanAttribute="[[expression]]" to bind to a boolean Attribute.
67+
6668
.property="[[expression]]" to bind to Proertys without using the attribute name (to disable side effects).
6769

6870
sub <template></template> elements are not bound, so elements like <iron-list> of polymer also work
6971

70-
use repeat:nameOfItem=[[enumerableExpression]] on a Template Element to repeat it for every instance of the enumerable.
72+
css type adopted-css
73+
74+
use repeat:nameOfItem="[[enumerableExpression]]" on a template element to repeat it for every instance of the enumerable.
7175
You could also use 'index' variable in the repeat binding for the current number. The attribute "repeat-index" could be used to change the name of the index variable.
7276
on a repeat you could use the repeat-changed-item-callback="[[this.itemCreated(item, nodes)]]
7377
!!caution!! => the repeat binding is only a preview at the moment, it redraws all items on array change
7478

79+
use if="[[expression]]" an a template element to show it conditionally
80+
7581
## Event Code Bindings
7682

7783
with for example @click="[[this.aa(event)]]" you could create a event binding wich could run any javascript code inside of the brackets.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"description": "Base Custom Webcomponent",
33
"name": "@node-projects/base-custom-webcomponent",
4-
"version": "0.25.9",
4+
"version": "0.25.10",
55
"type": "module",
66
"main": "./dist/index.js",
77
"author": "",

sample/index.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ <h1>Hello World</h1>
5252
$aaaonclick="[['this.getRootNode().host.ctx = \'' + myitem + '\'']]">[[myitem]] - [[index]]</button>
5353
</template>
5454
</template>
55+
<style type="adopted-css">
56+
button {
57+
color: green;
58+
}
59+
</style>
5560
</node-projects-dce>
5661

5762
<simple-dce-demo list='["aa","bb"]' ctx="test">
@@ -72,7 +77,18 @@ <h1>Hello World</h1>
7277
<template repeat:myitem="[[this.list]]">
7378
<button @click="[[this.ctx = myitem]]">[[myitem]] - [[index]]</button>
7479
</template>
80+
<template if="[[this.list == null]]">
81+
<button>111</button>
82+
</template>
83+
<template if="[[this.list != null]]">
84+
<button @click="[[this.list = null]]">222</button>
85+
</template>
7586
</template>
87+
<style type="adopted-css">
88+
button {
89+
color: blue;
90+
}
91+
</style>
7692
</node-projects-dce>
7793

7894
<simple-dce-demo list='["aa","bb"]' ctx="test">

src/BaseCustomWebComponent.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,14 @@ export class BaseCustomWebComponentNoAttachedTemplate extends HTMLElement {
188188
const b = () => this._bindingSetElementClass(<HTMLElement | SVGElement>node, camelCased, value, repeatBindingItems, host, context);
189189
this._bindings.push([b, null]);
190190
b();
191-
} else if (a.name == 'repeat-changed-item-callback') {
191+
} else if (a.name.length === 28 && a.name === 'repeat-changed-item-callback') {
192192
//do nothing
193+
} else if (a.name === 'if' && node instanceof HTMLTemplateElement) {
194+
const value = a.value.substring(2, a.value.length - 2).replaceAll('&amp;', '&');
195+
const elementsCache: Node[] = [];
196+
const b = () => this._bindingConditional(<HTMLTemplateElement>node, value, repeatBindingItems, elementsCache, host, context);
197+
this._bindings.push([b, null]);
198+
b();
193199
} else if (a.name.startsWith('repeat:')) {
194200
const value = a.value.substring(2, a.value.length - 2).replaceAll('&amp;', '&');
195201
const bindingItemVariableName = a.name.substring(7, a.name.length).replace(/-([a-z])/g, (g) => g[1].toUpperCase());
@@ -220,7 +226,7 @@ export class BaseCustomWebComponentNoAttachedTemplate extends HTMLElement {
220226
} else {
221227
let value = a.value.substring(2, a.value.length - 2).replaceAll('&amp;', '&');
222228
let camelCased = a.name
223-
if (a.name[0] !== '$')
229+
if (a.name[0] !== '$' && a.name[0] !== '?')
224230
camelCased = a.name.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
225231
let noNull = false;
226232
if (value[0] === '?') {
@@ -357,6 +363,30 @@ export class BaseCustomWebComponentNoAttachedTemplate extends HTMLElement {
357363
return value;
358364
}
359365

366+
private _bindingConditional(node: HTMLTemplateElement, expression: string, repeatBindingItems: repeatBindingItem[], elementsCache: Node[], host: any, context: any) {
367+
try {
368+
const value = this._bindingRunEval(expression, repeatBindingItems, null, host, context);
369+
370+
if (!value && elementsCache.length > 0) {
371+
for (let c of elementsCache) {
372+
c.parentNode.removeChild(c);
373+
}
374+
elementsCache.length = 0;
375+
}
376+
377+
if (value && elementsCache.length === 0) {
378+
let nd = <DocumentFragment>node.content.cloneNode(true);
379+
elementsCache.push(...nd.children);
380+
this._bindingsInternalParse(nd, repeatBindingItems, true, host, context);
381+
382+
node.parentNode.insertBefore(nd, node);
383+
}
384+
} catch (error) {
385+
if (!this._noWarningOnBindingErrors)
386+
console.warn((<Error>error).message, 'Failed to bind Conditional to expression "' + expression + '"', node);
387+
}
388+
}
389+
360390
private _bindingRepeat(node: HTMLTemplateElement, bindingProperty: string, bindingIndexName: string, expression: string, callback: string, repeatBindingItems: repeatBindingItem[], elementsCache: Node[], host: any, context: any) {
361391
try {
362392
const values = this._bindingRunEval(expression, repeatBindingItems, null, host, context);
@@ -445,6 +475,11 @@ export class BaseCustomWebComponentNoAttachedTemplate extends HTMLElement {
445475
(<Element>node).removeAttribute(property.substring(1));
446476
else
447477
(<Element>node).setAttribute(property.substring(1), value);
478+
} else if (property[0] === '?') {
479+
if (!value == true)
480+
(<Element>node).setAttribute(property.substring(1), '');
481+
else
482+
(<Element>node).removeAttribute(property.substring(1));
448483
}
449484
else if (property == 'class')
450485
(<Element>node).setAttribute(property, value);

src/DeclaritiveBaseCustomWebcomponent.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { BaseCustomWebComponentNoAttachedTemplate, css } from "./BaseCustomWebComponent.js";
1+
import { BaseCustomWebComponentNoAttachedTemplate, css, cssFromString } from "./BaseCustomWebComponent.js";
22
import { WeakArray } from "./WeakArray.js";
33

44
function camelToDashCase(text: string) {
@@ -18,10 +18,18 @@ class BaseDeclaritiveWebcomponent extends BaseCustomWebComponentNoAttachedTempla
1818
this.#firstConnect = true;
1919
if (window[this.localName].template === undefined) {
2020
window[this.localName].template = window[this.localName]._definingElement.querySelector('template');
21+
const styles: HTMLStyleElement[] = window[this.localName]._definingElement.querySelectorAll('style[type=adopted-css]');
22+
if (styles?.length)
23+
window[this.localName]._styles = Array.from(styles).map(x => cssFromString(x.textContent));
2124
}
2225
//@ts-ignore
2326
this._rootDocumentFragment = this.constructor.template.content.cloneNode(true);
2427
//@ts-ignore
28+
if (this.constructor._styles) {
29+
//@ts-ignore
30+
this.shadowRoot.adoptedStyleSheets = this.constructor._styles;
31+
}
32+
//@ts-ignore
2533
if (this.constructor._enableBindings)
2634
this._bindingsParse(null, true);
2735
this.shadowRoot.appendChild(this._rootDocumentFragment);
@@ -36,6 +44,12 @@ class BaseDeclaritiveWebcomponent extends BaseCustomWebComponentNoAttachedTempla
3644
_reapplyTemplateAfterUpdate() {
3745
this._bindings = null;
3846
this.shadowRoot.innerHTML = '';
47+
this.shadowRoot.adoptedStyleSheets = [];
48+
//@ts-ignore
49+
if (this.constructor._styles) {
50+
//@ts-ignore
51+
this.shadowRoot.adoptedStyleSheets = this.constructor._styles;
52+
}
3953
//@ts-ignore
4054
this._rootDocumentFragment = this.constructor.template.content.cloneNode(true);
4155
this.shadowRoot.appendChild(this._rootDocumentFragment);
@@ -85,6 +99,7 @@ class DeclaritiveBaseCustomWebcomponent extends BaseCustomWebComponentNoAttached
8599
window[name]._propertiesDictionary = null;
86100
window[name]._enableBindings = this.enableBindings;
87101
window[name]._definingElement = this;
102+
window[name]._styles = null;
88103
} else {
89104
const instanceArray = new WeakArray<BaseDeclaritiveWebcomponent>();
90105
instanceMap.set(name, instanceArray);
@@ -135,6 +150,7 @@ class DeclaritiveBaseCustomWebcomponent extends BaseCustomWebComponentNoAttached
135150
window[name]._propertiesDictionary = null;
136151
window[name]._enableBindings = this.enableBindings;
137152
window[name]._definingElement = this;
153+
window[name]._styles = null;
138154
window[name].prototype = Object.create(BaseDeclaritiveWebcomponent.prototype, { constructor: { value: window[name] } })
139155
if (!customElements.get(name))
140156
customElements.define(name, window[name]);
@@ -143,6 +159,9 @@ class DeclaritiveBaseCustomWebcomponent extends BaseCustomWebComponentNoAttached
143159

144160
upgradeAllInstances(template?: HTMLTemplateElement) {
145161
window[this.name].template = template ?? this.querySelector('template');
162+
const styles = this.querySelectorAll('style[type=adopted-css]');
163+
if (styles?.length)
164+
window[this.name]._styles = Array.from(styles).map(x => cssFromString(x.textContent));
146165
const instanceArray = instanceMap.get(this.name);
147166
for (const i of instanceArray)
148167
i._reapplyTemplateAfterUpdate();

0 commit comments

Comments
 (0)