@angular-architects/ngrx-toolkit 20.0.0 → 20.0.2
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/eslint.config.cjs +43 -0
- package/jest.config.ts +22 -0
- package/ng-package.json +7 -0
- package/package.json +4 -21
- package/project.json +37 -0
- package/redux-connector/docs/README.md +131 -0
- package/redux-connector/index.ts +6 -0
- package/redux-connector/ng-package.json +5 -0
- package/redux-connector/src/lib/create-redux.ts +102 -0
- package/redux-connector/src/lib/model.ts +89 -0
- package/redux-connector/src/lib/rxjs-interop/redux-method.ts +66 -0
- package/redux-connector/src/lib/signal-redux-store.ts +59 -0
- package/redux-connector/src/lib/util.ts +22 -0
- package/src/index.ts +43 -0
- package/src/lib/assertions/assertions.ts +9 -0
- package/src/lib/devtools/features/with-disabled-name-indicies.ts +31 -0
- package/src/lib/devtools/features/with-glitch-tracking.ts +35 -0
- package/src/lib/devtools/features/with-mapper.ts +34 -0
- package/src/lib/devtools/internal/current-action-names.ts +1 -0
- package/src/lib/devtools/internal/default-tracker.ts +60 -0
- package/src/lib/devtools/internal/devtools-feature.ts +37 -0
- package/src/lib/devtools/internal/devtools-syncer.service.ts +202 -0
- package/src/lib/devtools/internal/glitch-tracker.service.ts +61 -0
- package/src/lib/devtools/internal/models.ts +29 -0
- package/src/lib/devtools/provide-devtools-config.ts +32 -0
- package/src/lib/devtools/rename-devtools-name.ts +21 -0
- package/src/lib/devtools/tests/action-name.spec.ts +48 -0
- package/src/lib/devtools/tests/basic.spec.ts +111 -0
- package/src/lib/devtools/tests/connecting.spec.ts +37 -0
- package/src/lib/devtools/tests/helpers.spec.ts +43 -0
- package/src/lib/devtools/tests/naming.spec.ts +216 -0
- package/src/lib/devtools/tests/provide-devtools-config.spec.ts +25 -0
- package/src/lib/devtools/tests/types.spec.ts +19 -0
- package/src/lib/devtools/tests/update-state.spec.ts +29 -0
- package/src/lib/devtools/tests/with-devtools.spec.ts +5 -0
- package/src/lib/devtools/tests/with-glitch-tracking.spec.ts +272 -0
- package/src/lib/devtools/tests/with-mapper.spec.ts +69 -0
- package/src/lib/devtools/update-state.ts +38 -0
- package/src/lib/devtools/with-dev-tools-stub.ts +6 -0
- package/src/lib/devtools/with-devtools.ts +81 -0
- package/src/lib/immutable-state/deep-freeze.ts +43 -0
- package/src/lib/immutable-state/is-dev-mode.ts +6 -0
- package/src/lib/immutable-state/tests/with-immutable-state.spec.ts +278 -0
- package/src/lib/immutable-state/with-immutable-state.ts +150 -0
- package/src/lib/shared/prettify.ts +3 -0
- package/src/lib/shared/signal-store-models.ts +30 -0
- package/src/lib/shared/throw-if-null.ts +7 -0
- package/src/lib/storage-sync/features/with-indexed-db.ts +81 -0
- package/src/lib/storage-sync/features/with-local-storage.ts +58 -0
- package/src/lib/storage-sync/internal/indexeddb.service.ts +124 -0
- package/src/lib/storage-sync/internal/local-storage.service.ts +19 -0
- package/src/lib/storage-sync/internal/models.ts +62 -0
- package/src/lib/storage-sync/internal/session-storage.service.ts +18 -0
- package/src/lib/storage-sync/tests/indexeddb.service.spec.ts +99 -0
- package/src/lib/storage-sync/tests/with-storage-async.spec.ts +305 -0
- package/src/lib/storage-sync/tests/with-storage-sync.spec.ts +273 -0
- package/src/lib/storage-sync/with-storage-sync.ts +236 -0
- package/src/lib/with-call-state.spec.ts +42 -0
- package/src/lib/with-call-state.ts +195 -0
- package/src/lib/with-conditional.spec.ts +125 -0
- package/src/lib/with-conditional.ts +74 -0
- package/src/lib/with-data-service.spec.ts +564 -0
- package/src/lib/with-data-service.ts +433 -0
- package/src/lib/with-feature-factory.spec.ts +69 -0
- package/src/lib/with-feature-factory.ts +56 -0
- package/src/lib/with-pagination.spec.ts +135 -0
- package/src/lib/with-pagination.ts +373 -0
- package/src/lib/with-redux.spec.ts +258 -0
- package/src/lib/with-redux.ts +387 -0
- package/src/lib/with-reset.spec.ts +112 -0
- package/src/lib/with-reset.ts +62 -0
- package/src/lib/with-undo-redo.spec.ts +274 -0
- package/src/lib/with-undo-redo.ts +200 -0
- package/src/test-setup.ts +6 -0
- package/tsconfig.json +29 -0
- package/tsconfig.lib.json +17 -0
- package/tsconfig.lib.prod.json +9 -0
- package/tsconfig.spec.json +17 -0
- package/fesm2022/angular-architects-ngrx-toolkit-redux-connector.mjs +0 -119
- package/fesm2022/angular-architects-ngrx-toolkit-redux-connector.mjs.map +0 -1
- package/fesm2022/angular-architects-ngrx-toolkit.mjs +0 -1780
- package/fesm2022/angular-architects-ngrx-toolkit.mjs.map +0 -1
- package/index.d.ts +0 -938
- package/redux-connector/index.d.ts +0 -59
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { computed, inject } from '@angular/core';
|
|
2
|
+
import { TestBed } from '@angular/core/testing';
|
|
3
|
+
import {
|
|
4
|
+
patchState,
|
|
5
|
+
signalStore,
|
|
6
|
+
type,
|
|
7
|
+
withComputed,
|
|
8
|
+
withMethods,
|
|
9
|
+
withState,
|
|
10
|
+
} from '@ngrx/signals';
|
|
11
|
+
import { addEntity, withEntities } from '@ngrx/signals/entities';
|
|
12
|
+
import { withCallState } from './with-call-state';
|
|
13
|
+
import { withUndoRedo } from './with-undo-redo';
|
|
14
|
+
|
|
15
|
+
const testState = { test: '' };
|
|
16
|
+
const testKeys = ['test' as const];
|
|
17
|
+
const newValue = 'new value';
|
|
18
|
+
const newerValue = 'newer value';
|
|
19
|
+
|
|
20
|
+
describe('withUndoRedo', () => {
|
|
21
|
+
it('adds methods for undo, redo, canUndo, canRedo', () => {
|
|
22
|
+
TestBed.runInInjectionContext(() => {
|
|
23
|
+
const Store = signalStore(
|
|
24
|
+
withState(testState),
|
|
25
|
+
withUndoRedo({ keys: testKeys }),
|
|
26
|
+
);
|
|
27
|
+
const store = new Store();
|
|
28
|
+
|
|
29
|
+
expect(Object.keys(store)).toEqual([
|
|
30
|
+
'test',
|
|
31
|
+
'canUndo',
|
|
32
|
+
'canRedo',
|
|
33
|
+
'undo',
|
|
34
|
+
'redo',
|
|
35
|
+
'clearStack',
|
|
36
|
+
]);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should check keys and collection types', () => {
|
|
41
|
+
signalStore(
|
|
42
|
+
withState(testState),
|
|
43
|
+
// @ts-expect-error - should not allow invalid keys
|
|
44
|
+
withUndoRedo({ keys: ['tes'] }),
|
|
45
|
+
);
|
|
46
|
+
signalStore(
|
|
47
|
+
withState(testState),
|
|
48
|
+
withEntities({ entity: type(), collection: 'flight' }),
|
|
49
|
+
// @ts-expect-error - should not allow invalid keys when entities are present
|
|
50
|
+
withUndoRedo({ keys: ['flightIdsTest'] }),
|
|
51
|
+
);
|
|
52
|
+
signalStore(
|
|
53
|
+
withState(testState),
|
|
54
|
+
// @ts-expect-error - should not allow collections without named entities
|
|
55
|
+
withUndoRedo({ collections: ['tee'] }),
|
|
56
|
+
);
|
|
57
|
+
signalStore(
|
|
58
|
+
withState(testState),
|
|
59
|
+
withComputed((store) => ({ testComputed: computed(() => store.test()) })),
|
|
60
|
+
// @ts-expect-error - should not allow collections without named entities with other computed
|
|
61
|
+
withUndoRedo({ collections: ['tested'] }),
|
|
62
|
+
);
|
|
63
|
+
signalStore(
|
|
64
|
+
withEntities({ entity: type() }),
|
|
65
|
+
// @ts-expect-error - should not allow collections without named entities
|
|
66
|
+
withUndoRedo({ collections: ['test'] }),
|
|
67
|
+
);
|
|
68
|
+
signalStore(
|
|
69
|
+
withEntities({ entity: type(), collection: 'flight' }),
|
|
70
|
+
// @ts-expect-error - should not allow invalid collections
|
|
71
|
+
withUndoRedo({ collections: ['test'] }),
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('undo and redo', () => {
|
|
76
|
+
it('restores previous state for regular store key', () => {
|
|
77
|
+
TestBed.runInInjectionContext(() => {
|
|
78
|
+
const Store = signalStore(
|
|
79
|
+
withState(testState),
|
|
80
|
+
withMethods((store) => ({
|
|
81
|
+
updateTest: (newTest: string) =>
|
|
82
|
+
patchState(store, { test: newTest }),
|
|
83
|
+
})),
|
|
84
|
+
withUndoRedo({ keys: testKeys }),
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const store = new Store();
|
|
88
|
+
|
|
89
|
+
store.updateTest(newValue);
|
|
90
|
+
|
|
91
|
+
expect(store.test()).toEqual(newValue);
|
|
92
|
+
expect(store.canUndo()).toBe(true);
|
|
93
|
+
expect(store.canRedo()).toBe(false);
|
|
94
|
+
|
|
95
|
+
store.undo();
|
|
96
|
+
|
|
97
|
+
expect(store.test()).toEqual('');
|
|
98
|
+
expect(store.canUndo()).toBe(false);
|
|
99
|
+
expect(store.canRedo()).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('restores previous state for regular store key and respects skip', () => {
|
|
104
|
+
TestBed.runInInjectionContext(() => {
|
|
105
|
+
const Store = signalStore(
|
|
106
|
+
withState(testState),
|
|
107
|
+
withMethods((store) => ({
|
|
108
|
+
updateTest: (newTest: string) =>
|
|
109
|
+
patchState(store, { test: newTest }),
|
|
110
|
+
})),
|
|
111
|
+
withUndoRedo({ keys: testKeys, skip: 1 }),
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const store = new Store();
|
|
115
|
+
|
|
116
|
+
store.updateTest(newValue);
|
|
117
|
+
|
|
118
|
+
expect(store.test()).toEqual(newValue);
|
|
119
|
+
|
|
120
|
+
store.updateTest(newerValue);
|
|
121
|
+
|
|
122
|
+
store.undo();
|
|
123
|
+
|
|
124
|
+
expect(store.test()).toEqual(newValue);
|
|
125
|
+
expect(store.canUndo()).toBe(false);
|
|
126
|
+
|
|
127
|
+
store.undo();
|
|
128
|
+
|
|
129
|
+
// should not change
|
|
130
|
+
expect(store.test()).toEqual(newValue);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('undoes and redoes previous state for entity', () => {
|
|
135
|
+
const Store = signalStore(
|
|
136
|
+
withEntities({ entity: type<{ id: string }>() }),
|
|
137
|
+
withMethods((store) => ({
|
|
138
|
+
addEntity: (newTest: string) =>
|
|
139
|
+
patchState(store, addEntity({ id: newTest })),
|
|
140
|
+
})),
|
|
141
|
+
withUndoRedo(),
|
|
142
|
+
);
|
|
143
|
+
TestBed.configureTestingModule({ providers: [Store] });
|
|
144
|
+
TestBed.runInInjectionContext(() => {
|
|
145
|
+
const store = inject(Store);
|
|
146
|
+
|
|
147
|
+
expect(store.entities()).toEqual([]);
|
|
148
|
+
expect(store.canUndo()).toBe(false);
|
|
149
|
+
expect(store.canRedo()).toBe(false);
|
|
150
|
+
|
|
151
|
+
store.addEntity(newValue);
|
|
152
|
+
|
|
153
|
+
expect(store.entities()).toEqual([{ id: newValue }]);
|
|
154
|
+
expect(store.canUndo()).toBe(true);
|
|
155
|
+
expect(store.canRedo()).toBe(false);
|
|
156
|
+
|
|
157
|
+
store.addEntity(newerValue);
|
|
158
|
+
|
|
159
|
+
expect(store.entities()).toEqual([
|
|
160
|
+
{ id: newValue },
|
|
161
|
+
{ id: newerValue },
|
|
162
|
+
]);
|
|
163
|
+
expect(store.canUndo()).toBe(true);
|
|
164
|
+
expect(store.canRedo()).toBe(false);
|
|
165
|
+
|
|
166
|
+
store.undo();
|
|
167
|
+
|
|
168
|
+
expect(store.entities()).toEqual([{ id: newValue }]);
|
|
169
|
+
expect(store.canUndo()).toBe(true);
|
|
170
|
+
expect(store.canRedo()).toBe(true);
|
|
171
|
+
|
|
172
|
+
store.undo();
|
|
173
|
+
|
|
174
|
+
expect(store.entities()).toEqual([]);
|
|
175
|
+
expect(store.canUndo()).toBe(false);
|
|
176
|
+
expect(store.canRedo()).toBe(true);
|
|
177
|
+
|
|
178
|
+
store.redo();
|
|
179
|
+
|
|
180
|
+
expect(store.entities()).toEqual([{ id: newValue }]);
|
|
181
|
+
expect(store.canUndo()).toBe(true);
|
|
182
|
+
expect(store.canRedo()).toBe(true);
|
|
183
|
+
|
|
184
|
+
// should return canRedo=false after a change
|
|
185
|
+
store.addEntity('newest');
|
|
186
|
+
|
|
187
|
+
expect(store.canUndo()).toBe(true);
|
|
188
|
+
expect(store.canRedo()).toBe(false);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('restores previous state for named entity', () => {
|
|
193
|
+
TestBed.runInInjectionContext(() => {
|
|
194
|
+
const Store = signalStore(
|
|
195
|
+
withEntities({
|
|
196
|
+
entity: type<{ id: string }>(),
|
|
197
|
+
collection: 'flight',
|
|
198
|
+
}),
|
|
199
|
+
withMethods((store) => ({
|
|
200
|
+
addEntity: (newTest: string) =>
|
|
201
|
+
patchState(
|
|
202
|
+
store,
|
|
203
|
+
addEntity({ id: newTest }, { collection: 'flight' }),
|
|
204
|
+
),
|
|
205
|
+
})),
|
|
206
|
+
withCallState({ collection: 'flight' }),
|
|
207
|
+
withUndoRedo({ collections: ['flight'] }),
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
const store = new Store();
|
|
211
|
+
|
|
212
|
+
store.addEntity(newValue);
|
|
213
|
+
|
|
214
|
+
expect(store.flightEntities()).toEqual([{ id: newValue }]);
|
|
215
|
+
expect(store.canUndo()).toBe(true);
|
|
216
|
+
expect(store.canRedo()).toBe(false);
|
|
217
|
+
|
|
218
|
+
store.undo();
|
|
219
|
+
|
|
220
|
+
expect(store.flightEntities()).toEqual([]);
|
|
221
|
+
expect(store.canUndo()).toBe(false);
|
|
222
|
+
expect(store.canRedo()).toBe(true);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('clears undo redo stack', () => {
|
|
227
|
+
const Store = signalStore(
|
|
228
|
+
{ providedIn: 'root' },
|
|
229
|
+
withState(testState),
|
|
230
|
+
withMethods((store) => ({
|
|
231
|
+
update: (value: string) => patchState(store, { test: value }),
|
|
232
|
+
})),
|
|
233
|
+
withUndoRedo({ keys: testKeys }),
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
const store = TestBed.inject(Store);
|
|
237
|
+
|
|
238
|
+
store.update('Foo');
|
|
239
|
+
store.update('Bar');
|
|
240
|
+
store.undo();
|
|
241
|
+
store.clearStack();
|
|
242
|
+
|
|
243
|
+
expect(store.canUndo()).toBe(false);
|
|
244
|
+
expect(store.canRedo()).toBe(false);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('cannot undo after clearing and setting a new value', () => {
|
|
248
|
+
const Store = signalStore(
|
|
249
|
+
{ providedIn: 'root' },
|
|
250
|
+
withState(testState),
|
|
251
|
+
withMethods((store) => ({
|
|
252
|
+
update: (value: string) => patchState(store, { test: value }),
|
|
253
|
+
})),
|
|
254
|
+
withUndoRedo({ keys: testKeys }),
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
const store = TestBed.inject(Store);
|
|
258
|
+
|
|
259
|
+
store.update('Alan');
|
|
260
|
+
|
|
261
|
+
store.update('Gordon');
|
|
262
|
+
|
|
263
|
+
store.clearStack();
|
|
264
|
+
|
|
265
|
+
// After clearing the undo/redo stack, there is no previous item anymore.
|
|
266
|
+
// The following update becomes the first value.
|
|
267
|
+
// Since there is no other value before, it cannot be undone.
|
|
268
|
+
store.update('Hugh');
|
|
269
|
+
|
|
270
|
+
expect(store.canUndo()).toBe(false);
|
|
271
|
+
expect(store.canRedo()).toBe(false);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
});
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { Signal, isSignal, signal, untracked } from '@angular/core';
|
|
2
|
+
import {
|
|
3
|
+
EmptyFeatureResult,
|
|
4
|
+
SignalStoreFeature,
|
|
5
|
+
SignalStoreFeatureResult,
|
|
6
|
+
patchState,
|
|
7
|
+
signalStoreFeature,
|
|
8
|
+
watchState,
|
|
9
|
+
withComputed,
|
|
10
|
+
withHooks,
|
|
11
|
+
withMethods,
|
|
12
|
+
} from '@ngrx/signals';
|
|
13
|
+
import { capitalize } from './with-data-service';
|
|
14
|
+
|
|
15
|
+
export type StackItem = Record<string, unknown>;
|
|
16
|
+
|
|
17
|
+
export type NormalizedUndoRedoOptions = {
|
|
18
|
+
maxStackSize: number;
|
|
19
|
+
collections?: string[];
|
|
20
|
+
keys: string[];
|
|
21
|
+
skip: number;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const defaultOptions: NormalizedUndoRedoOptions = {
|
|
25
|
+
maxStackSize: 100,
|
|
26
|
+
keys: [],
|
|
27
|
+
skip: 0,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export function getUndoRedoKeys(collections?: string[]): string[] {
|
|
31
|
+
if (collections) {
|
|
32
|
+
return collections.flatMap((c) => [
|
|
33
|
+
`${c}EntityMap`,
|
|
34
|
+
`${c}Ids`,
|
|
35
|
+
`selected${capitalize(c)}Ids`,
|
|
36
|
+
`${c}Filter`,
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
return ['entityMap', 'ids', 'selectedIds', 'filter'];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type NonNever<T> = T extends never ? never : T;
|
|
43
|
+
|
|
44
|
+
type ExtractEntityCollection<T> = T extends `${infer U}Entities` ? U : never;
|
|
45
|
+
|
|
46
|
+
type ExtractEntityCollections<Store extends SignalStoreFeatureResult> =
|
|
47
|
+
NonNever<
|
|
48
|
+
{
|
|
49
|
+
[K in keyof Store['props']]: ExtractEntityCollection<K>;
|
|
50
|
+
}[keyof Store['props']]
|
|
51
|
+
>;
|
|
52
|
+
|
|
53
|
+
type OptionsForState<Store extends SignalStoreFeatureResult> = Partial<
|
|
54
|
+
Omit<NormalizedUndoRedoOptions, 'collections' | 'keys'>
|
|
55
|
+
> & {
|
|
56
|
+
collections?: ExtractEntityCollections<Store>[];
|
|
57
|
+
keys?: (keyof Store['state'])[];
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export function withUndoRedo<Input extends EmptyFeatureResult>(
|
|
61
|
+
options?: OptionsForState<Input>,
|
|
62
|
+
): SignalStoreFeature<
|
|
63
|
+
Input,
|
|
64
|
+
EmptyFeatureResult & {
|
|
65
|
+
props: {
|
|
66
|
+
canUndo: Signal<boolean>;
|
|
67
|
+
canRedo: Signal<boolean>;
|
|
68
|
+
};
|
|
69
|
+
methods: {
|
|
70
|
+
undo: () => void;
|
|
71
|
+
redo: () => void;
|
|
72
|
+
clearStack: () => void;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
> {
|
|
76
|
+
let previous: StackItem | null = null;
|
|
77
|
+
let skipOnce = false;
|
|
78
|
+
|
|
79
|
+
const normalized = {
|
|
80
|
+
...defaultOptions,
|
|
81
|
+
...options,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
//
|
|
85
|
+
// Design Decision: This feature has its own
|
|
86
|
+
// internal state.
|
|
87
|
+
//
|
|
88
|
+
|
|
89
|
+
const undoStack: StackItem[] = [];
|
|
90
|
+
const redoStack: StackItem[] = [];
|
|
91
|
+
|
|
92
|
+
const canUndo = signal(false);
|
|
93
|
+
const canRedo = signal(false);
|
|
94
|
+
|
|
95
|
+
const updateInternal = () => {
|
|
96
|
+
canUndo.set(undoStack.length !== 0);
|
|
97
|
+
canRedo.set(redoStack.length !== 0);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const keys = [...getUndoRedoKeys(normalized.collections), ...normalized.keys];
|
|
101
|
+
|
|
102
|
+
return signalStoreFeature(
|
|
103
|
+
withComputed(() => ({
|
|
104
|
+
canUndo: canUndo.asReadonly(),
|
|
105
|
+
canRedo: canRedo.asReadonly(),
|
|
106
|
+
})),
|
|
107
|
+
withMethods((store) => ({
|
|
108
|
+
undo(): void {
|
|
109
|
+
const item = undoStack.pop();
|
|
110
|
+
|
|
111
|
+
if (item && previous) {
|
|
112
|
+
redoStack.push(previous);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (item) {
|
|
116
|
+
skipOnce = true;
|
|
117
|
+
patchState(store, item);
|
|
118
|
+
previous = item;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
updateInternal();
|
|
122
|
+
},
|
|
123
|
+
redo(): void {
|
|
124
|
+
const item = redoStack.pop();
|
|
125
|
+
|
|
126
|
+
if (item && previous) {
|
|
127
|
+
undoStack.push(previous);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (item) {
|
|
131
|
+
skipOnce = true;
|
|
132
|
+
patchState(store, item);
|
|
133
|
+
previous = item;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
updateInternal();
|
|
137
|
+
},
|
|
138
|
+
clearStack(): void {
|
|
139
|
+
undoStack.splice(0);
|
|
140
|
+
redoStack.splice(0);
|
|
141
|
+
previous = null;
|
|
142
|
+
updateInternal();
|
|
143
|
+
},
|
|
144
|
+
})),
|
|
145
|
+
withHooks({
|
|
146
|
+
onInit(store) {
|
|
147
|
+
watchState(store, () => {
|
|
148
|
+
const cand = keys.reduce((acc, key) => {
|
|
149
|
+
const s = (store as Record<string | keyof Input['state'], unknown>)[
|
|
150
|
+
key
|
|
151
|
+
];
|
|
152
|
+
if (s && isSignal(s)) {
|
|
153
|
+
return {
|
|
154
|
+
...acc,
|
|
155
|
+
[key]: s(),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return acc;
|
|
159
|
+
}, {});
|
|
160
|
+
|
|
161
|
+
if (normalized.skip > 0) {
|
|
162
|
+
normalized.skip--;
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (skipOnce) {
|
|
167
|
+
skipOnce = false;
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
//
|
|
172
|
+
// Deep Comparison to prevent duplicated entries
|
|
173
|
+
// on the stack. This can e.g. happen after an undo
|
|
174
|
+
// if the component sends back the undone filter
|
|
175
|
+
// to the store.
|
|
176
|
+
//
|
|
177
|
+
if (JSON.stringify(cand) === JSON.stringify(previous)) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Clear redoStack after recorded action
|
|
182
|
+
redoStack.splice(0);
|
|
183
|
+
|
|
184
|
+
if (previous) {
|
|
185
|
+
undoStack.push(previous);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (redoStack.length > normalized.maxStackSize) {
|
|
189
|
+
undoStack.unshift();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
previous = cand;
|
|
193
|
+
|
|
194
|
+
// Don't propogate current reactive context
|
|
195
|
+
untracked(() => updateInternal());
|
|
196
|
+
});
|
|
197
|
+
},
|
|
198
|
+
}),
|
|
199
|
+
);
|
|
200
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2022",
|
|
4
|
+
"useDefineForClassFields": false,
|
|
5
|
+
"forceConsistentCasingInFileNames": true,
|
|
6
|
+
"strict": true,
|
|
7
|
+
"noImplicitOverride": true,
|
|
8
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
9
|
+
"noImplicitReturns": true,
|
|
10
|
+
"noFallthroughCasesInSwitch": true
|
|
11
|
+
},
|
|
12
|
+
"files": [],
|
|
13
|
+
"include": [],
|
|
14
|
+
"references": [
|
|
15
|
+
{
|
|
16
|
+
"path": "./tsconfig.lib.json"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"path": "./tsconfig.spec.json"
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"extends": "../../tsconfig.base.json",
|
|
23
|
+
"angularCompilerOptions": {
|
|
24
|
+
"enableI18nLegacyMessageIdFormat": false,
|
|
25
|
+
"strictInjectionParameters": true,
|
|
26
|
+
"strictInputAccessModifiers": true,
|
|
27
|
+
"strictTemplates": true
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "../../dist/out-tsc",
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"declarationMap": true,
|
|
7
|
+
"inlineSources": true,
|
|
8
|
+
"types": []
|
|
9
|
+
},
|
|
10
|
+
"exclude": [
|
|
11
|
+
"src/**/*.spec.ts",
|
|
12
|
+
"src/test-setup.ts",
|
|
13
|
+
"jest.config.ts",
|
|
14
|
+
"src/**/*.test.ts"
|
|
15
|
+
],
|
|
16
|
+
"include": ["src/**/*.ts"]
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "../../dist/out-tsc",
|
|
5
|
+
"module": "commonjs",
|
|
6
|
+
"target": "es2016",
|
|
7
|
+
"types": ["jest", "node"]
|
|
8
|
+
},
|
|
9
|
+
"files": ["src/test-setup.ts"],
|
|
10
|
+
"include": [
|
|
11
|
+
"jest.config.ts",
|
|
12
|
+
"src/**/*.test.ts",
|
|
13
|
+
"src/**/*.spec.ts",
|
|
14
|
+
"src/**/*.d.ts",
|
|
15
|
+
"src/lib/storage-sync/tests/with-storage-sync-indexedb.spec.ts"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, inject, makeEnvironmentProviders, provideEnvironmentInitializer, Injector } from '@angular/core';
|
|
3
|
-
import { rxMethod } from '@ngrx/signals/rxjs-interop';
|
|
4
|
-
import { pipe, tap, map } from 'rxjs';
|
|
5
|
-
|
|
6
|
-
function isUnsubscribable(fn) {
|
|
7
|
-
return !!fn?.unsubscribe;
|
|
8
|
-
}
|
|
9
|
-
function capitalize(str) {
|
|
10
|
-
return str ? str[0].toUpperCase() + str.substring(1) : str;
|
|
11
|
-
}
|
|
12
|
-
function isActionCreator(action) {
|
|
13
|
-
return Boolean(typeof action === 'function' &&
|
|
14
|
-
action &&
|
|
15
|
-
'type' in action &&
|
|
16
|
-
action.type &&
|
|
17
|
-
typeof action.type === 'string');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
class SignalReduxStore {
|
|
21
|
-
constructor() {
|
|
22
|
-
this.mapperDict = {};
|
|
23
|
-
this.dispatch = rxMethod(pipe(tap((action) => {
|
|
24
|
-
const callbacks = this.mapperDict[action.type];
|
|
25
|
-
if (callbacks?.storeMethod) {
|
|
26
|
-
if (isUnsubscribable(callbacks.storeMethod) &&
|
|
27
|
-
callbacks.resultMethod) {
|
|
28
|
-
return callbacks.storeMethod(action, (a) => {
|
|
29
|
-
const resultAction = callbacks.resultMethod?.(a);
|
|
30
|
-
this.dispatch(resultAction);
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
return callbacks?.storeMethod(action);
|
|
34
|
-
}
|
|
35
|
-
return;
|
|
36
|
-
})));
|
|
37
|
-
}
|
|
38
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
39
|
-
connectFeatureStore(mappers) {
|
|
40
|
-
mappers.forEach((mapper) => mapper.types.forEach((action) => (this.mapperDict[action] = {
|
|
41
|
-
storeMethod: mapper.storeMethod,
|
|
42
|
-
resultMethod: mapper.resultMethod,
|
|
43
|
-
})));
|
|
44
|
-
}
|
|
45
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: SignalReduxStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
46
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: SignalReduxStore, providedIn: 'root' }); }
|
|
47
|
-
}
|
|
48
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: SignalReduxStore, decorators: [{
|
|
49
|
-
type: Injectable,
|
|
50
|
-
args: [{
|
|
51
|
-
providedIn: 'root',
|
|
52
|
-
}]
|
|
53
|
-
}] });
|
|
54
|
-
function injectReduxDispatch() {
|
|
55
|
-
return inject(SignalReduxStore).dispatch;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function mapAction(...args) {
|
|
59
|
-
let resultMethod = args.pop();
|
|
60
|
-
let storeMethod = args.pop();
|
|
61
|
-
if (isActionCreator(storeMethod)) {
|
|
62
|
-
args.push(storeMethod);
|
|
63
|
-
storeMethod = resultMethod || storeMethod;
|
|
64
|
-
resultMethod = undefined;
|
|
65
|
-
}
|
|
66
|
-
const types = args.map((creator) => creator.type);
|
|
67
|
-
return {
|
|
68
|
-
types,
|
|
69
|
-
storeMethod,
|
|
70
|
-
resultMethod,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
function withActionMappers(...mappers) {
|
|
74
|
-
return mappers;
|
|
75
|
-
}
|
|
76
|
-
function createReduxState(storeName, signalStore, withActionMappers) {
|
|
77
|
-
const isRootProvider = signalStore?.ɵprov?.providedIn === 'root';
|
|
78
|
-
return {
|
|
79
|
-
[`provide${capitalize(storeName)}Store`]: (connectReduxDevtools = false) => makeEnvironmentProviders([
|
|
80
|
-
isRootProvider ? [] : signalStore,
|
|
81
|
-
provideEnvironmentInitializer(() => {
|
|
82
|
-
const initializerFn = ((signalReduxStore = inject(SignalReduxStore), store = inject(signalStore)) => () => {
|
|
83
|
-
if (connectReduxDevtools) {
|
|
84
|
-
// addStoreToReduxDevtools(store, storeName, false);
|
|
85
|
-
}
|
|
86
|
-
signalReduxStore.connectFeatureStore(withActionMappers(store));
|
|
87
|
-
})();
|
|
88
|
-
return initializerFn();
|
|
89
|
-
}),
|
|
90
|
-
]),
|
|
91
|
-
[`inject${capitalize(storeName)}Store`]: () => Object.assign(inject(signalStore), { dispatch: injectReduxDispatch() }),
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function reduxMethod(generator, resultMethodOrConfig, config) {
|
|
96
|
-
const injector = inject(Injector);
|
|
97
|
-
if (typeof resultMethodOrConfig === 'function') {
|
|
98
|
-
let unsubscribable;
|
|
99
|
-
const inputResultFn = ((input, resultMethod = resultMethodOrConfig) => {
|
|
100
|
-
const rxMethodWithResult = rxMethod(pipe(generator, map(resultMethod)), {
|
|
101
|
-
...(config || {}),
|
|
102
|
-
injector: config?.injector || injector,
|
|
103
|
-
});
|
|
104
|
-
const rxWithInput = rxMethodWithResult(input);
|
|
105
|
-
unsubscribable = { unsubscribe: rxWithInput.destroy.bind(rxWithInput) };
|
|
106
|
-
return rxWithInput;
|
|
107
|
-
});
|
|
108
|
-
inputResultFn.destroy = () => unsubscribable?.unsubscribe();
|
|
109
|
-
return inputResultFn;
|
|
110
|
-
}
|
|
111
|
-
return rxMethod(generator, resultMethodOrConfig);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Generated bundle index. Do not edit.
|
|
116
|
-
*/
|
|
117
|
-
|
|
118
|
-
export { createReduxState, mapAction, reduxMethod, withActionMappers };
|
|
119
|
-
//# sourceMappingURL=angular-architects-ngrx-toolkit-redux-connector.mjs.map
|