@planet-matrix/mobius-model 0.1.3 → 0.3.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 +54 -0
- package/README.md +21 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +12 -6
- package/dist/reactor/index.d.ts +3 -0
- package/dist/reactor/index.d.ts.map +1 -0
- package/dist/reactor/reactor-core/flags.d.ts.map +1 -0
- package/dist/reactor/reactor-core/index.d.ts.map +1 -0
- package/dist/reactor/reactor-core/primitive.d.ts +276 -0
- package/dist/reactor/reactor-core/primitive.d.ts.map +1 -0
- package/dist/{signal/signal-core → reactor/reactor-core}/reactive-system.d.ts +102 -22
- package/dist/reactor/reactor-core/reactive-system.d.ts.map +1 -0
- package/dist/reactor/reactor-operators/branch.d.ts +19 -0
- package/dist/reactor/reactor-operators/branch.d.ts.map +1 -0
- package/dist/reactor/reactor-operators/convert.d.ts +30 -0
- package/dist/reactor/reactor-operators/convert.d.ts.map +1 -0
- package/dist/reactor/reactor-operators/create.d.ts +26 -0
- package/dist/reactor/reactor-operators/create.d.ts.map +1 -0
- package/dist/reactor/reactor-operators/filter.d.ts +269 -0
- package/dist/reactor/reactor-operators/filter.d.ts.map +1 -0
- package/dist/reactor/reactor-operators/index.d.ts +8 -0
- package/dist/reactor/reactor-operators/index.d.ts.map +1 -0
- package/dist/reactor/reactor-operators/join.d.ts +48 -0
- package/dist/reactor/reactor-operators/join.d.ts.map +1 -0
- package/dist/reactor/reactor-operators/map.d.ts +165 -0
- package/dist/reactor/reactor-operators/map.d.ts.map +1 -0
- package/dist/reactor/reactor-operators/utility.d.ts +48 -0
- package/dist/reactor/reactor-operators/utility.d.ts.map +1 -0
- package/package.json +9 -12
- package/src/index.ts +1 -1
- package/src/reactor/README.md +18 -0
- package/src/reactor/index.ts +2 -0
- package/src/reactor/reactor-core/primitive.ts +1046 -0
- package/src/{signal/signal-core → reactor/reactor-core}/reactive-system.ts +392 -93
- package/src/reactor/reactor-operators/branch.ts +66 -0
- package/src/reactor/reactor-operators/convert.ts +70 -0
- package/src/reactor/reactor-operators/create.ts +66 -0
- package/src/reactor/reactor-operators/filter.ts +988 -0
- package/src/reactor/reactor-operators/index.ts +7 -0
- package/src/reactor/reactor-operators/join.ts +174 -0
- package/src/reactor/reactor-operators/map.ts +599 -0
- package/src/reactor/reactor-operators/utility.ts +102 -0
- package/tests/unit/{signal/computed.spec.ts → reactor/alien-signals-computed.spec.ts} +15 -10
- package/tests/unit/reactor/alien-signals-effect-scope.spec.ts +86 -0
- package/tests/unit/reactor/alien-signals-effect.spec.ts +395 -0
- package/tests/unit/reactor/alien-signals-topology.spec.ts +361 -0
- package/tests/unit/reactor/alien-signals-trigger.spec.ts +75 -0
- package/tests/unit/reactor/alien-signals-untrack.spec.ts +91 -0
- package/tests/unit/reactor/preact-signal.spec.ts +73 -0
- package/tests/unit/reactor/reactor-core.spec.ts +219 -0
- package/tests/unit/reactor/reactor-operators-branch.spec.ts +33 -0
- package/tests/unit/reactor/reactor-operators-convert.spec.ts +31 -0
- package/tests/unit/reactor/reactor-operators-create.spec.ts +47 -0
- package/tests/unit/reactor/reactor-operators-filter.spec.ts +604 -0
- package/tests/unit/reactor/reactor-operators-join.spec.ts +94 -0
- package/tests/unit/reactor/reactor-operators-map.spec.ts +327 -0
- package/tests/unit/reactor/reactor-operators-utility.spec.ts +55 -0
- package/dist/signal/index.d.ts +0 -3
- package/dist/signal/index.d.ts.map +0 -1
- package/dist/signal/signal-core/flags.d.ts.map +0 -1
- package/dist/signal/signal-core/index.d.ts.map +0 -1
- package/dist/signal/signal-core/primitive.d.ts +0 -67
- package/dist/signal/signal-core/primitive.d.ts.map +0 -1
- package/dist/signal/signal-core/reactive-system.d.ts.map +0 -1
- package/dist/signal/signal-operators/index.d.ts +0 -4
- package/dist/signal/signal-operators/index.d.ts.map +0 -1
- package/src/signal/index.ts +0 -2
- package/src/signal/signal-core/README.md +0 -4
- package/src/signal/signal-core/primitive.ts +0 -275
- package/src/signal/signal-operators/index.ts +0 -19
- package/tests/unit/signal/effect.spec.ts +0 -108
- /package/dist/{signal/signal-core → reactor/reactor-core}/flags.d.ts +0 -0
- /package/dist/{signal/signal-core → reactor/reactor-core}/index.d.ts +0 -0
- /package/src/{signal/signal-core → reactor/reactor-core}/flags.ts +0 -0
- /package/src/{signal/signal-core → reactor/reactor-core}/index.ts +0 -0
|
@@ -0,0 +1,988 @@
|
|
|
1
|
+
import type { Signal, SignalValueInitializer, ValueReactor } from "../reactor-core/index.ts"
|
|
2
|
+
|
|
3
|
+
import { castValueInitializer } from "./utility.ts"
|
|
4
|
+
import { effect, signal } from "../reactor-core/index.ts"
|
|
5
|
+
|
|
6
|
+
export interface CurrentOptions<V> {
|
|
7
|
+
target: ValueReactor<V>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Returns a Signal that holds the current value of the target ValueReactor.
|
|
11
|
+
*/
|
|
12
|
+
export const current = <V>(
|
|
13
|
+
options: CurrentOptions<V>
|
|
14
|
+
): Signal<V> => {
|
|
15
|
+
const { target } = options;
|
|
16
|
+
|
|
17
|
+
const result = signal<V>(() => {
|
|
18
|
+
return target.get()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
return result
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface NextOptions<V> {
|
|
25
|
+
target: ValueReactor<V>;
|
|
26
|
+
valueInitializer: SignalValueInitializer<V>
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Returns a Signal that holds the next value of the target ValueReactor
|
|
30
|
+
* after the initial value.
|
|
31
|
+
*
|
|
32
|
+
* The Signal will update its value only once, upon the next change of
|
|
33
|
+
* the target ValueReactor, and then it will not update anymore.
|
|
34
|
+
*/
|
|
35
|
+
export const next = <V>(
|
|
36
|
+
options: NextOptions<V>
|
|
37
|
+
): Signal<V> => {
|
|
38
|
+
const { target, valueInitializer } = options;
|
|
39
|
+
|
|
40
|
+
const result = signal(valueInitializer, {
|
|
41
|
+
onDispose: () => {
|
|
42
|
+
eTarget.dispose()
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const eTarget = effect((context) => {
|
|
47
|
+
const { isInitializingRun } = context
|
|
48
|
+
|
|
49
|
+
const value = target.get()
|
|
50
|
+
if (isInitializingRun === false) {
|
|
51
|
+
result.set(value)
|
|
52
|
+
eTarget.dispose()
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
return result
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface FilterInitializingFalsyValueGetterContext<V> {
|
|
60
|
+
isInitializingRun: true;
|
|
61
|
+
targetValue: V
|
|
62
|
+
}
|
|
63
|
+
export interface FilterUpdatingFalsyValueGetterContext<V> {
|
|
64
|
+
isInitializingRun: false;
|
|
65
|
+
targetValue: V
|
|
66
|
+
previousValue: V
|
|
67
|
+
}
|
|
68
|
+
export type FilterFalsyValueGetterContextOf<V> =
|
|
69
|
+
| FilterInitializingFalsyValueGetterContext<V>
|
|
70
|
+
| FilterUpdatingFalsyValueGetterContext<V>
|
|
71
|
+
export interface FilterOptions<V> {
|
|
72
|
+
target: ValueReactor<V>;
|
|
73
|
+
truthyPredicate: (value: V) => boolean;
|
|
74
|
+
falsyValueGetter: (context: FilterFalsyValueGetterContextOf<V>) => V;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Returns a Signal that only updates its value from the target
|
|
78
|
+
* ValueReactor when the predicate function returns true.
|
|
79
|
+
*/
|
|
80
|
+
export const filter = <V>(
|
|
81
|
+
options: FilterOptions<V>
|
|
82
|
+
): Signal<V> => {
|
|
83
|
+
const { target, truthyPredicate, falsyValueGetter } = options;
|
|
84
|
+
|
|
85
|
+
const result = signal<V>(castValueInitializer<V>(), {
|
|
86
|
+
onDispose: () => {
|
|
87
|
+
eTarget.dispose()
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
const eTarget = effect((context) => {
|
|
92
|
+
const value = target.get()
|
|
93
|
+
if (truthyPredicate(value) === true) {
|
|
94
|
+
result.set(value)
|
|
95
|
+
} else {
|
|
96
|
+
const { isInitializingRun } = context
|
|
97
|
+
if (isInitializingRun === true) {
|
|
98
|
+
const fallbackValue = falsyValueGetter({
|
|
99
|
+
isInitializingRun,
|
|
100
|
+
targetValue: value
|
|
101
|
+
})
|
|
102
|
+
result.set(fallbackValue)
|
|
103
|
+
} else {
|
|
104
|
+
const fallbackValue = falsyValueGetter({
|
|
105
|
+
isInitializingRun,
|
|
106
|
+
targetValue: value,
|
|
107
|
+
previousValue: result.getWithoutTrack()
|
|
108
|
+
})
|
|
109
|
+
result.set(fallbackValue)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
return result
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface AuditByCountOptions<V> {
|
|
118
|
+
target: ValueReactor<V>;
|
|
119
|
+
count: number;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Returns a Signal that updates its value with the latest value from
|
|
123
|
+
* the target ValueReactor after every specified count of emissions.
|
|
124
|
+
*/
|
|
125
|
+
export const auditByCount = <V>(
|
|
126
|
+
options: AuditByCountOptions<V>
|
|
127
|
+
): Signal<V> => {
|
|
128
|
+
const { target, count } = options;
|
|
129
|
+
|
|
130
|
+
if (count <= 0) {
|
|
131
|
+
throw new Error("auditByCount operator requires count to be greater than 0")
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
const result = signal(castValueInitializer<V>(), {
|
|
136
|
+
onDispose: () => {
|
|
137
|
+
eTarget.dispose()
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
let emissionCount = 0
|
|
142
|
+
const eTarget = effect((context) => {
|
|
143
|
+
const latestValue = target.get()
|
|
144
|
+
|
|
145
|
+
if (context.isInitializingRun === true) {
|
|
146
|
+
result.set(latestValue)
|
|
147
|
+
} else {
|
|
148
|
+
emissionCount = emissionCount + 1
|
|
149
|
+
if (emissionCount >= count) {
|
|
150
|
+
result.set(latestValue)
|
|
151
|
+
emissionCount = 0
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
return result
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface AuditByTimeOptions<V> {
|
|
160
|
+
target: ValueReactor<V>;
|
|
161
|
+
time: number;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Returns a Signal that updates its value with the latest value from
|
|
165
|
+
* the target ValueReactor at specified time intervals.
|
|
166
|
+
*/
|
|
167
|
+
export const auditByTime = <V>(
|
|
168
|
+
options: AuditByTimeOptions<V>
|
|
169
|
+
): Signal<V> => {
|
|
170
|
+
const { target, time } = options;
|
|
171
|
+
|
|
172
|
+
if (time <= 0) {
|
|
173
|
+
throw new Error("auditByTime operator requires timeInMilliseconds to be greater than 0")
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const result = signal<V>(() => target.getWithoutTrack(), {
|
|
177
|
+
onDispose: () => {
|
|
178
|
+
clearInterval(intervalId)
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
const intervalId = setInterval(() => {
|
|
183
|
+
result.set(target.getWithoutTrack())
|
|
184
|
+
}, time)
|
|
185
|
+
|
|
186
|
+
return result
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export interface AuditByTriggerOptions<VTarget, VTrigger> {
|
|
190
|
+
target: ValueReactor<VTarget>;
|
|
191
|
+
trigger: ValueReactor<VTrigger>;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Returns a Signal that only updates its value when the trigger
|
|
195
|
+
* ValueReactor emits a value, using the latest value from the target
|
|
196
|
+
* ValueReactor.
|
|
197
|
+
*/
|
|
198
|
+
export const auditByTrigger = <VTarget, VTrigger>(
|
|
199
|
+
options: AuditByTriggerOptions<VTarget, VTrigger>
|
|
200
|
+
): Signal<VTarget> => {
|
|
201
|
+
const { target, trigger } = options;
|
|
202
|
+
|
|
203
|
+
const result = signal<VTarget>(castValueInitializer<VTarget>(), {
|
|
204
|
+
onDispose: () => {
|
|
205
|
+
eTrigger.dispose()
|
|
206
|
+
}
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
const eTrigger = effect(() => {
|
|
210
|
+
trigger.get()
|
|
211
|
+
result.set(target.getWithoutTrack())
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
return result
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface AuditByToggleOptions<VTarget, VOpen, VClose> {
|
|
218
|
+
target: ValueReactor<VTarget>;
|
|
219
|
+
open: ValueReactor<VOpen>;
|
|
220
|
+
close: ValueReactor<VClose>;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Returns a Signal that updates its value with the latest value from
|
|
224
|
+
* the target ValueReactor when the close ValueReactor emits, but only
|
|
225
|
+
* if auditing was started by the open ValueReactor.
|
|
226
|
+
*/
|
|
227
|
+
export const auditByToggle = <VTarget, VOpen, VClose>(
|
|
228
|
+
options: AuditByToggleOptions<VTarget, VOpen, VClose>
|
|
229
|
+
): Signal<VTarget> => {
|
|
230
|
+
const { target, open, close } = options;
|
|
231
|
+
|
|
232
|
+
const result = signal(castValueInitializer<VTarget>(), {
|
|
233
|
+
onDispose: () => {
|
|
234
|
+
eTarget.dispose()
|
|
235
|
+
eOpen.dispose()
|
|
236
|
+
eClose.dispose()
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
let auditValue: VTarget | undefined = undefined
|
|
241
|
+
let auditing = false
|
|
242
|
+
|
|
243
|
+
const eOpen = effect(() => {
|
|
244
|
+
open.get()
|
|
245
|
+
auditing = true
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
const eTarget = effect(() => {
|
|
249
|
+
const value = target.get()
|
|
250
|
+
if (auditing === true) {
|
|
251
|
+
auditValue = value
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
const eClose = effect(() => {
|
|
256
|
+
close.get()
|
|
257
|
+
if (auditing === true) {
|
|
258
|
+
if (auditValue !== undefined) {
|
|
259
|
+
result.set(auditValue)
|
|
260
|
+
}
|
|
261
|
+
auditing = false
|
|
262
|
+
auditValue = undefined
|
|
263
|
+
}
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
return result
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export interface AuditByDynamicOptions<VTarget, VDynamic> {
|
|
270
|
+
target: ValueReactor<VTarget>;
|
|
271
|
+
dynamic: (value: VTarget) => ValueReactor<VDynamic>;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Returns a Signal that updates its value with the latest value from
|
|
275
|
+
* the target ValueReactor when the ValueReactor returned by the `dynamic`
|
|
276
|
+
* function emits. The `dynamic` function is called each time after emitting
|
|
277
|
+
* to get a new ValueReactor for the next audit period.
|
|
278
|
+
*/
|
|
279
|
+
export const auditByDynamic = <VTarget, VDynamic>(
|
|
280
|
+
options: AuditByDynamicOptions<VTarget, VDynamic>
|
|
281
|
+
): Signal<VTarget> => {
|
|
282
|
+
const { target, dynamic } = options;
|
|
283
|
+
|
|
284
|
+
const result = signal(castValueInitializer<VTarget>(), {
|
|
285
|
+
onDispose: () => {
|
|
286
|
+
eDynamic.dispose()
|
|
287
|
+
}
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
let trigger: ValueReactor<VDynamic> | undefined = undefined
|
|
291
|
+
const eDynamic = effect(() => {
|
|
292
|
+
trigger?.dispose()
|
|
293
|
+
const targetValue = target.getWithoutTrack()
|
|
294
|
+
trigger = dynamic(targetValue)
|
|
295
|
+
trigger.get()
|
|
296
|
+
result.set(targetValue)
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
return result
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export interface DebounceByCountOptions<V> {
|
|
303
|
+
target: ValueReactor<V>;
|
|
304
|
+
count: number;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Returns a Signal that updates its value with the latest value from
|
|
308
|
+
* the target ValueReactor after the specified count of emissions
|
|
309
|
+
* without being updated.
|
|
310
|
+
*/
|
|
311
|
+
export const debounceByCount = <V>(
|
|
312
|
+
options: DebounceByCountOptions<V>
|
|
313
|
+
): Signal<V> => {
|
|
314
|
+
const { target, count } = options;
|
|
315
|
+
|
|
316
|
+
if (count <= 0) {
|
|
317
|
+
throw new Error("debounceByCount operator requires count to be greater than 0")
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const result = signal(castValueInitializer<V>(), {
|
|
321
|
+
onDispose: () => {
|
|
322
|
+
eTarget.dispose()
|
|
323
|
+
}
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
let emissionCount = 0
|
|
327
|
+
const eTarget = effect((context) => {
|
|
328
|
+
const latestValue = target.get()
|
|
329
|
+
|
|
330
|
+
if (context.isInitializingRun === true) {
|
|
331
|
+
result.set(latestValue)
|
|
332
|
+
} else {
|
|
333
|
+
emissionCount = emissionCount + 1
|
|
334
|
+
if (emissionCount >= count) {
|
|
335
|
+
result.set(latestValue)
|
|
336
|
+
emissionCount = 0
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
return result
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export interface DebounceByTimeOptions<V> {
|
|
345
|
+
target: ValueReactor<V>;
|
|
346
|
+
time: number;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Returns a Signal that updates its value with the latest value from
|
|
350
|
+
* the target ValueReactor after the specified time in milliseconds
|
|
351
|
+
* has passed since the last update.
|
|
352
|
+
*/
|
|
353
|
+
export const debounceByTime = <V>(
|
|
354
|
+
options: DebounceByTimeOptions<V>
|
|
355
|
+
): Signal<V> => {
|
|
356
|
+
const { target, time } = options;
|
|
357
|
+
|
|
358
|
+
if (time <= 0) {
|
|
359
|
+
throw new Error("debounceTime operator requires timeInMilliseconds to be greater than 0")
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined = undefined
|
|
363
|
+
|
|
364
|
+
const result = signal<V>(castValueInitializer<V>(), {
|
|
365
|
+
onDispose: () => {
|
|
366
|
+
eTarget.dispose()
|
|
367
|
+
clearTimeout(timeoutId)
|
|
368
|
+
}
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
const eTarget = effect((context) => {
|
|
372
|
+
const latestValue = target.get()
|
|
373
|
+
|
|
374
|
+
if (context.isInitializingRun === true) {
|
|
375
|
+
result.set(latestValue)
|
|
376
|
+
} else {
|
|
377
|
+
clearTimeout(timeoutId)
|
|
378
|
+
timeoutId = setTimeout(() => {
|
|
379
|
+
result.set(latestValue)
|
|
380
|
+
timeoutId = undefined
|
|
381
|
+
}, time)
|
|
382
|
+
}
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
return result
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export interface DebounceByTriggerOptions<VTarget, VTrigger> {
|
|
389
|
+
target: ValueReactor<VTarget>;
|
|
390
|
+
trigger: ValueReactor<VTrigger>;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Returns a Signal that updates its value with the latest value from
|
|
394
|
+
* the target ValueReactor whenever the trigger ValueReactor
|
|
395
|
+
* emits a value.
|
|
396
|
+
*/
|
|
397
|
+
export const debounceByTrigger = <VTarget, VTrigger>(
|
|
398
|
+
options: DebounceByTriggerOptions<VTarget, VTrigger>
|
|
399
|
+
): Signal<VTarget> => {
|
|
400
|
+
const { target, trigger } = options;
|
|
401
|
+
|
|
402
|
+
const result = signal<VTarget>(castValueInitializer<VTarget>(), {
|
|
403
|
+
onDispose: () => {
|
|
404
|
+
eTrigger.dispose()
|
|
405
|
+
}
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
const eTrigger = effect(() => {
|
|
409
|
+
trigger.get()
|
|
410
|
+
result.set(target.getWithoutTrack())
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
return result
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export interface DebounceByToggleOptions<VTarget, VOpen, VClose> {
|
|
417
|
+
target: ValueReactor<VTarget>;
|
|
418
|
+
open: ValueReactor<VOpen>;
|
|
419
|
+
close: ValueReactor<VClose>;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Returns a Signal that updates its value with the latest value from
|
|
423
|
+
* the target ValueReactor when the close ValueReactor emits, but only
|
|
424
|
+
* if debouncing was started by the open ValueReactor.
|
|
425
|
+
*/
|
|
426
|
+
export const debounceByToggle = <VTarget, VOpen, VClose>(
|
|
427
|
+
options: DebounceByToggleOptions<VTarget, VOpen, VClose>
|
|
428
|
+
): Signal<VTarget> => {
|
|
429
|
+
const { target, open, close } = options;
|
|
430
|
+
|
|
431
|
+
const result = signal(castValueInitializer<VTarget>(), {
|
|
432
|
+
onDispose: () => {
|
|
433
|
+
eTarget.dispose()
|
|
434
|
+
eOpen.dispose()
|
|
435
|
+
eClose.dispose()
|
|
436
|
+
}
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
let debounceValue: VTarget | undefined = undefined
|
|
440
|
+
let debouncing = false
|
|
441
|
+
|
|
442
|
+
const eOpen = effect(() => {
|
|
443
|
+
open.get()
|
|
444
|
+
debouncing = true
|
|
445
|
+
})
|
|
446
|
+
|
|
447
|
+
const eTarget = effect(() => {
|
|
448
|
+
const value = target.get()
|
|
449
|
+
if (debouncing === true) {
|
|
450
|
+
debounceValue = value
|
|
451
|
+
}
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
const eClose = effect(() => {
|
|
455
|
+
close.get()
|
|
456
|
+
if (debouncing === true) {
|
|
457
|
+
if (debounceValue !== undefined) {
|
|
458
|
+
result.set(debounceValue)
|
|
459
|
+
}
|
|
460
|
+
debouncing = false
|
|
461
|
+
debounceValue = undefined
|
|
462
|
+
}
|
|
463
|
+
})
|
|
464
|
+
|
|
465
|
+
return result
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export interface DebounceByDynamicOptions<VTarget, VDynamic> {
|
|
469
|
+
target: ValueReactor<VTarget>;
|
|
470
|
+
dynamic: (value: VTarget) => ValueReactor<VDynamic>;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Returns a Signal that updates its value with the latest value from
|
|
474
|
+
* the target ValueReactor when the ValueReactor returned by the `dynamic`
|
|
475
|
+
* function emits. The `dynamic` function is called each time after emitting
|
|
476
|
+
* to get a new ValueReactor for the next debounce period.
|
|
477
|
+
*/
|
|
478
|
+
export const debounceByDynamic = <VTarget, VDynamic>(
|
|
479
|
+
options: DebounceByDynamicOptions<VTarget, VDynamic>
|
|
480
|
+
): Signal<VTarget> => {
|
|
481
|
+
const { target, dynamic } = options;
|
|
482
|
+
|
|
483
|
+
const result = signal(castValueInitializer<VTarget>(), {
|
|
484
|
+
onDispose: () => {
|
|
485
|
+
eDynamic.dispose()
|
|
486
|
+
}
|
|
487
|
+
})
|
|
488
|
+
|
|
489
|
+
let trigger: ValueReactor<VDynamic> | undefined = undefined
|
|
490
|
+
const eDynamic = effect(() => {
|
|
491
|
+
trigger?.dispose()
|
|
492
|
+
const targetValue = target.getWithoutTrack()
|
|
493
|
+
trigger = dynamic(targetValue)
|
|
494
|
+
trigger.get()
|
|
495
|
+
result.set(targetValue)
|
|
496
|
+
})
|
|
497
|
+
|
|
498
|
+
return result
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
export interface ThrottleByCountOptions<V> {
|
|
502
|
+
target: ValueReactor<V>;
|
|
503
|
+
count: number;
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Returns a Signal that updates its value with the latest value from
|
|
507
|
+
* the target ValueReactor at most once every specified count of emissions.
|
|
508
|
+
*/
|
|
509
|
+
export const throttleByCount = <V>(
|
|
510
|
+
options: ThrottleByCountOptions<V>
|
|
511
|
+
): Signal<V> => {
|
|
512
|
+
const { target, count } = options;
|
|
513
|
+
|
|
514
|
+
if (count <= 0) {
|
|
515
|
+
throw new Error("throttleByCount operator requires count to be greater than 0")
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const result = signal(castValueInitializer<V>(), {
|
|
519
|
+
onDispose: () => {
|
|
520
|
+
eTarget.dispose()
|
|
521
|
+
}
|
|
522
|
+
})
|
|
523
|
+
|
|
524
|
+
let emissionCount = 0
|
|
525
|
+
const eTarget = effect(() => {
|
|
526
|
+
const latestValue = target.get()
|
|
527
|
+
if (emissionCount === 0) {
|
|
528
|
+
result.set(latestValue)
|
|
529
|
+
}
|
|
530
|
+
emissionCount = emissionCount + 1
|
|
531
|
+
if (emissionCount >= count) {
|
|
532
|
+
emissionCount = 0
|
|
533
|
+
}
|
|
534
|
+
})
|
|
535
|
+
|
|
536
|
+
return result
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
export interface ThrottleByTimeOptions<V> {
|
|
540
|
+
target: ValueReactor<V>;
|
|
541
|
+
time: number;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Returns a Signal that updates its value with the latest value from
|
|
545
|
+
* the target ValueReactor at most once every specified time in milliseconds.
|
|
546
|
+
*/
|
|
547
|
+
export const throttleByTime = <V>(
|
|
548
|
+
options: ThrottleByTimeOptions<V>
|
|
549
|
+
): Signal<V> => {
|
|
550
|
+
const { target, time } = options;
|
|
551
|
+
|
|
552
|
+
if (time <= 0) {
|
|
553
|
+
throw new Error("throttleByTime operator requires timeInMilliseconds to be greater than 0")
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const result = signal<V>(castValueInitializer<V>(), {
|
|
557
|
+
onDispose: () => {
|
|
558
|
+
eTarget.dispose()
|
|
559
|
+
}
|
|
560
|
+
})
|
|
561
|
+
|
|
562
|
+
let canEmit = true
|
|
563
|
+
const eTarget = effect(() => {
|
|
564
|
+
const latestValue = target.get()
|
|
565
|
+
|
|
566
|
+
if (canEmit === true) {
|
|
567
|
+
result.set(latestValue)
|
|
568
|
+
canEmit = false
|
|
569
|
+
setTimeout(() => {
|
|
570
|
+
canEmit = true
|
|
571
|
+
}, time)
|
|
572
|
+
}
|
|
573
|
+
})
|
|
574
|
+
|
|
575
|
+
return result
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
export interface ThrottleByTriggerOptions<VTarget, VTrigger> {
|
|
579
|
+
target: ValueReactor<VTarget>;
|
|
580
|
+
trigger: ValueReactor<VTrigger>;
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Returns a Signal that updates its value with the latest value from
|
|
584
|
+
* the target ValueReactor at most once whenever the trigger
|
|
585
|
+
* ValueReactor emits.
|
|
586
|
+
*/
|
|
587
|
+
export const throttleByTrigger = <VTarget, VTrigger>(
|
|
588
|
+
options: ThrottleByTriggerOptions<VTarget, VTrigger>
|
|
589
|
+
): Signal<VTarget> => {
|
|
590
|
+
const { target, trigger } = options;
|
|
591
|
+
|
|
592
|
+
const result = signal<VTarget>(castValueInitializer<VTarget>(), {
|
|
593
|
+
onDispose: () => {
|
|
594
|
+
eTrigger.dispose()
|
|
595
|
+
eTarget.dispose()
|
|
596
|
+
}
|
|
597
|
+
})
|
|
598
|
+
|
|
599
|
+
let canEmit = false
|
|
600
|
+
const eTrigger = effect(() => {
|
|
601
|
+
trigger.get()
|
|
602
|
+
canEmit = true
|
|
603
|
+
})
|
|
604
|
+
const eTarget = effect(() => {
|
|
605
|
+
const latestValue = target.get()
|
|
606
|
+
if (canEmit === true) {
|
|
607
|
+
result.set(latestValue)
|
|
608
|
+
canEmit = false
|
|
609
|
+
}
|
|
610
|
+
})
|
|
611
|
+
|
|
612
|
+
return result
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
export interface ThrottleByToggleOptions<VTarget, VOpen, VClose> {
|
|
616
|
+
target: ValueReactor<VTarget>;
|
|
617
|
+
open: ValueReactor<VOpen>;
|
|
618
|
+
close: ValueReactor<VClose>;
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Returns a Signal that updates its value with the latest value from
|
|
622
|
+
* the target ValueReactor when the close ValueReactor emits, but only
|
|
623
|
+
* if throttling was started by the open ValueReactor.
|
|
624
|
+
*/
|
|
625
|
+
export const throttleByToggle = <VTarget, VOpen, VClose>(
|
|
626
|
+
options: ThrottleByToggleOptions<VTarget, VOpen, VClose>
|
|
627
|
+
): Signal<VTarget> => {
|
|
628
|
+
const { target, open, close } = options;
|
|
629
|
+
|
|
630
|
+
const result = signal(castValueInitializer<VTarget>(), {
|
|
631
|
+
onDispose: () => {
|
|
632
|
+
eTarget.dispose()
|
|
633
|
+
eOpen.dispose()
|
|
634
|
+
eClose.dispose()
|
|
635
|
+
}
|
|
636
|
+
})
|
|
637
|
+
|
|
638
|
+
let throttling = false
|
|
639
|
+
|
|
640
|
+
const eOpen = effect(() => {
|
|
641
|
+
open.get()
|
|
642
|
+
throttling = true
|
|
643
|
+
})
|
|
644
|
+
|
|
645
|
+
const eTarget = effect(() => {
|
|
646
|
+
const value = target.get()
|
|
647
|
+
if (throttling === true) {
|
|
648
|
+
result.set(value)
|
|
649
|
+
throttling = false
|
|
650
|
+
}
|
|
651
|
+
})
|
|
652
|
+
|
|
653
|
+
const eClose = effect(() => {
|
|
654
|
+
close.get()
|
|
655
|
+
throttling = false
|
|
656
|
+
})
|
|
657
|
+
|
|
658
|
+
return result
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
export interface ThrottleByDynamicOptions<VTarget, VDynamic> {
|
|
662
|
+
target: ValueReactor<VTarget>;
|
|
663
|
+
dynamic: (value: VTarget) => ValueReactor<VDynamic>;
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Returns a Signal that updates its value with the latest value from
|
|
667
|
+
* the target ValueReactor when the ValueReactor returned by the `dynamic`
|
|
668
|
+
* function emits. The `dynamic` function is called each time after emitting
|
|
669
|
+
* to get a new ValueReactor for the next throttle period.
|
|
670
|
+
*/
|
|
671
|
+
export const throttleByDynamic = <VTarget, VDynamic>(
|
|
672
|
+
options: ThrottleByDynamicOptions<VTarget, VDynamic>
|
|
673
|
+
): Signal<VTarget> => {
|
|
674
|
+
const { target, dynamic } = options;
|
|
675
|
+
|
|
676
|
+
const result = signal(castValueInitializer<VTarget>(), {
|
|
677
|
+
onDispose: () => {
|
|
678
|
+
eTrigger.dispose()
|
|
679
|
+
eTarget.dispose()
|
|
680
|
+
}
|
|
681
|
+
})
|
|
682
|
+
|
|
683
|
+
let trigger: ValueReactor<VDynamic> | undefined = undefined
|
|
684
|
+
let canEmit = true
|
|
685
|
+
const eTrigger = effect(() => {
|
|
686
|
+
trigger?.dispose()
|
|
687
|
+
const targetValue = target.getWithoutTrack()
|
|
688
|
+
trigger = dynamic(targetValue)
|
|
689
|
+
trigger.get()
|
|
690
|
+
canEmit = true
|
|
691
|
+
})
|
|
692
|
+
|
|
693
|
+
const eTarget = effect(() => {
|
|
694
|
+
const latestValue = target.get()
|
|
695
|
+
if (canEmit === true) {
|
|
696
|
+
result.set(latestValue)
|
|
697
|
+
canEmit = false
|
|
698
|
+
}
|
|
699
|
+
})
|
|
700
|
+
|
|
701
|
+
return result
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
export interface DistinctOptions<V, K> {
|
|
705
|
+
target: ValueReactor<V>;
|
|
706
|
+
keyGetter?: (value: V) => K;
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Returns a Signal that only updates its value with unique values
|
|
710
|
+
* from the target ValueReactor, determined by the key returned from
|
|
711
|
+
* the keyGetter function.
|
|
712
|
+
*/
|
|
713
|
+
export const distinct = <V, K>(
|
|
714
|
+
options: DistinctOptions<V, K>
|
|
715
|
+
): Signal<V> => {
|
|
716
|
+
const { target, keyGetter } = options;
|
|
717
|
+
|
|
718
|
+
const result = signal<V>(castValueInitializer<V>(), {
|
|
719
|
+
onDispose: () => {
|
|
720
|
+
eTarget.dispose()
|
|
721
|
+
}
|
|
722
|
+
})
|
|
723
|
+
|
|
724
|
+
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
725
|
+
const preparedKeyGetter = keyGetter ?? ((value: V): K => (value as unknown) as K)
|
|
726
|
+
|
|
727
|
+
const keySet = new Set<K>()
|
|
728
|
+
const eTarget = effect(() => {
|
|
729
|
+
const newValue = target.get()
|
|
730
|
+
const key = preparedKeyGetter(newValue);
|
|
731
|
+
if (keySet.has(key) === false) {
|
|
732
|
+
result.set(newValue)
|
|
733
|
+
keySet.add(key)
|
|
734
|
+
}
|
|
735
|
+
})
|
|
736
|
+
|
|
737
|
+
return result
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
export interface DistinctUntilChangedOptions<V, K> {
|
|
741
|
+
target: ValueReactor<V>;
|
|
742
|
+
keyGetter?: (value: V) => K;
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Returns a Signal that only updates its value when the key returned
|
|
746
|
+
* from the keyGetter function changes between emissions.
|
|
747
|
+
*/
|
|
748
|
+
export const distinctUntilChanged = <V, K>(
|
|
749
|
+
options: DistinctUntilChangedOptions<V, K>
|
|
750
|
+
): Signal<V> => {
|
|
751
|
+
const { target, keyGetter } = options;
|
|
752
|
+
|
|
753
|
+
const result = signal<V>(castValueInitializer<V>(), {
|
|
754
|
+
onDispose: () => {
|
|
755
|
+
eTarget.dispose()
|
|
756
|
+
}
|
|
757
|
+
})
|
|
758
|
+
|
|
759
|
+
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
760
|
+
const preparedKeyGetter = keyGetter ?? ((value: V): K => (value as unknown) as K)
|
|
761
|
+
|
|
762
|
+
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
763
|
+
let lastKey: K = Symbol("initial-key-for-distinct-until-changed") as unknown as K
|
|
764
|
+
const eTarget = effect(() => {
|
|
765
|
+
const newValue = target.get()
|
|
766
|
+
const key = preparedKeyGetter(newValue);
|
|
767
|
+
if (key !== lastKey) {
|
|
768
|
+
result.set(newValue)
|
|
769
|
+
lastKey = key
|
|
770
|
+
}
|
|
771
|
+
})
|
|
772
|
+
|
|
773
|
+
return result
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
export interface TakeByPredicateOptions<V> {
|
|
777
|
+
target: ValueReactor<V>;
|
|
778
|
+
predicate: (value: V) => boolean;
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Returns a Signal that updates its value with the latest value from
|
|
782
|
+
* the target ValueReactor, but only while the predicate function
|
|
783
|
+
* returns true.
|
|
784
|
+
*/
|
|
785
|
+
export const takeByPredicate = <V>(
|
|
786
|
+
options: TakeByPredicateOptions<V>
|
|
787
|
+
): Signal<V> => {
|
|
788
|
+
const { target, predicate } = options;
|
|
789
|
+
|
|
790
|
+
const result = signal<V>(castValueInitializer<V>(), {
|
|
791
|
+
onDispose: () => {
|
|
792
|
+
eTarget.dispose()
|
|
793
|
+
}
|
|
794
|
+
})
|
|
795
|
+
|
|
796
|
+
const eTarget = effect(() => {
|
|
797
|
+
const latestValue = target.get()
|
|
798
|
+
if (predicate(latestValue) === true) {
|
|
799
|
+
result.set(latestValue)
|
|
800
|
+
} else {
|
|
801
|
+
eTarget.dispose()
|
|
802
|
+
}
|
|
803
|
+
})
|
|
804
|
+
|
|
805
|
+
return result
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
export interface TakeByCountOptions<V> {
|
|
809
|
+
target: ValueReactor<V>;
|
|
810
|
+
count: number;
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Returns a Signal that updates its value with the latest value from
|
|
814
|
+
* the target ValueReactor, but only for the first `count` emissions.
|
|
815
|
+
*/
|
|
816
|
+
export const takeByCount = <V>(
|
|
817
|
+
options: TakeByCountOptions<V>
|
|
818
|
+
): Signal<V> => {
|
|
819
|
+
const { target, count } = options;
|
|
820
|
+
|
|
821
|
+
if (count <= 0) {
|
|
822
|
+
throw new Error("takeByCount operator requires count to be greater than 0")
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
const result = signal<V>(castValueInitializer<V>(), {
|
|
826
|
+
onDispose: () => {
|
|
827
|
+
eTarget.dispose()
|
|
828
|
+
}
|
|
829
|
+
})
|
|
830
|
+
|
|
831
|
+
let takenCount = 0
|
|
832
|
+
const eTarget = effect(() => {
|
|
833
|
+
const latestValue = target.get()
|
|
834
|
+
if (takenCount < count) {
|
|
835
|
+
result.set(latestValue)
|
|
836
|
+
takenCount = takenCount + 1
|
|
837
|
+
}
|
|
838
|
+
if (takenCount >= count) {
|
|
839
|
+
eTarget.dispose()
|
|
840
|
+
}
|
|
841
|
+
})
|
|
842
|
+
|
|
843
|
+
return result
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
export interface TakeByTimeOptions<T> {
|
|
847
|
+
target: ValueReactor<T>;
|
|
848
|
+
time: number;
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Returns a Signal that updates its value with the latest value from
|
|
852
|
+
* the target ValueReactor, but only for the specified time in milliseconds.
|
|
853
|
+
*/
|
|
854
|
+
export const takeByTime = <T>(
|
|
855
|
+
options: TakeByTimeOptions<T>
|
|
856
|
+
): Signal<T> => {
|
|
857
|
+
const { target, time } = options;
|
|
858
|
+
|
|
859
|
+
if (time <= 0) {
|
|
860
|
+
throw new Error("takeByTime operator requires timeInMilliseconds to be greater than 0")
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
const result = signal<T>(castValueInitializer<T>(), {
|
|
864
|
+
onDispose: () => {
|
|
865
|
+
eTarget.dispose()
|
|
866
|
+
clearTimeout(timeoutId)
|
|
867
|
+
}
|
|
868
|
+
})
|
|
869
|
+
|
|
870
|
+
const timeoutId = setTimeout(() => {
|
|
871
|
+
eTarget.dispose()
|
|
872
|
+
}, time)
|
|
873
|
+
|
|
874
|
+
const eTarget = effect(() => {
|
|
875
|
+
const latestValue = target.get()
|
|
876
|
+
result.set(latestValue)
|
|
877
|
+
})
|
|
878
|
+
|
|
879
|
+
return result
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
export interface TakeByTriggerOptions<VTarget, VTrigger> {
|
|
883
|
+
target: ValueReactor<VTarget>;
|
|
884
|
+
trigger: ValueReactor<VTrigger>;
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Returns a Signal that updates its value with the latest value from
|
|
888
|
+
* the target ValueReactor whenever the trigger ValueReactor emits a value.
|
|
889
|
+
*/
|
|
890
|
+
export const takeByTrigger = <VTarget, VTrigger>(
|
|
891
|
+
options: TakeByTriggerOptions<VTarget, VTrigger>
|
|
892
|
+
): Signal<VTarget> => {
|
|
893
|
+
const { target, trigger } = options;
|
|
894
|
+
|
|
895
|
+
const result = signal<VTarget>(castValueInitializer<VTarget>(), {
|
|
896
|
+
onDispose: () => {
|
|
897
|
+
eTrigger.dispose()
|
|
898
|
+
}
|
|
899
|
+
})
|
|
900
|
+
|
|
901
|
+
const eTrigger = effect(() => {
|
|
902
|
+
trigger.get()
|
|
903
|
+
result.set(target.getWithoutTrack())
|
|
904
|
+
})
|
|
905
|
+
|
|
906
|
+
return result
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
export interface TakeByToggleOptions<VTarget, VOpen, VClose> {
|
|
910
|
+
target: ValueReactor<VTarget>;
|
|
911
|
+
open: ValueReactor<VOpen>;
|
|
912
|
+
close: ValueReactor<VClose>;
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Returns a Signal that updates its value with the latest value from
|
|
916
|
+
* the target ValueReactor when the close ValueReactor emits, but only
|
|
917
|
+
* if taking was started by the open ValueReactor.
|
|
918
|
+
*/
|
|
919
|
+
export const takeByToggle = <VTarget, VOpen, VClose>(
|
|
920
|
+
options: TakeByToggleOptions<VTarget, VOpen, VClose>
|
|
921
|
+
): Signal<VTarget> => {
|
|
922
|
+
const { target, open, close } = options;
|
|
923
|
+
|
|
924
|
+
const result = signal<VTarget>(castValueInitializer<VTarget>(), {
|
|
925
|
+
onDispose: () => {
|
|
926
|
+
eTarget.dispose()
|
|
927
|
+
eOpen.dispose()
|
|
928
|
+
eClose.dispose()
|
|
929
|
+
}
|
|
930
|
+
})
|
|
931
|
+
|
|
932
|
+
let taking = false
|
|
933
|
+
|
|
934
|
+
const eOpen = effect(() => {
|
|
935
|
+
open.get()
|
|
936
|
+
taking = true
|
|
937
|
+
})
|
|
938
|
+
|
|
939
|
+
const eTarget = effect(() => {
|
|
940
|
+
const value = target.get()
|
|
941
|
+
if (taking === true) {
|
|
942
|
+
result.set(value)
|
|
943
|
+
}
|
|
944
|
+
})
|
|
945
|
+
|
|
946
|
+
const eClose = effect(() => {
|
|
947
|
+
close.get()
|
|
948
|
+
taking = false
|
|
949
|
+
})
|
|
950
|
+
|
|
951
|
+
return result
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
export interface TakeUntilTriggerOptions<VTarget, VTrigger> {
|
|
955
|
+
target: ValueReactor<VTarget>;
|
|
956
|
+
trigger: ValueReactor<VTrigger>;
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* Returns a Signal that updates its value with the latest value from
|
|
960
|
+
* the target ValueReactor until the trigger ValueReactor emits a value.
|
|
961
|
+
*/
|
|
962
|
+
export const takeUntilTrigger = <VTarget, VTrigger>(
|
|
963
|
+
options: TakeUntilTriggerOptions<VTarget, VTrigger>
|
|
964
|
+
): Signal<VTarget> => {
|
|
965
|
+
const { target, trigger } = options;
|
|
966
|
+
|
|
967
|
+
const result = signal<VTarget>(castValueInitializer<VTarget>(), {
|
|
968
|
+
onDispose: () => {
|
|
969
|
+
eTarget.dispose()
|
|
970
|
+
eTrigger.dispose()
|
|
971
|
+
}
|
|
972
|
+
})
|
|
973
|
+
|
|
974
|
+
const eTarget = effect(() => {
|
|
975
|
+
const latestValue = target.get()
|
|
976
|
+
result.set(latestValue)
|
|
977
|
+
})
|
|
978
|
+
|
|
979
|
+
const eTrigger = effect((context) => {
|
|
980
|
+
trigger.get()
|
|
981
|
+
if (context.isInitializingRun === false) {
|
|
982
|
+
eTarget.dispose()
|
|
983
|
+
eTrigger.dispose()
|
|
984
|
+
}
|
|
985
|
+
})
|
|
986
|
+
|
|
987
|
+
return result
|
|
988
|
+
}
|