@juulsgaard/signal-tools 19.0.0 → 21.0.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/dist/async.mjs +8 -8
- package/dist/index.d.ts +2 -1
- package/dist/loading-signal.mjs +1 -1
- package/dist/request-state/constructors.mjs +12 -3
- package/package.json +18 -18
- package/dist/async.js +0 -96
- package/dist/data-structures/signal-map.js +0 -134
- package/dist/data-structures/signal-queue.js +0 -115
- package/dist/data-structures/signal-set.js +0 -143
- package/dist/data-structures/signal-stack.js +0 -90
- package/dist/index.js +0 -16
- package/dist/interop.js +0 -11
- package/dist/loading-signal.js +0 -68
- package/dist/operators.js +0 -13
- package/dist/request-state/async-request-state.js +0 -48
- package/dist/request-state/constructors.js +0 -25
- package/dist/request-state/empty-request-state.js +0 -20
- package/dist/request-state/error-request-state.js +0 -20
- package/dist/request-state/loading-request-state.js +0 -25
- package/dist/request-state/request-state.js +0 -39
- package/dist/request-state/request-tracker.js +0 -64
- package/dist/request-state/static-request-state.js +0 -32
- package/dist/request-state/writable-request-state.js +0 -75
package/dist/async.mjs
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { assertInInjectionContext, signal, effect, DestroyRef, inject } from '@angular/core';
|
|
2
2
|
|
|
3
3
|
function debouncedSignal(inputSignal, delay = 500, options) {
|
|
4
|
-
if (options?.injector) assertInInjectionContext(debouncedSignal);
|
|
4
|
+
if (!options?.injector) assertInInjectionContext(debouncedSignal);
|
|
5
5
|
const output = signal(inputSignal());
|
|
6
6
|
effect((onDestroy) => {
|
|
7
7
|
const value = inputSignal();
|
|
8
8
|
const timeout = setTimeout(() => output.set(value), delay);
|
|
9
9
|
onDestroy(() => clearTimeout(timeout));
|
|
10
|
-
}, {
|
|
10
|
+
}, { injector: options?.injector });
|
|
11
11
|
return output;
|
|
12
12
|
}
|
|
13
13
|
function throttledSignal(inputSignal, delay = 500, options) {
|
|
14
|
-
if (options?.injector) assertInInjectionContext(throttledSignal);
|
|
14
|
+
if (!options?.injector) assertInInjectionContext(throttledSignal);
|
|
15
15
|
const output = signal(inputSignal());
|
|
16
16
|
let nextValue = inputSignal();
|
|
17
17
|
let timeout = void 0;
|
|
@@ -26,13 +26,13 @@ function throttledSignal(inputSignal, delay = 500, options) {
|
|
|
26
26
|
output.set(nextValue);
|
|
27
27
|
timeout = void 0;
|
|
28
28
|
}, delay);
|
|
29
|
-
}, {
|
|
29
|
+
}, { injector: options?.injector });
|
|
30
30
|
const onDestroy = options?.injector?.get(DestroyRef) ?? inject(DestroyRef);
|
|
31
31
|
onDestroy.onDestroy(() => clearTimeout(timeout));
|
|
32
32
|
return output;
|
|
33
33
|
}
|
|
34
34
|
function debouncedThrottledSignal(inputSignal, throttleDelay = 1e3, debounceDelay = 300, options) {
|
|
35
|
-
if (options?.injector) assertInInjectionContext(debouncedThrottledSignal);
|
|
35
|
+
if (!options?.injector) assertInInjectionContext(debouncedThrottledSignal);
|
|
36
36
|
const output = signal(inputSignal());
|
|
37
37
|
let nextValue = inputSignal();
|
|
38
38
|
let longTimeout = void 0;
|
|
@@ -54,13 +54,13 @@ function debouncedThrottledSignal(inputSignal, throttleDelay = 1e3, debounceDela
|
|
|
54
54
|
output.set(nextValue);
|
|
55
55
|
longTimeout = void 0;
|
|
56
56
|
}, throttleDelay);
|
|
57
|
-
}, {
|
|
57
|
+
}, { injector: options?.injector });
|
|
58
58
|
const onDestroy = options?.injector?.get(DestroyRef) ?? inject(DestroyRef);
|
|
59
59
|
onDestroy.onDestroy(() => clearTimeout(longTimeout));
|
|
60
60
|
return output;
|
|
61
61
|
}
|
|
62
62
|
function searchSignal(inputSignal, throttleDelay = 1e3, debounceDelay = 300, options) {
|
|
63
|
-
if (options?.injector) assertInInjectionContext(searchSignal);
|
|
63
|
+
if (!options?.injector) assertInInjectionContext(searchSignal);
|
|
64
64
|
const query = signal(inputSignal());
|
|
65
65
|
let nextValue = inputSignal();
|
|
66
66
|
let longTimeout = void 0;
|
|
@@ -87,7 +87,7 @@ function searchSignal(inputSignal, throttleDelay = 1e3, debounceDelay = 300, opt
|
|
|
87
87
|
query.set(nextValue);
|
|
88
88
|
longTimeout = void 0;
|
|
89
89
|
}, throttleDelay);
|
|
90
|
-
}, {
|
|
90
|
+
}, { injector: options?.injector });
|
|
91
91
|
const onDestroy = options?.injector?.get(DestroyRef) ?? inject(DestroyRef);
|
|
92
92
|
onDestroy.onDestroy(() => clearTimeout(longTimeout));
|
|
93
93
|
return query;
|
package/dist/index.d.ts
CHANGED
|
@@ -817,4 +817,5 @@ declare function requestTracker<T>(request: IValueRequestState<T>): ValueRequest
|
|
|
817
817
|
*/
|
|
818
818
|
declare function requestTracker(request: IRequestState): RequestTracker;
|
|
819
819
|
|
|
820
|
-
export { AsyncRequestState,
|
|
820
|
+
export { AsyncRequestState, EmptyRequestState, ErrorRequestState, IRequestState, IValueRequestState, LoadingRequestState, RequestTracker, SignalMap, SignalQueue, SignalSet, SignalStack, StaticRequestState, ValueRequestTracker, WritableRequestState, debouncedSignal, debouncedThrottledSignal, loadingSignal, requestState, requestTracker, searchSignal, signalMap, signalQueue, signalSet, signalStack, subjectToSignal, throttledSignal, toLoadingSignal };
|
|
821
|
+
export type { CreateLoadingSignalOptions, LoadingSignal, LoadingSignalOptions, ReadonlySignalMap, ReadonlySignalSet, SignalQueueDelta, SignalQueueOptions, SignalStackDelta };
|
package/dist/loading-signal.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { signal, assertInInjectionContext, DestroyRef, inject } from '@angular/core';
|
|
2
|
-
import { ILoadingState } from '@juulsgaard/rxjs-tools';
|
|
3
2
|
import { toSignal } from '@angular/core/rxjs-interop';
|
|
3
|
+
import { ILoadingState } from '@juulsgaard/rxjs-tools';
|
|
4
4
|
import { parseError } from '@juulsgaard/ts-tools';
|
|
5
5
|
|
|
6
6
|
function loadingSignal(value, options) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { isObservable } from 'rxjs';
|
|
1
|
+
import { isObservable, of, delay } from 'rxjs';
|
|
2
2
|
import { WritableRequestState } from './writable-request-state.mjs';
|
|
3
3
|
import { AsyncRequestState } from './async-request-state.mjs';
|
|
4
4
|
import { StaticRequestState } from './static-request-state.mjs';
|
|
5
5
|
import { ErrorRequestState } from './error-request-state.mjs';
|
|
6
|
-
import '
|
|
7
|
-
import '
|
|
6
|
+
import { LoadingRequestState } from './loading-request-state.mjs';
|
|
7
|
+
import { EmptyRequestState } from './empty-request-state.mjs';
|
|
8
8
|
|
|
9
9
|
function valueRequestState(data) {
|
|
10
10
|
if (isObservable(data)) return new AsyncRequestState(data);
|
|
@@ -14,12 +14,21 @@ function valueRequestState(data) {
|
|
|
14
14
|
function errorRequestState(error) {
|
|
15
15
|
return new ErrorRequestState(error instanceof Error ? () => error : error);
|
|
16
16
|
}
|
|
17
|
+
function loadingRequestState(duration, value) {
|
|
18
|
+
if (duration === void 0) return new LoadingRequestState();
|
|
19
|
+
return valueRequestState(of(value).pipe(delay(duration)));
|
|
20
|
+
}
|
|
21
|
+
function emptyRequestState() {
|
|
22
|
+
return new EmptyRequestState();
|
|
23
|
+
}
|
|
17
24
|
function writableRequestState(onCancel) {
|
|
18
25
|
return new WritableRequestState(onCancel);
|
|
19
26
|
}
|
|
20
27
|
const compiled = valueRequestState;
|
|
21
28
|
compiled.writable = writableRequestState;
|
|
22
29
|
compiled.error = errorRequestState;
|
|
30
|
+
compiled.loading = loadingRequestState;
|
|
31
|
+
compiled.empty = emptyRequestState;
|
|
23
32
|
const requestState = compiled;
|
|
24
33
|
|
|
25
34
|
export { requestState };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juulsgaard/signal-tools",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "21.0.0",
|
|
4
4
|
"description": "Tools for working with Angular signals",
|
|
5
5
|
"repository": "git@github.com:Juulsgaard/signal-tools.git",
|
|
6
6
|
"homepage": "https://juulsgaard.github.io/signal-tools/",
|
|
@@ -14,28 +14,28 @@
|
|
|
14
14
|
"dist/**"
|
|
15
15
|
],
|
|
16
16
|
"peerDependencies": {
|
|
17
|
-
"@angular/core": "^
|
|
18
|
-
"@juulsgaard/ts-tools": "~1.
|
|
19
|
-
"@juulsgaard/rxjs-tools": "~1.
|
|
20
|
-
"rxjs": "^7.8.
|
|
21
|
-
"typescript": "~5.
|
|
17
|
+
"@angular/core": "^21.2.0",
|
|
18
|
+
"@juulsgaard/ts-tools": "~1.8.0",
|
|
19
|
+
"@juulsgaard/rxjs-tools": "~1.9.0",
|
|
20
|
+
"rxjs": "^7.8.2",
|
|
21
|
+
"typescript": "~5.9.0"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"tslib": "^2.8.1"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@angular/core": "
|
|
28
|
-
"@juulsgaard/ts-tools": "1.
|
|
29
|
-
"@juulsgaard/rxjs-tools": "1.
|
|
30
|
-
"@types/jest": "^
|
|
31
|
-
"jest": "^
|
|
32
|
-
"rollup": "^4.
|
|
33
|
-
"rollup-plugin-dts": "^6.
|
|
34
|
-
"rollup-plugin-esbuild": "^6.
|
|
35
|
-
"rxjs": "7.8.
|
|
36
|
-
"ts-jest": "^29.
|
|
37
|
-
"typedoc": "^0.
|
|
38
|
-
"typescript": "~5.
|
|
27
|
+
"@angular/core": "^21.2.0",
|
|
28
|
+
"@juulsgaard/ts-tools": "1.8.0",
|
|
29
|
+
"@juulsgaard/rxjs-tools": "1.9.0",
|
|
30
|
+
"@types/jest": "^30.0.0",
|
|
31
|
+
"jest": "^30.3.0",
|
|
32
|
+
"rollup": "^4.60.2",
|
|
33
|
+
"rollup-plugin-dts": "^6.4.1",
|
|
34
|
+
"rollup-plugin-esbuild": "^6.2.1",
|
|
35
|
+
"rxjs": "7.8.2",
|
|
36
|
+
"ts-jest": "^29.4.9",
|
|
37
|
+
"typedoc": "^0.28.19",
|
|
38
|
+
"typescript": "~5.9.3"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "rollup -c",
|
package/dist/async.js
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { assertInInjectionContext, signal, effect, DestroyRef, inject } from '@angular/core';
|
|
2
|
-
|
|
3
|
-
function debouncedSignal(inputSignal, delay = 500, options) {
|
|
4
|
-
if (options?.injector) assertInInjectionContext(debouncedSignal);
|
|
5
|
-
const output = signal(inputSignal());
|
|
6
|
-
effect((onDestroy) => {
|
|
7
|
-
const value = inputSignal();
|
|
8
|
-
const timeout = setTimeout(() => output.set(value), delay);
|
|
9
|
-
onDestroy(() => clearTimeout(timeout));
|
|
10
|
-
}, { allowSignalWrites: true, injector: options?.injector });
|
|
11
|
-
return output;
|
|
12
|
-
}
|
|
13
|
-
function throttledSignal(inputSignal, delay = 500, options) {
|
|
14
|
-
if (options?.injector) assertInInjectionContext(throttledSignal);
|
|
15
|
-
const output = signal(inputSignal());
|
|
16
|
-
let nextValue = inputSignal();
|
|
17
|
-
let timeout = void 0;
|
|
18
|
-
effect(() => {
|
|
19
|
-
const value = inputSignal();
|
|
20
|
-
nextValue = value;
|
|
21
|
-
if (timeout) return;
|
|
22
|
-
if (options?.emitLeading) {
|
|
23
|
-
output.set(value);
|
|
24
|
-
}
|
|
25
|
-
timeout = setTimeout(() => {
|
|
26
|
-
output.set(nextValue);
|
|
27
|
-
timeout = void 0;
|
|
28
|
-
}, delay);
|
|
29
|
-
}, { allowSignalWrites: true, injector: options?.injector });
|
|
30
|
-
const onDestroy = options?.injector?.get(DestroyRef) ?? inject(DestroyRef);
|
|
31
|
-
onDestroy.onDestroy(() => clearTimeout(timeout));
|
|
32
|
-
return output;
|
|
33
|
-
}
|
|
34
|
-
function debouncedThrottledSignal(inputSignal, throttleDelay = 1e3, debounceDelay = 300, options) {
|
|
35
|
-
if (options?.injector) assertInInjectionContext(debouncedThrottledSignal);
|
|
36
|
-
const output = signal(inputSignal());
|
|
37
|
-
let nextValue = inputSignal();
|
|
38
|
-
let longTimeout = void 0;
|
|
39
|
-
function clearThrottle() {
|
|
40
|
-
if (!longTimeout) return;
|
|
41
|
-
clearTimeout(longTimeout);
|
|
42
|
-
longTimeout = void 0;
|
|
43
|
-
}
|
|
44
|
-
effect((onDestroy2) => {
|
|
45
|
-
const value = inputSignal();
|
|
46
|
-
const shortTimeout = setTimeout(() => {
|
|
47
|
-
output.set(value);
|
|
48
|
-
clearThrottle();
|
|
49
|
-
}, debounceDelay);
|
|
50
|
-
onDestroy2(() => clearTimeout(shortTimeout));
|
|
51
|
-
nextValue = value;
|
|
52
|
-
if (longTimeout) return;
|
|
53
|
-
longTimeout = setTimeout(() => {
|
|
54
|
-
output.set(nextValue);
|
|
55
|
-
longTimeout = void 0;
|
|
56
|
-
}, throttleDelay);
|
|
57
|
-
}, { allowSignalWrites: true, injector: options?.injector });
|
|
58
|
-
const onDestroy = options?.injector?.get(DestroyRef) ?? inject(DestroyRef);
|
|
59
|
-
onDestroy.onDestroy(() => clearTimeout(longTimeout));
|
|
60
|
-
return output;
|
|
61
|
-
}
|
|
62
|
-
function searchSignal(inputSignal, throttleDelay = 1e3, debounceDelay = 300, options) {
|
|
63
|
-
if (options?.injector) assertInInjectionContext(searchSignal);
|
|
64
|
-
const query = signal(inputSignal());
|
|
65
|
-
let nextValue = inputSignal();
|
|
66
|
-
let longTimeout = void 0;
|
|
67
|
-
function clearThrottle() {
|
|
68
|
-
if (!longTimeout) return;
|
|
69
|
-
clearTimeout(longTimeout);
|
|
70
|
-
longTimeout = void 0;
|
|
71
|
-
}
|
|
72
|
-
effect((onDestroy2) => {
|
|
73
|
-
const value = inputSignal();
|
|
74
|
-
if (value === void 0 || value === "") {
|
|
75
|
-
query.set(value);
|
|
76
|
-
clearThrottle();
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const shortTimeout = setTimeout(() => {
|
|
80
|
-
query.set(value);
|
|
81
|
-
clearThrottle();
|
|
82
|
-
}, debounceDelay);
|
|
83
|
-
onDestroy2(() => clearTimeout(shortTimeout));
|
|
84
|
-
nextValue = value;
|
|
85
|
-
if (longTimeout) return;
|
|
86
|
-
longTimeout = setTimeout(() => {
|
|
87
|
-
query.set(nextValue);
|
|
88
|
-
longTimeout = void 0;
|
|
89
|
-
}, throttleDelay);
|
|
90
|
-
}, { allowSignalWrites: true, injector: options?.injector });
|
|
91
|
-
const onDestroy = options?.injector?.get(DestroyRef) ?? inject(DestroyRef);
|
|
92
|
-
onDestroy.onDestroy(() => clearTimeout(longTimeout));
|
|
93
|
-
return query;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export { debouncedSignal, debouncedThrottledSignal, searchSignal, throttledSignal };
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { signal, computed, untracked } from '@angular/core';
|
|
2
|
-
|
|
3
|
-
class SignalMap {
|
|
4
|
-
constructor(values) {
|
|
5
|
-
this._map = signal(new Map(values));
|
|
6
|
-
this.value = this._map.asReadonly();
|
|
7
|
-
this.size = computed(() => this.value().size);
|
|
8
|
-
this.empty = computed(() => this.size() <= 0);
|
|
9
|
-
}
|
|
10
|
-
[Symbol.iterator]() {
|
|
11
|
-
return this.value()[Symbol.iterator]();
|
|
12
|
-
}
|
|
13
|
-
getCopy() {
|
|
14
|
-
return new Map(untracked(this.value));
|
|
15
|
-
}
|
|
16
|
-
//<editor-fold desc="Actions">
|
|
17
|
-
/**
|
|
18
|
-
* Remove all keys not in the whitelist
|
|
19
|
-
* @param whitelist - The keys to keep
|
|
20
|
-
*/
|
|
21
|
-
filter(whitelist) {
|
|
22
|
-
const length = whitelist && "size" in whitelist ? whitelist.size : whitelist?.length;
|
|
23
|
-
if (!length) {
|
|
24
|
-
return this.clear();
|
|
25
|
-
}
|
|
26
|
-
const whitelistSet = whitelist instanceof Set ? whitelist : new Set(whitelist);
|
|
27
|
-
const map = this.getCopy();
|
|
28
|
-
for (let [key] of untracked(this.value)) {
|
|
29
|
-
if (whitelistSet.has(key)) continue;
|
|
30
|
-
map.delete(key);
|
|
31
|
-
}
|
|
32
|
-
if (untracked(this.size) !== map.size) {
|
|
33
|
-
this._map.set(map);
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
/** Clear the collection */
|
|
39
|
-
clear() {
|
|
40
|
-
if (!untracked(this.size)) return false;
|
|
41
|
-
this._map.set(/* @__PURE__ */ new Map());
|
|
42
|
-
return true;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Add a value to the collection if the key is not in use
|
|
46
|
-
* @param key - The key to use
|
|
47
|
-
* @param value - The value to add
|
|
48
|
-
* @return added - Returns true if the value was added
|
|
49
|
-
*/
|
|
50
|
-
add(key, value) {
|
|
51
|
-
if (untracked(this.value).has(key)) return false;
|
|
52
|
-
const map = this.getCopy();
|
|
53
|
-
map.set(key, value);
|
|
54
|
-
this._map.set(map);
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Add a value to the collection
|
|
59
|
-
* @param key - The key to use
|
|
60
|
-
* @param value - The value to add
|
|
61
|
-
* @return added - Returns true if the value was added or changed
|
|
62
|
-
*/
|
|
63
|
-
set(key, value) {
|
|
64
|
-
const state = untracked(this.value);
|
|
65
|
-
if (state.has(key) && state.get(key) === value) return false;
|
|
66
|
-
const map = this.getCopy();
|
|
67
|
-
map.set(key, value);
|
|
68
|
-
this._map.set(map);
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Remove a key from the collection
|
|
73
|
-
* @param key - The key to remove
|
|
74
|
-
* @return removed - True if an item was removed
|
|
75
|
-
*/
|
|
76
|
-
delete(key) {
|
|
77
|
-
if (!untracked(this.value).has(key)) return false;
|
|
78
|
-
const map = this.getCopy();
|
|
79
|
-
map.delete(key);
|
|
80
|
-
this._map.set(map);
|
|
81
|
-
return true;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Remove keys from the collection
|
|
85
|
-
* @param values - The keys to remove
|
|
86
|
-
* @return removed - True if items were removed
|
|
87
|
-
*/
|
|
88
|
-
deleteRange(values) {
|
|
89
|
-
const map = this.getCopy();
|
|
90
|
-
const size = map.size;
|
|
91
|
-
values.forEach((v) => map.delete(v));
|
|
92
|
-
if (map.size === size) return false;
|
|
93
|
-
this._map.set(map);
|
|
94
|
-
return true;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Toggle a value in the map
|
|
98
|
-
* @param key - The key to toggle
|
|
99
|
-
* @param value - The value to insert if applicable
|
|
100
|
-
* @param state - A forced state (`true` = always add, `false` = always delete)
|
|
101
|
-
* @returns The applied change (`true` = item added, `false` = item removed, `undefined` = nothing changed)
|
|
102
|
-
*/
|
|
103
|
-
toggle(key, value, state) {
|
|
104
|
-
if (untracked(this.value).has(key)) {
|
|
105
|
-
if (state === true) return void 0;
|
|
106
|
-
const map2 = this.getCopy();
|
|
107
|
-
map2.delete(key);
|
|
108
|
-
this._map.set(map2);
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
if (state === false) return void 0;
|
|
112
|
-
const map = this.getCopy();
|
|
113
|
-
map.set(key, value);
|
|
114
|
-
this._map.set(map);
|
|
115
|
-
return true;
|
|
116
|
-
}
|
|
117
|
-
/** Manually modify the inner collection */
|
|
118
|
-
modify(modify) {
|
|
119
|
-
const map = this.getCopy();
|
|
120
|
-
modify(map);
|
|
121
|
-
this._map.set(map);
|
|
122
|
-
}
|
|
123
|
-
/** @inheritDoc */
|
|
124
|
-
has(key) {
|
|
125
|
-
return computed(() => this.value().has(key));
|
|
126
|
-
}
|
|
127
|
-
/** @inheritDoc */
|
|
128
|
-
get(key) {
|
|
129
|
-
return computed(() => this.value().get(key));
|
|
130
|
-
}
|
|
131
|
-
//</editor-fold>
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export { SignalMap };
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { signal, computed, untracked } from '@angular/core';
|
|
2
|
-
import { pairwiseSignal } from '../operators.js';
|
|
3
|
-
|
|
4
|
-
class SignalQueue {
|
|
5
|
-
constructor(options) {
|
|
6
|
-
this._items = signal([]);
|
|
7
|
-
/** A signal emitting the items of the queue */
|
|
8
|
-
this.items = this._items.asReadonly();
|
|
9
|
-
/** A signal emitting the number of items in the queue */
|
|
10
|
-
this.length = computed(() => this.items().length);
|
|
11
|
-
/** A signal emitting true if there are no items in the queue */
|
|
12
|
-
this.empty = computed(() => this.length() <= 0);
|
|
13
|
-
this._maxSize = options?.size;
|
|
14
|
-
this.front = computed(() => this.items().at(0));
|
|
15
|
-
this.back = computed(() => this.items().at(-1));
|
|
16
|
-
const delta = pairwiseSignal(this.items, []);
|
|
17
|
-
this.frontDelta = computed(() => {
|
|
18
|
-
const [prev, next] = delta();
|
|
19
|
-
const item = next.at(0);
|
|
20
|
-
return { item, added: !item ? false : !prev.includes(item) };
|
|
21
|
-
}, { equal: (a, b) => a.item === b.item });
|
|
22
|
-
this.backDelta = computed(() => {
|
|
23
|
-
const [prev, next] = delta();
|
|
24
|
-
const item = next.at(-1);
|
|
25
|
-
return { item, added: !item ? false : !prev.includes(item) };
|
|
26
|
-
}, { equal: (a, b) => a.item === b.item });
|
|
27
|
-
}
|
|
28
|
-
/** The max size of the queue */
|
|
29
|
-
get maxSize() {
|
|
30
|
-
return this._maxSize;
|
|
31
|
-
}
|
|
32
|
-
//</editor-fold>
|
|
33
|
-
//<editor-fold desc="Actions">
|
|
34
|
-
/**
|
|
35
|
-
* Removes an item from the scheduler
|
|
36
|
-
* @param item
|
|
37
|
-
*/
|
|
38
|
-
removeItem(item) {
|
|
39
|
-
const index = untracked(this.items).indexOf(item);
|
|
40
|
-
if (index < 0) return false;
|
|
41
|
-
const arr = [...untracked(this.items)];
|
|
42
|
-
arr.splice(index, 1);
|
|
43
|
-
this._items.set(arr);
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
46
|
-
dequeue(count) {
|
|
47
|
-
if (count !== void 0) {
|
|
48
|
-
if (count < 1) return [];
|
|
49
|
-
if (!untracked(this.length)) return [];
|
|
50
|
-
const items = untracked(this.items).slice(0, count);
|
|
51
|
-
this._items.set(untracked(this.items).slice(count));
|
|
52
|
-
return items;
|
|
53
|
-
}
|
|
54
|
-
if (!untracked(this.length)) return void 0;
|
|
55
|
-
const item = untracked(this.items).at(0);
|
|
56
|
-
this._items.set(untracked(this.items).slice(1));
|
|
57
|
-
return item;
|
|
58
|
-
}
|
|
59
|
-
removeFromBack(count) {
|
|
60
|
-
if (count !== void 0) {
|
|
61
|
-
if (count < 1) return [];
|
|
62
|
-
if (!untracked(this.length)) return [];
|
|
63
|
-
const start = count * -1;
|
|
64
|
-
const items = untracked(this.items).slice(start);
|
|
65
|
-
this._items.set(untracked(this.items).slice(0, start));
|
|
66
|
-
return items;
|
|
67
|
-
}
|
|
68
|
-
if (!untracked(this.length)) return void 0;
|
|
69
|
-
const item = untracked(this.items).at(-1);
|
|
70
|
-
this._items.set(untracked(this.items).slice(0, -1));
|
|
71
|
-
return item;
|
|
72
|
-
}
|
|
73
|
-
enqueue(...items) {
|
|
74
|
-
let list = [...untracked(this.items), ...items];
|
|
75
|
-
if (this.maxSize !== void 0 && list.length > this.maxSize) {
|
|
76
|
-
const delta = list.length - this.maxSize;
|
|
77
|
-
list = list.slice(delta);
|
|
78
|
-
}
|
|
79
|
-
this._items.set(list);
|
|
80
|
-
}
|
|
81
|
-
addToFront(...items) {
|
|
82
|
-
let list = [...items, ...untracked(this.items)];
|
|
83
|
-
if (this.maxSize !== void 0 && list.length > this.maxSize) {
|
|
84
|
-
const delta = list.length - this.maxSize;
|
|
85
|
-
list = list.slice(0, -delta);
|
|
86
|
-
}
|
|
87
|
-
this._items.set(list);
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Checks if the item exists in the queue
|
|
91
|
-
* @param item
|
|
92
|
-
*/
|
|
93
|
-
contains(item) {
|
|
94
|
-
return untracked(this.items).includes(item);
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Remove all elements from the set
|
|
98
|
-
*/
|
|
99
|
-
clear() {
|
|
100
|
-
this._items.set([]);
|
|
101
|
-
}
|
|
102
|
-
//</editor-fold>
|
|
103
|
-
/**
|
|
104
|
-
* Change the max size of the queue
|
|
105
|
-
* @param size - The new max size
|
|
106
|
-
*/
|
|
107
|
-
setMaxSize(size) {
|
|
108
|
-
this._maxSize = size;
|
|
109
|
-
if (size === void 0 || untracked(this.length) <= size) return;
|
|
110
|
-
const delta = untracked(this.length) - size;
|
|
111
|
-
this._items.set(untracked(this.items).slice(delta));
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export { SignalQueue };
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import { signal, computed, untracked } from '@angular/core';
|
|
2
|
-
|
|
3
|
-
class SignalSet {
|
|
4
|
-
constructor(values) {
|
|
5
|
-
this._set = signal(new Set(values));
|
|
6
|
-
this.value = this._set.asReadonly();
|
|
7
|
-
this.size = computed(() => this.value().size);
|
|
8
|
-
this.empty = computed(() => this.size() <= 0);
|
|
9
|
-
this.array = computed(() => Array.from(this.value()));
|
|
10
|
-
}
|
|
11
|
-
/** @inheritDoc */
|
|
12
|
-
[Symbol.iterator]() {
|
|
13
|
-
return this.value()[Symbol.iterator]();
|
|
14
|
-
}
|
|
15
|
-
getCopy() {
|
|
16
|
-
return new Set(untracked(this.value));
|
|
17
|
-
}
|
|
18
|
-
//<editor-fold desc="Actions">
|
|
19
|
-
/**
|
|
20
|
-
* Remove all keys not in the whitelist
|
|
21
|
-
* @param whitelist - The keys to keep
|
|
22
|
-
*/
|
|
23
|
-
filter(whitelist) {
|
|
24
|
-
const length = whitelist && "size" in whitelist ? whitelist.size : whitelist?.length;
|
|
25
|
-
if (!length) {
|
|
26
|
-
return this.clear();
|
|
27
|
-
}
|
|
28
|
-
const whitelistSet = whitelist instanceof Set ? whitelist : new Set(whitelist);
|
|
29
|
-
const set = this.getCopy();
|
|
30
|
-
for (let value of untracked(this.value)) {
|
|
31
|
-
if (whitelistSet.has(value)) continue;
|
|
32
|
-
set.delete(value);
|
|
33
|
-
}
|
|
34
|
-
if (untracked(this.size) !== set.size) {
|
|
35
|
-
this._set.set(set);
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
/** Clear the collection */
|
|
41
|
-
clear() {
|
|
42
|
-
if (!untracked(this.size)) return false;
|
|
43
|
-
this._set.set(/* @__PURE__ */ new Set());
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Add a value to the collection is not already present
|
|
48
|
-
* @param value - The value to add
|
|
49
|
-
* @return added - Returns true if the value was added
|
|
50
|
-
*/
|
|
51
|
-
add(value) {
|
|
52
|
-
if (untracked(this.value).has(value)) return false;
|
|
53
|
-
const set = this.getCopy();
|
|
54
|
-
set.add(value);
|
|
55
|
-
this._set.set(set);
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Add a list of values to the collection is not already present
|
|
60
|
-
* @param values - The values to add
|
|
61
|
-
* @return added - Returns true if any value was added
|
|
62
|
-
*/
|
|
63
|
-
addRange(values) {
|
|
64
|
-
const set = this.getCopy();
|
|
65
|
-
const size = set.size;
|
|
66
|
-
values.forEach((v) => set.add(v));
|
|
67
|
-
if (set.size === size) return false;
|
|
68
|
-
this._set.set(set);
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Reset the values in the collection to the provided list
|
|
73
|
-
* @param values - The values to set
|
|
74
|
-
* @return changed - Returns true if the collection changed
|
|
75
|
-
*/
|
|
76
|
-
set(values = []) {
|
|
77
|
-
if (!values.length && !this.size) return false;
|
|
78
|
-
if (values.length === untracked(this.size)) {
|
|
79
|
-
const same = values.every((x) => this.has(x));
|
|
80
|
-
if (same) return false;
|
|
81
|
-
}
|
|
82
|
-
this._set.set(new Set(values));
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Remove a value from the collection
|
|
87
|
-
* @param value - The key to remove
|
|
88
|
-
* @return removed - True if the value existed
|
|
89
|
-
*/
|
|
90
|
-
delete(value) {
|
|
91
|
-
if (!untracked(this.value).has(value)) return false;
|
|
92
|
-
const set = this.getCopy();
|
|
93
|
-
set.delete(value);
|
|
94
|
-
this._set.set(set);
|
|
95
|
-
return true;
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Remove values from the collection
|
|
99
|
-
* @param values - The values to remove
|
|
100
|
-
* @return removed - True if any values were removed
|
|
101
|
-
*/
|
|
102
|
-
deleteRange(values) {
|
|
103
|
-
const set = this.getCopy();
|
|
104
|
-
const size = set.size;
|
|
105
|
-
values.forEach((v) => set.delete(v));
|
|
106
|
-
if (set.size === size) return false;
|
|
107
|
-
this._set.set(set);
|
|
108
|
-
return true;
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Toggle a value in the set
|
|
112
|
-
* @param value - The value to toggle
|
|
113
|
-
* @param state - A forced state (`true` = always add, `false` = always delete)
|
|
114
|
-
* @returns The applied change (`true` = item added, `false` = item removed, `undefined` = nothing changed)
|
|
115
|
-
*/
|
|
116
|
-
toggle(value, state) {
|
|
117
|
-
if (untracked(this.value).has(value)) {
|
|
118
|
-
if (state === true) return void 0;
|
|
119
|
-
const set2 = this.getCopy();
|
|
120
|
-
set2.delete(value);
|
|
121
|
-
this._set.set(set2);
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
if (state === false) return void 0;
|
|
125
|
-
const set = this.getCopy();
|
|
126
|
-
set.add(value);
|
|
127
|
-
this._set.set(set);
|
|
128
|
-
return true;
|
|
129
|
-
}
|
|
130
|
-
/** Manually modify the inner collection */
|
|
131
|
-
modify(modify) {
|
|
132
|
-
const set = this.getCopy();
|
|
133
|
-
modify(set);
|
|
134
|
-
this._set.set(set);
|
|
135
|
-
}
|
|
136
|
-
/** @inheritDoc */
|
|
137
|
-
has(value) {
|
|
138
|
-
return computed(() => this.value().has(value));
|
|
139
|
-
}
|
|
140
|
-
//</editor-fold>
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export { SignalSet };
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { signal, computed, untracked } from '@angular/core';
|
|
2
|
-
import { pairwiseSignal } from '../operators.js';
|
|
3
|
-
|
|
4
|
-
class SignalStack {
|
|
5
|
-
constructor() {
|
|
6
|
-
this._items = signal([]);
|
|
7
|
-
/** A signal containing the items of the stack from bottom to top */
|
|
8
|
-
this.items = this._items.asReadonly();
|
|
9
|
-
/** A signal emitting the number of items in the stack */
|
|
10
|
-
this.length = computed(() => this.items().length);
|
|
11
|
-
/** A signal emitting true if there are no items in the stack */
|
|
12
|
-
this.empty = computed(() => this.length() <= 0);
|
|
13
|
-
this.top = computed(() => this.items().at(-1));
|
|
14
|
-
this.bottom = computed(() => this.items().at(0));
|
|
15
|
-
const delta = pairwiseSignal(this.items, []);
|
|
16
|
-
this.topDelta = computed(() => {
|
|
17
|
-
const [prev, next] = delta();
|
|
18
|
-
const item = next.at(-1);
|
|
19
|
-
return { item, added: !item ? false : !prev.includes(item) };
|
|
20
|
-
}, { equal: (a, b) => a.item === b.item });
|
|
21
|
-
this.bottomDelta = computed(() => {
|
|
22
|
-
const [prev, next] = delta();
|
|
23
|
-
const item = next.at(0);
|
|
24
|
-
return { item, added: !item ? false : !prev.includes(item) };
|
|
25
|
-
}, { equal: (a, b) => a.item === b.item });
|
|
26
|
-
}
|
|
27
|
-
//</editor-fold>
|
|
28
|
-
//<editor-fold desc="Actions">
|
|
29
|
-
/**
|
|
30
|
-
* Removes an item from the scheduler
|
|
31
|
-
* @param item
|
|
32
|
-
*/
|
|
33
|
-
removeItem(item) {
|
|
34
|
-
const index = untracked(this.items).indexOf(item);
|
|
35
|
-
if (index < 0) return false;
|
|
36
|
-
const arr = [...untracked(this.items)];
|
|
37
|
-
arr.splice(index, 1);
|
|
38
|
-
this._items.set(arr);
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
removeFromBottom(count) {
|
|
42
|
-
if (count !== void 0) {
|
|
43
|
-
if (count < 1) return [];
|
|
44
|
-
if (!untracked(this.length)) return [];
|
|
45
|
-
const items = untracked(this.items).slice(0, count);
|
|
46
|
-
this._items.set(untracked(this.items).slice(count));
|
|
47
|
-
return items;
|
|
48
|
-
}
|
|
49
|
-
if (!untracked(this.length)) return void 0;
|
|
50
|
-
const item = untracked(this.items).at(0);
|
|
51
|
-
this._items.set(untracked(this.items).slice(1));
|
|
52
|
-
return item;
|
|
53
|
-
}
|
|
54
|
-
pop(count) {
|
|
55
|
-
if (count !== void 0) {
|
|
56
|
-
if (count < 1) return [];
|
|
57
|
-
if (!untracked(this.length)) return [];
|
|
58
|
-
const start = count * -1;
|
|
59
|
-
const items = untracked(this.items).slice(start);
|
|
60
|
-
this._items.set(untracked(this.items).slice(0, start));
|
|
61
|
-
return items;
|
|
62
|
-
}
|
|
63
|
-
if (!untracked(this.length)) return void 0;
|
|
64
|
-
const item = untracked(this.items).at(-1);
|
|
65
|
-
this._items.set(untracked(this.items).slice(0, -1));
|
|
66
|
-
return item;
|
|
67
|
-
}
|
|
68
|
-
push(...items) {
|
|
69
|
-
this._items.set([...untracked(this.items), ...items]);
|
|
70
|
-
}
|
|
71
|
-
addToBottom(...items) {
|
|
72
|
-
this._items.set([...items, ...untracked(this.items)]);
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Checks if the item exists in the queue
|
|
76
|
-
* @param item
|
|
77
|
-
*/
|
|
78
|
-
contains(item) {
|
|
79
|
-
return untracked(this.items).includes(item);
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Remove all elements from the set
|
|
83
|
-
*/
|
|
84
|
-
clear() {
|
|
85
|
-
this._items.set([]);
|
|
86
|
-
}
|
|
87
|
-
//</editor-fold>
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export { SignalStack };
|
package/dist/index.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export { debouncedSignal, debouncedThrottledSignal, searchSignal, throttledSignal } from './async.js';
|
|
2
|
-
export { subjectToSignal } from './interop.js';
|
|
3
|
-
export { loadingSignal, toLoadingSignal } from './loading-signal.js';
|
|
4
|
-
export { SignalMap } from './data-structures/signal-map.js';
|
|
5
|
-
export { SignalSet } from './data-structures/signal-set.js';
|
|
6
|
-
export { SignalQueue } from './data-structures/signal-queue.js';
|
|
7
|
-
export { SignalStack } from './data-structures/signal-stack.js';
|
|
8
|
-
export { requestState } from './request-state/constructors.js';
|
|
9
|
-
export { IRequestState, IValueRequestState } from './request-state/request-state.js';
|
|
10
|
-
export { AsyncRequestState } from './request-state/async-request-state.js';
|
|
11
|
-
export { EmptyRequestState } from './request-state/empty-request-state.js';
|
|
12
|
-
export { ErrorRequestState } from './request-state/error-request-state.js';
|
|
13
|
-
export { LoadingRequestState } from './request-state/loading-request-state.js';
|
|
14
|
-
export { StaticRequestState } from './request-state/static-request-state.js';
|
|
15
|
-
export { WritableRequestState } from './request-state/writable-request-state.js';
|
|
16
|
-
export { RequestTracker, ValueRequestTracker, requestTracker } from './request-state/request-tracker.js';
|
package/dist/interop.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { signal } from '@angular/core';
|
|
2
|
-
import { BehaviorSubject } from 'rxjs';
|
|
3
|
-
|
|
4
|
-
function subjectToSignal(subject, initial) {
|
|
5
|
-
const init = subject instanceof BehaviorSubject ? subject.value : initial;
|
|
6
|
-
const sig = signal(init);
|
|
7
|
-
subject.subscribe((val) => sig.set(val));
|
|
8
|
-
return sig;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export { subjectToSignal };
|
package/dist/loading-signal.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { signal, assertInInjectionContext, DestroyRef, inject } from '@angular/core';
|
|
2
|
-
import { ILoadingState } from '@juulsgaard/rxjs-tools';
|
|
3
|
-
import { toSignal } from '@angular/core/rxjs-interop';
|
|
4
|
-
import { parseError } from '@juulsgaard/ts-tools';
|
|
5
|
-
|
|
6
|
-
function loadingSignal(value, options) {
|
|
7
|
-
const loadingSignal2 = value;
|
|
8
|
-
loadingSignal2.loading = options?.loading ?? signal(false);
|
|
9
|
-
loadingSignal2.error = options?.error ?? signal(void 0);
|
|
10
|
-
loadingSignal2.dispose = options?.dispose ?? (() => void 0);
|
|
11
|
-
return loadingSignal2;
|
|
12
|
-
}
|
|
13
|
-
function toLoadingSignal(loadingState, options) {
|
|
14
|
-
const injector = options?.injector;
|
|
15
|
-
const needsInjector = !!options?.autoDispose;
|
|
16
|
-
if (needsInjector && !injector) assertInInjectionContext(toLoadingSignal);
|
|
17
|
-
let state;
|
|
18
|
-
if (loadingState instanceof ILoadingState) {
|
|
19
|
-
state = fromLoadingState(loadingState);
|
|
20
|
-
} else if (loadingState instanceof Promise) {
|
|
21
|
-
state = fromPromise(loadingState);
|
|
22
|
-
} else {
|
|
23
|
-
state = fromObservable(loadingState);
|
|
24
|
-
}
|
|
25
|
-
if (options?.autoDispose) {
|
|
26
|
-
const destroy = injector?.get(DestroyRef) ?? inject(DestroyRef);
|
|
27
|
-
destroy.onDestroy(() => state.dispose());
|
|
28
|
-
}
|
|
29
|
-
return state;
|
|
30
|
-
}
|
|
31
|
-
function fromObservable(value$) {
|
|
32
|
-
const value = signal(void 0);
|
|
33
|
-
const loading = signal(true);
|
|
34
|
-
const error = signal(void 0);
|
|
35
|
-
const sub = value$.subscribe({
|
|
36
|
-
next: (val) => {
|
|
37
|
-
sub.unsubscribe();
|
|
38
|
-
loading.set(false);
|
|
39
|
-
value.set(val);
|
|
40
|
-
},
|
|
41
|
-
error: (err) => {
|
|
42
|
-
sub.unsubscribe();
|
|
43
|
-
loading.set(false);
|
|
44
|
-
error.set(parseError(err));
|
|
45
|
-
},
|
|
46
|
-
complete: () => {
|
|
47
|
-
sub.unsubscribe();
|
|
48
|
-
loading.set(false);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
return loadingSignal(value, { loading, error, dispose: () => sub.unsubscribe() });
|
|
52
|
-
}
|
|
53
|
-
function fromPromise(promise) {
|
|
54
|
-
const value = signal(void 0);
|
|
55
|
-
const loading = signal(true);
|
|
56
|
-
const error = signal(void 0);
|
|
57
|
-
promise.then((val) => value.set(val), (err) => error.set(parseError(err)));
|
|
58
|
-
promise.finally(() => loading.set(false));
|
|
59
|
-
return loadingSignal(value, { loading, error });
|
|
60
|
-
}
|
|
61
|
-
function fromLoadingState(state) {
|
|
62
|
-
const value = toSignal(state.result$, { manualCleanup: true });
|
|
63
|
-
const loading = toSignal(state.loading$, { manualCleanup: true, initialValue: true });
|
|
64
|
-
const error = toSignal(state.error$, { manualCleanup: true });
|
|
65
|
-
return loadingSignal(value, { loading, error, dispose: () => state.cancel() });
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export { loadingSignal, toLoadingSignal };
|
package/dist/operators.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { computed } from '@angular/core';
|
|
2
|
-
|
|
3
|
-
function pairwiseSignal(inputSignal, firstValue) {
|
|
4
|
-
let prevValue = firstValue;
|
|
5
|
-
return computed(() => {
|
|
6
|
-
const nextValue = inputSignal();
|
|
7
|
-
const tuple = [prevValue, nextValue];
|
|
8
|
-
prevValue = nextValue;
|
|
9
|
-
return tuple;
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export { pairwiseSignal };
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { ReplaySubject, isObservable, take, from } from 'rxjs';
|
|
2
|
-
import { signal, computed } from '@angular/core';
|
|
3
|
-
import { parseError, isString, isFunction } from '@juulsgaard/ts-tools';
|
|
4
|
-
import { CancelledError } from '@juulsgaard/rxjs-tools';
|
|
5
|
-
import { IValueRequestState } from './request-state.js';
|
|
6
|
-
|
|
7
|
-
class AsyncRequestState extends IValueRequestState {
|
|
8
|
-
constructor(request) {
|
|
9
|
-
super();
|
|
10
|
-
this._request$ = new ReplaySubject();
|
|
11
|
-
this.request$ = this._request$.asObservable();
|
|
12
|
-
this._result$ = new ReplaySubject();
|
|
13
|
-
this.result$ = this._result$.asObservable();
|
|
14
|
-
this._result = signal(void 0);
|
|
15
|
-
this.result = this._result.asReadonly();
|
|
16
|
-
this._loading = signal(true);
|
|
17
|
-
this.loading = this._loading.asReadonly();
|
|
18
|
-
this._error = signal(void 0);
|
|
19
|
-
this.error = this._error.asReadonly();
|
|
20
|
-
this.failed = computed(() => this._error() !== void 0);
|
|
21
|
-
const request$ = isObservable(request) ? request.pipe(take(1)) : from(request);
|
|
22
|
-
this.sub = request$.subscribe(this._request$);
|
|
23
|
-
this.request$.subscribe({
|
|
24
|
-
next: (value) => {
|
|
25
|
-
this._result$.next(value);
|
|
26
|
-
this._result.set(value);
|
|
27
|
-
},
|
|
28
|
-
error: (error) => {
|
|
29
|
-
this._error.set(parseError(error));
|
|
30
|
-
this._result$.complete();
|
|
31
|
-
this._loading.set(false);
|
|
32
|
-
},
|
|
33
|
-
complete: () => {
|
|
34
|
-
this._result$.complete();
|
|
35
|
-
this._loading.set(false);
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
cancel(error) {
|
|
40
|
-
if (this.sub.closed) return false;
|
|
41
|
-
this.sub.unsubscribe();
|
|
42
|
-
const fullError = isString(error) ? new CancelledError(error) : isFunction(error) ? error() : error;
|
|
43
|
-
this._request$.error(fullError);
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export { AsyncRequestState };
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { isObservable } from 'rxjs';
|
|
2
|
-
import { WritableRequestState } from './writable-request-state.js';
|
|
3
|
-
import { AsyncRequestState } from './async-request-state.js';
|
|
4
|
-
import { StaticRequestState } from './static-request-state.js';
|
|
5
|
-
import { ErrorRequestState } from './error-request-state.js';
|
|
6
|
-
import '@angular/core';
|
|
7
|
-
import '@juulsgaard/ts-tools';
|
|
8
|
-
|
|
9
|
-
function valueRequestState(data) {
|
|
10
|
-
if (isObservable(data)) return new AsyncRequestState(data);
|
|
11
|
-
if (data instanceof Promise) return new AsyncRequestState(data);
|
|
12
|
-
return new StaticRequestState(data);
|
|
13
|
-
}
|
|
14
|
-
function errorRequestState(error) {
|
|
15
|
-
return new ErrorRequestState(error instanceof Error ? () => error : error);
|
|
16
|
-
}
|
|
17
|
-
function writableRequestState(onCancel) {
|
|
18
|
-
return new WritableRequestState(onCancel);
|
|
19
|
-
}
|
|
20
|
-
const compiled = valueRequestState;
|
|
21
|
-
compiled.writable = writableRequestState;
|
|
22
|
-
compiled.error = errorRequestState;
|
|
23
|
-
const requestState = compiled;
|
|
24
|
-
|
|
25
|
-
export { requestState };
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { EMPTY } from 'rxjs';
|
|
2
|
-
import { signal } from '@angular/core';
|
|
3
|
-
import { IValueRequestState } from './request-state.js';
|
|
4
|
-
|
|
5
|
-
class EmptyRequestState extends IValueRequestState {
|
|
6
|
-
constructor() {
|
|
7
|
-
super(...arguments);
|
|
8
|
-
this.request$ = EMPTY;
|
|
9
|
-
this.result$ = this.request$;
|
|
10
|
-
this.result = signal(void 0).asReadonly();
|
|
11
|
-
this.loading = signal(false);
|
|
12
|
-
this.error = signal(void 0);
|
|
13
|
-
this.failed = signal(false);
|
|
14
|
-
}
|
|
15
|
-
cancel() {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export { EmptyRequestState };
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { EMPTY, throwError } from 'rxjs';
|
|
2
|
-
import { signal } from '@angular/core';
|
|
3
|
-
import { IValueRequestState } from './request-state.js';
|
|
4
|
-
|
|
5
|
-
class ErrorRequestState extends IValueRequestState {
|
|
6
|
-
constructor(getError) {
|
|
7
|
-
super();
|
|
8
|
-
this.result$ = EMPTY;
|
|
9
|
-
this.result = signal(void 0).asReadonly();
|
|
10
|
-
this.loading = signal(false);
|
|
11
|
-
this.failed = signal(true);
|
|
12
|
-
this.error = signal(getError());
|
|
13
|
-
this.request$ = throwError(getError);
|
|
14
|
-
}
|
|
15
|
-
cancel() {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export { ErrorRequestState };
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Subject } from 'rxjs';
|
|
2
|
-
import { signal } from '@angular/core';
|
|
3
|
-
import { IValueRequestState } from './request-state.js';
|
|
4
|
-
|
|
5
|
-
class LoadingRequestState extends IValueRequestState {
|
|
6
|
-
constructor() {
|
|
7
|
-
super(...arguments);
|
|
8
|
-
this._request$ = new Subject();
|
|
9
|
-
this.request$ = this._request$.asObservable();
|
|
10
|
-
this.result$ = this.request$;
|
|
11
|
-
this.result = signal(void 0).asReadonly();
|
|
12
|
-
this._loading = signal(true);
|
|
13
|
-
this.loading = this._loading.asReadonly();
|
|
14
|
-
this.error = signal(void 0);
|
|
15
|
-
this.failed = signal(false);
|
|
16
|
-
}
|
|
17
|
-
cancel() {
|
|
18
|
-
if (this._request$.closed) return false;
|
|
19
|
-
this._request$.complete();
|
|
20
|
-
this._loading.set(false);
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export { LoadingRequestState };
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { first } from 'rxjs';
|
|
2
|
-
import { parseError } from '@juulsgaard/ts-tools';
|
|
3
|
-
|
|
4
|
-
class IRequestState {
|
|
5
|
-
/**
|
|
6
|
-
* Subscribe to the underlying request Observable
|
|
7
|
-
* @param observer
|
|
8
|
-
*/
|
|
9
|
-
subscribe(observer) {
|
|
10
|
-
return this.request$.subscribe(observer);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
class IValueRequestState extends IRequestState {
|
|
14
|
-
subscribe(observer) {
|
|
15
|
-
return this.request$.subscribe(observer);
|
|
16
|
-
}
|
|
17
|
-
then(onValue, onError) {
|
|
18
|
-
this.request$.pipe(first()).subscribe({
|
|
19
|
-
next: (v) => onValue(v),
|
|
20
|
-
error: (e) => onError?.(parseError(e))
|
|
21
|
-
});
|
|
22
|
-
return this;
|
|
23
|
-
}
|
|
24
|
-
catch(onError) {
|
|
25
|
-
this.request$.subscribe({
|
|
26
|
-
error: (e) => onError(parseError(e))
|
|
27
|
-
});
|
|
28
|
-
return this;
|
|
29
|
-
}
|
|
30
|
-
finally(onComplete) {
|
|
31
|
-
this.request$.subscribe({
|
|
32
|
-
error: (e) => onComplete(parseError(e)),
|
|
33
|
-
complete: () => onComplete()
|
|
34
|
-
});
|
|
35
|
-
return this;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export { IRequestState, IValueRequestState };
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { computed, signal, untracked } from '@angular/core';
|
|
2
|
-
import { IValueRequestState } from './request-state.js';
|
|
3
|
-
import { requestState } from './constructors.js';
|
|
4
|
-
|
|
5
|
-
class RequestTracker {
|
|
6
|
-
constructor(request) {
|
|
7
|
-
/** Emits true if the current RequestState is loading */
|
|
8
|
-
this.loading = computed(() => this._request().loading());
|
|
9
|
-
/** Emits an error if the current RequestState is in an error state */
|
|
10
|
-
this.error = computed(() => this._request().error());
|
|
11
|
-
/** Emits true if the current RequestState has failed */
|
|
12
|
-
this.failed = computed(() => this._request().failed());
|
|
13
|
-
this._request = signal(request ?? requestState.empty());
|
|
14
|
-
this.request = this._request.asReadonly();
|
|
15
|
-
}
|
|
16
|
-
/** The currently active RequestState */
|
|
17
|
-
set current(req) {
|
|
18
|
-
this.set(req);
|
|
19
|
-
}
|
|
20
|
-
get current() {
|
|
21
|
-
return untracked(this.request);
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Set a new current RequestState
|
|
25
|
-
* @param state - The new RequestState
|
|
26
|
-
*/
|
|
27
|
-
set(state) {
|
|
28
|
-
this._request.set(state);
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Resets the Tracker to an Empty state
|
|
32
|
-
*/
|
|
33
|
-
reset() {
|
|
34
|
-
this._request.set(requestState.empty());
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
class ValueRequestTracker extends RequestTracker {
|
|
38
|
-
set current(req) {
|
|
39
|
-
this.set(req);
|
|
40
|
-
}
|
|
41
|
-
get current() {
|
|
42
|
-
return untracked(this.request);
|
|
43
|
-
}
|
|
44
|
-
constructor(request) {
|
|
45
|
-
super();
|
|
46
|
-
this._valueRequest = signal(request ?? requestState.empty());
|
|
47
|
-
this.request = this._valueRequest.asReadonly();
|
|
48
|
-
}
|
|
49
|
-
set(req) {
|
|
50
|
-
super.set(req);
|
|
51
|
-
this._valueRequest.set(req);
|
|
52
|
-
}
|
|
53
|
-
reset() {
|
|
54
|
-
super.reset();
|
|
55
|
-
this.set(requestState.empty());
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
function requestTracker(request) {
|
|
59
|
-
if (!request) return new ValueRequestTracker();
|
|
60
|
-
if (request instanceof IValueRequestState) return new ValueRequestTracker(request);
|
|
61
|
-
return new RequestTracker(request);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export { RequestTracker, ValueRequestTracker, requestTracker };
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { of } from 'rxjs';
|
|
2
|
-
import { signal } from '@angular/core';
|
|
3
|
-
import { IValueRequestState } from './request-state.js';
|
|
4
|
-
|
|
5
|
-
class StaticRequestState extends IValueRequestState {
|
|
6
|
-
constructor(value) {
|
|
7
|
-
super();
|
|
8
|
-
this.value = value;
|
|
9
|
-
this.loading = signal(false);
|
|
10
|
-
this.error = signal(void 0);
|
|
11
|
-
this.failed = signal(false);
|
|
12
|
-
this.request$ = of(value);
|
|
13
|
-
this.result$ = this.request$;
|
|
14
|
-
this.result = signal(value);
|
|
15
|
-
}
|
|
16
|
-
cancel() {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
then(next, _error) {
|
|
20
|
-
next(this.value);
|
|
21
|
-
return this;
|
|
22
|
-
}
|
|
23
|
-
catch(_onError) {
|
|
24
|
-
return this;
|
|
25
|
-
}
|
|
26
|
-
finally(onComplete) {
|
|
27
|
-
onComplete();
|
|
28
|
-
return this;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export { StaticRequestState };
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { ReplaySubject } from 'rxjs';
|
|
2
|
-
import { signal, computed } from '@angular/core';
|
|
3
|
-
import { parseError, isString, isFunction } from '@juulsgaard/ts-tools';
|
|
4
|
-
import { CancelledError } from '@juulsgaard/rxjs-tools';
|
|
5
|
-
import { IValueRequestState } from './request-state.js';
|
|
6
|
-
|
|
7
|
-
class WritableRequestState extends IValueRequestState {
|
|
8
|
-
constructor(onCancel) {
|
|
9
|
-
super();
|
|
10
|
-
this.onCancel = onCancel;
|
|
11
|
-
this._completed = false;
|
|
12
|
-
this._request$ = new ReplaySubject();
|
|
13
|
-
this.request$ = this._request$.asObservable();
|
|
14
|
-
this._result$ = new ReplaySubject();
|
|
15
|
-
this.result$ = this._result$.asObservable();
|
|
16
|
-
this._result = signal(void 0);
|
|
17
|
-
this.result = this._result.asReadonly();
|
|
18
|
-
this._loading = signal(true);
|
|
19
|
-
this.loading = this._loading.asReadonly();
|
|
20
|
-
this._error = signal(void 0);
|
|
21
|
-
this.error = this._error.asReadonly();
|
|
22
|
-
this.failed = computed(() => this._error() !== void 0);
|
|
23
|
-
this._cancelled = signal(false);
|
|
24
|
-
/** Emits true when the RequestState has been cancelled by a consumer, otherwise emits false */
|
|
25
|
-
this.cancelled = this._cancelled.asReadonly();
|
|
26
|
-
this._cancelled$ = new ReplaySubject();
|
|
27
|
-
/** Emits true when the RequestState has been cancelled by a consumer */
|
|
28
|
-
this.cancelled$ = this._cancelled$.asObservable();
|
|
29
|
-
}
|
|
30
|
-
get completed() {
|
|
31
|
-
return this._completed;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Create a Readonly version of the RequestState
|
|
35
|
-
*/
|
|
36
|
-
asReadonly() {
|
|
37
|
-
return this;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Set the RequestState to an Error state
|
|
41
|
-
* @param error - The error for the RequestState
|
|
42
|
-
*/
|
|
43
|
-
setError(error) {
|
|
44
|
-
if (this.completed) return;
|
|
45
|
-
this._completed = true;
|
|
46
|
-
this._request$.error(error);
|
|
47
|
-
this._result$.complete();
|
|
48
|
-
this._loading.set(false);
|
|
49
|
-
this._error.set(parseError(error));
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Set the RequestState to a completed state with a given value
|
|
53
|
-
* @param value - The value to emit
|
|
54
|
-
*/
|
|
55
|
-
setValue(value) {
|
|
56
|
-
if (this.completed) return;
|
|
57
|
-
this._completed = true;
|
|
58
|
-
this._request$.next(value);
|
|
59
|
-
this._request$.complete();
|
|
60
|
-
this._loading.set(false);
|
|
61
|
-
this._result.set(value);
|
|
62
|
-
}
|
|
63
|
-
cancel(error) {
|
|
64
|
-
if (this.completed) return false;
|
|
65
|
-
const fullError = isString(error) ? new CancelledError(error) : isFunction(error) ? error() : error;
|
|
66
|
-
this.setError(fullError);
|
|
67
|
-
this._cancelled.set(true);
|
|
68
|
-
this._cancelled$.next(true);
|
|
69
|
-
this._cancelled$.complete();
|
|
70
|
-
this.onCancel?.();
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export { WritableRequestState };
|