@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 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. `propertyList`: Optional, Array of objects. Objects defining properties of the component. Each property
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
- 5. `eventHandlerList`: Optional, Array of objects. Objects define event handlers of the component.
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
- 6. `actionList`: Optional, Array of objects. Objects define actions that can be defined on the component.
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
- 7. `display`: Optional, String. A CSS display attribute. A possible value can be
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 bby this library as follows:
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 file within the component, a possible solution is to use
128
- the `<link>` tagDef in the component's html, for exmaple, add the next line at the top of your component's html:
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 comonent itself as an argument and returns a string representing the html of the component.
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 proiperty without `sel` value.
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.
@@ -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.6.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.6.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 v=Object.defineProperty;var ne=Object.getOwnPropertyDescriptor;var re=Object.getOwnPropertyNames;var ie=Object.prototype.hasOwnProperty;var ue=(e,t)=>{for(var n in t)v(e,n,{get:t[n],enumerable:!0})},oe=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of re(t))!ie.call(e,i)&&i!==n&&v(e,i,{get:()=>t[i],enumerable:!(r=ne(t,i))||r.enumerable});return e};var fe=e=>oe(v({},"__esModule",{value:!0}),e);var at={};ue(at,{defineElement:()=>Ye});var se=Object.defineProperty,g=(e,t)=>{for(var n in t)se(e,n,{get:t[n],enumerable:!0})},l={};g(l,{add:()=>xe,all:()=>F,classPresentIf:()=>$e,createElement:()=>Ae,createElements:()=>M,first:()=>be,getAttributes:()=>Se,id:()=>Ee,removeElements:()=>Te,setContent:()=>Ne,tag:()=>_});var d={};g(d,{equals:()=>q,forEachEntry:()=>L,has:()=>le,isDate:()=>S,isFunction:()=>P,isInteger:()=>ce,isNil:()=>ae,isNumber:()=>R,isRegExp:()=>j,isString:()=>p});function ae(e){return e==null}function p(e){return m(e,"String")}function P(e){return m(e,"Function")}function S(e){return m(e,"Date")}function R(e){return m(e,"Number")?Number.isNaN(e)?!1:Number.isFinite(e):!p(e)||(e=e.trim(),e==="")?!1:!isNaN(e)}function ce(e){return R(e)?Number.isInteger(Number.parseFloat(e)):!1}function j(e){return m(e,"RegExp")}function m(e,t){return Object.prototype.toString.call(e)===`[object ${t}]`}function L(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 le(e,t){return!e||!t?!1:Object.prototype.hasOwnProperty.call(e,t)}function q(e,t){return e===t?!0:e===void 0||t===void 0?!1:de(e,t)}function de(e,t){return N(e)||N(t)?e===t:he(e,t)}var me=new Set(["boolean","number","bigint","string","symbol"]);function N(e){return me.has(typeof e)}function he(e,t){return ge(e,t)?pe(e,t)?!0:ye(e,t):!1}function ge(e,t){return T(e)===T(t)}function T(e){return Object.prototype.toString.call(e)}function pe(e,t){return S(e)&&S(t)?e.getTime()===t.getTime():!1}function ye(e,t){let n=Object.keys(e);return n.length!==Object.keys(t).length?!1:n.every(r=>q(e[r],t[r]))}function Ee(e,t=document){return O(t)&&(t=t.shadowRoot),t.getElementById(e)}function F(e,t=document){return O(t)&&(t=t.shadowRoot),Array.from(t.querySelectorAll(e))}function be(e,t=document){return O(t)&&(t=t.shadowRoot),e.includes("/")?we(e,t):t.querySelector(e)}function we(e,t){let n=e.split("/").map(r=>r.trim()).filter(r=>r.length>0);for(let r of n)if(t=ve(r,t),t===null)break;return t}function ve(e,t){return e==="shadowRoot"||e==="shadow-root"?t.shadowRoot:t.querySelector(e)}function O(e){return e&&e.shadowRoot&&e.tagName.includes("-")}function Se(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 M(e=""){if(e=e.trim(),!e)return[];let t=document.createElement("template");return t.innerHTML=e,Array.from(t.content.childNodes)}function Ae(e,t={},n=""){let r=_(e,t,n),i=M(r);return i.length===0?null:i[0]}function _(e,t={},n=""){if(!e)return"";let r=Le(t);return`<${e}${r}>${n}</${e}>`}function Le(e){let t=[];return L(e,(r,i)=>{t.push(`${r}="${i}"`)}),(t.length>0?" ":"")+t.join(" ")}var Oe=new Set(["beforebegin","afterbegin","beforeend","afterend"]);function xe(e,t,n="beforeend"){return n=n.toLowerCase(),Oe.has(n)?(p(t)?e.insertAdjacentHTML(n,t):Ce(e,t,n),!0):!1}function Ce(e,t,n){Array.isArray(t)?t.forEach(r=>e.insertAdjacentElement(n,r)):e.insertAdjacentElement(n,t)}function Ne(e,...t){e.innerHTML="",e.append(...t)}function Te(e,t=document){F(e,t).forEach(r=>{r.parentNode.removeChild(r)})}function $e(e,t,n){if(!e)return;let r=n?"add":"remove";e.classList[r](t)}var h={};g(h,{endsWith:()=>D,indexOf:()=>x,indexOfFirstMatch:()=>Pe,indexOfLastMatch:()=>Re,isEmpty:()=>y,removePrefix:()=>H,removeSuffix:()=>I,removeSurrounding:()=>je,replaceAll:()=>V,replaceTemplate:()=>Me,startsWith:()=>W,strip:()=>De,stripEnd:()=>We,stripStart:()=>_e,substringAfter:()=>qe,substringBefore:()=>Fe,trim:()=>A});function x(e,t,n=0,r=!1){return e?r?e.toLowerCase().indexOf(t.toLowerCase(),n):e.indexOf(t,n):-1}function Pe(e,t){return!t||!e?-1:e.split("").findIndex(t)}function Re(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 W(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 D(e,t,n=!1){return n?e.toLowerCase().endsWith(t.toLowerCase()):e.endsWith(t)}function H(e,t,n=!1){return W(e,t,n)&&(e=e.substring(t.length)),e}function I(e,t,n=!1){return D(e,t,n)&&(e=e.substring(0,e.length-t.length)),e}function je(e,t,n,r=!1){return I(H(e,t,r),n,r)}function qe(e,t,n=!1){if(!t)return e;let r=x(e,t,0,n);return r<0?"":e.substring(r+t.length)}function Fe(e,t,n=!1){if(!t)return"";let r=x(e,t,0,n);return r<0?e:e.substring(0,r)}function A(e){return y(e)?"":(p(e)||(e=String(e)),e.trim(e))}function y(e){return e==null||e===""}function V(e,t,n){if(P(String.prototype.replaceAll))return e.replaceAll(t,n);if(j(t))return e.replace(t,n);let r=new RegExp(t,"g");return e.replace(r,n)}function Me(e="",t={},n="${",r="}"){return L(t,(i,u)=>{u!==void 0&&(i=n+i+r,e=V(e,i,u))}),e}function _e(e,t=""){return y(e)?"":t?k(e,new Set(Array.from(t))):e}function k(e,t){for(let n=0;n<e.length;n++)if(!t.has(e.charAt(n)))return e.substring(n);return""}function We(e,t=""){return y(e)?"":t?B(e,new Set(Array.from(t))):e}function B(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 De(e,t=""){if(e===void 0||e==="")return"";if(!t)return e;let n=new Set(Array.from(t));return e=k(e,n),e?B(e,n):""}var He={};g(He,{compareLines:()=>Ie});function Ie(e,t,{trim:n=!0,skipEmpty:r=!0,caseSensitive:i=!0}={trim:!0,skipEmpty:!0,caseSensitive:!0}){return e=$(e,{trim:n,skipEmpty:r}),t=$(t,{trim:n,skipEmpty:r}),e.length!==t.length?`t1 has ${e.length} lines(s) while t2 has ${t.length} line(s).`:Ve(e,t,i)}function Ve(e,t,n){for(let r=0;r<e.length;r++){let i=ke(e[r],t[r],r,n);if(i.length>0)return i}return""}function ke(e,t,n,r){let i=r?e:e.toLowerCase(),u=r?t:t.toLowerCase();return i!==u?`Line #${n+1} mismatch.
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 $(e,{trim:t,skipEmpty:n}){return t&&(e=A(e)),e=e.split(`
10
- `),t&&(e=e.map(r=>A(r))),n&&(e=e.filter(r=>!!r)),e}function C({obj:e={},prop:t,sel:n,attr:r,root:i=document,getter:u,setter:o,onChange:f}){Xe(t),ze(e,t);let s={};return u||(u=()=>Ge({prop:t,sel:n,attr:r,root:i,objNotBound:s})),o||(o=a=>Je({prop:t,value:a,root:i,sel:n,attr:r,objNotBound:s})),Be({obj:e,prop:t,getter:u,setter:o,onChange:f})}function Be({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 E=e=>e.type==="checkbox",b=e=>e.type==="radio",G=e=>e.nodeName.toLowerCase()==="select",J=e=>e.nodeName.toLowerCase()==="input-field",K=e=>"value"in e,z=e=>new Set(Array.isArray(e)?e:[e]);function ze(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 Ge({prop:e,root:t,sel:n,attr:r,objNotBound:i}){return n?Ke(t,n,r):i[e]}function Je({prop:e,value:t,root:n,sel:r,attr:i,objNotBound:u}){if(r){Qe(n,r,t,i);return}u[e]=t}function Ke(e,t,n){let r=Q(e,t);if(r.length===0)return null;let i=r[0];if(n)return i.getAttribute(n);if(!K(i))return i.innerHTML;if(E(i))return r.filter(u=>E(u)&&u.checked).map(u=>u.value==="on"?u.name:u.value);if(G(i))return[...i.querySelectorAll("option")].filter(o=>o.selected).map(o=>o.value);if(!(b(i)&&(i=r.filter(b).find(u=>u.checked),!i)))return J(i)?i.getAttribute("value"):i.value}function Qe(e,t,n,r){let i=Q(e,t);if(i.length===0)return;let u=i[0];if(E(u)){let o=z(n);i.filter(E).forEach(f=>f.checked=o.has(f.value)||o.has(f.name));return}if(G(u)){let o=z(n);u.querySelectorAll("option").forEach(f=>f.selected=o.has(f.value));return}if(b(u)){i.filter(b).forEach(o=>o.checked=o.value===n);return}i.forEach(o=>Ue(o,n,r))}function Ue(e,t,n){n?e.setAttribute(n,t):K(e)?e.value=t:J(e)?e.setAttribute("value",t):e.innerHTML=t}function Q(e,t){let n=e.querySelectorAll(t);return n.length===0&&console.warn(`No elements found matching selector ${t}`),[...n]}function Xe(e){if(typeof e!="string"||e.length===0)throw"'prop' argument must be a String defining the name a property."}function Ye({nameWithDash:e,html:t,css:n,display:r,propertyList:i,actionList:u,eventHandlerList:o}){if(customElements.get(e))return!1;let f=class extends HTMLElement{constructor(){super();let s=this;it(this,t,n,r),this.wi={},this.wi.properties=Ze(this,i),this.wi.actions=nt(this,u),rt(this,o),this.wi.addProperty=function(a,c,w,Z,ee){let te={name:a,value:c,sel:w,attr:Z,onChange:ee};U(s.wi.properties,te,s)},this.wi.addAction=(a,c)=>X(s,s.wi.actions,a,c),this.wi.addEventListener=(a,c,w)=>Y(s,{sel:a,eventName:c,listener:w})}};return customElements.define(e,f),!0}function Ze(e,t){let n={};return tt(t)&&t.forEach(r=>U(n,r,e)),n}function U(e,t,n){let r=et(t,n);C({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 et(e,t){if(e.onChange)return(n,r)=>e.onChange(t,n,r)}function tt(e){if(!e)return!1;if(!Array.isArray(e))throw"propertyList must be an array of {name, value, [sel], [attr]} objects";return!0}function nt(e,t){let n={};return t&&t.forEach(r=>{X(e,n,r.name,r.action)}),n}function X(e,t,n,r){!d.isString(n)||!d.isFunction(r)||(t[n]=r.bind(e))}function rt(e,t){if(t){if(!Array.isArray(t))throw"eventHandlerList must be an array of {sel, eventName, listener} objects";t.forEach(n=>Y(e,n))}}function Y(e,{sel:t,eventName:n,listener:r}){l.all(t,e.shadowRoot).forEach(u=>{u.addEventListener(n,o=>{r(o,e)})})}function it(e,t,n,r){t=ut(e,t);let i=e.attachShadow({mode:"open"}),u=l.createElements(ot(n,r)+t);i.append(...u)}function ut(e,t){return d.isFunction(t)?t(e):t}function ot(e,t){return st(t)+ft(e)}function ft(e){return e=h.trim(e),e.length===0?"":(h.startsWith(e,"<style>",!1)||(e=l.tag("style",{},e)),e)}function st(e){return e=h.trim(e),e.length===0?"":`
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 fe(at);})();
15
+ `}return se(ct);})();
@@ -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.6.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.6.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.4.1",
37
- "esbuild": "^0.25.4",
38
- "eslint": "^9.27.0",
39
- "fs-extra": "^11.3.0",
40
- "npm-check-updates": "^18.0.1",
41
- "serve": "^14.2.4"
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.4",
45
- "@techexp/jshelper": "^0.7.0"
44
+ "@techexp/data-bind": "^0.9.5",
45
+ "@techexp/jshelper": "^0.7.2"
46
46
  }
47
47
  }
package/src/index.html CHANGED
@@ -1,4 +1,4 @@
1
- <html>
1
+ <html lang="en">
2
2
  <head>
3
3
  <title>WebItem Visual Test</title>
4
4
  <style>
@@ -0,0 +1,3 @@
1
+ h1 {
2
+ color: red;
3
+ }
@@ -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
+
@@ -5,7 +5,7 @@ import {Domer, Objecter} from '@techexp/jshelper'
5
5
 
6
6
  describe('webitem.defineElement()', () => {
7
7
 
8
- describe('element creattion', () => {
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: Required. String. Name of the custom element.
8
- * html: Optional. String or Function. The HTML content of the element
9
- * css: Optional. String. The CSS to apply on the element
10
- * propertyList: Optional. Array. Objects defining properties of the element. Each property
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: Optional. Array. Objects defining actions. Each action definition consists of
15
+ * * `actionList`: Optional. Array. Objects defining actions. Each action definition consists of
13
16
  * {name, action}, where action is a function definition.
14
- * eventHandlerList: Optional. Array. Objects defining event handlers of element. Each
17
+ * * `eventHandlerList`: Optional. Array. Objects defining event handlers of element. Each
15
18
  * handler definition consists of {sel, eventName, listener}
16
- * display: Optional. String. CSS display attribute. One of inline (default), inline-block, block.
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 "wi" that
23
+ * The created element is a standard DOM custom element with and extra property `wi` that
21
24
  * contains the following members:
22
- * properties: an object that contains all the defined properties in propertyList
23
- * actions: an object that contains all the defined actions in actionList
24
- * addProperty(name, value, sel, attr, onChange): a function with the given arguments to
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): a function with the given arguments to add new actions
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): a function with the given arguments to
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) {