mount-observer 0.0.1 → 0.0.2
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/MountObserver.js +87 -8
- package/MountObserver.ts +84 -9
- package/README.md +4 -2
- package/RootMutObs.js +1 -0
- package/RootMutObs.ts +1 -0
- package/package.json +1 -1
- package/tests/test6.html +39 -0
- package/tests/test6.spec.mjs +8 -0
- package/types.d.ts +33 -5
package/MountObserver.js
CHANGED
|
@@ -12,7 +12,7 @@ export class MountObserver extends EventTarget {
|
|
|
12
12
|
constructor(init) {
|
|
13
13
|
super();
|
|
14
14
|
const { match, whereElementIntersectsWith, whereMediaMatches } = init;
|
|
15
|
-
this.#isComplex = match.includes(' ') || match.includes(':');
|
|
15
|
+
this.#isComplex = match !== undefined && (match.includes(' ') || match.includes(':'));
|
|
16
16
|
if (whereElementIntersectsWith || whereMediaMatches)
|
|
17
17
|
throw 'NI'; //not implemented
|
|
18
18
|
this.#mountInit = init;
|
|
@@ -21,12 +21,31 @@ export class MountObserver extends EventTarget {
|
|
|
21
21
|
this.#disconnected = new WeakSet();
|
|
22
22
|
//this.#unmounted = new WeakSet();
|
|
23
23
|
}
|
|
24
|
+
#calculatedSelector;
|
|
25
|
+
get #selector() {
|
|
26
|
+
if (this.#calculatedSelector !== undefined)
|
|
27
|
+
return this.#calculatedSelector;
|
|
28
|
+
const { match, attribMatches } = this.#mountInit;
|
|
29
|
+
const base = match || '*';
|
|
30
|
+
if (attribMatches === undefined)
|
|
31
|
+
return base;
|
|
32
|
+
const matches = [];
|
|
33
|
+
attribMatches.forEach(x => {
|
|
34
|
+
const { names } = x;
|
|
35
|
+
names.forEach(y => {
|
|
36
|
+
matches.push(`${base}[${y}]`);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
this.#calculatedSelector = matches.join(',');
|
|
40
|
+
return this.#calculatedSelector;
|
|
41
|
+
}
|
|
24
42
|
async observe(within) {
|
|
25
43
|
const nodeToMonitor = this.#isComplex ? (within instanceof ShadowRoot ? within : within.getRootNode()) : within;
|
|
26
44
|
if (!mutationObserverLookup.has(nodeToMonitor)) {
|
|
27
45
|
mutationObserverLookup.set(nodeToMonitor, new RootMutObs(nodeToMonitor));
|
|
28
46
|
}
|
|
29
47
|
const rootMutObs = mutationObserverLookup.get(within);
|
|
48
|
+
const { attribMatches } = this.#mountInit;
|
|
30
49
|
rootMutObs.addEventListener('mutation-event', (e) => {
|
|
31
50
|
//TODO: disconnected
|
|
32
51
|
if (this.#isComplex) {
|
|
@@ -39,11 +58,38 @@ export class MountObserver extends EventTarget {
|
|
|
39
58
|
const doDisconnect = this.#mountInit.do?.onDisconnect;
|
|
40
59
|
for (const mutationRecord of mutationRecords) {
|
|
41
60
|
const { addedNodes, type, removedNodes } = mutationRecord;
|
|
42
|
-
|
|
61
|
+
console.log(mutationRecord);
|
|
43
62
|
const addedElements = Array.from(addedNodes).filter(x => x instanceof Element);
|
|
44
63
|
addedElements.forEach(x => elsToInspect.push(x));
|
|
45
64
|
if (type === 'attributes') {
|
|
46
|
-
const { target } = mutationRecord;
|
|
65
|
+
const { target, attributeName, oldValue } = mutationRecord;
|
|
66
|
+
if (target instanceof Element && attributeName !== null && attribMatches !== undefined && this.#mounted.has(target)) {
|
|
67
|
+
let idx = 0;
|
|
68
|
+
for (const attrMatch of attribMatches) {
|
|
69
|
+
const { names } = attrMatch;
|
|
70
|
+
if (names.includes(attributeName)) {
|
|
71
|
+
const newValue = target.getAttribute(attributeName);
|
|
72
|
+
// let parsedNewValue = undefined;
|
|
73
|
+
// switch(type){
|
|
74
|
+
// case 'boolean':
|
|
75
|
+
// parsedNewValue = newValue === 'true' ? true : newValue === 'false' ? false : null;
|
|
76
|
+
// break;
|
|
77
|
+
// case 'date':
|
|
78
|
+
// parsedNewValue = newValue === null ? null : new Date(newValue);
|
|
79
|
+
// break;
|
|
80
|
+
// case ''
|
|
81
|
+
// }
|
|
82
|
+
const attrChangeInfo = {
|
|
83
|
+
name: attributeName,
|
|
84
|
+
oldValue,
|
|
85
|
+
newValue,
|
|
86
|
+
idx
|
|
87
|
+
};
|
|
88
|
+
this.dispatchEvent(new AttrChangeEvent(target, attrChangeInfo));
|
|
89
|
+
}
|
|
90
|
+
idx++;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
47
93
|
elsToInspect.push(target);
|
|
48
94
|
}
|
|
49
95
|
const deletedElements = Array.from(removedNodes).filter(x => x instanceof Element);
|
|
@@ -73,7 +119,7 @@ export class MountObserver extends EventTarget {
|
|
|
73
119
|
//first unmount non matching
|
|
74
120
|
const alreadyMounted = this.#filterAndDismount();
|
|
75
121
|
const onMount = this.#mountInit.do?.onMount;
|
|
76
|
-
const imp = this.#mountInit
|
|
122
|
+
const { import: imp, attribMatches } = this.#mountInit;
|
|
77
123
|
for (const match of matching) {
|
|
78
124
|
if (alreadyMounted.has(match))
|
|
79
125
|
continue;
|
|
@@ -96,6 +142,28 @@ export class MountObserver extends EventTarget {
|
|
|
96
142
|
if (onMount !== undefined)
|
|
97
143
|
onMount(match, this, 'PostImport');
|
|
98
144
|
this.dispatchEvent(new MountEvent(match));
|
|
145
|
+
if (attribMatches !== undefined) {
|
|
146
|
+
let idx = 0;
|
|
147
|
+
for (const attribMatch of attribMatches) {
|
|
148
|
+
let newValue = null;
|
|
149
|
+
const { names } = attribMatch;
|
|
150
|
+
let nonNullName = names[0];
|
|
151
|
+
for (const name of names) {
|
|
152
|
+
const attrVal = match.getAttribute(name);
|
|
153
|
+
if (attrVal !== null)
|
|
154
|
+
nonNullName = name;
|
|
155
|
+
newValue = newValue || attrVal;
|
|
156
|
+
}
|
|
157
|
+
const attribInfo = {
|
|
158
|
+
oldValue: null,
|
|
159
|
+
newValue,
|
|
160
|
+
idx,
|
|
161
|
+
name: nonNullName
|
|
162
|
+
};
|
|
163
|
+
this.dispatchEvent(new AttrChangeEvent(match, attribInfo));
|
|
164
|
+
idx++;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
99
167
|
this.#mountedList?.push(new WeakRef(match));
|
|
100
168
|
//if(this.#unmounted.has(match)) this.#unmounted.delete(match);
|
|
101
169
|
}
|
|
@@ -113,7 +181,8 @@ export class MountObserver extends EventTarget {
|
|
|
113
181
|
const returnSet = new Set();
|
|
114
182
|
if (this.#mountedList !== undefined) {
|
|
115
183
|
const previouslyMounted = this.#mountedList.map(x => x.deref());
|
|
116
|
-
const {
|
|
184
|
+
const { whereSatisfies, whereInstanceOf } = this.#mountInit;
|
|
185
|
+
const match = this.#selector;
|
|
117
186
|
const elsToUnMount = previouslyMounted.filter(x => {
|
|
118
187
|
if (x === undefined)
|
|
119
188
|
return false;
|
|
@@ -132,7 +201,8 @@ export class MountObserver extends EventTarget {
|
|
|
132
201
|
return returnSet;
|
|
133
202
|
}
|
|
134
203
|
async #filterAndMount(els, checkMatch) {
|
|
135
|
-
const {
|
|
204
|
+
const { whereSatisfies, whereInstanceOf } = this.#mountInit;
|
|
205
|
+
const match = this.#selector;
|
|
136
206
|
const elsToMount = els.filter(x => {
|
|
137
207
|
if (checkMatch) {
|
|
138
208
|
if (!x.matches(match))
|
|
@@ -151,8 +221,7 @@ export class MountObserver extends EventTarget {
|
|
|
151
221
|
this.#mount(elsToMount);
|
|
152
222
|
}
|
|
153
223
|
async #inspectWithin(within) {
|
|
154
|
-
const
|
|
155
|
-
const els = Array.from(within.querySelectorAll(match));
|
|
224
|
+
const els = Array.from(within.querySelectorAll(this.#selector));
|
|
156
225
|
this.#filterAndMount(els, false);
|
|
157
226
|
}
|
|
158
227
|
unobserve() {
|
|
@@ -188,3 +257,13 @@ export class DisconnectEvent extends Event {
|
|
|
188
257
|
this.disconnectedElement = disconnectedElement;
|
|
189
258
|
}
|
|
190
259
|
}
|
|
260
|
+
export class AttrChangeEvent extends Event {
|
|
261
|
+
mountedElement;
|
|
262
|
+
attrChangeInfo;
|
|
263
|
+
static eventName = 'attr-change';
|
|
264
|
+
constructor(mountedElement, attrChangeInfo) {
|
|
265
|
+
super(AttrChangeEvent.eventName);
|
|
266
|
+
this.mountedElement = mountedElement;
|
|
267
|
+
this.attrChangeInfo = attrChangeInfo;
|
|
268
|
+
}
|
|
269
|
+
}
|
package/MountObserver.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {MountInit, MountContext, AddMutationEventListener,
|
|
2
2
|
MutationEvent, dismountEventName, mountEventName, IMountEvent, IDismountEvent,
|
|
3
|
-
disconnectedEventName, IDisconnectEvent
|
|
3
|
+
disconnectedEventName, IDisconnectEvent, IAttrChangeEvent, attrChangeEventName, AttrChangeInfo
|
|
4
4
|
} from './types';
|
|
5
5
|
import {RootMutObs} from './RootMutObs.js';
|
|
6
6
|
|
|
@@ -20,7 +20,7 @@ export class MountObserver extends EventTarget implements MountContext{
|
|
|
20
20
|
constructor(init: MountInit){
|
|
21
21
|
super();
|
|
22
22
|
const {match, whereElementIntersectsWith, whereMediaMatches} = init;
|
|
23
|
-
this.#isComplex = match.includes(' ') || match.includes(':')
|
|
23
|
+
this.#isComplex = match !== undefined && (match.includes(' ') || match.includes(':'));
|
|
24
24
|
if(whereElementIntersectsWith || whereMediaMatches) throw 'NI'; //not implemented
|
|
25
25
|
this.#mountInit = init;
|
|
26
26
|
this.#abortController = new AbortController();
|
|
@@ -29,12 +29,30 @@ export class MountObserver extends EventTarget implements MountContext{
|
|
|
29
29
|
//this.#unmounted = new WeakSet();
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
#calculatedSelector: string | undefined;
|
|
33
|
+
get #selector(){
|
|
34
|
+
if(this.#calculatedSelector !== undefined) return this.#calculatedSelector;
|
|
35
|
+
const {match, attribMatches} = this.#mountInit;
|
|
36
|
+
const base = match || '*';
|
|
37
|
+
if(attribMatches === undefined) return base;
|
|
38
|
+
const matches: Array<string> = [];
|
|
39
|
+
attribMatches.forEach(x => {
|
|
40
|
+
const {names} = x;
|
|
41
|
+
names.forEach(y => {
|
|
42
|
+
matches.push(`${base}[${y}]`)
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
this.#calculatedSelector = matches.join(',');
|
|
46
|
+
return this.#calculatedSelector;
|
|
47
|
+
}
|
|
48
|
+
|
|
32
49
|
async observe(within: Node){
|
|
33
50
|
const nodeToMonitor = this.#isComplex ? (within instanceof ShadowRoot ? within : within.getRootNode()) : within;
|
|
34
51
|
if(!mutationObserverLookup.has(nodeToMonitor)){
|
|
35
52
|
mutationObserverLookup.set(nodeToMonitor, new RootMutObs(nodeToMonitor));
|
|
36
53
|
}
|
|
37
54
|
const rootMutObs = mutationObserverLookup.get(within)!;
|
|
55
|
+
const {attribMatches} = this.#mountInit;
|
|
38
56
|
(rootMutObs as any as AddMutationEventListener).addEventListener('mutation-event', (e: MutationEvent) => {
|
|
39
57
|
//TODO: disconnected
|
|
40
58
|
if(this.#isComplex){
|
|
@@ -47,11 +65,39 @@ export class MountObserver extends EventTarget implements MountContext{
|
|
|
47
65
|
const doDisconnect = this.#mountInit.do?.onDisconnect;
|
|
48
66
|
for(const mutationRecord of mutationRecords){
|
|
49
67
|
const {addedNodes, type, removedNodes} = mutationRecord;
|
|
50
|
-
|
|
68
|
+
console.log(mutationRecord);
|
|
51
69
|
const addedElements = Array.from(addedNodes).filter(x => x instanceof Element) as Array<Element>;
|
|
52
70
|
addedElements.forEach(x => elsToInspect.push(x));
|
|
53
71
|
if(type === 'attributes'){
|
|
54
|
-
const {target} = mutationRecord;
|
|
72
|
+
const {target, attributeName, oldValue} = mutationRecord;
|
|
73
|
+
if(target instanceof Element && attributeName !== null && attribMatches !== undefined && this.#mounted.has(target)){
|
|
74
|
+
let idx = 0;
|
|
75
|
+
for(const attrMatch of attribMatches){
|
|
76
|
+
const {names} = attrMatch;
|
|
77
|
+
if(names.includes(attributeName)){
|
|
78
|
+
const newValue = target.getAttribute(attributeName);
|
|
79
|
+
// let parsedNewValue = undefined;
|
|
80
|
+
// switch(type){
|
|
81
|
+
// case 'boolean':
|
|
82
|
+
// parsedNewValue = newValue === 'true' ? true : newValue === 'false' ? false : null;
|
|
83
|
+
// break;
|
|
84
|
+
// case 'date':
|
|
85
|
+
// parsedNewValue = newValue === null ? null : new Date(newValue);
|
|
86
|
+
// break;
|
|
87
|
+
// case ''
|
|
88
|
+
|
|
89
|
+
// }
|
|
90
|
+
const attrChangeInfo: AttrChangeInfo = {
|
|
91
|
+
name: attributeName,
|
|
92
|
+
oldValue,
|
|
93
|
+
newValue,
|
|
94
|
+
idx
|
|
95
|
+
};
|
|
96
|
+
this.dispatchEvent(new AttrChangeEvent(target, attrChangeInfo));
|
|
97
|
+
}
|
|
98
|
+
idx++;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
55
101
|
elsToInspect.push(target as Element);
|
|
56
102
|
}
|
|
57
103
|
const deletedElements = Array.from(removedNodes).filter(x => x instanceof Element) as Array<Element>;
|
|
@@ -83,7 +129,7 @@ export class MountObserver extends EventTarget implements MountContext{
|
|
|
83
129
|
//first unmount non matching
|
|
84
130
|
const alreadyMounted = this.#filterAndDismount();
|
|
85
131
|
const onMount = this.#mountInit.do?.onMount;
|
|
86
|
-
const imp = this.#mountInit
|
|
132
|
+
const {import: imp, attribMatches} = this.#mountInit;
|
|
87
133
|
for(const match of matching){
|
|
88
134
|
if(alreadyMounted.has(match)) continue;
|
|
89
135
|
this.#mounted.add(match);
|
|
@@ -104,6 +150,27 @@ export class MountObserver extends EventTarget implements MountContext{
|
|
|
104
150
|
}
|
|
105
151
|
if(onMount !== undefined) onMount(match, this, 'PostImport');
|
|
106
152
|
this.dispatchEvent(new MountEvent(match));
|
|
153
|
+
if(attribMatches !== undefined){
|
|
154
|
+
let idx = 0;
|
|
155
|
+
for(const attribMatch of attribMatches){
|
|
156
|
+
let newValue = null;
|
|
157
|
+
const {names} = attribMatch;
|
|
158
|
+
let nonNullName = names[0];
|
|
159
|
+
for(const name of names){
|
|
160
|
+
const attrVal = match.getAttribute(name);
|
|
161
|
+
if(attrVal !== null) nonNullName = name;
|
|
162
|
+
newValue = newValue || attrVal;
|
|
163
|
+
}
|
|
164
|
+
const attribInfo: AttrChangeInfo = {
|
|
165
|
+
oldValue: null,
|
|
166
|
+
newValue,
|
|
167
|
+
idx,
|
|
168
|
+
name: nonNullName
|
|
169
|
+
};
|
|
170
|
+
this.dispatchEvent(new AttrChangeEvent(match, attribInfo));
|
|
171
|
+
idx++;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
107
174
|
this.#mountedList?.push(new WeakRef(match));
|
|
108
175
|
//if(this.#unmounted.has(match)) this.#unmounted.delete(match);
|
|
109
176
|
}
|
|
@@ -123,7 +190,8 @@ export class MountObserver extends EventTarget implements MountContext{
|
|
|
123
190
|
const returnSet = new Set<Element>();
|
|
124
191
|
if(this.#mountedList !== undefined){
|
|
125
192
|
const previouslyMounted = this.#mountedList.map(x => x.deref());
|
|
126
|
-
const {
|
|
193
|
+
const {whereSatisfies, whereInstanceOf} = this.#mountInit;
|
|
194
|
+
const match = this.#selector;
|
|
127
195
|
const elsToUnMount = previouslyMounted.filter(x => {
|
|
128
196
|
if(x === undefined) return false;
|
|
129
197
|
if(!x.matches(match)) return true;
|
|
@@ -140,7 +208,8 @@ export class MountObserver extends EventTarget implements MountContext{
|
|
|
140
208
|
}
|
|
141
209
|
|
|
142
210
|
async #filterAndMount(els: Array<Element>, checkMatch: boolean){
|
|
143
|
-
const {
|
|
211
|
+
const {whereSatisfies, whereInstanceOf} = this.#mountInit;
|
|
212
|
+
const match = this.#selector;
|
|
144
213
|
const elsToMount = els.filter(x => {
|
|
145
214
|
if(checkMatch){
|
|
146
215
|
if(!x.matches(match)) return false;
|
|
@@ -157,8 +226,7 @@ export class MountObserver extends EventTarget implements MountContext{
|
|
|
157
226
|
}
|
|
158
227
|
|
|
159
228
|
async #inspectWithin(within: Node){
|
|
160
|
-
const
|
|
161
|
-
const els = Array.from((within as Element).querySelectorAll(match));
|
|
229
|
+
const els = Array.from((within as Element).querySelectorAll(this.#selector));
|
|
162
230
|
this.#filterAndMount(els, false);
|
|
163
231
|
}
|
|
164
232
|
|
|
@@ -200,3 +268,10 @@ export class DisconnectEvent extends Event implements IDisconnectEvent{
|
|
|
200
268
|
}
|
|
201
269
|
}
|
|
202
270
|
|
|
271
|
+
export class AttrChangeEvent extends Event implements IAttrChangeEvent{
|
|
272
|
+
static eventName: attrChangeEventName = 'attr-change';
|
|
273
|
+
constructor(public mountedElement: Element, public attrChangeInfo: AttrChangeInfo){
|
|
274
|
+
super(AttrChangeEvent.eventName);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
package/README.md
CHANGED
|
@@ -135,8 +135,6 @@ const observer = new MountObserver({
|
|
|
135
135
|
})
|
|
136
136
|
```
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
|
|
140
138
|
## Subscribing
|
|
141
139
|
|
|
142
140
|
Subscribing can be done via:
|
|
@@ -181,6 +179,10 @@ If an element that is in "mounted" state according to a MountObserver instance i
|
|
|
181
179
|
4) If the new place it was added remains within the original rootNode and remains either dismounted or mounted, the MountObserver instance dispatches event "reconfirmed".
|
|
182
180
|
5) If the element no longer satisfies the criteria of the MountObserver instance, the MountObserver instance will dispatch event "dismount". The same is done in reverse for moved elements that started out in a "dismounted" state.
|
|
183
181
|
|
|
182
|
+
## Special support for attributes
|
|
183
|
+
|
|
184
|
+
Extra support is provided for monitoring attributes.
|
|
185
|
+
|
|
184
186
|
## Preemptive downloading
|
|
185
187
|
|
|
186
188
|
There are two significant steps to imports, each of which imposes a cost:
|
package/RootMutObs.js
CHANGED
package/RootMutObs.ts
CHANGED
package/package.json
CHANGED
package/tests/test6.html
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
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>Document</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id=div>
|
|
10
|
+
<span id=span></span>
|
|
11
|
+
</div>
|
|
12
|
+
<div id=target></div>
|
|
13
|
+
<script type=module>
|
|
14
|
+
import {MountObserver} from '../MountObserver.js';
|
|
15
|
+
const mo = new MountObserver({
|
|
16
|
+
match: '#span',
|
|
17
|
+
attribMatches:[
|
|
18
|
+
{
|
|
19
|
+
names: ['test-1']
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
do:{
|
|
23
|
+
onDisconnect: (el, ctx) => {
|
|
24
|
+
console.log({el, ctx});
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
mo.addEventListener('attr-change', e => {
|
|
30
|
+
console.log(e);
|
|
31
|
+
target.setAttribute('mark', 'good');
|
|
32
|
+
});
|
|
33
|
+
mo.observe(div);
|
|
34
|
+
setTimeout(() => {
|
|
35
|
+
span.setAttribute('test-1', 'hello')
|
|
36
|
+
}, 1000);
|
|
37
|
+
</script>
|
|
38
|
+
</body>
|
|
39
|
+
</html>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test';
|
|
2
|
+
test('test1', async ({ page }) => {
|
|
3
|
+
await page.goto('./tests/test6.html');
|
|
4
|
+
// wait for 1 second
|
|
5
|
+
await page.waitForTimeout(1000);
|
|
6
|
+
const editor = page.locator('#target');
|
|
7
|
+
await expect(editor).toHaveAttribute('mark', 'good');
|
|
8
|
+
});
|
package/types.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export interface MountInit{
|
|
2
|
-
readonly match
|
|
3
|
-
readonly
|
|
4
|
-
readonly
|
|
2
|
+
readonly match?: CSSMatch,
|
|
3
|
+
readonly attribMatches?: Array<AttribMatch>,
|
|
4
|
+
readonly whereElementIntersectsWith?: IntersectionObserverInit,
|
|
5
|
+
readonly whereMediaMatches?: MediaQuery,
|
|
5
6
|
readonly whereInstanceOf?: Array<typeof Node>, //[TODO] What's the best way to type this?,
|
|
6
|
-
readonly whereSatisfies
|
|
7
|
+
readonly whereSatisfies?: PipelineProcessor<boolean>,
|
|
7
8
|
readonly import?: ImportString | [ImportString, ImportAssertions] | PipelineProcessor,
|
|
8
9
|
readonly do?: {
|
|
9
10
|
readonly onMount: PipelineProcessor,
|
|
@@ -18,6 +19,14 @@ type CSSMatch = string;
|
|
|
18
19
|
type ImportString = string;
|
|
19
20
|
type MediaQuery = string;
|
|
20
21
|
|
|
22
|
+
export interface AttribMatch{
|
|
23
|
+
names: string[],
|
|
24
|
+
//for boolean, support true/false/mixed
|
|
25
|
+
// type?: 'number' | 'string' | 'date' | 'json-object' | 'boolean',
|
|
26
|
+
// valConverter?: (s: string) => any,
|
|
27
|
+
// validator?: (v: any) => boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
21
30
|
export interface MountContext {
|
|
22
31
|
// readonly mountInit: MountInit,
|
|
23
32
|
// readonly mountedRefs: WeakRef<Element>[],
|
|
@@ -42,10 +51,18 @@ export interface AddMutationEventListener {
|
|
|
42
51
|
}
|
|
43
52
|
//#endregion
|
|
44
53
|
|
|
54
|
+
interface AttrChangeInfo{
|
|
55
|
+
name: string,
|
|
56
|
+
oldValue: string | null,
|
|
57
|
+
newValue: string | null,
|
|
58
|
+
idx: number,
|
|
59
|
+
//parsedNewValue?: any,
|
|
60
|
+
}
|
|
61
|
+
|
|
45
62
|
//#region mount event
|
|
46
63
|
export type mountEventName = 'mount';
|
|
47
64
|
export interface IMountEvent{
|
|
48
|
-
mountedElement: Element
|
|
65
|
+
mountedElement: Element,
|
|
49
66
|
}
|
|
50
67
|
export type mountEventHandler = (e: IMountEvent) => void;
|
|
51
68
|
|
|
@@ -80,3 +97,14 @@ export interface AddDisconnectEventListener {
|
|
|
80
97
|
}
|
|
81
98
|
//endregion
|
|
82
99
|
|
|
100
|
+
//#region attribute change event
|
|
101
|
+
export type attrChangeEventName = 'attr-change';
|
|
102
|
+
export interface IAttrChangeEvent extends IMountEvent {
|
|
103
|
+
attrChangeInfo: AttrChangeInfo,
|
|
104
|
+
}
|
|
105
|
+
export type attrChangeEventHander = (e: IAttrChangeEvent) => void;
|
|
106
|
+
export interface AddAttrChangeEventistener{
|
|
107
|
+
addEventListener(eventName: attrChangeEventName, handler: attrChangeEventHander, options?: AddEventListenerOptions): void;
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
|