wrec 0.4.1 → 0.4.2
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 +31 -15
- package/dist/wrec.min.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,37 +37,38 @@ Here are the steps:
|
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
1. Create the file `my-counter.js` containing the following.
|
|
40
|
-
The
|
|
41
|
-
"
|
|
40
|
+
The tagged template literals with the tags `css` and `html` trigger the VS Code extension
|
|
41
|
+
"Prettier" to add syntax highlighting and format the CSS and HTML strings.
|
|
42
42
|
|
|
43
43
|
```js
|
|
44
|
-
import Wrec from "wrec";
|
|
44
|
+
import Wrec, { css, html } from "wrec";
|
|
45
45
|
|
|
46
46
|
class MyCounter extends Wrec {
|
|
47
47
|
static properties = {
|
|
48
48
|
count: { type: Number },
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
static css =
|
|
51
|
+
static css = css`
|
|
52
52
|
.counter {
|
|
53
53
|
display: flex;
|
|
54
54
|
align-items: center;
|
|
55
55
|
gap: 0.5rem;
|
|
56
56
|
}
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
button {
|
|
59
59
|
background-color: lightgreen;
|
|
60
60
|
}
|
|
61
|
-
|
|
61
|
+
|
|
62
62
|
button:disabled {
|
|
63
63
|
background-color: gray;
|
|
64
64
|
}
|
|
65
65
|
`;
|
|
66
66
|
|
|
67
|
-
static html =
|
|
67
|
+
static html = html`
|
|
68
68
|
<div>
|
|
69
|
-
<button onClick="decrement" type="button"
|
|
70
|
-
|
|
69
|
+
<button onClick="decrement" type="button" disabled="this.count === 0">
|
|
70
|
+
-
|
|
71
|
+
</button>
|
|
71
72
|
<span>this.count</span>
|
|
72
73
|
<button onClick="this.count++" type="button">+</button>
|
|
73
74
|
<span>(this.count < 10 ? "single" : "double") + " digit"</span>
|
|
@@ -129,12 +130,15 @@ Only attribute values and text content
|
|
|
129
130
|
that refer to modified web component properties are updated.
|
|
130
131
|
Attribute values and text content that contain references to properties
|
|
131
132
|
must be valid JavaScript expressions that are NOT surrounded by `${...}`.
|
|
132
|
-
For an example of this kind of web component, see `
|
|
133
|
+
For an example of this kind of web component, see `examples/hello-world.js`.
|
|
134
|
+
|
|
135
|
+
Wrec evaluates JavaScript expressions in the context of a web component instance
|
|
136
|
+
which can be referred to with the `this` keyword in the expressions.
|
|
133
137
|
|
|
134
138
|
Wrec supports conditional and iterative generation of HTML.
|
|
135
|
-
See `
|
|
139
|
+
See `examples/temperature-eval.js` for an example of a web component
|
|
136
140
|
that conditionally decides what to render based on an attribute value.
|
|
137
|
-
See `
|
|
141
|
+
See `examples/radio-group.js` for an example of a web component
|
|
138
142
|
that iterates over values in a comma-delimited attribute value
|
|
139
143
|
to determine what to render.
|
|
140
144
|
|
|
@@ -149,7 +153,7 @@ In all these cases, if the user changes the value of the form element,
|
|
|
149
153
|
the specified property is updated.
|
|
150
154
|
When the property is updated,
|
|
151
155
|
the displayed value of the form element is updated.
|
|
152
|
-
For examples, see `
|
|
156
|
+
For examples, see `examples/data-bind.js`.
|
|
153
157
|
|
|
154
158
|
Web components that extend `Wrec` can contribute values to
|
|
155
159
|
form submissions by adding the following line to their class definition.
|
|
@@ -184,7 +188,7 @@ to any domain except that of your web app:
|
|
|
184
188
|
|
|
185
189
|
## More Examples
|
|
186
190
|
|
|
187
|
-
Check out the web app in the `
|
|
191
|
+
Check out the web app in the `examples` directory.
|
|
188
192
|
To run it, cd to that directory, enter `npm install`,
|
|
189
193
|
enter `npm run dev`, and browse localhost:5173.
|
|
190
194
|
|
|
@@ -194,5 +198,17 @@ The next two uses the Wrec library.
|
|
|
194
198
|
Compare the files `counter-vanilla.js` and `counter-wrec.js`
|
|
195
199
|
to see how much using Wrec simplifies the code.
|
|
196
200
|
|
|
197
|
-
The `
|
|
201
|
+
The `examples` app renders several other
|
|
202
|
+
web components that are built with wrec.
|
|
198
203
|
Examine their code for more examples of wrec usage.
|
|
204
|
+
|
|
205
|
+
## Tests
|
|
206
|
+
|
|
207
|
+
wrec has an extensive set of Playwright tests.
|
|
208
|
+
To run them:
|
|
209
|
+
|
|
210
|
+
1. Clone the wrec repository.
|
|
211
|
+
1. cd to the `examples` directory.
|
|
212
|
+
1. Enter `npm install`.
|
|
213
|
+
1. Enter `npm run testui`.
|
|
214
|
+
1. Click the right pointing triangle.
|
package/dist/wrec.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const FIRST_CHAR="a-zA-Z_$",OTHER_CHAR=FIRST_CHAR+"0-9",IDENTIFIER=`[${FIRST_CHAR}][${OTHER_CHAR}]*`,REFERENCE_RE=new RegExp(`^this.${IDENTIFIER}$`),REFERENCES_RE=new RegExp(`this.${IDENTIFIER}`,"g"),SKIP=5;class Wrec extends HTMLElement{static#t=document.createElement("template");#e=new Map;#s;#r;#o=new Map;constructor(){super(),this.attachShadow({mode:"open"});this.constructor["#propertyToExpressionsMap"]||(this.constructor["#propertyToExpressionsMap"]=new Map),this.constructor.formAssociated&&(this.#r=this.attachInternals(),this.#s=new FormData,this.#r.setFormValue(this.#s))}attributeChangedCallback(t,e,s){const r=this.#n(t,s);this[t]=r,this.#i(t,r)}#a(t,e,s){t.addEventListener("input",t=>{this[e]=t.target.value});let r=this.#o.get(e);r||(r=[],this.#o.set(e,r)),r.push(s?{element:t,attrName:s}:t)}buildDOM(){const t=this.constructor;let e=t.css?`<style>${t.css}</style>`:"";e+=t.html,Wrec.#t.innerHTML=e,this.shadowRoot.replaceChildren(Wrec.#t.content.cloneNode(!0))}connectedCallback(){this.#c(),this.buildDOM(),requestAnimationFrame(()=>{this.#l(this.shadowRoot),this.#h(this.shadowRoot),this.constructor.processed=!0})}#c(){const t=this.constructor.properties,{observedAttributes:e}=this.constructor;for(const[s,r]of Object.entries(t))this.#u(s,r,e)}#u(t,e,s){const r=s.includes(t)&&this.hasAttribute(t)?this.#p(t):e.value
|
|
1
|
+
const FIRST_CHAR="a-zA-Z_$",OTHER_CHAR=FIRST_CHAR+"0-9",IDENTIFIER=`[${FIRST_CHAR}][${OTHER_CHAR}]*`,REFERENCE_RE=new RegExp(`^this.${IDENTIFIER}$`),REFERENCES_RE=new RegExp(`this.${IDENTIFIER}`,"g"),SKIP=5;class Wrec extends HTMLElement{static#t=document.createElement("template");#e=new Map;#s;#r;#o=new Map;constructor(){super(),this.attachShadow({mode:"open"});this.constructor["#propertyToExpressionsMap"]||(this.constructor["#propertyToExpressionsMap"]=new Map),this.constructor.formAssociated&&(this.#r=this.attachInternals(),this.#s=new FormData,this.#r.setFormValue(this.#s))}attributeChangedCallback(t,e,s){const r=this.#n(t,s);this[t]=r,this.#i(t,r)}#a(t,e,s){t.addEventListener("input",t=>{this[e]=t.target.value});let r=this.#o.get(e);r||(r=[],this.#o.set(e,r)),r.push(s?{element:t,attrName:s}:t)}buildDOM(){const t=this.constructor;let e=t.css?`<style>${t.css}</style>`:"";e+=t.html,Wrec.#t.innerHTML=e,this.shadowRoot.replaceChildren(Wrec.#t.content.cloneNode(!0))}connectedCallback(){this.#c(),this.buildDOM(),requestAnimationFrame(()=>{this.#l(this.shadowRoot),this.#h(this.shadowRoot),this.constructor.processed=!0})}#c(){const t=this.constructor.properties,{observedAttributes:e}=this.constructor;for(const[s,r]of Object.entries(t))this.#u(s,r,e)}#u(t,e,s){const r=s.includes(t)&&this.hasAttribute(t)?this.#p(t):e.value,o="#"+t;this[o]=r,Object.defineProperty(this,t,{enumerable:!0,get(){return this[o]},set(e){if(e===this[o])return;if(this[o]=e,this.hasAttribute(t)){e!==this.#p(t)&&this.#m(this,t,e)}this.#d(t);const s=this.propertyToParentPropertyMap,r=s?s.get(t):null;if(r){this.getRootNode().host.setAttribute(r,e)}this.#i(t,e)}})}#f(t){const e=t.localName.includes("-");for(const s of t.getAttributeNames()){const r=t.getAttribute(s);if(REFERENCE_RE.test(r)){const o=r.substring(SKIP);if(t[o]=this[o],"value"===s&&this.#a(t,o,s),e){let e=t.propertyToParentPropertyMap;e||(e=new Map,t.propertyToParentPropertyMap=e),e.set(s,o)}}this.#E(r,t,s)}}#b(expression){return(()=>eval(expression)).call(this)}#g(t){const{localName:e}=t;if("style"===e)return;const s=t.textContent.trim();if("textarea"===e&&REFERENCE_RE.test(s)){const e=s.substring(SKIP);this.#a(t,e),t.textContent=this[e]}else this.#E(s,t)}#p(t){return this.#n(t,this.getAttribute(t))}#n(t,e){const s=this.constructor.properties[t].type;return s===Number?Number(e):s===Boolean?Boolean(e):e}#h(t){const e=t.querySelectorAll("*");for(const t of e)this.#f(t),t.firstElementChild||this.#g(t)}static get observedAttributes(){return Object.keys(this.properties||{})}#d(t){const e=this.constructor["#propertyToExpressionsMap"].get(t)||[];for(const t of e){const e=this.#b(t),s=this.#e.get(t)||[];for(const t of s)if(t instanceof Element)this.#R(t,e);else{const{element:s,attrName:r}=t;this.#m(s,r,e)}}requestAnimationFrame(()=>{this.#A(t)})}static register(){const t=Wrec.#v(this.name);customElements.get(t)||customElements.define(t,this)}#E(t,e,s){const r=t.match(REFERENCES_RE);if(!r)return;this.constructor.processed||r.forEach(e=>{const s=e.substring(SKIP),r=this.constructor["#propertyToExpressionsMap"];let o=r.get(s);o||(o=[],r.set(s,o)),o.push(t)});let o=this.#e.get(t);o||(o=[],this.#e.set(t,o)),o.push(s?{element:e,attrName:s}:e);const n=this.#b(t);s?this.#m(e,s,n):this.#R(e,n)}static#v=t=>t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase();#i(t,e){this.#s&&(this.#s.set(t,e),this.#r.setFormValue(this.#s))}#m(t,e,s){const r=t.getAttribute(e);"boolean"==typeof s?s?r!==e&&t.setAttribute(e,e):t.removeAttribute(e):r!==s&&t.setAttribute(e,s)}#A(t){const e=this[t],s=this.#o.get(t)||[];for(const t of s)if(t instanceof Element)"textarea"===t.localName?t.value=e:t.textContent=e;else{const{element:s,attrName:r}=t;this.#m(s,r,e),s[r]=e}}#R(t,e){const{localName:s}=t;"textarea"===s?t.value=e:"string"==typeof e&&e.trim().startsWith("<")?(t.innerHTML=e,this.#l(t),this.#h(t)):t.textContent=e}#l(root){const elements=root.querySelectorAll("*");for(const element of elements)for(const attr of element.attributes){const{name:name}=attr;if(name.startsWith("on")){const eventName=name.slice(2).toLowerCase(),{value:value}=attr,fn="function"==typeof this[value]?t=>this[value](t):()=>eval(value);element.addEventListener(eventName,fn),element.removeAttribute(name)}}}}export default Wrec;export const css=String.raw;export const html=String.raw;
|