@telperion/ng-pack 0.1.3

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 ADDED
@@ -0,0 +1,95 @@
1
+ # @telperion/ng-pack
2
+
3
+ A collection of Angular utilities and libraries
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @telperion/ng-pack
9
+ ```
10
+
11
+ ## Available Modules
12
+
13
+ ### Storage Signals
14
+
15
+ **Import:** `@telperion/ng-pack/storage-signals`
16
+
17
+ Angular signals-based wrapper for browser's localStorage and sessionStorage with reactive updates.
18
+
19
+ #### Key Features
20
+
21
+ - 🚀 Signal-based API integrated with Angular's signal system
22
+ - 🔄 Reactive updates automatically synced across components
23
+ - 🎯 Nested property access using dot notation
24
+ - 🏪 Support for both localStorage and sessionStorage
25
+ - 🔑 Namespaced storage organization
26
+
27
+ #### Quick Start
28
+
29
+ ```typescript
30
+ import { ApplicationConfig } from '@angular/core';
31
+ import { provideLocalStorage } from '@telperion/ng-pack/storage-signals';
32
+
33
+ export const appConfig: ApplicationConfig = {
34
+ providers: [
35
+ provideLocalStorage('my-app'),
36
+ ]
37
+ };
38
+ ```
39
+
40
+ ```typescript
41
+ import { Component } from '@angular/core';
42
+ import { localStorageSignal } from '@telperion/ng-pack/storage-signals';
43
+
44
+ @Component({
45
+ selector: 'app-settings',
46
+ template: `
47
+ <div>
48
+ <p>Theme: {{ theme() }}</p>
49
+ <button (click)="theme.set('dark')">Dark Mode</button>
50
+ </div>
51
+ `
52
+ })
53
+ export class SettingsComponent {
54
+ // Access nested properties with dot notation
55
+ theme = localStorageSignal<string>('settings', 'ui.theme');
56
+ }
57
+ ```
58
+
59
+ [Full documentation →](./storage-signals/README.md)
60
+
61
+ ---
62
+
63
+ ### Template Signal Forms
64
+
65
+ **Import:** `@telperion/ng-pack/template-signal-forms`
66
+
67
+ 🚧 **Under Construction**
68
+
69
+ Signal-based forms utilities for Angular template-driven forms.
70
+
71
+ ---
72
+
73
+ ### Utils
74
+
75
+ **Import:** `@telperion/ng-pack/utils`
76
+
77
+ 🚧 **Under Construction**
78
+
79
+ Common Angular utilities and helpers.
80
+
81
+ ---
82
+
83
+ ## Development
84
+
85
+ ### Running Unit Tests
86
+
87
+ Run `pnpm nx test ng-pack` to execute the unit tests.
88
+
89
+ ### Building
90
+
91
+ Run `pnpm nx build ng-pack` to build the library.
92
+
93
+ ## License
94
+
95
+ This library is part of the Telperion monorepo.
@@ -0,0 +1,66 @@
1
+ import { InjectionToken, inject } from '@angular/core';
2
+ import { ReactiveWebLocalStorage, ReactiveWebSessionStorage } from '@thalesrc/reactive-storage';
3
+ import { toSignal } from '@angular/core/rxjs-interop';
4
+
5
+ function storageSignal(storage, store, key) {
6
+ const source = toSignal(storage.get(store, key));
7
+ function setter(value) {
8
+ storage.set(store, key, value);
9
+ }
10
+ function updater(updaterFn) {
11
+ const currentValue = source();
12
+ const newValue = updaterFn(currentValue);
13
+ storage.set(store, key, newValue);
14
+ }
15
+ function deleter() {
16
+ storage.delete(store, key);
17
+ }
18
+ return new Proxy(source, {
19
+ apply() {
20
+ return source();
21
+ },
22
+ get(target, prop, receiver) {
23
+ switch (prop) {
24
+ case 'set':
25
+ return setter;
26
+ case 'update':
27
+ return updater;
28
+ case 'delete':
29
+ return deleter;
30
+ default:
31
+ return Reflect.get(target, prop, receiver);
32
+ }
33
+ }
34
+ });
35
+ }
36
+
37
+ const LOCAL_STORAGE = new InjectionToken('Telperion Local Storage');
38
+ function provideLocalStorage(appName) {
39
+ return {
40
+ provide: LOCAL_STORAGE,
41
+ useValue: new ReactiveWebLocalStorage(appName)
42
+ };
43
+ }
44
+ function localStorageSignal(store, key) {
45
+ const storage = inject(LOCAL_STORAGE);
46
+ return storageSignal(storage, store, key);
47
+ }
48
+
49
+ const SESSION_STORAGE = new InjectionToken('Telperion Session Storage');
50
+ function provideSessionStorage(appName) {
51
+ return {
52
+ provide: SESSION_STORAGE,
53
+ useValue: new ReactiveWebSessionStorage(appName)
54
+ };
55
+ }
56
+ function sessionStorageSignal(store, key) {
57
+ const storage = inject(SESSION_STORAGE);
58
+ return storageSignal(storage, store, key);
59
+ }
60
+
61
+ /**
62
+ * Generated bundle index. Do not edit.
63
+ */
64
+
65
+ export { localStorageSignal, provideLocalStorage, provideSessionStorage, sessionStorageSignal };
66
+ //# sourceMappingURL=telperion-ng-pack-storage-signals.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telperion-ng-pack-storage-signals.mjs","sources":["../../storage-signals/src/storage.ts","../../storage-signals/src/local-storage.ts","../../storage-signals/src/session-storage.ts","../../storage-signals/src/telperion-ng-pack-storage-signals.ts"],"sourcesContent":["import { WritableSignal } from \"@angular/core\";\r\nimport { toSignal } from \"@angular/core/rxjs-interop\";\r\nimport { ReactiveStorage } from '@thalesrc/reactive-storage';\r\n\r\nexport interface StorageSignal<T> extends WritableSignal<T | null | undefined> {\r\n delete(): void;\r\n}\r\n\r\nexport function storageSignal<T>(storage: ReactiveStorage, store: string, key: string): StorageSignal<T> {\r\n const source = toSignal(storage.get<T>(store, key));\r\n\r\n function setter(value: T | null | undefined) {\r\n storage.set(store, key, value);\r\n }\r\n\r\n function updater(updaterFn: (value: T | null | undefined) => T | null | undefined) {\r\n const currentValue = source();\r\n const newValue = updaterFn(currentValue);\r\n\r\n storage.set(store, key, newValue);\r\n }\r\n\r\n function deleter() {\r\n storage.delete(store, key);\r\n }\r\n\r\n return new Proxy(source as StorageSignal<T>, {\r\n apply() {\r\n return source();\r\n },\r\n get(target, prop, receiver) {\r\n switch (prop) {\r\n case 'set':\r\n return setter;\r\n case 'update':\r\n return updater;\r\n case 'delete':\r\n return deleter;\r\n default:\r\n return Reflect.get(target, prop, receiver);\r\n }\r\n }\r\n })\r\n}\r\n","import { inject, InjectionToken, Provider } from \"@angular/core\";\r\nimport { ReactiveWebLocalStorage } from '@thalesrc/reactive-storage';\r\n\r\nimport { StorageSignal, storageSignal } from \"./storage\";\r\n\r\nconst LOCAL_STORAGE = new InjectionToken<ReactiveWebLocalStorage>('Telperion Local Storage');\r\n\r\nexport function provideLocalStorage(appName?: string): Provider {\r\n return {\r\n provide: LOCAL_STORAGE,\r\n useValue: new ReactiveWebLocalStorage(appName)\r\n };\r\n}\r\n\r\nexport function localStorageSignal<T>(store: string, key: string): StorageSignal<T> {\r\n const storage = inject(LOCAL_STORAGE);\r\n\r\n return storageSignal(storage, store, key);\r\n}\r\n","import { inject, InjectionToken, Provider } from \"@angular/core\";\r\nimport { ReactiveWebSessionStorage } from '@thalesrc/reactive-storage';\r\n\r\nimport { StorageSignal, storageSignal } from \"./storage\";\r\n\r\nconst SESSION_STORAGE = new InjectionToken<ReactiveWebSessionStorage>('Telperion Session Storage');\r\n\r\nexport function provideSessionStorage(appName?: string): Provider {\r\n return {\r\n provide: SESSION_STORAGE,\r\n useValue: new ReactiveWebSessionStorage(appName)\r\n };\r\n}\r\n\r\nexport function sessionStorageSignal<T>(store: string, key: string): StorageSignal<T> {\r\n const storage = inject(SESSION_STORAGE);\r\n\r\n return storageSignal(storage, store, key);\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;SAQgB,aAAa,CAAI,OAAwB,EAAE,KAAa,EAAE,GAAW,EAAA;AACnF,IAAA,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAI,KAAK,EAAE,GAAG,CAAC,CAAC;IAEnD,SAAS,MAAM,CAAC,KAA2B,EAAA;QACzC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC;IAChC;IAEA,SAAS,OAAO,CAAC,SAAgE,EAAA;AAC/E,QAAA,MAAM,YAAY,GAAG,MAAM,EAAE;AAC7B,QAAA,MAAM,QAAQ,GAAG,SAAS,CAAC,YAAY,CAAC;QAExC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,CAAC;IACnC;AAEA,IAAA,SAAS,OAAO,GAAA;AACd,QAAA,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC;IAC5B;AAEA,IAAA,OAAO,IAAI,KAAK,CAAC,MAA0B,EAAE;QAC3C,KAAK,GAAA;YACH,OAAO,MAAM,EAAE;QACjB,CAAC;AACD,QAAA,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAA;YACxB,QAAQ,IAAI;AACV,gBAAA,KAAK,KAAK;AACR,oBAAA,OAAO,MAAM;AACf,gBAAA,KAAK,QAAQ;AACX,oBAAA,OAAO,OAAO;AAChB,gBAAA,KAAK,QAAQ;AACX,oBAAA,OAAO,OAAO;AAChB,gBAAA;oBACE,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC;;QAEhD;AACD,KAAA,CAAC;AACJ;;ACtCA,MAAM,aAAa,GAAG,IAAI,cAAc,CAA0B,yBAAyB,CAAC;AAEtF,SAAU,mBAAmB,CAAC,OAAgB,EAAA;IAClD,OAAO;AACL,QAAA,OAAO,EAAE,aAAa;AACtB,QAAA,QAAQ,EAAE,IAAI,uBAAuB,CAAC,OAAO;KAC9C;AACH;AAEM,SAAU,kBAAkB,CAAI,KAAa,EAAE,GAAW,EAAA;AAC9D,IAAA,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,CAAC;IAErC,OAAO,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC;AAC3C;;ACbA,MAAM,eAAe,GAAG,IAAI,cAAc,CAA4B,2BAA2B,CAAC;AAE5F,SAAU,qBAAqB,CAAC,OAAgB,EAAA;IACpD,OAAO;AACL,QAAA,OAAO,EAAE,eAAe;AACxB,QAAA,QAAQ,EAAE,IAAI,yBAAyB,CAAC,OAAO;KAChD;AACH;AAEM,SAAU,oBAAoB,CAAI,KAAa,EAAE,GAAW,EAAA;AAChE,IAAA,MAAM,OAAO,GAAG,MAAM,CAAC,eAAe,CAAC;IAEvC,OAAO,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC;AAC3C;;AClBA;;AAEG;;;;"}
@@ -0,0 +1,69 @@
1
+ import * as i0 from '@angular/core';
2
+ import { NgModule, forwardRef } from '@angular/core';
3
+
4
+ class UtilsModule {
5
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UtilsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
6
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.5", ngImport: i0, type: UtilsModule });
7
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UtilsModule });
8
+ }
9
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UtilsModule, decorators: [{
10
+ type: NgModule,
11
+ args: [{
12
+ imports: [],
13
+ }]
14
+ }] });
15
+
16
+ /**
17
+ * Utility function to create a provider for a directive/component that can be injected as a service.
18
+ * This is useful for cases where you want to inject a directive/component instance into another directive/component.
19
+ *
20
+ * Example usage:
21
+ *
22
+ * files:
23
+ * - parent.directive.ts
24
+ * - child.directive.ts
25
+ * - parent.service.ts
26
+ *
27
+ * parent.service.ts:
28
+ * ```
29
+ * import { InjectionToken } from "@angular/core";
30
+ * import type { ParentDirective } from "./parent.directive";
31
+ *
32
+ * export const ParentService = new InjectionToken<ParentDirective>("ParentService");
33
+ * ```
34
+ *
35
+ * parent.directive.ts:
36
+ * ```
37
+ * @Directive({
38
+ * selector: 'parent',
39
+ * providers: [provideServiceDirective(ParentService, ParentDirective)],
40
+ * })
41
+ * export class ParentDirective {...}
42
+ * ```
43
+ * child.directive.ts:
44
+ * ```
45
+ * @Directive({
46
+ * selector: 'child',
47
+ * })
48
+ * export class ChildDirective {
49
+ * #parent = inject(ParentService);
50
+ * }
51
+ * ```
52
+ *
53
+ * @param token - The injection token to provide.
54
+ * @param directive - The directive/component class that will be provided.
55
+ * @returns A provider object that can be used in the providers array of an Angular module or component.
56
+ */
57
+ function provideServiceDirective(token, directive) {
58
+ return {
59
+ provide: token,
60
+ useExisting: forwardRef(() => directive)
61
+ };
62
+ }
63
+
64
+ /**
65
+ * Generated bundle index. Do not edit.
66
+ */
67
+
68
+ export { UtilsModule, provideServiceDirective };
69
+ //# sourceMappingURL=telperion-ng-pack-utils.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telperion-ng-pack-utils.mjs","sources":["../../utils/src/lib/utils-module.ts","../../utils/src/lib/provide-service-directive.ts","../../utils/src/telperion-ng-pack-utils.ts"],"sourcesContent":["import { NgModule } from '@angular/core';\n\n@NgModule({\n imports: [],\n})\nexport class UtilsModule {}\n","import { forwardRef, InjectionToken, Provider, Type } from \"@angular/core\";\r\n\r\n/**\r\n * Utility function to create a provider for a directive/component that can be injected as a service.\r\n * This is useful for cases where you want to inject a directive/component instance into another directive/component.\r\n *\r\n * Example usage:\r\n *\r\n * files:\r\n * - parent.directive.ts\r\n * - child.directive.ts\r\n * - parent.service.ts\r\n *\r\n * parent.service.ts:\r\n * ```\r\n * import { InjectionToken } from \"@angular/core\";\r\n * import type { ParentDirective } from \"./parent.directive\";\r\n *\r\n * export const ParentService = new InjectionToken<ParentDirective>(\"ParentService\");\r\n * ```\r\n *\r\n * parent.directive.ts:\r\n * ```\r\n * @Directive({\r\n * selector: 'parent',\r\n * providers: [provideServiceDirective(ParentService, ParentDirective)],\r\n * })\r\n * export class ParentDirective {...}\r\n * ```\r\n * child.directive.ts:\r\n * ```\r\n * @Directive({\r\n * selector: 'child',\r\n * })\r\n * export class ChildDirective {\r\n * #parent = inject(ParentService);\r\n * }\r\n * ```\r\n *\r\n * @param token - The injection token to provide.\r\n * @param directive - The directive/component class that will be provided.\r\n * @returns A provider object that can be used in the providers array of an Angular module or component.\r\n */\r\nexport function provideServiceDirective<T extends Type<unknown>>(\r\n token: InjectionToken<T>,\r\n directive: T\r\n): Provider {\r\n return {\r\n provide: token,\r\n useExisting: forwardRef(() => directive)\r\n }\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;MAKa,WAAW,CAAA;uGAAX,WAAW,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,QAAA,EAAA,CAAA;wGAAX,WAAW,EAAA,CAAA;wGAAX,WAAW,EAAA,CAAA;;2FAAX,WAAW,EAAA,UAAA,EAAA,CAAA;kBAHvB,QAAQ;AAAC,YAAA,IAAA,EAAA,CAAA;AACR,oBAAA,OAAO,EAAE,EAAE;AACZ,iBAAA;;;ACFD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCG;AACG,SAAU,uBAAuB,CACrC,KAAwB,EACxB,SAAY,EAAA;IAEZ,OAAO;AACL,QAAA,OAAO,EAAE,KAAK;AACd,QAAA,WAAW,EAAE,UAAU,CAAC,MAAM,SAAS;KACxC;AACH;;ACnDA;;AAEG;;;;"}
@@ -0,0 +1,19 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Component } from '@angular/core';
3
+ import { CommonModule } from '@angular/common';
4
+
5
+ class NgPack {
6
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: NgPack, deps: [], target: i0.ɵɵFactoryTarget.Component });
7
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.5", type: NgPack, isStandalone: true, selector: "tng-ng-pack", ngImport: i0, template: `<p>NgPack works!</p>`, isInline: true, styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
8
+ }
9
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: NgPack, decorators: [{
10
+ type: Component,
11
+ args: [{ selector: 'tng-ng-pack', imports: [CommonModule], template: `<p>NgPack works!</p>` }]
12
+ }] });
13
+
14
+ /**
15
+ * Generated bundle index. Do not edit.
16
+ */
17
+
18
+ export { NgPack };
19
+ //# sourceMappingURL=telperion-ng-pack.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telperion-ng-pack.mjs","sources":["../../src/lib/ng-pack/ng-pack.ts","../../src/telperion-ng-pack.ts"],"sourcesContent":["import { Component } from '@angular/core';\nimport { CommonModule } from '@angular/common';\n\n@Component({\n selector: 'tng-ng-pack',\n imports: [CommonModule],\n template: `<p>NgPack works!</p>`,\n styles: ``,\n})\nexport class NgPack {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;MASa,MAAM,CAAA;uGAAN,MAAM,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAN,MAAM,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,aAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAHP,CAAA,oBAAA,CAAsB,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,EAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EADtB,YAAY,EAAA,CAAA,EAAA,CAAA;;2FAIX,MAAM,EAAA,UAAA,EAAA,CAAA;kBANlB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,aAAa,EAAA,OAAA,EACd,CAAC,YAAY,CAAC,YACb,CAAA,oBAAA,CAAsB,EAAA;;;ACNlC;;AAEG;;;;"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@telperion/ng-pack",
3
+ "version": "0.1.3",
4
+ "peerDependencies": {
5
+ "@angular/common": "^21.0.0",
6
+ "@angular/core": "^21.0.0",
7
+ "@angular/forms": "^21.0.0",
8
+ "rxjs": "^7.8.1"
9
+ },
10
+ "dependencies": {
11
+ "@thalesrc/reactive-storage": "^1.0.5",
12
+ "tslib": "^2.3.0"
13
+ },
14
+ "sideEffects": false,
15
+ "bugs": "https://github.com/telperiontech/telperion/issues",
16
+ "license": "MIT",
17
+ "homepage": "https://open-source.telperion.tr",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/telperiontech/telperion.git"
21
+ },
22
+ "author": {
23
+ "name": "Ali Şahin Özçelik",
24
+ "email": "alisahinozcelik@gmail.com",
25
+ "url": "https://alisah.in"
26
+ },
27
+ "funding": {
28
+ "type": "patreon",
29
+ "url": "https://www.patreon.com/alisahin"
30
+ },
31
+ "module": "fesm2022/telperion-ng-pack.mjs",
32
+ "typings": "types/telperion-ng-pack.d.ts",
33
+ "exports": {
34
+ "./package.json": {
35
+ "default": "./package.json"
36
+ },
37
+ ".": {
38
+ "types": "./types/telperion-ng-pack.d.ts",
39
+ "default": "./fesm2022/telperion-ng-pack.mjs"
40
+ },
41
+ "./storage-signals": {
42
+ "types": "./types/telperion-ng-pack-storage-signals.d.ts",
43
+ "default": "./fesm2022/telperion-ng-pack-storage-signals.mjs"
44
+ },
45
+ "./utils": {
46
+ "types": "./types/telperion-ng-pack-utils.d.ts",
47
+ "default": "./fesm2022/telperion-ng-pack-utils.mjs"
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,335 @@
1
+ # @telperion/ng-pack/storage-signals
2
+
3
+ Angular signals-based wrapper for browser's localStorage and sessionStorage with reactive updates.
4
+
5
+ ## Features
6
+
7
+ - 🚀 **Signal-based API** - Seamlessly integrate with Angular's signal system
8
+ - 🔄 **Reactive updates** - Automatically sync changes across components
9
+ - 🎯 **Type-safe** - Full TypeScript support with generic typing
10
+ - 🏪 **Dual storage support** - Works with both localStorage and sessionStorage
11
+ - 🔑 **Namespaced storage** - Organize storage with app names and store names
12
+ - 🎯 **Nested property access** - Access deep object properties using dot notation
13
+ - 🧹 **Simple API** - Signal interface with `set()`, `update()`, and `delete()` methods
14
+
15
+ ## Installation
16
+
17
+ This is a secondary entry point of `@telperion/ng-pack`. Import from `@telperion/ng-pack/storage-signals`.
18
+
19
+ ```bash
20
+ npm install @telperion/ng-pack
21
+ ```
22
+
23
+ ## Setup
24
+
25
+ ### Local Storage
26
+
27
+ Configure localStorage in your application config:
28
+
29
+ ```typescript
30
+ import { ApplicationConfig } from '@angular/core';
31
+ import { provideLocalStorage } from '@telperion/ng-pack/storage-signals';
32
+
33
+ export const appConfig: ApplicationConfig = {
34
+ providers: [
35
+ provideLocalStorage('my-app'), // Optional app name for namespacing
36
+ // ... other providers
37
+ ]
38
+ };
39
+ ```
40
+
41
+ ### Session Storage
42
+
43
+ Configure sessionStorage in your application config:
44
+
45
+ ```typescript
46
+ import { ApplicationConfig } from '@angular/core';
47
+ import { provideSessionStorage } from '@telperion/ng-pack/storage-signals';
48
+
49
+ export const appConfig: ApplicationConfig = {
50
+ providers: [
51
+ provideSessionStorage('my-app'), // Optional app name for namespacing
52
+ // ... other providers
53
+ ]
54
+ };
55
+ ```
56
+
57
+ ## Usage
58
+
59
+ ### Basic Usage
60
+
61
+ ```typescript
62
+ import { Component } from '@angular/core';
63
+ import { localStorageSignal } from '@telperion/ng-pack/storage-signals';
64
+
65
+ @Component({
66
+ selector: 'app-user-settings',
67
+ template: `
68
+ <div>
69
+ <p>Theme: {{ theme() }}</p>
70
+ <button (click)="toggleTheme()">Toggle Theme</button>
71
+ <button (click)="resetTheme()">Reset</button>
72
+ </div>
73
+ `
74
+ })
75
+ export class UserSettingsComponent {
76
+ // Create a signal connected to localStorage
77
+ theme = localStorageSignal<string>('settings', 'theme');
78
+
79
+ toggleTheme() {
80
+ this.theme.update(current => current === 'dark' ? 'light' : 'dark');
81
+ }
82
+
83
+ resetTheme() {
84
+ this.theme.delete();
85
+ }
86
+ }
87
+ ```
88
+
89
+ ### Session Storage
90
+
91
+ ```typescript
92
+ import { Component } from '@angular/core';
93
+ import { sessionStorageSignal } from '@telperion/ng-pack/storage-signals';
94
+
95
+ @Component({
96
+ selector: 'app-wizard',
97
+ template: `
98
+ <div>
99
+ <p>Current Step: {{ currentStep() }}</p>
100
+ <button (click)="nextStep()">Next</button>
101
+ </div>
102
+ `
103
+ })
104
+ export class WizardComponent {
105
+ currentStep = sessionStorageSignal<number>('wizard', 'currentStep');
106
+
107
+ nextStep() {
108
+ this.currentStep.update(step => (step ?? 0) + 1);
109
+ }
110
+ }
111
+ ```
112
+
113
+ ### Nested Property Access
114
+
115
+ One of the most powerful features is the ability to access nested object properties using dot notation:
116
+
117
+ ```typescript
118
+ @Component({
119
+ selector: 'app-preferences',
120
+ template: `
121
+ <div>
122
+ <p>Theme: {{ theme() }}</p>
123
+ <p>Language: {{ language() }}</p>
124
+ <p>Notifications: {{ notifications() }}</p>
125
+ <button (click)="updateLanguage('es')">Spanish</button>
126
+ <button (click)="toggleNotifications()">Toggle Notifications</button>
127
+ </div>
128
+ `
129
+ })
130
+ export class PreferencesComponent {
131
+ // Access nested properties directly with dot notation
132
+ theme = localStorageSignal<string>('user', 'preferences.theme');
133
+ language = localStorageSignal<string>('user', 'preferences.language');
134
+ notifications = localStorageSignal<boolean>('user', 'preferences.notifications');
135
+
136
+ ngOnInit() {
137
+ // Initialize with default values if not set
138
+ if (!this.theme()) this.theme.set('light');
139
+ if (!this.language()) this.language.set('en');
140
+ if (this.notifications() === undefined) this.notifications.set(true);
141
+ }
142
+
143
+ updateLanguage(language: string) {
144
+ this.language.set(language);
145
+ }
146
+
147
+ toggleNotifications() {
148
+ this.notifications.update(enabled => !enabled);
149
+ }
150
+ }
151
+ ```
152
+
153
+ This approach provides several benefits:
154
+ - Each property has its own reactive signal
155
+ - No need for optional chaining or null checks in templates
156
+ - Updates to one property don't trigger re-renders for components using other properties
157
+ - More granular control and better performance
158
+
159
+ ### Cross-Component Synchronization
160
+
161
+ Changes to storage signals are automatically synchronized across all components:
162
+
163
+ ```typescript
164
+ // Component A
165
+ @Component({
166
+ selector: 'app-sidebar',
167
+ template: `<p>Count: {{ count() }}</p>`
168
+ })
169
+ export class SidebarComponent {
170
+ count = localStorageSignal<number>('app', 'count');
171
+ }
172
+
173
+ // Component B
174
+ @Component({
175
+ selector: 'app-header',
176
+ template: `
177
+ <button (click)="increment()">
178
+ Count: {{ count() }}
179
+ </button>
180
+ `
181
+ })
182
+ export class HeaderComponent {
183
+ count = localStorageSignal<number>('app', 'count');
184
+
185
+ increment() {
186
+ this.count.update(n => (n ?? 0) + 1);
187
+ }
188
+ }
189
+ // Both components will show the same value and update together!
190
+ ```
191
+
192
+ ## API Reference
193
+
194
+ ### `provideLocalStorage(appName?: string): Provider`
195
+
196
+ Creates a provider for localStorage integration.
197
+
198
+ **Parameters:**
199
+ - `appName` (optional) - Namespace prefix for all storage keys
200
+
201
+ **Returns:** Angular Provider
202
+
203
+ ---
204
+
205
+ ### `provideSessionStorage(appName?: string): Provider`
206
+
207
+ Creates a provider for sessionStorage integration.
208
+
209
+ **Parameters:**
210
+ - `appName` (optional) - Namespace prefix for all storage keys
211
+
212
+ **Returns:** Angular Provider
213
+
214
+ ---
215
+
216
+ ### `localStorageSignal<T>(store: string, key: string): StorageSignal<T>`
217
+
218
+ Creates a signal connected to localStorage. Must be called within an injection context.
219
+
220
+ **Parameters:**
221
+ - `store` - Storage namespace/category (e.g., 'settings', 'user')
222
+ - `key` - Unique key within the store
223
+
224
+ **Returns:** `StorageSignal<T>`
225
+
226
+ ---
227
+
228
+ ### `sessionStorageSignal<T>(store: string, key: string): StorageSignal<T>`
229
+
230
+ Creates a signal connected to sessionStorage. Must be called within an injection context.
231
+
232
+ **Parameters:**
233
+ - `store` - Storage namespace/category (e.g., 'wizard', 'form-data')
234
+ - `key` - Unique key within the store
235
+
236
+ **Returns:** `StorageSignal<T>`
237
+
238
+ ---
239
+
240
+ ### `StorageSignal<T>`
241
+
242
+ A signal interface extending Angular's `WritableSignal` with additional storage methods.
243
+
244
+ **Methods:**
245
+ - `()` - Read the current value
246
+ - `set(value: T | null | undefined)` - Set a new value
247
+ - `update(fn: (value: T | null | undefined) => T | null | undefined)` - Update using a function
248
+ - `delete()` - Remove the value from storage
249
+
250
+ ## Storage Organization
251
+
252
+ The library uses a hierarchical storage structure:
253
+
254
+ ```
255
+ [appName]:[store]:[key] = value
256
+ ```
257
+
258
+ Example:
259
+ ```typescript
260
+ // With provideLocalStorage('my-app')
261
+ localStorageSignal<string>('settings', 'theme');
262
+ // Creates key: "my-app:settings:theme"
263
+
264
+ localStorageSignal<number>('user', 'id');
265
+ // Creates key: "my-app:user:id"
266
+ ```
267
+
268
+ This organization helps prevent key collisions and makes storage management cleaner.
269
+
270
+ ## TypeScript Support
271
+
272
+ The library is fully typed with TypeScript generics:
273
+
274
+ ```typescript
275
+ // Type-safe signals
276
+ const theme = localStorageSignal<'light' | 'dark'>('settings', 'theme');
277
+ theme.set('light'); // ✓ OK
278
+ theme.set('blue'); // ✗ Type error
279
+
280
+ const count = localStorageSignal<number>('app', 'count');
281
+ count.set(42); // ✓ OK
282
+ count.set('42'); // ✗ Type error
283
+
284
+ // Complex types
285
+ interface User {
286
+ id: number;
287
+ name: string;
288
+ }
289
+
290
+ const user = localStorageSignal<User>('auth', 'currentUser');
291
+ user.set({ id: 1, name: 'Alice' }); // ✓ OK
292
+ ```
293
+
294
+ ## Best Practices
295
+
296
+ 1. **Use dot notation for nested properties** - Access deep object properties directly
297
+ ```typescript
298
+ // Instead of managing the whole object
299
+ const user = localStorageSignal<User>('app', 'user');
300
+ const name = user()?.profile?.name;
301
+
302
+ // Access nested properties directly
303
+ const userName = localStorageSignal<string>('app', 'user.profile.name');
304
+ const userEmail = localStorageSignal<string>('app', 'user.profile.email');
305
+ ```
306
+
307
+ 2. **Use meaningful store names** - Group related keys together
308
+ ```typescript
309
+ localStorageSignal('user-preferences', 'theme');
310
+ localStorageSignal('user-preferences', 'language');
311
+ ```
312
+
313
+ 3. **Initialize with defaults** - Check for null/undefined initial values
314
+ ```typescript
315
+ const theme = localStorageSignal<string>('settings', 'theme');
316
+ if (!theme()) {
317
+ theme.set('light');
318
+ }
319
+ ```
320
+
321
+ 4. **Clean up sensitive data** - Use `delete()` when appropriate
322
+ ```typescript
323
+ onLogout() {
324
+ this.authToken.delete();
325
+ }
326
+ ```
327
+
328
+ 5. **Use sessionStorage for temporary data** - Forms, wizards, temporary state
329
+ ```typescript
330
+ const formDraft = sessionStorageSignal('form', 'draft');
331
+ ```
332
+
333
+ ## License
334
+
335
+ This library is part of the Telperion monorepo.
@@ -0,0 +1,13 @@
1
+ import { WritableSignal, Provider } from '@angular/core';
2
+
3
+ interface StorageSignal<T> extends WritableSignal<T | null | undefined> {
4
+ delete(): void;
5
+ }
6
+
7
+ declare function provideLocalStorage(appName?: string): Provider;
8
+ declare function localStorageSignal<T>(store: string, key: string): StorageSignal<T>;
9
+
10
+ declare function provideSessionStorage(appName?: string): Provider;
11
+ declare function sessionStorageSignal<T>(store: string, key: string): StorageSignal<T>;
12
+
13
+ export { localStorageSignal, provideLocalStorage, provideSessionStorage, sessionStorageSignal };
@@ -0,0 +1,53 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Type, InjectionToken, Provider } from '@angular/core';
3
+
4
+ declare class UtilsModule {
5
+ static ɵfac: i0.ɵɵFactoryDeclaration<UtilsModule, never>;
6
+ static ɵmod: i0.ɵɵNgModuleDeclaration<UtilsModule, never, never, never>;
7
+ static ɵinj: i0.ɵɵInjectorDeclaration<UtilsModule>;
8
+ }
9
+
10
+ /**
11
+ * Utility function to create a provider for a directive/component that can be injected as a service.
12
+ * This is useful for cases where you want to inject a directive/component instance into another directive/component.
13
+ *
14
+ * Example usage:
15
+ *
16
+ * files:
17
+ * - parent.directive.ts
18
+ * - child.directive.ts
19
+ * - parent.service.ts
20
+ *
21
+ * parent.service.ts:
22
+ * ```
23
+ * import { InjectionToken } from "@angular/core";
24
+ * import type { ParentDirective } from "./parent.directive";
25
+ *
26
+ * export const ParentService = new InjectionToken<ParentDirective>("ParentService");
27
+ * ```
28
+ *
29
+ * parent.directive.ts:
30
+ * ```
31
+ * @Directive({
32
+ * selector: 'parent',
33
+ * providers: [provideServiceDirective(ParentService, ParentDirective)],
34
+ * })
35
+ * export class ParentDirective {...}
36
+ * ```
37
+ * child.directive.ts:
38
+ * ```
39
+ * @Directive({
40
+ * selector: 'child',
41
+ * })
42
+ * export class ChildDirective {
43
+ * #parent = inject(ParentService);
44
+ * }
45
+ * ```
46
+ *
47
+ * @param token - The injection token to provide.
48
+ * @param directive - The directive/component class that will be provided.
49
+ * @returns A provider object that can be used in the providers array of an Angular module or component.
50
+ */
51
+ declare function provideServiceDirective<T extends Type<unknown>>(token: InjectionToken<T>, directive: T): Provider;
52
+
53
+ export { UtilsModule, provideServiceDirective };
@@ -0,0 +1,8 @@
1
+ import * as i0 from '@angular/core';
2
+
3
+ declare class NgPack {
4
+ static ɵfac: i0.ɵɵFactoryDeclaration<NgPack, never>;
5
+ static ɵcmp: i0.ɵɵComponentDeclaration<NgPack, "tng-ng-pack", never, {}, {}, never, never, true, never>;
6
+ }
7
+
8
+ export { NgPack };
@@ -0,0 +1,3 @@
1
+ # @telperion/ng-pack/utils
2
+
3
+ Secondary entry point of `@telperion/ng-pack`. It can be used by importing from `@telperion/ng-pack/utils`.