mount-observer 0.0.7 → 0.0.8

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
@@ -28,25 +28,20 @@ export class MountObserver extends EventTarget {
28
28
  //this.#unmounted = new WeakSet();
29
29
  }
30
30
  #calculatedSelector;
31
- get #selector() {
31
+ #fullListOfAttrs;
32
+ //get #attrVals
33
+ async #selector() {
32
34
  if (this.#calculatedSelector !== undefined)
33
35
  return this.#calculatedSelector;
34
36
  const { on, whereAttr } = this.#mountInit;
35
- const base = on || '*';
37
+ const withoutAttrs = on || '*';
36
38
  if (whereAttr === undefined)
37
- return base;
38
- const { withFirstName, andQualifiers, withStemsIn } = whereAttr;
39
- const matches = [];
40
- const prefixLessMatches = andQualifiers === undefined ? [withFirstName]
41
- : andQualifiers.map(x => `${withFirstName}-${x}`);
42
- const stems = withStemsIn || ['data', 'enh', 'data-enh'];
43
- for (const stem of stems) {
44
- const prefix = typeof stem === 'string' ? stem : stem.stem;
45
- for (const prefixLessMatch of prefixLessMatches) {
46
- matches.push(`${prefix}-${prefixLessMatch}`);
47
- }
48
- }
49
- this.#calculatedSelector = matches.join(',');
39
+ return withoutAttrs;
40
+ const { getWhereAttrSelector } = await import('./getWhereAttrSelector.js');
41
+ const info = getWhereAttrSelector(whereAttr, withoutAttrs);
42
+ const { fullListOfAttrs, calculatedSelector } = info;
43
+ this.#fullListOfAttrs = fullListOfAttrs;
44
+ this.#calculatedSelector = calculatedSelector;
50
45
  return this.#calculatedSelector;
51
46
  }
52
47
  unobserve(within) {
@@ -90,8 +85,9 @@ export class MountObserver extends EventTarget {
90
85
  }
91
86
  }
92
87
  const rootMutObs = mutationObserverLookup.get(within);
93
- const { attribMatches } = this.#mountInit;
94
- rootMutObs.addEventListener('mutation-event', (e) => {
88
+ //const {whereAttr} = this.#mountInit;
89
+ const fullListOfAttrs = this.#fullListOfAttrs;
90
+ rootMutObs.addEventListener('mutation-event', async (e) => {
95
91
  //TODO: disconnected
96
92
  if (this.#isComplex) {
97
93
  this.#inspectWithin(within, false);
@@ -108,22 +104,11 @@ export class MountObserver extends EventTarget {
108
104
  addedElements.forEach(x => elsToInspect.push(x));
109
105
  if (type === 'attributes') {
110
106
  const { target, attributeName, oldValue } = mutationRecord;
111
- if (target instanceof Element && attributeName !== null && attribMatches !== undefined && this.#mounted.has(target)) {
112
- let idx = 0;
113
- for (const attrMatch of attribMatches) {
114
- const { names } = attrMatch;
115
- if (names.includes(attributeName)) {
107
+ if (target instanceof Element && attributeName !== null && this.#mounted.has(target)) {
108
+ if (fullListOfAttrs !== undefined) {
109
+ const idx = fullListOfAttrs.indexOf(attributeName);
110
+ if (idx > -1) {
116
111
  const newValue = target.getAttribute(attributeName);
117
- // let parsedNewValue = undefined;
118
- // switch(type){
119
- // case 'boolean':
120
- // parsedNewValue = newValue === 'true' ? true : newValue === 'false' ? false : null;
121
- // break;
122
- // case 'date':
123
- // parsedNewValue = newValue === null ? null : new Date(newValue);
124
- // break;
125
- // case ''
126
- // }
127
112
  const attrChangeInfo = {
128
113
  name: attributeName,
129
114
  oldValue,
@@ -132,7 +117,13 @@ export class MountObserver extends EventTarget {
132
117
  };
133
118
  this.dispatchEvent(new AttrChangeEvent(target, attrChangeInfo));
134
119
  }
135
- idx++;
120
+ }
121
+ else {
122
+ const { whereAttr } = this.#mountInit;
123
+ if (whereAttr !== undefined) {
124
+ const { doWhereAttr } = await import('./doWhereAttr.js');
125
+ doWhereAttr(whereAttr, attributeName, target, oldValue, this);
126
+ }
136
127
  }
137
128
  }
138
129
  elsToInspect.push(target);
@@ -164,9 +155,10 @@ export class MountObserver extends EventTarget {
164
155
  }
165
156
  async #mount(matching, initializing) {
166
157
  //first unmount non matching
167
- const alreadyMounted = this.#filterAndDismount();
158
+ const alreadyMounted = await this.#filterAndDismount();
168
159
  const mount = this.#mountInit.do?.mount;
169
- const { import: imp, attribMatches } = this.#mountInit;
160
+ const { import: imp } = this.#mountInit;
161
+ const fullListOfAttrs = this.#fullListOfAttrs;
170
162
  for (const match of matching) {
171
163
  if (alreadyMounted.has(match))
172
164
  continue;
@@ -197,27 +189,33 @@ export class MountObserver extends EventTarget {
197
189
  });
198
190
  }
199
191
  this.dispatchEvent(new MountEvent(match, initializing));
200
- if (attribMatches !== undefined) {
201
- let idx = 0;
202
- for (const attribMatch of attribMatches) {
203
- let newValue = null;
204
- const { names } = attribMatch;
205
- let nonNullName = names[0];
206
- for (const name of names) {
207
- const attrVal = match.getAttribute(name);
208
- if (attrVal !== null)
209
- nonNullName = name;
210
- newValue = newValue || attrVal;
192
+ if (fullListOfAttrs !== undefined) {
193
+ const { whereAttr } = this.#mountInit;
194
+ for (const name of fullListOfAttrs) {
195
+ if (whereAttr !== undefined) {
196
+ const { doWhereAttr } = await import('./doWhereAttr.js');
197
+ doWhereAttr(whereAttr, name, match, null, this);
211
198
  }
212
- const attribInfo = {
213
- oldValue: null,
214
- newValue,
215
- idx,
216
- name: nonNullName
217
- };
218
- this.dispatchEvent(new AttrChangeEvent(match, attribInfo));
219
- idx++;
220
199
  }
200
+ // let idx = 0;
201
+ // for(const attribMatch of attribMatches){
202
+ // let newValue = null;
203
+ // const {names} = attribMatch;
204
+ // let nonNullName = names[0];
205
+ // for(const name of names){
206
+ // const attrVal = match.getAttribute(name);
207
+ // if(attrVal !== null) nonNullName = name;
208
+ // newValue = newValue || attrVal;
209
+ // }
210
+ // const attribInfo: AttrChangeInfo = {
211
+ // oldValue: null,
212
+ // newValue,
213
+ // idx,
214
+ // name: nonNullName
215
+ // };
216
+ // this.dispatchEvent(new AttrChangeEvent(match, attribInfo));
217
+ // idx++;
218
+ // }
221
219
  }
222
220
  this.#mountedList?.push(new WeakRef(match));
223
221
  //if(this.#unmounted.has(match)) this.#unmounted.delete(match);
@@ -232,12 +230,12 @@ export class MountObserver extends EventTarget {
232
230
  this.dispatchEvent(new DismountEvent(unmatch));
233
231
  }
234
232
  }
235
- #filterAndDismount() {
233
+ async #filterAndDismount() {
236
234
  const returnSet = new Set();
237
235
  if (this.#mountedList !== undefined) {
238
236
  const previouslyMounted = this.#mountedList.map(x => x.deref());
239
237
  const { whereSatisfies, whereInstanceOf } = this.#mountInit;
240
- const match = this.#selector;
238
+ const match = await this.#selector();
241
239
  const elsToUnMount = previouslyMounted.filter(x => {
242
240
  if (x === undefined)
243
241
  return false;
@@ -257,7 +255,7 @@ export class MountObserver extends EventTarget {
257
255
  }
258
256
  async #filterAndMount(els, checkMatch, initializing) {
259
257
  const { whereSatisfies, whereInstanceOf } = this.#mountInit;
260
- const match = this.#selector;
258
+ const match = await this.#selector();
261
259
  const elsToMount = els.filter(x => {
262
260
  if (checkMatch) {
263
261
  if (!x.matches(match))
@@ -276,7 +274,7 @@ export class MountObserver extends EventTarget {
276
274
  this.#mount(elsToMount, initializing);
277
275
  }
278
276
  async #inspectWithin(within, initializing) {
279
- const els = Array.from(within.querySelectorAll(this.#selector));
277
+ const els = Array.from(within.querySelectorAll(await this.#selector()));
280
278
  this.#filterAndMount(els, false, initializing);
281
279
  }
282
280
  }
@@ -322,3 +320,4 @@ export class AttrChangeEvent extends Event {
322
320
  this.attrChangeInfo = attrChangeInfo;
323
321
  }
324
322
  }
323
+ //const hasRootInDefault = ['data', 'enh', 'data-enh']
package/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![How big is this package in your project?](https://img.shields.io/bundlephobia/minzip/mount-observer?style=for-the-badge)](https://bundlephobia.com/result?p=mount-observer)
4
4
  <img src="http://img.badgesize.io/https://cdn.jsdelivr.net/npm/mount-observer?compression=gzip">
5
5
 
6
+ Note that much of what is described below has not yet been polyfilled.
6
7
 
7
8
  # The MountObserver api.
8
9
 
@@ -10,7 +11,7 @@ Author: Bruce B. Anderson
10
11
 
11
12
  Issues / pr's / polyfill: [mount-observer](https://github.com/bahrus/mount-observer)
12
13
 
13
- Last Update: 2024-2-11
14
+ Last Update: 2024-2-14
14
15
 
15
16
  ## Benefits of this API
16
17
 
@@ -24,7 +25,7 @@ The underlying theme is this api is meant to make it easy for the developer to d
24
25
 
25
26
  There is quite a bit of functionality this proposal would open up, that is exceedingly difficult to polyfill reliably:
26
27
 
27
- 1. It is unclear how to use mutation observers to observe changes to [custom state](https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet). The closest thing might be a solution like [this](https://davidwalsh.name/detect-node-insertion), but that falls short for elements that aren't visible, or during template instantiation.
28
+ 1. It is unclear how to use mutation observers to observe changes to [custom state](https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet). The closest thing might be a solution like [this](https://davidwalsh.name/detect-node-insertion), but that falls short for elements that aren't visible, or during template instantiation, and requires carefully constructed "negating" queries if needing to know when the css selector is no longer matching.
28
29
 
29
30
  2. For simple css matches, like "my-element", or "[name='hello']" it is enough to use a mutation observer, and only observe the elements within the specified DOM region (more on that below). But as CSS has evolved, it is quite easy to think of numerous css selectors that would require us to expand our mutation observer to need to scan the entire Shadow DOM realm, or the entire DOM tree outside any Shadow DOM, for any and all mutations (including attribute changes), and re-evaluate every single element within the specified DOM region for new matches or old matches that no longer match. Things like child selectors, :has, and so on. All this is done, miraculously, by the browser in a performant way. Reproducing this in userland using JavaScript alone, matching the same performance seems impossible.
30
31
 
@@ -38,7 +39,7 @@ The amount of code necessary to accomplish these common tasks designed to improv
38
39
  1. Making lazy loading of resource dependencies easy, to the benefit of users with expensive networks.
39
40
  2. Supporting "binding from a distance" that can set property values of elements in bulk as the HTML streams in. For example, say a web page is streaming in HTML with thousands of input elements (say a long tax form). We want to have some indication in the head tag of the HTML (for example) to make all the input elements read only as they stream through the page. With css, we could do similar things, for example set the background to red of all input elements. Why can't we do something similar with setting properties like readOnly, disabled, etc? With this api, giving developers the "keys" to css filtering, so they can "mount a campaign" to apply common settings on them all feels like something that almost every web developer has mentally screamed to themselves "why can't I do that?", doesn't it?
40
41
  3. Supporting "progressive enhancement" more effectively.
41
- 2. Potentially by allowing the platform to do more work in the low-level (c/c++/rust?) code, without as much context switching into the JavaScript memory space, which may reduce cpu cycles as well. This is done by passing into the API substantial number of conditions, which can all be evaluated at a lower level, before surfacing up the developer "found one!".
42
+ 2. Potentially by allowing the platform to do more work in the low-level (c/c++/rust?) code, without as much context switching into the JavaScript memory space, which may reduce cpu cycles as well. This is done by passing into the API substantial number of conditions, which can all be evaluated at a lower level, before the api needs to surface up to the developer "found one!".
42
43
  3. As discussed earlier, to do the job right, polyfills really need to reexamine **all** the elements within the observed node for matches **anytime any element within the Shadow Root so much as sneezes (has attribute modified, changes custom state, etc)**, due to modern selectors such as the :has selector. Surely, the platform has found ways to do this more efficiently?
43
44
 
44
45
  The extra flexibility this new primitive would provide could be quite useful to things other than lazy loading of custom elements, such as implementing [custom enhancements](https://github.com/WICG/webcomponents/issues/1000) as well as [binding from a distance](https://github.com/WICG/webcomponents/issues/1035#issuecomment-1806393525) in userland.
@@ -100,6 +101,8 @@ const observer = new MountObserver({
100
101
  })
101
102
  ```
102
103
 
104
+ ... would work.
105
+
103
106
  This would allow developers to create "stylesheet" like capabilities.
104
107
 
105
108
 
@@ -206,7 +209,7 @@ Being that for both custom elements, as well as (hopefully) [custom enhancements
206
209
 
207
210
  We want to be alerted by the discovery of elements adorned by these attributes, but then continue to be alerted to changes of their values, and we can't enumerate which values we are interested in, so we must subscribe to all values as they change.
208
211
 
209
- ### Scenario 1 -- Custom Element integration with ObserveObservableAttributes API [WIP]
212
+ ### Scenario 1 -- Custom Element integration with ObserveObservedAttributes API [WIP]
210
213
 
211
214
  Example:
212
215
 
@@ -247,6 +250,8 @@ To make this discussion concrete, let's suppose the "canonical" names of those a
247
250
  my-enhancement=greetings
248
251
  my-enhancement-first-aspect=hello
249
252
  my-enhancement-second-aspect=goodbye
253
+ my-enhancement-first-aspect-wow-this-is-deep
254
+ my-enhancement-first-aspect-have-you-considered-using-json-for-this=just-saying
250
255
  ></section>
251
256
  </div>
252
257
  ```
@@ -263,17 +268,19 @@ We want to also support:
263
268
  data-my-enhancement=greetings
264
269
  data-my-enhancement-first-aspect=hello
265
270
  data-my-enhancement-second-aspect=goodbye
271
+ data-my-enhancement-first-aspect-wow-this-is-deep
272
+ data-my-enhancement-first-aspect-have-you-considered-using-json-for-this=just-saying
266
273
  ></section>
267
274
  </div>
268
275
  ```
269
276
 
270
277
  Based on the current unspoken rules, no one will raise an eyebrow with these attributes, because the platform has indicated it will generally avoid dashes in attributes (with an exception or two that will only happen in a blue moon, like aria-*).
271
278
 
272
- But now when we consider applying this enhancement to custom elements, we have a new risk. What's to prevent the custom element from having an attribute named my-enhancement-first-aspect? (Okay, with this particular example, the names are so long and generic it's unlikely, but who would ever use such a long, generic name in practice?)
279
+ But now when we consider applying this enhancement to custom elements, we have a new risk. What's to prevent the custom element from having an attribute named my-enhancement?
273
280
 
274
- So let's say we want to insist that on custom elements, we must have the dat- prefix?
281
+ So let's say we want to insist that on custom elements, we must have the data- prefix?
275
282
 
276
- And we want to support an alternative prefix to data, say enh-*, endorsed by [this proposal](https://github.com/WICG/webcomponents/issues/1000).
283
+ 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).
277
284
 
278
285
  Here's what the api provides:
279
286
 
@@ -291,6 +298,7 @@ const mo = new MountObserver({
291
298
  'enh-my-enhancement',
292
299
  'enh-my-enhancement-first-aspect',
293
300
  'enh-my-enhancement-second-aspect',
301
+ //...some ten more combinations not listed
294
302
  {
295
303
  name: 'my-enhancement',
296
304
  builtIn: true
@@ -302,7 +310,8 @@ const mo = new MountObserver({
302
310
  {
303
311
  name: 'my-enhancement-second-aspect',
304
312
  builtIn: true
305
- }
313
+ },
314
+ ...
306
315
  ]
307
316
 
308
317
  }
@@ -316,30 +325,33 @@ import {MountObserver} from '../MountObserver.js';
316
325
  const mo = new MountObserver({
317
326
  on: '*',
318
327
  whereAttr:{
319
- withFirstName: 'my-enhancement',
320
- andQualifiers: ['first-attr', 'second-attr', ''],
321
- withStemsIn: ['data', 'enh', 'data-enh'],
328
+ hasRootIn: ['data', 'enh', 'data-enh'],
329
+ hasBase: 'my-enhancement',
330
+ hasBranchIn: ['first-aspect', 'second-aspect', ''],
331
+ hasLeafIn: {
332
+ 'first-aspect': ['wow-this-is-deep', 'have-you-considered-using-json-for-this'],
333
+ }
322
334
  }
323
335
  });
324
336
  ```
325
337
 
326
- MountObserver supports both approaches
338
+ MountObserver provides a breakdown of the matching attribute when encountered:
327
339
 
328
340
  ```html
329
341
  <div id=div>
330
- <section class=hello my-enhancement-first-attr="hello"></section>
342
+ <section class=hello my-enhancement-first-attr-wow-this-is-deep="hello"></section>
331
343
  </div>
332
344
  <script type=module>
333
345
  import {MountObserver} from '../MountObserver.js';
334
346
  const mo = new MountObserver({
335
347
  on: '*',
336
348
  whereAttr:{
337
- hasPrefixesIn: ['enh-', 'data-enh-'],
338
- hasSuffixesIn:[
339
- 'my-enhancement-first-attr',
340
- 'my-enhancement-second-aspect'
341
- ],
342
-
349
+ hasRootIn: ['data', 'enh', 'data-enh'],
350
+ hasBase: 'my-enhancement',
351
+ hasBranchIn: ['first-attr', 'second-attr', ''],
352
+ hasLeafIn: {
353
+ 'first-attr': ['wow-this-is-deep', 'have-you-considered-using-json-for-this'],
354
+ }
343
355
  }
344
356
  });
345
357
  mo.addEventListener('observed-attr-change', e => {
@@ -347,10 +359,13 @@ MountObserver supports both approaches
347
359
  // {
348
360
  // matchingElement,
349
361
  // attrChangeInfo:{
350
- // fullName: 'data-enh-my-first-enhancement-attr',
351
- // suffix: 'my-first-enhancement-attr',
362
+ // name: 'data-my-enhancement-first-aspect-wow-this-is-deep'
363
+ // root: 'data',
364
+ // base: 'my-enhancement',
365
+ // branch: 'first-attr',
366
+ // leaf: 'wow-this-is-deep',
352
367
  // oldValue: null,
353
- // newValue: 'hello'
368
+ // newValue: 'good-bye'
354
369
  // idx: 0,
355
370
  // }
356
371
  // }
@@ -358,11 +373,44 @@ MountObserver supports both approaches
358
373
  mo.observe(div);
359
374
  setTimeout(() => {
360
375
  const myCustomElement = document.querySelector('my-custom-element');
361
- myCustomElement.setAttribute('my-first-observed-attribute', 'good-bye');
376
+ myCustomElement.setAttribute('data-my-enhancement-first-aspect-wow-this-is-deep', 'good-bye');
362
377
  }, 1000);
363
378
  </script>
364
379
  ```
365
380
 
381
+ Some libraries prefer to use the colon (:) rather than a dash to separate these levels of settings:
382
+
383
+ Possibly some libraries may prefer to mix it up a bit:
384
+
385
+
386
+ ```html
387
+ <div id=div>
388
+ <section class=hello
389
+ data-my-enhancement=greetings
390
+ data-my-enhancement:first-aspect=hello
391
+ data-my-enhancement:second-aspect=goodbye
392
+ data-my-enhancement:first-aspect--wow-this-is-deep
393
+ data-my-enhancement:first-aspect--have-you-considered-using-json-for-this=just-saying
394
+ ></section>
395
+ </div>
396
+ ```
397
+
398
+ To support, specify the delimiter thusly:
399
+
400
+ ```JavaScript
401
+ const mo = new MountObserver({
402
+ on: '*',
403
+ whereAttr:{
404
+ hasRootIn: ['data', 'enh', 'data-enh'],
405
+ hasBase: ['-', 'my-enhancement'],
406
+ hasBranchIn: [':', ['first-attr', 'second-attr', '']],
407
+ hasLeafIn: {
408
+ 'first-attr': ['--', ['wow-this-is-deep', 'have-you-considered-using-json-for-this']],
409
+ }
410
+ }
411
+ });
412
+ ```
413
+
366
414
  ## Preemptive downloading
367
415
 
368
416
  There are two significant steps to imports, each of which imposes a cost:
@@ -389,4 +437,3 @@ const observer = new MountObserver({
389
437
 
390
438
  So what this does is only check for the presence of an element with tag name "my-element", and it starts downloading the resource, even before the element has "mounted" based on other criteria.
391
439
 
392
-
package/doWhereAttr.js ADDED
@@ -0,0 +1,43 @@
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
+ }
@@ -0,0 +1,35 @@
1
+ export function getWhereAttrSelector(whereAttr, withoutAttrs) {
2
+ const { hasBase, hasBranchIn, hasRootIn } = whereAttr;
3
+ const fullListOfAttrs = [];
4
+ //TODO: share this block with doWhereAttr?
5
+ const hasBaseIsString = typeof hasBase === 'string';
6
+ const baseSelector = hasBaseIsString ? hasBase : hasBase[1];
7
+ const rootToBaseDelimiter = hasBaseIsString ? '-' : hasBase[0];
8
+ //end TODO
9
+ let prefixLessMatches = [baseSelector];
10
+ if (hasBranchIn !== undefined) {
11
+ let baseToBranchDelimiter = '-';
12
+ let branches;
13
+ if (hasBranchIn.length === 2 && Array.isArray(hasBranchIn[1])) {
14
+ baseToBranchDelimiter = hasBranchIn[0];
15
+ branches = hasBranchIn[1];
16
+ }
17
+ else {
18
+ branches = hasBranchIn;
19
+ }
20
+ prefixLessMatches = branches.map(x => `${baseSelector}${baseToBranchDelimiter}x`);
21
+ }
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}`);
27
+ }
28
+ }
29
+ const listOfSelectors = fullListOfAttrs.map(s => `${withoutAttrs}[${s}]`);
30
+ const calculatedSelector = listOfSelectors.join(',');
31
+ return {
32
+ fullListOfAttrs,
33
+ calculatedSelector
34
+ };
35
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mount-observer",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "Observe and act on css matches.",
5
5
  "main": "MountObserver.js",
6
6
  "module": "MountObserver.js",
package/types.d.ts CHANGED
@@ -1,14 +1,7 @@
1
1
  export interface MountInit{
2
2
  readonly on?: CSSMatch,
3
- readonly attribMatches?: Array<AttribMatch>,
4
- readonly whereAttr?: {
5
- withFirstName: string,
6
- andQualifiers?: Array<string>,
7
- withStemsIn?: Array<string | {
8
- stem: string,
9
- context: 'BuiltIn' | 'CustomElement'
10
- }>,
11
- },
3
+ //readonly attribMatches?: Array<AttribMatch>,
4
+ readonly whereAttr?: WhereAttr,
12
5
  readonly whereElementIntersectsWith?: IntersectionObserverInit,
13
6
  readonly whereMediaMatches?: MediaQuery,
14
7
  readonly whereInstanceOf?: Array<typeof Node>, //[TODO] What's the best way to type this?,
@@ -27,6 +20,14 @@ export interface MountInit{
27
20
  // */
28
21
  // readonly ignoreInitialMatches?: boolean,
29
22
  }
23
+ export interface WhereAttr{
24
+ hasBase: string | [string, string],
25
+ hasBranchIn?: Array<string> | [string, Array<string>],
26
+ hasRootIn?: Array<string | {
27
+ path: string,
28
+ context: 'BuiltIn' | 'CustomElement' | 'Both'
29
+ }>,
30
+ }
30
31
  type CSSMatch = string;
31
32
  type ImportString = string;
32
33
  type MediaQuery = string;
@@ -70,6 +71,10 @@ export interface AddMutationEventListener {
70
71
 
71
72
  interface AttrChangeInfo{
72
73
  name: string,
74
+ root?: string,
75
+ base?: string,
76
+ branch?: string,
77
+ leaf?: string, //TODO
73
78
  oldValue: string | null,
74
79
  newValue: string | null,
75
80
  idx: number,