@spearwolf/shadow-objects 0.33.0 → 0.35.0
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/CHANGELOG.md +350 -61
- package/README.md +66 -13
- package/bundle.js +6 -53
- package/package.json +15 -3
- package/src/ChangeTrailRefusedError.d.ts +27 -0
- package/src/ChangeTrailRefusedError.d.ts.map +1 -0
- package/src/ChangeTrailRefusedError.js +12 -0
- package/src/ChangeTrailRefusedError.js.map +7 -0
- package/src/EntityUuidInUseError.d.ts +19 -0
- package/src/EntityUuidInUseError.d.ts.map +1 -0
- package/src/EntityUuidInUseError.js +11 -0
- package/src/EntityUuidInUseError.js.map +7 -0
- package/src/WorkerTimeoutError.d.ts +26 -0
- package/src/WorkerTimeoutError.d.ts.map +1 -0
- package/src/WorkerTimeoutError.js +12 -0
- package/src/WorkerTimeoutError.js.map +7 -0
- package/src/bundle.d.ts +1 -1
- package/src/bundle.d.ts.map +1 -1
- package/src/bundle.js +1 -1
- package/src/bundle.js.map +2 -2
- package/src/constants.d.ts +13 -2
- package/src/constants.d.ts.map +1 -1
- package/src/constants.js +7 -1
- package/src/constants.js.map +2 -2
- package/src/create-worker.bundle.d.ts +1 -1
- package/src/create-worker.bundle.d.ts.map +1 -1
- package/src/create-worker.bundle.js.map +1 -1
- package/src/create-worker.d.ts +1 -1
- package/src/create-worker.d.ts.map +1 -1
- package/src/create-worker.js.map +1 -1
- package/src/elements/ShaeElement.d.ts +70 -2
- package/src/elements/ShaeElement.d.ts.map +1 -1
- package/src/elements/ShaeElement.js +129 -25
- package/src/elements/ShaeElement.js.map +3 -3
- package/src/elements/ShaeEntElement.d.ts +39 -1
- package/src/elements/ShaeEntElement.d.ts.map +1 -1
- package/src/elements/ShaeEntElement.js +397 -167
- package/src/elements/ShaeEntElement.js.map +2 -2
- package/src/elements/ShaeLifecycleElement.d.ts +71 -0
- package/src/elements/ShaeLifecycleElement.d.ts.map +1 -0
- package/src/elements/ShaeLifecycleElement.js +83 -0
- package/src/elements/ShaeLifecycleElement.js.map +7 -0
- package/src/elements/ShaePropElement.d.ts +61 -2
- package/src/elements/ShaePropElement.d.ts.map +1 -1
- package/src/elements/ShaePropElement.js +264 -219
- package/src/elements/ShaePropElement.js.map +2 -2
- package/src/elements/ShaeWorkerElement.d.ts +59 -5
- package/src/elements/ShaeWorkerElement.d.ts.map +1 -1
- package/src/elements/ShaeWorkerElement.js +271 -73
- package/src/elements/ShaeWorkerElement.js.map +2 -2
- package/src/elements/constants.d.ts +9 -0
- package/src/elements/constants.d.ts.map +1 -1
- package/src/elements/constants.js +18 -0
- package/src/elements/constants.js.map +2 -2
- package/src/elements/deferredTeardown.d.ts +24 -0
- package/src/elements/deferredTeardown.d.ts.map +1 -0
- package/src/elements/deferredTeardown.js +30 -0
- package/src/elements/deferredTeardown.js.map +7 -0
- package/src/elements/displayContentsRule.d.ts +25 -0
- package/src/elements/displayContentsRule.d.ts.map +1 -0
- package/src/elements/displayContentsRule.js +37 -0
- package/src/elements/displayContentsRule.js.map +7 -0
- package/src/elements/events.d.ts +10 -7
- package/src/elements/events.d.ts.map +1 -1
- package/src/elements/forwardCustomEvents.d.ts +19 -0
- package/src/elements/forwardCustomEvents.d.ts.map +1 -0
- package/src/elements/forwardCustomEvents.js +47 -0
- package/src/elements/forwardCustomEvents.js.map +7 -0
- package/src/elements/hostedSlots.d.ts +36 -0
- package/src/elements/hostedSlots.d.ts.map +1 -0
- package/src/elements/hostedSlots.js +118 -0
- package/src/elements/hostedSlots.js.map +7 -0
- package/src/elements/parentRemoval.d.ts +18 -0
- package/src/elements/parentRemoval.d.ts.map +1 -0
- package/src/elements/parentRemoval.js +60 -0
- package/src/elements/parentRemoval.js.map +7 -0
- package/src/elements/propValueConverters.d.ts +3 -0
- package/src/elements/propValueConverters.d.ts.map +1 -0
- package/src/elements/propValueConverters.js +54 -0
- package/src/elements/propValueConverters.js.map +7 -0
- package/src/elements/requestEntAncestor.d.ts +32 -0
- package/src/elements/requestEntAncestor.d.ts.map +1 -0
- package/src/elements/requestEntAncestor.js +16 -0
- package/src/elements/requestEntAncestor.js.map +7 -0
- package/src/elements.js.map +1 -1
- package/src/in-the-dark/Entity.d.ts +143 -3
- package/src/in-the-dark/Entity.d.ts.map +1 -1
- package/src/in-the-dark/Entity.js +330 -63
- package/src/in-the-dark/Entity.js.map +3 -3
- package/src/in-the-dark/Kernel.d.ts +98 -16
- package/src/in-the-dark/Kernel.d.ts.map +1 -1
- package/src/in-the-dark/Kernel.js +466 -379
- package/src/in-the-dark/Kernel.js.map +3 -3
- package/src/in-the-dark/Registry.d.ts +7 -1
- package/src/in-the-dark/Registry.d.ts.map +1 -1
- package/src/in-the-dark/Registry.js +112 -14
- package/src/in-the-dark/Registry.js.map +2 -2
- package/src/in-the-dark/ShadowObject.d.ts.map +1 -1
- package/src/in-the-dark/ShadowObject.js +1 -0
- package/src/in-the-dark/ShadowObject.js.map +2 -2
- package/src/in-the-dark/ShadowObjectCreationScope.d.ts +133 -0
- package/src/in-the-dark/ShadowObjectCreationScope.d.ts.map +1 -0
- package/src/in-the-dark/ShadowObjectCreationScope.js +603 -0
- package/src/in-the-dark/ShadowObjectCreationScope.js.map +7 -0
- package/src/in-the-dark/SignalsPath.d.ts +2 -0
- package/src/in-the-dark/SignalsPath.d.ts.map +1 -1
- package/src/in-the-dark/SignalsPath.js +6 -2
- package/src/in-the-dark/SignalsPath.js.map +2 -2
- package/src/in-the-dark/displayName.d.ts +4 -0
- package/src/in-the-dark/displayName.d.ts.map +1 -0
- package/src/in-the-dark/displayName.js +5 -0
- package/src/in-the-dark/displayName.js.map +7 -0
- package/src/in-the-dark/events.js.map +1 -1
- package/src/in-the-dark/importModule.d.ts +7 -0
- package/src/in-the-dark/importModule.d.ts.map +1 -1
- package/src/in-the-dark/importModule.js +5 -2
- package/src/in-the-dark/importModule.js.map +2 -2
- package/src/index.d.ts +9 -1
- package/src/index.d.ts.map +1 -1
- package/src/index.js +9 -1
- package/src/index.js.map +2 -2
- package/src/inspect/createKernelSnapshot.d.ts +17 -0
- package/src/inspect/createKernelSnapshot.d.ts.map +1 -0
- package/src/inspect/createKernelSnapshot.js +247 -0
- package/src/inspect/createKernelSnapshot.js.map +7 -0
- package/src/inspect/createViewSnapshot.d.ts +10 -0
- package/src/inspect/createViewSnapshot.d.ts.map +1 -0
- package/src/inspect/createViewSnapshot.js +143 -0
- package/src/inspect/createViewSnapshot.js.map +7 -0
- package/src/inspect/normalizeInspectRequest.d.ts +32 -0
- package/src/inspect/normalizeInspectRequest.d.ts.map +1 -0
- package/src/inspect/normalizeInspectRequest.js +55 -0
- package/src/inspect/normalizeInspectRequest.js.map +7 -0
- package/src/inspect/serializeValue.d.ts +13 -0
- package/src/inspect/serializeValue.d.ts.map +1 -0
- package/src/inspect/serializeValue.js +149 -0
- package/src/inspect/serializeValue.js.map +7 -0
- package/src/inspect/types.d.ts +260 -0
- package/src/inspect/types.d.ts.map +1 -0
- package/src/inspect/types.js +1 -0
- package/src/inspect/types.js.map +7 -0
- package/src/model-context/ModelContextLike.d.ts +59 -0
- package/src/model-context/ModelContextLike.d.ts.map +1 -0
- package/src/model-context/ModelContextLike.js +15 -0
- package/src/model-context/ModelContextLike.js.map +7 -0
- package/src/model-context/exposeShadowEnvsToModelContext.d.ts +48 -0
- package/src/model-context/exposeShadowEnvsToModelContext.d.ts.map +1 -0
- package/src/model-context/exposeShadowEnvsToModelContext.js +39 -0
- package/src/model-context/exposeShadowEnvsToModelContext.js.map +7 -0
- package/src/model-context/redactProps.d.ts +14 -0
- package/src/model-context/redactProps.d.ts.map +1 -0
- package/src/model-context/redactProps.js +22 -0
- package/src/model-context/redactProps.js.map +7 -0
- package/src/model-context/sharedExposure.d.ts +32 -0
- package/src/model-context/sharedExposure.d.ts.map +1 -0
- package/src/model-context/sharedExposure.js +108 -0
- package/src/model-context/sharedExposure.js.map +7 -0
- package/src/model-context/toolSupport.d.ts +72 -0
- package/src/model-context/toolSupport.d.ts.map +1 -0
- package/src/model-context/toolSupport.js +145 -0
- package/src/model-context/toolSupport.js.map +7 -0
- package/src/model-context/tools/findEntities.d.ts +15 -0
- package/src/model-context/tools/findEntities.d.ts.map +1 -0
- package/src/model-context/tools/findEntities.js +71 -0
- package/src/model-context/tools/findEntities.js.map +7 -0
- package/src/model-context/tools/getEntity.d.ts +16 -0
- package/src/model-context/tools/getEntity.d.ts.map +1 -0
- package/src/model-context/tools/getEntity.js +65 -0
- package/src/model-context/tools/getEntity.js.map +7 -0
- package/src/model-context/tools/getEntityTree.d.ts +4 -0
- package/src/model-context/tools/getEntityTree.d.ts.map +1 -0
- package/src/model-context/tools/getEntityTree.js +58 -0
- package/src/model-context/tools/getEntityTree.js.map +7 -0
- package/src/model-context/tools/getRegistry.d.ts +14 -0
- package/src/model-context/tools/getRegistry.d.ts.map +1 -0
- package/src/model-context/tools/getRegistry.js +39 -0
- package/src/model-context/tools/getRegistry.js.map +7 -0
- package/src/model-context/tools/index.d.ts +9 -0
- package/src/model-context/tools/index.d.ts.map +1 -0
- package/src/model-context/tools/index.js +16 -0
- package/src/model-context/tools/index.js.map +7 -0
- package/src/model-context/tools/listEnvs.d.ts +33 -0
- package/src/model-context/tools/listEnvs.d.ts.map +1 -0
- package/src/model-context/tools/listEnvs.js +37 -0
- package/src/model-context/tools/listEnvs.js.map +7 -0
- package/src/model-context.d.ts +13 -0
- package/src/model-context.d.ts.map +1 -0
- package/src/model-context.js +15 -0
- package/src/model-context.js.map +7 -0
- package/src/shadow-objects.d.ts +4 -0
- package/src/shadow-objects.d.ts.map +1 -1
- package/src/shadow-objects.js +6 -0
- package/src/shadow-objects.js.map +2 -2
- package/src/shadow-objects.worker.js +0 -1
- package/src/shadow-objects.worker.js.map +2 -2
- package/src/shae-ent.js.map +1 -1
- package/src/shae-prop.js +2 -2
- package/src/shae-prop.js.map +2 -2
- package/src/shae-worker.js.map +1 -1
- package/src/testing/TestEntity.d.ts +79 -0
- package/src/testing/TestEntity.d.ts.map +1 -0
- package/src/testing/TestEntity.js +135 -0
- package/src/testing/TestEntity.js.map +7 -0
- package/src/testing/createTestKernel.d.ts +11 -0
- package/src/testing/createTestKernel.d.ts.map +1 -0
- package/src/testing/createTestKernel.js +132 -0
- package/src/testing/createTestKernel.js.map +7 -0
- package/src/testing/mountShadowObject.d.ts +21 -0
- package/src/testing/mountShadowObject.d.ts.map +1 -0
- package/src/testing/mountShadowObject.js +72 -0
- package/src/testing/mountShadowObject.js.map +7 -0
- package/src/testing/recordKernelErrors.d.ts +21 -0
- package/src/testing/recordKernelErrors.d.ts.map +1 -0
- package/src/testing/recordKernelErrors.js +39 -0
- package/src/testing/recordKernelErrors.js.map +7 -0
- package/src/testing/settle.d.ts +15 -0
- package/src/testing/settle.d.ts.map +1 -0
- package/src/testing/settle.js +19 -0
- package/src/testing/settle.js.map +7 -0
- package/src/testing/types.d.ts +119 -0
- package/src/testing/types.d.ts.map +1 -0
- package/src/testing/types.js +1 -0
- package/src/testing/types.js.map +7 -0
- package/src/testing.d.ts +14 -0
- package/src/testing.d.ts.map +1 -0
- package/src/testing.js +11 -0
- package/src/testing.js.map +7 -0
- package/src/types.d.ts +185 -14
- package/src/types.d.ts.map +1 -1
- package/src/utils/ConsoleLogger.d.ts +45 -10
- package/src/utils/ConsoleLogger.d.ts.map +1 -1
- package/src/utils/ConsoleLogger.js +51 -26
- package/src/utils/ConsoleLogger.js.map +2 -2
- package/src/utils/FrameLoop.d.ts +42 -3
- package/src/utils/FrameLoop.d.ts.map +1 -1
- package/src/utils/FrameLoop.js +71 -10
- package/src/utils/FrameLoop.js.map +2 -2
- package/src/utils/MicrotaskCollector.d.ts +45 -0
- package/src/utils/MicrotaskCollector.d.ts.map +1 -0
- package/src/utils/MicrotaskCollector.js +33 -0
- package/src/utils/MicrotaskCollector.js.map +7 -0
- package/src/utils/MicrotaskGate.d.ts +17 -0
- package/src/utils/MicrotaskGate.d.ts.map +1 -0
- package/src/utils/MicrotaskGate.js +20 -0
- package/src/utils/MicrotaskGate.js.map +7 -0
- package/src/utils/array-utils.js.map +1 -1
- package/src/utils/attr-utils.d.ts +11 -0
- package/src/utils/attr-utils.d.ts.map +1 -1
- package/src/utils/attr-utils.js +5 -1
- package/src/utils/attr-utils.js.map +2 -2
- package/src/utils/constants.js.map +1 -1
- package/src/utils/generateUUID.d.ts +11 -0
- package/src/utils/generateUUID.d.ts.map +1 -1
- package/src/utils/generateUUID.js +31 -266
- package/src/utils/generateUUID.js.map +2 -2
- package/src/utils/props-utils.d.ts +3 -2
- package/src/utils/props-utils.d.ts.map +1 -1
- package/src/utils/props-utils.js +13 -24
- package/src/utils/props-utils.js.map +2 -2
- package/src/utils/runGuarded.d.ts +27 -0
- package/src/utils/runGuarded.d.ts.map +1 -0
- package/src/utils/runGuarded.js +11 -0
- package/src/utils/runGuarded.js.map +7 -0
- package/src/utils/toMaybe.d.ts.map +1 -1
- package/src/utils/toMaybe.js.map +1 -1
- package/src/utils/toNamespace.d.ts.map +1 -1
- package/src/utils/toNamespace.js.map +1 -1
- package/src/utils/toUrlString.d.ts +12 -0
- package/src/utils/toUrlString.d.ts.map +1 -1
- package/src/utils/toUrlString.js.map +2 -2
- package/src/utils/waitForMessageOfType.d.ts +12 -1
- package/src/utils/waitForMessageOfType.d.ts.map +1 -1
- package/src/utils/waitForMessageOfType.js +16 -1
- package/src/utils/waitForMessageOfType.js.map +2 -2
- package/src/view/ComponentChanges.d.ts +88 -1
- package/src/view/ComponentChanges.d.ts.map +1 -1
- package/src/view/ComponentChanges.js +243 -73
- package/src/view/ComponentChanges.js.map +2 -2
- package/src/view/ComponentContext.d.ts +206 -16
- package/src/view/ComponentContext.d.ts.map +1 -1
- package/src/view/ComponentContext.js +548 -91
- package/src/view/ComponentContext.js.map +2 -2
- package/src/view/ComponentMemory.d.ts +3 -3
- package/src/view/ComponentMemory.d.ts.map +1 -1
- package/src/view/ComponentMemory.js.map +1 -1
- package/src/view/IShadowObjectEnvProxy.d.ts +38 -3
- package/src/view/IShadowObjectEnvProxy.d.ts.map +1 -1
- package/src/view/LocalShadowObjectEnv.d.ts +8 -1
- package/src/view/LocalShadowObjectEnv.d.ts.map +1 -1
- package/src/view/LocalShadowObjectEnv.js +23 -7
- package/src/view/LocalShadowObjectEnv.js.map +2 -2
- package/src/view/RemoteWorkerEnv.d.ts +108 -5
- package/src/view/RemoteWorkerEnv.d.ts.map +1 -1
- package/src/view/RemoteWorkerEnv.js +244 -77
- package/src/view/RemoteWorkerEnv.js.map +2 -2
- package/src/view/ShadowEnv.d.ts +79 -2
- package/src/view/ShadowEnv.d.ts.map +1 -1
- package/src/view/ShadowEnv.js +323 -59
- package/src/view/ShadowEnv.js.map +3 -3
- package/src/view/ViewComponent.d.ts +78 -14
- package/src/view/ViewComponent.d.ts.map +1 -1
- package/src/view/ViewComponent.js +90 -14
- package/src/view/ViewComponent.js.map +2 -2
- package/src/view/cloneChangeTrail.js.map +2 -2
- package/src/worker/MessageRouter.d.ts +14 -0
- package/src/worker/MessageRouter.d.ts.map +1 -1
- package/src/worker/MessageRouter.js +98 -15
- package/src/worker/MessageRouter.js.map +2 -2
- package/src/worker/WorkerRuntime.d.ts +22 -1
- package/src/worker/WorkerRuntime.d.ts.map +1 -1
- package/src/worker/WorkerRuntime.js +60 -8
- package/src/worker/WorkerRuntime.js.map +2 -2
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
import { on } from "@spearwolf/eventize";
|
|
2
|
-
import { beQuiet, createEffect, createSignal } from "@spearwolf/signalize";
|
|
2
|
+
import { beQuiet, createEffect, createSignal, Effect, hibernate } from "@spearwolf/signalize";
|
|
3
3
|
import { VoidToken } from "../constants.js";
|
|
4
|
+
import { readBooleanAttribute } from "../utils/attr-utils.js";
|
|
5
|
+
import { ConsoleLogger } from "../utils/ConsoleLogger.js";
|
|
6
|
+
import { MicrotaskGate } from "../utils/MicrotaskGate.js";
|
|
4
7
|
import { ComponentContext } from "../view/ComponentContext.js";
|
|
5
|
-
import { ShadowEnv } from "../view/ShadowEnv.js";
|
|
6
8
|
import { ViewComponent } from "../view/ViewComponent.js";
|
|
7
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
ATTR_AUTO_DESTRUCT,
|
|
11
|
+
ATTR_FORWARD_CUSTOM_EVENTS,
|
|
12
|
+
ATTR_TOKEN,
|
|
13
|
+
RequestEntParentEventName,
|
|
14
|
+
ReRequestEntHostEventName,
|
|
15
|
+
ReRequestEntParentEventName
|
|
16
|
+
} from "./constants.js";
|
|
17
|
+
import { forwardCustomEventsFrom, isEmptyFilter, isSameFilter } from "./forwardCustomEvents.js";
|
|
18
|
+
import { HostedSlots } from "./hostedSlots.js";
|
|
19
|
+
import { stopWatchingForRemovalFrom, watchForRemovalFrom } from "./parentRemoval.js";
|
|
20
|
+
import { requestEntAncestor } from "./requestEntAncestor.js";
|
|
8
21
|
import { ShaeElement } from "./ShaeElement.js";
|
|
22
|
+
const flattenedParentOf = (node) => node.assignedSlot ?? node.parentNode ?? node.host ?? void 0;
|
|
23
|
+
const isBelow = (node, ancestor) => {
|
|
24
|
+
for (let current = flattenedParentOf(node); current != null; current = flattenedParentOf(current)) {
|
|
25
|
+
if (current === ancestor) return true;
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
};
|
|
29
|
+
const isInClosedShadowTree = (node) => {
|
|
30
|
+
for (let root = node.getRootNode(); root?.host != null; root = root.host.getRootNode()) {
|
|
31
|
+
if (root.mode === "closed") return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
};
|
|
9
35
|
class ShaeEntElement extends ShaeElement {
|
|
10
36
|
constructor() {
|
|
11
37
|
super();
|
|
@@ -14,8 +40,63 @@ class ShaeEntElement extends ShaeElement {
|
|
|
14
40
|
this.viewComponent$ = createSignal();
|
|
15
41
|
this.token$ = createSignal();
|
|
16
42
|
this.forwardCustomEvents$ = createSignal(false);
|
|
43
|
+
this.#logger = new ConsoleLogger("ShaeEntElement");
|
|
44
|
+
/**
|
|
45
|
+
* Whether this element already sat in a live tree while its constructor ran.
|
|
46
|
+
*
|
|
47
|
+
* An element created by the parser or by `createElement` runs its constructor before it enters
|
|
48
|
+
* the tree, so everything below it connects after it and finds it on the first request. Only an
|
|
49
|
+
* element upgraded in place can have entities below it that bound to a further ancestor while it
|
|
50
|
+
* was not yet answering. Markup written into a connected node through `innerHTML` reports the
|
|
51
|
+
* same, because the fragment parser builds the element undefined and upgrades it on insertion —
|
|
52
|
+
* that keeps the guard on the safe side, it errs towards asking too often.
|
|
53
|
+
*/
|
|
54
|
+
this.#wasUpgradedInPlace = this.isConnected;
|
|
55
|
+
/**
|
|
56
|
+
* Bumped whenever the subscriptions this element holds on its component have to be set up again.
|
|
57
|
+
*
|
|
58
|
+
* Ending a component takes every subscription off it, this element's among them, and the
|
|
59
|
+
* component itself stays where it is — so nothing about `viewComponent$` changes and the effects
|
|
60
|
+
* below have no reason of their own to run again. This signal is that reason.
|
|
61
|
+
*/
|
|
62
|
+
this.#reSubscribe$ = createSignal(0);
|
|
63
|
+
/**
|
|
64
|
+
* The wait for the next microtask is the point, not a detail: the subscriptions come off right
|
|
65
|
+
* behind the announcement, so a listener set up inside the handler would be taken off with them.
|
|
66
|
+
* And a context tearing itself down walks its own set of instances — the element stays out of
|
|
67
|
+
* that walk and comes back once it is over. However many announcements arrive in one task, one
|
|
68
|
+
* round of setting up answers them all.
|
|
69
|
+
*/
|
|
70
|
+
this.#reSubscribe = new MicrotaskGate(() => {
|
|
71
|
+
this.#reSubscribe$.set(this.#reSubscribe$.value + 1);
|
|
72
|
+
});
|
|
73
|
+
this.#applyComponentContext = (context) => {
|
|
74
|
+
const token = this.token$.value;
|
|
75
|
+
let vc = this.viewComponent$.value;
|
|
76
|
+
try {
|
|
77
|
+
if (vc) {
|
|
78
|
+
vc.context = context;
|
|
79
|
+
} else if (context) {
|
|
80
|
+
vc = new ViewComponent(token ?? VoidToken, {
|
|
81
|
+
context,
|
|
82
|
+
// read here and only here: the flag is immutable on a ViewComponent, so the moment the
|
|
83
|
+
// component is built is the only moment the attribute can still decide anything. That is
|
|
84
|
+
// also why it is not observed — a later write has nowhere to go
|
|
85
|
+
autoDestructionOnParentRemoval: readBooleanAttribute(this, ATTR_AUTO_DESTRUCT)
|
|
86
|
+
});
|
|
87
|
+
this.viewComponent$.set(vc);
|
|
88
|
+
}
|
|
89
|
+
} catch (error) {
|
|
90
|
+
this.logger.error("applying the component context of this entity failed:", { ns: this.ns, uuid: vc?.uuid }, error);
|
|
91
|
+
}
|
|
92
|
+
this.syncShadowObjects();
|
|
93
|
+
};
|
|
17
94
|
this.#shadowRootHostNeedsUpdate = true;
|
|
18
|
-
this
|
|
95
|
+
/** The `<slot>`s this element answers for. */
|
|
96
|
+
this.#hostedSlots = new HostedSlots(this);
|
|
97
|
+
this.#onSlotChange = (event) => {
|
|
98
|
+
this.#askPropertiesToReRequestHost();
|
|
99
|
+
this.#hostedSlots.takeUp(event);
|
|
19
100
|
const shadowRootHost = this.findShadowRootHost();
|
|
20
101
|
if (shadowRootHost == null) return;
|
|
21
102
|
this.dispatchEvent(
|
|
@@ -37,33 +118,121 @@ class ShaeEntElement extends ShaeElement {
|
|
|
37
118
|
}
|
|
38
119
|
};
|
|
39
120
|
this.#onRequestParent = (event) => {
|
|
40
|
-
const
|
|
41
|
-
if (requester === this) return;
|
|
42
|
-
if (
|
|
43
|
-
if (
|
|
121
|
+
const request = event.detail;
|
|
122
|
+
if (request?.requester == null || request.requester === this) return;
|
|
123
|
+
if (typeof request.answer !== "function") return;
|
|
124
|
+
if (request.ns !== void 0 && request.ns !== this.ns) return;
|
|
44
125
|
event.stopPropagation();
|
|
45
|
-
|
|
126
|
+
request.answer(this);
|
|
46
127
|
};
|
|
47
|
-
this
|
|
128
|
+
this.#updateTokenValue();
|
|
129
|
+
this.#updateForwardCustomEventsValue();
|
|
130
|
+
}
|
|
131
|
+
static {
|
|
132
|
+
this.observedAttributes = [...ShaeElement.observedAttributes, ATTR_TOKEN, ATTR_FORWARD_CUSTOM_EVENTS];
|
|
133
|
+
}
|
|
134
|
+
#logger;
|
|
135
|
+
/** The logger this element reports through: a subclass reads it and does not replace it. */
|
|
136
|
+
get logger() {
|
|
137
|
+
return this.#logger;
|
|
138
|
+
}
|
|
139
|
+
get componentContext() {
|
|
140
|
+
return this.componentContext$.value;
|
|
141
|
+
}
|
|
142
|
+
get viewComponent() {
|
|
143
|
+
return this.viewComponent$.value;
|
|
144
|
+
}
|
|
145
|
+
get uuid() {
|
|
146
|
+
return this.viewComponent?.uuid;
|
|
147
|
+
}
|
|
148
|
+
get token() {
|
|
149
|
+
return this.token$.value;
|
|
150
|
+
}
|
|
151
|
+
set token(token) {
|
|
152
|
+
this.token$.set(token);
|
|
153
|
+
}
|
|
154
|
+
#wasUpgradedInPlace;
|
|
155
|
+
/** The node this element is watched on, for as long as it hangs in one. */
|
|
156
|
+
#observedParentNode;
|
|
157
|
+
#reSubscribe$;
|
|
158
|
+
#reSubscribe;
|
|
159
|
+
/** Moves this element's entity into the context of whichever namespace it names. */
|
|
160
|
+
#namespaceBinding;
|
|
161
|
+
/** Writes the token back onto the attribute. */
|
|
162
|
+
#tokenReflection;
|
|
163
|
+
/** Writes the event filter back onto the attribute. */
|
|
164
|
+
#forwardCustomEventsReflection;
|
|
165
|
+
/** The eventize subscriptions this element holds on its component. */
|
|
166
|
+
#viewComponentListeners;
|
|
167
|
+
/** The patch that carries the component's own events out as DOM events. */
|
|
168
|
+
#forwardCustomEventsPatch;
|
|
169
|
+
/** Carries a new token through to the entity. */
|
|
170
|
+
#tokenToViewComponent;
|
|
171
|
+
/**
|
|
172
|
+
* Take up everything this element listens to.
|
|
173
|
+
*
|
|
174
|
+
* Called from {@link ShaeEntElement.restore} and from nowhere else — that is the one place where
|
|
175
|
+
* this element's subscriptions begin, at the first connect as well as after a teardown.
|
|
176
|
+
*/
|
|
177
|
+
#subscribe() {
|
|
178
|
+
this.#namespaceBinding = this.ns$.onChange((ns) => {
|
|
179
|
+
const previousContext = this.componentContext;
|
|
180
|
+
const previousNs = previousContext?.ns;
|
|
181
|
+
const previousViewComponent = this.viewComponent;
|
|
182
|
+
if (previousContext != null && previousViewComponent != null) {
|
|
183
|
+
previousContext.dispatchReRequestParentChildren(previousViewComponent);
|
|
184
|
+
}
|
|
185
|
+
this.#setParent(void 0);
|
|
48
186
|
this.componentContext$.set(ComponentContext.get(ns));
|
|
49
187
|
if (this.isConnected) {
|
|
50
188
|
this.#dispatchRequestParent();
|
|
189
|
+
this.#askPeersToReRequestParent();
|
|
190
|
+
}
|
|
191
|
+
if (previousNs != null && previousNs !== ns) {
|
|
192
|
+
this.syncShadowObjectsOf(previousNs);
|
|
51
193
|
}
|
|
52
194
|
});
|
|
53
|
-
this.#
|
|
54
|
-
this.
|
|
195
|
+
this.#tokenReflection = this.token$.onChange((token) => this.#reflectToken(token));
|
|
196
|
+
this.#forwardCustomEventsReflection = this.forwardCustomEvents$.onChange((val) => this.#reflectForwardCustomEvents(val));
|
|
197
|
+
this.#viewComponentListeners = createEffect(() => {
|
|
198
|
+
const vc = this.viewComponent$.get();
|
|
199
|
+
this.#reSubscribe$.get();
|
|
200
|
+
if (!vc) return;
|
|
201
|
+
const unsubscribe = [
|
|
202
|
+
on(vc, ComponentContext.ReRequestParentRoots, () => this.#reRequestParentAsRoot()),
|
|
203
|
+
on(vc, ComponentContext.ReRequestParent, (data) => this.#reRequestParent(data?.newAncestor)),
|
|
204
|
+
on(vc, ComponentContext.ReRequestEntHost, () => this.#askPropertiesToReRequestHost()),
|
|
205
|
+
// this one hangs on the same component as the three above and comes off with them, which
|
|
206
|
+
// is exactly why it belongs in the same effect: it is what puts all four back
|
|
207
|
+
on(vc, ViewComponent.Destroyed, () => this.#reSubscribe.schedule())
|
|
208
|
+
];
|
|
209
|
+
return () => {
|
|
210
|
+
for (const un of unsubscribe) un();
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
this.#forwardCustomEventsPatch = createEffect(() => {
|
|
214
|
+
const vc = this.viewComponent$.get();
|
|
215
|
+
this.#reSubscribe$.get();
|
|
216
|
+
if (!vc) return;
|
|
217
|
+
return forwardCustomEventsFrom(vc, this, this.forwardCustomEvents$.get());
|
|
218
|
+
});
|
|
219
|
+
this.#tokenToViewComponent = this.token$.onChange((token) => this.#writeTokenToViewComponent(token));
|
|
220
|
+
}
|
|
221
|
+
#reflectToken(token) {
|
|
222
|
+
this.reflectAttribute(ATTR_TOKEN, () => {
|
|
55
223
|
if (token == null) {
|
|
56
224
|
this.removeAttribute(ATTR_TOKEN);
|
|
57
225
|
} else if (this.getAttribute(ATTR_TOKEN) !== token) {
|
|
58
226
|
this.setAttribute(ATTR_TOKEN, token);
|
|
59
227
|
}
|
|
60
228
|
});
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
229
|
+
}
|
|
230
|
+
#reflectForwardCustomEvents(val) {
|
|
231
|
+
this.reflectAttribute(ATTR_FORWARD_CUSTOM_EVENTS, () => {
|
|
232
|
+
if (!val || isEmptyFilter(val)) {
|
|
64
233
|
this.removeAttribute(ATTR_FORWARD_CUSTOM_EVENTS);
|
|
65
234
|
} else if (val === true) {
|
|
66
|
-
if (
|
|
235
|
+
if (this.getAttribute(ATTR_FORWARD_CUSTOM_EVENTS) !== "") {
|
|
67
236
|
this.setAttribute(ATTR_FORWARD_CUSTOM_EVENTS, "");
|
|
68
237
|
}
|
|
69
238
|
} else {
|
|
@@ -73,94 +242,42 @@ class ShaeEntElement extends ShaeElement {
|
|
|
73
242
|
}
|
|
74
243
|
}
|
|
75
244
|
});
|
|
76
|
-
createEffect(() => {
|
|
77
|
-
const vc = this.viewComponent$.get();
|
|
78
|
-
if (vc) {
|
|
79
|
-
const unsubcribe = on(vc, ComponentContext.ReRequestParentRoots, () => this.#reReuestParentRoot());
|
|
80
|
-
const oldNs = vc.context?.ns;
|
|
81
|
-
return () => {
|
|
82
|
-
unsubcribe();
|
|
83
|
-
vc.destroy();
|
|
84
|
-
if (oldNs && oldNs !== this.ns) {
|
|
85
|
-
ShadowEnv.get(oldNs)?.sync();
|
|
86
|
-
} else {
|
|
87
|
-
this.syncShadowObjects();
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
createEffect(() => {
|
|
93
|
-
const vc = this.viewComponent$.get();
|
|
94
|
-
if (!vc) return;
|
|
95
|
-
const originalDispatchEvent = Object.hasOwn(vc, "dispatchEvent") ? Object.getPrototypeOf(vc).dispatchEvent : vc.dispatchEvent;
|
|
96
|
-
const filter = this.forwardCustomEvents$.get();
|
|
97
|
-
if (!filter) return;
|
|
98
|
-
const allowedTypes = filter instanceof Set ? filter : void 0;
|
|
99
|
-
const newDispatch = (type, data, traverseChildren) => {
|
|
100
|
-
originalDispatchEvent.call(vc, type, data, traverseChildren);
|
|
101
|
-
if (type === ComponentContext.ReRequestParentRoots) return;
|
|
102
|
-
if (allowedTypes && !allowedTypes.has(type)) return;
|
|
103
|
-
this.dispatchEvent(
|
|
104
|
-
new CustomEvent(type, {
|
|
105
|
-
bubbles: true,
|
|
106
|
-
composed: true,
|
|
107
|
-
detail: data
|
|
108
|
-
})
|
|
109
|
-
);
|
|
110
|
-
};
|
|
111
|
-
Object.defineProperty(vc, "dispatchEvent", {
|
|
112
|
-
value: newDispatch,
|
|
113
|
-
writable: true,
|
|
114
|
-
configurable: true
|
|
115
|
-
});
|
|
116
|
-
return () => {
|
|
117
|
-
if (Object.hasOwn(vc, "dispatchEvent") && vc.dispatchEvent === newDispatch) {
|
|
118
|
-
delete vc.dispatchEvent;
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
});
|
|
122
|
-
this.token$.onChange((token) => {
|
|
123
|
-
const vc = this.viewComponent$.value;
|
|
124
|
-
if (vc) {
|
|
125
|
-
vc.token = token;
|
|
126
|
-
this.syncShadowObjects();
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
this.style.display = "contents";
|
|
130
245
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
get viewComponent() {
|
|
138
|
-
return this.viewComponent$.value;
|
|
139
|
-
}
|
|
140
|
-
get uuid() {
|
|
141
|
-
return this.viewComponent?.uuid;
|
|
142
|
-
}
|
|
143
|
-
get token() {
|
|
144
|
-
return this.token$.value;
|
|
246
|
+
#writeTokenToViewComponent(token) {
|
|
247
|
+
const vc = this.viewComponent$.value;
|
|
248
|
+
if (vc == null || vc.token === token) return;
|
|
249
|
+
vc.token = token;
|
|
250
|
+
this.syncShadowObjects();
|
|
145
251
|
}
|
|
146
|
-
|
|
147
|
-
|
|
252
|
+
/**
|
|
253
|
+
* Take the subscriptions up and settle what drifted while none of them were listening.
|
|
254
|
+
*
|
|
255
|
+
* Runs on the first connect, where this element has never listened to anything, and again for one
|
|
256
|
+
* that comes back after a teardown.
|
|
257
|
+
*
|
|
258
|
+
* The three catch-up calls are not decoration. A released element still takes writes, so `token`,
|
|
259
|
+
* `ns` and `forward-custom-events` can each stand on a value its attribute never heard about —
|
|
260
|
+
* and `connectedCallback` reads exactly those attributes back into exactly those signals a few
|
|
261
|
+
* lines further on. Without the catch-up that read would overwrite the newer value with the older
|
|
262
|
+
* one, and the entity would go on carrying a token nobody wrote.
|
|
263
|
+
*
|
|
264
|
+
* `super.restore()` runs first and takes `ns` with it. That it writes the `ns` attribute before
|
|
265
|
+
* this class has re-subscribed to `ns$` costs nothing: the write puts the value the signal
|
|
266
|
+
* already holds onto the attribute, so the read it triggers changes no signal and reaches no
|
|
267
|
+
* handler.
|
|
268
|
+
*/
|
|
269
|
+
restore() {
|
|
270
|
+
super.restore();
|
|
271
|
+
this.#subscribe();
|
|
272
|
+
this.#reflectToken(this.token$.value);
|
|
273
|
+
this.#reflectForwardCustomEvents(this.forwardCustomEvents$.value);
|
|
274
|
+
this.#writeTokenToViewComponent(this.token$.value);
|
|
148
275
|
}
|
|
149
|
-
#parentObserver;
|
|
150
276
|
#unsubscribeViewComponentEffect;
|
|
277
|
+
#applyComponentContext;
|
|
151
278
|
#setupViewComponentEffect() {
|
|
152
279
|
this.#unsubscribeViewComponentEffect?.();
|
|
153
|
-
const unsubscribeComponentContext = this.componentContext$.onChange(
|
|
154
|
-
const token = this.token$.value;
|
|
155
|
-
let vc = this.viewComponent$.value;
|
|
156
|
-
if (vc) {
|
|
157
|
-
vc.context = context;
|
|
158
|
-
} else if (context) {
|
|
159
|
-
vc = new ViewComponent(token ?? VoidToken, { context });
|
|
160
|
-
this.viewComponent$.set(vc);
|
|
161
|
-
}
|
|
162
|
-
this.syncShadowObjects();
|
|
163
|
-
});
|
|
280
|
+
const unsubscribeComponentContext = this.componentContext$.onChange(this.#applyComponentContext);
|
|
164
281
|
this.#unsubscribeViewComponentEffect = () => {
|
|
165
282
|
unsubscribeComponentContext();
|
|
166
283
|
};
|
|
@@ -169,70 +286,89 @@ class ShaeEntElement extends ShaeElement {
|
|
|
169
286
|
this.#unsubscribeViewComponentEffect?.();
|
|
170
287
|
this.#unsubscribeViewComponentEffect = void 0;
|
|
171
288
|
}
|
|
289
|
+
// `getRootNode()` answers for every state of the tree — the shadow root for a node inside
|
|
290
|
+
// one, and the topmost node of its own chain for a node outside every tree. This reads `.host`
|
|
291
|
+
// off the result without checking for a `ShadowRoot` first: only a shadow root has one, so a
|
|
292
|
+
// node outside every tree reads no host, the same answer a check would have given
|
|
293
|
+
#readShadowRootHost() {
|
|
294
|
+
return this.getRootNode()?.host;
|
|
295
|
+
}
|
|
172
296
|
#shadowRootHost;
|
|
173
297
|
#shadowRootHostNeedsUpdate;
|
|
174
298
|
findShadowRootHost() {
|
|
175
299
|
if (this.#shadowRootHostNeedsUpdate) {
|
|
176
300
|
this.#shadowRootHostNeedsUpdate = false;
|
|
177
|
-
|
|
178
|
-
while (current) {
|
|
179
|
-
if (current.parentElement == null) {
|
|
180
|
-
const root = current.parentNode;
|
|
181
|
-
if (root) {
|
|
182
|
-
this.#shadowRootHost = root.host;
|
|
183
|
-
}
|
|
184
|
-
break;
|
|
185
|
-
}
|
|
186
|
-
current = current.parentElement;
|
|
187
|
-
}
|
|
301
|
+
this.#shadowRootHost = this.#readShadowRootHost();
|
|
188
302
|
}
|
|
189
303
|
return this.#shadowRootHost;
|
|
190
304
|
}
|
|
191
305
|
getParentNodeForObserver() {
|
|
192
|
-
return this.parentNode ?? this
|
|
306
|
+
return this.parentNode ?? this.#readShadowRootHost() ?? void 0;
|
|
193
307
|
}
|
|
194
308
|
connectedCallback() {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
this
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
309
|
+
hibernate(() => {
|
|
310
|
+
super.connectedCallback();
|
|
311
|
+
this.#shadowRootHostNeedsUpdate = true;
|
|
312
|
+
this.addEventListener("slotchange", this.#onSlotChange, { capture: false, passive: false });
|
|
313
|
+
this.addEventListener(RequestEntParentEventName, this.#onRequestParent, { capture: false, passive: false });
|
|
314
|
+
this.#setupViewComponentEffect();
|
|
315
|
+
beQuiet(() => this.#updateTokenValue());
|
|
316
|
+
const forwardCustomEvents = this.#readForwardCustomEventsAttribute();
|
|
317
|
+
if (!isSameFilter(forwardCustomEvents, this.forwardCustomEvents$.value)) {
|
|
318
|
+
this.forwardCustomEvents$.set(forwardCustomEvents);
|
|
319
|
+
}
|
|
320
|
+
if (this.componentContext == null) {
|
|
321
|
+
this.componentContext$.set(ComponentContext.get(this.ns));
|
|
322
|
+
} else {
|
|
323
|
+
this.#applyComponentContext(this.componentContext);
|
|
324
|
+
}
|
|
325
|
+
this.#dispatchRequestParent();
|
|
326
|
+
if (this.#wasUpgradedInPlace) {
|
|
327
|
+
this.#askPeersToReRequestParent();
|
|
328
|
+
this.#askPropertiesToReRequestHost();
|
|
329
|
+
}
|
|
330
|
+
this.#hostedSlots.collect();
|
|
331
|
+
this.#observeParentNode();
|
|
332
|
+
this.syncShadowObjects();
|
|
333
|
+
});
|
|
208
334
|
}
|
|
209
|
-
#
|
|
210
|
-
this.#
|
|
335
|
+
#observeParentNode() {
|
|
336
|
+
this.#unobserveParentNode();
|
|
211
337
|
const parent = this.getParentNodeForObserver();
|
|
212
338
|
if (parent) {
|
|
213
|
-
this
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
this.onParentChanged(this.getParentNodeForObserver(), parent);
|
|
220
|
-
break;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
339
|
+
watchForRemovalFrom(parent, this, () => {
|
|
340
|
+
this.#observedParentNode = void 0;
|
|
341
|
+
const newParent = this.getParentNodeForObserver();
|
|
342
|
+
if (newParent === parent) {
|
|
343
|
+
this.#observeParentNode();
|
|
344
|
+
return;
|
|
224
345
|
}
|
|
346
|
+
this.onParentChanged(newParent, parent);
|
|
225
347
|
});
|
|
226
|
-
this
|
|
348
|
+
const parentAfterRegistration = this.getParentNodeForObserver();
|
|
349
|
+
if (parentAfterRegistration === parent) {
|
|
350
|
+
this.#observedParentNode = parent;
|
|
351
|
+
} else {
|
|
352
|
+
stopWatchingForRemovalFrom(parent, this);
|
|
353
|
+
if (this.#observedParentNode == null && this.isConnected) {
|
|
354
|
+
try {
|
|
355
|
+
this.onParentChanged(parentAfterRegistration, parent);
|
|
356
|
+
} catch (error) {
|
|
357
|
+
console.error("a removal watcher failed:", error);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
227
361
|
}
|
|
228
362
|
}
|
|
363
|
+
#unobserveParentNode() {
|
|
364
|
+
if (this.#observedParentNode == null) return;
|
|
365
|
+
stopWatchingForRemovalFrom(this.#observedParentNode, this);
|
|
366
|
+
this.#observedParentNode = void 0;
|
|
367
|
+
}
|
|
229
368
|
onParentChanged(_newParent, _oldParent) {
|
|
230
369
|
this.#setParent(void 0);
|
|
231
370
|
this.#dispatchRequestParent();
|
|
232
|
-
|
|
233
|
-
#destroyParentObserver() {
|
|
234
|
-
this.#parentObserver?.disconnect();
|
|
235
|
-
this.#parentObserver = void 0;
|
|
371
|
+
this.#observeParentNode();
|
|
236
372
|
}
|
|
237
373
|
attributeChangedCallback(name) {
|
|
238
374
|
super.attributeChangedCallback(name);
|
|
@@ -244,28 +380,118 @@ class ShaeEntElement extends ShaeElement {
|
|
|
244
380
|
}
|
|
245
381
|
disconnectedCallback() {
|
|
246
382
|
this.#shadowRootHostNeedsUpdate = true;
|
|
247
|
-
this.#
|
|
248
|
-
this.
|
|
249
|
-
this
|
|
383
|
+
this.#unobserveParentNode();
|
|
384
|
+
this.#hostedSlots.releaseAll();
|
|
385
|
+
this.#stopAnswering();
|
|
386
|
+
this.#askPropertiesToReRequestHost();
|
|
250
387
|
this.#setParent(void 0);
|
|
388
|
+
const vc = this.viewComponent;
|
|
389
|
+
if (vc != null) {
|
|
390
|
+
this.componentContext?.dispatchReRequestParentChildren(vc);
|
|
391
|
+
}
|
|
251
392
|
this.componentContext$.set(void 0);
|
|
252
393
|
this.syncShadowObjects();
|
|
253
394
|
this.#destroyViewComponentEffect();
|
|
395
|
+
super.disconnectedCallback();
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Let go of everything this element listens to, and leave what it is made of standing.
|
|
399
|
+
*
|
|
400
|
+
* The signals are not destroyed — they are what carries this element's state across a teardown,
|
|
401
|
+
* and {@link ShaeEntElement.restore} subscribes to them again on the way back in.
|
|
402
|
+
*
|
|
403
|
+
* The `ViewComponent` stays too. `disconnectedCallback` has already taken it out of its
|
|
404
|
+
* `ComponentContext`, which is what unbinds it from this element's position; ending it on top of
|
|
405
|
+
* that would write a destroy entry into the change trail and take the entity down in the worker
|
|
406
|
+
* — a decision about the entity, not about this element. It is also what lets an element that
|
|
407
|
+
* comes back carry the same entity and the same uuid it left with.
|
|
408
|
+
*
|
|
409
|
+
* Both effects run their cleanup on `destroy()`, which is what this depends on: the one on the
|
|
410
|
+
* `dispatchEvent` patch has to, or the patched function would go on holding the component and
|
|
411
|
+
* everything reachable from it.
|
|
412
|
+
*/
|
|
413
|
+
teardown() {
|
|
414
|
+
this.#namespaceBinding?.();
|
|
415
|
+
this.#namespaceBinding = void 0;
|
|
416
|
+
this.#tokenReflection?.();
|
|
417
|
+
this.#tokenReflection = void 0;
|
|
418
|
+
this.#forwardCustomEventsReflection?.();
|
|
419
|
+
this.#forwardCustomEventsReflection = void 0;
|
|
420
|
+
this.#viewComponentListeners?.destroy();
|
|
421
|
+
this.#viewComponentListeners = void 0;
|
|
422
|
+
this.#forwardCustomEventsPatch?.destroy();
|
|
423
|
+
this.#forwardCustomEventsPatch = void 0;
|
|
424
|
+
this.#tokenToViewComponent?.();
|
|
425
|
+
this.#tokenToViewComponent = void 0;
|
|
426
|
+
this.#destroyViewComponentEffect();
|
|
427
|
+
this.#unobserveParentNode();
|
|
428
|
+
this.#hostedSlots.releaseAll();
|
|
429
|
+
this.#setParent(void 0);
|
|
430
|
+
if (this.isConnected) {
|
|
431
|
+
this.#stopAnswering();
|
|
432
|
+
this.#askPropertiesToReRequestHost();
|
|
433
|
+
const vc = this.viewComponent;
|
|
434
|
+
if (vc != null) {
|
|
435
|
+
this.componentContext?.dispatchReRequestParentChildren(vc);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
super.teardown();
|
|
439
|
+
}
|
|
440
|
+
/** Stop answering the parent and host requests that travel past this element. */
|
|
441
|
+
#stopAnswering() {
|
|
442
|
+
this.removeEventListener("slotchange", this.#onSlotChange, { capture: false });
|
|
443
|
+
this.removeEventListener(RequestEntParentEventName, this.#onRequestParent, { capture: false });
|
|
254
444
|
}
|
|
255
|
-
#
|
|
445
|
+
#reRequestParentAsRoot() {
|
|
256
446
|
if (this.isConnected) {
|
|
257
447
|
this.#setParent(void 0);
|
|
258
448
|
this.#dispatchRequestParent();
|
|
259
449
|
}
|
|
260
450
|
}
|
|
451
|
+
// nothing is cleared here: an element already bound to its closest ancestor gets the same answer
|
|
452
|
+
// back and #setParent turns around without touching anything, so the round leaves every
|
|
453
|
+
// correctly bound sibling standing in its parent's children. Only an element that gets no answer
|
|
454
|
+
// at all loses its binding, and that one sits below nothing any more
|
|
455
|
+
#reRequestParent(newAncestor) {
|
|
456
|
+
if (!this.isConnected) return;
|
|
457
|
+
if (newAncestor?.isConnected && !isBelow(this, newAncestor)) return;
|
|
458
|
+
this.#dispatchRequestParent();
|
|
459
|
+
}
|
|
460
|
+
// An element that becomes an entity while the tree around it already stands can be the new
|
|
461
|
+
// closest ancestor for entities that bound while it was not yet answering. Those entities are
|
|
462
|
+
// its peers in the component context — the children of its own parent, or the roots while it
|
|
463
|
+
// has none — and they are asked to request their parent once more.
|
|
464
|
+
//
|
|
465
|
+
// The context collects the request and runs one round a microtask later, for everything that
|
|
466
|
+
// arrives in the same task. Which peers that round reaches is decided when it runs, from this
|
|
467
|
+
// element's parent as it stands then — an element with no entity of its own is in no tree and
|
|
468
|
+
// is nobody's ancestor, so there is nothing to ask for.
|
|
469
|
+
//
|
|
470
|
+
// Both ways of having no entity end here, and neither costs a peer its parent. This element
|
|
471
|
+
// reaches the early return only when the join into the context threw before a component was
|
|
472
|
+
// ever built; the reachable half of that case is a component that was built and refused, which
|
|
473
|
+
// the round drops on its own because a refused component is no member of the context. What such
|
|
474
|
+
// an element could have handed a peer is an `entParentNode` pointing at an element that stands
|
|
475
|
+
// in no entity tree — the peer's `viewComponent.parent` would stay empty and the peer would
|
|
476
|
+
// stay a root either way. The next namespace change books the round again.
|
|
477
|
+
#askPeersToReRequestParent() {
|
|
478
|
+
const vc = this.viewComponent;
|
|
479
|
+
if (vc == null) return;
|
|
480
|
+
const newAncestor = isInClosedShadowTree(this) ? void 0 : this;
|
|
481
|
+
this.componentContext?.collectPeerReRequest(vc, { newAncestor });
|
|
482
|
+
}
|
|
483
|
+
// Properties below this element bind to the closest entity above them. This element becoming
|
|
484
|
+
// one — or ceasing to be one — changes that answer, and no component context can carry the
|
|
485
|
+
// message: a <shae-prop> has no view component to receive it. The event travels the same
|
|
486
|
+
// ascent every request travels, so it passes exactly the entities a property could be bound
|
|
487
|
+
// to right now, and nothing else.
|
|
488
|
+
#askPropertiesToReRequestHost() {
|
|
489
|
+
this.dispatchEvent(new CustomEvent(ReRequestEntHostEventName, { bubbles: true, composed: true, detail: { requester: this } }));
|
|
490
|
+
}
|
|
261
491
|
#dispatchRequestParent() {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
composed: true,
|
|
266
|
-
detail: { requester: this }
|
|
267
|
-
})
|
|
268
|
-
);
|
|
492
|
+
let entParent;
|
|
493
|
+
requestEntAncestor(this, { ns: this.ns, answer: (entNode) => entParent = entNode });
|
|
494
|
+
this.#setParent(entParent);
|
|
269
495
|
}
|
|
270
496
|
#unsubscribeFromParent;
|
|
271
497
|
#setParent(parent) {
|
|
@@ -287,7 +513,8 @@ class ShaeEntElement extends ShaeElement {
|
|
|
287
513
|
const vc = this.viewComponent$.get();
|
|
288
514
|
if (vc) {
|
|
289
515
|
const parentVC = parent.viewComponent$.get();
|
|
290
|
-
|
|
516
|
+
const ctx = vc.context;
|
|
517
|
+
vc.parent = ctx != null && parentVC?.context === ctx ? parentVC : void 0;
|
|
291
518
|
if (vc.parent == null) {
|
|
292
519
|
queueMicrotask(() => {
|
|
293
520
|
this.#dispatchRequestParent();
|
|
@@ -305,6 +532,7 @@ class ShaeEntElement extends ShaeElement {
|
|
|
305
532
|
}
|
|
306
533
|
}
|
|
307
534
|
}
|
|
535
|
+
#hostedSlots;
|
|
308
536
|
#onSlotChange;
|
|
309
537
|
#onReRequestParent;
|
|
310
538
|
#onRequestParent;
|
|
@@ -312,23 +540,25 @@ class ShaeEntElement extends ShaeElement {
|
|
|
312
540
|
if (this.hasAttribute(ATTR_TOKEN)) {
|
|
313
541
|
const token = this.getAttribute(ATTR_TOKEN)?.trim() || void 0;
|
|
314
542
|
this.token$.set(token);
|
|
543
|
+
} else {
|
|
544
|
+
this.token$.set(void 0);
|
|
315
545
|
}
|
|
316
546
|
}
|
|
547
|
+
/** The filter the `forward-custom-events` attribute currently spells out. */
|
|
548
|
+
#readForwardCustomEventsAttribute() {
|
|
549
|
+
if (!this.hasAttribute(ATTR_FORWARD_CUSTOM_EVENTS)) return false;
|
|
550
|
+
const val = this.getAttribute(ATTR_FORWARD_CUSTOM_EVENTS);
|
|
551
|
+
if (!val || val.trim().length === 0) return true;
|
|
552
|
+
const types = new Set(
|
|
553
|
+
val.split(",").map((s) => s.trim()).filter((s) => s.length > 0)
|
|
554
|
+
);
|
|
555
|
+
return types.size > 0 ? types : false;
|
|
556
|
+
}
|
|
557
|
+
// the write is unconditional on purpose: a list read from the attribute is a fresh Set every
|
|
558
|
+
// time, and it is that change of identity which drives the normalization of the attribute back
|
|
559
|
+
// to its canonical spelling
|
|
317
560
|
#updateForwardCustomEventsValue() {
|
|
318
|
-
|
|
319
|
-
const val = this.getAttribute(ATTR_FORWARD_CUSTOM_EVENTS);
|
|
320
|
-
if (!val || val.trim().length === 0) {
|
|
321
|
-
this.forwardCustomEvents$.set(true);
|
|
322
|
-
} else {
|
|
323
|
-
this.forwardCustomEvents$.set(
|
|
324
|
-
new Set(
|
|
325
|
-
val.split(",").map((s) => s.trim()).filter((s) => s.length > 0)
|
|
326
|
-
)
|
|
327
|
-
);
|
|
328
|
-
}
|
|
329
|
-
} else {
|
|
330
|
-
this.forwardCustomEvents$.set(false);
|
|
331
|
-
}
|
|
561
|
+
this.forwardCustomEvents$.set(this.#readForwardCustomEventsAttribute());
|
|
332
562
|
}
|
|
333
563
|
}
|
|
334
564
|
export {
|