@twobirds/microcomponents 0.0.33 → 0.0.37

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