@angular-libs/signal-storage 0.0.1-beta.0 → 0.0.1-beta.1
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/README.md
CHANGED
|
@@ -33,6 +33,7 @@ interface AppStorage {
|
|
|
33
33
|
@Component({
|
|
34
34
|
selector: 'app-root',
|
|
35
35
|
standalone: true,
|
|
36
|
+
providers: [SignalStorageService],
|
|
36
37
|
template: `
|
|
37
38
|
<div>
|
|
38
39
|
<p>Current Theme: {{ theme() }}</p>
|
|
@@ -62,6 +63,28 @@ export class AppComponent {
|
|
|
62
63
|
}
|
|
63
64
|
```
|
|
64
65
|
|
|
66
|
+
## Custom Storage & Server-Side Rendering (SSR)
|
|
67
|
+
|
|
68
|
+
By default, the library uses `localStorage`. If you want to use `sessionStorage` instead, or if you need to provide a custom storage implementation for Angular Server-Side Rendering (SSR) (since the `window` object is not available on the server), you can override the storage provider using `SIGNAL_STORAGE_TOKEN`.
|
|
69
|
+
|
|
70
|
+
### Using `sessionStorage`
|
|
71
|
+
|
|
72
|
+
You can easily switch to `sessionStorage` by providing the token in your `app.config.ts`, `app.module.ts` or similar:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { ApplicationConfig } from '@angular/core';
|
|
76
|
+
import { SIGNAL_STORAGE_TOKEN } from '@angular-libs/signal-storage';
|
|
77
|
+
|
|
78
|
+
export const appConfig: ApplicationConfig = {
|
|
79
|
+
providers: [
|
|
80
|
+
{
|
|
81
|
+
provide: SIGNAL_STORAGE_TOKEN,
|
|
82
|
+
useFactory: () => (typeof window !== 'undefined' ? window.sessionStorage : ({} as Storage)),
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
};
|
|
86
|
+
```
|
|
87
|
+
|
|
65
88
|
## License
|
|
66
89
|
|
|
67
90
|
MIT
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, signal, Optional, Inject, Injectable } from '@angular/core';
|
|
2
3
|
|
|
4
|
+
const SIGNAL_STORAGE_TOKEN = new InjectionToken('SIGNAL_STORAGE_TOKEN', {
|
|
5
|
+
providedIn: 'root',
|
|
6
|
+
factory: () => (typeof window !== 'undefined' ? window.localStorage : {}),
|
|
7
|
+
});
|
|
3
8
|
class SignalStorageService {
|
|
4
9
|
storage;
|
|
5
10
|
signals = new Map();
|
|
@@ -7,16 +12,15 @@ class SignalStorageService {
|
|
|
7
12
|
* Create a new SignalStorageService instance
|
|
8
13
|
* @param storage The storage to use (localStorage or sessionStorage). Defaults to localStorage.
|
|
9
14
|
*/
|
|
10
|
-
constructor(storage
|
|
11
|
-
this.storage = storage;
|
|
15
|
+
constructor(storage) {
|
|
16
|
+
this.storage = storage || (typeof window !== 'undefined' ? window.localStorage : {});
|
|
12
17
|
if (typeof window !== 'undefined') {
|
|
13
18
|
window.addEventListener('storage', (event) => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
this.signals.has(event.key)) {
|
|
19
|
+
const { key, newValue, storageArea } = event;
|
|
20
|
+
if (storageArea === this.storage && key && this.signals.has(key)) {
|
|
17
21
|
try {
|
|
18
|
-
const
|
|
19
|
-
this.signals.get(
|
|
22
|
+
const value = newValue ? JSON.parse(newValue) : null;
|
|
23
|
+
this.signals.get(key).set(value);
|
|
20
24
|
}
|
|
21
25
|
catch {
|
|
22
26
|
// Ignore parse errors from external changes
|
|
@@ -88,16 +92,24 @@ class SignalStorageService {
|
|
|
88
92
|
has(key) {
|
|
89
93
|
return this.storage.getItem(key) !== null;
|
|
90
94
|
}
|
|
91
|
-
/**
|
|
92
|
-
* Clear all localStorage
|
|
93
|
-
*/
|
|
95
|
+
/** Clear all localStorage */
|
|
94
96
|
clear() {
|
|
95
97
|
this.storage.clear();
|
|
96
98
|
for (const sig of this.signals.values()) {
|
|
97
99
|
sig.set(null);
|
|
98
100
|
}
|
|
99
101
|
}
|
|
102
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: SignalStorageService, deps: [{ token: SIGNAL_STORAGE_TOKEN, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
103
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: SignalStorageService });
|
|
100
104
|
}
|
|
105
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: SignalStorageService, decorators: [{
|
|
106
|
+
type: Injectable
|
|
107
|
+
}], ctorParameters: () => [{ type: Storage, decorators: [{
|
|
108
|
+
type: Optional
|
|
109
|
+
}, {
|
|
110
|
+
type: Inject,
|
|
111
|
+
args: [SIGNAL_STORAGE_TOKEN]
|
|
112
|
+
}] }] });
|
|
101
113
|
|
|
102
114
|
/*
|
|
103
115
|
* Public API Surface of signal-storage
|
|
@@ -107,5 +119,5 @@ class SignalStorageService {
|
|
|
107
119
|
* Generated bundle index. Do not edit.
|
|
108
120
|
*/
|
|
109
121
|
|
|
110
|
-
export { SignalStorageService };
|
|
122
|
+
export { SIGNAL_STORAGE_TOKEN, SignalStorageService };
|
|
111
123
|
//# sourceMappingURL=angular-libs-signal-storage.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"angular-libs-signal-storage.mjs","sources":["../../../../projects/angular-libs/signal-storage/src/lib/signal-storage.ts","../../../../projects/angular-libs/signal-storage/src/public-api.ts","../../../../projects/angular-libs/signal-storage/src/angular-libs-signal-storage.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"angular-libs-signal-storage.mjs","sources":["../../../../projects/angular-libs/signal-storage/src/lib/signal-storage.ts","../../../../projects/angular-libs/signal-storage/src/public-api.ts","../../../../projects/angular-libs/signal-storage/src/angular-libs-signal-storage.ts"],"sourcesContent":["import {\n signal,\n WritableSignal,\n Signal,\n Injectable,\n InjectionToken,\n Optional,\n Inject,\n} from '@angular/core';\n\nexport const SIGNAL_STORAGE_TOKEN = new InjectionToken<Storage>('SIGNAL_STORAGE_TOKEN', {\n providedIn: 'root',\n factory: () => (typeof window !== 'undefined' ? window.localStorage : ({} as Storage)),\n});\n\n@Injectable()\nexport class SignalStorageService<T extends Record<string, any> = {}> {\n private storage: Storage;\n private signals = new Map<keyof T, WritableSignal<any>>();\n\n /**\n * Create a new SignalStorageService instance\n * @param storage The storage to use (localStorage or sessionStorage). Defaults to localStorage.\n */\n constructor(@Optional() @Inject(SIGNAL_STORAGE_TOKEN) storage?: Storage) {\n this.storage = storage || (typeof window !== 'undefined' ? window.localStorage : ({} as Storage));\n if (typeof window !== 'undefined') {\n window.addEventListener('storage', (event: StorageEvent) => {\n const { key, newValue, storageArea } = event;\n if (storageArea === this.storage && key && this.signals.has(key as keyof T)) {\n try {\n const value = newValue ? JSON.parse(newValue) : null;\n this.signals.get(key as keyof T)!.set(value);\n } catch {\n // Ignore parse errors from external changes\n }\n }\n });\n }\n }\n /**\n * Get typed data from SignalStorageService\n * @param key The key to retrieve\n * @returns The typed data or null if not found\n */\n get<K extends keyof T>(key: K): T[K] | null;\n /**\n * Get typed data from SignalStorageService with a default value\n * @param key The key to retrieve\n * @param defaultValue The default value to return if key not found\n * @returns The stored data or the default value\n */\n get<K extends keyof T>(key: K, defaultValue: T[K]): T[K];\n get<K extends keyof T>(key: K, defaultValue?: T[K]): T[K] | null {\n const item = this.storage.getItem(key as string);\n if (item === null) {\n return defaultValue ?? null;\n }\n try {\n return JSON.parse(item) as T[K];\n } catch {\n return defaultValue ?? null;\n }\n }\n\n /**\n * Get a reactive Angular signal for a key\n * @param key The key to retrieve\n * @param defaultValue The default value to return if key not found\n * @returns A read-only Signal containing the stored data\n */\n getSignal<K extends keyof T>(key: K): Signal<T[K] | null>;\n getSignal<K extends keyof T>(key: K, defaultValue: T[K]): Signal<T[K]>;\n getSignal<K extends keyof T>(key: K, defaultValue?: T[K]): Signal<any> {\n if (!this.signals.has(key)) {\n const initialValue = this.get(key, defaultValue as T[K]);\n this.signals.set(key, signal(initialValue));\n }\n return this.signals.get(key)!.asReadonly();\n }\n\n /**\n * Set typed data in localStorage\n * @param key The key to set\n * @param value The value to store\n */\n set<K extends keyof T>(key: K, value: T[K]): void {\n try {\n this.storage.setItem(key as string, JSON.stringify(value));\n } catch (e) {\n console.error(\n `Error saving to storage for key \"${String(key)}\". Storage quota may be exceeded.`,\n e,\n );\n }\n if (this.signals.has(key)) {\n this.signals.get(key)!.set(value);\n }\n }\n\n /**\n * Update typed data based on current value using a callback\n * @param key The key to update\n * @param updateFn Callback function that receives the current value and returns the new value\n */\n update<K extends keyof T>(key: K, updateFn: (currentValue: T[K] | null) => T[K]): void {\n const currentValue = this.get(key);\n const newValue = updateFn(currentValue);\n this.set(key, newValue);\n }\n\n /**\n * Remove an item from localStorage\n * @param key The key to remove\n */\n remove<K extends keyof T>(key: K): void {\n this.storage.removeItem(key as string);\n if (this.signals.has(key)) {\n this.signals.get(key)!.set(null);\n }\n }\n\n /**\n * Check if a key exists in localStorage\n * @param key The key to check\n * @returns true if the key exists, false otherwise\n */\n has<K extends keyof T>(key: K): boolean {\n return this.storage.getItem(key as string) !== null;\n }\n\n /** Clear all localStorage */\n clear(): void {\n this.storage.clear();\n for (const sig of this.signals.values()) {\n sig.set(null);\n }\n }\n}\n","/*\n * Public API Surface of signal-storage\n */\n\nexport * from './lib/signal-storage';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, Signal } from '@angular/core';
|
|
2
3
|
|
|
4
|
+
declare const SIGNAL_STORAGE_TOKEN: InjectionToken<Storage>;
|
|
3
5
|
declare class SignalStorageService<T extends Record<string, any> = {}> {
|
|
4
6
|
private storage;
|
|
5
7
|
private signals;
|
|
@@ -52,10 +54,10 @@ declare class SignalStorageService<T extends Record<string, any> = {}> {
|
|
|
52
54
|
* @returns true if the key exists, false otherwise
|
|
53
55
|
*/
|
|
54
56
|
has<K extends keyof T>(key: K): boolean;
|
|
55
|
-
/**
|
|
56
|
-
* Clear all localStorage
|
|
57
|
-
*/
|
|
57
|
+
/** Clear all localStorage */
|
|
58
58
|
clear(): void;
|
|
59
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SignalStorageService<any>, [{ optional: true; }]>;
|
|
60
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<SignalStorageService<any>>;
|
|
59
61
|
}
|
|
60
62
|
|
|
61
|
-
export { SignalStorageService };
|
|
63
|
+
export { SIGNAL_STORAGE_TOKEN, SignalStorageService };
|