@twobirds/microcomponents 0.0.37 → 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.
package/src/MC.ts CHANGED
@@ -141,15 +141,33 @@ function remove(target: HTMLMcElement, key: string) {
141
141
  }
142
142
 
143
143
  // trigger an event
144
- function trigger(target: unknown, ev: McEvent | string, data: any = {}) {
144
+ function trigger(
145
+ target: HTMLElement,
146
+ ev: McEvent | string,
147
+ data: any = {},
148
+ bubble: string = 'l'
149
+ ) {
145
150
  let _mc = (target as any)?._mc;
146
151
  if (!_mc) return;
152
+
153
+ let event =
154
+ ev instanceof McEvent
155
+ ? new McEvent(ev.type, ev.data, 'l')
156
+ : new McEvent(ev, data, 'l'),
157
+ mcEvent: McEvent =
158
+ typeof ev !== 'string' ? (ev as McEvent) : new McEvent(ev, data, bubble);
159
+
147
160
  [...Object.values(_mc)].forEach((mc: any) => {
148
161
  //console.log( 'mc [', mc.constructor.name, ']', mc.constructor.prototype?.oneInit );
149
162
  if ((mc as any)?.trigger && typeof (mc as any)?.trigger === 'function') {
150
- (mc as any)?.trigger(ev, data);
163
+ (mc as any)?.trigger(event);
151
164
  }
152
165
  });
166
+
167
+ if (/[ud]/.test(mcEvent.bubble)) {
168
+ bubbleEvent(target, mcEvent);
169
+ }
170
+
153
171
  return;
154
172
  }
155
173
 
@@ -217,6 +235,37 @@ function autoAttachListeners(element: MC | DC) {
217
235
  }
218
236
  }
219
237
 
238
+ function bubbleEvent(target: HTMLElement, ev: McEvent) {
239
+ // at this point, either 'u' or 'd' bubbling should be indicated
240
+ // mcEvent bubbling is asynchronous, and the order of target MC execution in case of multiple matches is not guaranteed!
241
+ if (/[ud]/.test(ev.bubble)) {
242
+ setTimeout(() => {
243
+ // bubbling must include 'l' for local, or else no handlers are executed'
244
+ if (ev.bubble.indexOf('l') === -1) {
245
+ ev.bubble += 'l';
246
+ }
247
+
248
+ // bubble up towards root
249
+ if (ev.bubble.indexOf('u') > -1) {
250
+ let parent = traverseNodes(target, '', true)?.[0].target;
251
+ if (parent) DC.trigger(parent, ev);
252
+ }
253
+
254
+ // bubble down
255
+ if (ev.bubble.indexOf('d') > -1) {
256
+ let targets = new Set<HTMLMcElement>(
257
+ (target as HTMLMcElement)._mc
258
+ .children()
259
+ .map((c: unknown) => (c as any)?.target)
260
+ );
261
+ targets.forEach((target: HTMLMcElement) => {
262
+ if (target) DC.trigger(target, ev);
263
+ });
264
+ }
265
+ }, 0);
266
+ }
267
+ }
268
+
220
269
  class McBase {
221
270
  #target: WeakRef<any>; // avoid circular reference
222
271
 
@@ -245,22 +294,34 @@ class MC extends McBase {
245
294
 
246
295
  static trigger = trigger;
247
296
 
248
- trigger(ev: McEvent | string, data: any = {}, bubble?: string) {
297
+ trigger(ev: McEvent | string, data: any = {}, bubble: string = 'l') {
249
298
  let that = this;
250
299
 
251
300
  //console.log( that, '.trigger(', ev, data, bubble, ')' );
252
- bubble = bubble || 'l';
253
301
 
254
302
  // run all triggers
255
303
  if (that.constructor === MC || that.constructor === DC) {
304
+ let event =
305
+ ev instanceof McEvent
306
+ ? new McEvent(ev.type, ev.data, 'l')
307
+ : new McEvent(ev, data, 'l'),
308
+ mcEvent: McEvent =
309
+ typeof ev !== 'string'
310
+ ? (ev as McEvent)
311
+ : new McEvent(ev, data, bubble);
256
312
  [...Object.getOwnPropertyNames(that)].forEach((mc) => {
257
313
  if (
258
314
  (that as any)[mc]?.trigger &&
259
315
  typeof (that as any)[mc]?.trigger === 'function'
260
316
  ) {
261
- (that as any)[mc]?.trigger(ev, data, bubble);
317
+ (that as any)[mc]?.trigger(event);
262
318
  }
263
319
  });
320
+
321
+ if (/[ud]/.test(mcEvent.bubble)) {
322
+ bubbleEvent(that.target, mcEvent);
323
+ }
324
+
264
325
  return; // must return here!
265
326
  }
266
327
 
@@ -294,33 +355,9 @@ class MC extends McBase {
294
355
  return that;
295
356
  }
296
357
 
297
- // at this point, either 'u' or 'd' bubbling should be indicated
298
- // mcEvent bubbling is asynchronous, and the order of target MC execution in case of multiple matches is not guaranteed!
299
- if (
300
- isBrowser() &&
301
- /[ud]/.test(mcEvent.bubble) &&
302
- (that as any) instanceof MC
303
- ) {
304
- setTimeout(() => {
305
- // bubbling must include 'l' for local, or else no handlers are executed'
306
- if (mcEvent.bubble.indexOf('l') === -1) {
307
- mcEvent.bubble += 'l';
308
- }
309
-
310
- // bubble up towards root
311
- if (mcEvent.bubble.indexOf('u') > -1) {
312
- (that as any).parent().forEach((mc: any) => {
313
- mc instanceof MC ? (mc as any).trigger(mcEvent) : null;
314
- });
315
- }
316
-
317
- // bubble down
318
- if (mcEvent.bubble.indexOf('d') > -1) {
319
- (that as any).children().forEach((mc: any) => {
320
- mc instanceof MC ? (mc as any).trigger(mcEvent) : null;
321
- });
322
- }
323
- }, 0);
358
+ //console.log('bubble', mcEvent);
359
+ if (/[ud]/.test(mcEvent.bubble)) {
360
+ bubbleEvent(that.target, mcEvent);
324
361
  }
325
362
  }
326
363
  }
@@ -18,16 +18,15 @@ function isObservableObject(value: any): boolean {
18
18
  type Callback = (value: any) => void;
19
19
 
20
20
  function getPlaceholders(target: HTMLElement): string[] {
21
- let placeholders: Set<string> = new Set();
21
+ let placeholders: Set<string> = new Set(),
22
+ regEx = /\{[^\{\}]*\}/g;
22
23
 
23
24
  // scan attributes
24
25
  target
25
26
  .getAttributeNames()
26
- .filter((attr) => target.getAttribute(attr)?.match(/\{[^\{\}]*\}/g)) // extract placeholder(s)
27
+ .filter((attr) => target.getAttribute(attr)?.match(regEx)) // extract placeholder(s)
27
28
  .forEach((attr) => {
28
- let phs: Set<string> = new Set(
29
- target.getAttribute(attr)?.match(/\{[^\{\}]*\}/g)
30
- );
29
+ let phs: Set<string> = new Set(target.getAttribute(attr)?.match(regEx));
31
30
  phs.forEach((placeholder) =>
32
31
  placeholders.add(placeholder.replace(/[\{\}]/g, ''))
33
32
  );
@@ -39,12 +38,12 @@ function getPlaceholders(target: HTMLElement): string[] {
39
38
  return (
40
39
  // is text node and has placeholders
41
40
  childNode.nodeType === 3 &&
42
- ((childNode as any).nodeValue || '').match(/\{[^\{\}]*\}/g)
41
+ ((childNode as any).nodeValue || '').match(regEx)
43
42
  );
44
43
  })
45
44
  .forEach((childNode) => {
46
45
  ((childNode as any).nodeValue || '')
47
- .match(/\{[^\{\}]*\}/g)
46
+ .match(regEx)
48
47
  .forEach((placeholder: string) => {
49
48
  placeholders.add(placeholder.replace(/[\{\}]/g, ''));
50
49
  });
@@ -159,9 +158,10 @@ function getPlaceholderElements(
159
158
  function makePlaceHolders(
160
159
  element: HTMLElement | DocumentFragment | ShadowRoot
161
160
  ): void {
162
- getPlaceholderElements(element).forEach((e) =>
163
- DC.add(e, '_placeholders', new Placeholders(e))
164
- );
161
+ getPlaceholderElements(element).forEach((e: HTMLElement) => {
162
+ if ((e as HTMLMcElement)?._mc?._placeholders) return;
163
+ DC.add(e, '_placeholders', new Placeholders(e));
164
+ });
165
165
  }
166
166
 
167
167
  export type Observable = {
@@ -225,8 +225,7 @@ export const observe = (target: LooseObject, propertyName?: string) => {
225
225
 
226
226
  bind(target: HTMLElement | DocumentFragment | ShadowRoot) {
227
227
  const that = this,
228
- iso = isObservableObject(that.valueOf()),
229
- bound = (that.constructor as any).bound;
228
+ iso = isObservableObject(that.valueOf());
230
229
  //console.log('bind', iso, target);
231
230
 
232
231
  if (iso) {
package/src/test.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  import {
4
- HTMLMcElement,
5
4
  LooseObject,
6
5
  MC,
7
6
  DC,
@@ -48,13 +47,15 @@ class Translator extends DC {
48
47
  }
49
48
  });
50
49
 
51
- (that.Translation as Observable).bind(target);
50
+ (that.Translation as Observable).bind(translationTargetObject);
52
51
 
53
52
  this.target.setAttribute('language', 'en');
53
+
54
+ (this.Language as Observable).notify();
54
55
  }
55
56
 
56
57
  oneInit() {
57
- (this.Language as Observable).notify(); // trigger translation
58
+ //(this.Language as Observable).notify(); // trigger translation
58
59
  }
59
60
 
60
61
  onAttributeChanged(ev: McEvent) {
@@ -74,7 +75,7 @@ defineCE(
74
75
  // equivalent to custom element registry .define()
75
76
  'my-tag',
76
77
  class MyTag extends DC {
77
- #template: string = '{lang.click} {counter}';
78
+ #template: string = '<button>{lang.click} {counter}</button>';
78
79
 
79
80
  #languages = {
80
81
  en: {
@@ -92,6 +93,9 @@ defineCE(
92
93
 
93
94
  let that = this; // minifier help
94
95
 
96
+ // hide
97
+ target.setAttribute('style', 'display:none');
98
+
95
99
  // render
96
100
  target.innerHTML = this.#template;
97
101
 
@@ -114,6 +118,9 @@ defineCE(
114
118
 
115
119
  // bind Dom to target
116
120
  (that.Dom as Observable).bind(that.target);
121
+
122
+ // show
123
+ setTimeout(() => that.target.removeAttribute('style'), 100);
117
124
  }
118
125
 
119
126
  onAttributeChanged(ev: McEvent) {
@@ -123,6 +130,7 @@ defineCE(
123
130
 
124
131
  onClick(ev: McEvent) {
125
132
  this.Dom.counter++;
133
+ (this.target as HTMLElement).classList.add('clicked');
126
134
  }
127
135
  },
128
136
  ['language']
@@ -135,13 +143,11 @@ let MyTag: CustomElementConstructor = getCE('my-tag')!,
135
143
  stress test
136
144
  */
137
145
 
138
- /*
139
146
  let start = new Date().getTime();
140
147
 
141
- // create 1000 elements
142
- for (let x = 0; x < 1000; x++) {
143
- let mt = new MyTag('my-data');
144
- fragment.append(mt);
148
+ // create 100 elements
149
+ for (let x = 0; x < 100; x++) {
150
+ fragment.append(new MyTag());
145
151
  }
146
152
 
147
153
  let end = new Date().getTime();
@@ -149,8 +155,7 @@ let end = new Date().getTime();
149
155
  // and put them into the DOM
150
156
  document.body.appendChild(fragment);
151
157
 
152
- document.querySelectorAll('my-tag').forEach((e) => e.remove());
153
- */
158
+ //document.querySelectorAll('my-tag').forEach((e) => e.remove());
154
159
 
155
160
  /*
156
161
  console test
@@ -159,15 +164,13 @@ console test
159
164
  setTimeout(() => {
160
165
  console.clear();
161
166
 
162
- /*
163
167
  console.log(
164
- '%c \n\nStress Test:\n\n %cInstancing 1000 web components: %c %s ms.',
168
+ '%c \n\nStress Test:\n\n %cInstancing 100 web components: %c %s ms.',
165
169
  'font-weight: bold; color: black;',
166
170
  'font-weight: normal;',
167
171
  'font-weight: bold; color: blue;',
168
- 0
172
+ end - start
169
173
  );
170
- */
171
174
 
172
175
  document.body.appendChild(new MyTag());
173
176
 
@@ -184,4 +187,4 @@ setTimeout(() => {
184
187
 
185
188
  // will trigger the event on all micro components contained in <my-tag>
186
189
  DC.trigger(element, 'doSomething');
187
- }, 20);
190
+ }, 500);