@twobirds/microcomponents 0.1.21 → 0.2.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.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +91 -92
  3. package/dist/MC.min.js +1 -345
  4. package/dist/autoload.min.js +1 -0
  5. package/dist/core/MC.d.ts +401 -0
  6. package/dist/core/MC.d.ts.map +1 -0
  7. package/dist/core/MC.js +471 -0
  8. package/dist/core/autoload.d.ts +54 -0
  9. package/dist/core/autoload.d.ts.map +1 -0
  10. package/dist/core/autoload.js +158 -0
  11. package/dist/core/elements.d.ts +232 -0
  12. package/dist/core/elements.d.ts.map +1 -0
  13. package/dist/core/elements.js +123 -0
  14. package/dist/core/events.d.ts +231 -0
  15. package/dist/core/events.d.ts.map +1 -0
  16. package/dist/core/events.js +228 -0
  17. package/dist/core/mcGuards.d.ts +49 -0
  18. package/dist/core/mcGuards.d.ts.map +1 -0
  19. package/dist/core/mcGuards.js +19 -0
  20. package/dist/elements.min.js +1 -1
  21. package/dist/events.min.js +1 -0
  22. package/dist/formValues.min.js +1 -1
  23. package/dist/mcGuards.min.js +1 -0
  24. package/dist/microcomponents.d.ts +8 -5
  25. package/dist/microcomponents.d.ts.map +1 -1
  26. package/dist/microcomponents.js +8 -5
  27. package/dist/microcomponents.min.js +1 -1
  28. package/dist/observables.min.js +1 -1
  29. package/dist/state/formValues.d.ts +70 -0
  30. package/dist/state/formValues.d.ts.map +1 -0
  31. package/dist/state/formValues.js +334 -0
  32. package/dist/state/observables.d.ts +276 -0
  33. package/dist/state/observables.d.ts.map +1 -0
  34. package/dist/state/observables.js +382 -0
  35. package/dist/state/placeholders.d.ts +62 -0
  36. package/dist/state/placeholders.d.ts.map +1 -0
  37. package/dist/state/placeholders.js +181 -0
  38. package/dist/types/mcTypes.d.ts +201 -0
  39. package/dist/types/mcTypes.d.ts.map +1 -0
  40. package/dist/types/mcTypes.js +9 -0
  41. package/dist/utils/helpers.d.ts +195 -0
  42. package/dist/utils/helpers.d.ts.map +1 -0
  43. package/dist/utils/helpers.js +340 -0
  44. package/package.json +96 -62
  45. package/dist/MC.d.ts +0 -31
  46. package/dist/MC.d.ts.map +0 -1
  47. package/dist/MC.js +0 -249
  48. package/dist/MC.ts +0 -613
  49. package/dist/MicroComponents.svg +0 -15
  50. package/dist/elements.d.ts +0 -5
  51. package/dist/elements.d.ts.map +0 -1
  52. package/dist/elements.js +0 -49
  53. package/dist/elements.ts +0 -114
  54. package/dist/events.d.ts +0 -11
  55. package/dist/events.d.ts.map +0 -1
  56. package/dist/events.js +0 -55
  57. package/dist/events.ts +0 -158
  58. package/dist/formValues.d.ts +0 -3
  59. package/dist/formValues.d.ts.map +0 -1
  60. package/dist/formValues.js +0 -156
  61. package/dist/formValues.ts +0 -371
  62. package/dist/helpers.d.ts +0 -15
  63. package/dist/helpers.d.ts.map +0 -1
  64. package/dist/helpers.js +0 -113
  65. package/dist/helpers.ts +0 -219
  66. package/dist/main.mjs +0 -1
  67. package/dist/microcomponents.ts +0 -6
  68. package/dist/observables.d.ts +0 -11
  69. package/dist/observables.d.ts.map +0 -1
  70. package/dist/observables.js +0 -228
  71. package/dist/observables.ts +0 -449
@@ -1,371 +0,0 @@
1
- import { DC } from './MC.js';
2
- import { copyGettersSetters, LooseObject } from './helpers.js';
3
- import { observe } from './observables.js';
4
-
5
- /**
6
- Union type for form input elements.
7
- */
8
- type inputTypes = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
9
-
10
- /**
11
- Extracts the lowercase tag name of an element (e.g., 'input', 'select', 'textarea').
12
- @param e - The HTML element to extract the tag name from.
13
- @returns The lowercase tag name string.
14
- */
15
- const getInputName = (e: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement): string => {
16
- return e.tagName.toLowerCase();
17
- };
18
-
19
- /**
20
- Abstract base class for form input value handlers.
21
- Defines the interface that all handler classes must implement.
22
- */
23
- abstract class InputBase {
24
- /**
25
- Creates a new InputBase instance.
26
- @param e - The HTML element being handled (input, select, or textarea).
27
- */
28
- constructor(e: inputTypes) {}
29
-
30
- /**
31
- Gets the current value of the form control.
32
- @returns The value in an appropriate type for the control.
33
- */
34
- abstract get val(): any;
35
-
36
- /**
37
- Sets the value of the form control.
38
- @param value - The new value to assign (type depends on control).
39
- */
40
- abstract set val(value: any);
41
- }
42
-
43
- /**
44
- Handler class specifically for radio button groups.
45
- Provides synchronized access to the selected value in a group of related radio buttons.
46
- */
47
- class RadioHandler extends InputBase {
48
- #e: HTMLInputElement;
49
-
50
- /**
51
- Creates a new RadioHandler instance.
52
- @param e - The first radio button element in the group (used to determine name and form scope).
53
- */
54
- constructor(e: HTMLInputElement) {
55
- super(e);
56
- this.#e = e;
57
- }
58
-
59
- /**
60
- Gets the currently selected value of the radio button group using FormData API.
61
- @returns The string value of the selected radio button, or undefined if none is selected.
62
- */
63
- get val(): string {
64
- let ret = new FormData(this.#e.form as HTMLFormElement).get(this.#e.name);
65
-
66
- //console.log('radio[', this.#e.name, '] = "' + ret + '"');
67
- return ret as string;
68
- }
69
-
70
- /**
71
- Sets the selected radio button by value.
72
- Finds and checks the first radio input matching the specified value within the same group.
73
- @param value - The value to match against radio inputs in this group.
74
- */
75
- set val(value: string) {
76
- let arr = [
77
- ...(this.#e.form as HTMLFormElement).querySelectorAll(
78
- `input[type="radio"][name="${this.#e.name}"]`
79
- ),
80
- ] as HTMLInputElement[];
81
-
82
- (arr.filter((e) => e.getAttribute('value') === value).shift() as HTMLInputElement).checked =
83
- true;
84
- }
85
- }
86
-
87
- /**
88
- * Handler class for checkbhox inputs.
89
- * Provides synchronized get/set access to the checked state of a single checkbox.
90
- */
91
- class CheckboxHandler extends InputBase {
92
- #e: HTMLInputElement;
93
-
94
- /**
95
- Creates a new CheckboxHandler instance.
96
- @param e - The checkbox element to handle.
97
- */
98
- constructor(e: HTMLInputElement) {
99
- super(e);
100
- this.#e = e;
101
- }
102
-
103
- /**
104
- Gets the checked state of the checkbox.
105
- @returns True if the checkbox is checked, false otherwise.
106
- */
107
- get val(): boolean {
108
- return this.#e.checked;
109
- }
110
-
111
- /**
112
- Sets the checked state of the checkbox.
113
- @param value - The boolean value to assign as the new checked state.
114
- */
115
- set val(value: boolean) {
116
- this.#e.checked = value;
117
- }
118
- }
119
-
120
- /**
121
- Handler class for standard single-line input fields (text, password, email, etc.).
122
- Provides synchronized get/set access to the value of an input field.
123
- */
124
- class InputHandler extends InputBase {
125
- #e: HTMLInputElement;
126
-
127
- /**
128
- * Creates a new InputHandler instance.
129
- * @param e - The HTML input element to handle.
130
- */
131
- constructor(e: HTMLInputElement) {
132
- super(e);
133
- this.#e = e;
134
- }
135
-
136
- /**
137
- * Gets the current value of the input field.
138
- * @returns The string content of the input element.
139
- */
140
- get val(): string {
141
- return this.#e.value;
142
- }
143
-
144
- /**
145
- * Sets the value of the input field.
146
- * @param value - The new string value for the input element.
147
- */
148
- set val(value: string) {
149
- this.#e.value = value;
150
- }
151
- }
152
-
153
- /**
154
- Handler class for select dropdown elements, supporting both single and multiple selection modes.
155
- Provides synchronized get/set access to selected option values.
156
- */
157
- class SelectHandler extends InputBase {
158
- #e: HTMLSelectElement;
159
- #multi: boolean = false;
160
-
161
- /**
162
- * Creates a new SelectHandler instance.
163
- * @param e - The select element to handle.
164
- */
165
- constructor(e: HTMLSelectElement) {
166
- super(e);
167
- this.#e = e;
168
- this.#multi = e.multiple;
169
- }
170
-
171
- /**
172
- * Gets the currently selected option value(s).
173
- * For single selection, returns a string. For multiple selection, returns an array of strings.
174
- * @returns A string or array of strings representing selected values.
175
- */
176
- get val(): string[] | string {
177
- const ret: string[] = [];
178
-
179
- // get all options
180
- [...this.#e.querySelectorAll('option')]
181
- .filter((o) => o.selected)
182
- .forEach((o) => {
183
- ret.push(o.value);
184
- });
185
-
186
- if (this.#multi) return ret;
187
- return ret.length ? ret[0] : '';
188
- }
189
-
190
- /**
191
- * Sets the selected option(s) by value.
192
- * @param value - A string or array of strings representing values to select.
193
- */
194
- set val(value: string[] | string) {
195
- value = typeof value === 'string' ? [value] : value;
196
-
197
- // set all options
198
- [...this.#e.querySelectorAll('option')].forEach((o) => {
199
- if (value.indexOf(o.value) > -1) {
200
- o.selected = true;
201
- } else {
202
- o.selected = false;
203
- }
204
- });
205
- }
206
- }
207
-
208
- /**
209
- Handler class for textarea elements.
210
- Provides synchronized get/set access to the multi-line text content of a textarea.
211
- */
212
- class TextAreaHandler extends InputBase {
213
- #e: HTMLTextAreaElement;
214
-
215
- /**
216
- * Creates a new TextAreaHandler instance.
217
- * @param e - The textarea element to handle.
218
- */
219
- constructor(e: HTMLTextAreaElement) {
220
- super(e);
221
- this.#e = e;
222
- }
223
-
224
- /**
225
- * Gets the current value of the textarea (defaults to empty string if null/undefined).
226
- * @returns The string content of the textarea element.
227
- */
228
- get val(): string {
229
- return this.#e.value || '';
230
- }
231
-
232
- /**
233
- * Sets the value of the textarea.
234
- * @param value - The new string value for the textarea element.
235
- */
236
- set val(value: string) {
237
- this.#e.value = value;
238
- }
239
- }
240
-
241
- /**
242
- Collects all form controls within a given DOM scope and creates handler objects
243
- that provide synchronized get/set access to their values.
244
-
245
- @param e - Optional root DOM element or shadow root to search within; defaults to document if omitted.
246
- @returns An object where each key is the name attribute of an input, select, or textarea,
247
- and each value is a proxy-like accessor object with `val` getter/setter methods.
248
- */
249
- const getInputHandlers = (e: HTMLElement | ShadowRoot): LooseObject => {
250
- let inputList = [...(e || document).querySelectorAll('input,select,textarea')],
251
- handlers: LooseObject = {};
252
-
253
- inputList.forEach((input) => {
254
- const name: string | null = (input as inputTypes).getAttribute('name');
255
- if (name) {
256
- let handler: any = false;
257
- switch (getInputName(input as inputTypes)) {
258
- case 'input':
259
- switch ((input as HTMLInputElement).type) {
260
- case 'radio':
261
- handler = new RadioHandler(input as HTMLInputElement);
262
- break;
263
- case 'checkbox':
264
- handler = new CheckboxHandler(input as HTMLInputElement);
265
- break;
266
- default:
267
- handler = new InputHandler(input as HTMLInputElement);
268
- }
269
- break;
270
- case 'select':
271
- handler = new SelectHandler(input as HTMLSelectElement);
272
- break;
273
- case 'textarea':
274
- handler = new TextAreaHandler(input as HTMLTextAreaElement);
275
- break;
276
- default:
277
- console.warn('handler missing:', name, 'for', input.tagName.toLowerCase(), input);
278
- break;
279
- }
280
-
281
- if (handler) {
282
- DC.add(input as HTMLElement, name, handler);
283
- Object.defineProperty(handlers, name, {
284
- enumerable: true,
285
- configurable: true,
286
- get: (): any => {
287
- return handler.val;
288
- },
289
- set: (value: any) => {
290
- if (handler.val !== value) {
291
- handler.val = value;
292
- }
293
- },
294
- });
295
- }
296
- }
297
- });
298
-
299
- return handlers;
300
- };
301
-
302
- /**
303
- Attaches a form-values observable to a target object property, allowing two-way binding
304
- between the DOM form controls and the object's state.
305
-
306
- @param targetObject - The object to which the form values will be attached as an observable.
307
- @param propertyName - The name of the property on the target object that will hold the form data.
308
- @param dom - The DOM element or shadow root (typically a form) whose inputs should be observed and bound.
309
-
310
- @example
311
- HTML
312
- ```html
313
- <!-- HTML Form -->
314
- <form id="myForm">
315
- <input type="text" name="username" value="">
316
- <select name="role">
317
- <option value="admin">Admin</option>
318
- <option value="user">User</option>
319
- </select>
320
- <textarea name="bio"></textarea>
321
- <input type="checkbox" name="active">
322
- <input type="radio" name="gender" value="male"> Male
323
- <input type="radio" name="gender" value="female"> Female
324
- </form>
325
- ```
326
- Javascript
327
- ```typescript
328
- import { formValues } from './formValues.js';
329
-
330
- const state = {};
331
-
332
- // Bind the form to state.formData
333
- formValues(state, 'formData', document.getElementById('myForm'));
334
-
335
- // Read values from the form
336
- console.log(state.formData.username); // string
337
- console.log(state.formData.role); // "admin" or "user"
338
- console.log(state.formData.bio); // string
339
- console.log(state.formData.active); // boolean
340
- console.log(state.formData.gender); // "male" or "female"
341
-
342
- // Write values to the form (updates DOM automatically)
343
- state.formData.username = 'john_doe';
344
- state.formData.role = 'user';
345
- state.formData.bio = 'Hello world!';
346
- state.formData.active = true;
347
- state.formData.gender = 'male';
348
- ```
349
- */
350
- export function formValues(
351
- targetObject: LooseObject,
352
- propertyName: string,
353
- dom: HTMLElement | ShadowRoot
354
- ) {
355
- let handlers = getInputHandlers(dom);
356
-
357
- targetObject[propertyName] = Object.assign({}, handlers);
358
-
359
- observe(targetObject, propertyName);
360
-
361
- copyGettersSetters(handlers, targetObject[propertyName]);
362
-
363
- //Object.seal(targetObject[propertyName]); // object structure may not be changed from now on
364
-
365
- [...dom.querySelectorAll('input, textarea, select')].forEach((input) => {
366
- input.addEventListener('input', (ev) => {
367
- //console.log('input event', input, ev);
368
- targetObject[propertyName].notify();
369
- });
370
- });
371
- }
package/dist/helpers.d.ts DELETED
@@ -1,15 +0,0 @@
1
- export declare function isBrowser(): boolean;
2
- export interface LooseObject {
3
- [key: string | symbol]: any;
4
- }
5
- export declare function isNativeObject(value: any): boolean;
6
- export declare function sortObject(o: LooseObject): LooseObject;
7
- export declare function flattenObject(o: LooseObject, doSort?: boolean): LooseObject;
8
- export declare function deepEqual(o1: LooseObject, o2: LooseObject): boolean;
9
- export declare function nameSpace(ns: string | string[], obj?: LooseObject): any;
10
- export declare function parse(what: string | object | any[], parseThis: object): any;
11
- export declare function kebabToPascal(str: string): string;
12
- export declare function debounce(func: Function, milliseconds: number): Function;
13
- export declare function htmlToElements(html: string): HTMLElement[];
14
- export declare function copyGettersSetters(source: LooseObject, target: LooseObject): void;
15
- //# sourceMappingURL=helpers.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAaA,wBAAgB,SAAS,YAExB;AAKD,MAAM,WAAW,WAAW;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,GAAG,CAAC;CAC5B;AAOD,wBAAgB,cAAc,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAElD;AAOD,wBAAgB,UAAU,CAAC,CAAC,EAAE,WAAW,GAAG,WAAW,CAatD;AASD,wBAAgB,aAAa,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,GAAE,OAAc,GAAG,WAAW,CAgBjF;AAQD,wBAAgB,SAAS,CAAC,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,GAAG,OAAO,CAEnE;AAQD,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,GAAG,GAAE,WAAgB,GAAG,GAAG,CAW3E;AAUD,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,GAAG,EAAE,EAAE,SAAS,EAAE,MAAM,OAsCrE;AAQD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,UAKxC;AAUD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,GAAG,QAAQ,CASvE;AAQD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAU1D;AAQD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,QAO1E"}
package/dist/helpers.js DELETED
@@ -1,113 +0,0 @@
1
- export function isBrowser() {
2
- return window ? true : false;
3
- }
4
- export function isNativeObject(value) {
5
- return value && value['constructor'] && value.constructor === Object;
6
- }
7
- export function sortObject(o) {
8
- let result = {}, keys = [...Object.keys(o)].sort();
9
- keys.forEach((key) => {
10
- if (isNativeObject(o[key])) {
11
- result[key] = sortObject(o[key]);
12
- }
13
- else {
14
- result[key] = o[key];
15
- }
16
- });
17
- return result;
18
- }
19
- export function flattenObject(o, doSort = true) {
20
- let result = {};
21
- [...Object.getOwnPropertyNames(o)].forEach((propName) => {
22
- if (o[propName] instanceof Object) {
23
- const temp = flattenObject(o[propName], false);
24
- result[propName] = {};
25
- for (const j in temp) {
26
- result[propName + '.' + j] = temp[j];
27
- }
28
- }
29
- else {
30
- result[propName] = o[propName];
31
- }
32
- });
33
- return doSort ? sortObject(result) : result;
34
- }
35
- export function deepEqual(o1, o2) {
36
- return JSON.stringify(flattenObject(o1)) === JSON.stringify(flattenObject(o2));
37
- }
38
- export function nameSpace(ns, obj = {}) {
39
- let nsa = ns.constructor === String ? ns.split('.') : ns;
40
- if (!obj || !nsa[0] || !obj[nsa[0]]) {
41
- console.warn('ns', nsa[0], 'not in', obj);
42
- return;
43
- }
44
- if (obj[nsa[0]].constructor === Object && nsa.length > 1) {
45
- return nameSpace(nsa.splice(1), obj[nsa[0]]);
46
- }
47
- return obj[nsa[0]];
48
- }
49
- export function parse(what, parseThis) {
50
- var args = Array.from(arguments);
51
- if (args.length > 2) {
52
- while (args.length > 1) {
53
- args[0] = parse(args[0], args[1]);
54
- args.splice(1, 1);
55
- }
56
- return args[0];
57
- }
58
- if (typeof what === 'string') {
59
- what.match(/\{[^\{\}]*\}/g)?.forEach(function (pPropname) {
60
- var propname = pPropname.substring(1, pPropname.length - 1), value = nameSpace(propname, parseThis);
61
- if (typeof value !== 'undefined') {
62
- what = what.replace(pPropname, value);
63
- }
64
- });
65
- }
66
- else if (isNativeObject(what)) {
67
- switch (what.constructor) {
68
- case Object:
69
- Object.keys(what).forEach(function (pKey) {
70
- if (what.hasOwnProperty(pKey)) {
71
- what[pKey] = parse(what[pKey], parseThis);
72
- }
73
- });
74
- break;
75
- case Array:
76
- what.forEach(function (pValue, pKey, original) {
77
- original[pKey] = parse(what[pKey], parseThis);
78
- });
79
- break;
80
- }
81
- }
82
- return what;
83
- }
84
- export function kebabToPascal(str) {
85
- return str
86
- .split('-')
87
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
88
- .join('');
89
- }
90
- export function debounce(func, milliseconds) {
91
- let timeout;
92
- return () => {
93
- clearTimeout(timeout);
94
- timeout = setTimeout(function () {
95
- func(...arguments);
96
- }, milliseconds);
97
- };
98
- }
99
- export function htmlToElements(html) {
100
- let template = document.createElement('template');
101
- html = html.replace(/<([A-Za-z0-9\-]*)([^>\/]*)(\/>)/gi, '<$1$2></$1>');
102
- template.innerHTML = html;
103
- template.content.normalize();
104
- return Array.from(template.content.childNodes);
105
- }
106
- export function copyGettersSetters(source, target) {
107
- const descriptors = Object.getOwnPropertyDescriptors(source);
108
- for (const [key, descriptor] of Object.entries(descriptors)) {
109
- if ('get' in descriptor || 'set' in descriptor) {
110
- Object.defineProperty(target, key, descriptor);
111
- }
112
- }
113
- }