@twobirds/microcomponents 0.0.34 → 0.0.39

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 (120) hide show
  1. package/README.md +40 -27
  2. package/assets/documentation/{MicroComponents.md → MC.md} +17 -17
  3. package/assets/documentation/comparision.md +0 -0
  4. package/assets/documentation/elements.md +276 -0
  5. package/assets/documentation/magic.md +10 -68
  6. package/assets/my-tag.css +6 -2
  7. package/dist/MC.d.ts +3 -4
  8. package/dist/MC.d.ts.map +1 -1
  9. package/dist/MC.min.js +89 -66
  10. package/dist/MC.ts +213 -178
  11. package/dist/autoloader.ts +1 -1
  12. package/dist/{customElements.d.ts → elements.d.ts} +1 -1
  13. package/dist/elements.d.ts.map +1 -0
  14. package/dist/elements.min.js +1 -0
  15. package/dist/{customElements.ts → elements.ts} +23 -14
  16. package/dist/helpers.d.ts +6 -4
  17. package/dist/helpers.d.ts.map +1 -1
  18. package/dist/helpers.ts +95 -128
  19. package/dist/microcomponents.d.ts +3 -3
  20. package/dist/microcomponents.d.ts.map +1 -1
  21. package/dist/microcomponents.min.js +1 -1
  22. package/dist/microcomponents.ts +3 -3
  23. package/dist/{customObservables.d.ts → observables.d.ts} +3 -3
  24. package/dist/observables.d.ts.map +1 -0
  25. package/dist/observables.min.js +1 -0
  26. package/dist/observables.ts +353 -0
  27. package/dist/{customSelectors.d.ts → selectors.d.ts} +1 -1
  28. package/dist/selectors.d.ts.map +1 -0
  29. package/dist/selectors.min.js +1 -0
  30. package/dist/test.ts +135 -156
  31. package/klemmstein.png +0 -0
  32. package/package.json +52 -52
  33. package/public/MC.js +90 -67
  34. package/public/MC.js.map +1 -1
  35. package/public/MC.min.js +89 -66
  36. package/public/assets/search.js +1 -1
  37. package/public/autoloader.js +1 -1
  38. package/public/autoloader.js.map +1 -1
  39. package/public/classes/{DC.html → MC.DC.html} +60 -59
  40. package/public/classes/{MC.html → MC.MC.html} +41 -35
  41. package/public/classes/{McBase.html → MC.McBase.html} +28 -20
  42. package/public/classes/{McEvent.html → MC.McEvent.html} +48 -42
  43. package/public/documentation/{MicroComponents.md → MC.md} +17 -17
  44. package/public/documentation/comparision.md +0 -0
  45. package/public/documentation/elements.md +276 -0
  46. package/public/documentation/magic.md +10 -68
  47. package/public/{customElements.js → elements.js} +9 -10
  48. package/public/elements.js.map +1 -0
  49. package/public/elements.min.js +1 -0
  50. package/public/functions/{addListener.html → MC.addListener.html} +18 -12
  51. package/public/functions/{removeListener.html → MC.removeListener.html} +18 -12
  52. package/public/functions/elements.defineCE.html +78 -0
  53. package/public/functions/elements.getCE.html +63 -0
  54. package/public/functions/formValues.formValues.html +68 -0
  55. package/public/functions/observables.observe.html +65 -0
  56. package/public/functions/selectors._d.html +69 -0
  57. package/public/functions/selectors._t.html +69 -0
  58. package/public/helpers.js +66 -89
  59. package/public/helpers.js.map +1 -1
  60. package/public/index.html +48 -33
  61. package/public/interfaces/{HTMLMcElement.html → MC.HTMLMcElement.html} +601 -595
  62. package/public/interfaces/selectors.DSelector.html +1013 -0
  63. package/public/interfaces/selectors.TSelector.html +712 -0
  64. package/public/microcomponents.js +3 -3
  65. package/public/microcomponents.js.map +1 -1
  66. package/public/microcomponents.min.js +1 -1
  67. package/public/modules/MC.html +78 -0
  68. package/public/modules/elements.html +62 -0
  69. package/public/modules/formValues.html +60 -0
  70. package/public/modules/observables.html +65 -0
  71. package/public/modules/selectors.html +76 -0
  72. package/public/modules.html +11 -23
  73. package/public/my-tag.css +6 -2
  74. package/public/observables.js +259 -0
  75. package/public/observables.js.map +1 -0
  76. package/public/observables.min.js +1 -0
  77. package/public/{customSelectors.js → selectors.js} +1 -1
  78. package/public/selectors.js.map +1 -0
  79. package/public/selectors.min.js +1 -0
  80. package/public/src/MC.ts +413 -0
  81. package/public/src/McEvent.ts +96 -0
  82. package/public/src/autoloader.ts +98 -0
  83. package/{src/customElements.ts → public/src/elements.ts} +23 -14
  84. package/public/src/formValues.ts +275 -0
  85. package/public/src/helpers.ts +140 -0
  86. package/public/src/microcomponents.ts +8 -0
  87. package/public/src/observables.ts +353 -0
  88. package/public/src/test.ts +190 -0
  89. package/public/test.js +93 -118
  90. package/public/test.js.map +1 -1
  91. package/public/types/observables.Observable.html +91 -0
  92. package/public/types/selectors.selection.html +58 -0
  93. package/public/types/selectors.singleSelector.html +58 -0
  94. package/src/MC.ts +213 -178
  95. package/src/autoloader.ts +1 -1
  96. package/src/elements.ts +76 -0
  97. package/src/helpers.ts +95 -128
  98. package/src/microcomponents.ts +3 -3
  99. package/src/observables.ts +353 -0
  100. package/src/selectors.ts +1634 -0
  101. package/src/test.ts +135 -156
  102. package/typedoc.json +1 -1
  103. package/webpack.config.js +9 -9
  104. package/dist/customElements.d.ts.map +0 -1
  105. package/dist/customElements.min.js +0 -1
  106. package/dist/customObservables.d.ts.map +0 -1
  107. package/dist/customObservables.min.js +0 -1
  108. package/dist/customObservables.ts +0 -348
  109. package/dist/customSelectors.d.ts.map +0 -1
  110. package/dist/customSelectors.min.js +0 -1
  111. package/public/customElements.js.map +0 -1
  112. package/public/customElements.min.js +0 -1
  113. package/public/customObservables.js +0 -256
  114. package/public/customObservables.js.map +0 -1
  115. package/public/customObservables.min.js +0 -1
  116. package/public/customSelectors.js.map +0 -1
  117. package/public/customSelectors.min.js +0 -1
  118. package/src/customObservables.ts +0 -348
  119. /package/dist/{customSelectors.ts → selectors.ts} +0 -0
  120. /package/{src/customSelectors.ts → public/src/selectors.ts} +0 -0
@@ -0,0 +1,353 @@
1
+ 'use strict';
2
+
3
+ import {
4
+ LooseObject,
5
+ parse,
6
+ debounce,
7
+ deepEqual,
8
+ nameSpace,
9
+ flattenObject,
10
+ } from './helpers.js';
11
+
12
+ import { DC, HTMLMcElement, McEvent } from './MC.js';
13
+
14
+ function isObservableObject(value: any): boolean {
15
+ return value != null && typeof value === 'object';
16
+ }
17
+
18
+ type Callback = (value: any) => void;
19
+
20
+ function getPlaceholders(target: HTMLElement): string[] {
21
+ let placeholders: Set<string> = new Set(),
22
+ regEx = /\{[^\{\}]*\}/g;
23
+
24
+ // scan attributes
25
+ target
26
+ .getAttributeNames()
27
+ .filter((attr) => target.getAttribute(attr)?.match(regEx)) // extract placeholder(s)
28
+ .forEach((attr) => {
29
+ let phs: Set<string> = new Set(target.getAttribute(attr)?.match(regEx));
30
+ phs.forEach((placeholder) =>
31
+ placeholders.add(placeholder.replace(/[\{\}]/g, ''))
32
+ );
33
+ });
34
+
35
+ // scan children text nodes
36
+ [...target.childNodes]
37
+ .filter((childNode) => {
38
+ return (
39
+ // is text node and has placeholders
40
+ childNode.nodeType === 3 &&
41
+ ((childNode as any).nodeValue || '').match(regEx)
42
+ );
43
+ })
44
+ .forEach((childNode) => {
45
+ ((childNode as any).nodeValue || '')
46
+ .match(regEx)
47
+ .forEach((placeholder: string) => {
48
+ placeholders.add(placeholder.replace(/[\{\}]/g, ''));
49
+ });
50
+ });
51
+
52
+ // extract {}
53
+ let result: string[] = [...placeholders].map((placeholder) =>
54
+ placeholder.replace(/[\{\}]/g, '')
55
+ );
56
+
57
+ //console.log('getPlaceholders()', target, result);
58
+
59
+ return [...placeholders];
60
+ }
61
+
62
+ class Placeholders extends DC {
63
+ data: LooseObject = {};
64
+ placeholders: string[] = [];
65
+
66
+ constructor(target: HTMLElement) {
67
+ super(target);
68
+ let that = this;
69
+ getPlaceholders(target).forEach((property) => (that.data[property] = ''));
70
+ observe(that);
71
+ that.#attachCallbacks();
72
+ }
73
+
74
+ #attachCallbacks() {
75
+ let that = this,
76
+ target = that.target;
77
+
78
+ // console.log('attachCallbacks', that.target);
79
+
80
+ // scan attributes
81
+ target
82
+ .getAttributeNames()
83
+ .filter((attr: string) =>
84
+ target.getAttribute(attr)?.match(/\{[^\{\}]*\}/g)
85
+ ) // extract placeholder(s)
86
+ .forEach((attr: string) => {
87
+ let phs: Set<string> = new Set(
88
+ target.getAttribute(attr)?.match(/\{[^\{\}]*\}/g)
89
+ );
90
+ // attach callback
91
+ that.data.observe(
92
+ ((template: string) => (data: LooseObject) => {
93
+ let result: string = template;
94
+ phs.forEach((placeholder) => {
95
+ result = result.replace(
96
+ placeholder,
97
+ data[placeholder.replace(/[\{\}]/g, '')]
98
+ );
99
+ });
100
+ target.setAttribute(attr, result);
101
+ //console.log('cb setAttribute()', data, template, attr, result);
102
+ })(target.getAttribute(attr))
103
+ );
104
+ });
105
+
106
+ // scan children text nodes
107
+ [...target.childNodes]
108
+ .filter((childNode) => {
109
+ return (
110
+ // is text node and has placeholders
111
+ childNode.nodeType === 3 &&
112
+ ((childNode as any).nodeValue || '').match(/\{[^\{\}]*\}/g)
113
+ );
114
+ })
115
+ .forEach((childNode) => {
116
+ let phs: Set<string> = new Set(
117
+ childNode.nodeValue.match(/\{[^\{\}]*\}/g)
118
+ );
119
+
120
+ // attach callback
121
+ that.data.observe(
122
+ ((template: string) => (data: LooseObject) => {
123
+ let result: string = template;
124
+ phs.forEach((placeholder) => {
125
+ result = result.replace(
126
+ placeholder,
127
+ data[placeholder.replace(/[\{\}]/g, '')]
128
+ );
129
+ });
130
+ //console.log('cb nodeValue', phs, data, template, childNode, result);
131
+ childNode.nodeValue = result;
132
+ })(childNode.nodeValue)
133
+ );
134
+ });
135
+ }
136
+
137
+ onData(ev: McEvent) {
138
+ let that = this,
139
+ data: LooseObject = {};
140
+ Object.keys(ev.data).forEach((key) => {
141
+ if (that.data.hasOwnProperty(key)) data[key] = ev.data[key];
142
+ });
143
+ // console.log('onData()', ev, data, that.data);
144
+ if (JSON.stringify(data) !== JSON.stringify(that.data))
145
+ Object.assign(that.data, data); // will trigger callbacks
146
+ }
147
+ }
148
+
149
+ function getPlaceholderElements(
150
+ target: HTMLElement | DocumentFragment | ShadowRoot
151
+ ): HTMLElement[] {
152
+ let elements: Array<HTMLElement | DocumentFragment | ShadowRoot> = [target]
153
+ .concat([...(target.querySelectorAll('*') as unknown as HTMLElement[])])
154
+ .filter((e) => !!getPlaceholders(e as HTMLElement).length);
155
+ return elements as HTMLElement[];
156
+ }
157
+
158
+ function makePlaceHolders(
159
+ element: HTMLElement | DocumentFragment | ShadowRoot
160
+ ): void {
161
+ getPlaceholderElements(element).forEach((e: HTMLElement) => {
162
+ if ((e as HTMLMcElement)?._mc?._placeholders) return;
163
+ DC.add(e, '_placeholders', new Placeholders(e));
164
+ });
165
+ }
166
+
167
+ export type Observable = {
168
+ observe: (cb: Callback) => void;
169
+ notify: () => void;
170
+ bind: (target: HTMLElement | DocumentFragment | ShadowRoot) => void;
171
+ };
172
+
173
+ export const observe = (target: LooseObject, propertyName?: string) => {
174
+ let keys = !propertyName
175
+ ? [...Object.getOwnPropertyNames(target)]
176
+ : [propertyName];
177
+
178
+ keys.forEach((key) => {
179
+ // console.log( 'co propname', key, typeof target[key], target[key] );
180
+ if (key[0] === '_' || target?.[key] === undefined) return;
181
+ let val: any = target[key],
182
+ Constructor: any;
183
+
184
+ if (!val.prototype) {
185
+ switch (typeof val) {
186
+ case 'string':
187
+ Constructor = String;
188
+ break;
189
+ case 'number':
190
+ Constructor = Number;
191
+ break;
192
+ case 'boolean':
193
+ Constructor = Boolean;
194
+ break;
195
+ case 'object':
196
+ if (val instanceof Array) {
197
+ Constructor = Array;
198
+ } else {
199
+ Constructor = Object;
200
+ }
201
+ break;
202
+ default:
203
+ return;
204
+ }
205
+ }
206
+
207
+ let Observable = class extends Constructor {
208
+ constructor(val: any) {
209
+ super(val);
210
+ (this.constructor as LooseObject).callbacks = [];
211
+ if (isObservableObject(val)) {
212
+ // you cannot bind native values to the dom
213
+ (this.constructor as LooseObject).bound = [];
214
+ Object.getOwnPropertyNames(val).forEach((key) => {
215
+ this[key] = val[key];
216
+ });
217
+ }
218
+ }
219
+
220
+ observe(f: Function) {
221
+ //console.log('observe');
222
+ (this.constructor as any).callbacks.push(f);
223
+ return this;
224
+ }
225
+
226
+ bind(target: HTMLElement | DocumentFragment | ShadowRoot) {
227
+ const that = this,
228
+ iso = isObservableObject(that.valueOf());
229
+ //console.log('bind', iso, target);
230
+
231
+ if (iso) {
232
+ makePlaceHolders(target);
233
+ (this.constructor as any).callbacks.push((data: LooseObject) => {
234
+ DC.trigger(target, 'data', flattenObject(data), 'ld');
235
+ });
236
+ that.notify();
237
+ } else {
238
+ console.warn(
239
+ 'IGNORED - cannot bind a non-Object to the dom, IS:',
240
+ typeof that.valueOf(),
241
+ that
242
+ );
243
+ }
244
+
245
+ return this;
246
+ }
247
+
248
+ notify() {
249
+ let that = this,
250
+ data =
251
+ isObservableObject(that.valueOf()) &&
252
+ that.valueOf() instanceof Array === false
253
+ ? structuredClone(Object.assign({}, that))
254
+ : that.valueOf();
255
+ // console.log('notify', that.constructor, (that.constructor as any).callbacks.length, (that.constructor as any)?.bound?.length, data );
256
+ (that.constructor as any).callbacks.forEach((f: Function) => {
257
+ // console.log( 'callback', f, data);
258
+ f(data);
259
+ });
260
+ if (isObservableObject(that.valueOf())) {
261
+ // console.log('bound', this.constructor);
262
+ (that.constructor as any).bound.forEach((f: Function) => {
263
+ // console.log( 'bound', f, data);
264
+ f(data);
265
+ });
266
+ }
267
+ return this;
268
+ }
269
+ };
270
+
271
+ //(Observable.constructor as LooseObject).callbacks = [];
272
+ let o = new Observable(val);
273
+
274
+ Object.defineProperty(target, key, {
275
+ enumerable: true,
276
+ get() {
277
+ if (isObservableObject(o.valueOf())) {
278
+ let check = structuredClone(Object.assign({}, o));
279
+ setTimeout(
280
+ debounce(function checkChanges() {
281
+ let state = structuredClone(Object.assign({}, o));
282
+ //console.log( 'check changes', state, check );
283
+ if (!deepEqual(state, check)) {
284
+ //console.log( '-> not equal!' );
285
+ o.notify();
286
+ }
287
+ }, 0),
288
+ 0
289
+ );
290
+ }
291
+ return o;
292
+ },
293
+ set(val: any) {
294
+ //console.log( 'SET:', isObservableObject( val.valueOf() ), 'set:', val, 'to', isObservableObject( o.valueOf() ), o, );
295
+ if (typeof val === typeof o.valueOf()) {
296
+ let iso =
297
+ isObservableObject(o.valueOf()) &&
298
+ o.valueOf() instanceof Array === false;
299
+ //let iso = o.constructor.prototype.__proto__.constructor === Object;
300
+ //console.log('target iso:', iso);
301
+ if (iso) {
302
+ let old = structuredClone(Object.assign({}, o));
303
+ val = structuredClone(Object.assign({}, val));
304
+
305
+ let hasFormHandler =
306
+ (o.constructor as LooseObject)?.hasFormHandler ?? false,
307
+ callbacks = (o.constructor as LooseObject).callbacks,
308
+ bound = (o.constructor as LooseObject).bound;
309
+ o = new Observable(structuredClone(Object.assign({}, val)));
310
+
311
+ //console.log('after set o=', o, 'check', old);
312
+ (o.constructor as LooseObject).callbacks = hasFormHandler;
313
+ (o.constructor as LooseObject).callbacks = callbacks;
314
+ (o.constructor as LooseObject).bound = bound;
315
+
316
+ setTimeout(
317
+ // check for changes
318
+ debounce(function checkChanges() {
319
+ let state = structuredClone(Object.assign({}, o));
320
+ //console.log( 'check changes', state, check );
321
+ if (!deepEqual(state, old)) {
322
+ //console.log( '-> not equal!' );
323
+ o.notify();
324
+ }
325
+ }, 0),
326
+ 0
327
+ );
328
+ } else {
329
+ // simple native var
330
+ let old = o.valueOf(),
331
+ callbacks = (o.constructor as LooseObject).callbacks;
332
+ o = new Observable(val);
333
+ (o.constructor as LooseObject).callbacks = callbacks;
334
+ if (val !== old) {
335
+ o.notify();
336
+ }
337
+ }
338
+ } else {
339
+ console.warn(
340
+ 'IGNORED - cannot change observable types, IS:',
341
+ typeof o.valueOf(),
342
+ o,
343
+ '- CANNOT BECOME:',
344
+ typeof val
345
+ );
346
+ }
347
+ },
348
+ });
349
+
350
+ // replace old property with proxy (observable)
351
+ target[key] = val;
352
+ });
353
+ };
@@ -0,0 +1,190 @@
1
+ 'use strict';
2
+
3
+ import {
4
+ LooseObject,
5
+ MC,
6
+ DC,
7
+ McEvent,
8
+ defineCE,
9
+ getCE,
10
+ observe,
11
+ Observable,
12
+ } from './microcomponents.js';
13
+
14
+ //import { MC, DC, McEvent } from "./MC.js";
15
+ //import { defineCE, getCE } from "./elements.js";
16
+ //import { observe } from "./observables.js";
17
+
18
+ //MC.debug = true;
19
+
20
+ class Translator extends DC {
21
+ #translations: LooseObject = {};
22
+
23
+ Language: unknown = 'en';
24
+ Translation: unknown = {};
25
+
26
+ constructor(
27
+ target: HTMLElement | ShadowRoot | DocumentFragment,
28
+ translations: LooseObject,
29
+ translationTargetObject: HTMLElement
30
+ ) {
31
+ super(target as HTMLElement);
32
+ let that = this; // minifier help
33
+
34
+ // state
35
+ that.Language = 'en';
36
+ that.Translation = {};
37
+
38
+ observe(that);
39
+
40
+ // setup
41
+ that.#translations = translations;
42
+
43
+ // state
44
+ (that.Language as Observable).observe((lang) => {
45
+ if (that.#translations.hasOwnProperty(lang)) {
46
+ that.Translation = { lang: that.#translations[lang] }; // set translation
47
+ }
48
+ });
49
+
50
+ (that.Translation as Observable).bind(translationTargetObject);
51
+
52
+ this.target.setAttribute('language', 'en');
53
+
54
+ (this.Language as Observable).notify();
55
+ }
56
+
57
+ oneInit() {
58
+ //(this.Language as Observable).notify(); // trigger translation
59
+ }
60
+
61
+ onAttributeChanged(ev: McEvent) {
62
+ if (!ev.data.newValue) return;
63
+
64
+ let that = this;
65
+
66
+ switch (ev.data.name) {
67
+ case 'language':
68
+ that.Language = ev.data.newValue;
69
+ break;
70
+ }
71
+ }
72
+ }
73
+
74
+ defineCE(
75
+ // equivalent to custom element registry .define()
76
+ 'my-tag',
77
+ class MyTag extends DC {
78
+ #template: string = '<button>{lang.click} {counter}</button>';
79
+
80
+ #languages = {
81
+ en: {
82
+ click: 'click me',
83
+ },
84
+ de: {
85
+ click: 'klick mich',
86
+ },
87
+ };
88
+
89
+ Dom: LooseObject = {};
90
+
91
+ constructor(target: HTMLElement) {
92
+ super(target);
93
+
94
+ let that = this; // minifier help
95
+
96
+ // hide
97
+ target.setAttribute('style', 'display:none');
98
+
99
+ // render
100
+ target.innerHTML = this.#template;
101
+
102
+ // add public translator
103
+ DC.add(
104
+ target,
105
+ 'translator',
106
+ new Translator(target, that.#languages, target)
107
+ );
108
+ }
109
+
110
+ oneInit(ev: McEvent) {
111
+ let that = this;
112
+
113
+ // state
114
+ that.Dom = { counter: 0 };
115
+
116
+ // state: convert all props defined so far into observables
117
+ observe(that);
118
+
119
+ // bind Dom to target
120
+ (that.Dom as Observable).bind(that.target);
121
+
122
+ // show
123
+ setTimeout(() => that.target.removeAttribute('style'), 100);
124
+ }
125
+
126
+ onAttributeChanged(ev: McEvent) {
127
+ //console.log(ev);
128
+ this.target._mc.translator.Language = ev.data.newValue;
129
+ }
130
+
131
+ onClick(ev: McEvent) {
132
+ this.Dom.counter++;
133
+ (this.target as HTMLElement).classList.add('clicked');
134
+ }
135
+ },
136
+ ['language']
137
+ );
138
+
139
+ let MyTag: CustomElementConstructor = getCE('my-tag')!,
140
+ fragment = new DocumentFragment();
141
+
142
+ /*
143
+ stress test
144
+ */
145
+
146
+ let start = new Date().getTime();
147
+
148
+ // create 100 elements
149
+ for (let x = 0; x < 100; x++) {
150
+ fragment.append(new MyTag());
151
+ }
152
+
153
+ let end = new Date().getTime();
154
+
155
+ // and put them into the DOM
156
+ document.body.appendChild(fragment);
157
+
158
+ //document.querySelectorAll('my-tag').forEach((e) => e.remove());
159
+
160
+ /*
161
+ console test
162
+ */
163
+
164
+ setTimeout(() => {
165
+ console.clear();
166
+
167
+ console.log(
168
+ '%c \n\nStress Test:\n\n %cInstancing 100 web components: %c %s ms.',
169
+ 'font-weight: bold; color: black;',
170
+ 'font-weight: normal;',
171
+ 'font-weight: bold; color: blue;',
172
+ end - start
173
+ );
174
+
175
+ document.body.appendChild(new MyTag());
176
+
177
+ let element = document.querySelector('my-tag');
178
+
179
+ console.log(
180
+ "\n\n%cInspect this tag:%c \n( ...to see the inner structure in the '._mc' property )\n\n %O %c %s",
181
+ 'font-weight: bold;',
182
+ 'font-weight: normal;',
183
+ element,
184
+ 'font-weight: bold;',
185
+ '\n\nLook up code in the debugger: test.js\n\n'
186
+ );
187
+
188
+ // will trigger the event on all micro components contained in <my-tag>
189
+ DC.trigger(element, 'doSomething');
190
+ }, 500);