mount-observer 0.0.15 → 0.0.17
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 +40 -44
- package/README.md +14 -12
- package/birtualizeMatch.js +0 -1
- package/getWhereAttrSelector.js +49 -10
- package/package.json +1 -1
- package/types.d.ts +19 -9
- package/doWhereAttr.js +0 -43
package/MountObserver.js
CHANGED
|
@@ -29,6 +29,7 @@ export class MountObserver extends EventTarget {
|
|
|
29
29
|
//this.#unmounted = new WeakSet();
|
|
30
30
|
}
|
|
31
31
|
#calculatedSelector;
|
|
32
|
+
#attrParts;
|
|
32
33
|
#fullListOfAttrs;
|
|
33
34
|
//get #attrVals
|
|
34
35
|
async #selector() {
|
|
@@ -40,8 +41,9 @@ export class MountObserver extends EventTarget {
|
|
|
40
41
|
return withoutAttrs;
|
|
41
42
|
const { getWhereAttrSelector } = await import('./getWhereAttrSelector.js');
|
|
42
43
|
const info = getWhereAttrSelector(whereAttr, withoutAttrs);
|
|
43
|
-
const { fullListOfAttrs, calculatedSelector } = info;
|
|
44
|
+
const { fullListOfAttrs, calculatedSelector, partitionedAttrs } = info;
|
|
44
45
|
this.#fullListOfAttrs = fullListOfAttrs;
|
|
46
|
+
this.#attrParts = partitionedAttrs;
|
|
45
47
|
this.#calculatedSelector = calculatedSelector;
|
|
46
48
|
return this.#calculatedSelector;
|
|
47
49
|
}
|
|
@@ -98,6 +100,7 @@ export class MountObserver extends EventTarget {
|
|
|
98
100
|
}
|
|
99
101
|
}
|
|
100
102
|
async observe(within) {
|
|
103
|
+
await this.#selector();
|
|
101
104
|
this.objNde = new WeakRef(within);
|
|
102
105
|
const nodeToMonitor = this.#isComplex ? (within instanceof ShadowRoot ? within : within.getRootNode()) : within;
|
|
103
106
|
if (!mutationObserverLookup.has(nodeToMonitor)) {
|
|
@@ -114,7 +117,6 @@ export class MountObserver extends EventTarget {
|
|
|
114
117
|
}
|
|
115
118
|
}
|
|
116
119
|
const rootMutObs = mutationObserverLookup.get(within);
|
|
117
|
-
//const {whereAttr} = this.#mountInit;
|
|
118
120
|
const fullListOfAttrs = this.#fullListOfAttrs;
|
|
119
121
|
rootMutObs.addEventListener('mutation-event', async (e) => {
|
|
120
122
|
//TODO: disconnected
|
|
@@ -126,32 +128,33 @@ export class MountObserver extends EventTarget {
|
|
|
126
128
|
const elsToInspect = [];
|
|
127
129
|
//const elsToDisconnect: Array<Element> = [];
|
|
128
130
|
const doDisconnect = this.#mountInit.do?.disconnect;
|
|
131
|
+
let attrChangeInfosMap;
|
|
129
132
|
for (const mutationRecord of mutationRecords) {
|
|
130
133
|
const { addedNodes, type, removedNodes } = mutationRecord;
|
|
131
|
-
//console.log(mutationRecord);
|
|
132
134
|
const addedElements = Array.from(addedNodes).filter(x => x instanceof Element);
|
|
133
135
|
addedElements.forEach(x => elsToInspect.push(x));
|
|
134
136
|
if (type === 'attributes') {
|
|
135
137
|
const { target, attributeName, oldValue } = mutationRecord;
|
|
136
|
-
if (target instanceof Element && attributeName !== null
|
|
138
|
+
if (target instanceof Element && attributeName !== null /*&& this.#mounted.has(target)*/) {
|
|
137
139
|
if (fullListOfAttrs !== undefined) {
|
|
138
140
|
const idx = fullListOfAttrs.indexOf(attributeName);
|
|
139
|
-
if (idx
|
|
141
|
+
if (idx !== -1) {
|
|
142
|
+
if (attrChangeInfosMap === undefined)
|
|
143
|
+
attrChangeInfosMap = new Map();
|
|
144
|
+
let attrChangeInfos = attrChangeInfosMap.get(target);
|
|
145
|
+
if (attrChangeInfos === undefined) {
|
|
146
|
+
attrChangeInfos = [];
|
|
147
|
+
attrChangeInfosMap.set(target, attrChangeInfos);
|
|
148
|
+
}
|
|
140
149
|
const newValue = target.getAttribute(attributeName);
|
|
150
|
+
const parts = this.#attrParts[idx];
|
|
141
151
|
const attrChangeInfo = {
|
|
142
|
-
name: attributeName,
|
|
143
152
|
oldValue,
|
|
144
153
|
newValue,
|
|
145
|
-
idx
|
|
154
|
+
idx,
|
|
155
|
+
parts
|
|
146
156
|
};
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
const { whereAttr } = this.#mountInit;
|
|
152
|
-
if (whereAttr !== undefined) {
|
|
153
|
-
const { doWhereAttr } = await import('./doWhereAttr.js');
|
|
154
|
-
doWhereAttr(whereAttr, attributeName, target, oldValue, this);
|
|
157
|
+
attrChangeInfos.push(attrChangeInfo);
|
|
155
158
|
}
|
|
156
159
|
}
|
|
157
160
|
}
|
|
@@ -166,6 +169,11 @@ export class MountObserver extends EventTarget {
|
|
|
166
169
|
this.dispatchEvent(new DisconnectEvent(deletedElement));
|
|
167
170
|
}
|
|
168
171
|
}
|
|
172
|
+
if (attrChangeInfosMap !== undefined) {
|
|
173
|
+
for (const [key, value] of attrChangeInfosMap) {
|
|
174
|
+
this.dispatchEvent(new AttrChangeEvent(key, value));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
169
177
|
this.#filterAndMount(elsToInspect, true, false);
|
|
170
178
|
}, { signal: this.#abortController.signal });
|
|
171
179
|
await this.#inspectWithin(within, true);
|
|
@@ -213,35 +221,23 @@ export class MountObserver extends EventTarget {
|
|
|
213
221
|
}
|
|
214
222
|
this.dispatchEvent(new MountEvent(match, initializing));
|
|
215
223
|
if (fullListOfAttrs !== undefined) {
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
224
|
+
const attrParts = this.#attrParts;
|
|
225
|
+
const attrChangeInfos = [];
|
|
226
|
+
for (let idx = 0, ii = fullListOfAttrs.length; idx < ii; idx++) {
|
|
227
|
+
const name = fullListOfAttrs[idx];
|
|
228
|
+
const oldValue = null;
|
|
229
|
+
const newValue = match.getAttribute(name);
|
|
230
|
+
const parts = attrParts[idx];
|
|
231
|
+
attrChangeInfos.push({
|
|
232
|
+
idx,
|
|
233
|
+
newValue,
|
|
234
|
+
oldValue,
|
|
235
|
+
parts
|
|
236
|
+
});
|
|
222
237
|
}
|
|
223
|
-
|
|
224
|
-
// for(const attribMatch of attribMatches){
|
|
225
|
-
// let newValue = null;
|
|
226
|
-
// const {names} = attribMatch;
|
|
227
|
-
// let nonNullName = names[0];
|
|
228
|
-
// for(const name of names){
|
|
229
|
-
// const attrVal = match.getAttribute(name);
|
|
230
|
-
// if(attrVal !== null) nonNullName = name;
|
|
231
|
-
// newValue = newValue || attrVal;
|
|
232
|
-
// }
|
|
233
|
-
// const attribInfo: AttrChangeInfo = {
|
|
234
|
-
// oldValue: null,
|
|
235
|
-
// newValue,
|
|
236
|
-
// idx,
|
|
237
|
-
// name: nonNullName
|
|
238
|
-
// };
|
|
239
|
-
// this.dispatchEvent(new AttrChangeEvent(match, attribInfo));
|
|
240
|
-
// idx++;
|
|
241
|
-
// }
|
|
238
|
+
this.dispatchEvent(new AttrChangeEvent(match, attrChangeInfos));
|
|
242
239
|
}
|
|
243
240
|
this.#mountedList?.push(new WeakRef(match));
|
|
244
|
-
//if(this.#unmounted.has(match)) this.#unmounted.delete(match);
|
|
245
241
|
}
|
|
246
242
|
}
|
|
247
243
|
async #dismount(unmatching) {
|
|
@@ -342,12 +338,12 @@ export class DisconnectEvent extends Event {
|
|
|
342
338
|
}
|
|
343
339
|
export class AttrChangeEvent extends Event {
|
|
344
340
|
mountedElement;
|
|
345
|
-
|
|
341
|
+
attrChangeInfos;
|
|
346
342
|
static eventName = 'attr-change';
|
|
347
|
-
constructor(mountedElement,
|
|
343
|
+
constructor(mountedElement, attrChangeInfos) {
|
|
348
344
|
super(AttrChangeEvent.eventName);
|
|
349
345
|
this.mountedElement = mountedElement;
|
|
350
|
-
this.
|
|
346
|
+
this.attrChangeInfos = attrChangeInfos;
|
|
351
347
|
}
|
|
352
348
|
}
|
|
353
349
|
//const hasRootInDefault = ['data', 'enh', 'data-enh']
|
package/README.md
CHANGED
|
@@ -284,9 +284,9 @@ So let's say we want to insist that on custom elements, we must have the data- p
|
|
|
284
284
|
|
|
285
285
|
And we want to support an alternative, more semantic sounding prefix to data, say enh-*, endorsed by [this proposal](https://github.com/WICG/webcomponents/issues/1000).
|
|
286
286
|
|
|
287
|
-
Here's what the api
|
|
287
|
+
Here's what the api **doesn't** provide:
|
|
288
288
|
|
|
289
|
-
##
|
|
289
|
+
## Rejected option -- The carpal syndrome syntax
|
|
290
290
|
|
|
291
291
|
```JavaScript
|
|
292
292
|
import {MountObserver} from '../MountObserver.js';
|
|
@@ -320,7 +320,7 @@ const mo = new MountObserver({
|
|
|
320
320
|
});
|
|
321
321
|
```
|
|
322
322
|
|
|
323
|
-
##
|
|
323
|
+
## Supported -- The DRY Way
|
|
324
324
|
|
|
325
325
|
```JavaScript
|
|
326
326
|
import {MountObserver} from '../MountObserver.js';
|
|
@@ -360,16 +360,18 @@ MountObserver provides a breakdown of the matching attribute when encountered:
|
|
|
360
360
|
console.log(e);
|
|
361
361
|
// {
|
|
362
362
|
// matchingElement,
|
|
363
|
-
// attrChangeInfo:{
|
|
364
|
-
// name: 'data-my-enhancement-first-aspect-wow-this-is-deep'
|
|
365
|
-
// root: 'data',
|
|
366
|
-
// base: 'my-enhancement',
|
|
367
|
-
// branch: 'first-aspect',
|
|
368
|
-
// leaf: 'wow-this-is-deep',
|
|
369
|
-
// oldValue: null,
|
|
370
|
-
// newValue: 'good-bye'
|
|
363
|
+
// attrChangeInfo:[{
|
|
371
364
|
// idx: 0,
|
|
372
|
-
//
|
|
365
|
+
// oldValue: null,
|
|
366
|
+
// newValue: 'good-bye',
|
|
367
|
+
// parts:{
|
|
368
|
+
// name: 'data-my-enhancement-first-aspect-wow-this-is-deep'
|
|
369
|
+
// root: 'data',
|
|
370
|
+
// base: 'my-enhancement',
|
|
371
|
+
// branch: 'first-aspect',
|
|
372
|
+
// leaf: 'wow-this-is-deep',
|
|
373
|
+
// }
|
|
374
|
+
// }]
|
|
373
375
|
// }
|
|
374
376
|
});
|
|
375
377
|
mo.observe(div);
|
package/birtualizeMatch.js
CHANGED
package/getWhereAttrSelector.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
export function getWhereAttrSelector(whereAttr, withoutAttrs) {
|
|
2
2
|
const { hasBase, hasBranchIn, hasRootIn } = whereAttr;
|
|
3
|
+
const hasRootInGuaranteed = hasRootIn || [{
|
|
4
|
+
start: '',
|
|
5
|
+
context: 'Both'
|
|
6
|
+
}];
|
|
3
7
|
const fullListOfAttrs = [];
|
|
4
|
-
|
|
8
|
+
const partitionedAttrs = [];
|
|
9
|
+
let prefixLessMatches = [];
|
|
5
10
|
const hasBaseIsString = typeof hasBase === 'string';
|
|
6
11
|
const baseSelector = hasBaseIsString ? hasBase : hasBase[1];
|
|
7
12
|
const rootToBaseDelimiter = hasBaseIsString ? '-' : hasBase[0];
|
|
8
|
-
//end TODO
|
|
9
|
-
let prefixLessMatches = [baseSelector];
|
|
10
13
|
if (hasBranchIn !== undefined) {
|
|
11
14
|
let baseToBranchDelimiter = '-';
|
|
12
15
|
let branches;
|
|
@@ -17,19 +20,55 @@ export function getWhereAttrSelector(whereAttr, withoutAttrs) {
|
|
|
17
20
|
else {
|
|
18
21
|
branches = hasBranchIn;
|
|
19
22
|
}
|
|
20
|
-
prefixLessMatches = branches.map(x =>
|
|
23
|
+
prefixLessMatches = branches.map(x => ({
|
|
24
|
+
rootToBaseDelimiter,
|
|
25
|
+
base: baseSelector,
|
|
26
|
+
baseToBranchDelimiter,
|
|
27
|
+
branch: x
|
|
28
|
+
}));
|
|
21
29
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
else {
|
|
31
|
+
prefixLessMatches.push({
|
|
32
|
+
rootToBaseDelimiter,
|
|
33
|
+
base: baseSelector,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
for (const rootCnfg of hasRootInGuaranteed) {
|
|
37
|
+
const { start } = rootCnfg;
|
|
38
|
+
for (const match of prefixLessMatches) {
|
|
39
|
+
const { base, baseToBranchDelimiter, branch, rootToBaseDelimiter } = match;
|
|
40
|
+
for (const prefixLessMatch of prefixLessMatches) {
|
|
41
|
+
const { base, baseToBranchDelimiter, branch } = prefixLessMatch;
|
|
42
|
+
const startAndRootToBaseDelimiter = start ? `${start}${rootToBaseDelimiter}` : '';
|
|
43
|
+
if (branch) {
|
|
44
|
+
const name = `${startAndRootToBaseDelimiter}${base}${baseToBranchDelimiter}${branch}`;
|
|
45
|
+
fullListOfAttrs.push(name);
|
|
46
|
+
partitionedAttrs.push({
|
|
47
|
+
root: start,
|
|
48
|
+
name,
|
|
49
|
+
base,
|
|
50
|
+
branch,
|
|
51
|
+
rootCnfg
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const name = `${startAndRootToBaseDelimiter}${base}`;
|
|
56
|
+
fullListOfAttrs.push(name);
|
|
57
|
+
partitionedAttrs.push({
|
|
58
|
+
root: start,
|
|
59
|
+
name,
|
|
60
|
+
base,
|
|
61
|
+
rootCnfg
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
27
65
|
}
|
|
28
66
|
}
|
|
29
67
|
const listOfSelectors = fullListOfAttrs.map(s => `${withoutAttrs}[${s}]`);
|
|
30
68
|
const calculatedSelector = listOfSelectors.join(',');
|
|
31
69
|
return {
|
|
32
70
|
fullListOfAttrs,
|
|
33
|
-
calculatedSelector
|
|
71
|
+
calculatedSelector,
|
|
72
|
+
partitionedAttrs,
|
|
34
73
|
};
|
|
35
74
|
}
|
package/package.json
CHANGED
package/types.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export interface MountInit{
|
|
|
5
5
|
readonly whereAttr?: WhereAttr,
|
|
6
6
|
readonly whereElementIntersectsWith?: IntersectionObserverInit,
|
|
7
7
|
readonly whereMediaMatches?: MediaQuery,
|
|
8
|
-
readonly whereInstanceOf?: Array<
|
|
8
|
+
readonly whereInstanceOf?: Array<{new(): Element}>,
|
|
9
9
|
readonly whereSatisfies?: PipelineProcessor<boolean>,
|
|
10
10
|
readonly import?: ImportString | [ImportString, ImportAssertions] | PipelineProcessor,
|
|
11
11
|
readonly do?: {
|
|
@@ -21,13 +21,18 @@ export interface MountInit{
|
|
|
21
21
|
// */
|
|
22
22
|
// readonly ignoreInitialMatches?: boolean,
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
export interface RootCnfg{
|
|
26
|
+
start: string,
|
|
27
|
+
context: 'BuiltIn' | 'CustomElement' | 'Both'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
//export type RootAttrOptions = Array<string | RootCnfg>;
|
|
31
|
+
export type delimiter = string;
|
|
24
32
|
export interface WhereAttr{
|
|
25
|
-
hasBase: string | [
|
|
26
|
-
hasBranchIn?: Array<string> | [
|
|
27
|
-
hasRootIn?: Array<
|
|
28
|
-
path: string,
|
|
29
|
-
context: 'BuiltIn' | 'CustomElement' | 'Both'
|
|
30
|
-
}>,
|
|
33
|
+
hasBase: string | [delimiter, string],
|
|
34
|
+
hasBranchIn?: Array<string> | [delimiter, Array<string>],
|
|
35
|
+
hasRootIn?: Array<RootCnfg>,
|
|
31
36
|
}
|
|
32
37
|
type CSSMatch = string;
|
|
33
38
|
type ImportString = string;
|
|
@@ -70,15 +75,20 @@ export interface AddMutationEventListener {
|
|
|
70
75
|
}
|
|
71
76
|
//#endregion
|
|
72
77
|
|
|
73
|
-
interface
|
|
78
|
+
interface AttrParts{
|
|
74
79
|
name: string,
|
|
75
80
|
root?: string,
|
|
76
81
|
base?: string,
|
|
77
82
|
branch?: string,
|
|
78
83
|
leaf?: string, //TODO
|
|
84
|
+
rootCnfg?: RootCnfg
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
interface AttrChangeInfo{
|
|
79
88
|
oldValue: string | null,
|
|
80
89
|
newValue: string | null,
|
|
81
90
|
idx: number,
|
|
91
|
+
parts: AttrParts,
|
|
82
92
|
//parsedNewValue?: any,
|
|
83
93
|
}
|
|
84
94
|
|
|
@@ -123,7 +133,7 @@ export interface AddDisconnectEventListener {
|
|
|
123
133
|
//#region attribute change event
|
|
124
134
|
export type attrChangeEventName = 'attr-change';
|
|
125
135
|
export interface IAttrChangeEvent extends IMountEvent {
|
|
126
|
-
|
|
136
|
+
attrChangeInfos: Array<AttrChangeInfo>,
|
|
127
137
|
}
|
|
128
138
|
export type attrChangeEventHandler = (e: IAttrChangeEvent) => void;
|
|
129
139
|
export interface AddAttrChangeEventListener{
|
package/doWhereAttr.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { AttrChangeEvent } from "./MountObserver.js";
|
|
2
|
-
export function doWhereAttr(whereAttr, attributeName, target, oldValue, mo) {
|
|
3
|
-
const { hasRootIn, hasBase, hasBranchIn } = whereAttr;
|
|
4
|
-
const name = attributeName;
|
|
5
|
-
let restOfName = name;
|
|
6
|
-
let root;
|
|
7
|
-
let branch;
|
|
8
|
-
let idx = 0;
|
|
9
|
-
const hasBaseIsString = typeof hasBase === 'string';
|
|
10
|
-
const baseSelector = hasBaseIsString ? hasBase : hasBase[1];
|
|
11
|
-
const rootToBaseDelimiter = hasBaseIsString ? '-' : hasBase[0];
|
|
12
|
-
if (hasRootIn !== undefined) {
|
|
13
|
-
for (const rootTest in hasRootIn) {
|
|
14
|
-
if (restOfName.startsWith(rootTest)) {
|
|
15
|
-
root = rootTest;
|
|
16
|
-
restOfName = restOfName.substring(root.length + rootToBaseDelimiter.length);
|
|
17
|
-
break;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
if (!restOfName.startsWith(baseSelector))
|
|
22
|
-
return;
|
|
23
|
-
restOfName = restOfName.substring(hasBase.length);
|
|
24
|
-
if (hasBranchIn) {
|
|
25
|
-
for (const branchTest in hasBranchIn) {
|
|
26
|
-
if (restOfName.startsWith(branchTest)) {
|
|
27
|
-
branch = branchTest;
|
|
28
|
-
break;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
const newValue = target.getAttribute(attributeName);
|
|
33
|
-
const attrChangeInfo = {
|
|
34
|
-
name,
|
|
35
|
-
root,
|
|
36
|
-
base: baseSelector,
|
|
37
|
-
branch,
|
|
38
|
-
oldValue,
|
|
39
|
-
newValue,
|
|
40
|
-
idx
|
|
41
|
-
};
|
|
42
|
-
mo.dispatchEvent(new AttrChangeEvent(target, attrChangeInfo));
|
|
43
|
-
}
|