@twobirds/microcomponents 0.0.1 → 0.0.3
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/.gitlab-ci.yml +16 -0
- package/MicroComponentProposal_draft.md +10 -7
- package/README.md +10 -6
- package/assets/fiddle/MicroComponents.svg +15 -0
- package/assets/fiddle/fiddle.css +0 -0
- package/assets/fiddle/fiddle.js +0 -0
- package/assets/fiddle/index.html +0 -0
- package/assets/index.html +11 -0
- package/dist/microcomponents.d.ts +8 -3
- package/dist/microcomponents.d.ts.map +1 -1
- package/dist/microcomponents.js +87 -47
- package/dist/microcomponents.min.js +23 -16
- package/dist/microcomponents.ts +212 -0
- package/package.json +27 -6
- package/public/docs/assets/search.js +1 -0
- package/{docs → public/docs}/classes/microcomponents.MC.html +73 -31
- package/{docs → public/docs}/classes/microcomponents_es3.MC.html +16 -16
- package/{docs → public/docs}/functions/microcomponents_es3.__isEmpty.html +5 -5
- package/{docs → public/docs}/index.html +17 -12
- package/{docs → public/docs}/interfaces/microcomponents.HTMLElementMc.html +6 -6
- package/{docs → public/docs}/modules/microcomponents.html +5 -5
- package/{docs → public/docs}/modules/microcomponents_es3.html +4 -4
- package/{docs → public/docs}/modules.html +5 -5
- package/public/fiddle/MicroComponents.svg +15 -0
- package/public/fiddle/fiddle.css +0 -0
- package/public/fiddle/fiddle.js +0 -0
- package/public/fiddle/index.html +0 -0
- package/public/index.html +11 -0
- package/src/microcomponents.ts +97 -54
- package/typedoc.json +3 -3
- package/docs/assets/search.js +0 -1
- /package/{docs → public/docs}/.nojekyll +0 -0
- /package/{docs → public/docs}/assets/highlight.css +0 -0
- /package/{docs → public/docs}/assets/main.js +0 -0
- /package/{docs → public/docs}/assets/style.css +0 -0
package/.gitlab-ci.yml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# The Docker image that will be used to build your app
|
|
2
|
+
image: node:lts
|
|
3
|
+
# Functions that should be executed before the build script is run
|
|
4
|
+
before_script:
|
|
5
|
+
- npm i
|
|
6
|
+
pages:
|
|
7
|
+
script:
|
|
8
|
+
- npm run build
|
|
9
|
+
artifacts:
|
|
10
|
+
paths:
|
|
11
|
+
# The folder that contains the files to be exposed at the Page URL
|
|
12
|
+
- public
|
|
13
|
+
rules:
|
|
14
|
+
# This ensures that only pushes to the default branch will trigger
|
|
15
|
+
# a pages deploy
|
|
16
|
+
- if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
|
|
@@ -13,15 +13,18 @@ Microcomponents provide a way to encapsulate functionality and state within a ta
|
|
|
13
13
|
**Proposed API:**
|
|
14
14
|
|
|
15
15
|
- **Static Methods:**
|
|
16
|
-
- `MC.add(
|
|
17
|
-
- `MC.remove(target, key)`: Detach a micro component.
|
|
16
|
+
- `MC.add( taarget, key, element)`: Attach a top level micro component to an HTMLElement.
|
|
17
|
+
- `MC.remove( target, key )`: Detach a top level micro component from an HTMLElement.
|
|
18
|
+
- `MC.siblings( target )`: get top level micro components from an HTMLElement.
|
|
18
19
|
|
|
19
20
|
- **Instance Methods:**
|
|
20
|
-
- `
|
|
21
|
-
- `
|
|
22
|
-
- `
|
|
23
|
-
- `
|
|
24
|
-
- `
|
|
21
|
+
- `add( key, element )`: Attach a private Micro Component to a Micro Component.
|
|
22
|
+
- `remove( key )`: Detach a private Micro Component from a Micro Component.
|
|
23
|
+
- `siblings(search?)`: Retrieve sibling micro components.
|
|
24
|
+
- `parent(search?)`: Get parent top level micro components.
|
|
25
|
+
- `parents(search?)`: Get all ancestor top level micro components.
|
|
26
|
+
- `children(search?)`: Retrieve child top level micro components.
|
|
27
|
+
- `descendants(search?)`: Get all descendant top level micro components.
|
|
25
28
|
|
|
26
29
|
**Use Cases:**
|
|
27
30
|
1. Modular web applications.
|
package/README.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
## MICRO COMPONENTS
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|
|
|
5
5
|
This code aims at **improving Javascript modularity**. It does not describe a specific Micro Component itself but rather describes they way they are **organized**. A typical example of a **Micro Component** can be found in "additional concepts" below.
|
|
6
6
|
|
|
7
|
-
Using micro components enforces **DRY, KISS and SOLID** principles in Javascript programming. Read this [RFC](
|
|
7
|
+
Using micro components enforces **DRY, KISS and SOLID** principles in Javascript programming. Read this [RFC](https://gitlab.com/twoBirds/microcomponents/-/blob/main/MicroComponentProposal_draft.md?ref_type=heads) for the shortest info.
|
|
8
8
|
|
|
9
|
-
This is a low-level approach. It consists of a single class with
|
|
9
|
+
This is a low-level approach. It consists of a single class with 3 static methods and 5 selector methods, and weights in at ~2 kB. Also there is an ES3 version to help e.g. [hbbtv](https://en.wikipedia.org/wiki/Hybrid_Broadcast_Broadband_TV) modular JS coding, or any other device that relies on ES3.
|
|
10
10
|
|
|
11
11
|
For an overview, continue reading.
|
|
12
12
|
|
|
13
|
-
For a detailed functional description goto [Micro Component Usage](https://gitlab.com/twoBirds/microcomponents/-/blob/main/microcomponents_usage.md?ref_type=heads).
|
|
13
|
+
For a detailed functional description goto [Micro Component Usage](https://gitlab.com/twoBirds/microcomponents/-/blob/main/microcomponents_usage.md?ref_type=heads) and check the [typedoc API documentation](http://twobirds.gitlab.io/microcomponents/docs/modules.html).
|
|
14
14
|
|
|
15
15
|
To see what can be done, go to [additional concepts](https://gitlab.com/twoBirds/microcomponents/-/blob/main/microcomponents_magic.md?ref_type=heads) which delve a little deeper into how to use it, and what it already has been used for.
|
|
16
16
|
|
|
@@ -59,7 +59,7 @@ before:
|
|
|
59
59
|
|
|
60
60
|
code: (assume "Button" is some class in a library)
|
|
61
61
|
```javascript
|
|
62
|
-
import 'MC' from 'twobirds/microcomponents';
|
|
62
|
+
import 'MC' from '@twobirds/microcomponents';
|
|
63
63
|
import 'Button' from 'tb/ui'; // made-up example library
|
|
64
64
|
|
|
65
65
|
// select first button element in DOM
|
|
@@ -93,7 +93,11 @@ This functionality has been used in very big and complicated data processing pro
|
|
|
93
93
|
- to represent business processes and their tasks
|
|
94
94
|
- and lately to compose very complex native web components from distinct modular functionality.
|
|
95
95
|
|
|
96
|
-
I decided extract this from the [twoBirds framework](https://www.npmjs.com/package/twobirds-core), since it is framework agnostic and relies solely on the standard DOM and native javascript or typescript.
|
|
96
|
+
I decided to extract this functionality from the [twoBirds framework](https://www.npmjs.com/package/twobirds-core), since it is framework agnostic, and relies solely on the standard DOM and native javascript or typescript, and is a useful tool in itself.
|
|
97
|
+
|
|
98
|
+
## Useful Links
|
|
99
|
+
|
|
100
|
+
[API documentation](http://twobirds.gitlab.io/microcomponents/docs/modules.html)
|
|
97
101
|
|
|
98
102
|
## Contact
|
|
99
103
|
Feel free to contact me for any questions or inquiries:
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
|
2
|
+
<!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
|
|
3
|
+
<title>test</title>
|
|
4
|
+
<rect fill="transparent" x="0" y="0" width="200" height="200" fill-opacity="1"/>
|
|
5
|
+
<g style="fill:transparent">
|
|
6
|
+
<title>first bird</title>
|
|
7
|
+
<path fill="#18829c" d="
|
|
8
|
+
m15,160
|
|
9
|
+
a30,75 45 1 0 160,-150
|
|
10
|
+
a20,50 225 1 1 -110,100
|
|
11
|
+
a12,25 45 1 1 60,-50
|
|
12
|
+
a20,50 225 1 0 -110,100
|
|
13
|
+
" />
|
|
14
|
+
</g>
|
|
15
|
+
</svg>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Micro Components</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<a href="http://twobirds.gitlab.io/microcomponents/docs/modules.html" target="_blank">API</a>
|
|
10
|
+
</body>
|
|
11
|
+
</html>
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
export declare class MC {
|
|
2
2
|
private target;
|
|
3
3
|
private privateMCs;
|
|
4
|
+
_mc?: {
|
|
5
|
+
[key: string]: any;
|
|
6
|
+
};
|
|
4
7
|
static debug: boolean;
|
|
5
8
|
constructor(target?: HTMLElement | null);
|
|
6
|
-
static
|
|
7
|
-
add(
|
|
9
|
+
static siblings(target: HTMLElement, search?: string): any[];
|
|
10
|
+
static add(target: HTMLElement, key: string, element?: any): any[];
|
|
8
11
|
static remove(target: HTMLElement, key: string): void;
|
|
9
|
-
remove(key: string): void;
|
|
10
12
|
private getMicroComponents;
|
|
11
13
|
private traverseNodes;
|
|
14
|
+
private isDOM;
|
|
15
|
+
add(key?: string, element?: any): any;
|
|
16
|
+
remove(key: string): void;
|
|
12
17
|
siblings(search?: string): any[];
|
|
13
18
|
parent(search?: string): any[];
|
|
14
19
|
parents(search?: string): any[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"microcomponents.d.ts","sourceRoot":"","sources":["../src/microcomponents.ts"],"names":[],"mappings":"AACA,qBAAa,EAAE;IAEd,OAAO,CAAC,MAAM,CAAqB;IAEnC,OAAO,CAAC,UAAU,CAA8B;IAEhD,MAAM,CAAC,KAAK,EAAE,OAAO,CAAS;gBAElB,MAAM,GAAE,WAAW,GAAG,IAAW;
|
|
1
|
+
{"version":3,"file":"microcomponents.d.ts","sourceRoot":"","sources":["../src/microcomponents.ts"],"names":[],"mappings":"AACA,qBAAa,EAAE;IAEd,OAAO,CAAC,MAAM,CAAqB;IAEnC,OAAO,CAAC,UAAU,CAA8B;IAEhD,GAAG,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAM;IAElC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAS;gBAElB,MAAM,GAAE,WAAW,GAAG,IAAW;IAS7C,MAAM,CAAC,QAAQ,CAAE,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,MAAM,GAAI,GAAG,EAAE;IAQ9D,MAAM,CAAC,GAAG,CAAE,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,GAAQ,GAAI,GAAG,EAAE;IAqBxE,MAAM,CAAC,MAAM,CAAE,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAI,IAAI;IA8BvD,OAAO,CAAC,kBAAkB;IA0B1B,OAAO,CAAC,aAAa;IAmBrB,OAAO,CAAC,KAAK;IASb,GAAG,CAAE,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,GAAQ,GAAI,GAAG;IAU3C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAczB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,GAAG,EAAE;IAchC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,GAAG,EAAE;IAQ9B,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,GAAG,EAAE;IAQ/B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,GAAG,EAAE;IAahC,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,GAAG,EAAE;CAOnC;AAED,MAAM,WAAW,aAAc,SAAQ,WAAW;IACjD,GAAG,EAAE,EAAE,CAAC;CACR"}
|
package/dist/microcomponents.js
CHANGED
|
@@ -2,46 +2,52 @@
|
|
|
2
2
|
export class MC {
|
|
3
3
|
target;
|
|
4
4
|
privateMCs = {};
|
|
5
|
+
_mc = {};
|
|
5
6
|
static debug = false;
|
|
6
7
|
constructor(target = null) {
|
|
7
8
|
this.target = target;
|
|
8
9
|
this.privateMCs = {};
|
|
10
|
+
if (MC?.debug) {
|
|
11
|
+
this._mc = this.privateMCs;
|
|
12
|
+
}
|
|
13
|
+
;
|
|
14
|
+
}
|
|
15
|
+
// return all top-level micro components from an HTMLElement
|
|
16
|
+
static siblings(target, search) {
|
|
17
|
+
let t = target;
|
|
18
|
+
if (!t?._mc)
|
|
19
|
+
return [];
|
|
20
|
+
let mc = t._mc;
|
|
21
|
+
return search ? mc[search] ? [mc[search]] : [] : [...Object.entries(t._mc)];
|
|
9
22
|
}
|
|
10
23
|
// add a microcomponent to a target DOM node
|
|
11
|
-
static add(element = {}
|
|
24
|
+
static add(target, key, element = {}) {
|
|
12
25
|
const mc = target;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
mc._mc = new MC();
|
|
17
|
-
}
|
|
18
|
-
// add the element with the given property key to the _mc property object
|
|
19
|
-
if (key && !mc._mc[key]) {
|
|
20
|
-
mc._mc[key] = element;
|
|
21
|
-
if (target) {
|
|
22
|
-
target.setAttribute('_mc', '');
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return element;
|
|
27
|
-
}
|
|
28
|
-
// add a microcomponent to the DOM or to the private microcomponent collection
|
|
29
|
-
add(element = {}, key, target) {
|
|
30
|
-
// add the element
|
|
31
|
-
if (target && key) {
|
|
32
|
-
// ...to the DOM
|
|
33
|
-
MC.add(element, key, target);
|
|
26
|
+
// create the _mc property if necessary
|
|
27
|
+
if (!mc._mc) {
|
|
28
|
+
mc._mc = new MC();
|
|
34
29
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
// add the element with the given property key to the _mc property object
|
|
31
|
+
if (key && !mc._mc[key]) {
|
|
32
|
+
mc._mc[key] = element;
|
|
33
|
+
if (target) {
|
|
34
|
+
target.setAttribute('_mc', '');
|
|
35
|
+
}
|
|
38
36
|
}
|
|
39
37
|
return element;
|
|
40
38
|
}
|
|
41
|
-
// remove a micro component from the DOM
|
|
39
|
+
// remove a micro component from the target DOM node
|
|
42
40
|
static remove(target, key) {
|
|
41
|
+
if ( //silent fail
|
|
42
|
+
!key
|
|
43
|
+
|| typeof key !== 'string'
|
|
44
|
+
|| !target
|
|
45
|
+
|| target instanceof HTMLElement === false) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
;
|
|
43
49
|
const mc = target;
|
|
44
|
-
// delete the property from the
|
|
50
|
+
// delete the property from the HTMLElement _mc collection
|
|
45
51
|
if (mc._mc && mc._mc[key]) {
|
|
46
52
|
delete mc._mc[key];
|
|
47
53
|
}
|
|
@@ -51,16 +57,6 @@ export class MC {
|
|
|
51
57
|
}
|
|
52
58
|
return;
|
|
53
59
|
}
|
|
54
|
-
// remove a micro component from the DOM or the private microcomponent collection
|
|
55
|
-
remove(key) {
|
|
56
|
-
// remove the component from the private collection
|
|
57
|
-
if (key in this.privateMCs) {
|
|
58
|
-
delete this.privateMCs[key];
|
|
59
|
-
}
|
|
60
|
-
if (MC.debug && this.target) {
|
|
61
|
-
MC.remove(this.target, key);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
60
|
/*
|
|
65
61
|
helper methods
|
|
66
62
|
*/
|
|
@@ -69,11 +65,21 @@ export class MC {
|
|
|
69
65
|
let mcValues = [];
|
|
70
66
|
elements.forEach(e => {
|
|
71
67
|
if (e) {
|
|
72
|
-
if (
|
|
73
|
-
|
|
68
|
+
if (e instanceof HTMLElement) {
|
|
69
|
+
if (!search && e._mc) {
|
|
70
|
+
mcValues = mcValues.concat(Object.values(e._mc));
|
|
71
|
+
}
|
|
72
|
+
else if (search && e._mc && e._mc[search]) {
|
|
73
|
+
mcValues.push(e._mc[search]);
|
|
74
|
+
}
|
|
74
75
|
}
|
|
75
|
-
else if (
|
|
76
|
-
|
|
76
|
+
else if (e instanceof MC) {
|
|
77
|
+
if (!search) {
|
|
78
|
+
mcValues = mcValues.concat(Object.values(this.privateMCs));
|
|
79
|
+
}
|
|
80
|
+
else if (search && this.privateMCs[search]) {
|
|
81
|
+
mcValues.push(e._mc[search]);
|
|
82
|
+
}
|
|
77
83
|
}
|
|
78
84
|
}
|
|
79
85
|
});
|
|
@@ -97,27 +103,59 @@ export class MC {
|
|
|
97
103
|
}
|
|
98
104
|
return mcValues;
|
|
99
105
|
}
|
|
106
|
+
isDOM(element) {
|
|
107
|
+
return element && element instanceof HTMLElement;
|
|
108
|
+
}
|
|
109
|
+
/*
|
|
110
|
+
Methods to handle private MCs
|
|
111
|
+
*/
|
|
112
|
+
// add a microcomponent to the private microcomponent collection
|
|
113
|
+
add(key, element = {}) {
|
|
114
|
+
// add the element
|
|
115
|
+
if (key) {
|
|
116
|
+
this.privateMCs[key] = element; // also the first element in the private MC collection
|
|
117
|
+
}
|
|
118
|
+
return element;
|
|
119
|
+
}
|
|
120
|
+
// remove a micro component from the private microcomponent collection
|
|
121
|
+
remove(key) {
|
|
122
|
+
// remove the component from the private collection
|
|
123
|
+
if (key in this.privateMCs) {
|
|
124
|
+
delete this.privateMCs[key];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
100
127
|
/*
|
|
101
128
|
selector methods, non-chainable
|
|
102
129
|
*/
|
|
103
|
-
// all
|
|
130
|
+
// all private collection members
|
|
104
131
|
siblings(search) {
|
|
105
|
-
|
|
132
|
+
if (this.target) {
|
|
133
|
+
if (this.target instanceof HTMLElement === true) {
|
|
134
|
+
return this.getMicroComponents([this.target], search);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
const result = this.privateMCs[search || ''];
|
|
138
|
+
return search && result ? [result] : Object.entries(this.privateMCs);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return [];
|
|
106
142
|
}
|
|
107
|
-
// first anchestor DOM node, all
|
|
143
|
+
// first anchestor DOM node, all public MCs
|
|
108
144
|
parent(search) {
|
|
109
|
-
if (!this.target)
|
|
145
|
+
if (!this.isDOM(this.target))
|
|
110
146
|
return [];
|
|
111
147
|
return this.traverseNodes(this.target.parentNode, search, true);
|
|
112
148
|
}
|
|
113
|
-
// all anchestor DOM nodes, beginning with the closest, all
|
|
149
|
+
// all anchestor DOM nodes, beginning with the closest, all public MCs
|
|
114
150
|
parents(search) {
|
|
115
|
-
if (!this.target)
|
|
151
|
+
if (!this.isDOM(this.target))
|
|
116
152
|
return [];
|
|
117
153
|
return this.traverseNodes(this.target.parentNode, search, false);
|
|
118
154
|
}
|
|
119
155
|
// nearest child DOM nodes, all puclic MCs
|
|
120
156
|
children(search) {
|
|
157
|
+
if (!this.isDOM(this.target))
|
|
158
|
+
return [];
|
|
121
159
|
const id = crypto.randomUUID();
|
|
122
160
|
this.target.setAttribute('temp-id', id);
|
|
123
161
|
const myDomElements = this.target.querySelectorAll(`*[_mc]:not([temp-id="${id}"] [_mc] [_mc])`);
|
|
@@ -127,6 +165,8 @@ export class MC {
|
|
|
127
165
|
}
|
|
128
166
|
// all descendant DOM nodes, all puclic MCs
|
|
129
167
|
descendants(search) {
|
|
168
|
+
if (!this.isDOM(this.target))
|
|
169
|
+
return [];
|
|
130
170
|
const myDomElements = this.target.querySelectorAll('*[_mc]');
|
|
131
171
|
return this.getMicroComponents([...myDomElements], search);
|
|
132
172
|
}
|
|
@@ -1,27 +1,34 @@
|
|
|
1
|
-
'use strict';export class MC{target;privateMCs={};static debug=false;constructor(target=null){this.target=target;this.privateMCs={};}
|
|
2
|
-
static
|
|
3
|
-
|
|
1
|
+
'use strict';export class MC{target;privateMCs={};_mc={};static debug=false;constructor(target=null){this.target=target;this.privateMCs={};if(MC?.debug){this._mc=this.privateMCs;};}
|
|
2
|
+
static siblings(target,search){let t=target;if(!t?._mc)
|
|
3
|
+
return[];let mc=t._mc;return search?mc[search]?[mc[search]]:[]:[...Object.entries(t._mc)];}
|
|
4
|
+
static add(target,key,element={}){const mc=target;if(!mc._mc){mc._mc=new MC();}
|
|
5
|
+
if(key&&!mc._mc[key]){mc._mc[key]=element;if(target){target.setAttribute('_mc','');}}
|
|
4
6
|
return element;}
|
|
5
|
-
|
|
6
|
-
else if(key){this.privateMCs[key]=element;}
|
|
7
|
-
return element;}
|
|
8
|
-
static remove(target,key){const mc=target;if(mc._mc&&mc._mc[key]){delete mc._mc[key];}
|
|
7
|
+
static remove(target,key){if(!key||typeof key!=='string'||!target||target instanceof HTMLElement===false){return;};const mc=target;if(mc._mc&&mc._mc[key]){delete mc._mc[key];}
|
|
9
8
|
if(!Object.keys(mc._mc).length&&target.hasAttribute('_mc')){target.removeAttribute('_mc');}
|
|
10
9
|
return;}
|
|
11
|
-
|
|
12
|
-
if(
|
|
13
|
-
|
|
14
|
-
else if(search&&
|
|
10
|
+
getMicroComponents(elements,search){let mcValues=[];elements.forEach(e=>{if(e){if(e instanceof HTMLElement){if(!search&&e._mc){mcValues=mcValues.concat(Object.values(e._mc));}
|
|
11
|
+
else if(search&&e._mc&&e._mc[search]){mcValues.push(e._mc[search]);}}
|
|
12
|
+
else if(e instanceof MC){if(!search){mcValues=mcValues.concat(Object.values(this.privateMCs));}
|
|
13
|
+
else if(search&&this.privateMCs[search]){mcValues.push(e._mc[search]);}}}});return mcValues;}
|
|
15
14
|
traverseNodes(node,search,firstonly=true){let mcValues=[];while(node){if(node._mc){if(!search){mcValues=mcValues.concat(Object.values(node._mc));}
|
|
16
15
|
else if(search&&node._mc[search]){mcValues.push(node._mc[search]);}
|
|
17
16
|
if(firstonly)
|
|
18
17
|
return mcValues;}
|
|
19
18
|
node=node.parentNode;}
|
|
20
19
|
return mcValues;}
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
isDOM(element){return element&&element instanceof HTMLElement;}
|
|
21
|
+
add(key,element={}){if(key){this.privateMCs[key]=element;}
|
|
22
|
+
return element;}
|
|
23
|
+
remove(key){if(key in this.privateMCs){delete this.privateMCs[key];}}
|
|
24
|
+
siblings(search){if(this.target){if(this.target instanceof HTMLElement===true){return this.getMicroComponents([this.target],search);}
|
|
25
|
+
else{const result=this.privateMCs[search||''];return search&&result?[result]:Object.entries(this.privateMCs);}}
|
|
26
|
+
return[];}
|
|
27
|
+
parent(search){if(!this.isDOM(this.target))
|
|
23
28
|
return[];return this.traverseNodes(this.target.parentNode,search,true);}
|
|
24
|
-
parents(search){if(!this.target)
|
|
29
|
+
parents(search){if(!this.isDOM(this.target))
|
|
25
30
|
return[];return this.traverseNodes(this.target.parentNode,search,false);}
|
|
26
|
-
children(search){
|
|
27
|
-
|
|
31
|
+
children(search){if(!this.isDOM(this.target))
|
|
32
|
+
return[];const id=crypto.randomUUID();this.target.setAttribute('temp-id',id);const myDomElements=this.target.querySelectorAll(`*[_mc]:not([temp-id="${id}"][_mc][_mc])`);const mcValues=this.getMicroComponents([...myDomElements],search);this.target.removeAttribute('temp-id');return mcValues;}
|
|
33
|
+
descendants(search){if(!this.isDOM(this.target))
|
|
34
|
+
return[];const myDomElements=this.target.querySelectorAll('*[_mc]');return this.getMicroComponents([...myDomElements],search);}}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
export class MC {
|
|
3
|
+
|
|
4
|
+
private target: HTMLElement | null;
|
|
5
|
+
|
|
6
|
+
private privateMCs: { [key: string]: any } = {};
|
|
7
|
+
|
|
8
|
+
_mc?: { [key: string]: any } = {};
|
|
9
|
+
|
|
10
|
+
static debug: boolean = false;
|
|
11
|
+
|
|
12
|
+
constructor(target: HTMLElement | null = null) {
|
|
13
|
+
this.target = target;
|
|
14
|
+
this.privateMCs = {};
|
|
15
|
+
if ( MC?.debug ) {
|
|
16
|
+
this._mc = this.privateMCs;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// return all top-level micro components from an HTMLElement
|
|
21
|
+
static siblings( target: HTMLElement, search?: string ): any[] {
|
|
22
|
+
let t = target as HTMLElementMc;
|
|
23
|
+
if ( !t?._mc ) return [];
|
|
24
|
+
let mc: { [key: string]: any } = t._mc;
|
|
25
|
+
return search ? mc[search] ? [ mc[search] ] : [] : [ ...Object.entries( t._mc ) ];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// add a microcomponent to a target DOM node
|
|
29
|
+
static add( target: HTMLElement, key: string, element: any = {} ): any[] {
|
|
30
|
+
|
|
31
|
+
const mc = target as any;
|
|
32
|
+
|
|
33
|
+
// create the _mc property if necessary
|
|
34
|
+
if ( !mc._mc ){
|
|
35
|
+
mc._mc = new MC();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// add the element with the given property key to the _mc property object
|
|
39
|
+
if ( key && !mc._mc[ key ] ){
|
|
40
|
+
mc._mc[ key ] = element;
|
|
41
|
+
if ( target ) {
|
|
42
|
+
target.setAttribute('_mc', '' );
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return element;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// remove a micro component from the target DOM node
|
|
50
|
+
static remove( target: HTMLElement, key: string ): void {
|
|
51
|
+
|
|
52
|
+
if ( //silent fail
|
|
53
|
+
!key
|
|
54
|
+
|| typeof key !== 'string'
|
|
55
|
+
|| !target
|
|
56
|
+
|| target instanceof HTMLElement === false ) {
|
|
57
|
+
return;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const mc = target as any;
|
|
61
|
+
|
|
62
|
+
// delete the property from the HTMLElement _mc collection
|
|
63
|
+
if (mc._mc && mc._mc[key]) {
|
|
64
|
+
delete mc._mc[key];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// remove the _mc attribute if the DOM collection is empty
|
|
68
|
+
if ( !Object.keys(mc._mc).length && target.hasAttribute('_mc') ) {
|
|
69
|
+
target.removeAttribute('_mc');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/*
|
|
76
|
+
helper methods
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
// get all microcomponents from a list of HTMLElements
|
|
80
|
+
private getMicroComponents(elements: any[], search?: string): any[] {
|
|
81
|
+
|
|
82
|
+
let mcValues: any[] = [];
|
|
83
|
+
|
|
84
|
+
elements.forEach(e => {
|
|
85
|
+
if ( e ){
|
|
86
|
+
if ( e instanceof HTMLElement ){
|
|
87
|
+
if (!search && (e as HTMLElementMc)._mc) {
|
|
88
|
+
mcValues = mcValues.concat(Object.values((e as any)._mc));
|
|
89
|
+
} else if (search && (e as HTMLElementMc)._mc && (e as any)._mc[search]) {
|
|
90
|
+
mcValues.push((e as any)._mc[search]);
|
|
91
|
+
}
|
|
92
|
+
} else if (e instanceof MC ){
|
|
93
|
+
if ( !search ) {
|
|
94
|
+
mcValues = mcValues.concat(Object.values( this.privateMCs ));
|
|
95
|
+
} else if (search && this.privateMCs[search]) {
|
|
96
|
+
mcValues.push((e as any)._mc[search]);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return mcValues;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// get all ancestor DOM nodes up to the document
|
|
106
|
+
private traverseNodes(node: Node | null, search?: string, firstonly: boolean = true): any[] {
|
|
107
|
+
|
|
108
|
+
let mcValues: any[] = [];
|
|
109
|
+
|
|
110
|
+
while (node) {
|
|
111
|
+
if ((node as HTMLElementMc)._mc) {
|
|
112
|
+
if (!search) {
|
|
113
|
+
mcValues = mcValues.concat(Object.values((node as any)._mc));
|
|
114
|
+
} else if (search && (node as any)._mc[search]) {
|
|
115
|
+
mcValues.push((node as any)._mc[search]);
|
|
116
|
+
}
|
|
117
|
+
if ( firstonly ) return mcValues;
|
|
118
|
+
}
|
|
119
|
+
node = node.parentNode;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return mcValues;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private isDOM( element: any ): boolean {
|
|
126
|
+
return element && element instanceof HTMLElement;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/*
|
|
130
|
+
Methods to handle private MCs
|
|
131
|
+
*/
|
|
132
|
+
|
|
133
|
+
// add a microcomponent to the private microcomponent collection
|
|
134
|
+
add( key?: string, element: any = {} ): any {
|
|
135
|
+
|
|
136
|
+
// add the element
|
|
137
|
+
if ( key ){
|
|
138
|
+
this.privateMCs[key] = element; // also the first element in the private MC collection
|
|
139
|
+
}
|
|
140
|
+
return element;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// remove a micro component from the private microcomponent collection
|
|
144
|
+
remove(key: string): void {
|
|
145
|
+
|
|
146
|
+
// remove the component from the private collection
|
|
147
|
+
if (key in this.privateMCs) {
|
|
148
|
+
delete this.privateMCs[key];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/*
|
|
154
|
+
selector methods, non-chainable
|
|
155
|
+
*/
|
|
156
|
+
|
|
157
|
+
// all private collection members
|
|
158
|
+
siblings(search?: string): any[] {
|
|
159
|
+
if ( this.target ){
|
|
160
|
+
if ( this.target instanceof HTMLElement === true ){
|
|
161
|
+
return this.getMicroComponents( [ this.target ], search );
|
|
162
|
+
} else {
|
|
163
|
+
const result = this.privateMCs[search || ''];
|
|
164
|
+
return search && result ? [ result ] : Object.entries( this.privateMCs );
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return []
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
// first anchestor DOM node, all public MCs
|
|
172
|
+
parent(search?: string): any[] {
|
|
173
|
+
|
|
174
|
+
if ( !this.isDOM( this.target) ) return [];
|
|
175
|
+
|
|
176
|
+
return this.traverseNodes( ( this.target as HTMLElement).parentNode, search, true);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// all anchestor DOM nodes, beginning with the closest, all public MCs
|
|
180
|
+
parents(search?: string): any[] {
|
|
181
|
+
|
|
182
|
+
if ( !this.isDOM( this.target) ) return [];
|
|
183
|
+
|
|
184
|
+
return this.traverseNodes( (this.target as HTMLElement ).parentNode, search, false);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// nearest child DOM nodes, all puclic MCs
|
|
188
|
+
children(search?: string): any[] {
|
|
189
|
+
|
|
190
|
+
if ( !this.isDOM( this.target) ) return [];
|
|
191
|
+
|
|
192
|
+
const id = crypto.randomUUID();
|
|
193
|
+
(this.target as HTMLElement).setAttribute('temp-id', id);
|
|
194
|
+
const myDomElements = (this.target as HTMLElement).querySelectorAll(`*[_mc]:not([temp-id="${id}"] [_mc] [_mc])`);
|
|
195
|
+
const mcValues = this.getMicroComponents( [ ...myDomElements ], search);
|
|
196
|
+
(this.target as HTMLElement).removeAttribute('temp-id');
|
|
197
|
+
return mcValues;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// all descendant DOM nodes, all puclic MCs
|
|
201
|
+
descendants(search?: string): any[] {
|
|
202
|
+
|
|
203
|
+
if ( !this.isDOM( this.target) ) return [];
|
|
204
|
+
|
|
205
|
+
const myDomElements = (this.target as HTMLElement).querySelectorAll('*[_mc]');
|
|
206
|
+
return this.getMicroComponents( [ ...myDomElements ], search);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface HTMLElementMc extends HTMLElement {
|
|
211
|
+
_mc: MC;
|
|
212
|
+
}
|
package/package.json
CHANGED
|
@@ -1,19 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twobirds/microcomponents",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Micro Components Organization Class",
|
|
5
5
|
"main": "microcomponents.js",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"scripts": {
|
|
8
|
-
"
|
|
8
|
+
"typedoc": "typedoc",
|
|
9
|
+
"clean": "rm -rf build/*; rm -rf docs/*; rm -rf dist/*;",
|
|
10
|
+
"prep": "cp -R assets/* public || true; cp src/* build || true;",
|
|
11
|
+
"build": "npm run clean; npm run prep; tsc; npm run minify; npm run copyToDist;",
|
|
9
12
|
"minify": "jsmin -o build/microcomponents.es3.min.js ./src/microcomponents.es3.js;jsmin -o build/microcomponents.min.js build/microcomponents.js;",
|
|
10
|
-
"copyToDist": "cp build/* dist; cp MicroComponents.svg dist"
|
|
13
|
+
"copyToDist": "cp build/* dist; cp MicroComponents.svg dist; cp MicroComponents.svg public/fiddle;"
|
|
11
14
|
},
|
|
12
15
|
"author": "",
|
|
13
16
|
"license": "ISC",
|
|
14
17
|
"devDependencies": {
|
|
15
18
|
"jsmin": "^1.0.1",
|
|
16
|
-
"typedoc": "^0.
|
|
17
|
-
"typescript": "^5.6.
|
|
18
|
-
}
|
|
19
|
+
"typedoc": "^0.26.10",
|
|
20
|
+
"typescript": "^5.6.3"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"javascript",
|
|
24
|
+
"library",
|
|
25
|
+
"native",
|
|
26
|
+
"DOM manipulation",
|
|
27
|
+
"lightweight",
|
|
28
|
+
"standalone",
|
|
29
|
+
"SPA",
|
|
30
|
+
"PWA",
|
|
31
|
+
"ES3",
|
|
32
|
+
"ES5",
|
|
33
|
+
"ES6",
|
|
34
|
+
"ES7",
|
|
35
|
+
"ES2015",
|
|
36
|
+
"ES2016",
|
|
37
|
+
"client",
|
|
38
|
+
"API"
|
|
39
|
+
]
|
|
19
40
|
}
|