@studiometa/js-toolkit 4.0.0-alpha.1 → 4.0.0-alpha.2
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 +10 -0
- package/package.json +5 -2
- package/skills/studiometa-js-toolkit/SKILL.md +87 -0
- package/skills/studiometa-js-toolkit/references/api/configuration.md +169 -0
- package/skills/studiometa-js-toolkit/references/api/context/createContext.md +46 -0
- package/skills/studiometa-js-toolkit/references/api/context/createGroup.md +67 -0
- package/skills/studiometa-js-toolkit/references/api/context/index.md +69 -0
- package/skills/studiometa-js-toolkit/references/api/context/injectContext.md +58 -0
- package/skills/studiometa-js-toolkit/references/api/context/injectContextSync.md +44 -0
- package/skills/studiometa-js-toolkit/references/api/context/provideContext.md +61 -0
- package/skills/studiometa-js-toolkit/references/api/context/provideRootContext.md +42 -0
- package/skills/studiometa-js-toolkit/references/api/context/signal.md +85 -0
- package/skills/studiometa-js-toolkit/references/api/context/subscribeContext.md +71 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/children.md +92 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/component.md +75 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/index.md +41 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/inject.md +73 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/on.md +149 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/provide.md +77 -0
- package/skills/studiometa-js-toolkit/references/api/decorators/read-write.md +93 -0
- package/skills/studiometa-js-toolkit/references/api/diagnostics/DIAGNOSTICS.md +137 -0
- package/skills/studiometa-js-toolkit/references/api/diagnostics/EVENTS.md +45 -0
- package/skills/studiometa-js-toolkit/references/api/diagnostics/index.md +58 -0
- package/skills/studiometa-js-toolkit/references/api/diagnostics/reportDiagnostic.md +67 -0
- package/skills/studiometa-js-toolkit/references/api/diagnostics/warn.md +54 -0
- package/skills/studiometa-js-toolkit/references/api/dom/breakpoints.md +81 -0
- package/skills/studiometa-js-toolkit/references/api/dom/domUpdate.md +70 -0
- package/skills/studiometa-js-toolkit/references/api/dom/emitExtendable.md +68 -0
- package/skills/studiometa-js-toolkit/references/api/dom/index.md +26 -0
- package/skills/studiometa-js-toolkit/references/api/dom/swap.md +105 -0
- package/skills/studiometa-js-toolkit/references/api/dom/watchAttributeNamespace.md +96 -0
- package/skills/studiometa-js-toolkit/references/api/dom/watchAttributes.md +56 -0
- package/skills/studiometa-js-toolkit/references/api/dom/whenDOMSettled.md +41 -0
- package/skills/studiometa-js-toolkit/references/api/html/data-component.md +105 -0
- package/skills/studiometa-js-toolkit/references/api/html/data-mount.md +69 -0
- package/skills/studiometa-js-toolkit/references/api/html/data-option.md +106 -0
- package/skills/studiometa-js-toolkit/references/api/html/data-ref.md +63 -0
- package/skills/studiometa-js-toolkit/references/api/html/index.md +32 -0
- package/skills/studiometa-js-toolkit/references/api/index.md +73 -0
- package/skills/studiometa-js-toolkit/references/api/instance-events.md +75 -0
- package/skills/studiometa-js-toolkit/references/api/instance-methods.md +267 -0
- package/skills/studiometa-js-toolkit/references/api/instance-properties.md +90 -0
- package/skills/studiometa-js-toolkit/references/api/methods-hooks-events.md +149 -0
- package/skills/studiometa-js-toolkit/references/api/methods-hooks-lifecycle.md +90 -0
- package/skills/studiometa-js-toolkit/references/api/methods-hooks-options.md +96 -0
- package/skills/studiometa-js-toolkit/references/api/methods-hooks-services.md +164 -0
- package/skills/studiometa-js-toolkit/references/api/registry/defineManifest.md +57 -0
- package/skills/studiometa-js-toolkit/references/api/registry/fromMetaGlob.md +33 -0
- package/skills/studiometa-js-toolkit/references/api/registry/fromWebpackContext.md +37 -0
- package/skills/studiometa-js-toolkit/references/api/registry/getInstance.md +52 -0
- package/skills/studiometa-js-toolkit/references/api/registry/getInstances.md +48 -0
- package/skills/studiometa-js-toolkit/references/api/registry/getMountedInstances.md +39 -0
- package/skills/studiometa-js-toolkit/references/api/registry/getUnmountedInstances.md +46 -0
- package/skills/studiometa-js-toolkit/references/api/registry/index.md +49 -0
- package/skills/studiometa-js-toolkit/references/api/registry/registerComponent.md +93 -0
- package/skills/studiometa-js-toolkit/references/api/registry/registerComponents.md +37 -0
- package/skills/studiometa-js-toolkit/references/api/registry/registerManifest.md +72 -0
- package/skills/studiometa-js-toolkit/references/api/scheduler/defaultScheduler.md +104 -0
- package/skills/studiometa-js-toolkit/references/api/scheduler/index.md +27 -0
- package/skills/studiometa-js-toolkit/references/api/scheduler/nextFrame.md +31 -0
- package/skills/studiometa-js-toolkit/references/api/scheduler/viewTransition.md +53 -0
- package/skills/studiometa-js-toolkit/references/api/services/createService.md +95 -0
- package/skills/studiometa-js-toolkit/references/api/services/createServiceMixin.md +81 -0
- package/skills/studiometa-js-toolkit/references/api/services/index.md +94 -0
- package/skills/studiometa-js-toolkit/references/api/services/mixins.md +148 -0
- package/skills/studiometa-js-toolkit/references/api/services/perTarget.md +65 -0
- package/skills/studiometa-js-toolkit/references/api/services/toggle.md +80 -0
- package/skills/studiometa-js-toolkit/references/api/services/until.md +44 -0
- package/skills/studiometa-js-toolkit/references/api/services/useBreakpoint.md +42 -0
- package/skills/studiometa-js-toolkit/references/api/services/useDrag.md +119 -0
- package/skills/studiometa-js-toolkit/references/api/services/useInView.md +64 -0
- package/skills/studiometa-js-toolkit/references/api/services/useKey.md +105 -0
- package/skills/studiometa-js-toolkit/references/api/services/useMediaQuery.md +45 -0
- package/skills/studiometa-js-toolkit/references/api/services/useMutation.md +69 -0
- package/skills/studiometa-js-toolkit/references/api/services/usePointer.md +91 -0
- package/skills/studiometa-js-toolkit/references/api/services/usePrefersReducedMotion.md +42 -0
- package/skills/studiometa-js-toolkit/references/api/services/useRaf.md +91 -0
- package/skills/studiometa-js-toolkit/references/api/services/useResize.md +60 -0
- package/skills/studiometa-js-toolkit/references/api/services/useScroll.md +88 -0
- package/skills/studiometa-js-toolkit/references/api/services/useScrollProgress.md +88 -0
- package/skills/studiometa-js-toolkit/references/api/services/useWindowScroll.md +25 -0
- package/skills/studiometa-js-toolkit/references/api/services/useWindowSize.md +25 -0
- package/skills/studiometa-js-toolkit/references/api/storage/createStorage.md +126 -0
- package/skills/studiometa-js-toolkit/references/api/storage/index.md +33 -0
- package/skills/studiometa-js-toolkit/references/api/storage/presets.md +58 -0
- package/skills/studiometa-js-toolkit/references/api/storage/providers.md +109 -0
- package/skills/studiometa-js-toolkit/references/api/test/index.md +214 -0
- package/skills/studiometa-js-toolkit/references/guide/concepts/attribute-grammar.md +93 -0
- package/skills/studiometa-js-toolkit/references/guide/concepts/mutation-engine.md +71 -0
- package/skills/studiometa-js-toolkit/references/guide/concepts/philosophy.md +78 -0
- package/skills/studiometa-js-toolkit/references/guide/concepts/the-registry.md +110 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/autoloading.md +104 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/handling-diagnostics.md +104 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/mount-strategies.md +95 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/scheduling-work.md +124 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/sharing-state.md +218 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/swapping-content.md +92 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/testing-components.md +137 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/typing-components.md +190 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/using-decorators.md +168 -0
- package/skills/studiometa-js-toolkit/references/guide/going-further/using-storage.md +125 -0
- package/skills/studiometa-js-toolkit/references/guide/index.md +163 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/installation.md +144 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/lifecycle-hooks.md +174 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/managing-components.md +229 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/managing-options.md +235 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/managing-refs.md +174 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/using-services.md +218 -0
- package/skills/studiometa-js-toolkit/references/guide/introduction/working-with-events.md +251 -0
- package/skills/studiometa-js-toolkit/references/guide/migration/v3-to-v4.md +373 -0
- package/skills/studiometa-js-toolkit/references/utils/css.md +98 -0
- package/skills/studiometa-js-toolkit/references/utils/dom.md +54 -0
- package/skills/studiometa-js-toolkit/references/utils/easings.md +217 -0
- package/skills/studiometa-js-toolkit/references/utils/focus.md +84 -0
- package/skills/studiometa-js-toolkit/references/utils/history.md +69 -0
- package/skills/studiometa-js-toolkit/references/utils/index.md +53 -0
- package/skills/studiometa-js-toolkit/references/utils/is.md +84 -0
- package/skills/studiometa-js-toolkit/references/utils/load.md +59 -0
- package/skills/studiometa-js-toolkit/references/utils/math.md +129 -0
- package/skills/studiometa-js-toolkit/references/utils/motion.md +218 -0
- package/skills/studiometa-js-toolkit/references/utils/objects.md +68 -0
- package/skills/studiometa-js-toolkit/references/utils/scroll.md +128 -0
- package/skills/studiometa-js-toolkit/references/utils/strings.md +176 -0
- package/skills/studiometa-js-toolkit/references/utils/timing.md +111 -0
- package/skills/studiometa-js-toolkit/references/utils/transitions.md +122 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# domUpdate
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
domUpdate(target: Node, mutate: () => void | Promise<void>, detail?: Record<string, unknown>): Promise<void>
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
Announces a DOM mutation before it happens, so anything above the node can **take it over**.
|
|
8
|
+
|
|
9
|
+
This is the _take-over_ mode of the negotiated-event mechanism. The _delay_ mode is [`emitExtendable()`](emitExtendable.md).
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
The code that mutates announces instead of mutating:
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import { domUpdate } from '@studiometa/js-toolkit';
|
|
17
|
+
|
|
18
|
+
await domUpdate(this.$el, () => this.$el.replaceChildren(fragment));
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
An ancestor claims it:
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
import { EVENTS, viewTransition } from '@studiometa/js-toolkit';
|
|
25
|
+
|
|
26
|
+
this.$on(EVENTS.dom.update, ({ detail }) => detail.wrap(viewTransition));
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## The detail
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
interface DomUpdateDetail {
|
|
33
|
+
wrap(runner: DomUpdateRunner): void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type DomUpdateRunner =
|
|
37
|
+
| ((apply: () => void | Promise<void>) => void | Promise<unknown>)
|
|
38
|
+
| { update(mutate: () => void | Promise<void>): void | Promise<unknown> };
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The event is `EVENTS.dom.update` — `'js-toolkit:dom:update'` — dispatched on `target` as a bubbling, **non-cancelable** `CustomEvent`.
|
|
42
|
+
|
|
43
|
+
**The detail is one object**, like every other v4 event. A decorated handler reads `{ payload: { wrap } }` for what a raw listener reads as `event.detail`.
|
|
44
|
+
|
|
45
|
+
## What a claim composes with
|
|
46
|
+
|
|
47
|
+
| Claim | Effect |
|
|
48
|
+
| --------------------------------------------- | -------------------------------------------------------------------- |
|
|
49
|
+
| `wrap(viewTransition)` | the change plays as one batched native view transition |
|
|
50
|
+
| `wrap((apply) => this.$write(apply).promise)` | the change lands in the `write` phase, batched, cancelled on unmount |
|
|
51
|
+
| `wrap(motionView)` | any object with `update(mutate)` — which `MotionView` already is |
|
|
52
|
+
|
|
53
|
+
**Neither runner is the default.** The ancestor chooses the lane, because it knows whether the region animates.
|
|
54
|
+
|
|
55
|
+
## The rules
|
|
56
|
+
|
|
57
|
+
- **`defaultPrevented` is ignored.** The step is announced, not proposed.
|
|
58
|
+
- **The last claim wins.** Take-over mode keeps one runner.
|
|
59
|
+
- **A registration is valid only while the event dispatches.** A listener that keeps `wrap` and calls it later is reported as `protocol.late-registration` and ignored.
|
|
60
|
+
- **The work of the emitter always completes.** A runner that throws, rejects, or resolves without calling `apply` loses the animation, never the change — and that last case is reported as `protocol.unapplied-dom-update`.
|
|
61
|
+
- **An unclaimed `domUpdate()` is synchronous.** With no listener, the mutation runs before the returned promise exists.
|
|
62
|
+
- A failing runner is reported as `callback.dom-update-runner-failed`.
|
|
63
|
+
|
|
64
|
+
## It has no `Base` path
|
|
65
|
+
|
|
66
|
+
The helper dispatches its own event and is absent from `$emits`, so the negotiation code and the optional view-transition import stay out of the `Base` module graph. **Plain DOM code uses the same function with no instance.**
|
|
67
|
+
|
|
68
|
+
## With `swap()`
|
|
69
|
+
|
|
70
|
+
`domUpdate()` produces exactly the `wrap` that [`swap()`](swap.md) takes, and nothing else. Resilience policy stays here.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# emitExtendable
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
emitExtendable(target: Node, event: string, detail?: Record<string, unknown>): Promise<void>
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
Announces a step before it happens, so anything above the node can **delay** it.
|
|
8
|
+
|
|
9
|
+
This is the _delay_ mode of the negotiated-event mechanism. The _take-over_ mode is [`domUpdate()`](domUpdate.md).
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
The choreography announces its step and waits:
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import { emitExtendable } from '@studiometa/js-toolkit';
|
|
17
|
+
|
|
18
|
+
async close() {
|
|
19
|
+
await emitExtendable(this.$el, 'close');
|
|
20
|
+
this.$el.close();
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
An ancestor asks for more time:
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
this.$on('close', ({ detail }) => detail.waitUntil(this.leave()));
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## The detail
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
interface ExtendableDetail {
|
|
34
|
+
waitUntil(extension: Extension): void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type Extension = PromiseLike<unknown> | (() => unknown) | Record<string, unknown>;
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`waitUntil()` accepts three shapes:
|
|
41
|
+
|
|
42
|
+
| Shape | Example |
|
|
43
|
+
| ------------------------- | ------------------------------- |
|
|
44
|
+
| a thenable | `waitUntil(this.leave())` |
|
|
45
|
+
| a function | `waitUntil(() => this.enter())` |
|
|
46
|
+
| an object with the method | `waitUntil(transitioner)` |
|
|
47
|
+
|
|
48
|
+
**The duck-typed method has the name of the event**: `close()` for the `close` step of a dialog, `update(mutate)` for a DOM change. The function form covers any pair of method names.
|
|
49
|
+
|
|
50
|
+
The event is dispatched on `target` as a bubbling, **non-cancelable** `CustomEvent`.
|
|
51
|
+
|
|
52
|
+
## The rules
|
|
53
|
+
|
|
54
|
+
- **`defaultPrevented` is ignored.** The step is announced, not proposed.
|
|
55
|
+
- **Every registration is kept and all are awaited**, because two components that animate out must both be awaited. This is where it differs from `domUpdate()`, which keeps one.
|
|
56
|
+
- **A registration is valid only while the event dispatches.** A listener that keeps `waitUntil` and calls it later is reported as `protocol.late-registration` and ignored.
|
|
57
|
+
- **The step happens anyway.** An extension that rejects is swallowed, and reported as `callback.extendable-event-extension-failed`.
|
|
58
|
+
|
|
59
|
+
## It has no `Base` path
|
|
60
|
+
|
|
61
|
+
The helper dispatches its own event and is absent from `$emits`. Plain DOM code uses the same function with no instance.
|
|
62
|
+
|
|
63
|
+
## Which mode to reach for
|
|
64
|
+
|
|
65
|
+
| You are announcing | Use |
|
|
66
|
+
| ----------------------------------------------------- | --------------------------------- |
|
|
67
|
+
| an **action** someone may want to perform differently | [`domUpdate()`](domUpdate.md) |
|
|
68
|
+
| a **moment** someone may want to postpone | `emitExtendable()` |
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# DOM
|
|
2
|
+
|
|
3
|
+
The functions that change the document, negotiate a change, or watch an attribute the framework does not read.
|
|
4
|
+
|
|
5
|
+
## Changing content
|
|
6
|
+
|
|
7
|
+
- [`swap(target, content, options?)`](swap.md) — replace, prepend, append or morph, and wait for the framework to catch up
|
|
8
|
+
- [`whenDOMSettled()`](whenDOMSettled.md) — the completion boundary
|
|
9
|
+
|
|
10
|
+
## Negotiating a change
|
|
11
|
+
|
|
12
|
+
- [`domUpdate(target, mutate, detail?)`](domUpdate.md) — announce a mutation so an ancestor can take it over
|
|
13
|
+
- [`emitExtendable(target, event, detail?)`](emitExtendable.md) — announce a step so an ancestor can delay it
|
|
14
|
+
|
|
15
|
+
## Watching attributes
|
|
16
|
+
|
|
17
|
+
- [`watchAttributes(el, callback)`](watchAttributes.md) — every attribute of one element
|
|
18
|
+
- [`watchAttributeNamespace(el, ns, bind, options?)`](watchAttributeNamespace.md) — a namespace whose names cannot be enumerated
|
|
19
|
+
|
|
20
|
+
## Breakpoints
|
|
21
|
+
|
|
22
|
+
- [`BREAKPOINTS`, `getBreakpoints()`, `setBreakpoints()`](breakpoints.md)
|
|
23
|
+
|
|
24
|
+
## Read next
|
|
25
|
+
|
|
26
|
+
[The mutation engine](../../guide/concepts/mutation-engine.md) and [The attribute grammar](../../guide/concepts/attribute-grammar.md).
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# swap
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
swap(target: Element, content: SwapContent, options?: SwapOptions): Promise<void>
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
Replaces the **content** of an element and resolves once the framework has caught up.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
type SwapContent = string | Element | DocumentFragment;
|
|
11
|
+
|
|
12
|
+
interface SwapOptions {
|
|
13
|
+
mode?: 'replace' | 'prepend' | 'append' | 'morph';
|
|
14
|
+
wrap?: (mutate: () => void) => void | Promise<unknown>;
|
|
15
|
+
self?: boolean;
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { swap } from '@studiometa/js-toolkit';
|
|
23
|
+
|
|
24
|
+
async function load(target: Element) {
|
|
25
|
+
const response = await fetch('/fragment');
|
|
26
|
+
await swap(target, await response.text());
|
|
27
|
+
// The mutation is applied, the new components are mounted, the old ones unmounted.
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The returned promise resolves after [`whenDOMSettled()`](whenDOMSettled.md).
|
|
32
|
+
|
|
33
|
+
## What survives
|
|
34
|
+
|
|
35
|
+
**By default the element itself is never replaced**, so the caller's reference, its `id` and any instance on it survive. Only its children change.
|
|
36
|
+
|
|
37
|
+
`content` as a **string** is parsed in the parsing context of the target, so `<tr>`, `<li>` and `<option>` survive. As an `Element` or a `DocumentFragment` it is read as the incoming counterpart of the target, and its **children** become the new content.
|
|
38
|
+
|
|
39
|
+
## The modes
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
import { SWAP_MODES } from '@studiometa/js-toolkit';
|
|
43
|
+
|
|
44
|
+
SWAP_MODES.REPLACE; // 'replace' — the default
|
|
45
|
+
SWAP_MODES.PREPEND; // 'prepend'
|
|
46
|
+
SWAP_MODES.APPEND; // 'append'
|
|
47
|
+
SWAP_MODES.MORPH; // 'morph'
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`SwapMode` is derived from the frozen object — the pattern for every closed set of strings in the framework.
|
|
51
|
+
|
|
52
|
+
`prepend` and `append` stay in core because they need the same before-and-after script diff as the other two.
|
|
53
|
+
|
|
54
|
+
### `morph`
|
|
55
|
+
|
|
56
|
+
Identity survives — nodes, focus, expandos, instances — **but markup that is not sent does not.** Two consequences are morphdom's policy, not `swap()`'s:
|
|
57
|
+
|
|
58
|
+
- an element the incoming markup does not contain is discarded even from a preserved parent;
|
|
59
|
+
- morphdom syncs the `value` of an input from the incoming markup.
|
|
60
|
+
|
|
61
|
+
**Attributes are not synced on the target.** Core passes `childrenOnly: true` without `self`, because `data-component` and `data-mount` are lifecycle declarations of the target, not content.
|
|
62
|
+
|
|
63
|
+
## `self`
|
|
64
|
+
|
|
65
|
+
Replaces the target element **itself**, attributes included, instead of its children:
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
await swap(el, html, { mode: 'replace', self: true });
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
It is the axis a caller that matches an element of a response to an element of the page needs: an id-matched section otherwise keeps the classes of the element it replaces.
|
|
72
|
+
|
|
73
|
+
- With `self`, an `Element` content **is** the replacement rather than a container — the only way its own attributes reach the page. A string or a `DocumentFragment` contributes its **first top-level element**.
|
|
74
|
+
- `replace` puts the replacement in the place of the target, so the target **leaves** the document.
|
|
75
|
+
- `morph` morphs the target from the replacement without `childrenOnly`, so the element and its identity survive while its attributes are updated.
|
|
76
|
+
- `append` and `prepend` add to the children by definition, so `self` with either is a `swap.self-ignored` warning — as is content holding no element.
|
|
77
|
+
|
|
78
|
+
## Scripts
|
|
79
|
+
|
|
80
|
+
A `<script>` produced by the fragment parser is flagged as **already started** by the HTML specification and stays inert wherever it is moved. It runs only if it is **recreated** — and a script that was already in the page runs **twice** if it is recreated.
|
|
81
|
+
|
|
82
|
+
`swap()` owns that rule, once. Script adoption follows whatever ends up in the document, so a replacement carrying a script still runs it exactly once.
|
|
83
|
+
|
|
84
|
+
## `wrap`
|
|
85
|
+
|
|
86
|
+
The whole seam. It receives the single mutation of the swap and decides when it runs:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import { swap, viewTransition } from '@studiometa/js-toolkit';
|
|
90
|
+
|
|
91
|
+
async function load(el: Element, html: string) {
|
|
92
|
+
await swap(el, html, { wrap: (mutate) => viewTransition(mutate) });
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
[`domUpdate()`](domUpdate.md) produces that wrapper and nothing else, so an ancestor can claim the lane. **Resilience policy stays with `domUpdate()`** — `swap()` itself throws rather than swallowing.
|
|
97
|
+
|
|
98
|
+
## What is deliberately not in core
|
|
99
|
+
|
|
100
|
+
- **Attribute syncing in a default `morph`** — see above.
|
|
101
|
+
- **Transitions, view transitions, history, `id` matching and response parsing.** All caller policy.
|
|
102
|
+
|
|
103
|
+
## It is a free function
|
|
104
|
+
|
|
105
|
+
Not a `Base` method. `morphdom` is a real, statically imported dependency, and the subpath layout contains the cost: it is reachable through `swap()` only, so a page that never swaps never downloads it.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# watchAttributeNamespace
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
watchAttributeNamespace(
|
|
5
|
+
el: Element,
|
|
6
|
+
namespace: string,
|
|
7
|
+
bind: AttributeNamespaceBinder,
|
|
8
|
+
options?: { qualifiers?: readonly string[]; component?: string },
|
|
9
|
+
): () => void
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The mechanism for a namespace whose names **cannot be enumerated**. Declare the prefix, hand over a binder, and get the per-element observation, the keyed bindings and the teardown.
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
interface AttributeNamespaceDeclaration {
|
|
16
|
+
qualifier: string;
|
|
17
|
+
value: string;
|
|
18
|
+
attribute: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type AttributeNamespaceBinder = (declaration: AttributeNamespaceDeclaration) => (() => void) | void;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
import { watchAttributeNamespace } from '@studiometa/js-toolkit';
|
|
28
|
+
|
|
29
|
+
mounted() {
|
|
30
|
+
return watchAttributeNamespace(this.$el, 'data-on', ({ qualifier, value }) =>
|
|
31
|
+
new ActionEvent(this, qualifier, value).attach(),
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`Base` has no wrapper and owns no cleanup. A component calls it from `mounted()` and returns its cleanup.
|
|
37
|
+
|
|
38
|
+
## One binding per attribute, keyed by the name that produced it
|
|
39
|
+
|
|
40
|
+
That is what lets **one** code path cover all three shapes a change takes:
|
|
41
|
+
|
|
42
|
+
| Change | Effect |
|
|
43
|
+
| ------- | ------------------------------- |
|
|
44
|
+
| added | attach, with nothing to release |
|
|
45
|
+
| changed | release, then attach |
|
|
46
|
+
| removed | release, with nothing to attach |
|
|
47
|
+
|
|
48
|
+
A memoised parse cannot express the middle one — and rewriting an attribute in place is not hypothetical: `swap({ mode: 'morph' })` does it, and so does any templating around the element.
|
|
49
|
+
|
|
50
|
+
**The binder returns that binding's release**, or nothing when the declaration produced no binding. Returning nothing leaves nothing held, so a malformed value costs no bookkeeping.
|
|
51
|
+
|
|
52
|
+
## Declaration order survives a rewrite
|
|
53
|
+
|
|
54
|
+
The bindings are a `Map` keyed by attribute, and `set` on a key already there keeps its position — so a consumer applying its bindings in order is not reordered by an edit.
|
|
55
|
+
|
|
56
|
+
## Validating the qualifier's head
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
watchAttributeNamespace(el, 'data-bind', bind, {
|
|
60
|
+
qualifiers: ['class', 'prop', 'attr', 'style', 'text', 'html'],
|
|
61
|
+
component: 'Bind',
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Given a finite vocabulary, an unknown head warns **once per element and per name** with `attribute.unknown-qualifier` and binds nothing — so `data-bind:prpo.value` stops being an attribute that silently does nothing.
|
|
66
|
+
|
|
67
|
+
Omitted, the vocabulary is **open** and anything binds, which is the only honest answer for `data-on`, whose qualifiers are any DOM event.
|
|
68
|
+
|
|
69
|
+
## The qualifier and its parts
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
data-<namespace>:<qualifier>[.<part>…]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**A dot splits the qualifier, and core reads none of it.** Whether the first part names what a binding writes to (`data-bind:prop.value`) or modifies how it fires (`data-on:click.prevent`) is the business of whoever declared the namespace.
|
|
76
|
+
|
|
77
|
+
Core reads the qualifier's **head** only, to check it against the vocabulary you handed over, and never past it.
|
|
78
|
+
|
|
79
|
+
> Core owns when a declaration is re-parsed and how the attribute is observed. The namespace's owner owns what the string means.
|
|
80
|
+
|
|
81
|
+
## It is built on `watchAttributes()`
|
|
82
|
+
|
|
83
|
+
So its records join the one mutation engine's queue and are reported from the same batch: [`whenDOMSettled()`](whenDOMSettled.md), and therefore [`swap()`](swap.md), covers a namespaced declaration the way it covers a mount.
|
|
84
|
+
|
|
85
|
+
## When a namespace does not need this
|
|
86
|
+
|
|
87
|
+
If the whole set of names can be listed in advance, it belongs in the one page-wide `attributeFilter` instead:
|
|
88
|
+
|
|
89
|
+
| Namespace | Names | Mechanism |
|
|
90
|
+
| ----------------------- | ------------------------------------------- | ------------------------- |
|
|
91
|
+
| `data-option-columns` | `attribute × breakpoint` — enumerable | the one `attributeFilter` |
|
|
92
|
+
| `data-component` | fixed plus one per breakpoint — enumerable | the one `attributeFilter` |
|
|
93
|
+
| `data-on`, `data-track` | any DOM event — open | **this** |
|
|
94
|
+
| `data-bind` | finite head, open name — **not** enumerable | **this** |
|
|
95
|
+
|
|
96
|
+
`data-bind` is the case that shows why "finite or open" is the wrong axis on its own: its binding types are finite, but the class, property or attribute name after the dot is not. See [The attribute grammar](../../guide/concepts/attribute-grammar.md).
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# watchAttributes
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
watchAttributes(el: Element, callback: (change: AttributeChange) => void): () => void
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
Observes **every** attribute of one element, through a second, unfiltered observer.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
interface AttributeChange {
|
|
11
|
+
name: string;
|
|
12
|
+
value: string | null;
|
|
13
|
+
previousValue: string | null;
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Why it exists
|
|
18
|
+
|
|
19
|
+
`attributeFilter` takes exact names and the DOM has no wildcard, so the engine cannot see an attribute the framework cannot name. This is the opt-in, and **the page pays for the elements that ask**.
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
import { Base, watchAttributes } from '@studiometa/js-toolkit';
|
|
25
|
+
|
|
26
|
+
class Bindings extends Base {
|
|
27
|
+
static config = { name: 'Bindings' };
|
|
28
|
+
|
|
29
|
+
mounted() {
|
|
30
|
+
return watchAttributes(this.$el, ({ name, value, previousValue }) => {
|
|
31
|
+
if (!name.startsWith('data-my-')) return;
|
|
32
|
+
// …
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**The caller owns it.** The helper returns one idempotent cleanup and knows nothing about `Base`: a component calls it from `mounted()` and returns the cleanup.
|
|
39
|
+
|
|
40
|
+
## The contract
|
|
41
|
+
|
|
42
|
+
- **The records join the shared queue.** They are drained wherever the engine drains its own, [`whenDOMSettled()`](whenDOMSettled.md) included, and they are reported from the same background task — as the last step of the batch.
|
|
43
|
+
- **A callback runs after the framework work of the batch.** So a component that stops its watcher during the same batch in which its own declaration is withdrawn hears nothing about the attribute change.
|
|
44
|
+
- **Changes are coalesced**, with the rule of [`option<Name>Changed()`](../methods-hooks-options.md): several writes in one batch give one change, from the value before the first write to the value at the end, and a write that ends where it started is not a change.
|
|
45
|
+
- **The payload covers the element's whole attribute set**, framework names included, with raw strings and `null` for an absent attribute. **A caller narrows by prefix.**
|
|
46
|
+
- A failing callback is reported as `callback.attribute-watcher-failed`, so one watcher cannot stop another.
|
|
47
|
+
|
|
48
|
+
## What to reach for instead
|
|
49
|
+
|
|
50
|
+
| Want | Use |
|
|
51
|
+
| -------------------------------------------- | ------------------------------------------------------------- |
|
|
52
|
+
| a declared option | [`option<Name>Changed()`](../methods-hooks-options.md) |
|
|
53
|
+
| a whole namespace, with keyed bindings | [`watchAttributeNamespace()`](watchAttributeNamespace.md) |
|
|
54
|
+
| a subtree, character data, or a foreign node | [`useMutation()`](../services/useMutation.md) |
|
|
55
|
+
|
|
56
|
+
A declared option is already in the one page-wide filter, and costs no second observer.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# whenDOMSettled
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
whenDOMSettled(): Promise<void>
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
The completion boundary for morphing, fetch updates and breakpoint crossings.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { whenDOMSettled } from '@studiometa/js-toolkit';
|
|
13
|
+
|
|
14
|
+
async function replace(el: Element, html: string) {
|
|
15
|
+
el.innerHTML = html;
|
|
16
|
+
await whenDOMSettled();
|
|
17
|
+
// Every eager component in the new markup has mounted.
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
[`swap()`](swap.md) awaits it for you.
|
|
22
|
+
|
|
23
|
+
## What it waits for
|
|
24
|
+
|
|
25
|
+
- it **drains the pending mutation records**;
|
|
26
|
+
- it **follows the mutation chains of eager lifecycle work**, so markup a `mounted()` inserts is covered too;
|
|
27
|
+
- it resolves after the eager mounts **and the teardown**.
|
|
28
|
+
|
|
29
|
+
An **eager lazy** component is covered: the import promise joins the lifecycle-work set for the eager trigger only, so an awaited `swap()` waits for download, registration and mount.
|
|
30
|
+
|
|
31
|
+
Breakpoint work runs through the background lane, so a crossing's teardown, import and mount work is covered as well.
|
|
32
|
+
|
|
33
|
+
## What it does not wait for
|
|
34
|
+
|
|
35
|
+
- **visibility, interaction, idle or media conditions.** A component with `data-mount="visible"` has not mounted when this resolves, and that is the point: waiting on a viewport would mean never resolving on a page that is not scrolled.
|
|
36
|
+
- **the promises returned by `mounted()`.** An async `mounted()` is the component's own business.
|
|
37
|
+
|
|
38
|
+
## Where else it appears
|
|
39
|
+
|
|
40
|
+
- `watchAttributes()` records join the same queue, so a namespaced declaration is covered the way a mount is.
|
|
41
|
+
- A [`useMutation()`](../services/useMutation.md) subscriber that needs the framework's order rather than the platform's awaits this in its callback.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# `data-component`
|
|
2
|
+
|
|
3
|
+
Declares which components run on an element. **The instance exists because this attribute is here and the name is registered.**
|
|
4
|
+
|
|
5
|
+
## One component
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<div data-component="Slider"></div>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The token is the component's `config.name`.
|
|
12
|
+
|
|
13
|
+
## Several components
|
|
14
|
+
|
|
15
|
+
The value is a whitespace-separated token list, and each token gets its own instance:
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<a href="/next" data-component="Action Analytics Prefetch">Next</a>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The instances are independent. They share the element and nothing else.
|
|
22
|
+
|
|
23
|
+
## On native elements
|
|
24
|
+
|
|
25
|
+
`data-component` improves a native element rather than replacing it:
|
|
26
|
+
|
|
27
|
+
```html
|
|
28
|
+
<form data-component="Fetch">…</form>
|
|
29
|
+
<details data-component="Disclosure">…</details>
|
|
30
|
+
<a href="/page" data-component="Prefetch">…</a>
|
|
31
|
+
<tr data-component="Row">…</tr>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Nesting
|
|
35
|
+
|
|
36
|
+
A nested declaration is discovered by the same registry, on its own. Nesting is DOM ancestry, never ownership:
|
|
37
|
+
|
|
38
|
+
```html
|
|
39
|
+
<div data-component="Accordion">
|
|
40
|
+
<div data-component="AccordionItem">…</div>
|
|
41
|
+
<div data-component="AccordionItem">…</div>
|
|
42
|
+
</div>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Unmounting the parent does not unmount the children.
|
|
46
|
+
|
|
47
|
+
## Responsive declarations
|
|
48
|
+
|
|
49
|
+
One breakpoint-scoped companion attribute exists, with the spelling of responsive options:
|
|
50
|
+
|
|
51
|
+
```html
|
|
52
|
+
<div
|
|
53
|
+
data-component="Action Analytics"
|
|
54
|
+
data-component:xxs="MobileMenu MobileSearch"
|
|
55
|
+
data-component:m="DesktopMenu DesktopSearch"></div>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
- The plain token set is **always active**.
|
|
59
|
+
- At the active breakpoint the registry walks from the widest active suffix **down** and takes the first attribute present.
|
|
60
|
+
- That value is the **complete** responsive set. A wider value **replaces** every lower value; it does not merge with it.
|
|
61
|
+
- An empty value is a **stop**, so a name can be removed above a breakpoint.
|
|
62
|
+
- The effective declaration is the **union** of the unconditional set and the selected responsive set, without duplicates.
|
|
63
|
+
|
|
64
|
+
`TabletFeature` below runs at `s` and `m`, and is removed at `l`:
|
|
65
|
+
|
|
66
|
+
```html
|
|
67
|
+
<div data-component="Base" data-component:s="TabletFeature" data-component:l=""></div>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### What a crossing does
|
|
71
|
+
|
|
72
|
+
A crossing compares the effective set against the element's current state:
|
|
73
|
+
|
|
74
|
+
| Case | Effect |
|
|
75
|
+
| ------------------------- | --------------------------------------------------------------------- |
|
|
76
|
+
| a name is in both | it keeps its controller and its instance — nothing happens |
|
|
77
|
+
| a name is no longer there | `$unmount()`, then the instance is dropped from the element |
|
|
78
|
+
| a name is new | the normal pipeline: mount strategy, `data-mount`, lazy entry, events |
|
|
79
|
+
|
|
80
|
+
A crossing back gives a **new** identity, because the DOM stopped declaring that one. An inactive lazy declaration imports nothing.
|
|
81
|
+
|
|
82
|
+
Connected elements with a scoped declaration share **one** reference-counted `useBreakpoint()` subscription. A page with plain declarations opens none. Breakpoint work runs through the background lane, so `whenDOMSettled()` covers the teardown, import and mount work of a crossing.
|
|
83
|
+
|
|
84
|
+
**A suffix must name a configured breakpoint**
|
|
85
|
+
|
|
86
|
+
An unknown suffix is ignored with one `responsive.unknown-breakpoint` warning. That includes the v3 list syntax `data-component:xxs:xs:s`.
|
|
87
|
+
|
|
88
|
+
See [`setBreakpoints()`](../dom/breakpoints.md).
|
|
89
|
+
|
|
90
|
+
## Changing it from code
|
|
91
|
+
|
|
92
|
+
The attribute is live. Adding a token mounts a new instance; removing one unmounts and drops it:
|
|
93
|
+
|
|
94
|
+
```js
|
|
95
|
+
el.dataset.component = 'Slider Analytics';
|
|
96
|
+
await whenDOMSettled();
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Where the instances go
|
|
100
|
+
|
|
101
|
+
Each instance is published on the element under `Symbol.for('@studiometa/js-toolkit/instances')`. That map is not public API — [`getInstance()`](../registry/getInstance.md) and its three siblings are. In a console:
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
$0[Symbol.for('@studiometa/js-toolkit/instances')];
|
|
105
|
+
```
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# `data-mount`
|
|
2
|
+
|
|
3
|
+
Overrides, for one element, **when** its components mount.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<div data-component="Map" data-mount="visible"></div>
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## The values
|
|
10
|
+
|
|
11
|
+
| Value | Mounts when | Reversible |
|
|
12
|
+
| ------------------------ | -------------------------------------- | ---------- |
|
|
13
|
+
| `eager` | the element enters the document | no |
|
|
14
|
+
| `visible[:<rootMargin>]` | it first intersects the viewport | no |
|
|
15
|
+
| `in-view[:<rootMargin>]` | it intersects the viewport | **yes** |
|
|
16
|
+
| `idle` | the main thread becomes idle | no |
|
|
17
|
+
| `interaction` | the user first aims at **it** | no |
|
|
18
|
+
| `interaction:page` | the user first interacts with the page | no |
|
|
19
|
+
| `media:<query>` | the query is not empty and it matches | **yes** |
|
|
20
|
+
|
|
21
|
+
```html
|
|
22
|
+
<div data-component="Hero" data-mount="eager"></div>
|
|
23
|
+
<div data-component="Gallery" data-mount="visible:200px"></div>
|
|
24
|
+
<div data-component="Video" data-mount="in-view:-10% 0px"></div>
|
|
25
|
+
<div data-component="Analytics" data-mount="idle"></div>
|
|
26
|
+
<div data-component="Tooltip" data-mount="interaction"></div>
|
|
27
|
+
<div data-component="Chat" data-mount="interaction:page"></div>
|
|
28
|
+
<div data-component="Parallax" data-mount="media:(min-width: 64rem)"></div>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## The grammar
|
|
32
|
+
|
|
33
|
+
The accepted values are exactly `eager`, `visible`, `visible:`, `visible:<rootMargin>`, `in-view`, `in-view:`, `in-view:<rootMargin>`, `idle`, `interaction`, `interaction:`, `interaction:page` and `media:<non-empty query>`.
|
|
34
|
+
|
|
35
|
+
- A viewport suffix is passed straight through as `IntersectionObserverInit.rootMargin`.
|
|
36
|
+
- The empty suffix behaves as the bare strategy.
|
|
37
|
+
- `page` is the only scope the interaction strategy takes.
|
|
38
|
+
- There is **no** threshold, no root element, no JSON options and no second attribute.
|
|
39
|
+
|
|
40
|
+
## Precedence
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
data-mount > manifest entry mountStrategy > config.mountStrategy > 'eager'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Several components on one element share that element's `data-mount`.** A component that needs its own policy declares `config.mountStrategy` instead.
|
|
47
|
+
|
|
48
|
+
## Invalid values
|
|
49
|
+
|
|
50
|
+
An unknown value, an empty media query, or a `rootMargin` the browser refuses leaves the component **unmounted** and dispatches one cancelable `js-toolkit:diagnostic` with the code `component.invalid-mount-strategy`, the severity `error`, the component name and the original error.
|
|
51
|
+
|
|
52
|
+
The inert controller stays current while the declaration does not change, so it is not reported again; a corrected attribute replaces it. A failed strategy cannot stop the rest of a reconciliation.
|
|
53
|
+
|
|
54
|
+
## It is live
|
|
55
|
+
|
|
56
|
+
A change to `data-mount` disposes the old strategy and applies the final one. Controller identity guards a queued callback of a disposed strategy.
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
el.dataset.mount = 'eager';
|
|
60
|
+
await whenDOMSettled();
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## A waiting component has no instance
|
|
64
|
+
|
|
65
|
+
It is invisible to `$query()`, `$closest()`, `$watchChildren()` and `getInstances()`, and it announces nothing. There is nothing to guard against.
|
|
66
|
+
|
|
67
|
+
Once it has mounted and been stood down again — which only `in-view` and `media:` do — the instance exists and is unmounted, which is what [`getUnmountedInstances()`](../registry/getUnmountedInstances.md) answers.
|
|
68
|
+
|
|
69
|
+
See [Mount strategies](../../guide/going-further/mount-strategies.md) for the full behaviour of each value.
|