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 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 && this.#mounted.has(target)) {
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 > -1) {
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
- this.dispatchEvent(new AttrChangeEvent(target, attrChangeInfo));
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 { whereAttr } = this.#mountInit;
217
- for (const name of fullListOfAttrs) {
218
- if (whereAttr !== undefined) {
219
- const { doWhereAttr } = await import('./doWhereAttr.js');
220
- doWhereAttr(whereAttr, name, match, null, this);
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
- // let idx = 0;
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
- attrChangeInfo;
341
+ attrChangeInfos;
346
342
  static eventName = 'attr-change';
347
- constructor(mountedElement, attrChangeInfo) {
343
+ constructor(mountedElement, attrChangeInfos) {
348
344
  super(AttrChangeEvent.eventName);
349
345
  this.mountedElement = mountedElement;
350
- this.attrChangeInfo = attrChangeInfo;
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 provides:
287
+ Here's what the api **doesn't** provide:
288
288
 
289
- ## Option 1 -- The carpal syndrome syntax
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
- ## Option 2 -- The DRY Way
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);
@@ -66,7 +66,6 @@ export async function birtualizeMatch(self, el, level) {
66
66
  slot.removeAttribute('slot');
67
67
  }
68
68
  el.dispatchEvent(new LoadEvent(clone));
69
- //console.log('dispatched')
70
69
  }
71
70
  if (shadowRootModeOnLoad !== null) {
72
71
  const parent = el.parentElement;
@@ -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
- //TODO: share this block with doWhereAttr?
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 => `${baseSelector}${baseToBranchDelimiter}x`);
23
+ prefixLessMatches = branches.map(x => ({
24
+ rootToBaseDelimiter,
25
+ base: baseSelector,
26
+ baseToBranchDelimiter,
27
+ branch: x
28
+ }));
21
29
  }
22
- const stems = hasRootIn || [''];
23
- for (const stem of stems) {
24
- const prefix = typeof stem === 'string' ? stem : stem.path;
25
- for (const prefixLessMatch of prefixLessMatches) {
26
- fullListOfAttrs.push(prefix.length === 0 ? prefixLessMatch : `${prefix}${rootToBaseDelimiter}${prefixLessMatch}`);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mount-observer",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "description": "Observe and act on css matches.",
5
5
  "main": "MountObserver.js",
6
6
  "module": "MountObserver.js",
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<typeof Node>, //[TODO] What's the best way to type this?,
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 | [string, string],
26
- hasBranchIn?: Array<string> | [string, Array<string>],
27
- hasRootIn?: Array<string | {
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 AttrChangeInfo{
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
- attrChangeInfo: AttrChangeInfo,
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
- }