wrec 0.7.0 → 0.8.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 CHANGED
@@ -4,23 +4,8 @@
4
4
 
5
5
  Wrec is a small, zero dependency library that
6
6
  greatly simplifies building web components.
7
- Its main features are that it automates
8
- wiring event listeners and implementing reactivity.
9
-
10
- Wrec was inspired by [Lit](https://lit.dev).
11
- It has the following advantages over Lit:
12
-
13
- - Wrec is simpler ... just a single class to extend (Wrec).
14
- - Wrec is slightly smaller ... 4K versus 5.8K minified.
15
- - Wrec has a cleaner syntax ... no need to
16
- surround JS expressions with `${...}`.
17
- - Wrec provides automatic 2-way data binding ...
18
- no need to dispatch custom events and listen for them.
19
- - Wrec doesn't require a special syntax for Boolean attributes.
20
- - Wrec enables specifying the content of a `textarea` element
21
- with a JavaScript expressions in its text content.
22
-
23
- Wrec components have many of the features provided by Alpine.js.
7
+ It is described in detail, with several working examples,
8
+ in [my blog](https://mvolkmann.github.io/blog/wrec/).
24
9
 
25
10
  ## Getting Started
26
11
 
@@ -47,11 +32,11 @@ Here are the steps:
47
32
  "Prettier" to add syntax highlighting and format the CSS and HTML strings.
48
33
 
49
34
  ```js
50
- import Wrec, { css, html } from "wrec";
35
+ import Wrec, {css, html} from 'wrec';
51
36
 
52
37
  class MyCounter extends Wrec {
53
38
  static properties = {
54
- count: { type: Number },
39
+ count: {type: Number}
55
40
  };
56
41
 
57
42
  static css = css`
@@ -106,144 +91,8 @@ Here are the steps:
106
91
 
107
92
  1. Click the "-" and "+" buttons to verify that the component is working.
108
93
 
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
-
168
- ## Required Attributes
169
-
170
- The configuration object for required attributes in a Wrec component
171
- should contain `required: true`.
172
- For an example of this, see the `name` property in `examples/radio-group.js`.
173
-
174
- ## Boolean Attributes
175
-
176
- When the value of an attribute is a Boolean,
177
- wrec adds the attribute to the element with no value
178
- or removes the attribute from the element.
179
- This is commonly used for attributes like `disabled`.
180
-
181
- ## Event Listeners
182
-
183
- To wire event listeners,
184
- Wrec looks for attributes whose name begins with "on".
185
- It assumes the remainder of the attribute name is an event name.
186
- It also assumes that the value of the attribute is either
187
- a method name that should be called or code that should be executed
188
- when that event is dispatched.
189
- For example, with the attribute `onclick="increment"`,
190
- if `increment` is a method in the component, wrec will
191
- add an event listener to the element containing the attribute
192
- for "click" events and call `this.increment(event)`.
193
- Alternatively, the attribute `onclick="this.count++"`
194
- adds an event listener that increments `this.count`
195
- when the element is clicked.
196
-
197
- The case of the event name within the attribute name
198
- does not matter because Wrec lowercases the name.
199
- So the attributes in the previous examples
200
- can be replaced by `onClick="increment"`.
201
-
202
- ## Property Change Events
203
-
204
- Wrec components will dispatch change events whenever
205
- a property configured with `dispatch: true` changes.
206
- For an example of this,
207
- see the `checked` property in `examples/toggle-switch.js`.
208
- The component defined in `examples/binding-demo.js`
209
- listens for that event, as does the `script` in `examples/index.html`.
210
-
211
- ## Conditional and Iterative HTML Generation
212
-
213
- Wrec supports conditional and iterative generation of HTML.
214
- See `examples/temperature-eval.js` for an example of a web component
215
- that conditionally decides what to render based on an attribute value.
216
- See `examples/radio-group.js` for an example of a web component
217
- that iterates over values in a comma-delimited attribute value
218
- to determine what to render.
219
-
220
94
  ## Data Binding
221
95
 
222
- Wrec supports two-way data binding.
223
- See the example component binding-demo
224
- and the components it renders.
225
-
226
- Wrec two-way data binding can be used with HTML form elements.
227
-
228
- - `input` and `select` elements can have a `value` attribute
229
- whose value is "this.somePropertyName".
230
- - `textarea` elements can have text content
231
- that is "this.somePropertyName"
232
-
233
- In all these cases, if the user changes the value of the form element,
234
- the specified property is updated.
235
- When the property is updated,
236
- the displayed value of the form element is updated.
237
- For examples, see `examples/data-bind.js`.
238
-
239
- Data binding in Lit is not two-way like in wrec.
240
- A Lit component cannot simply pass one of its properties to
241
- a child Lit component and have the child can update the property.
242
- The child must dispatch custom events that
243
- the parent listens for so it can update its own state.
244
- For an example of this, see
245
- [wrec-compare](https://github.com/mvolkmann/lit-examples/blob/main/wrec-compare/binding-demo.ts).
246
-
247
96
  ## Form Submissions
248
97
 
249
98
  Web components that extend `Wrec` can contribute values to
@@ -253,80 +102,3 @@ Wrec looks for this automatically does the rest of the work.
253
102
  ```js
254
103
  static formAssociated = true;
255
104
  ```
256
-
257
- ## Error Checking
258
-
259
- Wrec checks for many kinds of errors and throws an `Error` when they are found.
260
- Look for messages in the DevTools console.
261
- The kinds of errors that are detected include:
262
-
263
- - attribute names in web component instances
264
- with no matching property declaration
265
- - attribute values with a type that differs from the declared property type
266
- - event handling function names that
267
- don't match any method name in the web component
268
- - expressions in attribute values or element text content
269
- that reference undeclared web component properties
270
- - expressions in element text content
271
- that do not evaluate to a string or number
272
-
273
- ## Security
274
-
275
- Wrec uses the JavaScript `eval` function to evaluate JavaScript expressions
276
- that are placed in attribute values and the text content of elements.
277
- This has security implications if those expressions
278
- can come from untrusted sources, so it is best avoid
279
- creating web components that use untrusted content in those ways.
280
-
281
- Perhaps the most dangerous thing the use of `eval` allows
282
- is sending HTTP requests to other servers.
283
- Such requests could contain data scraped from your web app
284
- in order to share it with unscrupulous sites.
285
-
286
- The easiest way to prevent this is to add a
287
- Content Security Policy (CSP) to your web app.
288
- Simply adding the following element as a child of the
289
- `head` element in each page blocks sending HTTP requests
290
- to any domain except that of your web app:
291
-
292
- ```html
293
- <meta http-equiv="Content-Security-Policy" content="connect-src 'self'" />
294
- ```
295
-
296
- ## More Examples
297
-
298
- Check out the web app in the `examples` directory.
299
- To run it, cd to that directory, enter `npm install`,
300
- enter `npm run dev`, and browse localhost:5173.
301
-
302
- This app begins by rendering "counter" components.
303
- The first is implemented as a vanilla web component.
304
- The next two uses the Wrec library.
305
- Compare the files `counter-vanilla.js` and `counter-wrec.js`
306
- to see how much using Wrec simplifies the code.
307
-
308
- The `examples` app renders several other
309
- web components that are built with wrec.
310
- Examine their code for more examples of wrec usage.
311
-
312
- ## Tests
313
-
314
- wrec has an extensive set of Playwright tests.
315
- To run them:
316
-
317
- 1. Clone the wrec repository.
318
- 1. cd to the `examples` directory.
319
- 1. Enter `npm install`.
320
- 1. Enter `npm run testui`.
321
- 1. Click the right pointing triangle.
322
-
323
- If there is no "Action" tab which displays a browser view of the running tests,
324
- reset the Playwright UI settings by entering one of these commands:
325
-
326
- ```bash
327
- # macOS
328
- rm -rf ~/Library/Caches/ms-playwright/.settings
329
-
330
- # Windows
331
- del %LOCALAPPDATA%\ms-playwright\.settings /s /q
332
- ```
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;#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;
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,r){const s=t.getAttribute(e);"boolean"==typeof r?r?s!==e&&t.setAttribute(e,e):t.removeAttribute(e):s!==r&&t.setAttribute(e,r)}function updateValue(t,e,r){t instanceof CSSRule?t.style.setProperty(e,r):updateAttribute(t,e,r)}class Wrec extends HTMLElement{static#t=new Map;static#e=new Map;#r=new Map;#s;#o;#i=new Map;propertyToParentPropertyMap=new Map;constructor(){super(),this.attachShadow({mode:"open"}),this.constructor.properties||(this.constructor.properties={}),this.constructor["#propertyToExpressionsMap"]||(this.constructor["#propertyToExpressionsMap"]=new Map),this.constructor.formAssociated&&(this.#o=this.attachInternals(),this.#s=new FormData,this.#o.setFormValue(this.#s))}attributeChangedCallback(t,e,r){const s=Wrec.getPropertyName(t),o=this.#n(s,r);this[s]=o,this.#a(s,o)}#c(t,e,r){t.addEventListener("input",t=>{this[e]=t.target.value});let s=this.#i.get(e);s||(s=[],this.#i.set(e,s)),s.push(r?{element:t,attrName:r}:t)}#p(){const t=this.constructor;let{_template:e}=t;if(!e){e=t.template=document.createElement("template");let r=t.css?`<style>${t.css}</style>`:"";r+=t.html,e.innerHTML=r}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:r}]of Object.entries(t))r&&(this[e]=this.#d(r))}#l(){const{observedAttributes:t,properties:e}=this.constructor;for(const[r,s]of Object.entries(e))this.#b(r,s,t)}#b(t,e,r){const s=Wrec.getAttributeName(t);e.required&&!this.hasAttribute(s)&&this.#E(this,t,"is a required attribute");const o=r.includes(t)&&this.hasAttribute(s)?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(r){if(r===this[i])return;this[i]=r;let o=this.constructor["#propertyToComputedMap"];if(o){const e=o.get(t)||[];for(const[t,r]of e)this[t]=this.#d(r)}if(this.hasAttribute(s)){r!==this.#g(t)&&updateAttribute(this,t,r)}this.#R(t);const n=this.propertyToParentPropertyMap.get(t);if(n){this.getRootNode().host.setAttribute(n,r)}this.#a(t,r),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 r of t.getAttributeNames()){const s=t.getAttribute(r),o=this.#N(t,s);if(o){const s=this[o];void 0===s&&this.#A(t,r,o),t[o]=s,"value"===r&&this.#c(t,o,r),e&&t.propertyToParentPropertyMap.set(Wrec.getPropertyName(r),o)}this.#T(s,t,r)}}#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 r=e.style.getPropertyValue(t);this.#T(r,e,t)}}else{const r=t.textContent.trim(),s=this.#N(t,r);"textarea"===e&&s?(this.#c(t,s),t.textContent=this[s]):this.#T(r,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 r=e.substring(SKIP);return void 0===this[r]&&this.#A(t,null,r),r}#R(t){const e=this.constructor["#propertyToExpressionsMap"].get(t)||[];for(const t of e){const e=this.#d(t),r=this.#r.get(t)||[];for(const t of r)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:r,uses:s}=e;let o=this.constructor["#propertyToComputedMap"];function i(e,r){let s=o.get(e);s||(s=[],o.set(e,s)),s.push([t,r])}o||(o=this.constructor["#propertyToComputedMap"]=new Map);const n=r.match(REFERENCES_RE)||[];for(const e of n){const s=e.substring(SKIP);void 0===this[s]&&this.#A(null,t,s),"function"!=typeof this[s]&&i(s,r)}if(s)for(const t of s.split(","))i(t,r)}#T(t,e,r){const s=this.#x(e,r,t);if(!s){const s=t.replaceAll("this..","this.");return void(r?updateValue(e,r,s):e.textContent=s)}this.constructor.processed||s.forEach(e=>{const r=e.substring(SKIP),s=this.constructor["#propertyToExpressionsMap"];let o=s.get(r);o||(o=[],s.set(r,o)),o.push(t)});let o=this.#r.get(t);o||(o=[],this.#r.set(t,o)),o.push(r?{element:e,attrName:r}:e);const i=this.#d(t);r?updateValue(e,r,i):this.#w(e,i)}#a(t,e){this.#s&&(this.#s.set(t,e),this.#o.setFormValue(this.#s))}#E(t,e,r){throw new Error(`component ${this.constructor.elementName()}`+(t?`, element "${t.localName}"`:"")+(e?`, attribute "${e}"`:"")+` ${r}`)}#A(t,e,r){this.#E(t,e,`refers to missing property "${r}"`)}#g(t){return this.#n(t,this.getAttribute(t))}#n(t,e){if(e?.match(REFERENCES_RE))return e;const{type:r}=this.constructor.properties[t];if(r===String)return e;if(r===Number){const r=Number(e);if(!isNaN(r))return r;this.#E(null,t,`must be a number, but was "${e}"`)}if(r===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],r=this.#i.get(t)||[];for(const t of r)if(t instanceof Element)"textarea"===t.localName?t.value=e:t.textContent=e;else{const{element:r,attrName:s}=t;updateAttribute(r,s,e),r[s]=e}}#w(t,e){const{localName:r}=t,s=typeof e;"string"!==s&&"number"!==s&&this.#E(t,null," computed content is not a string or number"),"textarea"===r?t.value=e:"string"===s&&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,r){const s=r.match(REFERENCES_RE);if(s)return s.forEach(r=>{const s=r.substring(SKIP);void 0===this[s]&&this.#A(t,e,s)}),s}#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=()=>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;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "wrec",
3
3
  "description": "a small library that greatly simplifies building web components",
4
4
  "author": "R. Mark Volkmann",
5
- "version": "0.7.0",
5
+ "version": "0.8.0",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",