wrec 0.5.11 → 0.6.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 +32 -27
- package/dist/wrec.min.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -106,6 +106,38 @@ Here are the steps:
|
|
|
106
106
|
|
|
107
107
|
1. Click the "-" and "+" buttons to verify that the component is working.
|
|
108
108
|
|
|
109
|
+
## Reactivity
|
|
110
|
+
|
|
111
|
+
Wrec supports reactivity.
|
|
112
|
+
Attribute values and the text content of elements
|
|
113
|
+
can refer to web component properties with the syntax `this.somePropertyName`.
|
|
114
|
+
In this context, `this` always refers to the parent web component.
|
|
115
|
+
The DOM of the web component is surgically updated.
|
|
116
|
+
Only attribute values and text content
|
|
117
|
+
that refer to modified web component properties are updated.
|
|
118
|
+
Attribute values and text content that contain references to properties
|
|
119
|
+
must be valid JavaScript expressions that are NOT surrounded by `${...}`.
|
|
120
|
+
For an example of this kind of web component, see `examples/hello-world.js`.
|
|
121
|
+
|
|
122
|
+
Wrec evaluates JavaScript expressions in the context of a web component instance
|
|
123
|
+
which can be referred to with the `this` keyword in the expressions.
|
|
124
|
+
|
|
125
|
+
To render "this." followed by a valid JavaScript identifier without
|
|
126
|
+
replacing it with the value of the expression, escaping with a double period.
|
|
127
|
+
For example, `this.count` will be reactively replaced with its value,
|
|
128
|
+
but `this..count` will render the text with a single period.
|
|
129
|
+
|
|
130
|
+
In insert the value of an expression
|
|
131
|
+
that does not use properties of the web component,
|
|
132
|
+
into an HTML template string,
|
|
133
|
+
surround the expression with the syntax `${...}`.
|
|
134
|
+
For example, assuming `DAYS` is a variable
|
|
135
|
+
whose value is an array of month names:
|
|
136
|
+
|
|
137
|
+
```html
|
|
138
|
+
<p>The month is ${DAYS[new Date().getDay()]}.</p>
|
|
139
|
+
```
|
|
140
|
+
|
|
109
141
|
## Required Attributes
|
|
110
142
|
|
|
111
143
|
The configuration object for required attributes in a Wrec component
|
|
@@ -149,33 +181,6 @@ see the `checked` property in `examples/toggle-switch.js`.
|
|
|
149
181
|
The component defined in `examples/binding-demo.js`
|
|
150
182
|
listens for that event, as does the `script` in `examples/index.html`.
|
|
151
183
|
|
|
152
|
-
## Reactivity
|
|
153
|
-
|
|
154
|
-
Wrec supports reactivity.
|
|
155
|
-
Attribute values and the text content of elements
|
|
156
|
-
can refer to web component properties with the syntax `this.somePropertyName`.
|
|
157
|
-
In this context, `this` always refers to the parent web component.
|
|
158
|
-
The DOM of the web component is surgically updated.
|
|
159
|
-
Only attribute values and text content
|
|
160
|
-
that refer to modified web component properties are updated.
|
|
161
|
-
Attribute values and text content that contain references to properties
|
|
162
|
-
must be valid JavaScript expressions that are NOT surrounded by `${...}`.
|
|
163
|
-
For an example of this kind of web component, see `examples/hello-world.js`.
|
|
164
|
-
|
|
165
|
-
Wrec evaluates JavaScript expressions in the context of a web component instance
|
|
166
|
-
which can be referred to with the `this` keyword in the expressions.
|
|
167
|
-
|
|
168
|
-
In insert the value of an expression
|
|
169
|
-
that does not use properties of the web component,
|
|
170
|
-
into an HTML template string,
|
|
171
|
-
surround the expression with the syntax `${...}`.
|
|
172
|
-
For example, assuming `DAYS` is a variable
|
|
173
|
-
whose value is an array of month names:
|
|
174
|
-
|
|
175
|
-
```html
|
|
176
|
-
<p>The month is ${DAYS[new Date().getDay()]}.</p>
|
|
177
|
-
```
|
|
178
|
-
|
|
179
184
|
## Conditional and Iterative HTML Generation
|
|
180
185
|
|
|
181
186
|
Wrec supports conditional and iterative generation of HTML.
|
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,defaultForType=t=>t===Number?0:t!==Boolean&&"";function updateAttribute(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)}class Wrec extends HTMLElement{#t=new Map;#e;#s;#r=new Map;constructor(){super(),this.attachShadow({mode:"open"});this.constructor["#propertyToExpressionsMap"]||(this.constructor["#propertyToExpressionsMap"]=new Map),this.constructor.formAssociated&&(this.#s=this.attachInternals(),this.#e=new FormData,this.#s.setFormValue(this.#e))}attributeChangedCallback(t,e,s){const r=this.#i(t,s);this[t]=r,this.#o(t,r)}#n(t,e,s){t.addEventListener("input",t=>{this[e]=t.target.value});let r=this.#r.get(e);r||(r=[],this.#r.set(e,r)),r.push(s?{element:t,attrName:s}:t)}#a(){const t=this.constructor;let{_template:e}=t;if(!e){e=t.template=document.createElement("template");let s=t.css?`<style>${t.css}</style>`:"";s+=t.html,e.innerHTML=s}this.shadowRoot.replaceChildren(e.content.cloneNode(!0))}connectedCallback(){this.#
|
|
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,defaultForType=t=>t===Number?0:t!==Boolean&&"";function updateAttribute(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)}class Wrec extends HTMLElement{#t=new Map;#e;#s;#r=new Map;constructor(){super(),this.attachShadow({mode:"open"});this.constructor["#propertyToExpressionsMap"]||(this.constructor["#propertyToExpressionsMap"]=new Map),this.constructor.formAssociated&&(this.#s=this.attachInternals(),this.#e=new FormData,this.#s.setFormValue(this.#e))}attributeChangedCallback(t,e,s){const r=this.#i(t,s);this[t]=r,this.#o(t,r)}#n(t,e,s){t.addEventListener("input",t=>{this[e]=t.target.value});let r=this.#r.get(e);r||(r=[],this.#r.set(e,r)),r.push(s?{element:t,attrName:s}:t)}#a(){const t=this.constructor;let{_template:e}=t;if(!e){e=t.template=document.createElement("template");let s=t.css?`<style>${t.css}</style>`:"";s+=t.html,e.innerHTML=s}this.shadowRoot.replaceChildren(e.content.cloneNode(!0))}connectedCallback(){this.#l(),this.#u(),this.#a(),requestAnimationFrame(()=>{this.#h(this.shadowRoot),this.#c(this.shadowRoot),this.constructor.processed=!0})}#u(){const{observedAttributes:t,properties:e}=this.constructor;for(const[s,r]of Object.entries(e))this.#p(s,r,t)}#p(t,e,s){e.required&&!this.hasAttribute(t)&&this.#m(this,t,"is a required attribute");const r=s.includes(t)&&this.hasAttribute(t)?this.#d(t):e.value||defaultForType(e.type),i="#"+t;this[i]=r,Object.defineProperty(this,t,{enumerable:!0,get(){return this[i]},set(s){if(s===this[i])return;if(this[i]=s,this.hasAttribute(t)){s!==this.#d(t)&&updateAttribute(this,t,s)}this.#f(t);const r=this.propertyToParentPropertyMap,o=r?r.get(t):null;if(o){this.getRootNode().host.setAttribute(o,s)}this.#o(t,s),e.dispatch&&this.dispatchEvent(new CustomEvent("change",{bubbles:!0,composed:!0,detail:{propertyName:t}}))}})}static elementName(){return this.name.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}#b(t){const e=t.localName.includes("-");for(const s of t.getAttributeNames()){const r=t.getAttribute(s),i=this.#E(t,r);if(i){const r=this[i];if(void 0===r&&this.#R(t,s,i),t[i]=r,"value"===s&&this.#n(t,i,s),e){let e=t.propertyToParentPropertyMap;e||(e=new Map,t.propertyToParentPropertyMap=e),e.set(s,i)}}this.#v(r,t,s)}}#g(expression){return(()=>eval(expression)).call(this)}#y(t){const{localName:e}=t;if("style"===e)return;const s=t.textContent.trim(),r=this.#E(t,s);"textarea"===e&&r?(this.#n(t,r),t.textContent=this[r]):this.#v(s,t)}#c(t){const e=t.querySelectorAll("*");for(const t of e)this.#b(t),t.firstElementChild||this.#y(t)}static get observedAttributes(){return Object.keys(this.properties||{})}#E(t,e){if(!REFERENCE_RE.test(e))return;const s=e.substring(SKIP);return void 0===this[s]&&this.#R(t,null,s),s}#f(t){const e=this.constructor["#propertyToExpressionsMap"].get(t)||[];for(const t of e){const e=this.#g(t),s=this.#t.get(t)||[];for(const t of s)if(t instanceof Element)this.#A(t,e);else{const{element:s,attrName:r}=t;updateAttribute(s,r,e)}}requestAnimationFrame(()=>{this.#N(t)})}static register(){const t=this.elementName();customElements.get(t)||customElements.define(t,this)}#v(t,e,s){const r=this.#w(e,s,t);if(!r)return t=t.replaceAll("this..","this."),void(s?e.setAttribute(s,t):e.textContent=t);this.constructor.processed||r.forEach(e=>{const s=e.substring(SKIP),r=this.constructor["#propertyToExpressionsMap"];let i=r.get(s);i||(i=[],r.set(s,i)),i.push(t)});let i=this.#t.get(t);i||(i=[],this.#t.set(t,i)),i.push(s?{element:e,attrName:s}:e);const o=this.#g(t);s?updateAttribute(e,s,o):this.#A(e,o)}#o(t,e){this.#e&&(this.#e.set(t,e),this.#s.setFormValue(this.#e))}#m(t,e,s){throw new Error(`component ${this.constructor.elementName()}`+(t?`, element "${t.localName}"`:"")+(e?`, attribute "${e}"`:"")+` ${s}`)}#R(t,e,s){this.#m(t,e,`refers to missing property "${s}"`)}#d(t){return this.#i(t,this.getAttribute(t))}#i(t,e){if(e?.match(REFERENCES_RE))return e;const{type:s}=this.constructor.properties[t];if(s===String)return e;if(s===Number){const s=Number(e);if(!isNaN(s))return s;this.#m(null,t,`must be a number, but was "${e}"`)}if(s===Boolean)return"true"===e||"false"!==e&&(e&&e!==t&&this.#m(null,t,"is a Boolean attribute, so its value must match attribute name or be missing"),e===t);this.#m(null,t,"does not specify its type")}#N(t){const e=this[t],s=this.#r.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;updateAttribute(s,r,e),s[r]=e}}#A(t,e){const{localName:s}=t,r=typeof e;"string"!==r&&"number"!==r&&this.#m(t,null," computed content is not a string or number"),"textarea"===s?t.value=e:"string"===r&&e.trim().startsWith("<")?(t.innerHTML=e,this.#h(t),this.#c(t)):t.textContent=e}#l(){const t=new Set(Object.keys(this.constructor.properties));for(const e of this.getAttributeNames())"id"!==e&&(e.startsWith("on")||t.has(e)||this.#m(null,e,"is not a supported attribute"))}#w(t,e,s){const r=s.match(REFERENCES_RE);if(r)return r.forEach(s=>{const r=s.substring(SKIP);void 0===this[r]&&this.#R(t,e,r)}),r}#h(root){for(const element of root.querySelectorAll("*")){const attributesToRemove=[];for(const attr of element.attributes){const attrName=attr.name;if(attrName.startsWith("on")){const eventName=attrName.slice(2).toLowerCase(),attrValue=attr.value;let fn;this.#w(element,attrName,attrValue),"function"==typeof this[attrValue]?fn=t=>this[attrValue](t):(this.#w(element,attrName,attrValue),fn=event=>eval(attrValue)),element.addEventListener(eventName,fn),attributesToRemove.push(attrName)}}for(const t of attributesToRemove)element.removeAttribute(t)}}}export default Wrec;export const css=String.raw;export const html=String.raw;
|