wrec 0.7.0 → 0.7.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 +61 -0
- package/dist/wrec.min.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -178,6 +178,67 @@ wrec adds the attribute to the element with no value
|
|
|
178
178
|
or removes the attribute from the element.
|
|
179
179
|
This is commonly used for attributes like `disabled`.
|
|
180
180
|
|
|
181
|
+
## Computed Properties
|
|
182
|
+
|
|
183
|
+
The value of a property can be computed using the values of other properties.
|
|
184
|
+
To do this, add the `computed` attribute to the description of the property.
|
|
185
|
+
For example:
|
|
186
|
+
|
|
187
|
+
```js
|
|
188
|
+
static properties = {
|
|
189
|
+
width: { type: Number },
|
|
190
|
+
height: { type: Number },
|
|
191
|
+
area: {
|
|
192
|
+
type: Number,
|
|
193
|
+
computed: "this.width * this.height"
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The `computed` expression can call a function.
|
|
199
|
+
For example:
|
|
200
|
+
|
|
201
|
+
```js
|
|
202
|
+
static properties = {
|
|
203
|
+
width: { type: Number },
|
|
204
|
+
height: { type: Number },
|
|
205
|
+
area: {
|
|
206
|
+
type: Number,
|
|
207
|
+
computed: "this.rectangleArea(this.width, this.height)"
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
rectangleArea(width, height) {
|
|
212
|
+
return width * height;
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
If the function depends on properties whose values are not passed to it,
|
|
217
|
+
add the `uses` property to specify a
|
|
218
|
+
comma-separated string of properties that the function uses.
|
|
219
|
+
Every time the value of one of those properties changes,
|
|
220
|
+
the function will be called again
|
|
221
|
+
to set a new value for the computed property.
|
|
222
|
+
For example:
|
|
223
|
+
|
|
224
|
+
```js
|
|
225
|
+
static properties = {
|
|
226
|
+
width: { type: Number },
|
|
227
|
+
height: { type: Number },
|
|
228
|
+
area: {
|
|
229
|
+
type: Number,
|
|
230
|
+
computed: "this.rectangleArea()",
|
|
231
|
+
uses: "width,height"
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
rectangleArea() {
|
|
236
|
+
return this.width * this.height;
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
For a full code example, see `examples/rectangle-area.js`.
|
|
241
|
+
|
|
181
242
|
## Event Listeners
|
|
182
243
|
|
|
183
244
|
To wire event listeners,
|
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)}function updateValue(t,e,s){t instanceof CSSRule?t.style.setProperty(e,s):updateAttribute(t,e,s)}class Wrec extends HTMLElement{#t=new Map
|
|
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{static#t=new Map;static#e=new Map;#s=new Map;#r;#o;#i=new Map;constructor(){super(),this.attachShadow({mode:"open"});this.constructor["#propertyToExpressionsMap"]||(this.constructor["#propertyToExpressionsMap"]=new Map),this.constructor.formAssociated&&(this.#o=this.attachInternals(),this.#r=new FormData,this.#o.setFormValue(this.#r))}attributeChangedCallback(t,e,s){const r=Wrec.getPropertyName(t),o=this.#n(r,s);this[r]=o,this.#a(r,o)}#c(t,e,s){t.addEventListener("input",t=>{this[e]=t.target.value});let r=this.#i.get(e);r||(r=[],this.#i.set(e,r)),r.push(s?{element:t,attrName:s}:t)}#p(){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.#u(),this.#l(),this.#p(),requestAnimationFrame(()=>{this.#h(this.shadowRoot),this.#m(this.shadowRoot),this.constructor.processed=!0,this.#f()})}#f(){const{properties:t}=this.constructor;for(const[e,{computed:s}]of Object.entries(t))s&&(this[e]=this.#d(s))}#l(){const{observedAttributes:t,properties:e}=this.constructor;for(const[s,r]of Object.entries(e))this.#b(s,r,t)}#b(t,e,s){const r=Wrec.getAttributeName(t);e.required&&!this.hasAttribute(r)&&this.#E(this,t,"is a required attribute");const o=s.includes(t)&&this.hasAttribute(r)?this.#g(t):e.value||defaultForType(e.type),i="#"+t;this[i]=o,e.computed&&this.#y(t,e),Object.defineProperty(this,t,{enumerable:!0,get(){return this[i]},set(s){if(s===this[i])return;this[i]=s;let o=this.constructor["#propertyToComputedMap"];if(o){const e=o.get(t)||[];for(const[t,s]of e)this[t]=this.#d(s)}if(this.hasAttribute(r)){s!==this.#g(t)&&updateAttribute(this,t,s)}this.#R(t),o=this.propertyToParentPropertyMap;const n=o?o.get(t):null;if(n){this.getRootNode().host.setAttribute(n,s)}this.#a(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()}#v(t){const e=t.localName.includes("-");for(const s of t.getAttributeNames()){const r=t.getAttribute(s),o=this.#N(t,r);if(o){const r=this[o];if(void 0===r&&this.#A(t,s,o),t[o]=r,"value"===s&&this.#c(t,o,s),e){let e=t.propertyToParentPropertyMap;e||(e=new Map,t.propertyToParentPropertyMap=e),e.set(s,o)}}this.#T(r,t,s)}}#d(expression){return(()=>eval(expression)).call(this)}#C(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.#T(s,e,t)}}else{const s=t.textContent.trim(),r=this.#N(t,s);"textarea"===e&&r?(this.#c(t,r),t.textContent=this[r]):this.#T(s,t)}}static getAttributeName(t){let e=Wrec.#t.get(t);return e||(e=t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),Wrec.#t.set(t,e)),e}static getPropertyName(t){let e=Wrec.#e.get(t);return e||(e=t.replace(/-([a-z])/g,(t,e)=>e.toUpperCase()),Wrec.#e.set(t,e)),e}#m(t){const e=t.querySelectorAll("*");for(const t of e)this.#v(t),t.firstElementChild||this.#C(t)}static get observedAttributes(){return Object.keys(this.properties||{}).map(Wrec.getAttributeName)}#N(t,e){if(!REFERENCE_RE.test(e))return;const s=e.substring(SKIP);return void 0===this[s]&&this.#A(t,null,s),s}#R(t){const e=this.constructor["#propertyToExpressionsMap"].get(t)||[];for(const t of e){const e=this.#d(t),s=this.#s.get(t)||[];for(const t of s)t instanceof Element?this.#w(t,e):updateValue(t.element,t.attrName,e)}requestAnimationFrame(()=>{this.#M(t)})}static register(){const t=this.elementName();customElements.get(t)||customElements.define(t,this)}#y(t,e){const{computed:s,uses:r}=e;let o=this.constructor["#propertyToComputedMap"];function i(e,s){let r=o.get(e);r||(r=[],o.set(e,r)),r.push([t,s])}o||(o=this.constructor["#propertyToComputedMap"]=new Map);const n=s.match(REFERENCES_RE)||[];for(const e of n){const r=e.substring(SKIP);void 0===this[r]&&this.#A(null,t,r),"function"!=typeof this[r]&&i(r,s)}if(r)for(const t of r.split(","))i(t,s)}#T(t,e,s){const r=this.#x(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.#s.get(t);o||(o=[],this.#s.set(t,o)),o.push(s?{element:e,attrName:s}:e);const i=this.#d(t);s?updateValue(e,s,i):this.#w(e,i)}#a(t,e){this.#r&&(this.#r.set(t,e),this.#o.setFormValue(this.#r))}#E(t,e,s){throw new Error(`component ${this.constructor.elementName()}`+(t?`, element "${t.localName}"`:"")+(e?`, attribute "${e}"`:"")+` ${s}`)}#A(t,e,s){this.#E(t,e,`refers to missing property "${s}"`)}#g(t){return this.#n(t,this.getAttribute(t))}#n(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.#E(null,t,`must be a number, but was "${e}"`)}if(s===Boolean)return"true"===e||"false"!==e&&(e&&e!==t&&this.#E(null,t,"is a Boolean attribute, so its value must match attribute name or be missing"),e===t);this.#E(null,t,"does not specify its type")}#M(t){const e=this[t],s=this.#i.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}}#w(t,e){const{localName:s}=t,r=typeof e;"string"!==r&&"number"!==r&&this.#E(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.#m(t)):t.textContent=e}#u(){const t=new Set(Object.keys(this.constructor.properties));for(const e of this.getAttributeNames())"id"!==e&&(e.startsWith("on")||t.has(Wrec.getPropertyName(e))||this.#E(null,e,"is not a supported attribute"))}#x(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.#A(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.#x(element,attrName,attrValue),"function"==typeof this[attrValue]?fn=t=>this[attrValue](t):(this.#x(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;
|