mount-observer 0.0.75 → 0.0.77
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 +15 -1
- package/MountObserver.ts +15 -1
- package/README.md +44 -0
- package/package.json +6 -2
- package/slotkin/getBreadth.js +19 -0
- package/slotkin/getBreadth.ts +21 -0
- package/slotkin/getFrag.js +9 -2
- package/slotkin/getFrag.ts +8 -3
- package/slotkin/wrap.js +2 -2
- package/slotkin/wrap.ts +2 -2
- package/ts-refs/be-directive/types.d.ts +43 -0
- package/ts-refs/be-switched/types.d.ts +5 -0
- package/ts-refs/mount-observer/types.d.ts +1 -0
- package/ts-refs/trans-render/dss/types.d.ts +14 -1
- package/ts-refs/trans-render/types.d.ts +2 -1
- package/ts-refs/when-resolved/types.d.ts +29 -0
package/MountObserver.js
CHANGED
|
@@ -434,6 +434,7 @@ export class MountObserver extends EventTarget {
|
|
|
434
434
|
return false;
|
|
435
435
|
if (!x.matches(match))
|
|
436
436
|
return true;
|
|
437
|
+
//TODO: add check for outside
|
|
437
438
|
if (whereSatisfies !== undefined) {
|
|
438
439
|
if (!whereSatisfies(x, this, { stage: 'Inspecting', initializing: false }))
|
|
439
440
|
return true;
|
|
@@ -446,13 +447,26 @@ export class MountObserver extends EventTarget {
|
|
|
446
447
|
this.#mountedList = Array.from(returnSet).map(x => new WeakRef(x));
|
|
447
448
|
return returnSet;
|
|
448
449
|
}
|
|
450
|
+
#outsideCheck(oElement, matchCandidate, outside) {
|
|
451
|
+
const elementsToExclude = Array.from(oElement.querySelectorAll(outside));
|
|
452
|
+
for (const elementToExclude of elementsToExclude) {
|
|
453
|
+
if (elementToExclude === matchCandidate || elementToExclude.contains(matchCandidate))
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
return true;
|
|
457
|
+
}
|
|
449
458
|
async #filterAndMount(els, target, checkMatch, initializing) {
|
|
450
|
-
const { whereSatisfies, whereInstanceOf, assigner } = this.#mountInit;
|
|
459
|
+
const { whereSatisfies, whereInstanceOf, assigner, outside } = this.#mountInit;
|
|
451
460
|
const match = await this.#selector();
|
|
452
461
|
const elsToMount = els.filter(x => {
|
|
453
462
|
if (checkMatch) {
|
|
454
463
|
if (!x.matches(match))
|
|
455
464
|
return false;
|
|
465
|
+
//TODO: check for outside
|
|
466
|
+
}
|
|
467
|
+
if (outside !== undefined) {
|
|
468
|
+
if (!this.#outsideCheck(target, x, outside))
|
|
469
|
+
return false;
|
|
456
470
|
}
|
|
457
471
|
if (whereSatisfies !== undefined) {
|
|
458
472
|
if (!whereSatisfies(x, this, { stage: 'Inspecting', initializing }))
|
package/MountObserver.ts
CHANGED
|
@@ -466,6 +466,7 @@ export class MountObserver extends EventTarget implements IMountObserver{
|
|
|
466
466
|
const elsToUnMount = previouslyMounted.filter(x => {
|
|
467
467
|
if(x === undefined) return false;
|
|
468
468
|
if(!x.matches(match)) return true;
|
|
469
|
+
//TODO: add check for outside
|
|
469
470
|
if(whereSatisfies !== undefined){
|
|
470
471
|
if(!whereSatisfies(x, this, {stage: 'Inspecting', initializing: false})) return true;
|
|
471
472
|
}
|
|
@@ -478,12 +479,25 @@ export class MountObserver extends EventTarget implements IMountObserver{
|
|
|
478
479
|
return returnSet;
|
|
479
480
|
}
|
|
480
481
|
|
|
482
|
+
#outsideCheck(oElement: Element, matchCandidate: Element, outside: string){
|
|
483
|
+
const elementsToExclude = Array.from(oElement.querySelectorAll(outside));
|
|
484
|
+
for(const elementToExclude of elementsToExclude){
|
|
485
|
+
if(elementToExclude === matchCandidate || elementToExclude.contains(matchCandidate)) return false;
|
|
486
|
+
}
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
|
|
481
490
|
async #filterAndMount(els: Array<Element>, target: Node, checkMatch: boolean, initializing: boolean){
|
|
482
|
-
const {whereSatisfies, whereInstanceOf, assigner} = this.#mountInit;
|
|
491
|
+
const {whereSatisfies, whereInstanceOf, assigner, outside} = this.#mountInit;
|
|
483
492
|
const match = await this.#selector();
|
|
484
493
|
const elsToMount = els.filter(x => {
|
|
485
494
|
if(checkMatch){
|
|
486
495
|
if(!x.matches(match)) return false;
|
|
496
|
+
|
|
497
|
+
//TODO: check for outside
|
|
498
|
+
}
|
|
499
|
+
if(outside !== undefined){
|
|
500
|
+
if(!this.#outsideCheck(target as Element, x, outside)) return false;
|
|
487
501
|
}
|
|
488
502
|
if(whereSatisfies !== undefined){
|
|
489
503
|
if(!whereSatisfies(x, this, {stage: 'Inspecting', initializing})) return false;
|
package/README.md
CHANGED
|
@@ -48,6 +48,8 @@ There is quite a bit of functionality this proposal would open up, that is excee
|
|
|
48
48
|
|
|
49
49
|
3. Knowing when an element, previously being monitored for, passes totally "out-of-scope", so that no more hard references to the element remain. This would allow for cleanup of no longer needed weak references without requiring polling.
|
|
50
50
|
|
|
51
|
+
4. Some css selectors, such as the [scope donut hole range](https://css-tricks.com/solved-by-css-donuts-scopes/#aa-donut-scoping-with-scope) aren't supported by oEl.querySelectorAll(...) or oEl.matches(...).
|
|
52
|
+
|
|
51
53
|
### Most significant use cases.
|
|
52
54
|
|
|
53
55
|
The amount of code necessary to accomplish these common tasks designed to improve the user experience is significant. Building it into the platform would potentially:
|
|
@@ -439,6 +441,48 @@ So I believe the prudent thing to do is wait for all the conditions to be satisf
|
|
|
439
441
|
|
|
440
442
|
The alternative to providing this feature, which I'm leaning towards, is to just ask the developer to create "specialized" mountObserver construction arguments, that turn on and off precisely when the developer needs to know.
|
|
441
443
|
|
|
444
|
+
|
|
445
|
+
## Support for "donut hole scoping"
|
|
446
|
+
|
|
447
|
+
While browsers are getting support for css based donut hole scoping, such support appears to be elusive for oElement.querySelectorAll(...) and oElement.matches(...). In fact it is unclear how oElement.matches(...) would ever support it. Such support would be quite useful. for microdata-based binding.
|
|
448
|
+
|
|
449
|
+
Ideally, should this proposal be built into the browser, it would as a matter of course support donut hole scoping.
|
|
450
|
+
|
|
451
|
+
For the polyfill, we need to support it as follows:
|
|
452
|
+
|
|
453
|
+
```html
|
|
454
|
+
<div id=myTest itemscope>
|
|
455
|
+
<span itemprop=name>
|
|
456
|
+
</div>
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
```JavaScript
|
|
460
|
+
const oElement = document.getElementById('myTest');
|
|
461
|
+
const observer = new MountObserver({
|
|
462
|
+
on:'[itemprop]',
|
|
463
|
+
outside: '[itemscope]'
|
|
464
|
+
do: {
|
|
465
|
+
mount: ({localName}, {modules, observer}) => {
|
|
466
|
+
...
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
disconnectedSignal: new AbortController().signal
|
|
470
|
+
});
|
|
471
|
+
observer.observe(oElement);
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
The check for "outside" is done via script:
|
|
475
|
+
|
|
476
|
+
```JavaScript
|
|
477
|
+
outsideCheck(oElement: Element, matchCandidate: Element, outside: string){
|
|
478
|
+
const elementsToExclude = Array.from(oElement.querySelectorAll(outside));
|
|
479
|
+
for(const elementToExclude of elementsToExclude){
|
|
480
|
+
if(elementToExclude === matchCandidate || elementToExclude.contains(matchCandidate)) return false;
|
|
481
|
+
}
|
|
482
|
+
return true;
|
|
483
|
+
}
|
|
484
|
+
```
|
|
485
|
+
|
|
442
486
|
## A tribute to attributes
|
|
443
487
|
|
|
444
488
|
Attributes of DOM elements are tricky. They've been around since the get-go of the Web, and they've survived multiple eras of web development, where different philosophies have prevailed, so prepare yourself for some esoteric discussions in what follows.
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mount-observer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.77",
|
|
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.54.1",
|
|
9
9
|
"ssi-server": "0.0.1"
|
|
10
10
|
},
|
|
11
11
|
"exports": {
|
|
@@ -65,6 +65,10 @@
|
|
|
65
65
|
"default": "./slotkin/beKindred.js",
|
|
66
66
|
"types": "./slotkin/beKindred.ts"
|
|
67
67
|
},
|
|
68
|
+
"./slotkin/getBreadth.js": {
|
|
69
|
+
"default": "./slotkin/getBreadth.js",
|
|
70
|
+
"types": "./slotkin/getBreadth.ts"
|
|
71
|
+
},
|
|
68
72
|
"./slotkin/getFrag.js": {
|
|
69
73
|
"default": "./slotkin/getFrag.js",
|
|
70
74
|
"types": "./slotkin/getFrag.ts"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function getBreadth(innerEl, base) {
|
|
2
|
+
const priors = [];
|
|
3
|
+
let ps = innerEl.previousSibling;
|
|
4
|
+
while (ps !== null && !(ps.nodeType === Node.COMMENT_NODE && ps.data.includes(` ${base} `))) {
|
|
5
|
+
priors.push(ps);
|
|
6
|
+
ps = ps.previousSibling;
|
|
7
|
+
}
|
|
8
|
+
if (ps !== null)
|
|
9
|
+
priors.push(ps);
|
|
10
|
+
const nexts = [];
|
|
11
|
+
let ns = innerEl.nextSibling;
|
|
12
|
+
while (ns !== null && !(ns.nodeType === Node.COMMENT_NODE && ns.data.includes(` /${base} `))) {
|
|
13
|
+
nexts.push(ns);
|
|
14
|
+
ns = ns.nextSibling;
|
|
15
|
+
}
|
|
16
|
+
if (ns !== null)
|
|
17
|
+
nexts.push(ns);
|
|
18
|
+
return [...priors.reverse(), ...nexts];
|
|
19
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function getBreadth(
|
|
2
|
+
innerEl: Element,
|
|
3
|
+
base: string,
|
|
4
|
+
){
|
|
5
|
+
const priors: Array<Node> = [];
|
|
6
|
+
let ps = innerEl.previousSibling as Comment | null;
|
|
7
|
+
while(ps !== null && !(ps.nodeType === Node.COMMENT_NODE && (ps as Comment).data.includes( ` ${base} `))){
|
|
8
|
+
priors.push(ps);
|
|
9
|
+
ps = ps.previousSibling as Comment | null;
|
|
10
|
+
}
|
|
11
|
+
if(ps !== null) priors.push(ps);
|
|
12
|
+
const nexts: Array<Node> = [];
|
|
13
|
+
let ns = innerEl.nextSibling as Comment | null;
|
|
14
|
+
while(ns !== null && !(ns.nodeType === Node.COMMENT_NODE && (ns as Comment).data.includes( ` /${base} `))){
|
|
15
|
+
nexts.push(ns);
|
|
16
|
+
ns = ns.nextSibling as Comment | null;
|
|
17
|
+
}
|
|
18
|
+
if(ns !== null) nexts.push(ns);
|
|
19
|
+
return [...priors.reverse(), ...nexts];
|
|
20
|
+
|
|
21
|
+
}
|
package/slotkin/getFrag.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
export function getFrag(templ, base) {
|
|
2
2
|
let openComment = templ.nextSibling;
|
|
3
|
-
if (openComment === null || openComment.nodeType !== Node.COMMENT_NODE
|
|
3
|
+
if (openComment === null || openComment.nodeType !== Node.COMMENT_NODE)
|
|
4
4
|
return null;
|
|
5
|
+
if (base !== undefined && !openComment.data.includes(` ${base} `))
|
|
6
|
+
return null;
|
|
7
|
+
if (base === undefined) {
|
|
8
|
+
base = openComment.data.trim().split(' ')[1];
|
|
9
|
+
if (base === undefined)
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
5
12
|
const returnArr = [openComment];
|
|
6
13
|
let ns = openComment.nextSibling;
|
|
7
|
-
while (ns !== null && !(ns.nodeType === Node.COMMENT_NODE &&
|
|
14
|
+
while (ns !== null && !(ns.nodeType === Node.COMMENT_NODE && ns.data.includes(` /${base} `))) {
|
|
8
15
|
returnArr.push(ns);
|
|
9
16
|
ns = ns.nextSibling;
|
|
10
17
|
}
|
package/slotkin/getFrag.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
export function getFrag(
|
|
2
2
|
templ: HTMLTemplateElement,
|
|
3
|
-
base
|
|
3
|
+
base?: string,
|
|
4
4
|
){
|
|
5
5
|
let openComment = templ.nextSibling as Comment | null;
|
|
6
|
-
if(openComment === null || openComment.nodeType !== Node.COMMENT_NODE
|
|
6
|
+
if(openComment === null || openComment.nodeType !== Node.COMMENT_NODE) return null;
|
|
7
|
+
if(base !== undefined && !openComment.data.includes(` ${base} `)) return null;
|
|
8
|
+
if(base === undefined){
|
|
9
|
+
base = openComment.data.trim().split(' ')[1];
|
|
10
|
+
if(base === undefined) return null;
|
|
11
|
+
}
|
|
7
12
|
const returnArr: Array<Node> = [openComment];
|
|
8
13
|
let ns = openComment.nextSibling;
|
|
9
|
-
while(ns !== null && !(ns.nodeType === Node.COMMENT_NODE &&
|
|
14
|
+
while(ns !== null && !(ns.nodeType === Node.COMMENT_NODE && (ns as Comment).data.includes( ` /${base} `))){
|
|
10
15
|
returnArr.push(ns);
|
|
11
16
|
ns = ns.nextSibling;
|
|
12
17
|
}
|
package/slotkin/wrap.js
CHANGED
|
@@ -4,9 +4,9 @@ export function wrap(templ, base, force = false) {
|
|
|
4
4
|
if (!wasWrapped) {
|
|
5
5
|
templ[wrapped] = base;
|
|
6
6
|
if (force || templ.content.childElementCount > 1) {
|
|
7
|
-
const start = document.createComment(base);
|
|
7
|
+
const start = document.createComment(` ${base} `);
|
|
8
8
|
templ.content.prepend(start);
|
|
9
|
-
const end = document.createComment(
|
|
9
|
+
const end = document.createComment(` /${base} `);
|
|
10
10
|
templ.content.appendChild(end);
|
|
11
11
|
}
|
|
12
12
|
}
|
package/slotkin/wrap.ts
CHANGED
|
@@ -9,9 +9,9 @@ export function wrap(
|
|
|
9
9
|
if (!wasWrapped) {
|
|
10
10
|
(<any>templ)[wrapped] = base;
|
|
11
11
|
if (force || templ.content.childElementCount > 1) {
|
|
12
|
-
const start = document.createComment(base);
|
|
12
|
+
const start = document.createComment(` ${base} `);
|
|
13
13
|
templ.content.prepend(start);
|
|
14
|
-
const end = document.createComment(
|
|
14
|
+
const end = document.createComment(` /${base} `);
|
|
15
15
|
templ.content.appendChild(end);
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -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
|
+
|
|
@@ -20,6 +20,7 @@ export interface EndUserProps extends IEnhancement<HTMLTemplateElement>{
|
|
|
20
20
|
hiddenStyle?: string;
|
|
21
21
|
toggleInert?: boolean;
|
|
22
22
|
deferRendering?: boolean;
|
|
23
|
+
/** delete content when condition evaluates to false */
|
|
23
24
|
minMem?: boolean;
|
|
24
25
|
/**
|
|
25
26
|
* Works with beOosoom decorator, so becomes inert when out of view
|
|
@@ -27,6 +28,10 @@ export interface EndUserProps extends IEnhancement<HTMLTemplateElement>{
|
|
|
27
28
|
beOosoom?: string;
|
|
28
29
|
js?: string;
|
|
29
30
|
transitional: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Use comments rather a DOM element to wrap multiple elements
|
|
33
|
+
*/
|
|
34
|
+
cmtWrap?: boolean;
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
export interface AllProps extends EndUserProps{
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
export interface JSONSerializableMountInit{
|
|
3
3
|
readonly on?: CSSMatch,
|
|
4
|
+
readonly outside?: CSSMatch,
|
|
4
5
|
readonly observedAttrsWhenMounted?: (string | ObservedSourceOfTruthAttribute)[],
|
|
5
6
|
readonly whereAttr?: WhereAttr,
|
|
6
7
|
readonly whereElementIntersectsWith?: IntersectionObserverInit,
|
|
@@ -29,6 +29,10 @@ export type DirectionalScopeSigils =
|
|
|
29
29
|
* itemscoped host
|
|
30
30
|
*/
|
|
31
31
|
|'$'
|
|
32
|
+
/**
|
|
33
|
+
* comment scope
|
|
34
|
+
*/
|
|
35
|
+
|'/**/'
|
|
32
36
|
;
|
|
33
37
|
|
|
34
38
|
export type AttrSigils =
|
|
@@ -129,12 +133,21 @@ export interface Specifier {
|
|
|
129
133
|
*/
|
|
130
134
|
modulo?: Modulo;
|
|
131
135
|
|
|
136
|
+
/**
|
|
137
|
+
* is a scope query within comments
|
|
138
|
+
*/
|
|
139
|
+
cmtWrap?: boolean;
|
|
140
|
+
|
|
132
141
|
/**
|
|
133
142
|
* itemscope hierarchy domain specifier
|
|
134
143
|
*/
|
|
135
144
|
is$cope?: boolean;
|
|
136
145
|
|
|
137
|
-
$copeDetail?: $copeDetail
|
|
146
|
+
$copeDetail?: $copeDetail;
|
|
147
|
+
|
|
148
|
+
constVal?: any;
|
|
149
|
+
|
|
150
|
+
enhBase?: string;
|
|
138
151
|
}
|
|
139
152
|
|
|
140
153
|
export type Modulo = 'aria-rowindex' | 'aria-colindex' | 'aria-rowindextext'
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {IEnhancement, BEAllProps} from '../trans-render/be/types';
|
|
2
|
+
import { Specifier } from "../trans-render/dss/types";
|
|
3
|
+
|
|
4
|
+
export interface EndUserProps extends IEnhancement{
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface AllProps extends EndUserProps{
|
|
8
|
+
parsedStatements: Array<ResolvingParameters>,
|
|
9
|
+
rawStatements?: Array<string>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type AP = AllProps;
|
|
13
|
+
|
|
14
|
+
export type PAP = Partial<AP>;
|
|
15
|
+
|
|
16
|
+
export type ProPAP = Promise<PAP>;
|
|
17
|
+
|
|
18
|
+
export type BAP = AP & BEAllProps;
|
|
19
|
+
|
|
20
|
+
export interface Actions{
|
|
21
|
+
hydrate(self: BAP): ProPAP;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ResolvingParameters{
|
|
25
|
+
remoteSpecifierString: string;
|
|
26
|
+
remoteSpecifier: Specifier;
|
|
27
|
+
localSpecifierString: string;
|
|
28
|
+
localSpecifier: Specifier;
|
|
29
|
+
}
|