mi-element 0.8.0 → 0.9.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/dist/context.js +1 -1
- package/dist/element.js +6 -3
- package/docs/element.md +81 -1
- package/package.json +3 -3
- package/src/context.js +1 -1
- package/src/element.js +29 -2
package/dist/context.js
CHANGED
|
@@ -67,7 +67,7 @@ class ContextConsumer {
|
|
|
67
67
|
_callback(value, unsubscribe) {
|
|
68
68
|
unsubscribe && (this.subscribe ? this.unsubscribe && (this.unsubscribe !== unsubscribe && this.unsubscribe(),
|
|
69
69
|
this.unsubscribe = unsubscribe) : unsubscribe()), this.validate(value) && (this.#value = value,
|
|
70
|
-
this.host.requestUpdate());
|
|
70
|
+
this.host.requestUpdate(value));
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
package/dist/element.js
CHANGED
|
@@ -52,10 +52,13 @@ class MiElement extends HTMLElement {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
connectedCallback() {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
this.renderRoot = shadowRootInit ? this.shadowRoot ?? this.attachShadow(shadowRootInit) : this,
|
|
55
|
+
const {shadowRootInit: shadowRootInit, useGlobalStyles: useGlobalStyles, template: template, formAssociated: formAssociated} = this.constructor;
|
|
56
|
+
if (this.#controllers.forEach(controller => controller.hostConnected?.()), this.renderRoot = shadowRootInit ? this.shadowRoot ?? this.attachShadow(shadowRootInit) : this,
|
|
58
57
|
this.addTemplate(template), useGlobalStyles && addGlobalStyles(this.renderRoot),
|
|
58
|
+
formAssociated && this.handleFormdata) {
|
|
59
|
+
const internals = this.attachInternals();
|
|
60
|
+
internals.form && this.on('formdata', ev => this.handleFormdata(ev), internals.form);
|
|
61
|
+
}
|
|
59
62
|
this.render(), this.requestUpdate();
|
|
60
63
|
}
|
|
61
64
|
disconnectedCallback() {
|
package/docs/element.md
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* [disconnectedCallback()](#disconnectedcallback)
|
|
8
8
|
* [attributeChangedCallback(name, oldValue, newValue)](#attributechangedcallbackname-oldvalue-newvalue)
|
|
9
9
|
* [Update Cycle](#update-cycle)
|
|
10
|
+
* [Form-Associated Elements](#form-associated-elements)
|
|
10
11
|
* [render()](#render)
|
|
11
12
|
* [update(changedAttributes)](#updatechangedattributes)
|
|
12
13
|
* [shouldUpdate(changedAttributes)](#shouldupdatechangedattributes)
|
|
@@ -206,6 +207,85 @@ class Counter extends MiElement {
|
|
|
206
207
|
}
|
|
207
208
|
```
|
|
208
209
|
|
|
210
|
+
## Form-Associated Elements
|
|
211
|
+
|
|
212
|
+
MiElement supports [form-associated custom elements][form-associated], allowing
|
|
213
|
+
your components to participate in HTML forms just like native form controls.
|
|
214
|
+
|
|
215
|
+
[form-associated]: https://web.dev/articles/form-associated-custom-elements
|
|
216
|
+
|
|
217
|
+
### Declaring a Form-Associated Element
|
|
218
|
+
|
|
219
|
+
Set `static formAssociated = true` on your component to enable form association:
|
|
220
|
+
|
|
221
|
+
```js
|
|
222
|
+
import { define, MiElement, html } from 'mi-element'
|
|
223
|
+
|
|
224
|
+
class CustomInput extends MiElement {
|
|
225
|
+
static formAssociated = true
|
|
226
|
+
|
|
227
|
+
static get properties() {
|
|
228
|
+
return {
|
|
229
|
+
name: { type: String },
|
|
230
|
+
value: { type: String, initial: '' }
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
static template = html`<input type="text" />`
|
|
235
|
+
|
|
236
|
+
render() {
|
|
237
|
+
this.refs = this.refsBySelector({ input: 'input' })
|
|
238
|
+
this.refs.input.addEventListener('input', (ev) => {
|
|
239
|
+
this.value = ev.target.value
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
define('custom-input', CustomInput)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Handling Form Data
|
|
248
|
+
|
|
249
|
+
Implement the `handleFormdata(ev)` method to submit your component's data with
|
|
250
|
+
the form:
|
|
251
|
+
|
|
252
|
+
```js
|
|
253
|
+
class CustomInput extends MiElement {
|
|
254
|
+
static formAssociated = true
|
|
255
|
+
|
|
256
|
+
// ...other code...
|
|
257
|
+
|
|
258
|
+
handleFormdata(ev) {
|
|
259
|
+
// Only include data if the component has a name attribute
|
|
260
|
+
if (this.name) {
|
|
261
|
+
ev.formData.append(this.name, this.refs.input.value)
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
The `handleFormdata` method is automatically called when the form is submitted or
|
|
268
|
+
when `FormData` is created from the form.
|
|
269
|
+
|
|
270
|
+
### Usage Example
|
|
271
|
+
|
|
272
|
+
```html
|
|
273
|
+
<form id="my-form">
|
|
274
|
+
<custom-input name="username" value="john"></custom-input>
|
|
275
|
+
<custom-input name="email" value="john@example.com"></custom-input>
|
|
276
|
+
<button type="submit">Submit</button>
|
|
277
|
+
</form>
|
|
278
|
+
|
|
279
|
+
<script>
|
|
280
|
+
const form = document.getElementById('my-form')
|
|
281
|
+
const formData = new FormData(form)
|
|
282
|
+
|
|
283
|
+
console.log(formData.get('username')) // 'john'
|
|
284
|
+
console.log(formData.get('email')) // 'john@example.com'
|
|
285
|
+
</script>
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
|
|
209
289
|
## render()
|
|
210
290
|
|
|
211
291
|
Initial rendering of the component. Try to render the component only once!
|
|
@@ -275,7 +355,7 @@ class Counter extends MiElement {
|
|
|
275
355
|
static template = `
|
|
276
356
|
<button id>Count</button>
|
|
277
357
|
<p>Counter value: <span>0</span></p>
|
|
278
|
-
|
|
358
|
+
``
|
|
279
359
|
|
|
280
360
|
render() {
|
|
281
361
|
// template is already rendered on `this.renderRoot`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mi-element",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
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",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"types"
|
|
70
70
|
],
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"mi-signal": "0.
|
|
72
|
+
"mi-signal": "0.9.0"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
75
|
"@eslint/js": "^9.39.2",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"typescript": "^5.9.3",
|
|
91
91
|
"vite": "^7.3.0",
|
|
92
92
|
"vitest": "^4.0.16",
|
|
93
|
-
"mi-html": "0.
|
|
93
|
+
"mi-html": "0.9.0"
|
|
94
94
|
},
|
|
95
95
|
"scripts": {
|
|
96
96
|
"all": "npm-run-all pretty lint test build types",
|
package/src/context.js
CHANGED
package/src/element.js
CHANGED
|
@@ -159,9 +159,11 @@ export class MiElement extends HTMLElement {
|
|
|
159
159
|
* @category lifecycle
|
|
160
160
|
*/
|
|
161
161
|
connectedCallback() {
|
|
162
|
-
this.#controllers.forEach((controller) => controller.hostConnected?.())
|
|
163
162
|
// @ts-expect-error
|
|
164
|
-
const { shadowRootInit, useGlobalStyles, template } =
|
|
163
|
+
const { shadowRootInit, useGlobalStyles, template, formAssociated } =
|
|
164
|
+
this.constructor
|
|
165
|
+
// connect all controllers
|
|
166
|
+
this.#controllers.forEach((controller) => controller.hostConnected?.())
|
|
165
167
|
this.renderRoot = shadowRootInit
|
|
166
168
|
? (this.shadowRoot ?? this.attachShadow(shadowRootInit))
|
|
167
169
|
: this
|
|
@@ -169,6 +171,31 @@ export class MiElement extends HTMLElement {
|
|
|
169
171
|
if (useGlobalStyles) {
|
|
170
172
|
addGlobalStyles(this.renderRoot)
|
|
171
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* handle formdata event if `handleFormdata` method is defined on the component.
|
|
176
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/formdata_event
|
|
177
|
+
* ```js
|
|
178
|
+
* class MyElement extends MiElement {
|
|
179
|
+
* static formAssociated = true // required to receive formdata event
|
|
180
|
+
* handleFormdata(ev) {
|
|
181
|
+
* const { name, value } = this.refs.input
|
|
182
|
+
* ev.formData.append(name, value)
|
|
183
|
+
* }
|
|
184
|
+
* render() {
|
|
185
|
+
* this.renderRoot.innerHTML = html`<input name="${this.name}" value="${this.value}">`
|
|
186
|
+
* this.refs = { input: this.renderRoot.querySelector('input') }
|
|
187
|
+
* }
|
|
188
|
+
* }
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
// @ts-expect-error
|
|
192
|
+
if (formAssociated && this.handleFormdata) {
|
|
193
|
+
const internals = this.attachInternals()
|
|
194
|
+
if (internals.form) {
|
|
195
|
+
// @ts-expect-error
|
|
196
|
+
this.on('formdata', (ev) => this.handleFormdata(ev), internals.form)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
172
199
|
this.render() // initial render
|
|
173
200
|
this.requestUpdate() // request initial update
|
|
174
201
|
}
|