@twobirds/microcomponents 0.0.84 → 0.0.86
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/README.md +22 -6
- package/assets/documentation/MC.md +60 -81
- package/assets/documentation/elements.md +6 -9
- package/assets/documentation/observables.md +97 -11
- package/assets/test/{my-tag.css → myButton.css} +4 -4
- package/assets/test/test.html +1 -1
- package/dist/MC.d.ts +6 -9
- package/dist/MC.d.ts.map +1 -1
- package/dist/MC.js +40 -73
- package/dist/MC.min.js +40 -73
- package/dist/MC.ts +67 -114
- package/dist/elements.min.js +1 -1
- package/dist/formValues.min.js +1 -1
- package/dist/microcomponents.min.js +1 -1
- package/dist/observables.min.js +1 -1
- package/package.json +2 -2
- package/public/MC.d.ts +6 -9
- package/public/MC.d.ts.map +1 -1
- package/public/MC.js +40 -73
- package/public/MC.js.map +1 -1
- package/public/MC.min.js +40 -73
- package/public/MC.ts +67 -114
- package/public/documentation/MC.md +60 -81
- package/public/documentation/elements.md +6 -9
- package/public/documentation/observables.md +97 -11
- package/public/elements.min.js +1 -1
- package/public/formValues.min.js +1 -1
- package/public/microcomponents.min.js +1 -1
- package/public/observables.min.js +1 -1
- package/public/src/MC.d.ts +6 -9
- package/public/src/MC.d.ts.map +1 -1
- package/public/src/MC.js +40 -73
- package/public/src/MC.js.map +1 -1
- package/public/src/MC.ts +67 -114
- package/public/src/test/test/grandparent.js +1 -1
- package/public/src/test/test/testform.js +1 -1
- package/public/src/test/test.js +23 -31
- package/public/src/test/test.js.map +1 -1
- package/public/src/test/test.ts +61 -72
- package/public/test/MC.d.ts +6 -9
- package/public/test/MC.d.ts.map +1 -1
- package/public/test/MC.js +40 -73
- package/public/test/MC.min.js +40 -73
- package/public/test/MC.ts +67 -114
- package/public/test/elements.min.js +1 -1
- package/public/test/formValues.min.js +1 -1
- package/public/test/microcomponents.min.js +1 -1
- package/public/test/{my-tag.css → myButton.css} +4 -4
- package/public/test/observables.min.js +1 -1
- package/public/test/test/grandparent.js +1 -1
- package/public/test/test/testform.js +1 -1
- package/public/test/test.html +1 -1
- package/public/test/test.js +23 -31
- package/public/test/test.js.map +1 -1
- package/public/test/test.ts +61 -72
- package/public/typedoc/assets/navigation.js +1 -1
- package/public/typedoc/assets/search.js +1 -1
- package/public/typedoc/classes/MC.DC.html +3 -4
- package/public/typedoc/classes/MC.MC.html +2 -4
- package/public/typedoc/classes/MC.McBase.html +2 -2
- package/public/typedoc/functions/MC.autoload.html +1 -1
- package/public/typedoc/functions/MC.off.html +1 -1
- package/public/typedoc/functions/MC.on.html +1 -1
- package/public/typedoc/functions/MC.one.html +1 -1
- package/public/typedoc/functions/MC.trigger.html +1 -0
- package/public/typedoc/functions/elements.defineCE.html +1 -1
- package/public/typedoc/functions/elements.getCE.html +1 -1
- package/public/typedoc/functions/formValues.formValues.html +1 -1
- package/public/typedoc/functions/observables.observe.html +1 -1
- package/public/typedoc/index.html +21 -10
- package/public/typedoc/interfaces/MC.HTMLMcElement.html +2 -2
- package/public/typedoc/modules/MC.html +3 -3
- package/public/typedoc/modules/elements.html +1 -1
- package/public/typedoc/modules/formValues.html +1 -1
- package/public/typedoc/modules/observables.html +1 -1
- package/public/typedoc/types/observables.Observable.html +1 -1
- package/src/MC.ts +67 -114
- package/src/test/test.ts +61 -72
package/public/MC.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { LooseObject } from './helpers.js';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
const isBrowser = new Function(
|
|
6
6
|
'try {return this===window;}catch(e){ return false;}'
|
|
7
7
|
);
|
|
8
8
|
|
|
@@ -21,61 +21,17 @@ export interface HTMLMcElement extends HTMLElement {
|
|
|
21
21
|
_mc: LooseObject;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
// get all microcomponents from a list of HTMLElements
|
|
25
|
-
function getMicroComponents(elements: any[], search: string): any[] {
|
|
26
|
-
let mcValues: any[] = [];
|
|
27
|
-
|
|
28
|
-
elements.forEach((e) => {
|
|
29
|
-
if (e) {
|
|
30
|
-
if (e instanceof HTMLElement) {
|
|
31
|
-
const _mc = (e as any)?._mc;
|
|
32
|
-
if (!search && _mc) {
|
|
33
|
-
mcValues = mcValues.concat(Object.values((e as any)._mc));
|
|
34
|
-
} else if (search && _mc && _mc[search]) {
|
|
35
|
-
mcValues.push(_mc[search]);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
return mcValues;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// get all ancestor DOM nodes up to the document root
|
|
44
|
-
function traverseNodes(
|
|
45
|
-
node: { [key: string]: any },
|
|
46
|
-
search: string,
|
|
47
|
-
firstonly = true
|
|
48
|
-
): any[] {
|
|
49
|
-
let mcValues: any[] = [];
|
|
50
|
-
while (node) {
|
|
51
|
-
if (node._mc) {
|
|
52
|
-
if (!search) {
|
|
53
|
-
mcValues = mcValues.concat(Object.values(node._mc));
|
|
54
|
-
} else if (search && node._mc[search]) {
|
|
55
|
-
mcValues.push(node._mc[search]);
|
|
56
|
-
}
|
|
57
|
-
if (firstonly) return mcValues;
|
|
58
|
-
}
|
|
59
|
-
node = node.parentNode;
|
|
60
|
-
}
|
|
61
|
-
return mcValues;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
24
|
function isDOM(element: { [key: string]: any }) {
|
|
65
25
|
return element && element instanceof HTMLElement;
|
|
66
26
|
}
|
|
67
27
|
|
|
68
28
|
// add a microcomponent to a target DOM node
|
|
69
|
-
function add(target: HTMLElement, key: string, element = {}):
|
|
29
|
+
function add( target: HTMLElement, key: string, element: any = {} ): HTMLMcElement {
|
|
70
30
|
let node = target as HTMLMcElement;
|
|
71
31
|
|
|
72
32
|
// create the _mc property if necessary
|
|
73
33
|
if (!node._mc) {
|
|
74
|
-
let debug = (DC as any)?.debug;
|
|
75
|
-
(DC as any).debug = false;
|
|
76
34
|
node._mc = new (DC as any)(target);
|
|
77
|
-
(DC as any).debug = debug;
|
|
78
|
-
delete node._mc._mc; // NOT NEEDED on the top level _mc property of a DOM HTMLElement
|
|
79
35
|
}
|
|
80
36
|
|
|
81
37
|
const _mc = node._mc;
|
|
@@ -88,7 +44,8 @@ function add(target: HTMLElement, key: string, element = {}): any {
|
|
|
88
44
|
target.setAttribute('data-mc', '');
|
|
89
45
|
}
|
|
90
46
|
}
|
|
91
|
-
|
|
47
|
+
|
|
48
|
+
return node;
|
|
92
49
|
}
|
|
93
50
|
|
|
94
51
|
// remove a micro component from the DOM
|
|
@@ -114,27 +71,14 @@ function remove(target: HTMLMcElement, key: string) {
|
|
|
114
71
|
}
|
|
115
72
|
|
|
116
73
|
// run the "init" lifecycle event on the complete _mc structure
|
|
117
|
-
function init(element:
|
|
118
|
-
if (element.constructor === DC) return;
|
|
119
|
-
|
|
74
|
+
function init(element: DC) {
|
|
120
75
|
setTimeout(() => {
|
|
76
|
+
if (element.constructor === DC) return;
|
|
121
77
|
element.trigger('Init');
|
|
122
|
-
/*
|
|
123
|
-
let keys = Object.keys( ( element as any)?._mc );
|
|
124
|
-
if ( keys.length ){
|
|
125
|
-
//console.log( 'inner Inits on [', mc.constructor.name, ']', keys );
|
|
126
|
-
keys?.forEach( (key: string) => {
|
|
127
|
-
let mc = ( element as any)?._mc?.[key];
|
|
128
|
-
//console.log( key, typeof mc?.oneInit, mc );
|
|
129
|
-
if ( ( mc as any )['oneInit'] && typeof ( mc as any ).oneInit == 'function' ){
|
|
130
|
-
( mc as any ).oneInit( new McEvent('Init') );
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
};
|
|
134
|
-
*/
|
|
135
78
|
}, 0);
|
|
136
79
|
}
|
|
137
80
|
|
|
81
|
+
// 'eventName' => '_mcEventName'
|
|
138
82
|
function getMcEventName( eventName: string ): string {
|
|
139
83
|
return '_mc' + eventName[0].toUpperCase() + eventName.substring(1);
|
|
140
84
|
}
|
|
@@ -156,8 +100,8 @@ function one( domNode: Node, eventName: string, handler: EventListenerOrEventLis
|
|
|
156
100
|
|
|
157
101
|
// create and dispatch custom event
|
|
158
102
|
function trigger(
|
|
159
|
-
domNode: HTMLElement,
|
|
160
|
-
event: CustomEvent | string,
|
|
103
|
+
domNode: HTMLElement,
|
|
104
|
+
event: CustomEvent | string,
|
|
161
105
|
data: any = {},
|
|
162
106
|
bubble: string = 'l'
|
|
163
107
|
){
|
|
@@ -199,7 +143,7 @@ function bubbleEvent( domNode: HTMLElement, ev: CustomEvent) {
|
|
|
199
143
|
.map((c: unknown) => (c as any)?.target)
|
|
200
144
|
);
|
|
201
145
|
targets.forEach((target: HTMLMcElement) => {
|
|
202
|
-
if (target)
|
|
146
|
+
if (target) target._mc.trigger(target, ev);
|
|
203
147
|
});
|
|
204
148
|
}
|
|
205
149
|
|
|
@@ -224,41 +168,44 @@ function autoAttachListeners(element: MC | DC) {
|
|
|
224
168
|
withoutOnOne = key.replace(/^on[e]{0,1}/, ''), // preserves upper/lowercase for non-native events
|
|
225
169
|
nativeEventName = withoutOnOne.toLowerCase(); // lowercase for native event names
|
|
226
170
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
on( target, withoutOnOne, listener, once );
|
|
171
|
+
if ( ('on' + nativeEventName) in target ) { // it is a native HTML event
|
|
172
|
+
let listener = (ev: Event) => {
|
|
173
|
+
that[key]( ev );
|
|
174
|
+
}
|
|
175
|
+
// console.log( 'AAL: attach native event listener', nativeEventName, 'to', target );
|
|
176
|
+
target.addEventListener( nativeEventName, listener );
|
|
177
|
+
} else { // it is a customEvent
|
|
178
|
+
// console.log( 'AAL: attach custom event listener', '_mc' + withoutOnOne , 'to key', key, 'in', element );
|
|
179
|
+
let listener = (ev: CustomEvent) => {
|
|
180
|
+
that[key]( ev );
|
|
181
|
+
}
|
|
182
|
+
on( target, withoutOnOne, listener as EventListenerOrEventListenerObject, once );
|
|
239
183
|
}
|
|
240
184
|
});
|
|
241
185
|
}
|
|
242
186
|
}
|
|
243
187
|
|
|
244
|
-
function getMcChildNodes(domNode: HTMLElement):
|
|
188
|
+
function getMcChildNodes(domNode: HTMLElement): DC[] | null {
|
|
245
189
|
const id = Math.random().toString().replace('.', '');
|
|
246
190
|
const selector = '[data-mc]:not([temp_id="' + id + '"] [data-mc] [data-mc])';
|
|
247
191
|
domNode.setAttribute('temp_id', id);
|
|
248
|
-
const
|
|
192
|
+
const mcArray: unknown[] =
|
|
193
|
+
( [ ...domNode.querySelectorAll(selector) ] as HTMLMcElement[])
|
|
194
|
+
.filter( (e) => !!e._mc )
|
|
195
|
+
.map( e => e._mc );
|
|
249
196
|
domNode.removeAttribute('temp_id');
|
|
250
|
-
return
|
|
197
|
+
return mcArray.length ? mcArray as DC[] : null;
|
|
251
198
|
}
|
|
252
199
|
|
|
253
200
|
class McBase {
|
|
254
201
|
private _target: WeakRef<any>; // avoid circular reference
|
|
255
202
|
|
|
256
|
-
constructor(target?: { [key: string]: any }
|
|
203
|
+
constructor(target?: { [key: string]: any } ) {
|
|
257
204
|
let element = this as unknown as MC | DC;
|
|
258
205
|
|
|
259
206
|
this._target = new WeakRef(target || {});
|
|
260
207
|
|
|
261
|
-
init(element); // async lifecycle event
|
|
208
|
+
init(element as DC); // async lifecycle event
|
|
262
209
|
|
|
263
210
|
autoAttachListeners(element);
|
|
264
211
|
}
|
|
@@ -273,12 +220,6 @@ class MC extends McBase {
|
|
|
273
220
|
constructor(target?: any) {
|
|
274
221
|
super(target);
|
|
275
222
|
}
|
|
276
|
-
|
|
277
|
-
static trigger = trigger;
|
|
278
|
-
|
|
279
|
-
trigger(ev: string, data: any = {}, bubble: string = 'l') {
|
|
280
|
-
// TBD
|
|
281
|
-
}
|
|
282
223
|
}
|
|
283
224
|
|
|
284
225
|
class DC extends McBase {
|
|
@@ -288,8 +229,8 @@ class DC extends McBase {
|
|
|
288
229
|
|
|
289
230
|
static add: Function = add;
|
|
290
231
|
static remove: Function = remove;
|
|
291
|
-
static trigger: Function = trigger;
|
|
292
232
|
|
|
233
|
+
/* event handling */
|
|
293
234
|
off( eventName: string, handler: EventListenerOrEventListenerObject ) {
|
|
294
235
|
off( this.target, eventName, handler );
|
|
295
236
|
}
|
|
@@ -302,45 +243,57 @@ class DC extends McBase {
|
|
|
302
243
|
one( this.target, eventName, handler );
|
|
303
244
|
}
|
|
304
245
|
|
|
305
|
-
trigger(
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
}
|
|
309
|
-
//trigger( this.target, ev, data, bubble);
|
|
246
|
+
trigger( eventName: string, data: any = {}, bubble: string = 'l') {
|
|
247
|
+
trigger( this.target, eventName, data, bubble);
|
|
310
248
|
}
|
|
311
249
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
250
|
+
/* DOM traversal */
|
|
251
|
+
parent(): DC | null {
|
|
252
|
+
let
|
|
253
|
+
target: HTMLElement = (this as DC).target;
|
|
254
|
+
if (!isDOM(target)) return null;
|
|
255
|
+
return (target.closest('[data-mc]') as HTMLMcElement)?._mc as DC || null;
|
|
316
256
|
}
|
|
317
257
|
|
|
318
|
-
ancestors(
|
|
319
|
-
let
|
|
320
|
-
|
|
321
|
-
|
|
258
|
+
ancestors(): DC[] | null {
|
|
259
|
+
let
|
|
260
|
+
currentNode: HTMLElement | null = (this as DC)?.target as HTMLMcElement,
|
|
261
|
+
result: DC[] = [];
|
|
262
|
+
|
|
263
|
+
if (!isDOM( currentNode )) return [];
|
|
264
|
+
|
|
265
|
+
while ( currentNode = (currentNode.closest('[data-mc]') ) ){
|
|
266
|
+
if ( (currentNode as HTMLMcElement)?._mc ){
|
|
267
|
+
result.push( (currentNode as HTMLMcElement)?._mc as DC );
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return result.length ? result : null;
|
|
322
272
|
}
|
|
323
273
|
|
|
324
|
-
children(
|
|
274
|
+
children(): DC[] | null {
|
|
325
275
|
let target: any = (this as any)?.target;
|
|
326
|
-
if (!isDOM(target)) return
|
|
327
|
-
|
|
328
|
-
const mcValues = getMicroComponents([...myDomElements], search);
|
|
329
|
-
return mcValues;
|
|
276
|
+
if (!isDOM(target)) return null;
|
|
277
|
+
return getMcChildNodes(target);
|
|
330
278
|
}
|
|
331
279
|
|
|
332
|
-
descendants(
|
|
333
|
-
|
|
280
|
+
descendants(): DC[] | null {
|
|
281
|
+
const target: any = (this as DC)?.target;
|
|
334
282
|
if (!isDOM(target)) return [];
|
|
335
|
-
const
|
|
336
|
-
|
|
283
|
+
const mcArray: unknown[] =
|
|
284
|
+
( [ ...target.querySelectorAll( '[data-mc]' ) ] as HTMLMcElement[])
|
|
285
|
+
.filter( (e) => !!e._mc )
|
|
286
|
+
.map( e => e._mc );
|
|
287
|
+
return mcArray.length ? mcArray as DC[] : null;
|
|
337
288
|
}
|
|
338
289
|
}
|
|
339
290
|
|
|
291
|
+
/*
|
|
292
|
+
Autoload Section...
|
|
293
|
+
*/
|
|
294
|
+
|
|
340
295
|
const loadingDCs = new Set<string>();
|
|
341
296
|
|
|
342
|
-
// this only applies to DCs defined in the data-mc attribute,
|
|
343
|
-
// since they need to be attached manually
|
|
344
297
|
function attachDC(element: string): Function {
|
|
345
298
|
|
|
346
299
|
//@ts-ignore
|
|
@@ -68,27 +68,43 @@ A special property attached to `HTMLElement` instances (when using `DC.add`) or
|
|
|
68
68
|
|
|
69
69
|
Each microcomponent instance has a read-only `target` property providing a back-reference:
|
|
70
70
|
|
|
71
|
-
- If the instance extends **`DC`**
|
|
72
|
-
- If the instance extends **`MC`** (or `DC`) and is nested within another component
|
|
71
|
+
- If the instance extends **`DC`** `.target` refers to the **DOM HTMLElement** it's attached to.
|
|
72
|
+
- If the instance extends **`MC`** (or `DC`) and is nested within another component, `.target` refers to the **parent MC/DC instance**.
|
|
73
73
|
|
|
74
74
|
This reference is managed using `WeakRef` internally by `McBase` to prevent circular references and potential memory leaks.
|
|
75
75
|
|
|
76
76
|
### Event System
|
|
77
77
|
|
|
78
|
-
A
|
|
78
|
+
A CustomEvent system allows components to communicate. These event handlers bubble differently than native events.
|
|
79
|
+
|
|
80
|
+
Any MC method name starting with `on` or `one` followed by an uppercase character will automatically be registered as a custom event handler in the DOM element.
|
|
81
|
+
|
|
82
|
+
The names maps like this:
|
|
83
|
+
**class method** `onHandlerMethod()` => **custom event handler** `_mcHandlerMethod`
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
- **Triggering:**
|
|
87
|
+
|
|
88
|
+
Events are dispatched using the `trigger` method:
|
|
89
|
+
- `myDomElement._mc.trigger('handlerMethod', { ... } )` ... from outside.
|
|
90
|
+
- `this.trigger( 'handlerMethod', data )` ...inside the current MC instance.
|
|
91
|
+
|
|
92
|
+
native:
|
|
93
|
+
- `myDomElement.dispatchEvent('_mcHandlerMethod', { detail: { ... } } )`;
|
|
94
|
+
|
|
79
95
|
|
|
80
|
-
- **Triggering:** Events are dispatched using the `trigger` method:
|
|
81
|
-
- `DC.trigger(targetElement, ...)`: Dispatches to all MCs on a specific DOM element. Handles bubbling (`u`/`d`).
|
|
82
|
-
- `this.trigger(...)`: Dispatches within the current MC instance and its potential nested MCs. Can also initiate bubbling if the instance is a `DC`.
|
|
83
96
|
- **Handling:**
|
|
84
97
|
- Components listen for events by defining methods named `onEventName` or `oneEventName` (for once-off handling).
|
|
85
|
-
- These are automatically detected by `autoAttachListeners
|
|
98
|
+
- These are automatically detected by `autoAttachListeners` internally.
|
|
86
99
|
- Native DOM events matching this pattern on `DC` instances are also automatically bound to the target element.
|
|
87
|
-
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
- **Bubbling:** Events triggered via `dcInstance.trigger` can bubble:
|
|
88
103
|
- `'l'`: Local only (default). Handlers on the target element's/instance's MCs are executed.
|
|
89
104
|
- `'u'`: Upwards. Event propagates to MCs on parent elements (DOM tree).
|
|
90
105
|
- `'d'`: Downwards. Event propagates to MCs on descendant elements (DOM tree).
|
|
91
|
-
-
|
|
106
|
+
- Any combination of the characters 'l','u', 'd' are possible (e.g., `'lu'`, `'ldu'`).
|
|
107
|
+
- Bubbling (`u`/`d`) occurs asynchronously via `setTimeout`.
|
|
92
108
|
|
|
93
109
|
### Observables (Mention)
|
|
94
110
|
|
|
@@ -101,7 +117,7 @@ The `DC` class provides methods (`parent`, `ancestors`, `children`, `descendants
|
|
|
101
117
|
|
|
102
118
|
### Autoloading Concept
|
|
103
119
|
|
|
104
|
-
An optional feature (`autoload()`) that automatically detects specific HTML tags (e.g., `<my-component>`) or elements with an `
|
|
120
|
+
An optional feature (`autoload()`) that automatically detects specific HTML tags (e.g., `<my-component>`) or elements with an `data-mc="my-component"` attribute and attempts to dynamically load the corresponding JavaScript module (e.g., `/my/component.js`).
|
|
105
121
|
|
|
106
122
|
### Important Rule
|
|
107
123
|
|
|
@@ -128,7 +144,6 @@ Base class for non-DOM or server-side components. Extends `McBase`.
|
|
|
128
144
|
|
|
129
145
|
- **Instance Properties**:
|
|
130
146
|
- `.target`: (Inherited from `McBase`) Refers to the parent MC/DC instance it's nested within.
|
|
131
|
-
- `._mc`: An object `{}` to hold nested microcomponent instances.
|
|
132
147
|
- **Instance Methods**:
|
|
133
148
|
- `.trigger(eventName, data)`: (Inherited) Triggers events locally within the instance and its nested `_mc` structure. Does not handle DOM bubbling.
|
|
134
149
|
|
|
@@ -138,26 +153,27 @@ Class for DOM-aware components. Extends `McBase`. **Client-side only.**
|
|
|
138
153
|
|
|
139
154
|
- **Static Methods**:
|
|
140
155
|
- `DC.add(domElement, key, mcInstance)`: Attaches an `mcInstance` (should be a `DC` or `MC`) to a `domElement` under the specified `key`. Creates `domElement._mc` if needed and sets the `_mc` attribute.
|
|
141
|
-
- `DC.remove(domElement, key)`: Removes the MC instance associated with `key` from `domElement._mc`. Removes the `
|
|
156
|
+
- `DC.remove(domElement, key)`: Removes the MC instance associated with `key` from `domElement._mc`. Removes the `data-mc` attribute if the collection becomes empty.
|
|
142
157
|
- `DC.trigger(domElement, eventName, data, bubble)`: Triggers an event on all MCs attached to `domElement`. Handles DOM bubbling (`'l'`, `'u'`, `'d'`).
|
|
143
158
|
- **Instance Properties**:
|
|
144
159
|
- `.target`: (Inherited from `McBase`) Refers to the DOM HTMLElement this `DC` instance is attached to (via `DC.add`).
|
|
145
|
-
- `._mc`: An object `{}` to hold nested microcomponent instances.
|
|
146
160
|
- **Instance Methods**:
|
|
147
|
-
- `.parent(
|
|
148
|
-
- `.ancestors(
|
|
149
|
-
- `.children(
|
|
150
|
-
- `.descendants(
|
|
151
|
-
- `.trigger(eventName, data, bubble)`: Triggers an event on this `DC` instance
|
|
161
|
+
- `.parent()`: Gets MC instances from the closest parent element that has attached MCs.
|
|
162
|
+
- `.ancestors()`: Gets MC instances from all ancestor elements with attached MCs.
|
|
163
|
+
- `.children()`: Gets MC instances from direct child elements with attached MCs.
|
|
164
|
+
- `.descendants()`: Gets MC instances from all descendant elements with attached MCs.
|
|
165
|
+
- `.trigger(eventName, data, bubble)`: Triggers an event on this `DC` instance. Can initiate DOM bubbling (`'l'`, `'u'`, `'d'`).
|
|
152
166
|
|
|
153
167
|
## Examples
|
|
154
168
|
|
|
155
169
|
### Typical Workflow
|
|
156
170
|
|
|
157
|
-
1. Define your component classes extending `MC` or `DC`.
|
|
158
|
-
2. Attach top-level `DC` instances to HTMLElements using `DC.add()`.
|
|
159
|
-
3.
|
|
160
|
-
|
|
171
|
+
1. Define your component classes extending `MC` or `DC`. These contain plain data, observable state, event callbacks and optional internal MCs.
|
|
172
|
+
2. Attach defined top-level `DC` instances to HTMLElements using `DC.add()`.
|
|
173
|
+
3. Then...
|
|
174
|
+
- Select and access other MCs using DOM traversal methods (`ancestors()`, `children()`, etc.)
|
|
175
|
+
- Dynamically attach event handlers (`on`, `one`, `off`).
|
|
176
|
+
- Communicate with other components via Events (`trigger`).
|
|
161
177
|
|
|
162
178
|
### Attach Micro Components (`DC.add`)
|
|
163
179
|
|
|
@@ -168,34 +184,38 @@ const myElement = document.getElementById('myDiv');
|
|
|
168
184
|
|
|
169
185
|
// Define an inner MC (doesn't need DOM access itself)
|
|
170
186
|
class MyInnerClass extends MC {
|
|
187
|
+
|
|
171
188
|
constructor(target) {
|
|
172
189
|
// target will be the parent MyPublicClass instance
|
|
173
190
|
super(target);
|
|
174
191
|
console.log('Inner MC target:', this.target);
|
|
175
192
|
}
|
|
193
|
+
|
|
176
194
|
doSomethingInner() {
|
|
177
195
|
console.log('Inner action!');
|
|
178
196
|
}
|
|
197
|
+
|
|
179
198
|
}
|
|
180
199
|
|
|
181
200
|
// Define an outer DC (needs DOM access or is top-level)
|
|
182
201
|
class MyPublicClass extends DC {
|
|
202
|
+
|
|
183
203
|
constructor(target) {
|
|
184
204
|
// target will be myElement
|
|
185
205
|
super(target);
|
|
186
206
|
console.log('Outer DC target:', this.target);
|
|
187
207
|
// Add an inner MC instance, nested within this DC instance
|
|
188
|
-
this.
|
|
208
|
+
this.myInner = new MyInnerClass(this); // 'this' becomes the target for MyInnerClass
|
|
189
209
|
}
|
|
210
|
+
|
|
190
211
|
doSomethingOuter() {
|
|
191
212
|
console.log('Outer action!');
|
|
192
213
|
}
|
|
193
214
|
|
|
194
|
-
// Example DOM event handler
|
|
195
215
|
onClick(event) {
|
|
196
216
|
console.log('Div clicked!', event);
|
|
197
217
|
// Access nested MC
|
|
198
|
-
this.
|
|
218
|
+
this.myInner.doSomethingInner();
|
|
199
219
|
}
|
|
200
220
|
}
|
|
201
221
|
|
|
@@ -217,12 +237,13 @@ const myElement = document.getElementById('myDiv');
|
|
|
217
237
|
|
|
218
238
|
DC.remove(myElement, 'myComponent');
|
|
219
239
|
|
|
220
|
-
// If myElement._mc is now empty, the '
|
|
240
|
+
// If myElement._mc is now empty, the 'data-mc' attribute is also removed.
|
|
221
241
|
```
|
|
242
|
+
Hint: I have yet to find a usage example for this... so far it simply exists as a counterpart to `DC.add()`.
|
|
222
243
|
|
|
223
244
|
### Retrieve Micro Components
|
|
224
245
|
|
|
225
|
-
Access attached components via the
|
|
246
|
+
Access attached components via the `_mc` property of the target element or parent component.
|
|
226
247
|
|
|
227
248
|
```javascript
|
|
228
249
|
// Get the container object for all MCs on an element
|
|
@@ -246,13 +267,13 @@ const targetElement = document.getElementById('myDiv');
|
|
|
246
267
|
const eventData = { message: 'Hello from outside!' };
|
|
247
268
|
|
|
248
269
|
// Trigger 'customEvent' locally on MCs attached to targetElement
|
|
249
|
-
|
|
270
|
+
trigger(targetElement, 'customEvent', eventData, 'l');
|
|
250
271
|
|
|
251
272
|
// Trigger 'customEvent' and bubble up the DOM tree
|
|
252
|
-
|
|
273
|
+
trigger(targetElement, 'customEvent', eventData, 'u');
|
|
253
274
|
|
|
254
275
|
// Trigger 'customEvent' locally, bubble up, and bubble down
|
|
255
|
-
|
|
276
|
+
trigger(targetElement, 'customEvent', eventData, 'lud');
|
|
256
277
|
```
|
|
257
278
|
|
|
258
279
|
2. Triggering from inside a component instance:
|
|
@@ -267,10 +288,6 @@ class MyTriggeringComponent extends DC {
|
|
|
267
288
|
// Trigger 'internalEvent' only on this instance and its nested _mc components
|
|
268
289
|
this.trigger('internalEvent', eventData); // Default bubble 'l'
|
|
269
290
|
|
|
270
|
-
// Trigger 'externalEvent' on sibling components attached to the same target element
|
|
271
|
-
// (and potentially bubble up/down based on the 'bubble' parameter)
|
|
272
|
-
DC.trigger(this.target, 'externalEvent', eventData, 'l'); // Trigger locally on siblings
|
|
273
|
-
|
|
274
291
|
// Trigger 'downwardEvent' on components attached to descendant elements
|
|
275
292
|
this.trigger('downwardEvent', eventData, 'd'); // Bubbles down from this component's target
|
|
276
293
|
}
|
|
@@ -287,48 +304,15 @@ class MyTriggeringComponent extends DC {
|
|
|
287
304
|
);
|
|
288
305
|
}
|
|
289
306
|
|
|
290
|
-
onExternalEvent(event) {
|
|
291
|
-
console.log(
|
|
292
|
-
`[${this.constructor.name}] received externalEvent:`,
|
|
293
|
-
event.data
|
|
294
|
-
);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
307
|
onDownwardEvent(event) {
|
|
298
308
|
console.log(
|
|
299
|
-
`[${this.constructor.name}] received downwardEvent
|
|
309
|
+
`[${this.constructor.name}] ERROR: received downwardEvent`,
|
|
300
310
|
event.data
|
|
301
311
|
);
|
|
302
312
|
}
|
|
303
313
|
}
|
|
304
314
|
```
|
|
305
315
|
|
|
306
|
-
### Retrieving Inner Micro Components
|
|
307
|
-
|
|
308
|
-
Access nested components directly via the parent's `_mc` property.
|
|
309
|
-
|
|
310
|
-
```javascript
|
|
311
|
-
class Inner extends MC {
|
|
312
|
-
constructor(target) {
|
|
313
|
-
super(target);
|
|
314
|
-
}
|
|
315
|
-
someInnerMethod() {
|
|
316
|
-
console.log('Inner method called!');
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
class Outer extends DC {
|
|
321
|
-
constructor(target) {
|
|
322
|
-
super(target);
|
|
323
|
-
this._mc.inner = new Inner(this);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
accessInner() {
|
|
327
|
-
this._mc?.inner.someInnerMethod();
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
```
|
|
331
|
-
|
|
332
316
|
### Navigating Parent/Ancestor MCs
|
|
333
317
|
|
|
334
318
|
Use .parent() and .ancestors() (available on DC instances) to find components attached to elements higher up the DOM tree.
|
|
@@ -337,7 +321,7 @@ These methods skip elements that don't have any MCs attached.
|
|
|
337
321
|
|
|
338
322
|
```javascript
|
|
339
323
|
// Inside a method of a DC instance:
|
|
340
|
-
const parentMcs = this.parent(); // Array of MC instances on the
|
|
324
|
+
const parentMcs = this.parent(); // Array of MC instances on the closest parent element that contains MCs
|
|
341
325
|
console.log(parentMcs);
|
|
342
326
|
|
|
343
327
|
const allAncestorMcs = this.ancestors(); // Array of all MC instances on all ancestor elements with MCs
|
|
@@ -394,9 +378,9 @@ class McWithHandlers extends DC {
|
|
|
394
378
|
}
|
|
395
379
|
|
|
396
380
|
// This is a custom event handler, not a native DOM event.
|
|
397
|
-
// It will be triggered by this.trigger('CustomAction')
|
|
381
|
+
// It will be triggered by this.trigger('CustomAction')
|
|
398
382
|
onCustomAction(mcEvent) {
|
|
399
|
-
console.log('Custom action triggered!', mcEvent.data);
|
|
383
|
+
console.log('Custom action triggered!', mcEvent.data, mcEvent.bubble);
|
|
400
384
|
}
|
|
401
385
|
}
|
|
402
386
|
|
|
@@ -433,7 +417,7 @@ class MyLifecycleComponent extends DC {
|
|
|
433
417
|
}
|
|
434
418
|
|
|
435
419
|
oneInit(event) {
|
|
436
|
-
//
|
|
420
|
+
// called asynchronous after the constructor finishes.
|
|
437
421
|
console.log('MyLifecycleComponent oneInit called!', event);
|
|
438
422
|
// Safe to access this.target or setup things that depend on the instance being fully constructed.
|
|
439
423
|
}
|
|
@@ -475,7 +459,7 @@ The necessary code is fetched and executed only when these components actually a
|
|
|
475
459
|
**./test/mymc.ts**
|
|
476
460
|
```typescript
|
|
477
461
|
'use strict';
|
|
478
|
-
import { DC
|
|
462
|
+
import { DC } from '../microcomponents.js';
|
|
479
463
|
|
|
480
464
|
export default class myMC extends DC {
|
|
481
465
|
constructor(target: HTMLElement) {
|
|
@@ -483,7 +467,7 @@ export default class myMC extends DC {
|
|
|
483
467
|
}
|
|
484
468
|
|
|
485
469
|
oneInit() {
|
|
486
|
-
|
|
470
|
+
console.log('testmc connected to DOM');
|
|
487
471
|
}
|
|
488
472
|
}
|
|
489
473
|
```
|
|
@@ -502,13 +486,8 @@ defineCE(
|
|
|
502
486
|
constructor(target: HTMLElement) {
|
|
503
487
|
super(target);
|
|
504
488
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
delete that._mc; // no inner classes needed
|
|
508
|
-
|
|
509
|
-
let shadow = (that as DC).target.attachShadow({ mode: 'open' });
|
|
510
|
-
shadow.innerHTML = `<style>${that.css}</style>${that.template}`;
|
|
511
|
-
|
|
489
|
+
let shadow = (this as DC).target.attachShadow({ mode: 'open' });
|
|
490
|
+
shadow.innerHTML = `<style>${this.css}</style>${this.template}`;
|
|
512
491
|
}
|
|
513
492
|
|
|
514
493
|
get template() {
|
|
@@ -50,15 +50,15 @@ document.body.append( myTag );
|
|
|
50
50
|
After:
|
|
51
51
|
```html
|
|
52
52
|
<body>
|
|
53
|
-
<my-tag
|
|
53
|
+
<my-tag data-mc>Hello World</my-tag>
|
|
54
54
|
</body>
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
**Hints**
|
|
58
58
|
- Your code extends the **[DC](https://gitlab.com/twoBirds/microcomponents/-/blob/main/assets/documentation/MicroComponents.md?ref_type=heads#dc-class-extends-mcbase)** superclass. This superclass provides methods to traverse the DOM and select other micro components contained in HTML elements.
|
|
59
59
|
- it is essential to understand that your web component code is only one of the numerous micro components that can reside inside `<my-tag>`
|
|
60
|
-
- After you created a custom element, the custom element will contain a **property
|
|
61
|
-
- Also the custom element then has an **attribute
|
|
60
|
+
- After you created a custom element, the custom element will contain a **property `_mc`** which contains your class instance.
|
|
61
|
+
- Also the custom element then has an **attribute `data-mc`** which will indicate that at least one micro component is attached to it.
|
|
62
62
|
- Note that you can hand over data natively via parameters, when handling custom elements in above way.
|
|
63
63
|
- The native way of usage, simply adding the tag to the template, is still possible. You have to use attributes then to hand in data.
|
|
64
64
|
|
|
@@ -98,7 +98,7 @@ defineCE(
|
|
|
98
98
|
['my-attribute'] // DOM node attributes to observe
|
|
99
99
|
);
|
|
100
100
|
|
|
101
|
-
let MyTag = getCE("my-tag"), //
|
|
101
|
+
let MyTag = getCE("my-tag"), // customElementRegistry.get("my-tag")
|
|
102
102
|
myTag = new MyTag(); // programmatically create a custom element instance
|
|
103
103
|
|
|
104
104
|
document.body.append( myTag );
|
|
@@ -147,7 +147,7 @@ document.body.append( new MyTag() );
|
|
|
147
147
|
**Hints**
|
|
148
148
|
- You can add as many other micro components to the DOM node as you like.
|
|
149
149
|
- The native web component lifecycle events will be triggered on **all** micro components.
|
|
150
|
-
- The event parameter handed over is an
|
|
150
|
+
- The event parameter handed over is an CustomEvent instance, that contains the original native event in its `detail` property.
|
|
151
151
|
|
|
152
152
|
## Native HTMLElement Event Handlers
|
|
153
153
|
|
|
@@ -182,7 +182,6 @@ document.body.append( new MyTag() );
|
|
|
182
182
|
|
|
183
183
|
**Hints**
|
|
184
184
|
- The native web component lifecycle events will be triggered on **all** micro components contained in the custom element, provided the MC contains a matching named handler.
|
|
185
|
-
- The event parameter handed over is an internal event instance, that contains the original native event in its `data` property.
|
|
186
185
|
|
|
187
186
|
# Lifecycle Event Handlers
|
|
188
187
|
|
|
@@ -216,7 +215,6 @@ document.body.append( new MyTag() );
|
|
|
216
215
|
|
|
217
216
|
**Hints**
|
|
218
217
|
- The native web component lifecycle events will be triggered on **all** micro components contained in the custom element, provided the specific micro component contains a matching named handler.
|
|
219
|
-
- The event parameter handed over is an internal event instance that contains no data.
|
|
220
218
|
|
|
221
219
|
# Custom Events
|
|
222
220
|
|
|
@@ -255,8 +253,7 @@ Alternatives:
|
|
|
255
253
|
|
|
256
254
|
document
|
|
257
255
|
.querySelector( 'my-tag' )
|
|
258
|
-
|
|
259
|
-
.trigger('specialEvent'); // ...on all micro components contained in <my-tag>
|
|
256
|
+
.dispatchEvent('specialEvent'); // ...on all micro components contained in <my-tag>
|
|
260
257
|
|
|
261
258
|
/*
|
|
262
259
|
traversing the DOM:
|