@spearwolf/shadow-objects 0.33.0 → 0.34.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 +320 -61
- package/README.md +57 -13
- package/bundle.js +5 -54
- package/package.json +7 -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 +25 -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 +9 -2
- package/src/constants.d.ts.map +1 -1
- package/src/constants.js +1 -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 +52 -2
- package/src/elements/ShaePropElement.d.ts.map +1 -1
- package/src/elements/ShaePropElement.js +240 -219
- package/src/elements/ShaePropElement.js.map +2 -2
- package/src/elements/ShaeWorkerElement.d.ts +49 -5
- package/src/elements/ShaeWorkerElement.d.ts.map +1 -1
- package/src/elements/ShaeWorkerElement.js +204 -73
- package/src/elements/ShaeWorkerElement.js.map +2 -2
- package/src/elements/constants.d.ts +6 -0
- package/src/elements/constants.d.ts.map +1 -1
- package/src/elements/constants.js +12 -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 +113 -3
- package/src/in-the-dark/Entity.d.ts.map +1 -1
- package/src/in-the-dark/Entity.js +292 -63
- package/src/in-the-dark/Entity.js.map +3 -3
- package/src/in-the-dark/Kernel.d.ts +76 -17
- package/src/in-the-dark/Kernel.d.ts.map +1 -1
- package/src/in-the-dark/Kernel.js +410 -378
- package/src/in-the-dark/Kernel.js.map +3 -3
- package/src/in-the-dark/Registry.d.ts.map +1 -1
- package/src/in-the-dark/Registry.js +83 -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 +126 -0
- package/src/in-the-dark/ShadowObjectCreationScope.d.ts.map +1 -0
- package/src/in-the-dark/ShadowObjectCreationScope.js +596 -0
- package/src/in-the-dark/ShadowObjectCreationScope.js.map +7 -0
- package/src/in-the-dark/SignalsPath.d.ts.map +1 -1
- package/src/in-the-dark/SignalsPath.js +2 -2
- package/src/in-the-dark/SignalsPath.js.map +2 -2
- 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 +7 -1
- package/src/index.d.ts.map +1 -1
- package/src/index.js +5 -1
- package/src/index.js.map +2 -2
- package/src/shadow-objects.d.ts +2 -0
- package/src/shadow-objects.d.ts.map +1 -1
- package/src/shadow-objects.js +2 -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/types.d.ts +35 -4
- 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 +6 -0
- package/src/utils/attr-utils.d.ts.map +1 -1
- package/src/utils/attr-utils.js +3 -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 +29 -3
- package/src/view/IShadowObjectEnvProxy.d.ts.map +1 -1
- package/src/view/LocalShadowObjectEnv.d.ts +1 -1
- package/src/view/LocalShadowObjectEnv.d.ts.map +1 -1
- package/src/view/LocalShadowObjectEnv.js +8 -6
- package/src/view/LocalShadowObjectEnv.js.map +2 -2
- package/src/view/RemoteWorkerEnv.d.ts +89 -5
- package/src/view/RemoteWorkerEnv.d.ts.map +1 -1
- package/src/view/RemoteWorkerEnv.js +195 -77
- package/src/view/RemoteWorkerEnv.js.map +2 -2
- package/src/view/ShadowEnv.d.ts +49 -2
- package/src/view/ShadowEnv.d.ts.map +1 -1
- package/src/view/ShadowEnv.js +223 -60
- package/src/view/ShadowEnv.js.map +2 -2
- 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 +75 -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,63 +1,35 @@
|
|
|
1
|
-
import { batch, createEffect, createSignal, link } from "@spearwolf/signalize";
|
|
1
|
+
import { batch, createEffect, createSignal, Effect, hibernate, link } from "@spearwolf/signalize";
|
|
2
2
|
import { readBooleanAttribute } from "../utils/attr-utils.js";
|
|
3
3
|
import { ConsoleLogger } from "../utils/ConsoleLogger.js";
|
|
4
|
-
import {
|
|
5
|
-
import { ATTR_NAME, ATTR_NO_TRIM, ATTR_TYPE, ATTR_VALUE } from "./constants.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
4
|
+
import { MicrotaskGate } from "../utils/MicrotaskGate.js";
|
|
5
|
+
import { ATTR_NAME, ATTR_NO_TRIM, ATTR_TYPE, ATTR_VALUE, ReRequestEntHostEventName } from "./constants.js";
|
|
6
|
+
import { propValueConverters } from "./propValueConverters.js";
|
|
7
|
+
import { requestEntAncestor } from "./requestEntAncestor.js";
|
|
8
|
+
import { ShaeLifecycleElement } from "./ShaeLifecycleElement.js";
|
|
9
|
+
const declarantsPerComponent = /* @__PURE__ */ new WeakMap();
|
|
10
|
+
const addDeclarant = (vc, name) => {
|
|
11
|
+
let names = declarantsPerComponent.get(vc);
|
|
12
|
+
if (names == null) {
|
|
13
|
+
names = /* @__PURE__ */ new Map();
|
|
14
|
+
declarantsPerComponent.set(vc, names);
|
|
15
|
+
}
|
|
16
|
+
names.set(name, (names.get(name) ?? 0) + 1);
|
|
17
|
+
};
|
|
18
|
+
const removeDeclarant = (vc, name) => {
|
|
19
|
+
const names = declarantsPerComponent.get(vc);
|
|
20
|
+
if (names == null) return true;
|
|
21
|
+
const remaining = (names.get(name) ?? 1) - 1;
|
|
22
|
+
if (remaining > 0) {
|
|
23
|
+
names.set(name, remaining);
|
|
24
|
+
return false;
|
|
13
25
|
}
|
|
14
|
-
|
|
26
|
+
names.delete(name);
|
|
27
|
+
if (names.size === 0) {
|
|
28
|
+
declarantsPerComponent.delete(vc);
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
15
31
|
};
|
|
16
|
-
|
|
17
|
-
"string",
|
|
18
|
-
"text",
|
|
19
|
-
"number",
|
|
20
|
-
"bigint",
|
|
21
|
-
"float",
|
|
22
|
-
"int",
|
|
23
|
-
"integer",
|
|
24
|
-
"hex",
|
|
25
|
-
"hexadecimal",
|
|
26
|
-
"oct",
|
|
27
|
-
"octal",
|
|
28
|
-
"bin",
|
|
29
|
-
"binary",
|
|
30
|
-
"bool",
|
|
31
|
-
"boolean",
|
|
32
|
-
"[]",
|
|
33
|
-
"text[]",
|
|
34
|
-
"string[]",
|
|
35
|
-
"number[]",
|
|
36
|
-
"float[]",
|
|
37
|
-
"int[]",
|
|
38
|
-
"integer[]",
|
|
39
|
-
"hex[]",
|
|
40
|
-
"hexadecimal[]",
|
|
41
|
-
"oct[]",
|
|
42
|
-
"octal[]",
|
|
43
|
-
"bin[]",
|
|
44
|
-
"binary[]",
|
|
45
|
-
"bool[]",
|
|
46
|
-
"boolean[]",
|
|
47
|
-
"int8array",
|
|
48
|
-
"uint8array",
|
|
49
|
-
"uint8clampedarray",
|
|
50
|
-
"int16array",
|
|
51
|
-
"uint16array",
|
|
52
|
-
"int32array",
|
|
53
|
-
"uint32array",
|
|
54
|
-
"float32array",
|
|
55
|
-
"float64array",
|
|
56
|
-
"bigint64array",
|
|
57
|
-
"biguint64array",
|
|
58
|
-
"json"
|
|
59
|
-
]);
|
|
60
|
-
class ShaePropElement extends HTMLElement {
|
|
32
|
+
class ShaePropElement extends ShaeLifecycleElement {
|
|
61
33
|
constructor() {
|
|
62
34
|
super();
|
|
63
35
|
this.isShaePropElement = true;
|
|
@@ -68,9 +40,78 @@ class ShaePropElement extends HTMLElement {
|
|
|
68
40
|
this.valueOut$ = createSignal();
|
|
69
41
|
this.type$ = createSignal();
|
|
70
42
|
this.shouldTrim$ = createSignal(true);
|
|
71
|
-
this
|
|
43
|
+
this.#logger = new ConsoleLogger("ShaePropElement");
|
|
44
|
+
/** Whether the missing-host warning has already gone out for this element. */
|
|
45
|
+
this.#reportedMissingHost = false;
|
|
46
|
+
// Two terms, and they answer two different questions. `isConnected` separates an element in
|
|
47
|
+
// the tree from one on its way out: a round booked before the departure has nothing left to
|
|
48
|
+
// say about a position the element no longer holds. `isDestroyed` separates one that is
|
|
49
|
+
// listening from one that was ended by hand and is still standing where it was — a lookup
|
|
50
|
+
// running there would write `entNode$` and register the re-request listener again that the
|
|
51
|
+
// teardown just took off, and the element would be listening after it let go of everything.
|
|
52
|
+
// The flag is up before `teardown()` runs, so a round that comes due afterwards reads the
|
|
53
|
+
// state the element is actually in.
|
|
54
|
+
//
|
|
55
|
+
// The test stands here and not in the gate: a `MicrotaskGate` has no way to call a booking
|
|
56
|
+
// off, and that is the whole of what tells it apart from `DeferredTeardown`.
|
|
57
|
+
this.#hostLookup = new MicrotaskGate(() => {
|
|
58
|
+
if (this.isConnected && !this.isDestroyed) {
|
|
59
|
+
this.#findEntNode();
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
// Determines the host from where the element stands right now. The request runs *without* a
|
|
63
|
+
// namespace: a property belongs to the closest entity above it, whatever namespace that entity
|
|
64
|
+
// is in.
|
|
65
|
+
//
|
|
66
|
+
// Nobody answering means there is no entity above this position, and the element says so. A
|
|
67
|
+
// binding without an answer belongs to a place that is no longer there — whether the element
|
|
68
|
+
// moved or the entity above it did. The cleanup on `entNode$` then takes the property off the
|
|
69
|
+
// entity that no longer holds it, instead of leaving the element writing into an entity it does
|
|
70
|
+
// not sit under any more.
|
|
72
71
|
this.#findEntNode = () => {
|
|
73
|
-
|
|
72
|
+
let found;
|
|
73
|
+
requestEntAncestor(this, { answer: (entNode) => found = entNode });
|
|
74
|
+
this.entNode$.set(found);
|
|
75
|
+
if (found == null && this.isConnected && !this.#reportedMissingHost) {
|
|
76
|
+
this.#reportedMissingHost = true;
|
|
77
|
+
this.logger.warn(`[${this.name}] no entity above this element, the property is set nowhere`, {
|
|
78
|
+
shaeProp: this
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
this.#listenForHostChanges();
|
|
82
|
+
};
|
|
83
|
+
// Someone above started or stopped answering. Two things separate this from a lookup the
|
|
84
|
+
// element makes on arrival, and both belong here, to the trigger — what an unanswered
|
|
85
|
+
// request means is the same on either path.
|
|
86
|
+
//
|
|
87
|
+
// It waits a microtask: the message arrives while the tree is still moving. An entity that
|
|
88
|
+
// announces its departure has left, but everything below it can be back in place before the
|
|
89
|
+
// tick ends, and an ancestor answering *right now* can be one this element is about to
|
|
90
|
+
// leave behind. Asking after the dust settles is asking once, about the tree that is
|
|
91
|
+
// actually there.
|
|
92
|
+
//
|
|
93
|
+
// And it drops the question if this element has left in the meantime, for the same reason
|
|
94
|
+
// `#reRequestParent` does in `ShaeEntElement`: an element that is out of the tree answers
|
|
95
|
+
// nothing about its position any more. Whether its departure is a move or an end is
|
|
96
|
+
// `#disconnectFromEntNode`'s to answer, one microtask later, and it is answered in one place.
|
|
97
|
+
// Nothing observable turns on the test — a departing entity takes its properties with it
|
|
98
|
+
// either way — so it stands as a rule, not as a measurement.
|
|
99
|
+
//
|
|
100
|
+
// `#hostLookup` bounds the cost: a page filling up with entities sends many messages through the
|
|
101
|
+
// same ancestor chain, and the gate turns them into one lookup per microtask and per element.
|
|
102
|
+
this.#onReRequestHost = () => {
|
|
103
|
+
this.#hostLookup.schedule();
|
|
104
|
+
};
|
|
105
|
+
this.#listenForHostChanges = () => {
|
|
106
|
+
const target = this.entNode$.value ?? this.ownerDocument;
|
|
107
|
+
if (target === this.#reRequestHostTarget) return;
|
|
108
|
+
this.#stopListeningForHostChanges();
|
|
109
|
+
target.addEventListener(ReRequestEntHostEventName, this.#onReRequestHost);
|
|
110
|
+
this.#reRequestHostTarget = target;
|
|
111
|
+
};
|
|
112
|
+
this.#stopListeningForHostChanges = () => {
|
|
113
|
+
this.#reRequestHostTarget?.removeEventListener(ReRequestEntHostEventName, this.#onReRequestHost);
|
|
114
|
+
this.#reRequestHostTarget = void 0;
|
|
74
115
|
};
|
|
75
116
|
this.#disconnectFromEntNode = () => {
|
|
76
117
|
queueMicrotask(() => {
|
|
@@ -83,16 +124,15 @@ class ShaePropElement extends HTMLElement {
|
|
|
83
124
|
this.name$.set(this.getAttribute(ATTR_NAME)?.trim() ?? void 0);
|
|
84
125
|
};
|
|
85
126
|
this.#readValueAttribute = () => {
|
|
86
|
-
this.
|
|
127
|
+
const value = this.getAttribute(ATTR_VALUE);
|
|
128
|
+
this.valueIn$.set(value === null || value === "" ? void 0 : value);
|
|
87
129
|
};
|
|
88
130
|
this.#readTypeAttribute = () => {
|
|
89
131
|
let type = this.getAttribute(ATTR_TYPE)?.trim().toLowerCase();
|
|
90
|
-
if (type && !
|
|
91
|
-
|
|
92
|
-
this
|
|
93
|
-
|
|
94
|
-
});
|
|
95
|
-
}
|
|
132
|
+
if (type && !propValueConverters.has(type)) {
|
|
133
|
+
this.logger.warn(`[${this.name}] unknown type "${type}"`, {
|
|
134
|
+
shaeProp: this
|
|
135
|
+
});
|
|
96
136
|
type = void 0;
|
|
97
137
|
}
|
|
98
138
|
this.type$.set(type);
|
|
@@ -100,7 +140,62 @@ class ShaePropElement extends HTMLElement {
|
|
|
100
140
|
this.#readNoTrimAttribute = () => {
|
|
101
141
|
this.shouldTrim$.set(!readBooleanAttribute(this, ATTR_NO_TRIM));
|
|
102
142
|
};
|
|
103
|
-
|
|
143
|
+
batch(() => {
|
|
144
|
+
this.#readNameAttribute();
|
|
145
|
+
this.#readValueAttribute();
|
|
146
|
+
this.#readTypeAttribute();
|
|
147
|
+
this.#readNoTrimAttribute();
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
static {
|
|
151
|
+
this.observedAttributes = [ATTR_NAME, ATTR_VALUE, ATTR_TYPE, ATTR_NO_TRIM];
|
|
152
|
+
}
|
|
153
|
+
#logger;
|
|
154
|
+
/** The logger this element reports through: a subclass reads it and does not replace it. */
|
|
155
|
+
get logger() {
|
|
156
|
+
return this.#logger;
|
|
157
|
+
}
|
|
158
|
+
/** Binds this element to the entity above it, and carries that entity's component through. */
|
|
159
|
+
#hostBinding;
|
|
160
|
+
/** Holds the property on the entity for as long as this element declares it. */
|
|
161
|
+
#declareProperty;
|
|
162
|
+
/** Writes the value through to the entity. */
|
|
163
|
+
#writePropertyValue;
|
|
164
|
+
/** Turns what the attribute spells into the value the property carries. */
|
|
165
|
+
#convertValue;
|
|
166
|
+
#reportedMissingHost;
|
|
167
|
+
/** The node this element listens on for a re-request of the host, so the listener can come off the same one. */
|
|
168
|
+
#reRequestHostTarget;
|
|
169
|
+
#hostLookup;
|
|
170
|
+
get name() {
|
|
171
|
+
return this.name$.value;
|
|
172
|
+
}
|
|
173
|
+
get value() {
|
|
174
|
+
return this.valueOut$.value;
|
|
175
|
+
}
|
|
176
|
+
set value(val) {
|
|
177
|
+
this.valueIn$.set(val);
|
|
178
|
+
}
|
|
179
|
+
get shouldTrim() {
|
|
180
|
+
return this.shouldTrim$.value;
|
|
181
|
+
}
|
|
182
|
+
get entNode() {
|
|
183
|
+
return this.entNode$.value;
|
|
184
|
+
}
|
|
185
|
+
set entNode(el) {
|
|
186
|
+
this.entNode$.set(el);
|
|
187
|
+
}
|
|
188
|
+
get viewComponent() {
|
|
189
|
+
return this.viewComponent$.value;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Take up everything this element listens to.
|
|
193
|
+
*
|
|
194
|
+
* Called from {@link ShaePropElement.restore} and from nowhere else — that is the one place where
|
|
195
|
+
* this element's subscriptions begin, at the first connect as well as after a teardown.
|
|
196
|
+
*/
|
|
197
|
+
#subscribe() {
|
|
198
|
+
this.#hostBinding = this.entNode$.onChange((entNode) => {
|
|
104
199
|
if (entNode) {
|
|
105
200
|
const con = link(entNode.viewComponent$, this.viewComponent$);
|
|
106
201
|
return () => {
|
|
@@ -110,18 +205,29 @@ class ShaePropElement extends HTMLElement {
|
|
|
110
205
|
this.viewComponent$.set(void 0);
|
|
111
206
|
}
|
|
112
207
|
});
|
|
113
|
-
createEffect(() => {
|
|
208
|
+
this.#declareProperty = createEffect(() => {
|
|
209
|
+
const vc = this.viewComponent$.get();
|
|
210
|
+
const name = this.name$.get();
|
|
211
|
+
if (vc == null || !name) return;
|
|
212
|
+
const entNode = this.entNode$.value;
|
|
213
|
+
addDeclarant(vc, name);
|
|
214
|
+
return () => {
|
|
215
|
+
if (removeDeclarant(vc, name)) {
|
|
216
|
+
vc.removeProperty(name);
|
|
217
|
+
entNode?.syncShadowObjects();
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
this.#writePropertyValue = createEffect(() => {
|
|
114
222
|
const vc = this.viewComponent$.get();
|
|
115
223
|
if (vc) {
|
|
116
224
|
const name = this.name$.get();
|
|
117
225
|
if (name) {
|
|
118
226
|
const value = this.valueOut$.get();
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
});
|
|
124
|
-
}
|
|
227
|
+
this.logger.debug(`[${this.name}] view-component set-property`, name, value, vc.uuid, {
|
|
228
|
+
viewComponent: vc,
|
|
229
|
+
shaeProp: this
|
|
230
|
+
});
|
|
125
231
|
vc.setProperty(name, value);
|
|
126
232
|
if (this.isConnected) {
|
|
127
233
|
this.entNode?.syncShadowObjects();
|
|
@@ -129,165 +235,56 @@ class ShaePropElement extends HTMLElement {
|
|
|
129
235
|
}
|
|
130
236
|
}
|
|
131
237
|
});
|
|
132
|
-
createEffect(() => {
|
|
238
|
+
this.#convertValue = createEffect(() => {
|
|
133
239
|
const type = this.type$.get();
|
|
134
240
|
const shouldTrim = this.shouldTrim$.get();
|
|
135
241
|
let value = this.valueIn$.get();
|
|
136
242
|
if (shouldTrim && typeof value === "string") {
|
|
137
243
|
value = value.trim();
|
|
138
244
|
}
|
|
139
|
-
value = value
|
|
140
|
-
if (
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
value
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
break;
|
|
154
|
-
case "int":
|
|
155
|
-
case "integer":
|
|
156
|
-
value = parseInt(value, 10);
|
|
157
|
-
break;
|
|
158
|
-
case "hex":
|
|
159
|
-
case "hexadecimal":
|
|
160
|
-
value = parseInt(value, 16);
|
|
161
|
-
break;
|
|
162
|
-
case "oct":
|
|
163
|
-
case "octal":
|
|
164
|
-
value = parseInt(value, 8);
|
|
165
|
-
break;
|
|
166
|
-
case "bin":
|
|
167
|
-
case "binary":
|
|
168
|
-
value = parseInt(value, 2);
|
|
169
|
-
break;
|
|
170
|
-
case "bool":
|
|
171
|
-
case "boolean":
|
|
172
|
-
value = TRUTHY_VALUES.has(value.toLowerCase());
|
|
173
|
-
break;
|
|
174
|
-
case "[]":
|
|
175
|
-
case "text[]":
|
|
176
|
-
case "string[]":
|
|
177
|
-
value = value.split(/\W+/);
|
|
178
|
-
break;
|
|
179
|
-
case "number[]":
|
|
180
|
-
value = value.split(/\s+/).map((v) => Number(v));
|
|
181
|
-
break;
|
|
182
|
-
case "float[]":
|
|
183
|
-
value = value.split(/\s+/).map((v) => parseFloat(v));
|
|
184
|
-
break;
|
|
185
|
-
case "int[]":
|
|
186
|
-
case "integer[]":
|
|
187
|
-
value = value.split(/\s+/).map((v) => parseInt(v, 10));
|
|
188
|
-
break;
|
|
189
|
-
case "hex[]":
|
|
190
|
-
case "hexadecimal[]":
|
|
191
|
-
value = value.split(/\W+/).map((v) => parseInt(v, 16));
|
|
192
|
-
break;
|
|
193
|
-
case "oct[]":
|
|
194
|
-
case "octal[]":
|
|
195
|
-
value = value.split(/\W+/).map((v) => parseInt(v, 8));
|
|
196
|
-
break;
|
|
197
|
-
case "bin[]":
|
|
198
|
-
case "binary[]":
|
|
199
|
-
value = value.split(/\W+/).map((v) => parseInt(v, 2));
|
|
200
|
-
break;
|
|
201
|
-
case "bool[]":
|
|
202
|
-
case "boolean[]":
|
|
203
|
-
value = value.split(/\W+/).map((v) => TRUTHY_VALUES.has(v.toLowerCase()));
|
|
204
|
-
break;
|
|
205
|
-
case "int8array":
|
|
206
|
-
value = new Int8Array(value.split(/\W+/).map((v) => Number(v)));
|
|
207
|
-
break;
|
|
208
|
-
case "uint8array":
|
|
209
|
-
value = new Uint8Array(value.split(/\W+/).map((v) => Number(v)));
|
|
210
|
-
break;
|
|
211
|
-
case "uint8clampedarray":
|
|
212
|
-
value = new Uint8ClampedArray(value.split(/\W+/).map((v) => Number(v)));
|
|
213
|
-
break;
|
|
214
|
-
case "int16array":
|
|
215
|
-
value = new Int16Array(value.split(/\W+/).map((v) => Number(v)));
|
|
216
|
-
break;
|
|
217
|
-
case "uint16array":
|
|
218
|
-
value = new Uint16Array(value.split(/\W+/).map((v) => Number(v)));
|
|
219
|
-
break;
|
|
220
|
-
case "int32array":
|
|
221
|
-
value = new Int32Array(value.split(/\W+/).map((v) => Number(v)));
|
|
222
|
-
break;
|
|
223
|
-
case "uint32array":
|
|
224
|
-
value = new Uint32Array(value.split(/\W+/).map((v) => Number(v)));
|
|
225
|
-
break;
|
|
226
|
-
case "float32array":
|
|
227
|
-
value = new Float32Array(value.split(/\s+/).map((v) => Number(v)));
|
|
228
|
-
break;
|
|
229
|
-
case "float64array":
|
|
230
|
-
value = new Float64Array(value.split(/\s+/).map((v) => Number(v)));
|
|
231
|
-
break;
|
|
232
|
-
case "bigint64array":
|
|
233
|
-
value = new BigInt64Array(value.split(/\W+/).map((v) => BigInt(v)));
|
|
234
|
-
break;
|
|
235
|
-
case "biguint64array":
|
|
236
|
-
value = new BigUint64Array(value.split(/\W+/).map((v) => BigInt(v)));
|
|
237
|
-
break;
|
|
238
|
-
case "json":
|
|
239
|
-
value = JSON.parse(value);
|
|
240
|
-
break;
|
|
241
|
-
default:
|
|
242
|
-
if (this.logger.isWarn) {
|
|
243
|
-
this.logger.warn(`[${this.name}] unknown type "${type}"`, {
|
|
244
|
-
value,
|
|
245
|
-
shaeProp: this
|
|
246
|
-
});
|
|
247
|
-
}
|
|
245
|
+
value = value ?? void 0;
|
|
246
|
+
if (typeof value === "string" && type) {
|
|
247
|
+
const convert = propValueConverters.get(type);
|
|
248
|
+
if (convert != null) {
|
|
249
|
+
try {
|
|
250
|
+
value = convert(value);
|
|
251
|
+
} catch (error) {
|
|
252
|
+
this.logger.error(`[${this.name}] could not convert the value into the type "${type}"`, {
|
|
253
|
+
value,
|
|
254
|
+
error,
|
|
255
|
+
shaeProp: this
|
|
256
|
+
});
|
|
257
|
+
value = void 0;
|
|
258
|
+
}
|
|
248
259
|
}
|
|
249
260
|
}
|
|
250
261
|
this.valueOut$.set(value);
|
|
251
262
|
});
|
|
252
|
-
batch(() => {
|
|
253
|
-
this.#readNameAttribute();
|
|
254
|
-
this.#readValueAttribute();
|
|
255
|
-
this.#readTypeAttribute();
|
|
256
|
-
this.#readNoTrimAttribute();
|
|
257
|
-
});
|
|
258
|
-
this.style.display = "contents";
|
|
259
|
-
}
|
|
260
|
-
static {
|
|
261
|
-
this.observedAttributes = [ATTR_NAME, ATTR_VALUE, ATTR_TYPE, ATTR_NO_TRIM];
|
|
262
|
-
}
|
|
263
|
-
get name() {
|
|
264
|
-
return this.name$.value;
|
|
265
|
-
}
|
|
266
|
-
get value() {
|
|
267
|
-
return this.valueOut$.value;
|
|
268
|
-
}
|
|
269
|
-
set value(val) {
|
|
270
|
-
this.valueIn$.set(val);
|
|
271
|
-
}
|
|
272
|
-
get shouldTrim() {
|
|
273
|
-
return this.shouldTrim$.value;
|
|
274
|
-
}
|
|
275
|
-
get entNode() {
|
|
276
|
-
return this.entNode$.value;
|
|
277
|
-
}
|
|
278
|
-
set entNode(el) {
|
|
279
|
-
this.entNode$.set(el);
|
|
280
263
|
}
|
|
281
|
-
|
|
282
|
-
|
|
264
|
+
/**
|
|
265
|
+
* Take this element's own subscriptions up. Its half of {@link ShaeLifecycleElement.restore}.
|
|
266
|
+
*
|
|
267
|
+
* There is nothing to catch up on here, unlike `<shae-ent>`: `connectedCallback` reads `value`,
|
|
268
|
+
* `name`, `type` and `no-trim` off the attributes and looks the host up again straight after
|
|
269
|
+
* this, so the attributes decide, and whatever was written to a released element through
|
|
270
|
+
* `prop.value` or `prop.entNode` is replaced by what the tree and the markup say. That is this
|
|
271
|
+
* element's rule in and out of a teardown alike — a `<shae-prop>` reads its position on every
|
|
272
|
+
* connect — and the documentation states it as the difference it is.
|
|
273
|
+
*/
|
|
274
|
+
restore() {
|
|
275
|
+
super.restore();
|
|
276
|
+
this.#subscribe();
|
|
283
277
|
}
|
|
284
278
|
connectedCallback() {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
279
|
+
hibernate(() => {
|
|
280
|
+
super.connectedCallback();
|
|
281
|
+
batch(() => {
|
|
282
|
+
this.#findEntNode();
|
|
283
|
+
this.#readNameAttribute();
|
|
284
|
+
this.#readValueAttribute();
|
|
285
|
+
this.#readTypeAttribute();
|
|
286
|
+
this.#readNoTrimAttribute();
|
|
287
|
+
});
|
|
291
288
|
});
|
|
292
289
|
}
|
|
293
290
|
attributeChangedCallback(name) {
|
|
@@ -307,9 +304,33 @@ class ShaePropElement extends HTMLElement {
|
|
|
307
304
|
}
|
|
308
305
|
}
|
|
309
306
|
disconnectedCallback() {
|
|
307
|
+
this.#stopListeningForHostChanges();
|
|
310
308
|
this.#disconnectFromEntNode();
|
|
309
|
+
super.disconnectedCallback();
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Release this element's own subscriptions. Its half of {@link ShaeLifecycleElement.teardown}.
|
|
313
|
+
*
|
|
314
|
+
* The `link()` between the host's `viewComponent$` and this element's own needs no line of its
|
|
315
|
+
* own: it is built in the cleanup path of the `entNode$` subscription, and taking that
|
|
316
|
+
* subscription off runs the cleanup that destroys it.
|
|
317
|
+
*/
|
|
318
|
+
teardown() {
|
|
319
|
+
this.#stopListeningForHostChanges();
|
|
320
|
+
this.#hostBinding?.();
|
|
321
|
+
this.#hostBinding = void 0;
|
|
322
|
+
this.#declareProperty?.destroy();
|
|
323
|
+
this.#declareProperty = void 0;
|
|
324
|
+
this.#writePropertyValue?.destroy();
|
|
325
|
+
this.#writePropertyValue = void 0;
|
|
326
|
+
this.#convertValue?.destroy();
|
|
327
|
+
this.#convertValue = void 0;
|
|
328
|
+
super.teardown();
|
|
311
329
|
}
|
|
312
330
|
#findEntNode;
|
|
331
|
+
#onReRequestHost;
|
|
332
|
+
#listenForHostChanges;
|
|
333
|
+
#stopListeningForHostChanges;
|
|
313
334
|
#disconnectFromEntNode;
|
|
314
335
|
#readNameAttribute;
|
|
315
336
|
#readValueAttribute;
|