mount-observer 0.0.78 → 0.0.79

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
@@ -63,7 +63,12 @@ export class MountObserver extends EventTarget {
63
63
  async composeFragment(fragment, level) {
64
64
  const bis = fragment.querySelectorAll(`${inclTemplQry}`);
65
65
  for (const bi of bis) {
66
- await this.#compose(bi, level);
66
+ if (bi.getAttribute('rel') === 'preload') {
67
+ (await import('./preloadContent.js')).preloadContent(bi, this.#mountInit.withTargetShadowRoot);
68
+ }
69
+ else {
70
+ await this.#compose(bi, level);
71
+ }
67
72
  }
68
73
  }
69
74
  async #compose(el, level) {
@@ -480,7 +485,12 @@ export class MountObserver extends EventTarget {
480
485
  });
481
486
  for (const elToMount of elsToMount) {
482
487
  if (elToMount.matches(inclTemplQry)) {
483
- await this.#compose(elToMount, 0);
488
+ if (elToMount instanceof HTMLTemplateElement && elToMount.getAttribute('rel') === 'preload') {
489
+ (await import('./preloadContent.js')).preloadContent(elToMount, this.#mountInit.withTargetShadowRoot);
490
+ }
491
+ else {
492
+ await this.#compose(elToMount, 0);
493
+ }
484
494
  }
485
495
  }
486
496
  await bindishIt(els, target, { assigner });
package/MountObserver.ts CHANGED
@@ -76,7 +76,12 @@ export class MountObserver extends EventTarget implements IMountObserver{
76
76
  async composeFragment(fragment: DocumentFragment, level: number){
77
77
  const bis = fragment.querySelectorAll(`${inclTemplQry}`) as NodeListOf<HTMLTemplateElement>;
78
78
  for(const bi of bis){
79
- await this.#compose(bi, level);
79
+ if(bi.getAttribute('rel') === 'preload'){
80
+ (await import('./preloadContent.js')).preloadContent(bi, this.#mountInit.withTargetShadowRoot);
81
+ }else{
82
+ await this.#compose(bi, level);
83
+ }
84
+
80
85
  }
81
86
  }
82
87
 
@@ -509,7 +514,12 @@ export class MountObserver extends EventTarget implements IMountObserver{
509
514
  });
510
515
  for(const elToMount of elsToMount){
511
516
  if(elToMount.matches(inclTemplQry)){
512
- await this.#compose(elToMount as HTMLTemplateElement, 0)
517
+ if(elToMount instanceof HTMLTemplateElement && elToMount.getAttribute('rel') === 'preload'){
518
+ (await import('./preloadContent.js')).preloadContent(elToMount, this.#mountInit.withTargetShadowRoot);
519
+ }else{
520
+ await this.#compose(elToMount as HTMLTemplateElement, 0);
521
+ }
522
+
513
523
  }
514
524
 
515
525
  }
package/README.md CHANGED
@@ -819,6 +819,15 @@ For example:
819
819
  <div>Strawberry Fields Forever</div>
820
820
  ```
821
821
 
822
+ Optionally, a rel=stream attribute can be specified. Other values of the attribute will result in different behavior from what is described below:
823
+
824
+ ```html
825
+ <template rel=stream src=#id-of-source-template>
826
+ <span part=greeting>hello</span>
827
+ <span part=parting>goodbye<span>
828
+ </template>
829
+ ```
830
+
822
831
  When it encounters such a thing, it searches "upwardly" through the chain of ShadowRoots for a template with id=id-of-source-template (in this case), and caches them as it finds them.
823
832
 
824
833
  Let's say the source template looks as follows:
@@ -862,8 +871,8 @@ What we end up with is:
862
871
  Some significant differences with slot support as used with (ShadowDOM'd) custom elements
863
872
 
864
873
  1. The mechanism to weave DOM together is more flexible here: We are searching for DOM elements that match all the attributes of the children of the *target* template, that template that is pulling in the intra document source template. The "part" attribute was used just as an example.
865
- 2. There is no mechanism for updating the slots. That is something under investigation with this userland [custom enhancement](https://github.com/bahrus/be-inclusive) that allows for updating the existing DOM tree based on identical syntax.
866
- 2. ShadowDOM's slots act on a "many to one" basis. Multiple light children with identical slot identifiers all get merged into a single (first?) matching slot within the Shadow DOM. These "birtual" (birth-only, virtual) inclusions, instead, follow the opposite approach -- a single element can get cloned into multiple slot targets as it weaves itself into the templates as they get merged together.
874
+ 2. There is no mechanism for updating slots. That is something under investigation with this userland [custom enhancement](https://github.com/bahrus/be-inclusive) that allows for updating the existing DOM tree based on identical syntax.
875
+ 2. ShadowDOM's slots act on a "many to one" basis. Multiple light children with identical slot identifiers all get merged into a single (first?) matching slot within the Shadow DOM. These "birtual" (birth-only, virtual) streaming inclusions, instead, follow the opposite approach -- a single element can get cloned into multiple slot targets as it weaves itself into the templates as they get merged together.
867
876
 
868
877
  ## Intra document html imports with Shadow DOM support
869
878
 
@@ -925,7 +934,7 @@ The [add src attribute to template to load a template from file](https://github.
925
934
  Just as it is useful to be able lazy load external imports when needed, it would also be useful to do the same for intra document HTML imports. The most straightforward way this could be done seems to be as follows, either introducing some attribute like "type=conditional", or defining a new element that inherits from the HTMLTemplateElement, for example:
926
935
 
927
936
  ```html
928
- <template id=source-template type=conditional>
937
+ <template id=source-template rel=conditional-stream>
929
938
 
930
939
  <template mount='{
931
940
  "on": ":not([defer-loading])",
@@ -961,6 +970,45 @@ Just as it is useful to be able lazy load external imports when needed, it would
961
970
  </compose>
962
971
  ```
963
972
 
973
+ ## Applying DRY to templates. [WIP]
974
+
975
+ Recall that with the previous examples, there was an implicit value of the rel attribute:
976
+
977
+ ```html
978
+ <template src=#source-template rel=stream>
979
+ <span slot=slot1>hello</span>
980
+ <span slot=slot2>goodbye<span>
981
+ </template>
982
+ ```
983
+
984
+ Now we provide another scenario where we want to specify a different kind of use of the src attribute adorning the template element -- simply as a way of saying "here is a template to be used within this context as templates are traditionally used (for cloning reusable HTML), but the actual contents for the template is defined remotely (intra document or via http).
985
+
986
+ My timing experiments indicate that it is faster to extract out all the needed template elements defined within a repeating template -- keep the contents that need repeated cloning lighter, and only clone fragments as needed from an external reference.
987
+
988
+ ```html
989
+ <html>
990
+ <head>
991
+ <template id=directory>
992
+ My Shared Content
993
+ </template>
994
+ </head>
995
+ <body>
996
+ <div itemscope>
997
+ <template id=directoryConsumer rel=preload src=#directory></template>
998
+ </div>
999
+ </body>
1000
+ <script type=module>
1001
+ import {waitForEvent} from 'mount-observer/waitForEvent.js'
1002
+ async function getContent(){
1003
+ if(directoryConsumer.remoteContent) return directoryConsumer.remoteContents;
1004
+ await waitForEvent(directoryConsumer, 'load');
1005
+ return directoryConsumer.remoteContents;
1006
+ }
1007
+ await getContent(directoryConsumer)
1008
+ </script>
1009
+ </html>
1010
+ ```
1011
+
964
1012
 
965
1013
 
966
1014
  ## Creating "frameworks" that revolve around MOSEs.
package/getContent.ts ADDED
@@ -0,0 +1,10 @@
1
+ import {waitForEvent} from './waitForEvent.js';
2
+ import {TemplateWithRemoteContent} from './ts-refs/mount-observer/types.js';
3
+
4
+ export async function getContent(templ: HTMLTemplateElement, target?: DocumentFragment | ShadowRoot | Document | Element) {
5
+ const templWithRemoteContent = templ as TemplateWithRemoteContent;
6
+ if(templWithRemoteContent.remoteContent) return templWithRemoteContent.remoteContent;
7
+ await waitForEvent(templ, 'load');
8
+ if(templWithRemoteContent.remoteContent) return templWithRemoteContent.remoteContent;
9
+ throw 500; //remote content not loaded
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mount-observer",
3
- "version": "0.0.78",
3
+ "version": "0.0.79",
4
4
  "description": "Observe and act on css matches.",
5
5
  "main": "MountObserver.js",
6
6
  "module": "MountObserver.js",
@@ -41,6 +41,10 @@
41
41
  "default": "./doCleanup.js",
42
42
  "types": "./doCleanup.ts"
43
43
  },
44
+ "./getContent.js": {
45
+ "default": "./getContent.js",
46
+ "types": "./getContent.ts"
47
+ },
44
48
  "./waitForEvent.js": {
45
49
  "default": "./waitForEvent.js",
46
50
  "types": "./waitForEvent.ts"
@@ -0,0 +1,42 @@
1
+ const remoteTemplElSym = Symbol.for('du3y+tfsAUGFHMG/iHZiMQ');
2
+ export async function preloadContent(templ, target) {
3
+ const templWithRemoteContent = templ;
4
+ if (templWithRemoteContent.remoteContent)
5
+ return templWithRemoteContent.remoteContent;
6
+ const src = templ.getAttribute('src');
7
+ if (!src)
8
+ throw 300; //no src attribute
9
+ const isIntraDoc = src[0] === '#';
10
+ if (!('remoteContent' in templWithRemoteContent)) {
11
+ //define a property on the template instance
12
+ Object.defineProperty(templWithRemoteContent, 'remoteContent', {
13
+ get() {
14
+ if (isIntraDoc) {
15
+ const ref = this[remoteTemplElSym]?.deref();
16
+ if (ref)
17
+ return ref.content;
18
+ }
19
+ else {
20
+ throw 'NI'; //not implemented
21
+ }
22
+ },
23
+ enumerable: true,
24
+ configurable: true,
25
+ });
26
+ }
27
+ else {
28
+ return;
29
+ }
30
+ if (isIntraDoc) {
31
+ const id = src.substring(1);
32
+ const { upShadowSearch } = await import('./upShadowSearch.js');
33
+ const remoteTempl = upShadowSearch(templ, id) || upShadowSearch((target || document), id);
34
+ if (!(remoteTempl instanceof HTMLTemplateElement))
35
+ throw 404; //not found
36
+ templWithRemoteContent[remoteTemplElSym] = new WeakRef(remoteTempl);
37
+ templWithRemoteContent.dispatchEvent(new Event('load'));
38
+ }
39
+ else {
40
+ throw 'NI'; //not implemented
41
+ }
42
+ }
@@ -0,0 +1,40 @@
1
+ import {TemplateWithRemoteContent} from './ts-refs/mount-observer/types.js';
2
+ const remoteTemplElSym = Symbol.for('du3y+tfsAUGFHMG/iHZiMQ');
3
+
4
+ export async function preloadContent(templ: HTMLTemplateElement, target?: DocumentFragment | ShadowRoot | Document | Element) {
5
+ const templWithRemoteContent = templ as TemplateWithRemoteContent & {
6
+ [remoteTemplElSym]?: WeakRef<HTMLTemplateElement>
7
+ };
8
+ if(templWithRemoteContent.remoteContent) return templWithRemoteContent.remoteContent;
9
+ const src = templ.getAttribute('src');
10
+ if(!src) throw 300; //no src attribute
11
+ const isIntraDoc = src[0] === '#';
12
+ if(!('remoteContent' in templWithRemoteContent)) {
13
+ //define a property on the template instance
14
+ Object.defineProperty(templWithRemoteContent, 'remoteContent', {
15
+ get(){
16
+ if(isIntraDoc){
17
+ const ref = this[remoteTemplElSym]?.deref();
18
+ if(ref) return ref.content;
19
+ }else{
20
+ throw 'NI'; //not implemented
21
+ }
22
+ },
23
+ enumerable: true,
24
+ configurable: true,
25
+
26
+ });
27
+ }else{
28
+ return;
29
+ }
30
+ if(isIntraDoc){
31
+ const id = src.substring(1);
32
+ const {upShadowSearch} = await import('./upShadowSearch.js');
33
+ const remoteTempl = upShadowSearch(templ, id) || upShadowSearch((target || document) as Element, id);
34
+ if(!(remoteTempl instanceof HTMLTemplateElement)) throw 404; //not found
35
+ templWithRemoteContent[remoteTemplElSym] = new WeakRef(remoteTempl);
36
+ templWithRemoteContent.dispatchEvent(new Event('load'));
37
+ }else{
38
+ throw 'NI'; //not implemented
39
+ }
40
+ }
@@ -0,0 +1,43 @@
1
+ import {IEnhancement, BEAllProps} from '../trans-render/be/types';
2
+
3
+ /**
4
+ *
5
+ */
6
+ export interface DirectoryPickerOptions {
7
+ /**
8
+ * By specifying an ID, the browser can remember different directories for different IDs. If the same ID is used for another picker, the picker opens in the same directory.
9
+ */
10
+ id?: string;
11
+
12
+ /**
13
+ * A string that defaults to "read" for read-only access or "readwrite" for read and write access to the directory.
14
+ */
15
+ mode?: 'read' | 'readwrite';
16
+
17
+ /**
18
+ * A FileSystemHandle or a well known directory ("desktop", "documents", "downloads", "music", "pictures", or "videos") to open the dialog in.
19
+ *
20
+ */
21
+ startIn?: 'documents' | 'downloads' | 'music' | 'pictures' | 'videos' | 'home' | 'desktop' | File;
22
+ }
23
+ export interface EndUserProps extends IEnhancement{
24
+ options: DirectoryPickerOptions;
25
+ noNudge: boolean;
26
+ }
27
+
28
+ export interface AllProps extends EndUserProps{
29
+ directoryHandle?: FileSystemDirectoryHandle;
30
+ }
31
+
32
+ export type AP = AllProps;
33
+
34
+ export type PAP = Partial<AP>;
35
+
36
+ export type ProPAP = Promise<PAP>;
37
+
38
+ export type BAP = AP & BEAllProps;
39
+
40
+ export interface Actions{
41
+ hydrate(self: BAP): ProPAP;
42
+ }
43
+
@@ -240,5 +240,9 @@ export type IshCtr = ({new() : Ishcycle}) | (() => Promise<{new() : Ishcycle}>);
240
240
 
241
241
  export type RefType = '#' | '!';
242
242
 
243
+ export interface TemplateWithRemoteContent extends HTMLTemplateElement {
244
+ remoteContent?: DocumentFragment,
245
+ }
246
+
243
247
 
244
248
 
@@ -219,7 +219,7 @@ export interface UnitOfWork<TProps, TMethods = TProps, TElement = {}>{
219
219
  /**
220
220
  * abbrev. for addEventListener
221
221
  */
222
- a?: AddEventListenerType<TProps, TMethods> | Array<AddEventListenerType<TProps, TMethods>>,
222
+ a?: 0 | AddEventListenerType<TProps, TMethods> | Array<AddEventListenerType<TProps, TMethods>>,
223
223
 
224
224
  /**
225
225
  * Specify how the value we want to apply to the target element should be derived from the observed props.
@@ -434,7 +434,7 @@ export interface AddEventListener<TProps, TMethods>{
434
434
  }
435
435
 
436
436
  export type XForm<TProps, TMethods, TElement = {}> = Partial<{
437
- [key in LHS<TProps, TElement>]: RHS<TProps, TMethods, TElement>;
437
+ [key in LHS<TProps & TMethods, TElement>]: RHS<TProps, TMethods, TElement>;
438
438
  }>;
439
439
 
440
440
  export interface Info {
@@ -0,0 +1,18 @@
1
+ export function upShadowSearch(ref, id) {
2
+ let rn = ref.getRootNode();
3
+ while (rn) {
4
+ let test = rn.getElementById(id);
5
+ if (test)
6
+ return test;
7
+ if (rn.host) {
8
+ test = rn.host[id];
9
+ if (test instanceof HTMLElement)
10
+ return test;
11
+ rn = rn.host.getRootNode();
12
+ }
13
+ else if (rn === document) {
14
+ return null;
15
+ }
16
+ throw 'NI';
17
+ }
18
+ }
@@ -0,0 +1,16 @@
1
+ export function upShadowSearch(ref: Element, id: string){
2
+ let rn = ref.getRootNode() as (Document | DocumentFragment | ShadowRoot) & { host?: Element };
3
+ while(rn){
4
+ let test = rn.getElementById(id);
5
+ if(test) return test;
6
+ if(rn.host){
7
+ test = rn.host[id];
8
+ if(test instanceof HTMLElement) return test;
9
+ rn = rn.host.getRootNode() as (DocumentFragment | ShadowRoot) & { host?: Element };
10
+ }else if(rn === document){
11
+ return null;
12
+ }
13
+ throw 'NI';
14
+ }
15
+ }
16
+