@techexp/webitem 0.6.0 → 0.7.0
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 +29 -9
- package/dist/webitem-esm.js +7 -3
- package/dist/webitem-script-min.js +5 -5
- package/dist/webitem-script.js +7 -3
- package/package.json +9 -9
- package/src/index.html +1 -1
- package/src/test/perf-test.css +3 -0
- package/src/test/webitem.perf.test.js +107 -0
- package/src/test/webitem.test.js +1 -1
- package/src/webitem.js +23 -17
package/README.md
CHANGED
|
@@ -71,7 +71,8 @@ object with the following keys:
|
|
|
71
71
|
`(webitem) => string`. Look down for an example.
|
|
72
72
|
3. `css`: Optional, String. The CSS to apply on the web component. This CSS will be name-spaced
|
|
73
73
|
within the component, and is not visible outside it.
|
|
74
|
-
4. `
|
|
74
|
+
4. `styleSheets`: Optional, array of `CSSStyleSheet` objects. This way you can share style sheets among components.
|
|
75
|
+
5. `propertyList`: Optional, Array of objects. Objects defining properties of the component. Each property
|
|
75
76
|
definition consists of `{name, value, [sel], [attr]}`.
|
|
76
77
|
1. `name`: Name of the property.
|
|
77
78
|
2. `value`: Initial value of the property.
|
|
@@ -79,18 +80,18 @@ object with the following keys:
|
|
|
79
80
|
4. `attr`: Optional, String. An attribute on the DOM element to bind its value.
|
|
80
81
|
5. `onChange`: Optional. Function. A function to be called when the property's value change through
|
|
81
82
|
an API call. The function can take three arguments `(webitem, oldValue, newValue)`
|
|
82
|
-
|
|
83
|
+
6. `eventHandlerList`: Optional, Array of objects. Objects define event handlers of the component.
|
|
83
84
|
Each event handler definition consists of `{sel, eventName, listener}`.
|
|
84
85
|
1. `sel`: A CSS selector of an element in the component.
|
|
85
86
|
2. `eventName`: Name of the event to bind to, e.g. `click`.
|
|
86
87
|
3. `listener`: A function to be called when the event occures. The function accepts two arguments,
|
|
87
88
|
an [event](https://developer.mozilla.org/en-US/docs/Web/API/Event) object, and `webitem` which is the web component.
|
|
88
|
-
|
|
89
|
+
7. `actionList`: Optional, Array of objects. Objects define actions that can be defined on the component.
|
|
89
90
|
Each action definition consists of `{name, action}`.
|
|
90
91
|
1. `name`: Name of the action.
|
|
91
92
|
2. `action`: a function definition. If you declare this function as a _classic_ (not arrow) function, using
|
|
92
93
|
`this` inside the function will refer to the component.
|
|
93
|
-
|
|
94
|
+
8. `display`: Optional, String. A CSS display attribute. A possible value can be
|
|
94
95
|
`inline` (default if missing), `inline-block`, or `block`. This controls how the component is displayed
|
|
95
96
|
inside its container.
|
|
96
97
|
|
|
@@ -99,7 +100,7 @@ already exists, in which case it will not be re-created.
|
|
|
99
100
|
|
|
100
101
|
The `defineElement()` function defines a standard _Custom Element_/_Web Component_ with all the
|
|
101
102
|
standard properties. It also adds an extra property `wi` that contains extra properties and
|
|
102
|
-
functions supported
|
|
103
|
+
functions supported by this library as follows:
|
|
103
104
|
|
|
104
105
|
1. `wi.properties`: An object with key/value as defined in the `propertyList` during element
|
|
105
106
|
definition.
|
|
@@ -124,8 +125,27 @@ functions supported bby this library as follows:
|
|
|
124
125
|
CSS applied to a web component (_through shadow DOM_) is scoped to the component, it does not interact
|
|
125
126
|
with CSS outside the component.
|
|
126
127
|
|
|
127
|
-
If you need to use a common CSS
|
|
128
|
-
|
|
128
|
+
If you need to use a common CSS among components, there are two options:
|
|
129
|
+
|
|
130
|
+
1. Use `CSSStyleSheet` object and pass in the `styleSheets` option. This is the recommended approach.
|
|
131
|
+
For example:
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
const sheet = new CSSStyleSheet()
|
|
135
|
+
sheet.replaceSync('h3 { color: red; }')
|
|
136
|
+
|
|
137
|
+
webitem.defineElement({
|
|
138
|
+
nameWithDash: 'with-sheets',
|
|
139
|
+
html: `
|
|
140
|
+
<div style="background-color:green">
|
|
141
|
+
<h3>with-sheets Web Item</h3>
|
|
142
|
+
</div>
|
|
143
|
+
`,
|
|
144
|
+
styleSheets: [sheet]
|
|
145
|
+
})
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
2. Use the `<link>` tag in the component's html, for example, add the next line at the top of your component's html:
|
|
129
149
|
|
|
130
150
|
```html
|
|
131
151
|
<link rel="stylesheet" href="css/common.css">
|
|
@@ -137,7 +157,7 @@ Next we will show some as well.
|
|
|
137
157
|
|
|
138
158
|
### Provide html as a function
|
|
139
159
|
This example shows how to provide the html content of the component as a function. The function takes
|
|
140
|
-
the
|
|
160
|
+
the component itself as an argument and returns a string representing the html of the component.
|
|
141
161
|
|
|
142
162
|
This approach allows you to introspect the definition of the element as given on the page.
|
|
143
163
|
|
|
@@ -210,7 +230,7 @@ console.log($('#bounded').wi.properties.country) // prints USA
|
|
|
210
230
|
The binding is _bi-directional_, changing the property's value will change the view, and changing the
|
|
211
231
|
value in the view will change the property's value.
|
|
212
232
|
|
|
213
|
-
Binding properties to DOM elements is optional, you can choose to define a
|
|
233
|
+
Binding properties to DOM elements is optional, you can choose to define a property without `sel` value.
|
|
214
234
|
|
|
215
235
|
For more details about bound properties, check [Data Bind](https://www.npmjs.com/package/@techexp/data-bind)
|
|
216
236
|
NPM package which is used by this library.
|
package/dist/webitem-esm.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// webitem.js Library to simplify creating HTML5 Custom Elements
|
|
2
2
|
// https://github.com/ahabra/webitem
|
|
3
|
-
// Copyright 2021 (C) Abdul Habra. Version 0.
|
|
3
|
+
// Copyright 2021 (C) Abdul Habra. Version 0.7.0.
|
|
4
4
|
// Apache License Version 2.0
|
|
5
5
|
|
|
6
6
|
|
|
@@ -12,6 +12,7 @@ function defineElement({
|
|
|
12
12
|
nameWithDash,
|
|
13
13
|
html,
|
|
14
14
|
css,
|
|
15
|
+
styleSheets,
|
|
15
16
|
display,
|
|
16
17
|
propertyList,
|
|
17
18
|
actionList,
|
|
@@ -22,7 +23,7 @@ function defineElement({
|
|
|
22
23
|
constructor() {
|
|
23
24
|
super();
|
|
24
25
|
const root = this;
|
|
25
|
-
addHtml(this, html, css, display);
|
|
26
|
+
addHtml(this, html, css, styleSheets, display);
|
|
26
27
|
this.wi = {};
|
|
27
28
|
this.wi.properties = bindProperties(this, propertyList);
|
|
28
29
|
this.wi.actions = defineActions(this, actionList);
|
|
@@ -89,11 +90,14 @@ function addHandler(root, { sel, eventName, listener }) {
|
|
|
89
90
|
});
|
|
90
91
|
});
|
|
91
92
|
}
|
|
92
|
-
function addHtml(root, html, css, display) {
|
|
93
|
+
function addHtml(root, html, css, styleSheets, display) {
|
|
93
94
|
html = getHtml(root, html);
|
|
94
95
|
const shadow = root.attachShadow({ mode: "open" });
|
|
95
96
|
const nodes = Domer.createElements(getCss(css, display) + html);
|
|
96
97
|
shadow.append(...nodes);
|
|
98
|
+
if (Array.isArray(styleSheets) && styleSheets.length > 0) {
|
|
99
|
+
shadow.adoptedStyleSheets.push(...styleSheets);
|
|
100
|
+
}
|
|
97
101
|
}
|
|
98
102
|
function getHtml(root, html) {
|
|
99
103
|
return Objecter.isFunction(html) ? html(root) : html;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
// webitem.js Library to simplify creating HTML5 Custom Elements
|
|
2
2
|
// https://github.com/ahabra/webitem
|
|
3
|
-
// Copyright 2021 (C) Abdul Habra. Version 0.
|
|
3
|
+
// Copyright 2021 (C) Abdul Habra. Version 0.7.0.
|
|
4
4
|
// Apache License Version 2.0
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
var webitem=(()=>{var
|
|
7
|
+
var webitem=(()=>{var S=Object.defineProperty;var re=Object.getOwnPropertyDescriptor;var ie=Object.getOwnPropertyNames;var ue=Object.prototype.hasOwnProperty;var oe=(e,t)=>{for(var n in t)S(e,n,{get:t[n],enumerable:!0})},fe=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of ie(t))!ue.call(e,i)&&i!==n&&S(e,i,{get:()=>t[i],enumerable:!(r=re(t,i))||r.enumerable});return e};var se=e=>fe(S({},"__esModule",{value:!0}),e);var ct={};oe(ct,{defineElement:()=>Ze});var ae=Object.defineProperty,p=(e,t)=>{for(var n in t)ae(e,n,{get:t[n],enumerable:!0})},l={};p(l,{add:()=>Ce,all:()=>M,classPresentIf:()=>Pe,createElement:()=>Le,createElements:()=>_,first:()=>we,getAttributes:()=>Ae,id:()=>be,removeElements:()=>$e,setContent:()=>Te,tag:()=>W});var d={};p(d,{equals:()=>F,forEachEntry:()=>O,has:()=>de,isDate:()=>A,isFunction:()=>R,isInteger:()=>le,isNil:()=>ce,isNumber:()=>j,isRegExp:()=>q,isString:()=>y});function ce(e){return e==null}function y(e){return m(e,"String")}function R(e){return m(e,"Function")}function A(e){return m(e,"Date")}function j(e){return m(e,"Number")?Number.isNaN(e)?!1:Number.isFinite(e):!y(e)||(e=e.trim(),e==="")?!1:!isNaN(e)}function le(e){return j(e)?Number.isInteger(Number.parseFloat(e)):!1}function q(e){return m(e,"RegExp")}function m(e,t){return Object.prototype.toString.call(e)===`[object ${t}]`}function O(e,t){if(!(!e||!t)){if(Array.isArray(e)){e.forEach((n,r)=>{t(r,n)});return}Object.entries(e).forEach(n=>t(n[0],n[1]))}}function de(e,t){return!e||!t?!1:Object.prototype.hasOwnProperty.call(e,t)}function F(e,t){return e===t?!0:e===void 0||t===void 0?!1:me(e,t)}function me(e,t){return T(e)||T(t)?e===t:ge(e,t)}var he=new Set(["boolean","number","bigint","string","symbol"]);function T(e){return he.has(typeof e)}function ge(e,t){return pe(e,t)?ye(e,t)?!0:Ee(e,t):!1}function pe(e,t){return $(e)===$(t)}function $(e){return Object.prototype.toString.call(e)}function ye(e,t){return A(e)&&A(t)?e.getTime()===t.getTime():!1}function Ee(e,t){let n=Object.keys(e);return n.length!==Object.keys(t).length?!1:n.every(r=>F(e[r],t[r]))}function be(e,t=document){return x(t)&&(t=t.shadowRoot),t.getElementById(e)}function M(e,t=document){return x(t)&&(t=t.shadowRoot),Array.from(t.querySelectorAll(e))}function we(e,t=document){return x(t)&&(t=t.shadowRoot),e.includes("/")?ve(e,t):t.querySelector(e)}function ve(e,t){let n=e.split("/").map(r=>r.trim()).filter(r=>r.length>0);for(let r of n)if(t=Se(r,t),t===null)break;return t}function Se(e,t){return e==="shadowRoot"||e==="shadow-root"?t.shadowRoot:t.querySelector(e)}function x(e){return e&&e.shadowRoot&&e.tagName.includes("-")}function Ae(e){let t={},n=e.attributes;if(!n||n.length===0)return t;for(let r=0;r<n.length;r++){let i=n[r];t[i.name]=i.value}return t}function _(e=""){if(e=e.trim(),!e)return[];let t=document.createElement("template");return t.innerHTML=e,Array.from(t.content.childNodes)}function Le(e,t={},n=""){let r=W(e,t,n),i=_(r);return i.length===0?null:i[0]}function W(e,t={},n=""){if(!e)return"";let r=Oe(t);return`<${e}${r}>${n}</${e}>`}function Oe(e){let t=[];return O(e,(r,i)=>{t.push(`${r}="${i}"`)}),(t.length>0?" ":"")+t.join(" ")}var xe=new Set(["beforebegin","afterbegin","beforeend","afterend"]);function Ce(e,t,n="beforeend"){return n=n.toLowerCase(),xe.has(n)?(y(t)?e.insertAdjacentHTML(n,t):Ne(e,t,n),!0):!1}function Ne(e,t,n){Array.isArray(t)?t.forEach(r=>e.insertAdjacentElement(n,r)):e.insertAdjacentElement(n,t)}function Te(e,...t){e.innerHTML="",e.append(...t)}function $e(e,t=document){M(e,t).forEach(r=>{r.parentNode.removeChild(r)})}function Pe(e,t,n){if(!e)return;let r=n?"add":"remove";e.classList[r](t)}var h={};p(h,{endsWith:()=>H,indexOf:()=>C,indexOfFirstMatch:()=>Re,indexOfLastMatch:()=>je,isEmpty:()=>E,removePrefix:()=>I,removeSuffix:()=>V,removeSurrounding:()=>qe,replaceAll:()=>k,replaceTemplate:()=>_e,startsWith:()=>D,strip:()=>He,stripEnd:()=>De,stripStart:()=>We,substringAfter:()=>Fe,substringBefore:()=>Me,trim:()=>L});function C(e,t,n=0,r=!1){return e?r?e.toLowerCase().indexOf(t.toLowerCase(),n):e.indexOf(t,n):-1}function Re(e,t){return!t||!e?-1:e.split("").findIndex(t)}function je(e,t){if(!t||!e)return-1;let n=e.split("");for(let r=n.length;r>=0;--r)if(t(n[r],r))return r;return-1}function D(e="",t=void 0,n=!1){if(n){let r=e.substring(0,t.length).toLowerCase();return t.toLowerCase()===r}return e.startsWith(t)}function H(e,t,n=!1){return n?e.toLowerCase().endsWith(t.toLowerCase()):e.endsWith(t)}function I(e,t,n=!1){return D(e,t,n)&&(e=e.substring(t.length)),e}function V(e,t,n=!1){return H(e,t,n)&&(e=e.substring(0,e.length-t.length)),e}function qe(e,t,n,r=!1){return V(I(e,t,r),n,r)}function Fe(e,t,n=!1){if(!t)return e;let r=C(e,t,0,n);return r<0?"":e.substring(r+t.length)}function Me(e,t,n=!1){if(!t)return"";let r=C(e,t,0,n);return r<0?e:e.substring(0,r)}function L(e){return E(e)?"":(y(e)||(e=String(e)),e.trim(e))}function E(e){return e==null||e===""}function k(e,t,n){if(R(String.prototype.replaceAll))return e.replaceAll(t,n);if(q(t))return e.replace(t,n);let r=new RegExp(t,"g");return e.replace(r,n)}function _e(e="",t={},n="${",r="}"){return O(t,(i,u)=>{u!==void 0&&(i=n+i+r,e=k(e,i,u))}),e}function We(e,t=""){return E(e)?"":t?B(e,new Set(Array.from(t))):e}function B(e,t){for(let n=0;n<e.length;n++)if(!t.has(e.charAt(n)))return e.substring(n);return""}function De(e,t=""){return E(e)?"":t?z(e,new Set(Array.from(t))):e}function z(e,t){for(let n=e.length-1;n>=0;n--)if(!t.has(e.charAt(n)))return e.substring(0,n+1);return""}function He(e,t=""){if(e===void 0||e==="")return"";if(!t)return e;let n=new Set(Array.from(t));return e=B(e,n),e?z(e,n):""}var Ie={};p(Ie,{compareLines:()=>Ve});function Ve(e,t,{trim:n=!0,skipEmpty:r=!0,caseSensitive:i=!0}={trim:!0,skipEmpty:!0,caseSensitive:!0}){return e=P(e,{trim:n,skipEmpty:r}),t=P(t,{trim:n,skipEmpty:r}),e.length!==t.length?`t1 has ${e.length} lines(s) while t2 has ${t.length} line(s).`:ke(e,t,i)}function ke(e,t,n){for(let r=0;r<e.length;r++){let i=Be(e[r],t[r],r,n);if(i.length>0)return i}return""}function Be(e,t,n,r){let i=r?e:e.toLowerCase(),u=r?t:t.toLowerCase();return i!==u?`Line #${n+1} mismatch.
|
|
8
8
|
${e}
|
|
9
|
-
${t}`:""}function
|
|
10
|
-
`),t&&(e=e.map(r=>
|
|
9
|
+
${t}`:""}function P(e,{trim:t,skipEmpty:n}){return t&&(e=L(e)),e=e.split(`
|
|
10
|
+
`),t&&(e=e.map(r=>L(r))),n&&(e=e.filter(r=>!!r)),e}function N({obj:e={},prop:t,sel:n,attr:r,root:i=document,getter:u,setter:o,onChange:f}){Ye(t),Ge(e,t);let g={};return u||(u=()=>Je({prop:t,sel:n,attr:r,root:i,objNotBound:g})),o||(o=s=>Ke({prop:t,value:s,root:i,sel:n,attr:r,objNotBound:g})),ze({obj:e,prop:t,getter:u,setter:o,onChange:f})}function ze({obj:e,prop:t,getter:n,setter:r,onChange:i}){return Object.defineProperty(e,t,{get:()=>n(),set:o=>{if(i){let f=n(t);f!==o&&i(f,o)}r(o)},configurable:!0,enumerable:!0}),e}var b=e=>e.type==="checkbox",w=e=>e.type==="radio",J=e=>e.nodeName.toLowerCase()==="select",K=e=>e.nodeName.toLowerCase()==="input-field",Q=e=>"value"in e,G=e=>new Set(Array.isArray(e)?e:[e]);function Ge(e,t){let n=e[t];return n!==void 0&&(console.info(`Property '${t}' already exists in object. Will override previous definition but retain old value of ${n}.`),e[t]=n),n}function Je({prop:e,root:t,sel:n,attr:r,objNotBound:i}){return n?Qe(t,n,r):i[e]}function Ke({prop:e,value:t,root:n,sel:r,attr:i,objNotBound:u}){if(r){Ue(n,r,t,i);return}u[e]=t}function Qe(e,t,n){let r=U(e,t);if(r.length===0)return null;let i=r[0];if(n)return i.getAttribute(n);if(!Q(i))return i.innerHTML;if(b(i))return r.filter(u=>b(u)&&u.checked).map(u=>u.value==="on"?u.name:u.value);if(J(i))return[...i.querySelectorAll("option")].filter(o=>o.selected).map(o=>o.value);if(!(w(i)&&(i=r.filter(w).find(u=>u.checked),!i)))return K(i)?i.getAttribute("value"):i.value}function Ue(e,t,n,r){let i=U(e,t);if(i.length===0)return;let u=i[0];if(b(u)){let o=G(n);i.filter(b).forEach(f=>f.checked=o.has(f.value)||o.has(f.name));return}if(J(u)){let o=G(n);u.querySelectorAll("option").forEach(f=>f.selected=o.has(f.value));return}if(w(u)){i.filter(w).forEach(o=>o.checked=o.value===n);return}i.forEach(o=>Xe(o,n,r))}function Xe(e,t,n){n?e.setAttribute(n,t):Q(e)?e.value=t:K(e)?e.setAttribute("value",t):e.innerHTML=t}function U(e,t){let n=e.querySelectorAll(t);return n.length===0&&console.warn(`No elements found matching selector ${t}`),[...n]}function Ye(e){if(typeof e!="string"||e.length===0)throw"'prop' argument must be a String defining the name a property."}function Ze({nameWithDash:e,html:t,css:n,styleSheets:r,display:i,propertyList:u,actionList:o,eventHandlerList:f}){if(customElements.get(e))return!1;let g=class extends HTMLElement{constructor(){super();let s=this;ut(this,t,n,r,i),this.wi={},this.wi.properties=et(this,u),this.wi.actions=rt(this,o),it(this,f),this.wi.addProperty=function(a,c,v,ee,te){let ne={name:a,value:c,sel:v,attr:ee,onChange:te};X(s.wi.properties,ne,s)},this.wi.addAction=(a,c)=>Y(s,s.wi.actions,a,c),this.wi.addEventListener=(a,c,v)=>Z(s,{sel:a,eventName:c,listener:v})}};return customElements.define(e,g),!0}function et(e,t){let n={};return nt(t)&&t.forEach(r=>X(n,r,e)),n}function X(e,t,n){let r=tt(t,n);N({obj:e,prop:t.name,sel:t.sel,attr:t.attr,root:n.shadowRoot,onChange:r}),t.value!==void 0&&(e[t.name]=t.value)}function tt(e,t){if(e.onChange)return(n,r)=>e.onChange(t,n,r)}function nt(e){if(!e)return!1;if(!Array.isArray(e))throw"propertyList must be an array of {name, value, [sel], [attr]} objects";return!0}function rt(e,t){let n={};return t&&t.forEach(r=>{Y(e,n,r.name,r.action)}),n}function Y(e,t,n,r){!d.isString(n)||!d.isFunction(r)||(t[n]=r.bind(e))}function it(e,t){if(t){if(!Array.isArray(t))throw"eventHandlerList must be an array of {sel, eventName, listener} objects";t.forEach(n=>Z(e,n))}}function Z(e,{sel:t,eventName:n,listener:r}){l.all(t,e.shadowRoot).forEach(u=>{u.addEventListener(n,o=>{r(o,e)})})}function ut(e,t,n,r,i){t=ot(e,t);let u=e.attachShadow({mode:"open"}),o=l.createElements(ft(n,i)+t);u.append(...o),Array.isArray(r)&&r.length>0&&u.adoptedStyleSheets.push(...r)}function ot(e,t){return d.isFunction(t)?t(e):t}function ft(e,t){return at(t)+st(e)}function st(e){return e=h.trim(e),e.length===0?"":(h.startsWith(e,"<style>",!1)||(e=l.tag("style",{},e)),e)}function at(e){return e=h.trim(e),e.length===0?"":`
|
|
11
11
|
<style>
|
|
12
12
|
:host { display: ${e};}
|
|
13
13
|
:host([hidden]) {display: none;}
|
|
14
14
|
</style>
|
|
15
|
-
`}return
|
|
15
|
+
`}return se(ct);})();
|
package/dist/webitem-script.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// webitem.js Library to simplify creating HTML5 Custom Elements
|
|
2
2
|
// https://github.com/ahabra/webitem
|
|
3
|
-
// Copyright 2021 (C) Abdul Habra. Version 0.
|
|
3
|
+
// Copyright 2021 (C) Abdul Habra. Version 0.7.0.
|
|
4
4
|
// Apache License Version 2.0
|
|
5
5
|
|
|
6
6
|
|
|
@@ -575,6 +575,7 @@ ${t2}`;
|
|
|
575
575
|
nameWithDash,
|
|
576
576
|
html,
|
|
577
577
|
css,
|
|
578
|
+
styleSheets,
|
|
578
579
|
display,
|
|
579
580
|
propertyList,
|
|
580
581
|
actionList,
|
|
@@ -585,7 +586,7 @@ ${t2}`;
|
|
|
585
586
|
constructor() {
|
|
586
587
|
super();
|
|
587
588
|
const root = this;
|
|
588
|
-
addHtml(this, html, css, display);
|
|
589
|
+
addHtml(this, html, css, styleSheets, display);
|
|
589
590
|
this.wi = {};
|
|
590
591
|
this.wi.properties = bindProperties(this, propertyList);
|
|
591
592
|
this.wi.actions = defineActions(this, actionList);
|
|
@@ -652,11 +653,14 @@ ${t2}`;
|
|
|
652
653
|
});
|
|
653
654
|
});
|
|
654
655
|
}
|
|
655
|
-
function addHtml(root, html, css, display) {
|
|
656
|
+
function addHtml(root, html, css, styleSheets, display) {
|
|
656
657
|
html = getHtml(root, html);
|
|
657
658
|
const shadow = root.attachShadow({ mode: "open" });
|
|
658
659
|
const nodes = Domer_exports.createElements(getCss(css, display) + html);
|
|
659
660
|
shadow.append(...nodes);
|
|
661
|
+
if (Array.isArray(styleSheets) && styleSheets.length > 0) {
|
|
662
|
+
shadow.adoptedStyleSheets.push(...styleSheets);
|
|
663
|
+
}
|
|
660
664
|
}
|
|
661
665
|
function getHtml(root, html) {
|
|
662
666
|
return Objecter_exports.isFunction(html) ? html(root) : html;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@techexp/webitem",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Library to simplify creating Web Components/Custom Elements",
|
|
5
5
|
"author": "Abdul Habra",
|
|
6
6
|
"license": "Apache",
|
|
@@ -33,15 +33,15 @@
|
|
|
33
33
|
"@web/test-runner": "^0.20.2",
|
|
34
34
|
"@web/test-runner-commands": "^0.9.0",
|
|
35
35
|
"@web/test-runner-puppeteer": "^0.18.0",
|
|
36
|
-
"chalk": "^5.
|
|
37
|
-
"esbuild": "^0.
|
|
38
|
-
"eslint": "^9.
|
|
39
|
-
"fs-extra": "^11.3.
|
|
40
|
-
"npm-check-updates": "^
|
|
41
|
-
"serve": "^14.2.
|
|
36
|
+
"chalk": "^5.6.2",
|
|
37
|
+
"esbuild": "^0.27.2",
|
|
38
|
+
"eslint": "^9.39.2",
|
|
39
|
+
"fs-extra": "^11.3.3",
|
|
40
|
+
"npm-check-updates": "^19.3.1",
|
|
41
|
+
"serve": "^14.2.5"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@techexp/data-bind": "^0.9.
|
|
45
|
-
"@techexp/jshelper": "^0.7.
|
|
44
|
+
"@techexp/data-bind": "^0.9.5",
|
|
45
|
+
"@techexp/jshelper": "^0.7.2"
|
|
46
46
|
}
|
|
47
47
|
}
|
package/src/index.html
CHANGED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {expect} from '@esm-bundle/chai'
|
|
2
|
+
import * as webitem from '../webitem'
|
|
3
|
+
import {Domer} from "@techexp/jshelper";
|
|
4
|
+
|
|
5
|
+
describe('webitem performance tests', () => {
|
|
6
|
+
const pathToLocalCss = 'src/test/perf-test.css'
|
|
7
|
+
const propertyList = [ {name: 'life', value:42} ]
|
|
8
|
+
const count = 10000
|
|
9
|
+
|
|
10
|
+
console.log(`Each benchmark repeats ${count} times:`)
|
|
11
|
+
|
|
12
|
+
it('benchmark with no css', () => {
|
|
13
|
+
const nameWithDash = 'wi-perf-no-css'
|
|
14
|
+
const html = `<h1>${nameWithDash}</h1>`
|
|
15
|
+
|
|
16
|
+
const wasCreated = webitem.defineElement({nameWithDash, html, propertyList})
|
|
17
|
+
expect(wasCreated).to.be.true
|
|
18
|
+
const el = createAndAddElement(nameWithDash, `hello`)
|
|
19
|
+
expect(getH1Color(el)).to.equal('rgb(0, 0, 0)')
|
|
20
|
+
|
|
21
|
+
const usualDelay = 80
|
|
22
|
+
benchmark('No CSS', nameWithDash, count, usualDelay)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('benchmark with style in body', () => {
|
|
26
|
+
const nameWithDash = 'wi-perf-style-in-body'
|
|
27
|
+
const html = `
|
|
28
|
+
<style>
|
|
29
|
+
h1 { color: red; }
|
|
30
|
+
</style>
|
|
31
|
+
<h1>${nameWithDash}</h1>`
|
|
32
|
+
|
|
33
|
+
const wasCreated = webitem.defineElement({nameWithDash, html, propertyList})
|
|
34
|
+
expect(wasCreated).to.be.true
|
|
35
|
+
const el = createAndAddElement(nameWithDash, `hello`)
|
|
36
|
+
expect(getH1Color(el)).to.equal('rgb(255, 0, 0)')
|
|
37
|
+
|
|
38
|
+
const usualDelay = 80
|
|
39
|
+
benchmark('CSS style in body', nameWithDash, count, usualDelay)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
it('benchmark with css link in html', () => {
|
|
44
|
+
const nameWithDash = 'wi-perf-css-in-html'
|
|
45
|
+
const html = `
|
|
46
|
+
<link rel="stylesheet" href="${pathToLocalCss}">
|
|
47
|
+
<h1>${nameWithDash}</h1>`
|
|
48
|
+
|
|
49
|
+
const wasCreated = webitem.defineElement({nameWithDash, html, propertyList})
|
|
50
|
+
expect(wasCreated).to.be.true
|
|
51
|
+
const el = createAndAddElement(nameWithDash, `hello`)
|
|
52
|
+
// FIXME why does this fail?
|
|
53
|
+
// expect(getH1Color(el)).to.equal('rgb(255, 0, 0)')
|
|
54
|
+
|
|
55
|
+
const usualDelay = 140
|
|
56
|
+
benchmark('CSS link in html', nameWithDash, count, usualDelay)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('benchmark with constructable sheet', () => {
|
|
60
|
+
const nameWithDash = 'wi-perf-constructable-sheet'
|
|
61
|
+
const html = `<h1 id="title">${nameWithDash}</h1>`
|
|
62
|
+
|
|
63
|
+
const sheet = new CSSStyleSheet()
|
|
64
|
+
sheet.replaceSync('h1 { color: red; }')
|
|
65
|
+
|
|
66
|
+
const styleSheets = [sheet]
|
|
67
|
+
|
|
68
|
+
const wasCreated = webitem.defineElement({nameWithDash, html,
|
|
69
|
+
styleSheets,
|
|
70
|
+
propertyList})
|
|
71
|
+
expect(wasCreated).to.be.true
|
|
72
|
+
const el = createAndAddElement(nameWithDash, `hello`)
|
|
73
|
+
expect(getH1Color(el)).to.equal('rgb(255, 0, 0)')
|
|
74
|
+
|
|
75
|
+
const usualDelay = 70
|
|
76
|
+
benchmark('Constructable sheet', nameWithDash, count, usualDelay)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
function benchmark(title, wiName, count, usualDelay) {
|
|
82
|
+
// warm up
|
|
83
|
+
for (let i=0; i< 100; i++) {
|
|
84
|
+
const el = createAndAddElement(wiName, `hello ${i}`)
|
|
85
|
+
expect(el.wi.properties.life).to.equal(42)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const t0 = Date.now()
|
|
89
|
+
for (let i=0; i< count; i++) {
|
|
90
|
+
createAndAddElement(wiName, `hello ${i}`)
|
|
91
|
+
}
|
|
92
|
+
const end = Date.now()
|
|
93
|
+
console.log(`${title}: Duration=${end-t0} millis. Usually about ${usualDelay} millis.`)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function createAndAddElement(name, content) {
|
|
97
|
+
const el = Domer.createElement(name, {}, content)
|
|
98
|
+
document.body.appendChild(el)
|
|
99
|
+
return el
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function getH1Color(el) {
|
|
103
|
+
const h1 = Domer.first('h1', el.shadowRoot)
|
|
104
|
+
const style = window.getComputedStyle(h1)
|
|
105
|
+
return style.color
|
|
106
|
+
}
|
|
107
|
+
|
package/src/test/webitem.test.js
CHANGED
|
@@ -5,7 +5,7 @@ import {Domer, Objecter} from '@techexp/jshelper'
|
|
|
5
5
|
|
|
6
6
|
describe('webitem.defineElement()', () => {
|
|
7
7
|
|
|
8
|
-
describe('element
|
|
8
|
+
describe('element creation', () => {
|
|
9
9
|
it('creates a new element', () => {
|
|
10
10
|
const name = 'wi-t1'
|
|
11
11
|
webitem.defineElement({nameWithDash: name})
|
package/src/webitem.js
CHANGED
|
@@ -3,32 +3,35 @@ import bind from '@techexp/data-bind'
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Define a custom element
|
|
6
|
+
*
|
|
6
7
|
* @param options An object containing the following fields
|
|
7
|
-
* nameWithDash
|
|
8
|
-
* html
|
|
9
|
-
* css
|
|
10
|
-
*
|
|
8
|
+
* * `nameWithDash`: Required. String. Name of the custom element.
|
|
9
|
+
* * `html`: Optional. String or Function. The HTML content of the element
|
|
10
|
+
* * `css`: Optional. String. The CSS to apply on the element
|
|
11
|
+
* * `styleSheets`: Optional: An array of `CSSStyleSheet` objects. This way you can share
|
|
12
|
+
* style sheets among components.
|
|
13
|
+
* * `propertyList`: Optional. Array. Objects defining properties of the element. Each property
|
|
11
14
|
* definition consists of {name, value, [sel], [attr], [onChange]}
|
|
12
|
-
* actionList
|
|
15
|
+
* * `actionList`: Optional. Array. Objects defining actions. Each action definition consists of
|
|
13
16
|
* {name, action}, where action is a function definition.
|
|
14
|
-
* eventHandlerList
|
|
17
|
+
* * `eventHandlerList`: Optional. Array. Objects defining event handlers of element. Each
|
|
15
18
|
* handler definition consists of {sel, eventName, listener}
|
|
16
|
-
* display
|
|
17
|
-
* @returns true if the element was created, false if the element already exists, in which case
|
|
19
|
+
* * `display`: Optional. String. CSS display attribute. One of inline (default), inline-block, block.
|
|
20
|
+
* @returns `true` if the element was created, `false` if the element already exists, in which case
|
|
18
21
|
* it will not be re-created.
|
|
19
22
|
*
|
|
20
|
-
* The created element is a standard DOM custom element with and extra property
|
|
23
|
+
* The created element is a standard DOM custom element with and extra property `wi` that
|
|
21
24
|
* contains the following members:
|
|
22
|
-
* properties
|
|
23
|
-
* actions
|
|
24
|
-
* addProperty(name, value, sel, attr, onChange)
|
|
25
|
+
* * `properties`: an object that contains all the defined properties in propertyList
|
|
26
|
+
* * `actions`: an object that contains all the defined actions in actionList
|
|
27
|
+
* * `addProperty(name, value, sel, attr, onChange)`: a function with the given arguments to
|
|
25
28
|
* add new properties on the instance of the web element
|
|
26
|
-
* addAction(name, action)
|
|
29
|
+
* * `addAction(name, action)`: a function with the given arguments to add new actions
|
|
27
30
|
* on the instance of the web element
|
|
28
|
-
* addEventListener(sel, eventName, listener)
|
|
31
|
+
* * `addEventListener(sel, eventName, listener)`: a function with the given arguments to
|
|
29
32
|
* add new event listeners on the instance of the web element
|
|
30
33
|
*/
|
|
31
|
-
export function defineElement({nameWithDash, html, css, display,
|
|
34
|
+
export function defineElement({nameWithDash, html, css, styleSheets, display,
|
|
32
35
|
propertyList, actionList, eventHandlerList}) {
|
|
33
36
|
|
|
34
37
|
if (customElements.get(nameWithDash)) return false
|
|
@@ -37,7 +40,7 @@ export function defineElement({nameWithDash, html, css, display,
|
|
|
37
40
|
constructor() {
|
|
38
41
|
super()
|
|
39
42
|
const root = this
|
|
40
|
-
addHtml(this, html, css, display)
|
|
43
|
+
addHtml(this, html, css, styleSheets, display)
|
|
41
44
|
this.wi = {}
|
|
42
45
|
this.wi.properties = bindProperties(this, propertyList)
|
|
43
46
|
this.wi.actions = defineActions(this, actionList)
|
|
@@ -117,12 +120,15 @@ function addHandler(root, {sel, eventName, listener}) {
|
|
|
117
120
|
})
|
|
118
121
|
}
|
|
119
122
|
|
|
120
|
-
function addHtml(root, html, css, display) {
|
|
123
|
+
function addHtml(root, html, css, styleSheets, display) {
|
|
121
124
|
html = getHtml(root, html)
|
|
122
125
|
|
|
123
126
|
const shadow = root.attachShadow({mode: 'open'})
|
|
124
127
|
const nodes = Domer.createElements(getCss(css, display) + html)
|
|
125
128
|
shadow.append(...nodes)
|
|
129
|
+
if (Array.isArray(styleSheets) && styleSheets.length > 0) {
|
|
130
|
+
shadow.adoptedStyleSheets.push(...styleSheets)
|
|
131
|
+
}
|
|
126
132
|
}
|
|
127
133
|
|
|
128
134
|
function getHtml(root, html) {
|