mount-observer 0.0.22 → 0.0.24

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
@@ -181,7 +181,8 @@ export class MountObserver extends EventTarget {
181
181
  }, { signal: this.#abortController.signal });
182
182
  await this.#inspectWithin(within, true);
183
183
  }
184
- synthesize(within, customElement, mose) {
184
+ static synthesize(within, customElement, mose) {
185
+ mose.type = 'mountobserver';
185
186
  const name = customElements.getName(customElement);
186
187
  if (name === null)
187
188
  throw 400;
package/README.md CHANGED
@@ -72,6 +72,8 @@ Invoking "disconnect" as shown above causes the observer to emit event "disconne
72
72
 
73
73
  The argument can also be an array of objects that fit the pattern shown above.
74
74
 
75
+ In fact, as we will see, where it makes sense, where we see examples that are strings, we will also allow for arrays of such strings. For example, the "on" key can point to an array of CSS selectors (and in this case the mount/dismount callbacks would need to provide an index of which one matched). I only recommend adding this complexity if what I suspect is true -- providing this support can reduce "context switching" between threads / memory spaces (c++ vs JavaScript), and thus improve performance.
76
+
75
77
  If no imports are specified, it would go straight to do.* (if any such callbacks are specified), and it will also dispatch events as discussed below.
76
78
 
77
79
  This only searches for elements matching 'my-element' outside any shadow DOM.
@@ -103,15 +105,15 @@ const observer = new MountObserver({
103
105
  observer.observe(document);
104
106
  ```
105
107
 
106
- Th key can accept either a single import or multiple (via an array).
108
+ Once again, the key can accept either a single import or it can also support multiple imports (via an array).
107
109
 
108
110
  The do event won't be invoked until all the imports have been successfully completed and inserted into the modules array.
109
111
 
110
- Previously, this proposal called for allowing arrow functions as well, thinking that could be a good interim way to support bundlers. But the valuable input provided by [doeixd](https://github.com/doeixd) makes me think that that interim support could just as effectively be done by the developer in the do methods.
112
+ Previously, this proposal called for allowing arrow functions as well, thinking that could be a good interim way to support bundlers, as well as multiple imports. But the valuable input provided by [doeixd](https://github.com/doeixd) makes me think that that interim support could more effectively be done by the developer in the do methods.
111
113
 
112
114
  This proposal would also include support for JSON and HTML module imports.
113
115
 
114
- ## MountObserver script element
116
+ ## MountObserver script elements (MOSEs)
115
117
 
116
118
  Following an approach similar to the [speculation api](https://developer.chrome.com/blog/speculation-rules-improvements), we can add a script element anywhere in the DOM:
117
119
 
@@ -161,7 +163,7 @@ If no id is found in the parent ShadowRoot (or in the parent window if the shado
161
163
 
162
164
  But if a matching id is found, then the values from the parent script element get merged in with the one in the child, with the child settings, including the event handling attributes.
163
165
 
164
- We will come back to some [additional features](#mountobserver-script-element-minutiae) of using these script elements later, but wanted to cover the highlights of this proposal before getting bogged down in some tedious logistics.
166
+ We will come back to some [additional features](#creating-frameworks-that-revolve-around-moses) of using these script elements later, but wanted to cover the highlights of this proposal before getting bogged down in some tedious logistics.
165
167
 
166
168
  ## Binding from a distance
167
169
 
@@ -323,7 +325,7 @@ The alternative to providing this feature, which I'm leaning towards, is to just
323
325
 
324
326
  ## A tribute to attributes
325
327
 
326
- Attributes of DOM elements are tricky. They've been around since the get-go, and they've survived multiple generations of the Web where different philosophies have prevailed, so prepare yourself for some subtle discussion in what follows.
328
+ Attributes of DOM elements are tricky. They've been around since the get-go of the Web, and they've survived multiple generations, where different philosophies have prevailed, so prepare yourself for some subtle discussion in what follows.
327
329
 
328
330
  Extra support is provided for monitoring attributes. There are two primary reasons for needing to provide special support for attributes with this API:
329
331
 
@@ -583,7 +585,6 @@ Tentative rules:
583
585
  The thinking here is that longer roots indicate higher "specificity", so it is safer to use that one.
584
586
 
585
587
 
586
-
587
588
  ## Preemptive downloading
588
589
 
589
590
  There are two significant steps to imports, each of which imposes a cost:
@@ -715,19 +716,29 @@ This proposal (and polyfill) also supports the option to utilize ShadowDOM / slo
715
716
 
716
717
  The discussion there leads to an open question whether a processing instruction would be better. I think the compose tag would make much more sense, vs a processing instruction, as it could then support slotted children (behaving similar to the Beatles' example above). Or maybe another tag should be introduced that is the equivalent of the slot, to avoid confusion. or some equivalent. But I strongly suspect that could significantly reduce the payload size of some documents, if we can reuse blocks of HTML, inserting sections of customized content for each instance.
717
718
 
718
- ## MountObserver script element minutiae
719
+ ## Creating "frameworks" that revolve around MOSEs.
720
+
721
+ Often, we will want to define a large number of "mount observer script elements (MOSEs)" programmatically, and we need it to be done in a generic way, that can be published and easily referenced.
719
722
 
720
- Often, we will want to define a large number of "mount observers" programmatically, and we need it to be done in a generic way. This is a problem space that [be-hive](https://github.com/bahrus/be-hive) is grappling with. In particular, we want to publish enhancements that take advantage of this inheritable infrastructure of declarative configuration, but we don't want to burden the developer with having to manually list all these configurations, we want it to happen automatically.
723
+ This is a problem space that [be-hive](https://github.com/bahrus/be-hive) is grappling with, and is used as an example for this section, to simply make things more concrete. But we can certainly envision other "frameworks" that could leverage this feature for a variety of purposes, including other families of behaviors/enhancements, or "binding from a distance" syntaxes.
724
+
725
+ In particular, *be-hive* supports publishing [enhancements](https://github.com/bahrus/be-enhanced) that take advantage of the DOM filtering ability that the MountObserver provides, that "ties the knot" based on CSS matches in the DOM to behaviors/enhancements that we want to attach directly onto the matching elements. *be-hive" seeks to take advantage of the inheritable infrastructure that MountObserver script elements provide, but we don't want to burden the developer with having to manually list all these configurations, we want it to happen automatically, only expecting manual intervention when we need some special customizations within a specific ShadowDOM scope.
721
726
 
722
727
  To support this, we propose these highlights:
723
728
 
724
- 1. Adding a "synthesize" method to the MountObserver api, only if observing a shadowRoot (or the top level document). This would provide a kind of passage way from the imperative api to the declarative one.
725
- 2. Synthesize method appends a script element of type MountObserver, that dispatches event from the synthesizing custom element it gets appended to, so subscribers don't need to add a general mutation observer in order to know when parent shadow roots had a MountObserver script tag inserted.
729
+ 1. Adding a static "synthesize" method to the MountObserver api. This would provide a kind of passage-way from the imperative api to the declarative one.
730
+ 2. As the Synthesize method is called repeatedly from different packages that work with that framework, it creates a cluster of MOSEs wrapped inside a custom element ("be-hive") that the framework developer authors. It appends script elements with type="mountobserver" to the custom element instance sitting in the DOM, that dispatches events from the synthesizing custom element it gets appended to, so subscribers in child Shadow DOM's don't need to add a general mutation observer in order to know when parent shadow roots had a MOSE inserted that it needs to act on. This allows the child Shadow DOM's to inherit (in this case) behaviors/enhancements from the parent Shadow DOM.
731
+
732
+ So framework developers can develop a bespoke custom element that inherits from the class "*Synthesizer*" that is part of this package / proposal, that is used to group families of MountObservers together.
726
733
 
734
+ What functionality do these "synthesizing" custom elements provide, what value-add proposition do they fulfill over what is built into the MountObserver polyfill / package?
727
735
 
728
- So developers can develop a custom element, used to group families of MountObservers together.
736
+ The sky is the limit, but focusing on the first example, be-hive, they are:
729
737
 
730
- If one inspects the DOM, one would see grouped (already "parsed") MountObservers, like so:
738
+ 1. Managing, interpreting and parsing the attributes that add semantic enhancement vocabularies onto exiting elements.
739
+ 2. Establishing the "handshake" that imports the enhancement package, instantiates the enhancement, and passes properties that wee previously assigned to the pre-enhanced element to the attached enhancement/behavior.
740
+
741
+ If one inspects the DOM, one will see grouped (already "parsed") MOSEs, like so:
731
742
 
732
743
  ```html
733
744
  <be-hive>
@@ -736,29 +747,19 @@ If one inspects the DOM, one would see grouped (already "parsed") MountObservers
736
747
  </be-hive>
737
748
  ```
738
749
 
739
- But the developer would not need to set these up automatically.
740
-
741
- Instead, the framework developer would define a custom element that inherits from base class that this proposal/polyfill provides.
750
+ Without the help of the synthesize method / Synthesizer base class, the developer would need to set these up manually, so this lifts a significant burden from the shoulders of people who want to leverage these behaviors/enhancements in a seamless way.
742
751
 
743
- Let's say the framework developer creates an extending Web Component with constructor: BeHive.
744
-
745
- Then rather than invoking:
752
+ The developer of each package defines their MOSE "template", and then syndicates it via the synthesize method:
746
753
 
747
754
  ```JavaScript
748
- mountObserver.observe(rootNode);
755
+ mountObserver.synthesize(rootNode, BeHive, mose)
749
756
  ```
750
757
 
751
- we would invoke:
758
+ What this method does is it:
752
759
 
753
- ```JavaScript
754
- mountObserver.synthesize(rootNode, BeHive, mountObserverScriptElement)
755
- ```
756
-
757
- The MountObserver api would:
758
-
759
- 1. Use [customElements.getName](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/getName) to get the name of the custom element (say it is 'be-hive').
760
- 2. Search for a be-hive tag inside the root node (with special logic for the "head" element). If not found, create it.
761
- 3. Place the script element inside.
760
+ 1. Uses [customElements.getName](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/getName) to get the name of the custom element (say it is 'be-hive') from the provided constructor.
761
+ 2. Searches for a be-hive tag inside the root node (with special logic for the "head" element). If not found, creates it.
762
+ 3. Places the MOSE inside.
762
763
 
763
764
 
764
765
  Then in our shadowroot, rather than adding a script type=mountobserver for every single mount observer we want to inherit, we could reference the group via simply:
@@ -767,5 +768,15 @@ Then in our shadowroot, rather than adding a script type=mountobserver for every
767
768
  <be-hive></be-hive>
768
769
  ```
769
770
 
771
+ And we can give each inheriting ShadowRoot a personality of its own by customizing the settings within that shadow scope, by (manually?) adding a MOSE with matching id that overrides the inheriting settings with custom settings:
770
772
 
773
+ ```html
774
+ <be-hive>
775
+ <script type=mountobserver id=be-searching>
776
+ {
777
+ ...my custom settings
778
+ }
779
+ </script>
780
+ </be-hive>
781
+ ```
771
782
 
package/Synthesizer.js CHANGED
@@ -6,12 +6,12 @@ export class Synthesizer extends HTMLElement {
6
6
  for (const mutation of mutationList) {
7
7
  const { addedNodes } = mutation;
8
8
  for (const node of addedNodes) {
9
- if (!(node instanceof HTMLScriptElement))
9
+ if (!(node instanceof HTMLScriptElement) || node.type !== 'mountobserver')
10
10
  continue;
11
11
  const mose = node;
12
12
  this.mountObserverElements.push(mose);
13
- this.#import(mose);
14
- const e = new SyntheticEvent(mose);
13
+ this.activate(mose);
14
+ const e = new SynthesizeEvent(mose);
15
15
  this.dispatchEvent(e);
16
16
  }
17
17
  }
@@ -21,25 +21,35 @@ export class Synthesizer extends HTMLElement {
21
21
  const init = {
22
22
  childList: true
23
23
  };
24
- this.#mutationObserver = new MutationObserver(this.mutationCallback);
25
- this.#mutationObserver.observe(this.getRootNode());
26
- this.#inherit();
24
+ this.querySelectorAll('script[type="mountobserver"]').forEach(s => {
25
+ const mose = s;
26
+ this.mountObserverElements.push(mose);
27
+ this.activate(mose);
28
+ });
29
+ this.#mutationObserver = new MutationObserver(this.mutationCallback.bind(this));
30
+ this.#mutationObserver.observe(this, init);
31
+ this.inherit();
27
32
  }
28
- #import(mose) {
33
+ activate(mose) {
29
34
  const { init, do: d, id } = mose;
30
- const se = document.createElement('script');
31
- se.init = init;
32
- se.id = id;
33
- se.do = d;
34
35
  const mi = {
35
36
  do: d,
36
37
  ...init
37
38
  };
38
39
  const mo = new MountObserver(mi);
39
- se.observer = mo;
40
+ mose.observer = mo;
41
+ mo.observe(this.getRootNode());
42
+ }
43
+ import(mose) {
44
+ const { init, do: d, id, synConfig } = mose;
45
+ const se = document.createElement('script');
46
+ se.init = { ...init };
47
+ se.id = id;
48
+ se.do = { ...d };
49
+ se.synConfig = { ...synConfig };
40
50
  this.appendChild(se);
41
51
  }
42
- #inherit() {
52
+ inherit() {
43
53
  const rn = this.getRootNode();
44
54
  const host = rn.host;
45
55
  if (!host)
@@ -49,11 +59,11 @@ export class Synthesizer extends HTMLElement {
49
59
  const parentScopeSynthesizer = parentShadowRealm.querySelector(localName);
50
60
  const { mountObserverElements } = parentScopeSynthesizer;
51
61
  for (const moe of mountObserverElements) {
52
- this.#import(moe);
62
+ this.import(moe);
53
63
  }
54
64
  if (parentScopeSynthesizer !== null) {
55
- parentScopeSynthesizer.addEventListener(SyntheticEvent.eventName, e => {
56
- this.#import(e.mountObserverElement);
65
+ parentScopeSynthesizer.addEventListener(SynthesizeEvent.eventName, e => {
66
+ this.import(e.mountObserverElement);
57
67
  });
58
68
  }
59
69
  }
@@ -61,6 +71,9 @@ export class Synthesizer extends HTMLElement {
61
71
  if (this.#mutationObserver !== undefined) {
62
72
  this.#mutationObserver.disconnect();
63
73
  }
74
+ for (const mose of this.mountObserverElements) {
75
+ mose.observer.disconnect(this.getRootNode());
76
+ }
64
77
  }
65
78
  }
66
79
  // https://github.com/webcomponents-cg/community-protocols/issues/12#issuecomment-872415080
@@ -68,11 +81,11 @@ export class Synthesizer extends HTMLElement {
68
81
  * The `mutation-event` event represents something that happened.
69
82
  * We can document it here.
70
83
  */
71
- export class SyntheticEvent extends Event {
84
+ export class SynthesizeEvent extends Event {
72
85
  mountObserverElement;
73
86
  static eventName = 'synthesize';
74
87
  constructor(mountObserverElement) {
75
- super(SyntheticEvent.eventName);
88
+ super(SynthesizeEvent.eventName);
76
89
  this.mountObserverElement = mountObserverElement;
77
90
  }
78
91
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mount-observer",
3
- "version": "0.0.22",
3
+ "version": "0.0.24",
4
4
  "description": "Observe and act on css matches.",
5
5
  "main": "MountObserver.js",
6
6
  "module": "MountObserver.js",
package/types.d.ts CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  export interface JSONSerializableMountInit{
4
4
  readonly on?: CSSMatch,
5
+ observedAttrsWhenMounted?: string[],
5
6
  readonly whereAttr?: WhereAttr,
6
7
  readonly whereElementIntersectsWith?: IntersectionObserverInit,
7
8
  readonly whereMediaMatches?: MediaQuery,
@@ -37,7 +38,7 @@ export interface RootCnfg{
37
38
  //export type RootAttrOptions = Array<string | RootCnfg>;
38
39
  export type delimiter = string;
39
40
  export interface WhereAttr{
40
- isIn?: Array<string>,
41
+
41
42
  hasBase?: string | [delimiter, string],
42
43
  hasBranchIn?: Array<string> | [delimiter, Array<string>],
43
44
  hasRootIn?: Array<RootCnfg>,
@@ -67,6 +68,7 @@ export interface IMountObserver {
67
68
  disconnect(within: Node): void;
68
69
  module?: any;
69
70
  mountedElements: WeakSet<Element>;
71
+ readAttrs(match: Element, branchIndexes?: Set<number>) : AttrChangeInfo[]
70
72
  }
71
73
 
72
74
  export interface MountContext{
@@ -170,11 +172,15 @@ export interface AddLoadEventListener{
170
172
  //#endregion
171
173
 
172
174
  //#region MountObserver Script Element
173
- export interface MountObserverScriptElement extends HTMLScriptElement{
175
+ export interface MOSEAddedProps<TSynConfig=any>{
174
176
  init: JSONSerializableMountInit;
175
- //mountedElements: Array<WeakRef<Element>>;
176
177
  observer: IMountObserver;
177
178
  do: MountObserverCallbacks;
179
+ synConfig: TSynConfig;
180
+ }
181
+ export interface MOSE<TSynConfig=any>
182
+ extends HTMLScriptElement, MOSEAddedProps<TSynConfig>{
183
+
178
184
  }
179
185
  //#endregion
180
186