@xaendar/core 0.9.18 → 0.9.22
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.
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
//#region ../packages/core/src/signals/input/input-instance.symbol.ts
|
|
2
|
+
/**
|
|
3
|
+
* Unique symbol used to mark an object as a valid `InputSignal` instance.
|
|
4
|
+
*
|
|
5
|
+
* Set to `true` on every `InputSignal` created by the `input()` factory.
|
|
6
|
+
* Used by {@link isInputSignal} to distinguish input signals from plain
|
|
7
|
+
* `Signal.State` instances at runtime without exposing the marker in the
|
|
8
|
+
* public API.
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
var INPUT_SIGNAL_INSTANCE_SYMBOL = Symbol(`InputSignalInstance`);
|
|
13
|
+
/**
|
|
14
|
+
* Type guard that checks whether a given value is an `InputSignal` instance.
|
|
15
|
+
*
|
|
16
|
+
* Inspects the presence of {@link INPUT_SIGNAL_INSTANCE_SYMBOL} on the object,
|
|
17
|
+
* which is set to `true` by the `input()` factory for every valid instance.
|
|
18
|
+
*
|
|
19
|
+
* @param instance - The value to inspect.
|
|
20
|
+
* @returns `true` if `instance` is an `InputSignal`, `false` otherwise.
|
|
21
|
+
*/
|
|
22
|
+
function isInputSignal(instance) {
|
|
23
|
+
return instance?.[INPUT_SIGNAL_INSTANCE_SYMBOL];
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region ../packages/core/src/signals/input/input-set.symbol.ts
|
|
27
|
+
/**
|
|
28
|
+
* This symbol is used to call the set method of the InputSignal
|
|
29
|
+
* Normally the set method is permitted internally and should not be called
|
|
30
|
+
* by the User
|
|
31
|
+
*/
|
|
32
|
+
var INPUT_SIGNAL_SET_SYMBOL = Symbol(`InputSignalSet`);
|
|
33
|
+
/**
|
|
34
|
+
* Asserts that the provided symbol matches the internal {@link INPUT_SIGNAL_SET_SYMBOL} symbol,
|
|
35
|
+
* ensuring the caller has access to internal APIs.
|
|
36
|
+
*
|
|
37
|
+
* Throws if the symbol does not match, preventing external code from
|
|
38
|
+
* invoking methods intended for internal use only.
|
|
39
|
+
*
|
|
40
|
+
* @param symbol - The symbol to validate against {@link INPUT_SIGNAL_SET_SYMBOL}.
|
|
41
|
+
* @throws {Error} If `symbol` does not match {@link INPUT_SIGNAL_SET_SYMBOL}.
|
|
42
|
+
* @internal
|
|
43
|
+
*/
|
|
44
|
+
function assertPrivateContext(symbol) {
|
|
45
|
+
if (symbol !== INPUT_SIGNAL_SET_SYMBOL) throw new Error("Invalid symbol for InputSignal set method");
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region ../packages/core/src/signals/input/input.ts
|
|
49
|
+
/**
|
|
50
|
+
* Creates an `InputSignal` — a specialized reactive state designed for use
|
|
51
|
+
* as a property signal in web components.
|
|
52
|
+
*
|
|
53
|
+
* Unlike a plain `Signal.State`, the `set` method of an `InputSignal` is
|
|
54
|
+
* restricted to internal callers (identified by the private symbol) and
|
|
55
|
+
* accepts an optional `transform` function that converts incoming values
|
|
56
|
+
* (e.g. raw HTML attribute strings) into the internally stored type before
|
|
57
|
+
* updating the signal.
|
|
58
|
+
*
|
|
59
|
+
* @param value - The initial value of the signal.
|
|
60
|
+
* @param options - Optional configuration including an equality function,
|
|
61
|
+
* lifecycle hooks, and a `transform` function applied to incoming values.
|
|
62
|
+
* @returns A new `InputSignal` instance.
|
|
63
|
+
*/
|
|
64
|
+
function input(value, options) {
|
|
65
|
+
const transform = options?.transform;
|
|
66
|
+
delete options?.transform;
|
|
67
|
+
const signal = new Signal.State(value, options);
|
|
68
|
+
const originalSet = signal.set;
|
|
69
|
+
const getter = function() {
|
|
70
|
+
return signal.get();
|
|
71
|
+
};
|
|
72
|
+
Object.assign(getter, {
|
|
73
|
+
set(newValue, symbol) {
|
|
74
|
+
assertPrivateContext(symbol);
|
|
75
|
+
const transformedValue = transform ? transform(newValue) : newValue;
|
|
76
|
+
originalSet.call(signal, transformedValue);
|
|
77
|
+
},
|
|
78
|
+
get: signal.get.bind(signal)
|
|
79
|
+
});
|
|
80
|
+
getter[INPUT_SIGNAL_INSTANCE_SYMBOL] = true;
|
|
81
|
+
return getter;
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region ../packages/core/src/signals/computed/computed.ts
|
|
85
|
+
/**
|
|
86
|
+
* Creates a read-only computed signal whose value is derived from other signals.
|
|
87
|
+
*
|
|
88
|
+
* Returns a callable getter that reads the current value, augmented with the
|
|
89
|
+
* underlying {@link Signal.State} API.
|
|
90
|
+
*
|
|
91
|
+
* @template Value - The type of the computed value. Defaults to `any`.
|
|
92
|
+
* @param value - The initial (seed) value of the computed signal.
|
|
93
|
+
* @param options - Configuration options for the underlying signal.
|
|
94
|
+
* @returns A {@link Computed} instance.
|
|
95
|
+
*/
|
|
96
|
+
function computed(value, options) {
|
|
97
|
+
const signal = new Signal.State(value, options);
|
|
98
|
+
const getter = function() {
|
|
99
|
+
return signal.get();
|
|
100
|
+
};
|
|
101
|
+
Object.assign(getter, { get: signal.get.bind(signal) });
|
|
102
|
+
return getter;
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region ../packages/core/src/signals/effect/effect.ts
|
|
106
|
+
/**
|
|
107
|
+
* Runs a side-effectful function and automatically re-runs it whenever any
|
|
108
|
+
* Signal read during its execution changes.
|
|
109
|
+
*
|
|
110
|
+
* Internally, `effect` wraps the user callback inside a `Computed` node
|
|
111
|
+
* (for dependency tracking) and observes it with a `Watcher` (for push
|
|
112
|
+
* notifications). When any tracked dependency changes, the `Watcher`
|
|
113
|
+
* schedules a microtask that re-evaluates the `Computed`, which in turn
|
|
114
|
+
* re-runs the user callback and re-registers the new set of dependencies.
|
|
115
|
+
*
|
|
116
|
+
* The returned disposer function stops the effect: it unwatches the internal
|
|
117
|
+
* `Computed` from the `Watcher`, severing all dependency subscriptions so
|
|
118
|
+
* the callback is never called again and the graph nodes can be
|
|
119
|
+
* garbage-collected.
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```ts
|
|
123
|
+
* const count = new State(0);
|
|
124
|
+
*
|
|
125
|
+
* const stop = effect(() => {
|
|
126
|
+
* console.log('count is', count.get());
|
|
127
|
+
* });
|
|
128
|
+
* // logs: "count is 0"
|
|
129
|
+
*
|
|
130
|
+
* count.set(1); // logs: "count is 1"
|
|
131
|
+
* count.set(2); // logs: "count is 2"
|
|
132
|
+
*
|
|
133
|
+
* stop(); // no more logs
|
|
134
|
+
* count.set(3); // silent
|
|
135
|
+
* ```
|
|
136
|
+
*
|
|
137
|
+
* @param fn - The side-effectful function to run. Any Signal read inside it
|
|
138
|
+
* is tracked as a dependency.
|
|
139
|
+
* @returns A disposer function that, when called, permanently stops the effect.
|
|
140
|
+
*/
|
|
141
|
+
function effect(fn, options) {
|
|
142
|
+
/**
|
|
143
|
+
* Wrap the user callback in a Computed so that automatic dependency
|
|
144
|
+
* tracking (via pushComputed / popComputed) works for free.
|
|
145
|
+
* The Computed always returns `undefined` — we only care about the
|
|
146
|
+
* side-effects and the tracked sources, not the value.
|
|
147
|
+
*/
|
|
148
|
+
const computed = new Signal.Computed(() => fn());
|
|
149
|
+
let needsEnqueue = true;
|
|
150
|
+
/**
|
|
151
|
+
* The Watcher is notified synchronously as soon as any tracked dependency
|
|
152
|
+
* changes. Its job is purely to schedule the re-execution; the actual
|
|
153
|
+
* re-evaluation happens asynchronously in a microtask so that multiple
|
|
154
|
+
* synchronous signal updates are batched into a single re-run.
|
|
155
|
+
*/
|
|
156
|
+
const watcher = new Signal.subtle.Watcher(() => {
|
|
157
|
+
if (needsEnqueue) {
|
|
158
|
+
needsEnqueue = false;
|
|
159
|
+
queueMicrotask(() => {
|
|
160
|
+
needsEnqueue = true;
|
|
161
|
+
options?.onBeforeRun?.();
|
|
162
|
+
const pendings = watcher.getPending();
|
|
163
|
+
for (let i = 0; i < pendings.length; i++) pendings[i].get();
|
|
164
|
+
options?.onAfterRun?.();
|
|
165
|
+
watcher.watch();
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
options?.onBeforeRun?.();
|
|
170
|
+
watcher.watch(computed);
|
|
171
|
+
computed.get();
|
|
172
|
+
options?.onAfterRun?.();
|
|
173
|
+
/**
|
|
174
|
+
* Disposer — call this to permanently stop the effect.
|
|
175
|
+
*
|
|
176
|
+
* Unwatching the Computed tears down the entire live dependency chain
|
|
177
|
+
* (Watcher → Computed → all sources), preventing any further
|
|
178
|
+
* notifications and allowing GC.
|
|
179
|
+
*/
|
|
180
|
+
return () => {
|
|
181
|
+
options?.onCleanup?.();
|
|
182
|
+
watcher.unwatch(computed);
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region ../packages/core/src/signals/signal/signal.ts
|
|
187
|
+
/**
|
|
188
|
+
* Creates a writable reactive signal.
|
|
189
|
+
*
|
|
190
|
+
* Returns a callable getter that reads the current value, augmented with the
|
|
191
|
+
* underlying {@link Signal.State} API and an `update` method to derive the next
|
|
192
|
+
* value from the previous one.
|
|
193
|
+
*
|
|
194
|
+
* @template T - The type of the stored value. Defaults to `any`.
|
|
195
|
+
* @param value - The initial value of the signal.
|
|
196
|
+
* @param options - Configuration options for the signal.
|
|
197
|
+
* @returns A {@link SignalType} instance.
|
|
198
|
+
*/
|
|
199
|
+
function signal(value, options) {
|
|
200
|
+
const signal = new Signal.State(value, options);
|
|
201
|
+
const getter = function() {
|
|
202
|
+
return signal.get();
|
|
203
|
+
};
|
|
204
|
+
Object.assign(getter, {
|
|
205
|
+
set: signal.set.bind(signal),
|
|
206
|
+
get: signal.get.bind(signal),
|
|
207
|
+
update: (updater) => signal.set(updater(signal.get()))
|
|
208
|
+
});
|
|
209
|
+
return getter;
|
|
210
|
+
}
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region ../packages/core/src/signals/untracked.ts
|
|
213
|
+
/**
|
|
214
|
+
* Executes a function without tracking any dependencies.
|
|
215
|
+
* @param fn - The function to execute without tracking.
|
|
216
|
+
* @returns The result of the function execution.
|
|
217
|
+
*/
|
|
218
|
+
var untracked = Signal.subtle.untrack;
|
|
219
|
+
//#endregion
|
|
220
|
+
Object.defineProperty(exports, "INPUT_SIGNAL_SET_SYMBOL", {
|
|
221
|
+
enumerable: true,
|
|
222
|
+
get: function() {
|
|
223
|
+
return INPUT_SIGNAL_SET_SYMBOL;
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
Object.defineProperty(exports, "computed", {
|
|
227
|
+
enumerable: true,
|
|
228
|
+
get: function() {
|
|
229
|
+
return computed;
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
Object.defineProperty(exports, "effect", {
|
|
233
|
+
enumerable: true,
|
|
234
|
+
get: function() {
|
|
235
|
+
return effect;
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
Object.defineProperty(exports, "input", {
|
|
239
|
+
enumerable: true,
|
|
240
|
+
get: function() {
|
|
241
|
+
return input;
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
Object.defineProperty(exports, "isInputSignal", {
|
|
245
|
+
enumerable: true,
|
|
246
|
+
get: function() {
|
|
247
|
+
return isInputSignal;
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
Object.defineProperty(exports, "signal", {
|
|
251
|
+
enumerable: true,
|
|
252
|
+
get: function() {
|
|
253
|
+
return signal;
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
Object.defineProperty(exports, "untracked", {
|
|
257
|
+
enumerable: true,
|
|
258
|
+
get: function() {
|
|
259
|
+
return untracked;
|
|
260
|
+
}
|
|
261
|
+
});
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
//#region ../packages/core/src/signals/input/input-instance.symbol.ts
|
|
2
|
+
/**
|
|
3
|
+
* Unique symbol used to mark an object as a valid `InputSignal` instance.
|
|
4
|
+
*
|
|
5
|
+
* Set to `true` on every `InputSignal` created by the `input()` factory.
|
|
6
|
+
* Used by {@link isInputSignal} to distinguish input signals from plain
|
|
7
|
+
* `Signal.State` instances at runtime without exposing the marker in the
|
|
8
|
+
* public API.
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
var INPUT_SIGNAL_INSTANCE_SYMBOL = Symbol(`InputSignalInstance`);
|
|
13
|
+
/**
|
|
14
|
+
* Type guard that checks whether a given value is an `InputSignal` instance.
|
|
15
|
+
*
|
|
16
|
+
* Inspects the presence of {@link INPUT_SIGNAL_INSTANCE_SYMBOL} on the object,
|
|
17
|
+
* which is set to `true` by the `input()` factory for every valid instance.
|
|
18
|
+
*
|
|
19
|
+
* @param instance - The value to inspect.
|
|
20
|
+
* @returns `true` if `instance` is an `InputSignal`, `false` otherwise.
|
|
21
|
+
*/
|
|
22
|
+
function isInputSignal(instance) {
|
|
23
|
+
return instance?.[INPUT_SIGNAL_INSTANCE_SYMBOL];
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region ../packages/core/src/signals/input/input-set.symbol.ts
|
|
27
|
+
/**
|
|
28
|
+
* This symbol is used to call the set method of the InputSignal
|
|
29
|
+
* Normally the set method is permitted internally and should not be called
|
|
30
|
+
* by the User
|
|
31
|
+
*/
|
|
32
|
+
var INPUT_SIGNAL_SET_SYMBOL = Symbol(`InputSignalSet`);
|
|
33
|
+
/**
|
|
34
|
+
* Asserts that the provided symbol matches the internal {@link INPUT_SIGNAL_SET_SYMBOL} symbol,
|
|
35
|
+
* ensuring the caller has access to internal APIs.
|
|
36
|
+
*
|
|
37
|
+
* Throws if the symbol does not match, preventing external code from
|
|
38
|
+
* invoking methods intended for internal use only.
|
|
39
|
+
*
|
|
40
|
+
* @param symbol - The symbol to validate against {@link INPUT_SIGNAL_SET_SYMBOL}.
|
|
41
|
+
* @throws {Error} If `symbol` does not match {@link INPUT_SIGNAL_SET_SYMBOL}.
|
|
42
|
+
* @internal
|
|
43
|
+
*/
|
|
44
|
+
function assertPrivateContext(symbol) {
|
|
45
|
+
if (symbol !== INPUT_SIGNAL_SET_SYMBOL) throw new Error("Invalid symbol for InputSignal set method");
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region ../packages/core/src/signals/input/input.ts
|
|
49
|
+
/**
|
|
50
|
+
* Creates an `InputSignal` — a specialized reactive state designed for use
|
|
51
|
+
* as a property signal in web components.
|
|
52
|
+
*
|
|
53
|
+
* Unlike a plain `Signal.State`, the `set` method of an `InputSignal` is
|
|
54
|
+
* restricted to internal callers (identified by the private symbol) and
|
|
55
|
+
* accepts an optional `transform` function that converts incoming values
|
|
56
|
+
* (e.g. raw HTML attribute strings) into the internally stored type before
|
|
57
|
+
* updating the signal.
|
|
58
|
+
*
|
|
59
|
+
* @param value - The initial value of the signal.
|
|
60
|
+
* @param options - Optional configuration including an equality function,
|
|
61
|
+
* lifecycle hooks, and a `transform` function applied to incoming values.
|
|
62
|
+
* @returns A new `InputSignal` instance.
|
|
63
|
+
*/
|
|
64
|
+
function input(value, options) {
|
|
65
|
+
const transform = options?.transform;
|
|
66
|
+
delete options?.transform;
|
|
67
|
+
const signal = new Signal.State(value, options);
|
|
68
|
+
const originalSet = signal.set;
|
|
69
|
+
const getter = function() {
|
|
70
|
+
return signal.get();
|
|
71
|
+
};
|
|
72
|
+
Object.assign(getter, {
|
|
73
|
+
set(newValue, symbol) {
|
|
74
|
+
assertPrivateContext(symbol);
|
|
75
|
+
const transformedValue = transform ? transform(newValue) : newValue;
|
|
76
|
+
originalSet.call(signal, transformedValue);
|
|
77
|
+
},
|
|
78
|
+
get: signal.get.bind(signal)
|
|
79
|
+
});
|
|
80
|
+
getter[INPUT_SIGNAL_INSTANCE_SYMBOL] = true;
|
|
81
|
+
return getter;
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region ../packages/core/src/signals/computed/computed.ts
|
|
85
|
+
/**
|
|
86
|
+
* Creates a read-only computed signal whose value is derived from other signals.
|
|
87
|
+
*
|
|
88
|
+
* Returns a callable getter that reads the current value, augmented with the
|
|
89
|
+
* underlying {@link Signal.State} API.
|
|
90
|
+
*
|
|
91
|
+
* @template Value - The type of the computed value. Defaults to `any`.
|
|
92
|
+
* @param value - The initial (seed) value of the computed signal.
|
|
93
|
+
* @param options - Configuration options for the underlying signal.
|
|
94
|
+
* @returns A {@link Computed} instance.
|
|
95
|
+
*/
|
|
96
|
+
function computed(value, options) {
|
|
97
|
+
const signal = new Signal.State(value, options);
|
|
98
|
+
const getter = function() {
|
|
99
|
+
return signal.get();
|
|
100
|
+
};
|
|
101
|
+
Object.assign(getter, { get: signal.get.bind(signal) });
|
|
102
|
+
return getter;
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region ../packages/core/src/signals/effect/effect.ts
|
|
106
|
+
/**
|
|
107
|
+
* Runs a side-effectful function and automatically re-runs it whenever any
|
|
108
|
+
* Signal read during its execution changes.
|
|
109
|
+
*
|
|
110
|
+
* Internally, `effect` wraps the user callback inside a `Computed` node
|
|
111
|
+
* (for dependency tracking) and observes it with a `Watcher` (for push
|
|
112
|
+
* notifications). When any tracked dependency changes, the `Watcher`
|
|
113
|
+
* schedules a microtask that re-evaluates the `Computed`, which in turn
|
|
114
|
+
* re-runs the user callback and re-registers the new set of dependencies.
|
|
115
|
+
*
|
|
116
|
+
* The returned disposer function stops the effect: it unwatches the internal
|
|
117
|
+
* `Computed` from the `Watcher`, severing all dependency subscriptions so
|
|
118
|
+
* the callback is never called again and the graph nodes can be
|
|
119
|
+
* garbage-collected.
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```ts
|
|
123
|
+
* const count = new State(0);
|
|
124
|
+
*
|
|
125
|
+
* const stop = effect(() => {
|
|
126
|
+
* console.log('count is', count.get());
|
|
127
|
+
* });
|
|
128
|
+
* // logs: "count is 0"
|
|
129
|
+
*
|
|
130
|
+
* count.set(1); // logs: "count is 1"
|
|
131
|
+
* count.set(2); // logs: "count is 2"
|
|
132
|
+
*
|
|
133
|
+
* stop(); // no more logs
|
|
134
|
+
* count.set(3); // silent
|
|
135
|
+
* ```
|
|
136
|
+
*
|
|
137
|
+
* @param fn - The side-effectful function to run. Any Signal read inside it
|
|
138
|
+
* is tracked as a dependency.
|
|
139
|
+
* @returns A disposer function that, when called, permanently stops the effect.
|
|
140
|
+
*/
|
|
141
|
+
function effect(fn, options) {
|
|
142
|
+
/**
|
|
143
|
+
* Wrap the user callback in a Computed so that automatic dependency
|
|
144
|
+
* tracking (via pushComputed / popComputed) works for free.
|
|
145
|
+
* The Computed always returns `undefined` — we only care about the
|
|
146
|
+
* side-effects and the tracked sources, not the value.
|
|
147
|
+
*/
|
|
148
|
+
const computed = new Signal.Computed(() => fn());
|
|
149
|
+
let needsEnqueue = true;
|
|
150
|
+
/**
|
|
151
|
+
* The Watcher is notified synchronously as soon as any tracked dependency
|
|
152
|
+
* changes. Its job is purely to schedule the re-execution; the actual
|
|
153
|
+
* re-evaluation happens asynchronously in a microtask so that multiple
|
|
154
|
+
* synchronous signal updates are batched into a single re-run.
|
|
155
|
+
*/
|
|
156
|
+
const watcher = new Signal.subtle.Watcher(() => {
|
|
157
|
+
if (needsEnqueue) {
|
|
158
|
+
needsEnqueue = false;
|
|
159
|
+
queueMicrotask(() => {
|
|
160
|
+
needsEnqueue = true;
|
|
161
|
+
options?.onBeforeRun?.();
|
|
162
|
+
const pendings = watcher.getPending();
|
|
163
|
+
for (let i = 0; i < pendings.length; i++) pendings[i].get();
|
|
164
|
+
options?.onAfterRun?.();
|
|
165
|
+
watcher.watch();
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
options?.onBeforeRun?.();
|
|
170
|
+
watcher.watch(computed);
|
|
171
|
+
computed.get();
|
|
172
|
+
options?.onAfterRun?.();
|
|
173
|
+
/**
|
|
174
|
+
* Disposer — call this to permanently stop the effect.
|
|
175
|
+
*
|
|
176
|
+
* Unwatching the Computed tears down the entire live dependency chain
|
|
177
|
+
* (Watcher → Computed → all sources), preventing any further
|
|
178
|
+
* notifications and allowing GC.
|
|
179
|
+
*/
|
|
180
|
+
return () => {
|
|
181
|
+
options?.onCleanup?.();
|
|
182
|
+
watcher.unwatch(computed);
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region ../packages/core/src/signals/signal/signal.ts
|
|
187
|
+
/**
|
|
188
|
+
* Creates a writable reactive signal.
|
|
189
|
+
*
|
|
190
|
+
* Returns a callable getter that reads the current value, augmented with the
|
|
191
|
+
* underlying {@link Signal.State} API and an `update` method to derive the next
|
|
192
|
+
* value from the previous one.
|
|
193
|
+
*
|
|
194
|
+
* @template T - The type of the stored value. Defaults to `any`.
|
|
195
|
+
* @param value - The initial value of the signal.
|
|
196
|
+
* @param options - Configuration options for the signal.
|
|
197
|
+
* @returns A {@link SignalType} instance.
|
|
198
|
+
*/
|
|
199
|
+
function signal(value, options) {
|
|
200
|
+
const signal = new Signal.State(value, options);
|
|
201
|
+
const getter = function() {
|
|
202
|
+
return signal.get();
|
|
203
|
+
};
|
|
204
|
+
Object.assign(getter, {
|
|
205
|
+
set: signal.set.bind(signal),
|
|
206
|
+
get: signal.get.bind(signal),
|
|
207
|
+
update: (updater) => signal.set(updater(signal.get()))
|
|
208
|
+
});
|
|
209
|
+
return getter;
|
|
210
|
+
}
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region ../packages/core/src/signals/untracked.ts
|
|
213
|
+
/**
|
|
214
|
+
* Executes a function without tracking any dependencies.
|
|
215
|
+
* @param fn - The function to execute without tracking.
|
|
216
|
+
* @returns The result of the function execution.
|
|
217
|
+
*/
|
|
218
|
+
var untracked = Signal.subtle.untrack;
|
|
219
|
+
//#endregion
|
|
220
|
+
export { input as a, computed as i, signal as n, INPUT_SIGNAL_SET_SYMBOL as o, effect as r, isInputSignal as s, untracked as t };
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { EffectOptions } from '../../../../../schematics/packages/signals/src/public-api';
|
|
2
|
+
import { NoArgsVoidFunction } from '../../../../../schematics/packages/types/src/public-api';
|
|
3
|
+
import { SignalOptions } from '../../../../../schematics/packages/signals/src/public-api';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A read-only reactive value derived from other signals.
|
|
7
|
+
*
|
|
8
|
+
* Wraps {@link Signal.Computed} and is also callable as a function to read the
|
|
9
|
+
* current computed value.
|
|
10
|
+
*
|
|
11
|
+
* @template Value - The type of the computed value. Defaults to `any`.
|
|
12
|
+
*/
|
|
13
|
+
declare type Computed<Value = any> = Signal.Computed<Value> & {
|
|
14
|
+
/**
|
|
15
|
+
* Reads the current computed value, recomputing it if any dependency changed.
|
|
16
|
+
*
|
|
17
|
+
* @returns The current value of type `Value`.
|
|
18
|
+
*/
|
|
19
|
+
(): Value;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Creates a read-only computed signal whose value is derived from other signals.
|
|
24
|
+
*
|
|
25
|
+
* Returns a callable getter that reads the current value, augmented with the
|
|
26
|
+
* underlying {@link Signal.State} API.
|
|
27
|
+
*
|
|
28
|
+
* @template Value - The type of the computed value. Defaults to `any`.
|
|
29
|
+
* @param value - The initial (seed) value of the computed signal.
|
|
30
|
+
* @param options - Configuration options for the underlying signal.
|
|
31
|
+
* @returns A {@link Computed} instance.
|
|
32
|
+
*/
|
|
33
|
+
export declare function computed<Value = any>(value: Value, options?: SignalOptions<Value>): Computed<Value>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Runs a side-effectful function and automatically re-runs it whenever any
|
|
37
|
+
* Signal read during its execution changes.
|
|
38
|
+
*
|
|
39
|
+
* Internally, `effect` wraps the user callback inside a `Computed` node
|
|
40
|
+
* (for dependency tracking) and observes it with a `Watcher` (for push
|
|
41
|
+
* notifications). When any tracked dependency changes, the `Watcher`
|
|
42
|
+
* schedules a microtask that re-evaluates the `Computed`, which in turn
|
|
43
|
+
* re-runs the user callback and re-registers the new set of dependencies.
|
|
44
|
+
*
|
|
45
|
+
* The returned disposer function stops the effect: it unwatches the internal
|
|
46
|
+
* `Computed` from the `Watcher`, severing all dependency subscriptions so
|
|
47
|
+
* the callback is never called again and the graph nodes can be
|
|
48
|
+
* garbage-collected.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* const count = new State(0);
|
|
53
|
+
*
|
|
54
|
+
* const stop = effect(() => {
|
|
55
|
+
* console.log('count is', count.get());
|
|
56
|
+
* });
|
|
57
|
+
* // logs: "count is 0"
|
|
58
|
+
*
|
|
59
|
+
* count.set(1); // logs: "count is 1"
|
|
60
|
+
* count.set(2); // logs: "count is 2"
|
|
61
|
+
*
|
|
62
|
+
* stop(); // no more logs
|
|
63
|
+
* count.set(3); // silent
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @param fn - The side-effectful function to run. Any Signal read inside it
|
|
67
|
+
* is tracked as a dependency.
|
|
68
|
+
* @returns A disposer function that, when called, permanently stops the effect.
|
|
69
|
+
*/
|
|
70
|
+
export declare function effect(fn: NoArgsVoidFunction, options?: EffectOptions): NoArgsVoidFunction;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Creates an `InputSignal` — a specialized reactive state designed for use
|
|
74
|
+
* as a property signal in web components.
|
|
75
|
+
*
|
|
76
|
+
* Unlike a plain `Signal.State`, the `set` method of an `InputSignal` is
|
|
77
|
+
* restricted to internal callers (identified by the private symbol) and
|
|
78
|
+
* accepts an optional `transform` function that converts incoming values
|
|
79
|
+
* (e.g. raw HTML attribute strings) into the internally stored type before
|
|
80
|
+
* updating the signal.
|
|
81
|
+
*
|
|
82
|
+
* @param value - The initial value of the signal.
|
|
83
|
+
* @param options - Optional configuration including an equality function,
|
|
84
|
+
* lifecycle hooks, and a `transform` function applied to incoming values.
|
|
85
|
+
* @returns A new `InputSignal` instance.
|
|
86
|
+
*/
|
|
87
|
+
export declare function input<ActualValue = unknown, IncomingValue = ActualValue>(value?: ActualValue, options?: InputSignalOptions<ActualValue, IncomingValue>): InputSignal<ActualValue, IncomingValue>;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* A reactive value sourced from outside the component (e.g. an attribute).
|
|
91
|
+
*
|
|
92
|
+
* Wraps {@link Signal.State} without its `set` method (inputs are not set
|
|
93
|
+
* directly) and is callable as a function to read the current value. An
|
|
94
|
+
* optional {@link InputSignalOptions.transform} can convert the incoming value
|
|
95
|
+
* into the actual stored value.
|
|
96
|
+
*
|
|
97
|
+
* @template ActualValue - The internal type stored by the signal. Defaults to `unknown`.
|
|
98
|
+
* @template IncomingValue - The raw type received from outside. Defaults to `ActualValue`.
|
|
99
|
+
*/
|
|
100
|
+
declare type InputSignal<ActualValue = unknown, IncomingValue = ActualValue> = Omit<Signal.State<ActualValue>, 'set'> & {
|
|
101
|
+
/**
|
|
102
|
+
* Reads the current value of the input signal.
|
|
103
|
+
*
|
|
104
|
+
* @returns The current value of type `ActualValue`.
|
|
105
|
+
*/
|
|
106
|
+
(): ActualValue;
|
|
107
|
+
/**
|
|
108
|
+
* Creates a new input signal.
|
|
109
|
+
*
|
|
110
|
+
* @param value - The initial incoming value.
|
|
111
|
+
* @param options - Optional configuration, including a `transform` function.
|
|
112
|
+
*/
|
|
113
|
+
new (value: IncomingValue, options?: InputSignalOptions<ActualValue, IncomingValue>): InputSignal<ActualValue, IncomingValue>;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Options used to configure an `InputSignal`.
|
|
118
|
+
*
|
|
119
|
+
* Extends {@link SignalOptions} with an optional `transform` function that
|
|
120
|
+
* converts the incoming value (e.g. an attribute string) into the actual
|
|
121
|
+
* internal value stored by the signal.
|
|
122
|
+
*
|
|
123
|
+
* @template ActualValue - The internal type stored by the signal. Defaults to `unknown`.
|
|
124
|
+
* @template IncomingValue - The raw type received from outside (e.g. from an attribute). Defaults to `ActualValue`.
|
|
125
|
+
*/
|
|
126
|
+
declare type InputSignalOptions<ActualValue = unknown, IncomingValue = ActualValue> = SignalOptions<ActualValue> & {
|
|
127
|
+
/**
|
|
128
|
+
* Optional function to transform the incoming value before it is stored.
|
|
129
|
+
*
|
|
130
|
+
* @param value - The raw incoming value.
|
|
131
|
+
* @returns The transformed value of type `ActualValue`.
|
|
132
|
+
*/
|
|
133
|
+
transform?: (value: IncomingValue) => ActualValue;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Creates a writable reactive signal.
|
|
138
|
+
*
|
|
139
|
+
* Returns a callable getter that reads the current value, augmented with the
|
|
140
|
+
* underlying {@link Signal.State} API and an `update` method to derive the next
|
|
141
|
+
* value from the previous one.
|
|
142
|
+
*
|
|
143
|
+
* @template T - The type of the stored value. Defaults to `any`.
|
|
144
|
+
* @param value - The initial value of the signal.
|
|
145
|
+
* @param options - Configuration options for the signal.
|
|
146
|
+
* @returns A {@link SignalType} instance.
|
|
147
|
+
*/
|
|
148
|
+
export declare function signal<T = any>(value: T, options?: SignalOptions<T>): Signal_2<T>;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* A writable reactive value.
|
|
152
|
+
*
|
|
153
|
+
* Wraps {@link Signal.State} and is also callable as a function to set a new
|
|
154
|
+
* value directly.
|
|
155
|
+
*
|
|
156
|
+
* @template Value - The type of the stored value. Defaults to `any`.
|
|
157
|
+
*/
|
|
158
|
+
declare type Signal_2<Value = any> = Signal.State<Value> & {
|
|
159
|
+
/**
|
|
160
|
+
* Get the current value of the signal.
|
|
161
|
+
*
|
|
162
|
+
* @returns The current value of the signal.
|
|
163
|
+
*/
|
|
164
|
+
(): Value;
|
|
165
|
+
/**
|
|
166
|
+
* Updates the signal value based on its previous value.
|
|
167
|
+
*
|
|
168
|
+
* @param updater - Function receiving the previous value and returning the next one.
|
|
169
|
+
*/
|
|
170
|
+
update(updater: (prev: Value) => Value): void;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Executes a function without tracking any dependencies.
|
|
175
|
+
* @param fn - The function to execute without tracking.
|
|
176
|
+
* @returns The result of the function execution.
|
|
177
|
+
*/
|
|
178
|
+
export declare const untracked: typeof Signal.subtle.untrack;
|
|
179
|
+
|
|
180
|
+
export { }
|
package/dist/signals.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_signals = require("./signals-C-55LNEj.cjs");
|
|
3
|
+
exports.computed = require_signals.computed;
|
|
4
|
+
exports.effect = require_signals.effect;
|
|
5
|
+
exports.input = require_signals.input;
|
|
6
|
+
exports.signal = require_signals.signal;
|
|
7
|
+
exports.untracked = require_signals.untracked;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_signals = require("./signals-C-55LNEj.cjs");
|
|
1
3
|
//#region ../packages/core/src/decorators/event.decorator.ts
|
|
2
4
|
function isEventOptions(value) {
|
|
3
5
|
return !!value && typeof value === "object" && ("bubbles" in value || "cancelable" in value || "composed" in value);
|
|
@@ -63,89 +65,6 @@ var INTERNAL_OBSERVED_ATTRIBUTES = `observedAttributes`;
|
|
|
63
65
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
64
66
|
var MATHML_NS = "http://www.w3.org/1998/Math/MathML";
|
|
65
67
|
//#endregion
|
|
66
|
-
//#region ../packages/core/src/signals/input/input-instance.symbol.ts
|
|
67
|
-
/**
|
|
68
|
-
* Unique symbol used to mark an object as a valid `InputSignal` instance.
|
|
69
|
-
*
|
|
70
|
-
* Set to `true` on every `InputSignal` created by the `input()` factory.
|
|
71
|
-
* Used by {@link isInputSignal} to distinguish input signals from plain
|
|
72
|
-
* `Signal.State` instances at runtime without exposing the marker in the
|
|
73
|
-
* public API.
|
|
74
|
-
*
|
|
75
|
-
* @internal
|
|
76
|
-
*/
|
|
77
|
-
var INPUT_SIGNAL_INSTANCE_SYMBOL = Symbol(`InputSignalInstance`);
|
|
78
|
-
/**
|
|
79
|
-
* Type guard that checks whether a given value is an `InputSignal` instance.
|
|
80
|
-
*
|
|
81
|
-
* Inspects the presence of {@link INPUT_SIGNAL_INSTANCE_SYMBOL} on the object,
|
|
82
|
-
* which is set to `true` by the `input()` factory for every valid instance.
|
|
83
|
-
*
|
|
84
|
-
* @param instance - The value to inspect.
|
|
85
|
-
* @returns `true` if `instance` is an `InputSignal`, `false` otherwise.
|
|
86
|
-
*/
|
|
87
|
-
function isInputSignal(instance) {
|
|
88
|
-
return instance?.[INPUT_SIGNAL_INSTANCE_SYMBOL];
|
|
89
|
-
}
|
|
90
|
-
//#endregion
|
|
91
|
-
//#region ../packages/core/src/signals/input/input-set.symbol.ts
|
|
92
|
-
/**
|
|
93
|
-
* This symbol is used to call the set method of the InputSignal
|
|
94
|
-
* Normally the set method is permitted internally and should not be called
|
|
95
|
-
* by the User
|
|
96
|
-
*/
|
|
97
|
-
var INPUT_SIGNAL_SET_SYMBOL = Symbol(`InputSignalSet`);
|
|
98
|
-
/**
|
|
99
|
-
* Asserts that the provided symbol matches the internal {@link INPUT_SIGNAL_SET_SYMBOL} symbol,
|
|
100
|
-
* ensuring the caller has access to internal APIs.
|
|
101
|
-
*
|
|
102
|
-
* Throws if the symbol does not match, preventing external code from
|
|
103
|
-
* invoking methods intended for internal use only.
|
|
104
|
-
*
|
|
105
|
-
* @param symbol - The symbol to validate against {@link INPUT_SIGNAL_SET_SYMBOL}.
|
|
106
|
-
* @throws {Error} If `symbol` does not match {@link INPUT_SIGNAL_SET_SYMBOL}.
|
|
107
|
-
* @internal
|
|
108
|
-
*/
|
|
109
|
-
function assertPrivateContext(symbol) {
|
|
110
|
-
if (symbol !== INPUT_SIGNAL_SET_SYMBOL) throw new Error("Invalid symbol for InputSignal set method");
|
|
111
|
-
}
|
|
112
|
-
//#endregion
|
|
113
|
-
//#region ../packages/core/src/signals/input/input.ts
|
|
114
|
-
/**
|
|
115
|
-
* Creates an `InputSignal` — a specialized reactive state designed for use
|
|
116
|
-
* as a property signal in web components.
|
|
117
|
-
*
|
|
118
|
-
* Unlike a plain `Signal.State`, the `set` method of an `InputSignal` is
|
|
119
|
-
* restricted to internal callers (identified by the private symbol) and
|
|
120
|
-
* accepts an optional `transform` function that converts incoming values
|
|
121
|
-
* (e.g. raw HTML attribute strings) into the internally stored type before
|
|
122
|
-
* updating the signal.
|
|
123
|
-
*
|
|
124
|
-
* @param value - The initial value of the signal.
|
|
125
|
-
* @param options - Optional configuration including an equality function,
|
|
126
|
-
* lifecycle hooks, and a `transform` function applied to incoming values.
|
|
127
|
-
* @returns A new `InputSignal` instance.
|
|
128
|
-
*/
|
|
129
|
-
function input(value, options) {
|
|
130
|
-
const transform = options?.transform;
|
|
131
|
-
delete options?.transform;
|
|
132
|
-
const signal = new Signal.State(value, options);
|
|
133
|
-
const originalSet = signal.set;
|
|
134
|
-
const getter = function() {
|
|
135
|
-
return signal.get();
|
|
136
|
-
};
|
|
137
|
-
Object.assign(getter, {
|
|
138
|
-
set(newValue, symbol) {
|
|
139
|
-
assertPrivateContext(symbol);
|
|
140
|
-
const transformedValue = transform ? transform(newValue) : newValue;
|
|
141
|
-
originalSet.call(signal, transformedValue);
|
|
142
|
-
},
|
|
143
|
-
get: signal.get.bind(signal)
|
|
144
|
-
});
|
|
145
|
-
getter[INPUT_SIGNAL_INSTANCE_SYMBOL] = true;
|
|
146
|
-
return getter;
|
|
147
|
-
}
|
|
148
|
-
//#endregion
|
|
149
68
|
//#region ../packages/core/src/decorators/property.decorator.ts
|
|
150
69
|
var propertyDecoratorOptionsWithRequiredBrand = Symbol("PropertyDecoratorOptionsWithRequiredBrand");
|
|
151
70
|
function createPropertyDecorator(value, options) {
|
|
@@ -161,7 +80,7 @@ function createPropertyDecorator(value, options) {
|
|
|
161
80
|
actualValue = value;
|
|
162
81
|
actualOptions = options;
|
|
163
82
|
} else actualOptions = value;
|
|
164
|
-
const signal = input(actualValue, {
|
|
83
|
+
const signal = require_signals.input(actualValue, {
|
|
165
84
|
equals: actualOptions?.equals,
|
|
166
85
|
watched: actualOptions?.watched,
|
|
167
86
|
unwatched: actualOptions?.unwatched,
|
|
@@ -338,8 +257,8 @@ var BaseWebComponent = class extends HTMLElement {
|
|
|
338
257
|
attributeChangedCallback(name, _oldValue, newValue) {
|
|
339
258
|
const context = this;
|
|
340
259
|
if (!(name in context)) throw new Error(`Attribute ${name} is not associated to any property`);
|
|
341
|
-
if (!isInputSignal(context[name])) throw new Error(`Property ${name} is not an InputSignal`);
|
|
342
|
-
context[name].set(newValue, INPUT_SIGNAL_SET_SYMBOL);
|
|
260
|
+
if (!require_signals.isInputSignal(context[name])) throw new Error(`Property ${name} is not an InputSignal`);
|
|
261
|
+
context[name].set(newValue, require_signals.INPUT_SIGNAL_SET_SYMBOL);
|
|
343
262
|
}
|
|
344
263
|
/**
|
|
345
264
|
* Called by the browser engine each time the element is inserted into the DOM.
|
|
@@ -362,142 +281,6 @@ var BaseWebComponent = class extends HTMLElement {
|
|
|
362
281
|
}
|
|
363
282
|
};
|
|
364
283
|
//#endregion
|
|
365
|
-
//#region ../packages/core/src/signals/computed/computed.ts
|
|
366
|
-
/**
|
|
367
|
-
* Creates a read-only computed signal whose value is derived from other signals.
|
|
368
|
-
*
|
|
369
|
-
* Returns a callable getter that reads the current value, augmented with the
|
|
370
|
-
* underlying {@link Signal.State} API.
|
|
371
|
-
*
|
|
372
|
-
* @template Value - The type of the computed value. Defaults to `any`.
|
|
373
|
-
* @param value - The initial (seed) value of the computed signal.
|
|
374
|
-
* @param options - Configuration options for the underlying signal.
|
|
375
|
-
* @returns A {@link Computed} instance.
|
|
376
|
-
*/
|
|
377
|
-
function computed(value, options) {
|
|
378
|
-
const signal = new Signal.State(value, options);
|
|
379
|
-
const getter = function() {
|
|
380
|
-
return signal.get();
|
|
381
|
-
};
|
|
382
|
-
Object.assign(getter, { get: signal.get.bind(signal) });
|
|
383
|
-
return getter;
|
|
384
|
-
}
|
|
385
|
-
//#endregion
|
|
386
|
-
//#region ../packages/core/src/signals/effect/effect.ts
|
|
387
|
-
/**
|
|
388
|
-
* Runs a side-effectful function and automatically re-runs it whenever any
|
|
389
|
-
* Signal read during its execution changes.
|
|
390
|
-
*
|
|
391
|
-
* Internally, `effect` wraps the user callback inside a `Computed` node
|
|
392
|
-
* (for dependency tracking) and observes it with a `Watcher` (for push
|
|
393
|
-
* notifications). When any tracked dependency changes, the `Watcher`
|
|
394
|
-
* schedules a microtask that re-evaluates the `Computed`, which in turn
|
|
395
|
-
* re-runs the user callback and re-registers the new set of dependencies.
|
|
396
|
-
*
|
|
397
|
-
* The returned disposer function stops the effect: it unwatches the internal
|
|
398
|
-
* `Computed` from the `Watcher`, severing all dependency subscriptions so
|
|
399
|
-
* the callback is never called again and the graph nodes can be
|
|
400
|
-
* garbage-collected.
|
|
401
|
-
*
|
|
402
|
-
* @example
|
|
403
|
-
* ```ts
|
|
404
|
-
* const count = new State(0);
|
|
405
|
-
*
|
|
406
|
-
* const stop = effect(() => {
|
|
407
|
-
* console.log('count is', count.get());
|
|
408
|
-
* });
|
|
409
|
-
* // logs: "count is 0"
|
|
410
|
-
*
|
|
411
|
-
* count.set(1); // logs: "count is 1"
|
|
412
|
-
* count.set(2); // logs: "count is 2"
|
|
413
|
-
*
|
|
414
|
-
* stop(); // no more logs
|
|
415
|
-
* count.set(3); // silent
|
|
416
|
-
* ```
|
|
417
|
-
*
|
|
418
|
-
* @param fn - The side-effectful function to run. Any Signal read inside it
|
|
419
|
-
* is tracked as a dependency.
|
|
420
|
-
* @returns A disposer function that, when called, permanently stops the effect.
|
|
421
|
-
*/
|
|
422
|
-
function effect(fn, options) {
|
|
423
|
-
/**
|
|
424
|
-
* Wrap the user callback in a Computed so that automatic dependency
|
|
425
|
-
* tracking (via pushComputed / popComputed) works for free.
|
|
426
|
-
* The Computed always returns `undefined` — we only care about the
|
|
427
|
-
* side-effects and the tracked sources, not the value.
|
|
428
|
-
*/
|
|
429
|
-
const computed = new Signal.Computed(() => fn());
|
|
430
|
-
let needsEnqueue = true;
|
|
431
|
-
/**
|
|
432
|
-
* The Watcher is notified synchronously as soon as any tracked dependency
|
|
433
|
-
* changes. Its job is purely to schedule the re-execution; the actual
|
|
434
|
-
* re-evaluation happens asynchronously in a microtask so that multiple
|
|
435
|
-
* synchronous signal updates are batched into a single re-run.
|
|
436
|
-
*/
|
|
437
|
-
const watcher = new Signal.subtle.Watcher(() => {
|
|
438
|
-
if (needsEnqueue) {
|
|
439
|
-
needsEnqueue = false;
|
|
440
|
-
queueMicrotask(() => {
|
|
441
|
-
needsEnqueue = true;
|
|
442
|
-
options?.onBeforeRun?.();
|
|
443
|
-
const pendings = watcher.getPending();
|
|
444
|
-
for (let i = 0; i < pendings.length; i++) pendings[i].get();
|
|
445
|
-
options?.onAfterRun?.();
|
|
446
|
-
watcher.watch();
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
});
|
|
450
|
-
options?.onBeforeRun?.();
|
|
451
|
-
watcher.watch(computed);
|
|
452
|
-
computed.get();
|
|
453
|
-
options?.onAfterRun?.();
|
|
454
|
-
/**
|
|
455
|
-
* Disposer — call this to permanently stop the effect.
|
|
456
|
-
*
|
|
457
|
-
* Unwatching the Computed tears down the entire live dependency chain
|
|
458
|
-
* (Watcher → Computed → all sources), preventing any further
|
|
459
|
-
* notifications and allowing GC.
|
|
460
|
-
*/
|
|
461
|
-
return () => {
|
|
462
|
-
options?.onCleanup?.();
|
|
463
|
-
watcher.unwatch(computed);
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
//#endregion
|
|
467
|
-
//#region ../packages/core/src/signals/signal/signal.ts
|
|
468
|
-
/**
|
|
469
|
-
* Creates a writable reactive signal.
|
|
470
|
-
*
|
|
471
|
-
* Returns a callable getter that reads the current value, augmented with the
|
|
472
|
-
* underlying {@link Signal.State} API and an `update` method to derive the next
|
|
473
|
-
* value from the previous one.
|
|
474
|
-
*
|
|
475
|
-
* @template T - The type of the stored value. Defaults to `any`.
|
|
476
|
-
* @param value - The initial value of the signal.
|
|
477
|
-
* @param options - Configuration options for the signal.
|
|
478
|
-
* @returns A {@link SignalType} instance.
|
|
479
|
-
*/
|
|
480
|
-
function signal(value, options) {
|
|
481
|
-
const signal = new Signal.State(value, options);
|
|
482
|
-
const getter = function() {
|
|
483
|
-
return signal.get();
|
|
484
|
-
};
|
|
485
|
-
Object.assign(getter, {
|
|
486
|
-
set: signal.set.bind(signal),
|
|
487
|
-
get: signal.get.bind(signal),
|
|
488
|
-
update: (updater) => signal.set(updater(signal.get()))
|
|
489
|
-
});
|
|
490
|
-
return getter;
|
|
491
|
-
}
|
|
492
|
-
//#endregion
|
|
493
|
-
//#region ../packages/core/src/signals/untracked.ts
|
|
494
|
-
/**
|
|
495
|
-
* Executes a function without tracking any dependencies.
|
|
496
|
-
* @param fn - The function to execute without tracking.
|
|
497
|
-
* @returns The result of the function execution.
|
|
498
|
-
*/
|
|
499
|
-
var untracked = Signal.subtle.untrack;
|
|
500
|
-
//#endregion
|
|
501
284
|
//#region ../packages/core/src/utils/context.util.ts
|
|
502
285
|
/**
|
|
503
286
|
* Tracks identifier scope during run time template function execution
|
|
@@ -719,11 +502,11 @@ function createAnchor(label, parentNode, context, referenceNode = null) {
|
|
|
719
502
|
function _for(parentNode, parentContext, condition, trackExpression, forFn) {
|
|
720
503
|
const anchor = createAnchor("for", parentNode, parentContext);
|
|
721
504
|
let entries = /* @__PURE__ */ new Map();
|
|
722
|
-
const unlistener = effect(() => {
|
|
505
|
+
const unlistener = require_signals.effect(() => {
|
|
723
506
|
const items = condition();
|
|
724
507
|
const newKeys = items.map((item) => trackExpression(item));
|
|
725
508
|
const newKeySet = new Set(newKeys);
|
|
726
|
-
untracked(() => {
|
|
509
|
+
require_signals.untracked(() => {
|
|
727
510
|
const newEntries = /* @__PURE__ */ new Map();
|
|
728
511
|
for (const [key, entry] of entries) if (!newKeySet.has(key)) {
|
|
729
512
|
entry.context.unlisten();
|
|
@@ -769,11 +552,11 @@ function _for(parentNode, parentContext, condition, trackExpression, forFn) {
|
|
|
769
552
|
* @returns A handle exposing the resolved variables and an `update` function.
|
|
770
553
|
*/
|
|
771
554
|
function _iterationVariables(context, items, index, itemName, aliases) {
|
|
772
|
-
const $index = signal(index);
|
|
773
|
-
const $first = signal(index === 0);
|
|
774
|
-
const $last = signal(index === items.length - 1);
|
|
775
|
-
const $even = signal(index % 2 === 0);
|
|
776
|
-
const $odd = signal(index % 2 !== 0);
|
|
555
|
+
const $index = require_signals.signal(index);
|
|
556
|
+
const $first = require_signals.signal(index === 0);
|
|
557
|
+
const $last = require_signals.signal(index === items.length - 1);
|
|
558
|
+
const $even = require_signals.signal(index % 2 === 0);
|
|
559
|
+
const $odd = require_signals.signal(index % 2 !== 0);
|
|
777
560
|
const retVal = {
|
|
778
561
|
vars: {
|
|
779
562
|
[itemName]: items[index],
|
|
@@ -831,7 +614,7 @@ function _if(parentNode, parentContext, blocks) {
|
|
|
831
614
|
break;
|
|
832
615
|
default: fn = (state) => handleIfElseIf(parentNode, parentContext, blocks, state, anchor);
|
|
833
616
|
}
|
|
834
|
-
const unlistener = effect(() => state = fn(state));
|
|
617
|
+
const unlistener = require_signals.effect(() => state = fn(state));
|
|
835
618
|
parentContext.listen(unlistener);
|
|
836
619
|
}
|
|
837
620
|
/**
|
|
@@ -915,7 +698,7 @@ function checkAndUpdateState(parentNode, parentContext, state, newState, conditi
|
|
|
915
698
|
state.context.unlisten();
|
|
916
699
|
parentContext.removeChild(state.context);
|
|
917
700
|
}
|
|
918
|
-
const context = untracked(() => conditionalBlockFn(parentNode, parentContext, anchor));
|
|
701
|
+
const context = require_signals.untracked(() => conditionalBlockFn(parentNode, parentContext, anchor));
|
|
919
702
|
parentContext.addChild(context);
|
|
920
703
|
return {
|
|
921
704
|
activeBranch: newState,
|
|
@@ -962,8 +745,8 @@ function _renderElement(parentNode, context, anchor, tagName, attributes, events
|
|
|
962
745
|
const element = context.createElement(tagName);
|
|
963
746
|
mountNode(element, parentNode, context, anchor);
|
|
964
747
|
for (let i = 0; i < attributes.length; i++) {
|
|
965
|
-
const { name, value,
|
|
966
|
-
|
|
748
|
+
const { name, value, reactive } = attributes[i];
|
|
749
|
+
reactive ? context.listen(require_signals.effect(() => element.setAttribute(name, String(value())))) : element.setAttribute(name, String(value()));
|
|
967
750
|
}
|
|
968
751
|
for (let i = 0; i < events.length; i++) {
|
|
969
752
|
const event = events[i];
|
|
@@ -1017,7 +800,7 @@ function createMATHMLElement(tagName) {
|
|
|
1017
800
|
function _renderText(parentNode, context, textFn) {
|
|
1018
801
|
const node = document.createTextNode(textFn());
|
|
1019
802
|
mountNode(node, parentNode, context);
|
|
1020
|
-
context.listen(effect(() => node.textContent = textFn()));
|
|
803
|
+
context.listen(require_signals.effect(() => node.textContent = textFn()));
|
|
1021
804
|
}
|
|
1022
805
|
/**
|
|
1023
806
|
* Creates a static (non-reactive) text node with a literal string value.
|
|
@@ -1065,4 +848,25 @@ function _switch(parentNode, parentContext, expression, blocks) {
|
|
|
1065
848
|
})));
|
|
1066
849
|
}
|
|
1067
850
|
//#endregion
|
|
1068
|
-
|
|
851
|
+
exports.BaseWebComponent = BaseWebComponent;
|
|
852
|
+
exports.Context = Context;
|
|
853
|
+
exports.Event = Event;
|
|
854
|
+
exports.Property = Property;
|
|
855
|
+
exports.WebComponent = WebComponent;
|
|
856
|
+
exports._for = _for;
|
|
857
|
+
exports._if = _if;
|
|
858
|
+
exports._iterationVariables = _iterationVariables;
|
|
859
|
+
exports._renderElement = _renderElement;
|
|
860
|
+
exports._renderLiteralText = _renderLiteralText;
|
|
861
|
+
exports._renderText = _renderText;
|
|
862
|
+
exports._switch = _switch;
|
|
863
|
+
exports.computed = require_signals.computed;
|
|
864
|
+
exports.createAnchor = createAnchor;
|
|
865
|
+
exports.createElement = createElement;
|
|
866
|
+
exports.createMATHMLElement = createMATHMLElement;
|
|
867
|
+
exports.createSVGElement = createSVGElement;
|
|
868
|
+
exports.effect = require_signals.effect;
|
|
869
|
+
exports.input = require_signals.input;
|
|
870
|
+
exports.mountNode = mountNode;
|
|
871
|
+
exports.signal = require_signals.signal;
|
|
872
|
+
exports.untracked = require_signals.untracked;
|
package/package.json
CHANGED
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xaendar/core",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.22",
|
|
4
4
|
"description": "A library containing core utils such as webcomponent base classes and theming support",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"type": "module",
|
|
7
|
-
"main": "./dist/xaendar-core.
|
|
8
|
-
"module": "./dist/xaendar-core.
|
|
9
|
-
"types": "./dist/xaendar-core.
|
|
7
|
+
"main": "./dist/xaendar-core.js",
|
|
8
|
+
"module": "./dist/xaendar-core.js",
|
|
9
|
+
"types": "./dist/xaendar-core.d.ts",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
12
12
|
"import": {
|
|
13
|
-
"types": "./dist/xaendar-core.
|
|
14
|
-
"default": "./dist/xaendar-core.
|
|
13
|
+
"types": "./dist/xaendar-core.d.ts",
|
|
14
|
+
"default": "./dist/xaendar-core.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"signals": {
|
|
18
|
+
"import": {
|
|
19
|
+
"types": "./dist/signals.d.ts",
|
|
20
|
+
"default": "./dist/signals.js"
|
|
15
21
|
}
|
|
16
22
|
}
|
|
17
23
|
},
|
|
18
24
|
"dependencies": {
|
|
19
|
-
"@xaendar/signals": "0.9.
|
|
20
|
-
"@xaendar/types": "0.9.
|
|
25
|
+
"@xaendar/signals": "0.9.22",
|
|
26
|
+
"@xaendar/types": "0.9.22"
|
|
21
27
|
}
|
|
22
28
|
}
|