@twobirds/microcomponents 0.2.1 → 0.2.3

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.
Files changed (50) hide show
  1. package/README.md +45 -20
  2. package/dist/MC.min.js +1 -1
  3. package/dist/autoload.min.js +1 -1
  4. package/dist/core/MC.d.ts +289 -238
  5. package/dist/core/MC.d.ts.map +1 -1
  6. package/dist/core/MC.js +445 -184
  7. package/dist/core/elements.d.ts +58 -231
  8. package/dist/core/elements.d.ts.map +1 -1
  9. package/dist/core/elements.js +99 -214
  10. package/dist/core/index.d.ts +75 -0
  11. package/dist/core/index.d.ts.map +1 -0
  12. package/dist/core/index.js +72 -0
  13. package/dist/core/mcGuards.d.ts +7 -7
  14. package/dist/core/mcGuards.d.ts.map +1 -1
  15. package/dist/core/mcGuards.js +1 -1
  16. package/dist/elements.min.js +1 -1
  17. package/dist/formValues.min.js +1 -1
  18. package/dist/mcGuards.min.js +1 -1
  19. package/dist/microcomponents.min.js +1 -1
  20. package/dist/observables.min.js +1 -1
  21. package/dist/state/formValues.d.ts +113 -66
  22. package/dist/state/formValues.d.ts.map +1 -1
  23. package/dist/state/formValues.js +134 -68
  24. package/dist/state/index.d.ts +7 -0
  25. package/dist/state/index.d.ts.map +1 -0
  26. package/dist/state/index.js +5 -0
  27. package/dist/state/observables.d.ts +66 -133
  28. package/dist/state/observables.d.ts.map +1 -1
  29. package/dist/state/observables.js +185 -83
  30. package/dist/state/placeholders.d.ts +1 -0
  31. package/dist/state/placeholders.d.ts.map +1 -1
  32. package/dist/state/placeholders.js +1 -0
  33. package/dist/types/index.d.ts +7 -0
  34. package/dist/types/index.d.ts.map +1 -0
  35. package/dist/types/index.js +1 -0
  36. package/dist/types/mcTypes.d.ts +3 -3
  37. package/dist/types/mcTypes.js +1 -1
  38. package/dist/utils/autoload.d.ts +6 -2
  39. package/dist/utils/autoload.d.ts.map +1 -1
  40. package/dist/utils/autoload.js +7 -5
  41. package/dist/utils/events.d.ts +22 -79
  42. package/dist/utils/events.d.ts.map +1 -1
  43. package/dist/utils/events.js +63 -1
  44. package/dist/utils/helpers.d.ts +10 -9
  45. package/dist/utils/helpers.d.ts.map +1 -1
  46. package/dist/utils/helpers.js +26 -14
  47. package/dist/utils/index.d.ts +17 -0
  48. package/dist/utils/index.d.ts.map +1 -0
  49. package/dist/utils/index.js +12 -0
  50. package/package.json +13 -15
@@ -1,70 +1,117 @@
1
1
  /**
2
- * @module Form Values
3
- *
4
- * This module exports a single function {@link formValues} that traverses a DOM element
5
- * (usually an HTMLFormElement), collecting all input fields and creating a two-way bound
6
- * observable inside the target object:
7
- * - When input values change, the observable object updates accordingly.
8
- * - When the observable property values change, the input field values update in the DOM.
9
- *
10
- * **Prerequisites:**
11
- * - Input fields must have a `name` attribute.
12
- * - Names must be unique within the form.
13
- *
14
- * @example
15
- * ```ts
16
- * import { DC } from '@twobirds/microcomponents';
17
- * import { formValues } from '@twobirds/microcomponents/formValues';
18
- *
19
- * class MyMC extends DC {
20
- * constructor(target: HTMLElement) {
21
- * super(target);
22
- * this.target.innerHTML = '<form><input name="textinput" value="" /></form>';
23
- *
24
- * // link the form property to the DOM input fields
25
- * formValues(this, 'form', this.target);
26
- *
27
- * // changing the form property updates the DOM automatically
28
- * this.form.textinput = 'Hello world!';
29
- * }
30
- * }
31
- * ```
32
- */
2
+ Two-way binding between DOM form controls (`input`, `select`, `textarea`) and an observable
3
+ object property. Reads reflect live DOM values; writes update the controls synchronously.
4
+ Most likely you will only need the the first variant mentioned in the examples below.
5
+
6
+ @module Form Values
7
+
8
+ @example Plugin — chainable instance method on DC components
9
+ ```ts
10
+ import { DC, defineCE, formValuePlugin } from '@twobirds/microcomponents';
11
+
12
+ formValuePlugin(DC);
13
+
14
+ class CheckoutForm extends DC {
15
+ static assets = ['targetHtml'];
16
+
17
+ oneAssets() {
18
+ this
19
+ .formValues(this.target, 'formData')
20
+ .subscribe('formData', (data) => console.log(data))
21
+ .bind('formData', this.target);
22
+ }
23
+ }
24
+
25
+ defineCE('checkout-form', CheckoutForm);
26
+ ```
27
+
28
+ @example Standalone function — bind a plain object to a form in the DOM
29
+ ```ts
30
+ import { formValues } from '@twobirds/microcomponents/formValues';
31
+
32
+ const state = {};
33
+ formValues(state, 'formData', document.getElementById('myForm'));
34
+ console.log(state.formData.username); // reads <input name="username"> value
35
+ state.formData.role = 'admin'; // writes back to the <select> control
36
+ ```
37
+ */
33
38
  /**
34
- * Attaches a form-values observable to a target object property, allowing two-way binding
35
- * between the DOM form controls and the object's state.
36
- *
37
- * @param targetObject - The object to which the form values will be attached as an observable.
38
- * @param propertyName - The name of the property on the target object that will hold the form data.
39
- * @param dom - The DOM element or shadow root (typically a form) whose inputs should be observed and bound.
40
- *
41
- * @example
42
- * ```html
43
- * <form id="myForm">
44
- * <input type="text" name="username" value="">
45
- * <select name="role"><option value="admin">Admin</option><option value="user">User</option></select>
46
- * <textarea name="bio"></textarea>
47
- * <input type="checkbox" name="active">
48
- * <input type="radio" name="gender" value="male"> Male
49
- * <input type="radio" name="gender" value="female"> Female
50
- * </form>
51
- * ```
52
- *
53
- * ```ts
54
- * import { formValues } from '@twobirds/microcomponents/formValues';
55
- *
56
- * const state = {};
57
- * formValues(state, 'formData', document.getElementById('myForm'));
58
- *
59
- * // Read values from the form
60
- * console.log(state.formData.username); // string
61
- * console.log(state.formData.role); // "admin" or "user"
62
- * console.log(state.formData.active); // boolean
63
- *
64
- * // Write values to the form (updates DOM automatically)
65
- * state.formData.username = 'john_doe';
66
- * state.formData.gender = 'male';
67
- * ```
68
- */
39
+ @hidden
40
+
41
+ Attaches a form-values observable to a target object property, enabling two-way binding
42
+ between DOM form controls and the object's state.
43
+
44
+ Scans the given DOM subtree for `input`, `select`, and `textarea` elements with a `name`
45
+ attribute and creates live getters/setters on the resulting observable. Input events trigger
46
+ automatic notifications so subscribers react to user changes.
47
+
48
+ @param targetObject - The object that will receive the form-data property.
49
+ @param propertyName - The property name under which the bound form data is stored.
50
+ @param dom - HTMLElement or ShadowRoot containing the form controls.
51
+
52
+ @example Bind a plain object to an HTML form element
53
+ ```ts
54
+ import { formValues } from '@twobirds/microcomponents/formValues';
55
+
56
+ const state = {};
57
+ formValues(state, 'formData', document.getElementById('myForm'));
58
+ console.log(state.formData.username); // reads <input name="username"> value
59
+ state.formData.role = 'admin'; // writes back to the <select> control
60
+ ```
61
+
62
+ @example Subscribe to form changes inside a component
63
+ ```ts
64
+ import { DC, defineCE, formValues } from '@twobirds/microcomponents';
65
+
66
+ class MyForm extends DC {
67
+ static assets = ['targetHtml'];
68
+
69
+ oneAssets() {
70
+ formValues(this, 'formData', this.target);
71
+ this.formData.subscribe((data) => console.log('form changed:', data));
72
+ }
73
+ }
74
+
75
+ defineCE('my-form', MyForm);
76
+ ```
77
+ */
69
78
  export declare function formValues<T extends object>(targetObject: T, propertyName: keyof T & string, dom: HTMLElement | ShadowRoot): void;
79
+ /**
80
+ Attaches a convenience method `formValues()` to the prototype of a microcomponent class so that
81
+ form controls can be bound directly on the instance without importing the standalone function.
82
+
83
+ This plugin also pulls in all observable methods from {@link observePlugin} — `notify()`,
84
+ `subscribe()`, and `bind()` — so you can chain them fluently after binding form data.
85
+
86
+ Call it once at module level before defining any component classes. The plugin is idempotent —
87
+ subsequent calls are no-ops.
88
+
89
+ @param Class - A constructor function whose prototype chain includes {@link Core.McBase}.
90
+
91
+ @example
92
+ ```ts
93
+ import { DC, defineCE, formValuePlugin } from '@twobirds/microcomponents';
94
+
95
+ formValuePlugin(DC);
96
+
97
+ class CheckoutForm extends DC {
98
+ static assets = ['targetHtml'];
99
+
100
+ oneAssets() {
101
+ this
102
+ .formValues(this.target, 'formData')
103
+ .subscribe('formData', (data) => console.log(data))
104
+ .bind('formData', this.target);
105
+ }
106
+ }
107
+
108
+ defineCE('checkout-form', CheckoutForm);
109
+ ```
110
+ */
111
+ export declare function formValuePlugin(Class: any): void;
112
+ declare module '../core/MC' {
113
+ interface McBase<TTarget extends object = object> {
114
+ formValues(target: object, propName: string): this;
115
+ }
116
+ }
70
117
  //# sourceMappingURL=formValues.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"formValues.d.ts","sourceRoot":"","sources":["../../src/state/formValues.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AA4SH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAC1C,YAAY,EAAE,CAAC,EACf,YAAY,EAAE,MAAM,CAAC,GAAG,MAAM,EAC9B,GAAG,EAAE,WAAW,GAAG,UAAU,QAuB7B"}
1
+ {"version":3,"file":"formValues.d.ts","sourceRoot":"","sources":["../../src/state/formValues.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoCE;AA8SF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuCE;AACF,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAC1C,YAAY,EAAE,CAAC,EACf,YAAY,EAAE,MAAM,CAAC,GAAG,MAAM,EAC9B,GAAG,EAAE,WAAW,GAAG,UAAU,QAuB7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+BE;AACF,wBAAgB,eAAe,CAAC,KAAK,EAAE,GAAG,QA2BzC;AAED,OAAO,QAAQ,YAAY,CAAC;IAC3B,UAAiB,MAAM,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM;QACtD,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;KACnD;CACD"}
@@ -1,38 +1,44 @@
1
1
  /**
2
- * @module Form Values
3
- *
4
- * This module exports a single function {@link formValues} that traverses a DOM element
5
- * (usually an HTMLFormElement), collecting all input fields and creating a two-way bound
6
- * observable inside the target object:
7
- * - When input values change, the observable object updates accordingly.
8
- * - When the observable property values change, the input field values update in the DOM.
9
- *
10
- * **Prerequisites:**
11
- * - Input fields must have a `name` attribute.
12
- * - Names must be unique within the form.
13
- *
14
- * @example
15
- * ```ts
16
- * import { DC } from '@twobirds/microcomponents';
17
- * import { formValues } from '@twobirds/microcomponents/formValues';
18
- *
19
- * class MyMC extends DC {
20
- * constructor(target: HTMLElement) {
21
- * super(target);
22
- * this.target.innerHTML = '<form><input name="textinput" value="" /></form>';
23
- *
24
- * // link the form property to the DOM input fields
25
- * formValues(this, 'form', this.target);
26
- *
27
- * // changing the form property updates the DOM automatically
28
- * this.form.textinput = 'Hello world!';
29
- * }
30
- * }
31
- * ```
32
- */
2
+ Two-way binding between DOM form controls (`input`, `select`, `textarea`) and an observable
3
+ object property. Reads reflect live DOM values; writes update the controls synchronously.
4
+ Most likely you will only need the the first variant mentioned in the examples below.
5
+
6
+ @module Form Values
7
+
8
+ @example Plugin — chainable instance method on DC components
9
+ ```ts
10
+ import { DC, defineCE, formValuePlugin } from '@twobirds/microcomponents';
11
+
12
+ formValuePlugin(DC);
13
+
14
+ class CheckoutForm extends DC {
15
+ static assets = ['targetHtml'];
16
+
17
+ oneAssets() {
18
+ this
19
+ .formValues(this.target, 'formData')
20
+ .subscribe('formData', (data) => console.log(data))
21
+ .bind('formData', this.target);
22
+ }
23
+ }
24
+
25
+ defineCE('checkout-form', CheckoutForm);
26
+ ```
27
+
28
+ @example Standalone function — bind a plain object to a form in the DOM
29
+ ```ts
30
+ import { formValues } from '@twobirds/microcomponents/formValues';
31
+
32
+ const state = {};
33
+ formValues(state, 'formData', document.getElementById('myForm'));
34
+ console.log(state.formData.username); // reads <input name="username"> value
35
+ state.formData.role = 'admin'; // writes back to the <select> control
36
+ ```
37
+ */
33
38
  import { DC } from '../core/MC.js';
34
39
  import { copyGettersSetters, isBrowser } from '../utils/helpers.js';
35
- import { observe } from './observables.js';
40
+ import { observe, observePlugin } from './observables.js';
41
+ observePlugin(DC);
36
42
  /**
37
43
  Extracts the lowercase tag name of an element (e.g., 'input', 'select', 'textarea').
38
44
  @param inputElement - The HTML element to extract the tag name from.
@@ -177,7 +183,7 @@ class SelectHandler extends InputBase {
177
183
  .forEach((o) => {
178
184
  ret.push(o.value);
179
185
  });
180
- return this.#multi ? ret.join(',') : ret[0];
186
+ return this.#multi ? ret : ret[0];
181
187
  }
182
188
  /**
183
189
  * Sets the selected option(s) by value.
@@ -279,41 +285,45 @@ const getInputHandlers = (e) => {
279
285
  return handlers;
280
286
  };
281
287
  /**
282
- * Attaches a form-values observable to a target object property, allowing two-way binding
283
- * between the DOM form controls and the object's state.
284
- *
285
- * @param targetObject - The object to which the form values will be attached as an observable.
286
- * @param propertyName - The name of the property on the target object that will hold the form data.
287
- * @param dom - The DOM element or shadow root (typically a form) whose inputs should be observed and bound.
288
- *
289
- * @example
290
- * ```html
291
- * <form id="myForm">
292
- * <input type="text" name="username" value="">
293
- * <select name="role"><option value="admin">Admin</option><option value="user">User</option></select>
294
- * <textarea name="bio"></textarea>
295
- * <input type="checkbox" name="active">
296
- * <input type="radio" name="gender" value="male"> Male
297
- * <input type="radio" name="gender" value="female"> Female
298
- * </form>
299
- * ```
300
- *
301
- * ```ts
302
- * import { formValues } from '@twobirds/microcomponents/formValues';
303
- *
304
- * const state = {};
305
- * formValues(state, 'formData', document.getElementById('myForm'));
306
- *
307
- * // Read values from the form
308
- * console.log(state.formData.username); // string
309
- * console.log(state.formData.role); // "admin" or "user"
310
- * console.log(state.formData.active); // boolean
311
- *
312
- * // Write values to the form (updates DOM automatically)
313
- * state.formData.username = 'john_doe';
314
- * state.formData.gender = 'male';
315
- * ```
316
- */
288
+ @hidden
289
+
290
+ Attaches a form-values observable to a target object property, enabling two-way binding
291
+ between DOM form controls and the object's state.
292
+
293
+ Scans the given DOM subtree for `input`, `select`, and `textarea` elements with a `name`
294
+ attribute and creates live getters/setters on the resulting observable. Input events trigger
295
+ automatic notifications so subscribers react to user changes.
296
+
297
+ @param targetObject - The object that will receive the form-data property.
298
+ @param propertyName - The property name under which the bound form data is stored.
299
+ @param dom - HTMLElement or ShadowRoot containing the form controls.
300
+
301
+ @example Bind a plain object to an HTML form element
302
+ ```ts
303
+ import { formValues } from '@twobirds/microcomponents/formValues';
304
+
305
+ const state = {};
306
+ formValues(state, 'formData', document.getElementById('myForm'));
307
+ console.log(state.formData.username); // reads <input name="username"> value
308
+ state.formData.role = 'admin'; // writes back to the <select> control
309
+ ```
310
+
311
+ @example Subscribe to form changes inside a component
312
+ ```ts
313
+ import { DC, defineCE, formValues } from '@twobirds/microcomponents';
314
+
315
+ class MyForm extends DC {
316
+ static assets = ['targetHtml'];
317
+
318
+ oneAssets() {
319
+ formValues(this, 'formData', this.target);
320
+ this.formData.subscribe((data) => console.log('form changed:', data));
321
+ }
322
+ }
323
+
324
+ defineCE('my-form', MyForm);
325
+ ```
326
+ */
317
327
  export function formValues(targetObject, propertyName, dom) {
318
328
  /* v8 ignore next */
319
329
  if (!isBrowser())
@@ -332,3 +342,59 @@ export function formValues(targetObject, propertyName, dom) {
332
342
  });
333
343
  });
334
344
  }
345
+ /**
346
+ Attaches a convenience method `formValues()` to the prototype of a microcomponent class so that
347
+ form controls can be bound directly on the instance without importing the standalone function.
348
+
349
+ This plugin also pulls in all observable methods from {@link observePlugin} — `notify()`,
350
+ `subscribe()`, and `bind()` — so you can chain them fluently after binding form data.
351
+
352
+ Call it once at module level before defining any component classes. The plugin is idempotent —
353
+ subsequent calls are no-ops.
354
+
355
+ @param Class - A constructor function whose prototype chain includes {@link Core.McBase}.
356
+
357
+ @example
358
+ ```ts
359
+ import { DC, defineCE, formValuePlugin } from '@twobirds/microcomponents';
360
+
361
+ formValuePlugin(DC);
362
+
363
+ class CheckoutForm extends DC {
364
+ static assets = ['targetHtml'];
365
+
366
+ oneAssets() {
367
+ this
368
+ .formValues(this.target, 'formData')
369
+ .subscribe('formData', (data) => console.log(data))
370
+ .bind('formData', this.target);
371
+ }
372
+ }
373
+
374
+ defineCE('checkout-form', CheckoutForm);
375
+ ```
376
+ */
377
+ export function formValuePlugin(Class) {
378
+ const McBaseProto = Object.getPrototypeOf(Class.prototype);
379
+ if (!Object.hasOwn(McBaseProto, 'formValues')) {
380
+ Object.assign(McBaseProto, {
381
+ /**
382
+ * Bind all named form controls inside a DOM subtree to an observable property on this instance.
383
+ * Ensures the property becomes observable and stays in sync with input changes.
384
+ * @param target - HTMLElement or ShadowRoot containing the form controls.
385
+ * @param propName - The name of the property that will hold the bound form data.
386
+ * @returns This instance for chaining.
387
+
388
+ @example
389
+ ```ts
390
+ this.formValues(this.target, 'formData');
391
+ // now this.formData.username reflects <input name="username"> live
392
+ ```
393
+ */
394
+ formValues: function (target, propName) {
395
+ formValues(this, propName, target);
396
+ return this; // chainable
397
+ },
398
+ });
399
+ }
400
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ @module State
3
+ */
4
+ export { observe, observePlugin, isAlreadyObserved } from './observables.js';
5
+ export type { Observable, Callback, Observed, ObservedKey, Unobserved } from './observables.js';
6
+ export { formValues, formValuePlugin } from './formValues.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/state/index.ts"],"names":[],"mappings":"AAAA;;EAEE;AACF,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC7E,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAChG,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ @module State
3
+ */
4
+ export { observe, observePlugin, isAlreadyObserved } from './observables.js';
5
+ export { formValues, formValuePlugin } from './formValues.js';