mi-element 0.6.6 → 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 +11 -9
- package/dist/context.js +6 -0
- package/dist/element.js +92 -109
- package/dist/escape.js +12 -5
- package/dist/index.js +5 -5
- package/dist/refs.js +1 -9
- package/dist/styling.js +12 -5
- package/docs/context.md +43 -27
- package/docs/controller.md +4 -0
- package/docs/element.md +47 -60
- package/docs/reactivity.md +2 -2
- package/docs/signal.md +13 -6
- package/docs/store.md +1 -0
- package/docs/styling.md +17 -5
- package/package.json +16 -20
- package/src/context.js +8 -0
- package/src/element.js +203 -197
- package/src/escape.js +17 -13
- package/src/index.js +4 -14
- package/src/refs.js +0 -24
- package/src/styling.js +19 -9
- package/types/context.d.ts +2 -0
- package/types/element.d.ts +53 -34
- package/types/escape.d.ts +1 -1
- package/types/index.d.ts +4 -8
- package/types/refs.d.ts +0 -11
- package/types/styling.d.ts +1 -3
- package/dist/index.min.js +0 -2
- package/dist/index.min.js.map +0 -1
- package/src/min.js +0 -17
- package/types/min.d.ts +0 -1
package/docs/element.md
CHANGED
|
@@ -28,44 +28,32 @@ from the `static attributes` object. From there setters and getters for property
|
|
|
28
28
|
changes using `.[name] = newValue` instead of `setAttribute(name, newValue)` are
|
|
29
29
|
applied.
|
|
30
30
|
|
|
31
|
-
Direct properties can also made observable with `static properties` as long as
|
|
32
|
-
not yet being defined within attributes.
|
|
33
31
|
|
|
34
32
|
```js
|
|
35
33
|
class extends MiElement {
|
|
36
34
|
/**
|
|
37
|
-
* Declare observable attributes
|
|
38
|
-
*
|
|
35
|
+
* Declare observable attributes with this getter.
|
|
36
|
+
* Use `true` to define boolean attributes!
|
|
37
|
+
* Do not use `static attribute = { text: false }` as components attributes
|
|
39
38
|
* will use a shallow copy only. With the getter we always get a real "deep"
|
|
40
39
|
* copy.
|
|
41
40
|
*
|
|
42
|
-
* For yet to defined numbers, boolean or strings use `Number`, `Boolean`,
|
|
43
|
-
* `String`. Attributes are accessible via `.[name]` or `.getAttribute(name)`.
|
|
44
41
|
* Avoid using attributes which are HTMLElement properties e.g. `className`.
|
|
42
|
+
* camelCased attributes will be made observable using its kebab-cased name.
|
|
45
43
|
*/
|
|
46
|
-
static get
|
|
44
|
+
static get properties () {
|
|
47
45
|
return {
|
|
48
|
-
text:
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
text: {},
|
|
47
|
+
focus: { type: Boolean },
|
|
48
|
+
// define property only
|
|
49
|
+
numberPropOnly: { attribute: false, type: Number }
|
|
51
50
|
}
|
|
52
51
|
}
|
|
52
|
+
|
|
53
53
|
/**
|
|
54
|
-
*
|
|
55
|
-
* Any changed value of a property, using `.[name] = nextValue` assignment,
|
|
56
|
-
* will cause a rerender.
|
|
57
|
-
* In case of objects or arrays consider changing the reference with the
|
|
58
|
-
* spread operator like `{...obj}` or `[...arr]` creating a new reference.
|
|
59
|
-
* If name is already declared in `attributes` it will be ignored.
|
|
54
|
+
* optionally declare "non observable" or internal properties
|
|
60
55
|
*/
|
|
61
|
-
|
|
62
|
-
return { prop: 0 }
|
|
63
|
-
}
|
|
64
|
-
constructor() {
|
|
65
|
-
super()
|
|
66
|
-
// optionally declare "non observable" and internal properties
|
|
67
|
-
this.foo = 'foo'
|
|
68
|
-
}
|
|
56
|
+
foo = 'foo'
|
|
69
57
|
}
|
|
70
58
|
```
|
|
71
59
|
|
|
@@ -74,10 +62,10 @@ class extends MiElement {
|
|
|
74
62
|
Invoked when a component is being added to the document's DOM.
|
|
75
63
|
|
|
76
64
|
Micro components create `this.renderRoot` (typically same as `this.shadowRoot`
|
|
77
|
-
for open components) using `this.attachShadow(
|
|
78
|
-
options are taken from the components `static
|
|
65
|
+
for open components) using `this.attachShadow(shadowRootInit)`. Shadow root
|
|
66
|
+
options are taken from the components `static shadowRootInit = { mode: 'open'
|
|
79
67
|
}`. In advanced cases where no shadow root is desired, set `static
|
|
80
|
-
|
|
68
|
+
shadowRootInit = null`
|
|
81
69
|
|
|
82
70
|
The most common use case is adding event listeners to external nodes in
|
|
83
71
|
`connectedCallback()`. Typically, anything done in `connectedCallback()` should
|
|
@@ -90,7 +78,7 @@ Then the first `render()` is issued with a `requestUpdate()`
|
|
|
90
78
|
class extends MiElement {
|
|
91
79
|
// { mode: 'open' } is the default shadow root option
|
|
92
80
|
// use `null` for no shadow root or { mode: 'closed' } for closed mode
|
|
93
|
-
static
|
|
81
|
+
static shadowRootInit = { mode: 'open' }
|
|
94
82
|
|
|
95
83
|
connectedCallback() {
|
|
96
84
|
super.connectedCallback() // don't forget to call the super method
|
|
@@ -160,24 +148,19 @@ flowchart TD
|
|
|
160
148
|
disconnectedCallback("disconnectedCallback()")
|
|
161
149
|
render("render()")
|
|
162
150
|
requestUpdate("requestUpdate()")
|
|
163
|
-
shouldUpdate("shouldUpdate(changedAttributes)")
|
|
164
151
|
update("update(changedAttributes)")
|
|
165
152
|
|
|
166
|
-
setAttribute("setAttribute(name, newVale)")
|
|
167
|
-
setProperty(".[name] = newValue")
|
|
153
|
+
setAttribute("el.setAttribute(name, newVale)")
|
|
154
|
+
setProperty("el.[name] = newValue")
|
|
168
155
|
|
|
169
156
|
START --> constructoR
|
|
170
157
|
constructoR -.->|"mount to DOM"| connectedCallback
|
|
171
158
|
connectedCallback --> render
|
|
172
159
|
render --> requestUpdate
|
|
173
|
-
requestUpdate -.->|async|
|
|
174
|
-
shouldUpdate -->|true| update
|
|
175
|
-
|
|
176
|
-
setAttribute -->|"attributeChangedCallback"| requestUpdate
|
|
177
|
-
setProperty --> requestUpdate
|
|
160
|
+
requestUpdate -.->|async| update
|
|
178
161
|
|
|
179
|
-
|
|
180
|
-
|
|
162
|
+
setAttribute -->|"attributeChangedCallback()"| requestUpdate
|
|
163
|
+
setProperty -->requestUpdate
|
|
181
164
|
|
|
182
165
|
connectedCallback -.->|"unmount from DOM"| disconnectedCallback
|
|
183
166
|
disconnectedCallback --> END
|
|
@@ -189,8 +172,8 @@ A micro component usually implements `render()` and `update()`:
|
|
|
189
172
|
import { define, MiElement, refsBySelector } from 'mi-element'
|
|
190
173
|
|
|
191
174
|
class Counter extends MiElement {
|
|
192
|
-
static get
|
|
193
|
-
return { value:
|
|
175
|
+
static get properties() {
|
|
176
|
+
return { value: { type: Number } }
|
|
194
177
|
}
|
|
195
178
|
|
|
196
179
|
// define the innerHTML template for the component
|
|
@@ -226,20 +209,19 @@ class Counter extends MiElement {
|
|
|
226
209
|
## render()
|
|
227
210
|
|
|
228
211
|
Initial rendering of the component. Try to render the component only once!
|
|
229
|
-
If you need re-rendering by recrating the DOM do this outside of `render()`
|
|
230
212
|
|
|
231
213
|
Within the `render()` method, bear in mind to:
|
|
232
214
|
|
|
233
215
|
- Avoid changing the component's state.
|
|
234
216
|
- Avoid producing any side effects.
|
|
235
|
-
- Use only the component's
|
|
217
|
+
- Use only the component's properties as input.
|
|
236
218
|
|
|
237
219
|
!!! WARNING XSS - Cross-Site Scripting
|
|
238
220
|
|
|
239
221
|
Using [`innerHTML`][innerHTML] to create the components DOM is susceptible to
|
|
240
222
|
[XSS][XSS] attacks in case that user-supplied data contains valid HTML markup.
|
|
241
223
|
|
|
242
|
-
In all other cases you may consider the <code>
|
|
224
|
+
In all other cases you may consider the <code>html``</code> template literal or
|
|
243
225
|
`escHtml()` from the "mi-element" import, which escapes user-supplied data.
|
|
244
226
|
|
|
245
227
|
[innerHTML]: https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML
|
|
@@ -287,19 +269,19 @@ adding `id` attributes to the nodes where updates shall happen or event
|
|
|
287
269
|
listeners must be applied.
|
|
288
270
|
|
|
289
271
|
```js
|
|
290
|
-
import { MiElement, define
|
|
272
|
+
import { MiElement, define } from 'mi-element'
|
|
291
273
|
|
|
292
274
|
class Counter extends MiElement {
|
|
293
275
|
static template = `
|
|
294
276
|
<button id>Count</button>
|
|
295
|
-
<p>Counter value: <span
|
|
277
|
+
<p>Counter value: <span>0</span></p>
|
|
296
278
|
`
|
|
297
279
|
|
|
298
280
|
render() {
|
|
299
281
|
// template is already rendered on `this.renderRoot`
|
|
300
282
|
|
|
301
|
-
// get refs though `
|
|
302
|
-
this.refs =
|
|
283
|
+
// get refs though `refsBySelector`
|
|
284
|
+
this.refs = this.refsBySelector({ button: 'button', count: 'p > span'})
|
|
303
285
|
// this.refs == {button: <button>, count: <span>}
|
|
304
286
|
}
|
|
305
287
|
}
|
|
@@ -315,6 +297,11 @@ rendered elements as much as possible.
|
|
|
315
297
|
|
|
316
298
|
```js
|
|
317
299
|
class Counter extends MiElement {
|
|
300
|
+
render() {
|
|
301
|
+
// ...
|
|
302
|
+
this.update()
|
|
303
|
+
}
|
|
304
|
+
|
|
318
305
|
// ...
|
|
319
306
|
update() {
|
|
320
307
|
this.refs.count.textContent = this.value
|
|
@@ -327,18 +314,18 @@ any changed attributes are passed.
|
|
|
327
314
|
|
|
328
315
|
To mitigate [XSS][] attacks prefer the use of `.textContent` and avoid
|
|
329
316
|
~~`.innerHTML`~~. For attribute changes use `.setAttribute(name, newValue)`.
|
|
330
|
-
Both `.textContent` and `setAttribute()` provide escaping for you.
|
|
331
|
-
|
|
332
317
|
|
|
333
318
|
For finer control on updates the use of signals is encouraged. With this there
|
|
334
|
-
is no need to add logic to `
|
|
319
|
+
is no need to add logic to `update()`.
|
|
335
320
|
|
|
336
321
|
```js
|
|
337
322
|
import { MiElement, Signal } from 'mi-element'
|
|
338
323
|
|
|
339
324
|
class Counter extends MiElement {
|
|
340
|
-
static get
|
|
341
|
-
return {
|
|
325
|
+
static get properties() {
|
|
326
|
+
return {
|
|
327
|
+
value: { type: Number }
|
|
328
|
+
}
|
|
342
329
|
}
|
|
343
330
|
|
|
344
331
|
static template = `
|
|
@@ -346,8 +333,15 @@ class Counter extends MiElement {
|
|
|
346
333
|
<p>Counter value: <span>0</span></p>
|
|
347
334
|
`
|
|
348
335
|
|
|
336
|
+
constructor() {
|
|
337
|
+
super()
|
|
338
|
+
// set initial values
|
|
339
|
+
this.value = 0
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
|
|
349
343
|
render() {
|
|
350
|
-
const refs = refsBySelector(
|
|
344
|
+
const refs = this.refsBySelector({
|
|
351
345
|
button: 'button',
|
|
352
346
|
count: 'span'
|
|
353
347
|
})
|
|
@@ -365,13 +359,6 @@ class Counter extends MiElement {
|
|
|
365
359
|
}
|
|
366
360
|
```
|
|
367
361
|
|
|
368
|
-
## shouldUpdate(changedAttributes)
|
|
369
|
-
|
|
370
|
-
Convenience method in order to be able to decide on the changed attributes,
|
|
371
|
-
whether `update()` should be called or not.
|
|
372
|
-
|
|
373
|
-
Return `true` if component should be updated.
|
|
374
|
-
|
|
375
362
|
## on(eventName, listener, \[node\])
|
|
376
363
|
|
|
377
364
|
Adds listener function for eventName. listener is removed before component
|
package/docs/reactivity.md
CHANGED
|
@@ -16,9 +16,9 @@ import { html, render } from 'mi-html'
|
|
|
16
16
|
define(
|
|
17
17
|
'mi-counter',
|
|
18
18
|
class extends MiElement {
|
|
19
|
-
static get
|
|
19
|
+
static get properties() {
|
|
20
20
|
return {
|
|
21
|
-
count:
|
|
21
|
+
count: { type: Number } //<< this.count is already a signal
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
package/docs/signal.md
CHANGED
|
@@ -24,7 +24,7 @@ For convenience there is a `createSignal(initialValue<T>): State<T>` function to
|
|
|
24
24
|
create a signal.
|
|
25
25
|
|
|
26
26
|
```js
|
|
27
|
-
import { createSignal, State } from 'mi-
|
|
27
|
+
import { createSignal, State } from 'mi-signal'
|
|
28
28
|
|
|
29
29
|
const signal = createSignal(1)
|
|
30
30
|
// same as
|
|
@@ -58,7 +58,7 @@ signals state as well as to update on any change through
|
|
|
58
58
|
_synchronously_!
|
|
59
59
|
|
|
60
60
|
```js
|
|
61
|
-
import { createSignal, effect } from 'mi-
|
|
61
|
+
import { createSignal, effect } from 'mi-signal'
|
|
62
62
|
|
|
63
63
|
const signal = createSignal(1)
|
|
64
64
|
|
|
@@ -157,14 +157,15 @@ MiElement attributes are backed by signals. To subscribe to reactive changes a
|
|
|
157
157
|
`Signal.effect` callback can be used on all observed attributes.
|
|
158
158
|
|
|
159
159
|
```js
|
|
160
|
-
import { effect, define, MiElement
|
|
160
|
+
import { effect, define, MiElement } from 'mi-element'
|
|
161
|
+
import { createSignal } from 'mi-signal'
|
|
161
162
|
|
|
162
163
|
define(
|
|
163
164
|
'mi-counter',
|
|
164
165
|
class extends MiElement {
|
|
165
166
|
static template = `
|
|
166
|
-
<button
|
|
167
|
-
<div
|
|
167
|
+
<button> + </button>
|
|
168
|
+
<div></div>
|
|
168
169
|
`
|
|
169
170
|
|
|
170
171
|
static get attributes() {
|
|
@@ -174,8 +175,14 @@ define(
|
|
|
174
175
|
}
|
|
175
176
|
}
|
|
176
177
|
|
|
178
|
+
// define the signal to make all properties reactive
|
|
179
|
+
static createSignal = createSignal
|
|
180
|
+
|
|
177
181
|
render() {
|
|
178
|
-
|
|
182
|
+
// define initial value
|
|
183
|
+
this.count = this.count ?? 0
|
|
184
|
+
|
|
185
|
+
this.refs = this.refsBySelector({ button: 'button', div: 'div' })
|
|
179
186
|
this.refs.button.addEventListener('click', () => {
|
|
180
187
|
// change observed and reactive attribute...
|
|
181
188
|
this.count++
|
package/docs/store.md
CHANGED
package/docs/styling.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- !toc (minlevel=2) -->
|
|
4
4
|
|
|
5
|
-
- [
|
|
5
|
+
- [classNames](#classnames)
|
|
6
6
|
- [styleMap](#stylemap)
|
|
7
7
|
- [addGlobalStyles](#addglobalstyles)
|
|
8
8
|
|
|
@@ -10,16 +10,16 @@
|
|
|
10
10
|
|
|
11
11
|
# Styling
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## classNames
|
|
14
14
|
|
|
15
15
|
Obtain a class string from an object. Only class-names with trueish values are
|
|
16
16
|
returned.
|
|
17
17
|
|
|
18
18
|
```js
|
|
19
|
-
import {
|
|
19
|
+
import { classNames } from 'mi-element'
|
|
20
20
|
|
|
21
|
-
const className =
|
|
22
|
-
//> className == 'enabled number'
|
|
21
|
+
const className = classNames({ enabled: true, hidden: '', number: 1 }, 'always')
|
|
22
|
+
//> className == 'enabled number always'
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
## styleMap
|
|
@@ -74,4 +74,16 @@ customElements.define(
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
)
|
|
77
|
+
|
|
78
|
+
// with MiElement
|
|
79
|
+
import { define, MiElement } from 'mi-element'
|
|
80
|
+
|
|
81
|
+
define('x-with-global-styles', class extends MiElement {
|
|
82
|
+
static useGlobalStyles() {
|
|
83
|
+
return true
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
static template = '<h1>Hello World</h1>'
|
|
87
|
+
})
|
|
88
|
+
|
|
77
89
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mi-element",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Build lightweight reactive micro web-components",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"homepage": "https://github.com/commenthol/mi-element/tree/main/packages/mi-element#readme",
|
|
@@ -25,11 +25,6 @@
|
|
|
25
25
|
"types": "./types/index.d.ts",
|
|
26
26
|
"default": "./dist/index.js"
|
|
27
27
|
},
|
|
28
|
-
"./min": {
|
|
29
|
-
"sourceMap": "./dist/index.min.js.map",
|
|
30
|
-
"types": "./types/min.d.ts",
|
|
31
|
-
"default": "./dist/index.min.js"
|
|
32
|
-
},
|
|
33
28
|
"./element": {
|
|
34
29
|
"development": "./src/element.js",
|
|
35
30
|
"types": "./types/element.d.ts",
|
|
@@ -74,31 +69,32 @@
|
|
|
74
69
|
"types"
|
|
75
70
|
],
|
|
76
71
|
"dependencies": {
|
|
77
|
-
"mi-signal": "0.
|
|
72
|
+
"mi-signal": "0.7.0"
|
|
78
73
|
},
|
|
79
74
|
"devDependencies": {
|
|
80
|
-
"@eslint/js": "^9.39.
|
|
75
|
+
"@eslint/js": "^9.39.2",
|
|
81
76
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
82
77
|
"@rollup/plugin-terser": "^0.4.4",
|
|
83
78
|
"@testing-library/dom": "^10.4.1",
|
|
84
|
-
"@types/node": "^24.10.
|
|
85
|
-
"@vitest/browser": "^4.0.
|
|
86
|
-
"@vitest/browser-playwright": "^4.0.
|
|
87
|
-
"@vitest/coverage-istanbul": "^4.0.
|
|
88
|
-
"eslint": "^9.39.
|
|
79
|
+
"@types/node": "^24.10.4",
|
|
80
|
+
"@vitest/browser": "^4.0.16",
|
|
81
|
+
"@vitest/browser-playwright": "^4.0.16",
|
|
82
|
+
"@vitest/coverage-istanbul": "^4.0.16",
|
|
83
|
+
"eslint": "^9.39.2",
|
|
89
84
|
"globals": "^16.5.0",
|
|
90
85
|
"npm-run-all2": "^8.0.4",
|
|
91
|
-
"playwright": "^1.
|
|
92
|
-
"prettier": "^3.
|
|
93
|
-
"rimraf": "^6.1.
|
|
94
|
-
"rollup": "^4.
|
|
86
|
+
"playwright": "^1.57.0",
|
|
87
|
+
"prettier": "^3.7.4",
|
|
88
|
+
"rimraf": "^6.1.2",
|
|
89
|
+
"rollup": "^4.54.0",
|
|
95
90
|
"typescript": "^5.9.3",
|
|
96
|
-
"vite": "^7.
|
|
97
|
-
"vitest": "^4.0.
|
|
91
|
+
"vite": "^7.3.0",
|
|
92
|
+
"vitest": "^4.0.16",
|
|
93
|
+
"mi-html": "0.7.0"
|
|
98
94
|
},
|
|
99
95
|
"scripts": {
|
|
100
96
|
"all": "npm-run-all pretty lint test build types",
|
|
101
|
-
"build": "rimraf dist && rollup -c &&
|
|
97
|
+
"build": "rimraf dist && rollup -c && ls -al dist",
|
|
102
98
|
"dev": "npm run test:browser",
|
|
103
99
|
"docs": "cd docs; for f in *.md; do markedpp -s -i $f; done",
|
|
104
100
|
"example": "vite --open /example/",
|
package/src/context.js
CHANGED