mount-observer 0.1.35 → 0.1.37
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/README.md +171 -5
- package/Synthesizer.js +6 -4
- package/Synthesizer.ts +6 -4
- package/handlers/CedeScript.js +100 -0
- package/handlers/CedeScript.ts +106 -0
- package/handlers/EMCScript.js +2 -2
- package/handlers/EMCScript.ts +2 -2
- package/handlers/elementIntersection.js +73 -73
- package/index.ts +2 -0
- package/package.json +3 -3
- package/types/agrace/types.d.ts +11 -0
- package/types/assign-gingerly/types.d.ts +18 -7
- package/types/be-bound/types.d.ts +2 -1
- package/types/do-invoke/types.d.ts +2 -2
- package/types/do-toggle/types.d.ts +1 -1
- package/types/inferencer/types.d.ts +46 -0
- package/types/mount-observer/types.d.ts +1 -1
- package/types/roundabout/types.d.ts +34 -3
package/README.md
CHANGED
|
@@ -37,6 +37,7 @@ The following features have been implemented and tested:
|
|
|
37
37
|
- ✅ **Shared MutationObserver**: Efficient observer sharing across instances
|
|
38
38
|
- ✅ **Code splitting**: Conditional features loaded on-demand
|
|
39
39
|
- ✅ **Memory management**: WeakRef usage for DOM node references
|
|
40
|
+
- ✅ **Cede Scripts**: Declarative custom element definition via `<script type="cede">`
|
|
40
41
|
|
|
41
42
|
### Not Yet Implemented
|
|
42
43
|
- ❌ Reconnect event handling
|
|
@@ -666,6 +667,138 @@ export default class MyEnhancement {
|
|
|
666
667
|
|
|
667
668
|
[Implemented as EMCScript requirement](requirements/Done/EMCScript.md)
|
|
668
669
|
|
|
670
|
+
## Custom Element Definition (Cede) Scripts
|
|
671
|
+
|
|
672
|
+
The `builtIns.cedeScript` handler enables declarative custom element definition using `<script type="cede">` elements. "Cede" stands for **C**ustom **E**lement **De**finition. It creates a new class that extends an existing custom element class and defines it in the appropriate registry — all without writing any JavaScript.
|
|
673
|
+
|
|
674
|
+
**Why use Cede Scripts?**
|
|
675
|
+
|
|
676
|
+
- Define custom elements declaratively in HTML
|
|
677
|
+
- Extend existing base classes without writing boilerplate
|
|
678
|
+
- Wire up features from JSON configuration (powered by [assign-gingerly's `defineWithFeatures`](https://github.com/bahrus/assign-gingerly/blob/baseline/docs/defineWithFeatures.md))
|
|
679
|
+
- Works with scoped custom element registries
|
|
680
|
+
- Enables template-based custom elements where the parent's fragment becomes the template
|
|
681
|
+
- Supports external JSON config via `src` attribute (with import maps)
|
|
682
|
+
- Zero JavaScript required for element definition
|
|
683
|
+
|
|
684
|
+
**Basic usage (simple extension):**
|
|
685
|
+
|
|
686
|
+
```html
|
|
687
|
+
<time-ticker>
|
|
688
|
+
<script type="cede" data-extends="xtal-element"></script>
|
|
689
|
+
</time-ticker>
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
**With inline feature configuration:**
|
|
693
|
+
|
|
694
|
+
```html
|
|
695
|
+
<time-ticker>
|
|
696
|
+
<script type="cede" data-extends="el-maker">{
|
|
697
|
+
"assignFeatures": {
|
|
698
|
+
"roundabout": {
|
|
699
|
+
"customData": {"template": "my-template"},
|
|
700
|
+
"withAttrs": {"base": "ra"},
|
|
701
|
+
"callbackForwarding": ["connectedCallback"]
|
|
702
|
+
},
|
|
703
|
+
"truthSourcer": {
|
|
704
|
+
"callbackForwarding": ["connectedCallback", "attributeChangedCallback"]
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}</script>
|
|
708
|
+
</time-ticker>
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
**With external JSON config:**
|
|
712
|
+
|
|
713
|
+
```html
|
|
714
|
+
<time-ticker>
|
|
715
|
+
<script type="cede" data-extends="el-maker" src="./time-ticker-config.json"></script>
|
|
716
|
+
</time-ticker>
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
The `src` attribute uses JSON import assertions, so it works with import maps for bare specifiers (e.g., `src="my-configs/time-ticker.json"`).
|
|
720
|
+
|
|
721
|
+
**What happens:**
|
|
722
|
+
|
|
723
|
+
1. The handler finds the script element's `customElementRegistry` (falls back to global `customElements`)
|
|
724
|
+
2. Parses configuration from `src` (JSON import), inline `textContent`, or a pre-existing `export` property
|
|
725
|
+
3. Stores the parsed config on `scriptElement.export` and dispatches a `resolved` event
|
|
726
|
+
4. Delegates to [`defineWithFeatures`](https://github.com/bahrus/assign-gingerly/blob/baseline/docs/defineWithFeatures.md) which:
|
|
727
|
+
- Awaits `registry.whenDefined('el-maker')` to get the base class
|
|
728
|
+
- Resolves async feature spawns from the base class's `static supportedFeatures`
|
|
729
|
+
- Creates a subclass and sets `NewCtr.seedRef = new WeakRef(scriptEl)`
|
|
730
|
+
- Wires up features via `assignFeatures`
|
|
731
|
+
- Calls `registry.define('time-ticker', NewCtr)`
|
|
732
|
+
5. If `registry.get('time-ticker')` already exists, does nothing (first definition wins)
|
|
733
|
+
|
|
734
|
+
The tag name comes from the `localName` of the script element's parent.
|
|
735
|
+
|
|
736
|
+
**Accessing the seed reference:**
|
|
737
|
+
|
|
738
|
+
The `seedRef` static property gives the custom element class access to the original script element. The primary use case is extracting the parent's (Shadow) Fragment to create a cloneable template for other instances:
|
|
739
|
+
|
|
740
|
+
```javascript
|
|
741
|
+
class XtalElement extends HTMLElement {
|
|
742
|
+
connectedCallback() {
|
|
743
|
+
const seedScript = this.constructor.seedRef?.deref();
|
|
744
|
+
if (seedScript) {
|
|
745
|
+
const parentFragment = seedScript.parentElement.shadowRoot;
|
|
746
|
+
// Use parentFragment as a template for cloning
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
**With scoped registries:**
|
|
753
|
+
|
|
754
|
+
```html
|
|
755
|
+
<my-app>
|
|
756
|
+
#shadow (with scoped registry)
|
|
757
|
+
<time-ticker>
|
|
758
|
+
<script type="cede" data-extends="xtal-element"></script>
|
|
759
|
+
</time-ticker>
|
|
760
|
+
</my-app>
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
The handler uses the script element's `customElementRegistry` property, so it naturally works with [scoped custom element registries](https://developer.chrome.com/blog/scoped-registries) (Chrome 146+, latest WebKit/Safari). If no scoped registry is present, it falls back to the global `customElements` registry.
|
|
764
|
+
|
|
765
|
+
**Bootstrapping:**
|
|
766
|
+
|
|
767
|
+
```html
|
|
768
|
+
<script type="module">
|
|
769
|
+
import { MountObserver } from 'mount-observer/MountObserver.js';
|
|
770
|
+
|
|
771
|
+
// Handler provides matching and whereInstanceOf via static properties
|
|
772
|
+
const observer = new MountObserver({
|
|
773
|
+
do: 'builtIns.cedeScript'
|
|
774
|
+
});
|
|
775
|
+
observer.observe(document);
|
|
776
|
+
</script>
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
Or declaratively via a MOSE:
|
|
780
|
+
|
|
781
|
+
```html
|
|
782
|
+
<script type="mountobserver">
|
|
783
|
+
{
|
|
784
|
+
"do": "builtIns.cedeScript"
|
|
785
|
+
}
|
|
786
|
+
</script>
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
**Key behaviors:**
|
|
790
|
+
|
|
791
|
+
- If the parent element's tag is already defined in the registry, the handler silently no-ops
|
|
792
|
+
- If multiple cede scripts exist under the same parent, the first one to resolve wins
|
|
793
|
+
- The script element must have a `parentElement` or the handler throws
|
|
794
|
+
- `whenDefined` awaits indefinitely — the base class must eventually be registered
|
|
795
|
+
- Empty scripts (no JSON, no `src`) work as simple class extension with no features
|
|
796
|
+
- Feature spawn resolution is cached per base class — defining 10 elements extending the same base only imports each feature once
|
|
797
|
+
|
|
798
|
+
For full details on the base class contract (`static supportedFeatures`, `fallbackSpawn`, spawn caching, and `assignFeatures` wiring), see the [defineWithFeatures documentation](https://github.com/bahrus/assign-gingerly/blob/baseline/docs/defineWithFeatures.md).
|
|
799
|
+
|
|
800
|
+
[Implemented as SupportForCedeScripts requirement](requirements/SupportForCedeScripts.md)
|
|
801
|
+
|
|
669
802
|
## Syndicating Mount Observers with Synthesizer
|
|
670
803
|
|
|
671
804
|
The `Synthesizer` abstract base class enables automatic propagation of mount observer configurations across shadow DOM boundaries. It acts as a "syndicator-subscriber" pattern where a syndicator in the document root broadcasts script elements to subscribers in shadow roots.
|
|
@@ -680,9 +813,9 @@ The `Synthesizer` abstract base class enables automatic propagation of mount obs
|
|
|
680
813
|
|
|
681
814
|
**How it works:**
|
|
682
815
|
|
|
683
|
-
1. **Syndicator** (in document root): Watches for `script[type="mountobserver"]` and `script[type="
|
|
816
|
+
1. **Syndicator** (in document root): Watches for `script[type="mountobserver"]`, `script[type="emc"]`, and `script[type="cede"]` elements and broadcasts them to subscribers
|
|
684
817
|
2. **Subscriber** (in shadow roots): Receives and clones script elements from the syndicator
|
|
685
|
-
3. **Automatic activation**: Both syndicator and subscriber activate
|
|
818
|
+
3. **Automatic activation**: Both syndicator and subscriber activate 8 built-in handlers in their respective root nodes
|
|
686
819
|
|
|
687
820
|
**Basic usage:**
|
|
688
821
|
|
|
@@ -730,7 +863,7 @@ The `Synthesizer` abstract base class enables automatic propagation of mount obs
|
|
|
730
863
|
|
|
731
864
|
**What happens:**
|
|
732
865
|
|
|
733
|
-
1. The syndicator (`<app-synthesizer>` in document root) activates
|
|
866
|
+
1. The syndicator (`<app-synthesizer>` in document root) activates 8 built-in handlers:
|
|
734
867
|
- `builtIns.mountObserverScript` - Process MOSE scripts
|
|
735
868
|
- `builtIns.scriptExport` - Expose module exports from script elements
|
|
736
869
|
- `builtIns.HTMLInclude` - Enable intra-document HTML includes
|
|
@@ -738,6 +871,7 @@ The `Synthesizer` abstract base class enables automatic propagation of mount obs
|
|
|
738
871
|
- `builtIns.generateIds` - Auto-generate unique IDs
|
|
739
872
|
- `builtIns.emcParserScript` - Load parsers for EMC scripts
|
|
740
873
|
- `builtIns.emcScript` - Process EMC scripts
|
|
874
|
+
- `builtIns.cedeScript` - Declarative custom element definition
|
|
741
875
|
|
|
742
876
|
<details>
|
|
743
877
|
<summary>Why these handlers?</summary>
|
|
@@ -751,6 +885,7 @@ These handlers form the core infrastructure for declarative progressive enhancem
|
|
|
751
885
|
- **generateIds**: Automates ID generation for forms and accessibility features
|
|
752
886
|
- **emcParserScript**: Enables lazy-loading of complex parsers for enhancement attributes
|
|
753
887
|
- **emcScript**: Processes enhancement configurations for progressive enhancement
|
|
888
|
+
- **cedeScript**: Declarative custom element definition via `<script type="cede">` — extends a base class and defines the parent's tag name
|
|
754
889
|
|
|
755
890
|
Together, these handlers enable a complete HTML-first development workflow where behaviors, enhancements, and configurations can be declared in HTML and automatically propagated across shadow DOM boundaries.
|
|
756
891
|
|
|
@@ -768,7 +903,7 @@ Together, these handlers enable a complete HTML-first development workflow where
|
|
|
768
903
|
- Subscribe to `addedscriptelement` events for new scripts
|
|
769
904
|
- Clone each script element and copy its `export` property
|
|
770
905
|
- Append cloned scripts to their own light children
|
|
771
|
-
- Activate the same
|
|
906
|
+
- Activate the same 8 built-in handlers in their shadow root
|
|
772
907
|
|
|
773
908
|
**Syndicator vs Subscriber:**
|
|
774
909
|
|
|
@@ -4067,7 +4202,7 @@ The platform provides some nice help with managing forms, including IDREF depend
|
|
|
4067
4202
|
|
|
4068
4203
|
This would be useful for other linkages as well, which the platform doesn't support currently.
|
|
4069
4204
|
|
|
4070
|
-
Again, because of the
|
|
4205
|
+
Again, because of the mountWhy these handlers-observer being the "first point of contact" with the DOM, this is supported by mount-observer as well.
|
|
4071
4206
|
|
|
4072
4207
|
```html
|
|
4073
4208
|
<section id=section>
|
|
@@ -4164,3 +4299,34 @@ To keep the api uniform, we hide this discrepancy by pretending the form element
|
|
|
4164
4299
|
|
|
4165
4300
|
</script>
|
|
4166
4301
|
```
|
|
4302
|
+
|
|
4303
|
+
## Viewing Demos Locally
|
|
4304
|
+
|
|
4305
|
+
1. Install git
|
|
4306
|
+
2. Fork/clone this repo
|
|
4307
|
+
3. Install node.js
|
|
4308
|
+
4. Open command window to folder where you cloned this repo
|
|
4309
|
+
5. > git submodule update --init --recursive
|
|
4310
|
+
6. > npm install
|
|
4311
|
+
7. > npm run serve
|
|
4312
|
+
8. Open http://localhost:8000/ in a modern browser
|
|
4313
|
+
|
|
4314
|
+
## Running Tests
|
|
4315
|
+
|
|
4316
|
+
```
|
|
4317
|
+
> npm run test
|
|
4318
|
+
```
|
|
4319
|
+
|
|
4320
|
+
## Using from ESM Module:
|
|
4321
|
+
|
|
4322
|
+
```JavaScript
|
|
4323
|
+
import {MountObserver} 'mount-observer/MountObserver.js';
|
|
4324
|
+
```
|
|
4325
|
+
|
|
4326
|
+
## Using from CDN:
|
|
4327
|
+
|
|
4328
|
+
```html
|
|
4329
|
+
<script type=module crossorigin=anonymous>
|
|
4330
|
+
import 'https://esm.sh/mount-observer';
|
|
4331
|
+
</script>
|
|
4332
|
+
```
|
package/Synthesizer.js
CHANGED
|
@@ -15,6 +15,8 @@ import 'mount-observer/handlers/EMCParserScript.js';
|
|
|
15
15
|
import { emcParser } from 'mount-observer/handlers/EMCParserScript.js';
|
|
16
16
|
import 'mount-observer/handlers/GenIds.js';
|
|
17
17
|
import { genIds } from 'mount-observer/handlers/GenIds.js';
|
|
18
|
+
import 'mount-observer/handlers/CedeScript.js';
|
|
19
|
+
import { cedeScript } from 'mount-observer/handlers/CedeScript.js';
|
|
18
20
|
/**
|
|
19
21
|
* Track which root nodes have already had handlers activated.
|
|
20
22
|
* Uses WeakSet to avoid memory leaks when nodes are garbage collected.
|
|
@@ -57,7 +59,7 @@ export class Synthesizer extends HTMLElement {
|
|
|
57
59
|
* List of built-in handlers to activate.
|
|
58
60
|
*/
|
|
59
61
|
static builtInHandlers = [
|
|
60
|
-
mos, scriptExport, include, hoist, genIds, emcParser, emc
|
|
62
|
+
mos, scriptExport, include, hoist, genIds, emcParser, emc, cedeScript
|
|
61
63
|
];
|
|
62
64
|
connectedCallback() {
|
|
63
65
|
// Synthesizer elements are infrastructure, not UI
|
|
@@ -149,7 +151,7 @@ export class Synthesizer extends HTMLElement {
|
|
|
149
151
|
*/
|
|
150
152
|
#initializeSyndicator() {
|
|
151
153
|
// Process existing script elements
|
|
152
|
-
const scripts = this.querySelectorAll('script[type="mountobserver"], script[type="emc"], script[type="emc-parser"]');
|
|
154
|
+
const scripts = this.querySelectorAll('script[type="mountobserver"], script[type="emc"], script[type="emc-parser"], script[type="cede"]');
|
|
153
155
|
scripts.forEach(script => {
|
|
154
156
|
if (this.checkIfAllowed(script)) {
|
|
155
157
|
this.#broadcastScript(script);
|
|
@@ -161,7 +163,7 @@ export class Synthesizer extends HTMLElement {
|
|
|
161
163
|
for (const node of mutation.addedNodes) {
|
|
162
164
|
if (node instanceof HTMLScriptElement) {
|
|
163
165
|
const type = node.getAttribute('type');
|
|
164
|
-
if (type === 'mountobserver' || type === 'emc' || type === 'emc-parser') {
|
|
166
|
+
if (type === 'mountobserver' || type === 'emc' || type === 'emc-parser' || type === 'cede') {
|
|
165
167
|
if (this.checkIfAllowed(node)) {
|
|
166
168
|
this.#broadcastScript(node);
|
|
167
169
|
}
|
|
@@ -194,7 +196,7 @@ export class Synthesizer extends HTMLElement {
|
|
|
194
196
|
}
|
|
195
197
|
// Process existing scripts from syndicator
|
|
196
198
|
// Only process scripts that pass the syndicator's filtering
|
|
197
|
-
const scripts = syndicator.querySelectorAll('script[type="mountobserver"], script[type="emc"], script[type="emc-parser"]');
|
|
199
|
+
const scripts = syndicator.querySelectorAll('script[type="mountobserver"], script[type="emc"], script[type="emc-parser"], script[type="cede"]');
|
|
198
200
|
scripts.forEach(script => {
|
|
199
201
|
if (syndicator.checkIfAllowed(script)) {
|
|
200
202
|
this.#processScript(script);
|
package/Synthesizer.ts
CHANGED
|
@@ -16,6 +16,8 @@ import 'mount-observer/handlers/EMCParserScript.js';
|
|
|
16
16
|
import {emcParser} from 'mount-observer/handlers/EMCParserScript.js';
|
|
17
17
|
import 'mount-observer/handlers/GenIds.js';
|
|
18
18
|
import {genIds} from 'mount-observer/handlers/GenIds.js';
|
|
19
|
+
import 'mount-observer/handlers/CedeScript.js';
|
|
20
|
+
import {cedeScript} from 'mount-observer/handlers/CedeScript.js';
|
|
19
21
|
|
|
20
22
|
/**
|
|
21
23
|
* Track which root nodes have already had handlers activated.
|
|
@@ -61,7 +63,7 @@ export abstract class Synthesizer extends HTMLElement {
|
|
|
61
63
|
* List of built-in handlers to activate.
|
|
62
64
|
*/
|
|
63
65
|
protected static builtInHandlers = [
|
|
64
|
-
mos, scriptExport, include, hoist, genIds, emcParser, emc
|
|
66
|
+
mos, scriptExport, include, hoist, genIds, emcParser, emc, cedeScript
|
|
65
67
|
];
|
|
66
68
|
|
|
67
69
|
connectedCallback(): void {
|
|
@@ -168,7 +170,7 @@ export abstract class Synthesizer extends HTMLElement {
|
|
|
168
170
|
*/
|
|
169
171
|
#initializeSyndicator(): void {
|
|
170
172
|
// Process existing script elements
|
|
171
|
-
const scripts = this.querySelectorAll('script[type="mountobserver"], script[type="emc"], script[type="emc-parser"]');
|
|
173
|
+
const scripts = this.querySelectorAll('script[type="mountobserver"], script[type="emc"], script[type="emc-parser"], script[type="cede"]');
|
|
172
174
|
scripts.forEach(script => {
|
|
173
175
|
if (this.checkIfAllowed(script as HTMLScriptElement)) {
|
|
174
176
|
this.#broadcastScript(script as HTMLScriptElement);
|
|
@@ -181,7 +183,7 @@ export abstract class Synthesizer extends HTMLElement {
|
|
|
181
183
|
for (const node of mutation.addedNodes) {
|
|
182
184
|
if (node instanceof HTMLScriptElement) {
|
|
183
185
|
const type = node.getAttribute('type');
|
|
184
|
-
if (type === 'mountobserver' || type === 'emc' || type === 'emc-parser') {
|
|
186
|
+
if (type === 'mountobserver' || type === 'emc' || type === 'emc-parser' || type === 'cede') {
|
|
185
187
|
if (this.checkIfAllowed(node)) {
|
|
186
188
|
this.#broadcastScript(node);
|
|
187
189
|
}
|
|
@@ -219,7 +221,7 @@ export abstract class Synthesizer extends HTMLElement {
|
|
|
219
221
|
|
|
220
222
|
// Process existing scripts from syndicator
|
|
221
223
|
// Only process scripts that pass the syndicator's filtering
|
|
222
|
-
const scripts = syndicator.querySelectorAll('script[type="mountobserver"], script[type="emc"], script[type="emc-parser"]');
|
|
224
|
+
const scripts = syndicator.querySelectorAll('script[type="mountobserver"], script[type="emc"], script[type="emc-parser"], script[type="cede"]');
|
|
223
225
|
scripts.forEach(script => {
|
|
224
226
|
if (syndicator.checkIfAllowed(script as HTMLScriptElement)) {
|
|
225
227
|
this.#processScript(script as HTMLScriptElement);
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { EvtRt } from '../EvtRt.js';
|
|
2
|
+
import { MountObserver } from '../MountObserver.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handler for `<script type="cede" data-extends="...">` elements.
|
|
5
|
+
* "Cede" stands for Custom Element Definition.
|
|
6
|
+
*
|
|
7
|
+
* Delegates to assign-gingerly's `defineWithFeatures` to create a custom element
|
|
8
|
+
* class extending the base specified by `data-extends`, optionally wiring up
|
|
9
|
+
* features from JSON configuration (inline or via `src`).
|
|
10
|
+
*
|
|
11
|
+
* The new class gets a static `seedRef` WeakRef pointing back to the script
|
|
12
|
+
* element, allowing the custom element to extract the parent's (Shadow) Fragment
|
|
13
|
+
* and create a cloneable template from it.
|
|
14
|
+
*
|
|
15
|
+
* Examples:
|
|
16
|
+
* ```html
|
|
17
|
+
* <!-- Simple (no features) -->
|
|
18
|
+
* <time-ticker>
|
|
19
|
+
* <script type="cede" data-extends="xtal-element"></script>
|
|
20
|
+
* </time-ticker>
|
|
21
|
+
*
|
|
22
|
+
* <!-- With inline feature config -->
|
|
23
|
+
* <time-ticker>
|
|
24
|
+
* <script type="cede" data-extends="el-maker">{
|
|
25
|
+
* "assignFeatures": {
|
|
26
|
+
* "roundabout": { "callbackForwarding": ["connectedCallback"] }
|
|
27
|
+
* }
|
|
28
|
+
* }</script>
|
|
29
|
+
* </time-ticker>
|
|
30
|
+
*
|
|
31
|
+
* <!-- With external JSON config -->
|
|
32
|
+
* <time-ticker>
|
|
33
|
+
* <script type="cede" data-extends="el-maker" src="./time-ticker-config.json"></script>
|
|
34
|
+
* </time-ticker>
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export class CedeScriptHandler extends EvtRt {
|
|
38
|
+
static matching = 'script[type="cede"][data-extends]';
|
|
39
|
+
static whereInstanceOf = HTMLScriptElement;
|
|
40
|
+
async mount(mountedElement, mountConfig, context) {
|
|
41
|
+
this.abort();
|
|
42
|
+
const scriptEl = mountedElement;
|
|
43
|
+
const extendsName = scriptEl.dataset.extends;
|
|
44
|
+
if (!extendsName)
|
|
45
|
+
return;
|
|
46
|
+
const parentEl = scriptEl.parentElement;
|
|
47
|
+
if (!parentEl) {
|
|
48
|
+
throw new Error('CedeScript: script element must have a parentElement');
|
|
49
|
+
}
|
|
50
|
+
const tagName = parentEl.localName;
|
|
51
|
+
const registry = scriptEl.customElementRegistry || customElements;
|
|
52
|
+
// Already defined? Do nothing (first one prevails).
|
|
53
|
+
if (registry.get(tagName))
|
|
54
|
+
return;
|
|
55
|
+
// Parse config: from export, src, or inline JSON
|
|
56
|
+
let config = scriptEl.export;
|
|
57
|
+
if (!config) {
|
|
58
|
+
const srcAttr = scriptEl.getAttribute('src');
|
|
59
|
+
if (srcAttr) {
|
|
60
|
+
try {
|
|
61
|
+
const module = await import(srcAttr, { with: { type: 'json' } });
|
|
62
|
+
config = module.default;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
throw new Error(`Failed to import JSON from '${srcAttr}': ${error instanceof Error ? error.message : String(error)}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const jsonText = scriptEl.textContent?.trim();
|
|
70
|
+
if (jsonText) {
|
|
71
|
+
try {
|
|
72
|
+
config = JSON.parse(jsonText);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
throw new Error(`Failed to parse JSON content: ${error instanceof Error ? error.message : String(error)}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
config = {};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Store parsed config and dispatch resolved event
|
|
83
|
+
scriptEl.export = config;
|
|
84
|
+
const { ResolvedEvent } = await import('../Events.js');
|
|
85
|
+
scriptEl.dispatchEvent(new ResolvedEvent(config));
|
|
86
|
+
}
|
|
87
|
+
// Delegate to defineWithFeatures
|
|
88
|
+
const { defineWithFeatures } = await import('assign-gingerly/defineWithFeatures.js');
|
|
89
|
+
// Race condition guard: check again after async operations
|
|
90
|
+
if (registry.get(tagName))
|
|
91
|
+
return;
|
|
92
|
+
await defineWithFeatures(tagName, extendsName, config, registry, {
|
|
93
|
+
onSubclassCreated(NewCtr) {
|
|
94
|
+
NewCtr.seedRef = new WeakRef(scriptEl);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
MountObserver.define('builtIns.cedeScript', CedeScriptHandler);
|
|
100
|
+
export const cedeScript = 'builtIns.cedeScript';
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { EvtRt } from '../EvtRt.js';
|
|
2
|
+
import { MountConfig, MountContext } from '../types/mount-observer/types.js';
|
|
3
|
+
import { MountObserver } from '../MountObserver.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Handler for `<script type="cede" data-extends="...">` elements.
|
|
7
|
+
* "Cede" stands for Custom Element Definition.
|
|
8
|
+
*
|
|
9
|
+
* Delegates to assign-gingerly's `defineWithFeatures` to create a custom element
|
|
10
|
+
* class extending the base specified by `data-extends`, optionally wiring up
|
|
11
|
+
* features from JSON configuration (inline or via `src`).
|
|
12
|
+
*
|
|
13
|
+
* The new class gets a static `seedRef` WeakRef pointing back to the script
|
|
14
|
+
* element, allowing the custom element to extract the parent's (Shadow) Fragment
|
|
15
|
+
* and create a cloneable template from it.
|
|
16
|
+
*
|
|
17
|
+
* Examples:
|
|
18
|
+
* ```html
|
|
19
|
+
* <!-- Simple (no features) -->
|
|
20
|
+
* <time-ticker>
|
|
21
|
+
* <script type="cede" data-extends="xtal-element"></script>
|
|
22
|
+
* </time-ticker>
|
|
23
|
+
*
|
|
24
|
+
* <!-- With inline feature config -->
|
|
25
|
+
* <time-ticker>
|
|
26
|
+
* <script type="cede" data-extends="el-maker">{
|
|
27
|
+
* "assignFeatures": {
|
|
28
|
+
* "roundabout": { "callbackForwarding": ["connectedCallback"] }
|
|
29
|
+
* }
|
|
30
|
+
* }</script>
|
|
31
|
+
* </time-ticker>
|
|
32
|
+
*
|
|
33
|
+
* <!-- With external JSON config -->
|
|
34
|
+
* <time-ticker>
|
|
35
|
+
* <script type="cede" data-extends="el-maker" src="./time-ticker-config.json"></script>
|
|
36
|
+
* </time-ticker>
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export class CedeScriptHandler extends EvtRt {
|
|
40
|
+
static matching = 'script[type="cede"][data-extends]';
|
|
41
|
+
static whereInstanceOf = HTMLScriptElement;
|
|
42
|
+
|
|
43
|
+
async mount(mountedElement: Element, mountConfig: MountConfig, context: MountContext): Promise<void> {
|
|
44
|
+
this.abort();
|
|
45
|
+
const scriptEl = mountedElement as HTMLScriptElement;
|
|
46
|
+
const extendsName = scriptEl.dataset.extends;
|
|
47
|
+
if (!extendsName) return;
|
|
48
|
+
|
|
49
|
+
const parentEl = scriptEl.parentElement;
|
|
50
|
+
if (!parentEl) {
|
|
51
|
+
throw new Error('CedeScript: script element must have a parentElement');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const tagName = parentEl.localName;
|
|
55
|
+
const registry = (scriptEl as any).customElementRegistry || customElements;
|
|
56
|
+
|
|
57
|
+
// Already defined? Do nothing (first one prevails).
|
|
58
|
+
if (registry.get(tagName)) return;
|
|
59
|
+
|
|
60
|
+
// Parse config: from export, src, or inline JSON
|
|
61
|
+
let config: Record<string, any> = (scriptEl as any).export;
|
|
62
|
+
if (!config) {
|
|
63
|
+
const srcAttr = scriptEl.getAttribute('src');
|
|
64
|
+
if (srcAttr) {
|
|
65
|
+
try {
|
|
66
|
+
const module = await import(srcAttr, { with: { type: 'json' } } as any);
|
|
67
|
+
config = module.default;
|
|
68
|
+
} catch (error) {
|
|
69
|
+
throw new Error(`Failed to import JSON from '${srcAttr}': ${error instanceof Error ? error.message : String(error)}`);
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
const jsonText = scriptEl.textContent?.trim();
|
|
73
|
+
if (jsonText) {
|
|
74
|
+
try {
|
|
75
|
+
config = JSON.parse(jsonText);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
throw new Error(`Failed to parse JSON content: ${error instanceof Error ? error.message : String(error)}`);
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
config = {};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Store parsed config and dispatch resolved event
|
|
85
|
+
(scriptEl as any).export = config;
|
|
86
|
+
const { ResolvedEvent } = await import('../Events.js');
|
|
87
|
+
scriptEl.dispatchEvent(new ResolvedEvent(config));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Delegate to defineWithFeatures
|
|
91
|
+
const { defineWithFeatures } = await import('assign-gingerly/defineWithFeatures.js');
|
|
92
|
+
|
|
93
|
+
// Race condition guard: check again after async operations
|
|
94
|
+
if (registry.get(tagName)) return;
|
|
95
|
+
|
|
96
|
+
await defineWithFeatures(tagName, extendsName, config as any, registry, {
|
|
97
|
+
onSubclassCreated(NewCtr) {
|
|
98
|
+
(NewCtr as any).seedRef = new WeakRef(scriptEl);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
MountObserver.define('builtIns.cedeScript', CedeScriptHandler);
|
|
105
|
+
|
|
106
|
+
export const cedeScript = 'builtIns.cedeScript';
|
package/handlers/EMCScript.js
CHANGED
|
@@ -152,8 +152,8 @@ export class EMCScriptHandler extends EvtRt {
|
|
|
152
152
|
if (!enh) {
|
|
153
153
|
throw new Error('Element does not have enh property. Make sure ElementMountExtension is loaded.');
|
|
154
154
|
}
|
|
155
|
-
// Pass synthesizerElement through SpawnContext
|
|
156
|
-
const spawnContext =
|
|
155
|
+
// Pass synthesizerElement and full EMC config through SpawnContext
|
|
156
|
+
const spawnContext = { synthesizerElement, emc: emcConfig };
|
|
157
157
|
await enh.get(enhancementConfig, spawnContext);
|
|
158
158
|
}
|
|
159
159
|
/**
|
package/handlers/EMCScript.ts
CHANGED
|
@@ -180,8 +180,8 @@ export class EMCScriptHandler extends EvtRt {
|
|
|
180
180
|
throw new Error('Element does not have enh property. Make sure ElementMountExtension is loaded.');
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
// Pass synthesizerElement through SpawnContext
|
|
184
|
-
const spawnContext =
|
|
183
|
+
// Pass synthesizerElement and full EMC config through SpawnContext
|
|
184
|
+
const spawnContext = { synthesizerElement, emc: emcConfig };
|
|
185
185
|
await enh.get(enhancementConfig, spawnContext);
|
|
186
186
|
}
|
|
187
187
|
|
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.setupElementIntersection = setupElementIntersection;
|
|
4
|
-
exports.isElementIntersecting = isElementIntersecting;
|
|
5
|
-
var Events_js_1 = require("./Events.js");
|
|
6
|
-
function setupElementIntersection(init, rootNodeRef, mountedElements, modules, observer, matchesSelector, handleMatch) {
|
|
7
|
-
var whereElementIntersectsWith = init.whereElementIntersectsWith;
|
|
8
|
-
if (!whereElementIntersectsWith) {
|
|
9
|
-
throw new Error('whereElementIntersectsWith is required');
|
|
10
|
-
}
|
|
11
|
-
// Track which elements are currently intersecting
|
|
12
|
-
var intersectingElements = new WeakSet();
|
|
13
|
-
// Create IntersectionObserver with the provided options
|
|
14
|
-
var intersectionObserver = new IntersectionObserver(function (entries) {
|
|
15
|
-
for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
|
|
16
|
-
var entry = entries_1[_i];
|
|
17
|
-
var element = entry.target;
|
|
18
|
-
if (entry.isIntersecting) {
|
|
19
|
-
// Element is now intersecting
|
|
20
|
-
intersectingElements.add(element);
|
|
21
|
-
// Check if element matches all other conditions and mount if so
|
|
22
|
-
if (matchesSelector(element)) {
|
|
23
|
-
handleMatch(element);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
// Element is no longer intersecting
|
|
28
|
-
intersectingElements.delete(element);
|
|
29
|
-
// Dismount if it was mounted
|
|
30
|
-
if (mountedElements.weakSet.has(element)) {
|
|
31
|
-
dismountElement(element);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}, whereElementIntersectsWith);
|
|
36
|
-
function dismountElement(element) {
|
|
37
|
-
// Remove from mounted elements
|
|
38
|
-
mountedElements.weakSet.delete(element);
|
|
39
|
-
for (var _i = 0, _a = mountedElements.setWeak; _i < _a.length; _i++) {
|
|
40
|
-
var ref = _a[_i];
|
|
41
|
-
if (ref.deref() === element) {
|
|
42
|
-
mountedElements.setWeak.delete(ref);
|
|
43
|
-
break;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
// Dispatch dismount event
|
|
47
|
-
observer.dispatchEvent(new Events_js_1.DismountEvent(element, 'intersection-failed', init));
|
|
48
|
-
}
|
|
49
|
-
function observeElement(element) {
|
|
50
|
-
intersectionObserver.observe(element);
|
|
51
|
-
}
|
|
52
|
-
return {
|
|
53
|
-
intersectionObserver: intersectionObserver,
|
|
54
|
-
observeElement: observeElement,
|
|
55
|
-
cleanup: function () {
|
|
56
|
-
intersectionObserver.disconnect();
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Check if an element is currently intersecting
|
|
62
|
-
* This is called from #matchesSelector to determine if intersection condition is met
|
|
63
|
-
*/
|
|
64
|
-
function isElementIntersecting(element, intersectionObserver) {
|
|
65
|
-
// If no intersection observer is set up, consider all elements as intersecting
|
|
66
|
-
if (!intersectionObserver) {
|
|
67
|
-
return true;
|
|
68
|
-
}
|
|
69
|
-
// When intersection observer is active, we can't synchronously determine intersection state
|
|
70
|
-
// The element will be observed and the callback will handle mounting when it intersects
|
|
71
|
-
// Return false here to prevent immediate mounting
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setupElementIntersection = setupElementIntersection;
|
|
4
|
+
exports.isElementIntersecting = isElementIntersecting;
|
|
5
|
+
var Events_js_1 = require("./Events.js");
|
|
6
|
+
function setupElementIntersection(init, rootNodeRef, mountedElements, modules, observer, matchesSelector, handleMatch) {
|
|
7
|
+
var whereElementIntersectsWith = init.whereElementIntersectsWith;
|
|
8
|
+
if (!whereElementIntersectsWith) {
|
|
9
|
+
throw new Error('whereElementIntersectsWith is required');
|
|
10
|
+
}
|
|
11
|
+
// Track which elements are currently intersecting
|
|
12
|
+
var intersectingElements = new WeakSet();
|
|
13
|
+
// Create IntersectionObserver with the provided options
|
|
14
|
+
var intersectionObserver = new IntersectionObserver(function (entries) {
|
|
15
|
+
for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
|
|
16
|
+
var entry = entries_1[_i];
|
|
17
|
+
var element = entry.target;
|
|
18
|
+
if (entry.isIntersecting) {
|
|
19
|
+
// Element is now intersecting
|
|
20
|
+
intersectingElements.add(element);
|
|
21
|
+
// Check if element matches all other conditions and mount if so
|
|
22
|
+
if (matchesSelector(element)) {
|
|
23
|
+
handleMatch(element);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
// Element is no longer intersecting
|
|
28
|
+
intersectingElements.delete(element);
|
|
29
|
+
// Dismount if it was mounted
|
|
30
|
+
if (mountedElements.weakSet.has(element)) {
|
|
31
|
+
dismountElement(element);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}, whereElementIntersectsWith);
|
|
36
|
+
function dismountElement(element) {
|
|
37
|
+
// Remove from mounted elements
|
|
38
|
+
mountedElements.weakSet.delete(element);
|
|
39
|
+
for (var _i = 0, _a = mountedElements.setWeak; _i < _a.length; _i++) {
|
|
40
|
+
var ref = _a[_i];
|
|
41
|
+
if (ref.deref() === element) {
|
|
42
|
+
mountedElements.setWeak.delete(ref);
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Dispatch dismount event
|
|
47
|
+
observer.dispatchEvent(new Events_js_1.DismountEvent(element, 'intersection-failed', init));
|
|
48
|
+
}
|
|
49
|
+
function observeElement(element) {
|
|
50
|
+
intersectionObserver.observe(element);
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
intersectionObserver: intersectionObserver,
|
|
54
|
+
observeElement: observeElement,
|
|
55
|
+
cleanup: function () {
|
|
56
|
+
intersectionObserver.disconnect();
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check if an element is currently intersecting
|
|
62
|
+
* This is called from #matchesSelector to determine if intersection condition is met
|
|
63
|
+
*/
|
|
64
|
+
function isElementIntersecting(element, intersectionObserver) {
|
|
65
|
+
// If no intersection observer is set up, consider all elements as intersecting
|
|
66
|
+
if (!intersectionObserver) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
// When intersection observer is active, we can't synchronously determine intersection state
|
|
70
|
+
// The element will be observed and the callback will handle mounting when it intersects
|
|
71
|
+
// Return false here to prevent immediate mounting
|
|
72
|
+
return false;
|
|
73
|
+
}
|
package/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ export { MountObserverScriptHandler } from './handlers/MountObserverScript.js';
|
|
|
12
12
|
export { EMCScriptHandler } from './handlers/EMCScript.js';
|
|
13
13
|
export { HoistTemplateHandler } from './handlers/HoistTemplate.js';
|
|
14
14
|
export { HTMLIncludeHandler } from './handlers/HTMLInclude.js';
|
|
15
|
+
export { CedeScriptHandler } from './handlers/CedeScript.js';
|
|
15
16
|
export { upShadowSearch } from './upShadowSearch.js';
|
|
16
17
|
export type {
|
|
17
18
|
MountConfig,
|
|
@@ -44,3 +45,4 @@ import './handlers/MountObserverScript.js';
|
|
|
44
45
|
import './handlers/EMCScript.js';
|
|
45
46
|
import './handlers/HoistTemplate.js';
|
|
46
47
|
import './handlers/HTMLInclude.js';
|
|
48
|
+
import './handlers/CedeScript.js';
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mount-observer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.37",
|
|
4
4
|
"description": "Observe and act on css matches.",
|
|
5
5
|
"main": "MountObserver.js",
|
|
6
6
|
"module": "MountObserver.js",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"assign-gingerly": "0.0.
|
|
8
|
+
"assign-gingerly": "0.0.50",
|
|
9
9
|
"id-generation": "0.0.4"
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"@playwright/test": "1.
|
|
12
|
+
"@playwright/test": "1.60.0",
|
|
13
13
|
"spa-ssi": "0.0.27"
|
|
14
14
|
},
|
|
15
15
|
"exports": {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {RAConfig} from '../roundabout/types';
|
|
2
|
+
import {AttrPatterns} from '../assign-gingerly/types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Assign Gingerly Roundabout Config
|
|
6
|
+
*/
|
|
7
|
+
export interface AgraceConfig<TProps = unknown, TActions = TProps, ETProps = TProps, TCustomData = unknown> {
|
|
8
|
+
raConfig: RAConfig<TProps, TActions, ETProps, TCustomData>,
|
|
9
|
+
withAttrs?: AttrPatterns<TProps>,
|
|
10
|
+
template?: string | HTMLTemplateElement,
|
|
11
|
+
}
|
|
@@ -212,6 +212,13 @@ export interface SpawnContext<T = any, TMountContext = any> {
|
|
|
212
212
|
* Used for scoped parser registry access during attribute parsing.
|
|
213
213
|
*/
|
|
214
214
|
synthesizerElement?: Element;
|
|
215
|
+
/**
|
|
216
|
+
* The full EMC configuration object that triggered this spawn.
|
|
217
|
+
* Passed through so enhancement classes can access their full configuration
|
|
218
|
+
* (including customData) without needing to separately import the JSON file.
|
|
219
|
+
* This avoids duplicate JSON imports when using emoji shorthand aliases.
|
|
220
|
+
*/
|
|
221
|
+
emc?: any;
|
|
215
222
|
}
|
|
216
223
|
|
|
217
224
|
/**
|
|
@@ -226,6 +233,14 @@ export interface IAssignGingerlyOptions {
|
|
|
226
233
|
registry?: typeof EnhancementRegistry | EnhancementRegistry;
|
|
227
234
|
bypassChecks?: boolean;
|
|
228
235
|
withMethods?: string[] | Set<string>;
|
|
236
|
+
aka?: Record<string, string>;
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* AbortSignal for cleaning up reactive subscriptions (@eachTime)
|
|
240
|
+
* Required when using @eachTime symbol for reactive iteration
|
|
241
|
+
* When the signal is aborted, all event listeners are automatically removed
|
|
242
|
+
*/
|
|
243
|
+
signal?: AbortSignal;
|
|
229
244
|
}
|
|
230
245
|
|
|
231
246
|
/**
|
|
@@ -242,7 +257,6 @@ export declare class EnhancementRegisteredEvent extends Event {
|
|
|
242
257
|
* Extends EventTarget to dispatch events when configs are registered
|
|
243
258
|
*/
|
|
244
259
|
export declare class EnhancementRegistry extends EventTarget {
|
|
245
|
-
private items;
|
|
246
260
|
push(items: EnhancementConfig | EnhancementConfig[]): void;
|
|
247
261
|
getItems(): EnhancementConfig[];
|
|
248
262
|
findBySymbol(symbol: symbol | string): EnhancementConfig | undefined;
|
|
@@ -303,10 +317,7 @@ export declare class ElementEnhancementGateway{
|
|
|
303
317
|
}
|
|
304
318
|
|
|
305
319
|
export interface ElementEnhancement{
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
export interface ElementInfer{
|
|
310
|
-
value: any;
|
|
311
|
-
eventType: string
|
|
320
|
+
get(registryItem: EnhancementConfig | string | symbol, mountCtx?: any): any;
|
|
321
|
+
dispose(registryItem: EnhancementConfig | string | symbol): void;
|
|
322
|
+
whenResolved(registryItem: EnhancementConfig | string | symbol, mountCtx?: any): Promise<any>;
|
|
312
323
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ElementEnhancementGateway } from "../assign-gingerly/types";
|
|
1
|
+
import { ElementEnhancementGateway, SpawnContext } from "../assign-gingerly/types";
|
|
2
2
|
import { StatementsResult } from "../nested-regex-groups/types";
|
|
3
3
|
|
|
4
4
|
export interface Specifier {
|
|
@@ -23,7 +23,7 @@ export type ProPAP = Promise<PAP>
|
|
|
23
23
|
|
|
24
24
|
export interface Actions{
|
|
25
25
|
hydrate(self: AP): ProPAP;
|
|
26
|
-
init(self: AP, enhancedElement: Element, initVals: PAP): Promise<void>
|
|
26
|
+
init(self: AP, enhancedElement: Element, ctx: SpawnContext, initVals: PAP): Promise<void>
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { EnhancementConfig } from "../assign-gingerly/types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Symbol for smart value assignment
|
|
5
|
+
*/
|
|
6
|
+
export declare const value: symbol;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Symbol for smart display assignment
|
|
10
|
+
*/
|
|
11
|
+
export declare const display: symbol;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Enhancement class that provides smart value and display property inference
|
|
15
|
+
*/
|
|
16
|
+
export declare class Infer<TValue = any, TDisplay = any> {
|
|
17
|
+
get enhancedElement(): Element;
|
|
18
|
+
constructor(enhancedElement?: Element);
|
|
19
|
+
get value(): TValue | undefined;
|
|
20
|
+
set value(nv: TValue);
|
|
21
|
+
get display(): TDisplay | undefined;
|
|
22
|
+
set display(nv: TDisplay);
|
|
23
|
+
get eventType(): string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Registry item for the Infer enhancement
|
|
28
|
+
*/
|
|
29
|
+
export declare const registryItem: EnhancementConfig;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Infer the most appropriate value property for an element
|
|
33
|
+
*/
|
|
34
|
+
export declare function inferValueProperty(element: Element): string;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Infer the most appropriate display property for an element
|
|
38
|
+
*/
|
|
39
|
+
export declare function inferDisplayProperty(element: Element): string;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Infer the most appropriate event type for an element
|
|
43
|
+
*/
|
|
44
|
+
export declare function inferEventType(element: Element): string;
|
|
45
|
+
|
|
46
|
+
export default registryItem;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Core types for MountObserver v2 - Polyfill Supported Scenario I
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {EnhancementConfigBase, EnhKey, AttrPatterns} from '../assign-gingerly/types';
|
|
4
4
|
|
|
5
5
|
export type Constructor = new (...args: any[]) => any;
|
|
6
6
|
|
|
@@ -24,11 +24,17 @@ export interface LogicOp<Props = any, TActions = Props>{
|
|
|
24
24
|
|
|
25
25
|
delay?: number,
|
|
26
26
|
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Extends LogicOp with a `do` property for specifying which function to call.
|
|
31
|
+
* Used by positractions where the function is generic and view-model-neutral.
|
|
32
|
+
*/
|
|
33
|
+
export interface LogicOpWithDo<Props = any, TActions = Props> extends LogicOp<Props, TActions>{
|
|
27
34
|
do?:
|
|
28
35
|
| Function
|
|
29
36
|
| (keyof TActions & string)
|
|
30
37
|
| PropsToProps<Props>
|
|
31
|
-
|
|
32
38
|
}
|
|
33
39
|
|
|
34
40
|
export type Actions<TProps = any, TActions = TProps> =
|
|
@@ -46,6 +52,8 @@ export type Compacts<TProps = any, TActions = TProps> =
|
|
|
46
52
|
| Partial<{[key in `when_${keyof TProps & string}_changes_toggle_${keyof TProps & string}`]: number}>
|
|
47
53
|
| Partial<{[key in `when_${keyof TProps & string}_changes_inc_${keyof TProps & string}_by`]: number}>
|
|
48
54
|
| Partial<{[key in `when_${keyof TProps & string}_changes_dispatch`]: string}> //TODO
|
|
55
|
+
| Partial<{[key in `on_${string}_of_${keyof TProps & string}_inc_${keyof TProps & string}_by`]: number}>
|
|
56
|
+
| Partial<{[key in `on_${string}_of_${keyof TProps & string}_set_${keyof TProps & string}_to`]: any}>
|
|
49
57
|
;
|
|
50
58
|
|
|
51
59
|
export type Hitches<TProps = any, TActions = TProps> =
|
|
@@ -58,7 +66,7 @@ export type Handlers<ETProps = any, TActions = ETProps> =
|
|
|
58
66
|
export type Positractions<TProps = any, TActions = TProps> =
|
|
59
67
|
| Array<Positraction<TProps, TActions>>;
|
|
60
68
|
|
|
61
|
-
export interface Positraction<TProps = any, TActions = TProps> extends
|
|
69
|
+
export interface Positraction<TProps = any, TActions = TProps> extends LogicOpWithDo<TProps, TActions> {
|
|
62
70
|
do:
|
|
63
71
|
| Function
|
|
64
72
|
| (keyof TActions & string)
|
|
@@ -71,13 +79,30 @@ export interface Positraction<TProps = any, TActions = TProps> extends LogicOp<T
|
|
|
71
79
|
assignTo?: Array<null | (keyof TProps & string)>
|
|
72
80
|
}
|
|
73
81
|
|
|
82
|
+
/**
|
|
83
|
+
* A merge is a fully JSON-serializable reactive rule.
|
|
84
|
+
* When its conditions are met, it calls assignFrom(vm, assignFrom, { from: vm })
|
|
85
|
+
* to resolve RHS path strings against the vm and assign the results into the vm.
|
|
86
|
+
* No method or code is required.
|
|
87
|
+
*/
|
|
88
|
+
export interface Merge<TProps = any> extends LogicOp<TProps> {
|
|
89
|
+
/**
|
|
90
|
+
* Pattern object whose keys are LHS assignGingerly paths and whose
|
|
91
|
+
* values are RHS `?.`-prefixed path strings resolved against the vm.
|
|
92
|
+
*/
|
|
93
|
+
assign: Record<string, any>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export type Merges<TProps = any> = Array<Merge<TProps>>;
|
|
97
|
+
|
|
74
98
|
export interface RAConfig<TProps = unknown, TActions = TProps, ETProps = TProps, TCustomData = unknown> {
|
|
75
99
|
actions?: Actions<TProps,TActions>,
|
|
76
100
|
compacts?: Compacts<TProps, TActions>,
|
|
77
101
|
//onsets?: Onsets<TProps, TActions>,
|
|
78
102
|
handlers?: Handlers<ETProps, TActions>,
|
|
79
|
-
|
|
103
|
+
hitches?: Hitches<TProps, TActions>,
|
|
80
104
|
positractions?: Positractions<TProps>,
|
|
105
|
+
merges?: Merges<TProps>,
|
|
81
106
|
/**
|
|
82
107
|
* Configure automatic WeakRef wrapping for properties
|
|
83
108
|
*
|
|
@@ -128,6 +153,12 @@ export interface RoundaboutOptions<TProps = unknown, TActions = TProps, ETProps
|
|
|
128
153
|
* - Diamond dependencies (A→B, A→C, B→D, C→D)
|
|
129
154
|
*/
|
|
130
155
|
internalRouting?: boolean,
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Options passed to every internal assignGingerly call.
|
|
159
|
+
* See IAssignGingerlyOptions in assign-gingerly for details.
|
|
160
|
+
*/
|
|
161
|
+
assignGingerlyOptions?: import('../assign-gingerly/types.js').IAssignGingerlyOptions,
|
|
131
162
|
|
|
132
163
|
|
|
133
164
|
}
|