mount-observer 0.0.39 → 0.0.41
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 +50 -3
- package/MountObserver.ts +52 -7
- package/README.md +82 -13
- package/package.json +2 -2
- package/ts-refs/be-a-beacon/types.d.ts +3 -2
- package/ts-refs/be-alit/types.d.ts +1 -0
- package/ts-refs/be-based/types.d.ts +32 -0
- package/ts-refs/be-bound/types.d.ts +0 -3
- package/ts-refs/be-buttoned-up/types.d.ts +21 -0
- package/ts-refs/be-calculating/types.d.ts +4 -0
- package/ts-refs/be-clonable/types.d.ts +28 -0
- package/ts-refs/be-delible/types.d.ts +26 -0
- package/ts-refs/be-elevating/types.d.ts +55 -0
- package/ts-refs/be-eventing/types.d.ts +27 -0
- package/ts-refs/be-formalizing/types.d.ts +29 -0
- package/ts-refs/be-formidable/types.d.ts +64 -0
- package/ts-refs/be-kvetching/types.d.ts +24 -0
- package/ts-refs/be-literate/types.d.ts +10 -2
- package/ts-refs/be-mediating/types.d.ts +34 -0
- package/ts-refs/be-methodical/types.d.ts +20 -0
- package/ts-refs/be-modding/types.d.ts +18 -0
- package/ts-refs/{be-observant → be-observing}/types.d.ts +7 -4
- package/ts-refs/be-parsed/types.d.ts +19 -0
- package/ts-refs/be-persistent/types.d.ts +66 -0
- package/ts-refs/be-reformable/types.d.ts +48 -0
- package/ts-refs/be-render-neutral/types.d.ts +29 -0
- package/ts-refs/be-switched/types.d.ts +25 -19
- package/ts-refs/be-typed/types.d.ts +36 -0
- package/ts-refs/be-written/types.d.ts +37 -0
- package/ts-refs/for-fetch/types.d.ts +175 -0
- package/ts-refs/mount-observer/types.d.ts +6 -1
- package/ts-refs/trans-render/XV/types.d.ts +69 -0
- package/ts-refs/trans-render/asmr/types.d.ts +130 -0
- package/ts-refs/trans-render/be/types.d.ts +188 -0
- package/ts-refs/trans-render/dss/types.d.ts +159 -0
- package/ts-refs/trans-render/froop/types.d.ts +451 -0
- package/ts-refs/trans-render/funions/types.d.ts +12 -0
- package/ts-refs/trans-render/lib/mixins/types.d.ts +42 -0
- package/ts-refs/trans-render/lib/prs/types.d.ts +39 -0
- package/ts-refs/trans-render/lib/types.d.ts +489 -0
- package/ts-refs/trans-render/types.d.ts +14 -2
- package/ts-refs/xtal-element/types.d.ts +42 -0
package/MountObserver.js
CHANGED
|
@@ -21,11 +21,14 @@ export class MountObserver extends EventTarget {
|
|
|
21
21
|
isComplex = reducedMatch.includes(' ') || (reducedMatch.includes(':') && reducedMatch.includes('('));
|
|
22
22
|
}
|
|
23
23
|
this.#isComplex = isComplex;
|
|
24
|
-
if (whereElementIntersectsWith
|
|
24
|
+
if (whereElementIntersectsWith)
|
|
25
25
|
throw 'NI'; //not implemented
|
|
26
26
|
this.#mountInit = init;
|
|
27
27
|
this.#abortController = new AbortController();
|
|
28
|
-
this.mountedElements =
|
|
28
|
+
this.mountedElements = {
|
|
29
|
+
weakSet: new WeakSet(),
|
|
30
|
+
setWeak: new Set(),
|
|
31
|
+
};
|
|
29
32
|
this.#disconnected = new WeakSet();
|
|
30
33
|
//this.#unmounted = new WeakSet();
|
|
31
34
|
}
|
|
@@ -109,6 +112,33 @@ export class MountObserver extends EventTarget {
|
|
|
109
112
|
this.dispatchEvent(new Event('disconnectedCallback'));
|
|
110
113
|
}
|
|
111
114
|
async observe(within) {
|
|
115
|
+
const init = this.#mountInit;
|
|
116
|
+
const { whereMediaMatches } = init;
|
|
117
|
+
if (whereMediaMatches === undefined) {
|
|
118
|
+
await this.#observe2(within);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const mql = window.matchMedia(whereMediaMatches);
|
|
122
|
+
if (mql.matches) {
|
|
123
|
+
await this.#observe2(within);
|
|
124
|
+
}
|
|
125
|
+
mql.addEventListener('change', async (e) => {
|
|
126
|
+
if (e.matches) {
|
|
127
|
+
if (this.objNde === undefined) {
|
|
128
|
+
await this.#observe2(within);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
await this.#mountAll();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
if (this.objNde !== undefined) {
|
|
136
|
+
await this.#dismountAll();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
async #observe2(within) {
|
|
112
142
|
await this.#selector();
|
|
113
143
|
this.objNde = new WeakRef(within);
|
|
114
144
|
const nodeToMonitor = this.#isComplex ? (within instanceof ShadowRoot ? within : within.getRootNode()) : within;
|
|
@@ -218,10 +248,14 @@ export class MountObserver extends EventTarget {
|
|
|
218
248
|
const alreadyMounted = await this.#filterAndDismount();
|
|
219
249
|
const mount = this.#mountInit.do?.mount;
|
|
220
250
|
const { import: imp } = this.#mountInit;
|
|
251
|
+
const me = this.mountedElements;
|
|
221
252
|
for (const match of matching) {
|
|
222
253
|
if (alreadyMounted.has(match))
|
|
223
254
|
continue;
|
|
224
|
-
|
|
255
|
+
if (!me.weakSet.has(match)) {
|
|
256
|
+
me.setWeak.add(new WeakRef(match));
|
|
257
|
+
me.weakSet.add(match);
|
|
258
|
+
}
|
|
225
259
|
if (imp !== undefined) {
|
|
226
260
|
switch (typeof imp) {
|
|
227
261
|
case 'string':
|
|
@@ -312,6 +346,19 @@ export class MountObserver extends EventTarget {
|
|
|
312
346
|
this.dispatchEvent(new DismountEvent(unmatch));
|
|
313
347
|
}
|
|
314
348
|
}
|
|
349
|
+
async #dismountAll() {
|
|
350
|
+
const mounted = this.#mountedList;
|
|
351
|
+
if (mounted === undefined)
|
|
352
|
+
return;
|
|
353
|
+
this.#dismount(mounted.map(x => x.deref()).filter(x => x !== undefined));
|
|
354
|
+
}
|
|
355
|
+
async #mountAll() {
|
|
356
|
+
//TODO: copilot created, check if needed
|
|
357
|
+
const { whereSatisfies, whereInstanceOf } = this.#mountInit;
|
|
358
|
+
const match = await this.#selector();
|
|
359
|
+
const els = Array.from(document.querySelectorAll(match));
|
|
360
|
+
this.#filterAndMount(els, false, true);
|
|
361
|
+
}
|
|
315
362
|
async #filterAndDismount() {
|
|
316
363
|
const returnSet = new Set();
|
|
317
364
|
if (this.#mountedList !== undefined) {
|
package/MountObserver.ts
CHANGED
|
@@ -2,7 +2,7 @@ import {MountInit, IMountObserver, AddMutationEventListener,
|
|
|
2
2
|
MutationEvent, dismountEventName, mountEventName, IMountEvent, IDismountEvent,
|
|
3
3
|
disconnectedEventName, IDisconnectEvent, IAttrChangeEvent, attrChangeEventName, AttrChangeInfo, loadEventName, ILoadEvent,
|
|
4
4
|
AttrParts,
|
|
5
|
-
MOSE
|
|
5
|
+
MOSE, WeakDual
|
|
6
6
|
} from './ts-refs/mount-observer/types';
|
|
7
7
|
import {RootMutObs} from './RootMutObs.js';
|
|
8
8
|
export {MOSE} from './ts-refs/mount-observer/types';
|
|
@@ -14,7 +14,7 @@ export class MountObserver extends EventTarget implements IMountObserver{
|
|
|
14
14
|
#mountInit: MountInit;
|
|
15
15
|
//#rootMutObs: RootMutObs | undefined;
|
|
16
16
|
#abortController: AbortController;
|
|
17
|
-
mountedElements:
|
|
17
|
+
mountedElements: WeakDual<Element>;
|
|
18
18
|
#mountedList: Array<WeakRef<Element>> | undefined;
|
|
19
19
|
#disconnected: WeakSet<Element>;
|
|
20
20
|
//#unmounted: WeakSet<Element>;
|
|
@@ -31,10 +31,13 @@ export class MountObserver extends EventTarget implements IMountObserver{
|
|
|
31
31
|
isComplex = reducedMatch.includes(' ') || (reducedMatch.includes(':') && reducedMatch.includes('('));
|
|
32
32
|
}
|
|
33
33
|
this.#isComplex = isComplex;
|
|
34
|
-
if(whereElementIntersectsWith
|
|
34
|
+
if(whereElementIntersectsWith) throw 'NI'; //not implemented
|
|
35
35
|
this.#mountInit = init;
|
|
36
36
|
this.#abortController = new AbortController();
|
|
37
|
-
this.mountedElements =
|
|
37
|
+
this.mountedElements = {
|
|
38
|
+
weakSet: new WeakSet(),
|
|
39
|
+
setWeak: new Set(),
|
|
40
|
+
};
|
|
38
41
|
this.#disconnected = new WeakSet();
|
|
39
42
|
//this.#unmounted = new WeakSet();
|
|
40
43
|
}
|
|
@@ -118,6 +121,32 @@ export class MountObserver extends EventTarget implements IMountObserver{
|
|
|
118
121
|
}
|
|
119
122
|
|
|
120
123
|
async observe(within: Node){
|
|
124
|
+
const init = this.#mountInit;
|
|
125
|
+
const {whereMediaMatches} = init;
|
|
126
|
+
if(whereMediaMatches === undefined){
|
|
127
|
+
await this.#observe2(within);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const mql = window.matchMedia(whereMediaMatches);
|
|
131
|
+
if(mql.matches){
|
|
132
|
+
await this.#observe2(within);
|
|
133
|
+
}
|
|
134
|
+
mql.addEventListener('change', async (e) => {
|
|
135
|
+
if(e.matches){
|
|
136
|
+
if(this.objNde === undefined){
|
|
137
|
+
await this.#observe2(within);
|
|
138
|
+
}else{
|
|
139
|
+
await this.#mountAll();
|
|
140
|
+
}
|
|
141
|
+
}else{
|
|
142
|
+
if(this.objNde !== undefined){
|
|
143
|
+
await this.#dismountAll();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async #observe2(within: Node){
|
|
121
150
|
await this.#selector();
|
|
122
151
|
this.objNde = new WeakRef(within);
|
|
123
152
|
const nodeToMonitor = this.#isComplex ? (within instanceof ShadowRoot ? within : within.getRootNode()) : within;
|
|
@@ -201,7 +230,6 @@ export class MountObserver extends EventTarget implements IMountObserver{
|
|
|
201
230
|
}, {signal: this.#abortController.signal});
|
|
202
231
|
|
|
203
232
|
await this.#inspectWithin(within, true);
|
|
204
|
-
|
|
205
233
|
}
|
|
206
234
|
|
|
207
235
|
static synthesize(within: Document | ShadowRoot, customElement: {new(): HTMLElement}, mose: MOSE){
|
|
@@ -232,10 +260,13 @@ export class MountObserver extends EventTarget implements IMountObserver{
|
|
|
232
260
|
const alreadyMounted = await this.#filterAndDismount();
|
|
233
261
|
const mount = this.#mountInit.do?.mount;
|
|
234
262
|
const {import: imp} = this.#mountInit;
|
|
235
|
-
|
|
263
|
+
const me = this.mountedElements;
|
|
236
264
|
for(const match of matching){
|
|
237
265
|
if(alreadyMounted.has(match)) continue;
|
|
238
|
-
|
|
266
|
+
if(!me.weakSet.has(match)){
|
|
267
|
+
me.setWeak.add(new WeakRef(match));
|
|
268
|
+
me.weakSet.add(match);
|
|
269
|
+
}
|
|
239
270
|
if(imp !== undefined){
|
|
240
271
|
switch(typeof imp){
|
|
241
272
|
case 'string':
|
|
@@ -332,6 +363,20 @@ export class MountObserver extends EventTarget implements IMountObserver{
|
|
|
332
363
|
}
|
|
333
364
|
}
|
|
334
365
|
|
|
366
|
+
async #dismountAll(){
|
|
367
|
+
const mounted = this.#mountedList;
|
|
368
|
+
if(mounted === undefined) return;
|
|
369
|
+
this.#dismount(mounted.map(x => x.deref()).filter(x => x !== undefined) as Array<Element>);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
async #mountAll(){
|
|
373
|
+
//TODO: copilot created, check if needed
|
|
374
|
+
const {whereSatisfies, whereInstanceOf} = this.#mountInit;
|
|
375
|
+
const match = await this.#selector();
|
|
376
|
+
const els = Array.from(document.querySelectorAll(match));
|
|
377
|
+
this.#filterAndMount(els, false, true);
|
|
378
|
+
}
|
|
379
|
+
|
|
335
380
|
async #filterAndDismount(): Promise<Set<Element>>{
|
|
336
381
|
const returnSet = new Set<Element>();
|
|
337
382
|
if(this.#mountedList !== undefined){
|
package/README.md
CHANGED
|
@@ -11,15 +11,17 @@ Author: Bruce B. Anderson (with valuable feedback from @doeixd )
|
|
|
11
11
|
|
|
12
12
|
Issues / pr's / polyfill: [mount-observer](https://github.com/bahrus/mount-observer)
|
|
13
13
|
|
|
14
|
-
Last Update:
|
|
14
|
+
Last Update: Dec 11, 2024
|
|
15
15
|
|
|
16
16
|
## Benefits of this API
|
|
17
17
|
|
|
18
18
|
What follows is a far more ambitious alternative to the [lazy custom element proposal](https://github.com/w3c/webcomponents/issues/782). The goals of the MountObserver api are more encompassing, and less focused on registering custom elements. In fact, this proposal addresses numerous use cases in one api. It is basically mapping common filtering conditions in the DOM, to mounting a "campaign" of some sort, like importing a resource, and/or progressively enhancing an element, and/or "binding from a distance".
|
|
19
19
|
|
|
20
|
-
["Binding from a distance"](https://github.com/WICG/webcomponents/issues/1035#issuecomment-1806393525) refers to empowering the developer to essentially manage their own "stylesheets" -- but rather than for purposes of styling, using these rules to attach behaviors, set property values, etc, to the HTML as it streams in. Libraries that take this approach include [Corset](https://corset.dev/) and [trans-render](https://github.com/bahrus/trans-render). The concept has been promoted by a [number](https://bkardell.com/blog/CSSLike.html) [of](https://www.w3.org/TR/NOTE-AS) [prominent](https://www.xanthir.com/blog/b4K_0) voices in the community.
|
|
20
|
+
["Binding from a distance"](https://github.com/WICG/webcomponents/issues/1035#issuecomment-1806393525) refers to empowering the developer to essentially manage their own "stylesheets" -- but rather than for purposes of styling, using these rules to attach behaviors, set property values, etc, to the HTML as it streams in. Libraries that take this approach include [Corset](https://corset.dev/) and [trans-render](https://github.com/bahrus/trans-render), [selector-observer](https://github.com/josh/selector-observer), [pure](http://web.archive.org/web/20160313152905/https://beebole.com/pure/), [weld](https://github.com/tmpvar/weld), [bess](https://github.com/bkardell/bess). The concept has been promoted by a [number](https://bkardell.com/blog/CSSLike.html) [of](https://www.w3.org/TR/NOTE-AS) [prominent](https://www.xanthir.com/blog/b4K_0) voices in the community.
|
|
21
21
|
|
|
22
|
-
The underlying theme is this api is meant to make it easy for the developer to do the right thing, by encouraging lazy loading and smaller footprints. It rolls up most all the other observer api's into one, including, potentially, [a selector
|
|
22
|
+
The underlying theme is this api is meant to make it easy for the developer to do the right thing, by encouraging lazy loading and smaller footprints. It rolls up most all the other observer api's into one, including, potentially, [a selector observer](https://github.com/whatwg/dom/issues/1285), which may be a similar duplicate to [the match-media counterpart proposal](https://github.com/whatwg/dom/issues/1225).
|
|
23
|
+
|
|
24
|
+
### Finite Element Analysis
|
|
23
25
|
|
|
24
26
|
Most every web application can be recursively broken down into logical regions, building blocks which are assembled together to form the whole site.
|
|
25
27
|
|
|
@@ -33,7 +35,7 @@ At the micro level, components will have few, if any, dependencies, and those de
|
|
|
33
35
|
|
|
34
36
|
ES module based web components may or may not be the best fit for these application macro "modules". A better fit might be a server-centric solution, like Rails, just to take an example.
|
|
35
37
|
|
|
36
|
-
A significant pain point has to do with
|
|
38
|
+
A significant pain point has to do with downloading all the third-party web components and/or (progressive) enhancements that these macro components / compositions require, and loading them into memory only when needed.
|
|
37
39
|
|
|
38
40
|
|
|
39
41
|
### Does this api make the impossible possible?
|
|
@@ -162,7 +164,7 @@ So what this does is only check for the presence of an element with tag name "my
|
|
|
162
164
|
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:
|
|
163
165
|
|
|
164
166
|
```html
|
|
165
|
-
<script type="mountobserver"
|
|
167
|
+
<script type="mountobserver" onload="{...}" onmount="{
|
|
166
168
|
const {matchingElement} = event;
|
|
167
169
|
const {localName} = matchingElement;
|
|
168
170
|
if(!customElements.get(localName)) {
|
|
@@ -188,11 +190,38 @@ const {modules, observer, mountedElements, mountInit} = myMountObserver;
|
|
|
188
190
|
|
|
189
191
|
The "scope" of the observer would be the ShadowRoot containing the script element (or the document outside Shadow if placed outside any shadow DOM, like in the head element).
|
|
190
192
|
|
|
191
|
-
|
|
193
|
+
Once again, arrays of settings could be supported, which, in practice, would greatly increase the ratio between declarative, JSON-parsable instructions that could be performed in low-level c++/rust threads, vs custom JavaScript in the example above. The events / callbacks would need to provide the index of which set of criteria was just fulfilled.
|
|
192
194
|
|
|
193
195
|
> [!Note]
|
|
194
196
|
> To support the event handlers above, I believe it would require that CSP solutions factor in both the inner content of the script element as well as all the event handlers via the string concatenation operator. I actually think such support is quite critical due to lack of support of import.meta.[some reference to the script element] not being available, as it was pre-ES Modules.
|
|
195
197
|
|
|
198
|
+
## Specific solution for lazy loading custom element definitions
|
|
199
|
+
|
|
200
|
+
Since the example we've been dwelling on so far (lazy custom element definition) seems like such a pressing, common requirement, and was in fact the originating impetus for this proposal, we can go a step further and make the example above 100% declarative, thus resulting in a less clunky interplay between JSON and custom script. This is meant as a way of illustrating how the platform could continue to extend this proposal going forward.
|
|
201
|
+
|
|
202
|
+
The syntax below is just one, "spit-balling" way this could be done, as an example, and would require absorbing final heuristics from other custom element initiatives (such as declarative custom elements) when they get added to the platform.
|
|
203
|
+
|
|
204
|
+
```html
|
|
205
|
+
<script type="mountobserver">
|
|
206
|
+
{
|
|
207
|
+
"on":"my-element",
|
|
208
|
+
"import": [
|
|
209
|
+
["./my-element-small.css", {type: "css"}],
|
|
210
|
+
"./my-element.js",
|
|
211
|
+
],
|
|
212
|
+
"define": {
|
|
213
|
+
"targetRegistry": "CustomElements",
|
|
214
|
+
"targetScope": "global",
|
|
215
|
+
"styleModules": [0],
|
|
216
|
+
"classDefinition": {
|
|
217
|
+
"module": 1,
|
|
218
|
+
"exportSymbol": 'MyElement'
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
</script>
|
|
223
|
+
```
|
|
224
|
+
|
|
196
225
|
|
|
197
226
|
## Shadow Root inheritance
|
|
198
227
|
|
|
@@ -266,20 +295,25 @@ const observer = new MountObserver({
|
|
|
266
295
|
on: 'div > p + p ~ span[class$="name"]',
|
|
267
296
|
whereMediaMatches: '(max-width: 1250px)',
|
|
268
297
|
whereSizeOfContainerMatches: '(min-width: 700px)',
|
|
269
|
-
whereInstanceOf: [HTMLMarqueeElement],
|
|
270
|
-
whereSatisfies: async (matchingElement, context) => true,
|
|
298
|
+
whereInstanceOf: [HTMLMarqueeElement], //or ['HTMLMarqueeElement']
|
|
271
299
|
whereLangIn: ['en-GB'],
|
|
272
300
|
whereConnection:{
|
|
273
301
|
effectiveTypeIn: ["slow-2g"],
|
|
274
302
|
},
|
|
275
303
|
import: ['./my-element-small.css', {type: 'css'}],
|
|
276
304
|
do: {
|
|
305
|
+
confirm: (matchingElement, (e: MountObserverConfirmEvent) => {
|
|
306
|
+
e.isSatisfied = true;
|
|
307
|
+
e.preventDefault();
|
|
308
|
+
}),
|
|
277
309
|
mount: ({localName}, {modules}) => {
|
|
278
310
|
...
|
|
279
311
|
},
|
|
280
312
|
dismount: ...,
|
|
281
313
|
disconnect: ...,
|
|
314
|
+
move: ...,
|
|
282
315
|
reconnect: ...,
|
|
316
|
+
confirm: ...,
|
|
283
317
|
reconfirm: ...,
|
|
284
318
|
exit: ...,
|
|
285
319
|
forget: ...,
|
|
@@ -287,7 +321,14 @@ const observer = new MountObserver({
|
|
|
287
321
|
});
|
|
288
322
|
```
|
|
289
323
|
|
|
290
|
-
|
|
324
|
+
## InstanceOf checks in detail
|
|
325
|
+
|
|
326
|
+
Carving out the special "whereInstanceOf" check is provided based on the assumption that there's a performance benefit from doing so. If not, the developer could just add that check inside the "confirm" callback logic. For built-in elements, we can alternatively provide the string name, as indicated in the comment above, which certainly makes it JSON serializable, thus making it easy as pie to include in the MOSE JSON payload. I don't think there would be any ambiguity in doing so, which means I believe that answers the mystery in my mind whether it could be part of the low-level checklist that could be done within the c++/rust code / thread.
|
|
327
|
+
|
|
328
|
+
The picture becomes murkier for custom elements. The best solution in that case seems to be to utilize customElements.getName(...) as a basis for the match, but at first glance, that could preclude being able to use base classes which a family of custom elements subclass, if that superclass isn't itself a custom element. I suppose the solution to this conundrum, when warranted, is simply to burden the developer with defining a custom element for the superclass, and thus assigning it a name, applicable within ShadowDOM scopes as needed, even though it isn't actually necessarily used for any live custom elements. This would require already having imported the base class, only benefitting from lazy loading the code needed for each sub class, which might not always be all that high as a percentage, compared to the base class.
|
|
329
|
+
|
|
330
|
+
However, where this support for "whereInstanceOf" would be *most* helpful is when it comes to [*custom enhancements*](https://github.com/WICG/webcomponents/issues/1000) that only wish to lazily layer some heavy lifting functionality on top of certain families of already loaded and upgraded custom elements (possibly in addition to some (specified) built in elements). Here, the lazy loading of the *entire custom **enhancement***, based on the presence in the DOM of a member of the family of custom elements, would, if my calculations are correct, result in providing a significant benefit.
|
|
331
|
+
|
|
291
332
|
|
|
292
333
|
<!--
|
|
293
334
|
|
|
@@ -295,13 +336,14 @@ Callbacks like we see above are useful for tight coupling, and probably are unma
|
|
|
295
336
|
|
|
296
337
|
-->
|
|
297
338
|
|
|
298
|
-
However, since these rules may be of interest to multiple parties, it is useful to also provide the ability for multiple parties to subscribe to these css rules. This can be done via:
|
|
299
|
-
|
|
300
339
|
## Subscribing
|
|
301
340
|
|
|
302
341
|
Subscribing can be done via:
|
|
303
342
|
|
|
304
343
|
```JavaScript
|
|
344
|
+
observer.addEventListener('confirm', e => {
|
|
345
|
+
e.isSatisfied = true; //or false to prevent the mount event below
|
|
346
|
+
});
|
|
305
347
|
observer.addEventListener('mount', e => {
|
|
306
348
|
console.log({
|
|
307
349
|
matchingElement: e.matchingElement,
|
|
@@ -314,6 +356,9 @@ observer.addEventListener('dismount', e => {
|
|
|
314
356
|
observer.addEventListener('disconnect', e => {
|
|
315
357
|
...
|
|
316
358
|
});
|
|
359
|
+
observer.addEventListener('move', e => {
|
|
360
|
+
...
|
|
361
|
+
});
|
|
317
362
|
observer.addEventListener('reconnect', e => {
|
|
318
363
|
...
|
|
319
364
|
});
|
|
@@ -336,14 +381,28 @@ The moment a MountObserver instance's "observe" method is called (passing in a r
|
|
|
336
381
|
|
|
337
382
|
If an element that is in "mounted" state according to a MountObserver instance is moved from one parent DOM element to another:
|
|
338
383
|
|
|
339
|
-
1) "disconnect" event is dispatched from the MountObserver instance the moment the mounted element is disconnected from the DOM fragment.
|
|
384
|
+
1) "disconnect" event is dispatched from the MountObserver instance the moment the mounted element is disconnected from the DOM fragment (but not if employing the experimental but promising atomic moving API).
|
|
340
385
|
2) If/when the element is added somewhere else in the DOM tree, the mountObserver instance will dispatch event "reconnect", regardless of where. [Note: can't polyfill this very easily]
|
|
341
386
|
3) If the mounted element is added outside the rootNode being observed, the mountObserver instance will dispatch event "exit", and the MountObserver instance will relinquish any further responsibility for this element.
|
|
342
387
|
4) Ideally event "forget" would be dispatched just before the platform garbage collects an element the MountObserver instance is still monitoring, after all hard references are relinquished (or is that self-contradictory?).
|
|
343
388
|
5) If the new place it was added remains within the original rootNode and remains mounted, the MountObserver instance dispatches event "reconfirmed".
|
|
344
389
|
6) If the element no longer satisfies the criteria of the MountObserver instance, the MountObserver instance will dispatch event "dismount".
|
|
345
390
|
|
|
346
|
-
|
|
391
|
+
The move event would become available at the outset of the [atomic moving](https://github.com/whatwg/dom/issues/1255) proposal getting shipped universally.
|
|
392
|
+
|
|
393
|
+
## Justification for callbacks as well as events, and discussion of the signature
|
|
394
|
+
|
|
395
|
+
Callbacks like we saw in our earlier examples above are useful for tight coupling, and probably are unmatched in terms of performance. The expression that the "do" field points to could in fact be a (stateful) user defined class instance.
|
|
396
|
+
|
|
397
|
+
However, since these rules may be of interest to multiple parties, it is useful to also provide the ability for multiple parties to subscribe to these DOM filtering events.
|
|
398
|
+
|
|
399
|
+
If the performance isn't impacted, I think it would be most convenient for the developer if, at a minimum, the second argument of the callbacks above in fact precisely match the loosely coupled events. The callback would get the first dibs on the event, and have the opportunity to prevent the event from going any further before getting dispatched, using something like preventDefault. I don't yet have any compelling use cases for that scenario, but I think there probably are some.
|
|
400
|
+
|
|
401
|
+
In which case the argument becomes quite strong that the inconsistency of making the callback methods above have a separate parameter where the matching element is passed is unwise. Simply making the matching element be part of the event payload, as is done for the loosely coupled events discussed above, would reduce the learning curve, and make it easier to share logic between the two.
|
|
402
|
+
|
|
403
|
+
On the other hand, providing the matching element as a separate parameter makes the ergonomics a tiny bit smoother as far as dynamically ascertaining the local name and other properties of the element (i.e. destructuring requires one more step for lazily defining the custom element).
|
|
404
|
+
|
|
405
|
+
I'm on the fence on that one. I think the benefits either way to DX are so small, that performance metrics should probably dictate which way to go.
|
|
347
406
|
|
|
348
407
|
## Dismounting
|
|
349
408
|
|
|
@@ -687,6 +746,16 @@ This proposal "sneaks in" one more feature, that perhaps should stand separately
|
|
|
687
746
|
|
|
688
747
|
Also, this proposal is partly focused on better management of importing resources "from a distance", in particular via imports carried out via http. Is it such a stretch to look closely at scenarios where that distance happens to be shorter, i.e. found somewhere [in the document tree structure](https://github.com/tc39/proposal-module-expressions)?
|
|
689
748
|
|
|
749
|
+
The need for importing templates by id is also demonstrated by Corset's [Todo list example](https://codepen.io/matthewp/details/ZEXpJYr):
|
|
750
|
+
|
|
751
|
+
```CSS
|
|
752
|
+
#todos {
|
|
753
|
+
each-items: ${todos};
|
|
754
|
+
each-template: select(#todo-template);
|
|
755
|
+
each-key: title;
|
|
756
|
+
}
|
|
757
|
+
```
|
|
758
|
+
|
|
690
759
|
The mount-observer is always on the lookout for template tags with a src attribute starting with #:
|
|
691
760
|
|
|
692
761
|
```html
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mount-observer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.41",
|
|
4
4
|
"description": "Observe and act on css matches.",
|
|
5
5
|
"main": "MountObserver.js",
|
|
6
6
|
"module": "MountObserver.js",
|
|
7
7
|
"devDependencies": {
|
|
8
|
-
"@playwright/test": "1.
|
|
8
|
+
"@playwright/test": "1.49.1",
|
|
9
9
|
"ssi-server": "0.0.1"
|
|
10
10
|
},
|
|
11
11
|
"exports": {
|
|
@@ -11,8 +11,9 @@ export type PAP = Partial<AP>;
|
|
|
11
11
|
|
|
12
12
|
export type ProPAP = Promise<PAP>;
|
|
13
13
|
|
|
14
|
+
export type BAP = AP & BEAllProps;
|
|
14
15
|
|
|
15
16
|
export interface Actions{
|
|
16
|
-
hydrate(self:
|
|
17
|
-
retire(self:
|
|
17
|
+
hydrate(self: BAP): PAP;
|
|
18
|
+
retire(self: BAP): void;
|
|
18
19
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//using be-render-neutral for now, but that could change in the future
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {IEnhancement, BEAllProps} from '../trans-render/be/types';
|
|
2
|
+
|
|
3
|
+
export interface EndUserProps extends IEnhancement{
|
|
4
|
+
forAll?: Array<string>,
|
|
5
|
+
base?: string,
|
|
6
|
+
fileName?: string
|
|
7
|
+
puntOn?: Array<string>,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface AllProps extends EndUserProps{
|
|
11
|
+
forAll: Array<string>,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
export interface PuntEvent{
|
|
16
|
+
count: number,
|
|
17
|
+
instance: Element,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type AP = AllProps;
|
|
21
|
+
|
|
22
|
+
export type PAP = Partial<AP>;
|
|
23
|
+
|
|
24
|
+
export type ProPAP = Promise<PAP>
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
export interface Actions{
|
|
29
|
+
hydrate(self: AP & BEAllProps): PAP;
|
|
30
|
+
//finale(): void;
|
|
31
|
+
}
|
|
32
|
+
|
|
@@ -12,10 +12,7 @@ export interface EndUserProps extends IEnhancement{
|
|
|
12
12
|
export interface AllProps extends EndUserProps{
|
|
13
13
|
bindingRules: Array<BindingRule>,
|
|
14
14
|
bindings: Array<Binding>,
|
|
15
|
-
//partialBindingRules?: Array<BindingRule>,
|
|
16
15
|
isParsed?: boolean,
|
|
17
|
-
//parsedWith?: boolean,
|
|
18
|
-
//parsedBetween?: boolean,
|
|
19
16
|
rawStatements?: Array<string>
|
|
20
17
|
}
|
|
21
18
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {IEnhancement, BEAllProps} from '../trans-render/be/types';
|
|
2
|
+
|
|
3
|
+
export interface EndUserProps extends IEnhancement<HTMLButtonElement>{
|
|
4
|
+
closeOnSelect: boolean;
|
|
5
|
+
eventName: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface AllProps extends EndUserProps {}
|
|
9
|
+
|
|
10
|
+
export type AP = AllProps;
|
|
11
|
+
|
|
12
|
+
export type PAP = Partial<AP>;
|
|
13
|
+
|
|
14
|
+
export type ProPAP = Promise<PAP>;
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
export type BAP = AP & BEAllProps;
|
|
18
|
+
|
|
19
|
+
export interface Actions{
|
|
20
|
+
hydrate(self: BAP): PAP
|
|
21
|
+
}
|
|
@@ -8,6 +8,7 @@ export interface EndUserProps extends IEnhancement<HTMLElement>{
|
|
|
8
8
|
forAttr?: string,
|
|
9
9
|
handler: string
|
|
10
10
|
eventArg: string,
|
|
11
|
+
js: string,
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export interface AllProps extends EndUserProps{
|
|
@@ -26,10 +27,12 @@ export interface AllProps extends EndUserProps{
|
|
|
26
27
|
isAttached?: boolean,
|
|
27
28
|
isOutputEl?: boolean,
|
|
28
29
|
enhElLocalName: string,
|
|
30
|
+
enhKey: string,
|
|
29
31
|
categorized?: boolean,
|
|
30
32
|
remSpecifierLen?: number,
|
|
31
33
|
propToAO: {[key: string] : AbsorbingObject},
|
|
32
34
|
checkedRegistry: boolean,
|
|
35
|
+
notYetParsedJS: boolean,
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
export type AP = AllProps;
|
|
@@ -50,4 +53,5 @@ export interface Actions{
|
|
|
50
53
|
genRemoteSpecifiers(self: BAP): PAP;
|
|
51
54
|
seek(self: BAP): ProPAP;
|
|
52
55
|
hydrate(self: BAP): ProPAP;
|
|
56
|
+
parseJS(self: BAP): ProPAP;
|
|
53
57
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { IEnhancement, BEAllProps } from '../trans-render/be/types';
|
|
2
|
+
|
|
3
|
+
export interface EndUserProps extends IEnhancement{
|
|
4
|
+
triggerInsertPosition: InsertPosition;
|
|
5
|
+
cloneInsertPosition?: InsertPosition;
|
|
6
|
+
buttonContent: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface AllProps extends EndUserProps{
|
|
10
|
+
byob?: boolean,
|
|
11
|
+
trigger?: WeakRef<HTMLButtonElement>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
export type AP = AllProps;
|
|
16
|
+
|
|
17
|
+
export type PAP = Partial<AP>;
|
|
18
|
+
|
|
19
|
+
export type ProPAP = Promise<PAP>;
|
|
20
|
+
|
|
21
|
+
export type BAP = AP & BEAllProps;
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
export interface Actions{
|
|
25
|
+
addCloneBtn(self: BAP): ProPAP;
|
|
26
|
+
setBtnContent(self: BAP): void;
|
|
27
|
+
beCloned(self: BAP): void;
|
|
28
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { IEnhancement, BEAllProps } from '../trans-render/be/types';
|
|
2
|
+
|
|
3
|
+
export interface EndUserProps extends IEnhancement{
|
|
4
|
+
triggerInsertPosition: InsertPosition;
|
|
5
|
+
buttonContent: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface AllProps extends EndUserProps{
|
|
9
|
+
byob?: boolean,
|
|
10
|
+
trigger: WeakRef<HTMLButtonElement>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type AP = AllProps;
|
|
14
|
+
|
|
15
|
+
export type PAP = Partial<AP>;
|
|
16
|
+
|
|
17
|
+
export type ProPAP = Promise<PAP>;
|
|
18
|
+
|
|
19
|
+
export type BAP = AP & BEAllProps;
|
|
20
|
+
|
|
21
|
+
export interface Actions{
|
|
22
|
+
|
|
23
|
+
addDeleteBtn(self: BAP): ProPAP ;
|
|
24
|
+
setBtnContent(self: BAP): void;
|
|
25
|
+
beDeleted(self: BAP): void;
|
|
26
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {IEnhancement, BEAllProps} from '../trans-render/be/types';
|
|
2
|
+
import {AbsorbingObject, SharingObject} from '../trans-render/asmr/types';
|
|
3
|
+
import { Specifier } from "../trans-render/dss/types";
|
|
4
|
+
|
|
5
|
+
export interface EndUserProps extends IEnhancement{
|
|
6
|
+
/**
|
|
7
|
+
* abbrev for pass server-rendered value
|
|
8
|
+
* This will pass the initial value from the enhanced element if applicable
|
|
9
|
+
*/
|
|
10
|
+
passSRV: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface AP extends EndUserProps {
|
|
14
|
+
//parsedStatements?: Array<ElevatingParameters>,
|
|
15
|
+
bindings: Array<Binding>,
|
|
16
|
+
rawStatements?: Array<string>,
|
|
17
|
+
bindingRules: Array<BindingRule>,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type AllProps = AP;
|
|
21
|
+
|
|
22
|
+
export type BAP = AP & BEAllProps;
|
|
23
|
+
|
|
24
|
+
export type PAP = Partial<AP>
|
|
25
|
+
|
|
26
|
+
export type ProPAP = Promise<PAP>
|
|
27
|
+
|
|
28
|
+
export interface Actions{
|
|
29
|
+
noAttrs(self: BAP): ProPAP;
|
|
30
|
+
hydrate(self: BAP): ProPAP;
|
|
31
|
+
getBindings(self: BAP): ProPAP;
|
|
32
|
+
onRawStatements(self: BAP): void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// export interface ElevatingParameters {
|
|
36
|
+
// localPropToElevate?: string,
|
|
37
|
+
// remoteSpecifiers: Array<Specifier>,
|
|
38
|
+
// localEventType?: string,
|
|
39
|
+
// }
|
|
40
|
+
|
|
41
|
+
export interface BindingRule {
|
|
42
|
+
|
|
43
|
+
localPropToElevate?: string,
|
|
44
|
+
localEventType?: string,
|
|
45
|
+
dependencyPart?: string,
|
|
46
|
+
remoteSpecifier?: Specifier,
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface Binding {
|
|
52
|
+
remoteShareObj: SharingObject,
|
|
53
|
+
localAbsObj: AbsorbingObject,
|
|
54
|
+
}
|
|
55
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {IEnhancement, BEAllProps} from '../trans-render/be/types';
|
|
2
|
+
|
|
3
|
+
// export interface EventHandlingScriptElement extends HTMLScriptElement{
|
|
4
|
+
// on: {[key: string]: (e: Event) => void}
|
|
5
|
+
// e: (e: Event) => void;
|
|
6
|
+
// }
|
|
7
|
+
|
|
8
|
+
export interface EndUserProps extends IEnhancement<HTMLScriptElement>{
|
|
9
|
+
nudges: string,
|
|
10
|
+
on: string,
|
|
11
|
+
onNudges: string,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface AllProps extends EndUserProps{}
|
|
15
|
+
|
|
16
|
+
export type AP = AllProps;
|
|
17
|
+
|
|
18
|
+
export type PAP = Partial<AP>;
|
|
19
|
+
|
|
20
|
+
export type ProPAP = Promise<PAP>;
|
|
21
|
+
|
|
22
|
+
export type BAP = AP & BEAllProps;
|
|
23
|
+
|
|
24
|
+
export interface Actions{
|
|
25
|
+
calcDefaults(self: BAP): ProPAP,
|
|
26
|
+
hydrate(self: BAP): ProPAP,
|
|
27
|
+
}
|