wrec 0.5.11 → 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 +59 -27
- package/dist/wrec.min.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -106,6 +106,65 @@ 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
|
+
|
|
141
|
+
## Reactive CSS
|
|
142
|
+
|
|
143
|
+
CSS property values cannot refer to web component properties
|
|
144
|
+
with the syntax `this.somePropertyName`.
|
|
145
|
+
This is because the CSS parser in browsers removes invalid property values.
|
|
146
|
+
A workaround is to place such references in a CSS variable
|
|
147
|
+
and refer to that variable from a CSS property.
|
|
148
|
+
For an example of this, see `examples/data-binding.js`
|
|
149
|
+
which contains the following CSS:
|
|
150
|
+
|
|
151
|
+
```css
|
|
152
|
+
p {
|
|
153
|
+
--color: this.color;
|
|
154
|
+
--size: this.size;
|
|
155
|
+
color: var(--color);
|
|
156
|
+
font-size: calc(var(--size) * 1px);
|
|
157
|
+
margin: 6px 0;
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
CSS variable values can be any valid JavaScript expression.
|
|
162
|
+
The example above can be changed to double the size as follows:
|
|
163
|
+
|
|
164
|
+
```css
|
|
165
|
+
--size: this.size * 2;
|
|
166
|
+
```
|
|
167
|
+
|
|
109
168
|
## Required Attributes
|
|
110
169
|
|
|
111
170
|
The configuration object for required attributes in a Wrec component
|
|
@@ -149,33 +208,6 @@ see the `checked` property in `examples/toggle-switch.js`.
|
|
|
149
208
|
The component defined in `examples/binding-demo.js`
|
|
150
209
|
listens for that event, as does the `script` in `examples/index.html`.
|
|
151
210
|
|
|
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
211
|
## Conditional and Iterative HTML Generation
|
|
180
212
|
|
|
181
213
|
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.#
|
|
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)}function updateValue(t,e,s){t instanceof CSSRule?t.style.setProperty(e,s):updateAttribute(t,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.#o(t,s);this[t]=r,this.#i(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),o="#"+t;this[o]=r,Object.defineProperty(this,t,{enumerable:!0,get(){return this[o]},set(s){if(s===this[o])return;if(this[o]=s,this.hasAttribute(t)){s!==this.#d(t)&&updateAttribute(this,t,s)}this.#f(t);const r=this.propertyToParentPropertyMap,i=r?r.get(t):null;if(i){this.getRootNode().host.setAttribute(i,s)}this.#i(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),o=this.#E(t,r);if(o){const r=this[o];if(void 0===r&&this.#R(t,s,o),t[o]=r,"value"===s&&this.#n(t,o,s),e){let e=t.propertyToParentPropertyMap;e||(e=new Map,t.propertyToParentPropertyMap=e),e.set(s,o)}}this.#v(r,t,s)}}#y(expression){return(()=>eval(expression)).call(this)}#g(t){const{localName:e}=t;if("style"===e){for(const e of t.sheet.cssRules)if(e.type===CSSRule.STYLE_RULE)for(const t of e.style)if(t.startsWith("--")){const s=e.style.getPropertyValue(t);this.#v(s,e,t)}}else{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.#g(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.#y(t),s=this.#t.get(t)||[];for(const t of s)t instanceof Element?this.#N(t,e):updateValue(t.element,t.attrName,e)}requestAnimationFrame(()=>{this.#A(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){const r=t.replaceAll("this..","this.");return void(s?updateValue(e,s,r):e.textContent=r)}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.#t.get(t);o||(o=[],this.#t.set(t,o)),o.push(s?{element:e,attrName:s}:e);const i=this.#y(t);s?updateValue(e,s,i):this.#N(e,i)}#i(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.#o(t,this.getAttribute(t))}#o(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")}#A(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}}#N(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;
|