mount-observer 0.0.70 → 0.0.72
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 +42 -19
- package/MountObserver.ts +53 -19
- package/README.md +29 -8
- package/compose.js +66 -70
- package/compose.ts +98 -92
- package/package.json +5 -1
- package/refid/getAdjRefs.js +34 -12
- package/refid/getAdjRefs.ts +32 -10
- package/slotkin/affine.js +39 -0
- package/slotkin/affine.ts +46 -0
- package/slotkin/beKindred.js +36 -21
- package/slotkin/beKindred.ts +45 -21
- package/slotkin/wrap.js +13 -0
- package/slotkin/wrap.ts +17 -0
- package/ts-refs/be-inclusive/types.d.ts +12 -10
- package/ts-refs/mount-observer/types.d.ts +2 -0
package/MountObserver.js
CHANGED
|
@@ -67,22 +67,38 @@ export class MountObserver extends EventTarget {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
async #compose(el, level) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
const src = el.getAttribute('src');
|
|
71
|
+
if (src === null || src.length < 2)
|
|
72
|
+
return;
|
|
73
|
+
const refType = src[0];
|
|
74
|
+
if (!['!', '#'].includes(refType))
|
|
75
|
+
return;
|
|
76
|
+
const { compose } = await import('./compose.js');
|
|
77
|
+
await compose(this, el, level, src.substring(1), refType);
|
|
74
78
|
}
|
|
75
79
|
#templLookUp = new Map();
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
#searchForComment(refName, fragment) {
|
|
81
|
+
const iterator = document.evaluate(`//comment()[.="${refName}"]`, fragment, null, XPathResult.ANY_TYPE, null);
|
|
82
|
+
//console.log({xpathResult})
|
|
83
|
+
try {
|
|
84
|
+
let thisNode = iterator.iterateNext();
|
|
85
|
+
return thisNode;
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async findByID(refName, fragment, refType) {
|
|
92
|
+
if (this.#templLookUp.has(refName))
|
|
93
|
+
return this.#templLookUp.get(refName);
|
|
94
|
+
let templ = null;
|
|
95
|
+
templ = refType === '#' ? fragment.querySelector(`#${refName}`) : this.#searchForComment(refName, fragment);
|
|
80
96
|
if (templ === null) {
|
|
81
97
|
let rootToSearchOutwardFrom = ((fragment.isConnected ? fragment.getRootNode() : this.#mountInit.withTargetShadowRoot) || document);
|
|
82
|
-
templ = rootToSearchOutwardFrom.getElementById(
|
|
98
|
+
templ = refType === '#' ? rootToSearchOutwardFrom.getElementById(refName) : this.#searchForComment(refName, rootToSearchOutwardFrom);
|
|
83
99
|
while (templ === null && rootToSearchOutwardFrom !== document) {
|
|
84
100
|
rootToSearchOutwardFrom = (rootToSearchOutwardFrom.host || rootToSearchOutwardFrom).getRootNode();
|
|
85
|
-
templ = rootToSearchOutwardFrom.getElementById(
|
|
101
|
+
templ = refType === '#' ? rootToSearchOutwardFrom.getElementById(refName) : this.#searchForComment(refName, rootToSearchOutwardFrom);
|
|
86
102
|
}
|
|
87
103
|
}
|
|
88
104
|
if (templ !== null) {
|
|
@@ -98,20 +114,27 @@ export class MountObserver extends EventTarget {
|
|
|
98
114
|
let first = true;
|
|
99
115
|
for (const adjRef of adjRefs) {
|
|
100
116
|
const clone = adjRef.cloneNode(true);
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
117
|
+
if (refType === '#' && clone instanceof Element) {
|
|
118
|
+
if (first && adjRefs.length > 1) {
|
|
119
|
+
clone.setAttribute('itemref', '<autogen>');
|
|
120
|
+
newTempl[wasItemReffed] = true;
|
|
121
|
+
first = false;
|
|
122
|
+
}
|
|
123
|
+
clone.removeAttribute('id');
|
|
105
124
|
}
|
|
106
|
-
clone.removeAttribute('id');
|
|
107
125
|
fragment.appendChild(clone);
|
|
108
126
|
}
|
|
109
|
-
|
|
110
|
-
|
|
127
|
+
if (templ instanceof Element) {
|
|
128
|
+
const { doCleanup } = await import('./doCleanup.js');
|
|
129
|
+
doCleanup(templ, fragment);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
//TODO: cleanup
|
|
133
|
+
}
|
|
111
134
|
newTempl.content.appendChild(fragment);
|
|
112
135
|
templ = newTempl;
|
|
113
136
|
}
|
|
114
|
-
this.#templLookUp.set(
|
|
137
|
+
this.#templLookUp.set(refName, templ);
|
|
115
138
|
}
|
|
116
139
|
return templ;
|
|
117
140
|
}
|
|
@@ -500,7 +523,7 @@ function areAllIdle(mutObs) {
|
|
|
500
523
|
return true;
|
|
501
524
|
}
|
|
502
525
|
const refCountErr = 'mount-observer ref count mismatch';
|
|
503
|
-
export const inclTemplQry = 'template[src^="#"]:not([hidden])';
|
|
526
|
+
export const inclTemplQry = 'template[src^="#"]:not([hidden]),template[src^="!"]:not([hidden])';
|
|
504
527
|
// https://github.com/webcomponents-cg/community-protocols/issues/12#issuecomment-872415080
|
|
505
528
|
/**
|
|
506
529
|
* The `mutation-event` event represents something that happened.
|
package/MountObserver.ts
CHANGED
|
@@ -4,7 +4,8 @@ import {MountInit, IMountObserver, AddMutationEventListener,
|
|
|
4
4
|
AttrParts,
|
|
5
5
|
MOSE, WeakDual,
|
|
6
6
|
MountObserverOptions,
|
|
7
|
-
Assigner
|
|
7
|
+
Assigner,
|
|
8
|
+
RefType
|
|
8
9
|
} from './ts-refs/mount-observer/types';
|
|
9
10
|
import {RootMutObs} from './RootMutObs.js';
|
|
10
11
|
import {bindish, bindishIt} from './bindish.js';
|
|
@@ -81,24 +82,48 @@ export class MountObserver extends EventTarget implements IMountObserver{
|
|
|
81
82
|
|
|
82
83
|
|
|
83
84
|
async #compose(el: HTMLTemplateElement, level: number){
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
const src = el.getAttribute('src');
|
|
86
|
+
if(src === null || src.length < 2) return;
|
|
87
|
+
const refType = src[0] as RefType;
|
|
88
|
+
if(!['!', '#'].includes(refType)) return;
|
|
89
|
+
const {compose} = await import('./compose.js');
|
|
90
|
+
await compose(this, el, level, src.substring(1), refType);
|
|
91
|
+
|
|
88
92
|
|
|
89
93
|
}
|
|
90
94
|
#templLookUp: Map<string, HTMLElement> = new Map();
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
#searchForComment(refName: string, fragment: Node){
|
|
96
|
+
const iterator = document.evaluate(
|
|
97
|
+
`//comment()[.="${refName}"]`,
|
|
98
|
+
fragment,
|
|
99
|
+
null,
|
|
100
|
+
XPathResult.ANY_TYPE,
|
|
101
|
+
null
|
|
102
|
+
);
|
|
103
|
+
//console.log({xpathResult})
|
|
104
|
+
try {
|
|
105
|
+
let thisNode = iterator.iterateNext();
|
|
106
|
+
return thisNode;
|
|
107
|
+
}catch(e){
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async findByID(
|
|
112
|
+
refName: string, fragment: DocumentFragment,
|
|
113
|
+
refType: RefType): Promise<HTMLElement | DocumentFragment | null>{
|
|
114
|
+
if(this.#templLookUp.has(refName)) return this.#templLookUp.get(refName)!;
|
|
115
|
+
let templ: Node | null = null;
|
|
116
|
+
templ = refType === '#' ? fragment.querySelector(`#${refName}`) : this.#searchForComment(refName, fragment);
|
|
94
117
|
if(templ === null){
|
|
95
118
|
let rootToSearchOutwardFrom = ((fragment.isConnected ? fragment.getRootNode() : this.#mountInit.withTargetShadowRoot) || document) as any;
|
|
96
|
-
templ = rootToSearchOutwardFrom.getElementById(
|
|
119
|
+
templ = refType === '#' ? rootToSearchOutwardFrom.getElementById(refName) : this.#searchForComment(refName, rootToSearchOutwardFrom);
|
|
97
120
|
while(templ === null && rootToSearchOutwardFrom !== (document as any as DocumentFragment) ){
|
|
98
121
|
rootToSearchOutwardFrom = (rootToSearchOutwardFrom.host || rootToSearchOutwardFrom).getRootNode() as DocumentFragment;
|
|
99
|
-
templ = rootToSearchOutwardFrom.getElementById(
|
|
122
|
+
templ = refType === '#' ? rootToSearchOutwardFrom.getElementById(refName) : this.#searchForComment(refName, rootToSearchOutwardFrom);
|
|
100
123
|
}
|
|
101
124
|
}
|
|
125
|
+
|
|
126
|
+
|
|
102
127
|
if(templ !== null) {
|
|
103
128
|
if(!(templ instanceof HTMLTemplateElement)){
|
|
104
129
|
const newTempl = document.createElement('template');
|
|
@@ -112,20 +137,29 @@ export class MountObserver extends EventTarget implements IMountObserver{
|
|
|
112
137
|
let first = true;
|
|
113
138
|
for(const adjRef of adjRefs){
|
|
114
139
|
const clone = adjRef.cloneNode(true) as HTMLElement;
|
|
115
|
-
if(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
140
|
+
if(refType === '#' && clone instanceof Element){
|
|
141
|
+
if(first && adjRefs.length > 1){
|
|
142
|
+
clone.setAttribute('itemref', '<autogen>');
|
|
143
|
+
(<any>newTempl)[wasItemReffed] = true;
|
|
144
|
+
first = false;
|
|
145
|
+
}
|
|
146
|
+
clone.removeAttribute('id');
|
|
119
147
|
}
|
|
120
|
-
|
|
148
|
+
|
|
121
149
|
fragment.appendChild(clone);
|
|
122
150
|
}
|
|
123
|
-
|
|
124
|
-
|
|
151
|
+
if(templ instanceof Element){
|
|
152
|
+
const {doCleanup} = await import('./doCleanup.js');
|
|
153
|
+
doCleanup(templ as HTMLElement, fragment);
|
|
154
|
+
|
|
155
|
+
}else{
|
|
156
|
+
//TODO: cleanup
|
|
157
|
+
}
|
|
125
158
|
newTempl.content.appendChild(fragment);
|
|
126
159
|
templ = newTempl;
|
|
160
|
+
|
|
127
161
|
}
|
|
128
|
-
this.#templLookUp.set(
|
|
162
|
+
this.#templLookUp.set(refName, templ as HTMLTemplateElement);
|
|
129
163
|
}
|
|
130
164
|
return templ as HTMLTemplateElement;
|
|
131
165
|
}
|
|
@@ -523,7 +557,7 @@ function areAllIdle(mutObs: Array<RootMutObs>){
|
|
|
523
557
|
|
|
524
558
|
|
|
525
559
|
const refCountErr = 'mount-observer ref count mismatch';
|
|
526
|
-
export const inclTemplQry = 'template[src^="#"]:not([hidden])';
|
|
560
|
+
export const inclTemplQry = 'template[src^="#"]:not([hidden]),template[src^="!"]:not([hidden])';
|
|
527
561
|
|
|
528
562
|
export interface MountObserver extends IMountObserver{}
|
|
529
563
|
|
package/README.md
CHANGED
|
@@ -769,8 +769,8 @@ For example:
|
|
|
769
769
|
<div>Your Mother Should Know</div>
|
|
770
770
|
<div>I Am the Walrus</div>
|
|
771
771
|
<template src=#id-of-source-template>
|
|
772
|
-
<span
|
|
773
|
-
<span
|
|
772
|
+
<span part=greeting>hello</span>
|
|
773
|
+
<span part=parting>goodbye<span>
|
|
774
774
|
</template>
|
|
775
775
|
<div>Strawberry Fields Forever</div>
|
|
776
776
|
```
|
|
@@ -781,24 +781,45 @@ Let's say the source template looks as follows:
|
|
|
781
781
|
|
|
782
782
|
```html
|
|
783
783
|
<template id=id-of-source-template>
|
|
784
|
-
<div>
|
|
784
|
+
<div>
|
|
785
|
+
You say, <span part=parting></span> and I say,
|
|
786
|
+
<span part=greeting></span>, <span part=greeting></span>, <span part=greeting></span>
|
|
787
|
+
</div>
|
|
788
|
+
<div>
|
|
789
|
+
I don't know why you say
|
|
790
|
+
<span part=greeting></span>
|
|
791
|
+
I say
|
|
792
|
+
<span part=parting></span>
|
|
793
|
+
</div>
|
|
785
794
|
</template>
|
|
786
795
|
```
|
|
787
796
|
|
|
788
|
-
What we
|
|
797
|
+
What we end up with is:
|
|
789
798
|
|
|
790
799
|
|
|
791
800
|
```html
|
|
792
801
|
<div>Your Mother Should Know</div>
|
|
793
802
|
<div>I Am the Walrus</div>
|
|
794
|
-
|
|
803
|
+
<?+?>
|
|
804
|
+
<div>
|
|
805
|
+
You say, <span part=parting>goodbye</span> and I say,
|
|
806
|
+
<span part=greeting>hello</span>, <span part=greeting>hello</span>, <span part=greeting>hello</span>
|
|
807
|
+
</div>
|
|
808
|
+
<div>
|
|
809
|
+
I don't know why you say
|
|
810
|
+
<span part=greeting>goodbye</span>
|
|
811
|
+
I say
|
|
812
|
+
<span part=parting>hello</span>
|
|
813
|
+
</div>
|
|
814
|
+
<?-?>
|
|
795
815
|
<div>Strawberry Fields Forever</div>
|
|
796
816
|
```
|
|
797
817
|
|
|
798
|
-
Some significant differences with
|
|
818
|
+
Some significant differences with slot support as used with (ShadowDOM'd) custom elements
|
|
799
819
|
|
|
800
|
-
1.
|
|
801
|
-
2.
|
|
820
|
+
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.
|
|
821
|
+
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.
|
|
822
|
+
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.
|
|
802
823
|
|
|
803
824
|
## Intra document html imports with Shadow DOM support
|
|
804
825
|
|
package/compose.js
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { wasItemReffed } from './MountObserver.js';
|
|
2
|
+
//import {prep} from './slotkin/affine.js';
|
|
3
|
+
//goal: deprecate this key, in favor of comments
|
|
2
4
|
export const childRefsKey = Symbol.for('Wr0WPVh84k+O93miuENdMA');
|
|
3
5
|
export const cloneKey = Symbol.for('LD97VKZYc02CQv23DT/6fQ');
|
|
4
6
|
const autogenKey = Symbol.for('YpP5EP0i1UKcBBBH9tsm0w');
|
|
5
|
-
|
|
7
|
+
//const wrapped = Symbol.for('50tzQZt95ECXUtHF7a40og');
|
|
8
|
+
export async function compose(self, el, level, refName, refType) {
|
|
6
9
|
const src = el.getAttribute('src');
|
|
7
10
|
if (src === null)
|
|
8
11
|
return;
|
|
9
12
|
el.removeAttribute('src');
|
|
10
|
-
const templID = src
|
|
13
|
+
//const templID = src!.substring(1);
|
|
14
|
+
//const refType = src![0];
|
|
11
15
|
const fragment = self.objNde?.deref();
|
|
12
16
|
if (fragment === undefined)
|
|
13
17
|
return;
|
|
14
|
-
const templ = await self.findByID(
|
|
18
|
+
const templ = await self.findByID(refName, fragment, refType);
|
|
15
19
|
if (!(templ instanceof HTMLTemplateElement))
|
|
16
20
|
throw 404;
|
|
21
|
+
if (refType === '#') {
|
|
22
|
+
(await import('./slotkin/wrap.js')).wrap(templ, refName);
|
|
23
|
+
}
|
|
17
24
|
const clone = templ.content.cloneNode(true);
|
|
18
25
|
const dataLd = el.dataset.ld;
|
|
19
26
|
const wasReffed = templ[wasItemReffed];
|
|
@@ -48,63 +55,55 @@ export async function compose(self, el, level) {
|
|
|
48
55
|
delete el.dataset.ld;
|
|
49
56
|
}
|
|
50
57
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
for (const innerTempl of innerTempls) {
|
|
58
|
-
const innerSlots = innerTempl.content.querySelectorAll(slotQry);
|
|
59
|
-
for (const innerSlot of innerSlots) {
|
|
60
|
-
targets.push(innerSlot);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
for (const target of targets) {
|
|
64
|
-
const slotClone = slot.cloneNode(true);
|
|
65
|
-
target.after(slotClone);
|
|
66
|
-
target.remove();
|
|
58
|
+
if (el.content.childElementCount > 0) {
|
|
59
|
+
const { affine } = await import('./slotkin/affine.js');
|
|
60
|
+
const children = Array.from(el.content.children);
|
|
61
|
+
for (const child of children) {
|
|
62
|
+
//TODO support clean up
|
|
63
|
+
const mo = affine(clone, child);
|
|
67
64
|
}
|
|
68
65
|
}
|
|
69
66
|
await self.composeFragment(clone, level + 1);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
67
|
+
// if (false) {
|
|
68
|
+
// const shadowRootModeOnLoad = el.getAttribute('shadowRootModeOnLoad') as null | ShadowRootMode;
|
|
69
|
+
// if (shadowRootModeOnLoad === null && level === 0) {
|
|
70
|
+
// const slotMap = el.getAttribute('slotmap');
|
|
71
|
+
// let map = slotMap === null ? undefined : JSON.parse(slotMap);
|
|
72
|
+
// const slots = clone.querySelectorAll('[slot]');
|
|
73
|
+
// for (const slot of slots) {
|
|
74
|
+
// if (map !== undefined) {
|
|
75
|
+
// const slotName = slot.slot;
|
|
76
|
+
// for (const key in map) {
|
|
77
|
+
// if (slot.matches(key)) {
|
|
78
|
+
// const targetAttSymbols = map[key] as string;
|
|
79
|
+
// for (const sym of targetAttSymbols) {
|
|
80
|
+
// switch (sym) {
|
|
81
|
+
// case '|':
|
|
82
|
+
// slot.setAttribute('itemprop', slotName);
|
|
83
|
+
// break;
|
|
84
|
+
// case '$':
|
|
85
|
+
// slot.setAttribute('itemscope', '');
|
|
86
|
+
// slot.setAttribute('itemprop', slotName);
|
|
87
|
+
// break;
|
|
88
|
+
// case '@':
|
|
89
|
+
// slot.setAttribute('name', slotName);
|
|
90
|
+
// break;
|
|
91
|
+
// case '.':
|
|
92
|
+
// slot.classList.add(slotName);
|
|
93
|
+
// break;
|
|
94
|
+
// case '%':
|
|
95
|
+
// slot.part.add(slotName);
|
|
96
|
+
// break;
|
|
97
|
+
// }
|
|
98
|
+
// }
|
|
99
|
+
// }
|
|
100
|
+
// }
|
|
101
|
+
// }
|
|
102
|
+
// slot.removeAttribute('slot');
|
|
103
|
+
// }
|
|
104
|
+
// el.dispatchEvent(new LoadEvent(clone));
|
|
105
|
+
// }
|
|
106
|
+
// }
|
|
108
107
|
if (level === 0) {
|
|
109
108
|
const refs = [];
|
|
110
109
|
for (const child of clone.children) {
|
|
@@ -120,17 +119,14 @@ export async function compose(self, el, level) {
|
|
|
120
119
|
cloneStashed = true;
|
|
121
120
|
}
|
|
122
121
|
else {
|
|
123
|
-
if (shadowRootModeOnLoad !== null) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
el.after(clone);
|
|
133
|
-
}
|
|
122
|
+
// if (false /*shadowRootModeOnLoad !== null */) {
|
|
123
|
+
// const parent = el.parentElement;
|
|
124
|
+
// if (parent === null) throw 404;
|
|
125
|
+
// if (parent.shadowRoot === null) parent.attachShadow({ mode: shadowRootModeOnLoad });
|
|
126
|
+
// parent.shadowRoot?.append(clone);
|
|
127
|
+
// } else {
|
|
128
|
+
el.after(clone);
|
|
129
|
+
//}
|
|
134
130
|
}
|
|
135
131
|
//moving the code down here broke be-inclusive Example2.html (but maybe it caused something else to work, so will need to revisit)
|
|
136
132
|
//check to make sure the progresive loading of css-charts works as before.
|
|
@@ -138,7 +134,7 @@ export async function compose(self, el, level) {
|
|
|
138
134
|
// el.dispatchEvent(new LoadEvent(clone));
|
|
139
135
|
// }
|
|
140
136
|
if (!cloneStashed) {
|
|
141
|
-
if (level !== 0 ||
|
|
137
|
+
if (level !== 0 || el.attributes.length === 0)
|
|
142
138
|
el.remove();
|
|
143
139
|
}
|
|
144
140
|
}
|
package/compose.ts
CHANGED
|
@@ -1,158 +1,164 @@
|
|
|
1
|
-
import { ILoadEvent, loadEventName } from './ts-refs/mount-observer/types';
|
|
1
|
+
import { ILoadEvent, loadEventName, RefType } from './ts-refs/mount-observer/types';
|
|
2
2
|
import { MountObserver, inclTemplQry, wasItemReffed } from './MountObserver.js';
|
|
3
|
+
//import {prep} from './slotkin/affine.js';
|
|
3
4
|
|
|
5
|
+
//goal: deprecate this key, in favor of comments
|
|
4
6
|
export const childRefsKey = Symbol.for('Wr0WPVh84k+O93miuENdMA');
|
|
5
7
|
export const cloneKey = Symbol.for('LD97VKZYc02CQv23DT/6fQ');
|
|
6
8
|
const autogenKey = Symbol.for('YpP5EP0i1UKcBBBH9tsm0w');
|
|
9
|
+
//const wrapped = Symbol.for('50tzQZt95ECXUtHF7a40og');
|
|
7
10
|
export async function compose(
|
|
8
|
-
self: MountObserver,
|
|
9
|
-
el: HTMLTemplateElement,
|
|
10
|
-
level: number
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
self: MountObserver,
|
|
12
|
+
el: HTMLTemplateElement,
|
|
13
|
+
level: number,
|
|
14
|
+
refName: string,
|
|
15
|
+
refType: RefType,
|
|
16
|
+
) {
|
|
17
|
+
const src = el.getAttribute('src'); if (src === null) return;
|
|
13
18
|
el.removeAttribute('src');
|
|
14
|
-
const templID = src!.substring(1);
|
|
19
|
+
//const templID = src!.substring(1);
|
|
20
|
+
//const refType = src![0];
|
|
15
21
|
const fragment = self.objNde?.deref() as DocumentFragment;
|
|
16
|
-
if(fragment === undefined) return;
|
|
17
|
-
const templ = await self.findByID(
|
|
18
|
-
if(!(templ instanceof HTMLTemplateElement)) throw 404;
|
|
22
|
+
if (fragment === undefined) return;
|
|
23
|
+
const templ = await self.findByID(refName, fragment, refType);
|
|
24
|
+
if (!(templ instanceof HTMLTemplateElement)) throw 404;
|
|
25
|
+
if (refType === '#') {
|
|
26
|
+
(await import('./slotkin/wrap.js')).wrap(templ, refName);
|
|
27
|
+
}
|
|
28
|
+
|
|
19
29
|
const clone = templ.content.cloneNode(true) as DocumentFragment;
|
|
20
30
|
const dataLd = el.dataset.ld;
|
|
21
31
|
const wasReffed = (<any>templ)[wasItemReffed];
|
|
22
|
-
if(wasReffed || dataLd){
|
|
32
|
+
if (wasReffed || dataLd) {
|
|
23
33
|
const firstElement = clone.firstElementChild!;
|
|
24
|
-
if(wasReffed){
|
|
34
|
+
if (wasReffed) {
|
|
25
35
|
let ns = firstElement.nextElementSibling;
|
|
26
36
|
const ids = [];
|
|
27
37
|
let count = (<any>window)[autogenKey];
|
|
28
|
-
if(count === undefined){
|
|
38
|
+
if (count === undefined) {
|
|
29
39
|
count = 0;
|
|
30
|
-
}else{
|
|
40
|
+
} else {
|
|
31
41
|
count++;
|
|
32
42
|
}
|
|
33
43
|
(<any>window)[autogenKey] = count;
|
|
34
|
-
while(ns !== null){
|
|
44
|
+
while (ns !== null) {
|
|
35
45
|
const id = ns.id = `mount-observer-${count}`;
|
|
36
46
|
ids.push(id);
|
|
37
47
|
ns = ns.nextElementSibling;
|
|
38
48
|
}
|
|
39
49
|
firstElement.setAttribute('itemref', ids.join(' '));
|
|
40
50
|
}
|
|
41
|
-
|
|
42
|
-
if(dataLd){
|
|
51
|
+
|
|
52
|
+
if (dataLd) {
|
|
43
53
|
const parsed = JSON.parse(dataLd);
|
|
44
54
|
let type = parsed['@type'];
|
|
45
55
|
const itemscopeAttr = firstElement.getAttribute('itemscope');
|
|
46
|
-
if(type && !itemscopeAttr){
|
|
56
|
+
if (type && !itemscopeAttr) {
|
|
47
57
|
firstElement.setAttribute('itemscope', type);
|
|
48
58
|
}
|
|
49
59
|
(<any>firstElement)['ish'] = parsed;
|
|
50
60
|
delete el.dataset.ld;
|
|
51
61
|
}
|
|
52
|
-
|
|
62
|
+
|
|
53
63
|
}
|
|
54
|
-
|
|
64
|
+
if (el.content.childElementCount > 0) {
|
|
65
|
+
const { affine } = await import('./slotkin/affine.js');
|
|
66
|
+
const children = Array.from(el.content.children);
|
|
67
|
+
for (const child of children) {
|
|
68
|
+
//TODO support clean up
|
|
69
|
+
const mo = affine(clone, child);
|
|
55
70
|
|
|
56
|
-
for(const slot of slots){
|
|
57
|
-
const name = slot.getAttribute('slot')!;
|
|
58
|
-
const slotQry = `slot[name="${name}"]`;
|
|
59
|
-
const targets = Array.from(clone.querySelectorAll(slotQry));
|
|
60
|
-
const innerTempls = clone.querySelectorAll(inclTemplQry) as NodeListOf<HTMLTemplateElement>;
|
|
61
|
-
for(const innerTempl of innerTempls){
|
|
62
|
-
const innerSlots = innerTempl.content.querySelectorAll(slotQry);
|
|
63
|
-
for(const innerSlot of innerSlots){
|
|
64
|
-
targets.push(innerSlot);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
for(const target of targets){
|
|
68
|
-
const slotClone = slot.cloneNode(true) as Element;
|
|
69
|
-
target.after(slotClone);
|
|
70
|
-
target.remove();
|
|
71
71
|
}
|
|
72
|
+
|
|
72
73
|
}
|
|
74
|
+
|
|
75
|
+
|
|
73
76
|
await self.composeFragment(clone, level + 1);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
77
|
+
// if (false) {
|
|
78
|
+
// const shadowRootModeOnLoad = el.getAttribute('shadowRootModeOnLoad') as null | ShadowRootMode;
|
|
79
|
+
// if (shadowRootModeOnLoad === null && level === 0) {
|
|
80
|
+
|
|
81
|
+
// const slotMap = el.getAttribute('slotmap');
|
|
82
|
+
// let map = slotMap === null ? undefined : JSON.parse(slotMap);
|
|
83
|
+
// const slots = clone.querySelectorAll('[slot]');
|
|
84
|
+
// for (const slot of slots) {
|
|
85
|
+
// if (map !== undefined) {
|
|
86
|
+
// const slotName = slot.slot;
|
|
87
|
+
// for (const key in map) {
|
|
88
|
+
// if (slot.matches(key)) {
|
|
89
|
+
// const targetAttSymbols = map[key] as string;
|
|
90
|
+
// for (const sym of targetAttSymbols) {
|
|
91
|
+
// switch (sym) {
|
|
92
|
+
// case '|':
|
|
93
|
+
// slot.setAttribute('itemprop', slotName);
|
|
94
|
+
// break;
|
|
95
|
+
// case '$':
|
|
96
|
+
// slot.setAttribute('itemscope', '');
|
|
97
|
+
// slot.setAttribute('itemprop', slotName);
|
|
98
|
+
// break;
|
|
99
|
+
// case '@':
|
|
100
|
+
// slot.setAttribute('name', slotName);
|
|
101
|
+
// break;
|
|
102
|
+
// case '.':
|
|
103
|
+
// slot.classList.add(slotName);
|
|
104
|
+
// break;
|
|
105
|
+
// case '%':
|
|
106
|
+
// slot.part.add(slotName);
|
|
107
|
+
// break;
|
|
108
|
+
// }
|
|
109
|
+
// }
|
|
110
|
+
// }
|
|
111
|
+
// }
|
|
112
|
+
// }
|
|
113
|
+
// slot.removeAttribute('slot');
|
|
114
|
+
// }
|
|
115
|
+
// el.dispatchEvent(new LoadEvent(clone));
|
|
116
|
+
// }
|
|
117
|
+
// }
|
|
118
|
+
|
|
119
|
+
if (level === 0) {
|
|
114
120
|
const refs: Array<WeakRef<Element>> = [];
|
|
115
|
-
for(const child of clone.children){
|
|
121
|
+
for (const child of clone.children) {
|
|
116
122
|
refs.push(new WeakRef(child));
|
|
117
123
|
}
|
|
118
124
|
(<any>el)[childRefsKey] = refs;
|
|
119
|
-
|
|
125
|
+
|
|
120
126
|
}
|
|
121
127
|
//if template has itemscope attribute, assume want to do some data binding before instantiating into
|
|
122
128
|
//DOM fragment.
|
|
123
129
|
let cloneStashed = false;
|
|
124
|
-
if(el.hasAttribute('itemscope')){
|
|
130
|
+
if (el.hasAttribute('itemscope')) {
|
|
125
131
|
(<any>el)[cloneKey] = clone;
|
|
126
132
|
cloneStashed = true;
|
|
127
|
-
}else{
|
|
128
|
-
if(shadowRootModeOnLoad !== null){
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}else{
|
|
133
|
+
} else {
|
|
134
|
+
// if (false /*shadowRootModeOnLoad !== null */) {
|
|
135
|
+
// const parent = el.parentElement;
|
|
136
|
+
// if (parent === null) throw 404;
|
|
137
|
+
// if (parent.shadowRoot === null) parent.attachShadow({ mode: shadowRootModeOnLoad });
|
|
138
|
+
// parent.shadowRoot?.append(clone);
|
|
139
|
+
// } else {
|
|
134
140
|
el.after(clone);
|
|
135
|
-
}
|
|
141
|
+
//}
|
|
136
142
|
}
|
|
137
143
|
//moving the code down here broke be-inclusive Example2.html (but maybe it caused something else to work, so will need to revisit)
|
|
138
144
|
//check to make sure the progresive loading of css-charts works as before.
|
|
139
145
|
// if(level === 0){
|
|
140
146
|
// el.dispatchEvent(new LoadEvent(clone));
|
|
141
147
|
// }
|
|
142
|
-
|
|
143
|
-
if(!cloneStashed){
|
|
144
|
-
if(level !== 0 ||
|
|
148
|
+
|
|
149
|
+
if (!cloneStashed) {
|
|
150
|
+
if (level !== 0 || el.attributes.length === 0) el.remove();
|
|
145
151
|
}
|
|
146
152
|
|
|
147
153
|
}
|
|
148
154
|
|
|
149
|
-
export class LoadEvent extends Event implements ILoadEvent{
|
|
155
|
+
export class LoadEvent extends Event implements ILoadEvent {
|
|
150
156
|
static eventName: loadEventName = 'load';
|
|
151
|
-
constructor(public clone: DocumentFragment){
|
|
157
|
+
constructor(public clone: DocumentFragment) {
|
|
152
158
|
super(LoadEvent.eventName);
|
|
153
159
|
}
|
|
154
160
|
}
|
|
155
161
|
|
|
156
|
-
interface HTMLElementEventMap{
|
|
162
|
+
interface HTMLElementEventMap {
|
|
157
163
|
'load': LoadEvent,
|
|
158
164
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mount-observer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.72",
|
|
4
4
|
"description": "Observe and act on css matches.",
|
|
5
5
|
"main": "MountObserver.js",
|
|
6
6
|
"module": "MountObserver.js",
|
|
@@ -64,6 +64,10 @@
|
|
|
64
64
|
"./slotkin/beKindred.js": {
|
|
65
65
|
"default": "./slotkin/beKindred.js",
|
|
66
66
|
"types": "./slotkin/beKindred.ts"
|
|
67
|
+
},
|
|
68
|
+
"./slotkin/wrap.js": {
|
|
69
|
+
"default": "./slotkin/wrap.js",
|
|
70
|
+
"types": "./slotkin/wrap.ts"
|
|
67
71
|
}
|
|
68
72
|
},
|
|
69
73
|
"files": [
|
package/refid/getAdjRefs.js
CHANGED
|
@@ -1,16 +1,38 @@
|
|
|
1
1
|
import { splitRefs } from './splitRefs.js';
|
|
2
|
-
export function getAdjRefs(
|
|
3
|
-
const returnArr = [
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
2
|
+
export function getAdjRefs(node) {
|
|
3
|
+
const returnArr = [node];
|
|
4
|
+
if (node.nodeType === node.COMMENT_NODE) {
|
|
5
|
+
const openText = node.data.split(' ')[0];
|
|
6
|
+
const closedText = `/${openText}`;
|
|
7
|
+
let ns = node.nextSibling;
|
|
8
|
+
while (ns) {
|
|
9
|
+
returnArr.push(ns);
|
|
10
|
+
if (node.nodeType === node.COMMENT_NODE && node.data === closedText) {
|
|
11
|
+
return returnArr;
|
|
12
|
+
}
|
|
13
|
+
ns = ns.nextSibling;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
const el = node;
|
|
18
|
+
const itemref = el.getAttribute('itemref');
|
|
19
|
+
if (itemref === null)
|
|
20
|
+
return returnArr;
|
|
21
|
+
const itemrefList = splitRefs(itemref); // itemref.split(' ').map((id) => id.trim()).filter((id) => id.length > 0);
|
|
22
|
+
if (itemrefList.length === 0)
|
|
23
|
+
return returnArr;
|
|
24
|
+
let ns = el.nextSibling;
|
|
25
|
+
while (ns !== null) {
|
|
26
|
+
if (ns instanceof Element) {
|
|
27
|
+
if (ns.id && itemrefList.includes(ns.id)) {
|
|
28
|
+
returnArr.push(ns);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
return returnArr;
|
|
33
|
+
}
|
|
34
|
+
ns = ns.nextSibling;
|
|
35
|
+
}
|
|
14
36
|
}
|
|
15
37
|
return returnArr;
|
|
16
38
|
}
|
package/refid/getAdjRefs.ts
CHANGED
|
@@ -1,15 +1,37 @@
|
|
|
1
1
|
import { splitRefs } from './splitRefs.js';
|
|
2
|
-
export function getAdjRefs(
|
|
3
|
-
const returnArr = [
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
export function getAdjRefs(node: Node){
|
|
3
|
+
const returnArr: Array<Node> = [node];
|
|
4
|
+
if(node.nodeType === node.COMMENT_NODE){
|
|
5
|
+
const openText = (node as Comment).data.split(' ')[0];
|
|
6
|
+
const closedText = `/${openText}`;
|
|
7
|
+
let ns = node.nextSibling;
|
|
8
|
+
while(ns){
|
|
9
|
+
returnArr.push(ns);
|
|
10
|
+
if(node.nodeType === node.COMMENT_NODE && (node as Comment).data === closedText){
|
|
11
|
+
return returnArr;
|
|
12
|
+
}
|
|
13
|
+
ns = ns.nextSibling;
|
|
14
|
+
}
|
|
15
|
+
}else{
|
|
16
|
+
const el = node as Element;
|
|
17
|
+
const itemref = el.getAttribute('itemref');
|
|
18
|
+
if(itemref === null) return returnArr;
|
|
19
|
+
const itemrefList = splitRefs(itemref);// itemref.split(' ').map((id) => id.trim()).filter((id) => id.length > 0);
|
|
20
|
+
if(itemrefList.length === 0) return returnArr;
|
|
21
|
+
let ns = el.nextSibling;
|
|
22
|
+
while(ns !== null){
|
|
23
|
+
if(ns instanceof Element){
|
|
24
|
+
if(ns.id && itemrefList.includes(ns.id)){
|
|
25
|
+
returnArr.push(ns);
|
|
26
|
+
}
|
|
27
|
+
}else{
|
|
28
|
+
return returnArr;
|
|
29
|
+
}
|
|
30
|
+
ns = ns.nextSibling;
|
|
31
|
+
}
|
|
32
|
+
|
|
12
33
|
}
|
|
34
|
+
|
|
13
35
|
return returnArr;
|
|
14
36
|
}
|
|
15
37
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { toQuery } from './toQuery.js';
|
|
2
|
+
import { splitRefs } from '../refid/splitRefs.js';
|
|
3
|
+
export function affine(fragment, el) {
|
|
4
|
+
const qry = toQuery(el);
|
|
5
|
+
const { elFragment, map } = prep(el);
|
|
6
|
+
const matches = Array.from(fragment.querySelectorAll(qry));
|
|
7
|
+
for (const match of matches) {
|
|
8
|
+
clone(match, elFragment, map);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function prep(el) {
|
|
12
|
+
const elFragment = new DocumentFragment();
|
|
13
|
+
const clone = el.cloneNode(true);
|
|
14
|
+
for (const child of clone.childNodes) {
|
|
15
|
+
elFragment.appendChild(child);
|
|
16
|
+
}
|
|
17
|
+
const insertAttrs = el.getAttribute('-i');
|
|
18
|
+
let map = null;
|
|
19
|
+
if (insertAttrs !== null) {
|
|
20
|
+
const attrs = splitRefs(insertAttrs);
|
|
21
|
+
map = {};
|
|
22
|
+
for (const attr of attrs) {
|
|
23
|
+
map[attr] = el.getAttribute(attr);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
elFragment, map
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function clone(matchingElement, elFragment, map) {
|
|
31
|
+
const fragmentClone = elFragment.cloneNode(true);
|
|
32
|
+
matchingElement.replaceChildren(fragmentClone);
|
|
33
|
+
if (map !== null) {
|
|
34
|
+
for (const key in map) {
|
|
35
|
+
const value = map[key];
|
|
36
|
+
matchingElement.setAttribute(key, value);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {toQuery} from './toQuery.js';
|
|
2
|
+
import {splitRefs} from '../refid/splitRefs.js';
|
|
3
|
+
|
|
4
|
+
export function affine(fragment: DocumentFragment | Element,
|
|
5
|
+
el: Element){
|
|
6
|
+
const qry = toQuery(el);
|
|
7
|
+
const {elFragment, map} = prep(el);
|
|
8
|
+
const matches = Array.from(fragment.querySelectorAll(qry));
|
|
9
|
+
for(const match of matches){
|
|
10
|
+
clone(match, elFragment, map);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function prep(el: Element){
|
|
15
|
+
const elFragment = new DocumentFragment();
|
|
16
|
+
const clone = el.cloneNode(true);
|
|
17
|
+
for(const child of clone.childNodes){
|
|
18
|
+
elFragment.appendChild(child);
|
|
19
|
+
}
|
|
20
|
+
const insertAttrs = el.getAttribute('-i');
|
|
21
|
+
let map: {[key: string]: string} | null = null;
|
|
22
|
+
if(insertAttrs !== null){
|
|
23
|
+
const attrs = splitRefs(insertAttrs);
|
|
24
|
+
map = {};
|
|
25
|
+
for(const attr of attrs){
|
|
26
|
+
map[attr] = el.getAttribute(attr)!;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
elFragment, map
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function clone(
|
|
35
|
+
matchingElement: Element,
|
|
36
|
+
elFragment: DocumentFragment,
|
|
37
|
+
map: {[key: string]: string} | null){
|
|
38
|
+
const fragmentClone = elFragment.cloneNode(true) as DocumentFragment;
|
|
39
|
+
matchingElement.replaceChildren(fragmentClone);
|
|
40
|
+
if(map !== null){
|
|
41
|
+
for(const key in map){
|
|
42
|
+
const value = map[key]!;
|
|
43
|
+
matchingElement.setAttribute(key, value);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
package/slotkin/beKindred.js
CHANGED
|
@@ -1,30 +1,45 @@
|
|
|
1
1
|
import { toQuery } from './toQuery.js';
|
|
2
|
-
import {
|
|
2
|
+
import { MountObserver } from '../MountObserver.js';
|
|
3
|
+
import { prep, clone } from './affine.js';
|
|
4
|
+
const previousObservers = new WeakMap();
|
|
3
5
|
export function beKindred(fragment, el) {
|
|
6
|
+
if (!fragment.isConnected)
|
|
7
|
+
throw 'too soon, use affine';
|
|
4
8
|
const qry = toQuery(el);
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (insertAttrs !== null) {
|
|
14
|
-
const attrs = splitRefs(insertAttrs);
|
|
15
|
-
map = {};
|
|
16
|
-
for (const attr of attrs) {
|
|
17
|
-
map[attr] = el.getAttribute(attr);
|
|
9
|
+
const previousObserversOfFragment = previousObservers.get(fragment);
|
|
10
|
+
if (previousObserversOfFragment !== undefined) {
|
|
11
|
+
const staleObservers = previousObserversOfFragment.filter(x => el.matches(x[0]));
|
|
12
|
+
const nonStaleObservers = previousObserversOfFragment.filter(x => !el.matches(x[0]));
|
|
13
|
+
if (staleObservers !== undefined && staleObservers.length > 0) {
|
|
14
|
+
for (const staleObserver of staleObservers) {
|
|
15
|
+
staleObserver[1].disconnect(fragment);
|
|
16
|
+
}
|
|
18
17
|
}
|
|
18
|
+
previousObservers.set(fragment, nonStaleObservers);
|
|
19
19
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
const { elFragment, map } = prep(el);
|
|
21
|
+
const mo = new MountObserver({
|
|
22
|
+
on: qry,
|
|
23
|
+
do: {
|
|
24
|
+
mount: (matchingElement) => {
|
|
25
|
+
clone(matchingElement, elFragment, map);
|
|
26
|
+
// const fragmentClone = elFragment.cloneNode(true) as DocumentFragment;
|
|
27
|
+
// matchingElement.replaceChildren(fragmentClone);
|
|
28
|
+
// if(map !== null){
|
|
29
|
+
// for(const key in map){
|
|
30
|
+
// const value = map[key]!;
|
|
31
|
+
// matchingElement.setAttribute(key, value);
|
|
32
|
+
// }
|
|
33
|
+
// }
|
|
27
34
|
}
|
|
28
35
|
}
|
|
36
|
+
});
|
|
37
|
+
mo.observe(fragment);
|
|
38
|
+
if (previousObservers.has(fragment)) {
|
|
39
|
+
previousObservers.get(fragment).push([qry, mo]);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
previousObservers.set(fragment, [[qry, mo]]);
|
|
29
43
|
}
|
|
44
|
+
return mo;
|
|
30
45
|
}
|
package/slotkin/beKindred.ts
CHANGED
|
@@ -1,31 +1,55 @@
|
|
|
1
1
|
import {toQuery} from './toQuery.js';
|
|
2
2
|
import {splitRefs} from '../refid/splitRefs.js';
|
|
3
|
+
import {MountObserver} from '../MountObserver.js';
|
|
4
|
+
import {prep, clone} from './affine.js';
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
type OnMountObserver = [string, MountObserver];
|
|
7
|
+
const previousObservers = new WeakMap<DocumentFragment | Element, Array<OnMountObserver>>();
|
|
8
|
+
|
|
9
|
+
export function beKindred(
|
|
10
|
+
fragment: DocumentFragment | Element,
|
|
11
|
+
el: Element,
|
|
12
|
+
){
|
|
13
|
+
if(!fragment.isConnected) throw 'too soon, use affine';
|
|
5
14
|
const qry = toQuery(el);
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if(insertAttrs !== null){
|
|
15
|
-
const attrs = splitRefs(insertAttrs);
|
|
16
|
-
map = {};
|
|
17
|
-
for(const attr of attrs){
|
|
18
|
-
map[attr] = el.getAttribute(attr)!;
|
|
15
|
+
const previousObserversOfFragment = previousObservers.get(fragment);
|
|
16
|
+
if(previousObserversOfFragment !== undefined){
|
|
17
|
+
const staleObservers = previousObserversOfFragment.filter(x => el.matches(x[0]));
|
|
18
|
+
const nonStaleObservers = previousObserversOfFragment.filter(x => !el.matches(x[0]));
|
|
19
|
+
if(staleObservers !== undefined && staleObservers.length > 0){
|
|
20
|
+
for(const staleObserver of staleObservers){
|
|
21
|
+
staleObserver[1].disconnect(fragment);
|
|
22
|
+
}
|
|
19
23
|
}
|
|
24
|
+
previousObservers.set(fragment, nonStaleObservers);
|
|
20
25
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
const {elFragment, map} = prep(el);
|
|
30
|
+
|
|
31
|
+
const mo = new MountObserver({
|
|
32
|
+
on: qry,
|
|
33
|
+
do: {
|
|
34
|
+
mount: (matchingElement) => {
|
|
35
|
+
clone(matchingElement, elFragment, map);
|
|
36
|
+
// const fragmentClone = elFragment.cloneNode(true) as DocumentFragment;
|
|
37
|
+
// matchingElement.replaceChildren(fragmentClone);
|
|
38
|
+
// if(map !== null){
|
|
39
|
+
// for(const key in map){
|
|
40
|
+
// const value = map[key]!;
|
|
41
|
+
// matchingElement.setAttribute(key, value);
|
|
42
|
+
// }
|
|
43
|
+
// }
|
|
28
44
|
}
|
|
29
45
|
}
|
|
46
|
+
});
|
|
47
|
+
mo.observe(fragment);
|
|
48
|
+
if(previousObservers.has(fragment)){
|
|
49
|
+
previousObservers.get(fragment)!.push([qry, mo]);
|
|
50
|
+
}else{
|
|
51
|
+
previousObservers.set(fragment, [[qry, mo]]);
|
|
30
52
|
}
|
|
53
|
+
return mo;
|
|
54
|
+
|
|
31
55
|
}
|
package/slotkin/wrap.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const wrapped = Symbol.for('50tzQZt95ECXUtHF7a40og');
|
|
2
|
+
export function wrap(templ, base) {
|
|
3
|
+
const wasWrapped = templ[wrapped];
|
|
4
|
+
if (!wasWrapped) {
|
|
5
|
+
templ[wrapped] = true;
|
|
6
|
+
if (templ.content.childElementCount > 1) {
|
|
7
|
+
const start = document.createComment(base);
|
|
8
|
+
templ.content.prepend(start);
|
|
9
|
+
const end = document.createComment(`/${base}`);
|
|
10
|
+
templ.content.appendChild(end);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
package/slotkin/wrap.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const wrapped = Symbol.for('50tzQZt95ECXUtHF7a40og');
|
|
2
|
+
|
|
3
|
+
export function wrap(
|
|
4
|
+
templ: HTMLTemplateElement,
|
|
5
|
+
base: string,
|
|
6
|
+
){
|
|
7
|
+
const wasWrapped = (<any>templ)[wrapped];
|
|
8
|
+
if (!wasWrapped) {
|
|
9
|
+
(<any>templ)[wrapped] = true;
|
|
10
|
+
if (templ.content.childElementCount > 1) {
|
|
11
|
+
const start = document.createComment(base);
|
|
12
|
+
templ.content.prepend(start);
|
|
13
|
+
const end = document.createComment(`/${base}`);
|
|
14
|
+
templ.content.appendChild(end);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import {IEnhancement, BEAllProps} from '../trans-render/be/types';
|
|
2
|
-
import {
|
|
2
|
+
import {Specifier} from '../trans-render/dss/types';
|
|
3
3
|
|
|
4
|
-
export interface EndUserProps<TProps, TMethods, TElement = {}> extends IEnhancement
|
|
5
|
-
|
|
6
|
-
xform: XForm<TProps, TMethods, TElement>,
|
|
7
|
-
initModel?: TProps & TMethods,
|
|
8
|
-
slotMap?: any,
|
|
4
|
+
export interface EndUserProps<TProps, TMethods, TElement = {}> extends IEnhancement{
|
|
5
|
+
|
|
9
6
|
}
|
|
10
7
|
|
|
11
8
|
export interface AllProps<TProps, TMethods, TElement = {}> extends EndUserProps<TProps, TMethods, TElement>{
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
includeRules: Array<IncludeRule>;
|
|
10
|
+
nodesToInclude: Array<Element>;
|
|
14
11
|
}
|
|
15
12
|
|
|
16
13
|
export type AP = AllProps<any, any, any>;
|
|
@@ -23,6 +20,11 @@ export type BAP = AP & BEAllProps;
|
|
|
23
20
|
|
|
24
21
|
|
|
25
22
|
export interface Actions{
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
hydrate(self: BAP): Promise<PAP>;
|
|
24
|
+
include(self: BAP): void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface IncludeRule {
|
|
28
|
+
remoteSpecifierString?: string,
|
|
29
|
+
remoteSpecifier: Specifier,
|
|
28
30
|
}
|