wrec 0.0.3 → 0.1.1

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
@@ -12,18 +12,99 @@ In exchange, Wrec:
12
12
  - doesn't require any tooling
13
13
  - doesn't require a build process
14
14
 
15
- The main features of Wrec are that it
16
- automates wiring event listeners
17
- and automates implementing reactivity.
15
+ The main features of Wrec are that it automates
16
+ wiring event listeners and implementing reactivity.
18
17
 
19
- Check out the web app in the `demo` directory.
20
- To run it, cd to that directory, enter `npm install`,
21
- enter `npm run dev`, and browse localhost:5173.
22
- This app begins by rendering "counter" components.
23
- The first is implemented as a vanilla web component.
24
- The next two uses the Wrec library.
25
- Compare the files `counter-vanilla.js` and `counter-wrec.js`
26
- to see how much using Wrec simplifies the code.
18
+ ## Getting Started
19
+
20
+ Let's use wrec to implement a counter component.
21
+ Here are the steps:
22
+
23
+ 1. Create a new directory for the project and `cd` to it.
24
+
25
+ 1. Create a `package.json` file entering `npm init`.
26
+
27
+ 1. Install wrec by entering `npm i wrec`.
28
+
29
+ 1. Install vite by entering `npm i -D vite`.
30
+ This is only used to run a local HTTP server.
31
+
32
+ 1. Add the following script in `package.json`:
33
+
34
+ ```json
35
+ "dev": "vite"
36
+ ```
37
+
38
+ 1. Create the file `my-counter.js` containing the following.
39
+ The comments `/*css*/` and `/*html*/` trigger the VS Code extension
40
+ "es6-string-html" to add syntax highlighting to the CSS and HTML strings.
41
+
42
+ ```js
43
+ import Wrec from "wrec";
44
+
45
+ class MyCounter extends Wrec {
46
+ static properties = {
47
+ count: { type: Number, reflect: true },
48
+ };
49
+
50
+ css() {
51
+ return /*css*/ `
52
+ .counter {
53
+ display: flex;
54
+ align-items: center;
55
+ gap: 0.5rem;
56
+ }
57
+
58
+ button {
59
+ background-color: lightgreen;
60
+ }
61
+
62
+ button:disabled {
63
+ background-color: gray;
64
+ }
65
+ `;
66
+ }
67
+
68
+ html() {
69
+ return /*html*/ `
70
+ <div>
71
+ <button onClick="decrement" type="button"
72
+ disabled="this.count === 0">-</button>
73
+ <span>this.count</span>
74
+ <button onClick="this.count++" type="button">+</button>
75
+ <span>(this.count < 10 ? "single" : "double") + " digit"</span>
76
+ </div>
77
+ `;
78
+ }
79
+
80
+ decrement() {
81
+ if (this.count > 0) this.count--;
82
+ }
83
+ }
84
+
85
+ MyCounter.register();
86
+ ```
87
+
88
+ 1. Create the file `index.html` containing the following.
89
+
90
+ ```html
91
+ <html>
92
+ <head>
93
+ <script src="my-counter.js" type="module"></script>
94
+ </head>
95
+ <body>
96
+ <my-counter count="3"></my-counter>
97
+ </body>
98
+ </html>
99
+ ```
100
+
101
+ 1. Start a local server by entering `npm run dev`.
102
+
103
+ 1. Browse localhost:5173.
104
+
105
+ 1. Click the "-" and "+" buttons to verify that the component is working.
106
+
107
+ ## More Detail
27
108
 
28
109
  To wire event listeners,
29
110
  Wrec looks for attributes whose name begins with "on".
@@ -80,3 +161,18 @@ that conditionally decides what to render based on an attribute value.
80
161
  See `demo/radio-group.js` for an example of a web component
81
162
  that iterates over values in a comma-delimited attribute value
82
163
  to determine what to render.
164
+
165
+ ## More Examples
166
+
167
+ Check out the web app in the `demo` directory.
168
+ To run it, cd to that directory, enter `npm install`,
169
+ enter `npm run dev`, and browse localhost:5173.
170
+
171
+ This app begins by rendering "counter" components.
172
+ The first is implemented as a vanilla web component.
173
+ The next two uses the Wrec library.
174
+ Compare the files `counter-vanilla.js` and `counter-wrec.js`
175
+ to see how much using Wrec simplifies the code.
176
+
177
+ The app renders several other web components that are built with wrec.
178
+ Examine their code for more examples of wrec usage.
@@ -0,0 +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,"g"),SKIP=5;class Wrec extends HTMLElement{static#t=new Map;static#e=document.createElement("template");#s=new Map;#r;#o;#n=new Map;constructor(){super(),this.attachShadow({mode:"open"}),this.constructor.formAssociated&&(this.#o=this.attachInternals(),this.#r=new FormData,this.#o.setFormValue(this.#r))}attributeChangedCallback(t,e,s){const r=this.#i(t,s);this[t]=r,this.#a(t,r)}#c(t,e,s){t.addEventListener("input",t=>{this[e]=t.target.value});let r=this.#n.get(e);r||(r=[],this.#n.set(e,r)),r.push(s?{element:t,attrName:s}:t)}buildDOM(){let t=this.constructor.prototype.css?`<style>${this.css()}</style>`:"";t+=this.html(),Wrec.#e.innerHTML=t,this.shadowRoot.replaceChildren(Wrec.#e.content.cloneNode(!0))}connectedCallback(){this.#l(),this.buildDOM(),requestAnimationFrame(()=>{this.#p(this.shadowRoot),this.#h(this.shadowRoot),this.constructor.processed=!0})}#l(){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.#m(t):e.value;this["_"+t]=r,Object.defineProperty(this,t,{enumerable:!0,get(){return this["_"+t]},set(e){if(e===this["_"+t])return;this["_"+t]=e;if(this.constructor.properties[t].reflect&&this.hasAttribute(t)){e!==this.#m(t)&&this.#d(this,t,e)}this.#f(t);const s=this.propertyToParentPropertyMap,r=s?s.get(t):null;if(r){this.getRootNode().host.setAttribute(r,e)}this.#a(t,e)}})}#b(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),n=this[o];if(t.setAttribute(s,n),t[s]=n,this.#c(t,o,s),e){let e=t.propertyToParentPropertyMap;e||(e=new Map,t.propertyToParentPropertyMap=e),e.set(s,o)}}this.#E(r,t,s)}}static#g(expression,context){return function(){return eval(expression)}.call(context)}#R(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);t.textContent=this[e],this.#c(t,e)}else this.#E(s,t)}#m(t){return this.#i(t,this.getAttribute(t))}#i(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.#b(t),t.firstElementChild||this.#R(t)}static get observedAttributes(){return Object.keys(this.properties||{})}#f(t){const e=Wrec.#t.get(t)||[];for(const t of e){const e=Wrec.#g(t,this),s=this.#s.get(t)||[];for(const t of s)if(t instanceof Element)this.#A(t,e);else{const{element:s,attrName:r}=t;this.#d(s,r,e)}}this.#y(t)}static register(){const t=Wrec.#v(this.name);customElements.get(t)||customElements.define(t,this)}#E(t,e,s){const r=t.match(REFERENCE_RE);if(!r)return;this.constructor.processed||r.forEach(e=>{const s=e.substring(SKIP);let r=Wrec.#t.get(s);r||(r=[],Wrec.#t.set(s,r)),r.push(t)});let o=this.#s.get(t);o||(o=[],this.#s.set(t,o)),o.push(s?{element:e,attrName:s}:e);const n=Wrec.#g(t,this);s?this.#d(e,s,n):this.#A(e,n)}static#v=t=>t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase();#a(t,e){this.#r&&(this.#r.set(t,e),this.#o.setFormValue(this.#r))}#d(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)}#y(t){const e=this[t],s=this.#n.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.#d(s,r,e),s[r]=e}}#A(t,e){const{localName:s}=t;"textarea"===s?t.value=e:"string"==typeof e&&e.trim().startsWith("<")?(t.innerHTML=e,this.#p(t),this.#h(t)):t.textContent=e}#p(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;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wrec",
3
3
  "author": "R. Mark Volkmann",
4
- "version": "0.0.3",
4
+ "version": "0.1.1",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",