@shival99/z-ui 0.0.1 → 1.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/assets/css/animations.css +207 -0
- package/assets/css/base.css +76 -0
- package/assets/css/tailwind.css +53 -0
- package/assets/css/themes/gray.css +73 -0
- package/assets/css/themes/green.css +75 -0
- package/assets/css/themes/hospital.css +79 -0
- package/assets/css/themes/neutral.css +73 -0
- package/assets/css/themes/orange.css +73 -0
- package/assets/css/themes/slate.css +73 -0
- package/assets/css/themes/stone.css +73 -0
- package/assets/css/themes/violet.css +73 -0
- package/assets/css/themes/zinc.css +73 -0
- package/assets/images/avatar.svg +6 -0
- package/assets/images/logo.svg +6 -0
- package/fesm2022/shival99-z-ui-components-z-accordion.mjs +148 -0
- package/fesm2022/shival99-z-ui-components-z-accordion.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-breadcrumb.mjs +74 -0
- package/fesm2022/shival99-z-ui-components-z-breadcrumb.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-button.mjs +155 -0
- package/fesm2022/shival99-z-ui-components-z-button.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-calendar.mjs +2335 -0
- package/fesm2022/shival99-z-ui-components-z-calendar.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-checkbox.mjs +240 -0
- package/fesm2022/shival99-z-ui-components-z-checkbox.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-code.mjs +139 -0
- package/fesm2022/shival99-z-ui-components-z-code.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-drawer.mjs +664 -0
- package/fesm2022/shival99-z-ui-components-z-drawer.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-dropdown-menu.mjs +55 -0
- package/fesm2022/shival99-z-ui-components-z-dropdown-menu.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-editor.mjs +411 -0
- package/fesm2022/shival99-z-ui-components-z-editor.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-filter.mjs +794 -0
- package/fesm2022/shival99-z-ui-components-z-filter.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-icon.mjs +451 -0
- package/fesm2022/shival99-z-ui-components-z-icon.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-input.mjs +804 -0
- package/fesm2022/shival99-z-ui-components-z-input.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-loading.mjs +105 -0
- package/fesm2022/shival99-z-ui-components-z-loading.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-menu.mjs +351 -0
- package/fesm2022/shival99-z-ui-components-z-menu.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-modal.mjs +722 -0
- package/fesm2022/shival99-z-ui-components-z-modal.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-pagination.mjs +131 -0
- package/fesm2022/shival99-z-ui-components-z-pagination.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-popover.mjs +917 -0
- package/fesm2022/shival99-z-ui-components-z-popover.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-radio.mjs +154 -0
- package/fesm2022/shival99-z-ui-components-z-radio.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-select.mjs +998 -0
- package/fesm2022/shival99-z-ui-components-z-select.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-skeleton.mjs +139 -0
- package/fesm2022/shival99-z-ui-components-z-skeleton.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-switch.mjs +127 -0
- package/fesm2022/shival99-z-ui-components-z-switch.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-table.mjs +2628 -0
- package/fesm2022/shival99-z-ui-components-z-table.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-tabs.mjs +259 -0
- package/fesm2022/shival99-z-ui-components-z-tabs.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-timeline.mjs +335 -0
- package/fesm2022/shival99-z-ui-components-z-timeline.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-toast.mjs +93 -0
- package/fesm2022/shival99-z-ui-components-z-toast.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-tooltip.mjs +660 -0
- package/fesm2022/shival99-z-ui-components-z-tooltip.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-upload.mjs +504 -0
- package/fesm2022/shival99-z-ui-components-z-upload.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-i18n.mjs +258 -0
- package/fesm2022/shival99-z-ui-i18n.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-pipes.mjs +116 -0
- package/fesm2022/shival99-z-ui-pipes.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-providers.mjs +203 -0
- package/fesm2022/shival99-z-ui-providers.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-services.mjs +919 -0
- package/fesm2022/shival99-z-ui-services.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-utils.mjs +591 -0
- package/fesm2022/shival99-z-ui-utils.mjs.map +1 -0
- package/fesm2022/z-ui.mjs +3 -19924
- package/fesm2022/z-ui.mjs.map +1 -1
- package/package.json +129 -1
- package/types/shival99-z-ui-components-z-accordion.d.ts +55 -0
- package/types/shival99-z-ui-components-z-breadcrumb.d.ts +36 -0
- package/types/shival99-z-ui-components-z-button.d.ts +41 -0
- package/types/shival99-z-ui-components-z-calendar.d.ts +300 -0
- package/types/shival99-z-ui-components-z-checkbox.d.ts +84 -0
- package/types/shival99-z-ui-components-z-code.d.ts +35 -0
- package/types/shival99-z-ui-components-z-drawer.d.ts +232 -0
- package/types/shival99-z-ui-components-z-dropdown-menu.d.ts +50 -0
- package/types/shival99-z-ui-components-z-editor.d.ts +115 -0
- package/types/shival99-z-ui-components-z-filter.d.ts +268 -0
- package/types/shival99-z-ui-components-z-icon.d.ts +291 -0
- package/types/shival99-z-ui-components-z-input.d.ts +188 -0
- package/types/shival99-z-ui-components-z-loading.d.ts +46 -0
- package/types/shival99-z-ui-components-z-menu.d.ts +116 -0
- package/types/shival99-z-ui-components-z-modal.d.ts +270 -0
- package/types/shival99-z-ui-components-z-pagination.d.ts +52 -0
- package/types/shival99-z-ui-components-z-popover.d.ts +134 -0
- package/types/shival99-z-ui-components-z-radio.d.ts +63 -0
- package/types/shival99-z-ui-components-z-select.d.ts +268 -0
- package/types/shival99-z-ui-components-z-skeleton.d.ts +55 -0
- package/types/shival99-z-ui-components-z-switch.d.ts +48 -0
- package/types/shival99-z-ui-components-z-table.d.ts +482 -0
- package/types/shival99-z-ui-components-z-tabs.d.ts +75 -0
- package/types/shival99-z-ui-components-z-timeline.d.ts +98 -0
- package/types/shival99-z-ui-components-z-toast.d.ts +61 -0
- package/types/shival99-z-ui-components-z-tooltip.d.ts +85 -0
- package/types/shival99-z-ui-components-z-upload.d.ts +136 -0
- package/types/shival99-z-ui-i18n.d.ts +50 -0
- package/types/shival99-z-ui-pipes.d.ts +36 -0
- package/types/shival99-z-ui-providers.d.ts +132 -0
- package/types/shival99-z-ui-services.d.ts +364 -0
- package/types/shival99-z-ui-utils.d.ts +145 -0
- package/types/z-ui.d.ts +3 -4977
|
@@ -0,0 +1,919 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Injectable, inject, signal } from '@angular/core';
|
|
3
|
+
import { BehaviorSubject, Subject, firstValueFrom } from 'rxjs';
|
|
4
|
+
import { DOCUMENT } from '@angular/common';
|
|
5
|
+
import { TranslateService } from '@ngx-translate/core';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Z-Cache Service
|
|
9
|
+
* A localStorage-based cache service with optional encryption
|
|
10
|
+
*/
|
|
11
|
+
const Z_CACHE_DEFAULT_PREFIX = 'z_';
|
|
12
|
+
class ZCacheService {
|
|
13
|
+
static _prefix = Z_CACHE_DEFAULT_PREFIX;
|
|
14
|
+
static _encrypt = true;
|
|
15
|
+
/**
|
|
16
|
+
* Configure the cache service
|
|
17
|
+
*/
|
|
18
|
+
static configure(config) {
|
|
19
|
+
if (config.prefix) {
|
|
20
|
+
ZCacheService._prefix = config.prefix;
|
|
21
|
+
}
|
|
22
|
+
if (config.encrypt !== undefined) {
|
|
23
|
+
ZCacheService._encrypt = config.encrypt;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
static _encode(data) {
|
|
27
|
+
const payload = { __z: true, __d: data };
|
|
28
|
+
return btoa(encodeURIComponent(JSON.stringify(payload)));
|
|
29
|
+
}
|
|
30
|
+
static _decode(data) {
|
|
31
|
+
try {
|
|
32
|
+
const decoded = JSON.parse(decodeURIComponent(atob(data)));
|
|
33
|
+
if (!decoded.__z) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return decoded.__d;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
static _getFullKey(key, encrypt) {
|
|
43
|
+
const prefixedKey = `${ZCacheService._prefix}${key}`;
|
|
44
|
+
return encrypt ? ZCacheService._encode(prefixedKey) : prefixedKey;
|
|
45
|
+
}
|
|
46
|
+
static _safeOperation(operation, fallback) {
|
|
47
|
+
try {
|
|
48
|
+
return operation();
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.error('[ZCache] Operation error:', error);
|
|
52
|
+
return fallback;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get a value from cache
|
|
57
|
+
* @param key - Cache key
|
|
58
|
+
* @param defaultValue - Default value if not found
|
|
59
|
+
* @param encrypt - Whether to use encryption (default: service config)
|
|
60
|
+
*/
|
|
61
|
+
static get(key, defaultValue, encrypt = ZCacheService._encrypt) {
|
|
62
|
+
return ZCacheService._safeOperation(() => {
|
|
63
|
+
const _key = ZCacheService._getFullKey(key, encrypt);
|
|
64
|
+
const result = localStorage.getItem(_key);
|
|
65
|
+
if (!result) {
|
|
66
|
+
return defaultValue;
|
|
67
|
+
}
|
|
68
|
+
const data = encrypt ? ZCacheService._decode(result) : JSON.parse(result);
|
|
69
|
+
return data ?? defaultValue;
|
|
70
|
+
}, defaultValue);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Set a value in cache
|
|
74
|
+
* @param key - Cache key
|
|
75
|
+
* @param data - Data to store
|
|
76
|
+
* @param encrypt - Whether to use encryption
|
|
77
|
+
*/
|
|
78
|
+
static set(key, data, encrypt = ZCacheService._encrypt) {
|
|
79
|
+
return ZCacheService._safeOperation(() => {
|
|
80
|
+
const _key = ZCacheService._getFullKey(key, encrypt);
|
|
81
|
+
const _value = encrypt ? ZCacheService._encode(data) : JSON.stringify(data);
|
|
82
|
+
localStorage.setItem(_key, _value);
|
|
83
|
+
return true;
|
|
84
|
+
}, false);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Delete a key from cache
|
|
88
|
+
* @param key - Cache key
|
|
89
|
+
* @param encrypt - Whether key is encrypted
|
|
90
|
+
*/
|
|
91
|
+
static delete(key, encrypt = ZCacheService._encrypt) {
|
|
92
|
+
return ZCacheService._safeOperation(() => {
|
|
93
|
+
const _key = ZCacheService._getFullKey(key, encrypt);
|
|
94
|
+
localStorage.removeItem(_key);
|
|
95
|
+
return true;
|
|
96
|
+
}, false);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Delete multiple keys from cache
|
|
100
|
+
* @param keys - Array of cache keys
|
|
101
|
+
* @param encrypt - Whether keys are encrypted
|
|
102
|
+
*/
|
|
103
|
+
static deleteMultiple(keys, encrypt = ZCacheService._encrypt) {
|
|
104
|
+
return ZCacheService._safeOperation(() => {
|
|
105
|
+
keys.forEach(key => {
|
|
106
|
+
const _key = ZCacheService._getFullKey(key, encrypt);
|
|
107
|
+
localStorage.removeItem(_key);
|
|
108
|
+
});
|
|
109
|
+
return true;
|
|
110
|
+
}, false);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Clear all cache with the configured prefix
|
|
114
|
+
*/
|
|
115
|
+
static clear() {
|
|
116
|
+
return ZCacheService._safeOperation(() => {
|
|
117
|
+
const keysToRemove = [];
|
|
118
|
+
const prefixEncoded = ZCacheService._encode(ZCacheService._prefix);
|
|
119
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
120
|
+
const key = localStorage.key(i);
|
|
121
|
+
if (key?.startsWith(ZCacheService._prefix) || key?.startsWith(prefixEncoded)) {
|
|
122
|
+
keysToRemove.push(key);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
keysToRemove.forEach(key => localStorage.removeItem(key));
|
|
126
|
+
return true;
|
|
127
|
+
}, false);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Check if a key exists in cache
|
|
131
|
+
* @param key - Cache key
|
|
132
|
+
* @param encrypt - Whether key is encrypted
|
|
133
|
+
*/
|
|
134
|
+
static has(key, encrypt = ZCacheService._encrypt) {
|
|
135
|
+
return ZCacheService._safeOperation(() => {
|
|
136
|
+
const _key = ZCacheService._getFullKey(key, encrypt);
|
|
137
|
+
return localStorage.getItem(_key) !== null;
|
|
138
|
+
}, false);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get all keys with the configured prefix
|
|
142
|
+
*/
|
|
143
|
+
static keys() {
|
|
144
|
+
return ZCacheService._safeOperation(() => {
|
|
145
|
+
const result = [];
|
|
146
|
+
const prefixEncoded = ZCacheService._encode(ZCacheService._prefix);
|
|
147
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
148
|
+
const key = localStorage.key(i);
|
|
149
|
+
if (key?.startsWith(ZCacheService._prefix)) {
|
|
150
|
+
result.push(key.slice(ZCacheService._prefix.length));
|
|
151
|
+
}
|
|
152
|
+
else if (key?.startsWith(prefixEncoded)) {
|
|
153
|
+
const decoded = ZCacheService._decode(key);
|
|
154
|
+
if (decoded) {
|
|
155
|
+
result.push(decoded.slice(ZCacheService._prefix.length));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return result;
|
|
160
|
+
}, []);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Z-IndexDB Service
|
|
166
|
+
* A robust IndexedDB service with multi-store support, encryption, and retry logic
|
|
167
|
+
*/
|
|
168
|
+
/** Default configuration */
|
|
169
|
+
const Z_INDEXDB_DEFAULT_CONFIG = {
|
|
170
|
+
dbName: 'ZDatabase',
|
|
171
|
+
version: 1,
|
|
172
|
+
mode: 'readwrite',
|
|
173
|
+
defaultStore: 'ZStore',
|
|
174
|
+
};
|
|
175
|
+
/** Batch size for bulk operations */
|
|
176
|
+
const Z_INDEXDB_BATCH_SIZE = 100;
|
|
177
|
+
class ZIndexDbService {
|
|
178
|
+
_db = null;
|
|
179
|
+
_dbName;
|
|
180
|
+
_version;
|
|
181
|
+
_mode;
|
|
182
|
+
_dbReady;
|
|
183
|
+
_stores = new Map();
|
|
184
|
+
_defaultStoreName;
|
|
185
|
+
_globalProtectedKeys;
|
|
186
|
+
constructor(config = {}) {
|
|
187
|
+
this._dbName = config.dbName ?? Z_INDEXDB_DEFAULT_CONFIG.dbName;
|
|
188
|
+
this._version = config.version ?? Z_INDEXDB_DEFAULT_CONFIG.version;
|
|
189
|
+
this._mode = config.mode ?? Z_INDEXDB_DEFAULT_CONFIG.mode;
|
|
190
|
+
this._defaultStoreName = config.defaultStore ?? Z_INDEXDB_DEFAULT_CONFIG.defaultStore;
|
|
191
|
+
this._globalProtectedKeys = config.protectedKeys ?? [];
|
|
192
|
+
if (config.stores?.length) {
|
|
193
|
+
config.stores.forEach(store => this._stores.set(store.name, store));
|
|
194
|
+
}
|
|
195
|
+
if (!this._stores.has(this._defaultStoreName)) {
|
|
196
|
+
this._stores.set(this._defaultStoreName, { name: this._defaultStoreName, encrypt: true });
|
|
197
|
+
}
|
|
198
|
+
this._dbReady = this._initDb();
|
|
199
|
+
}
|
|
200
|
+
_getStoreConfig(storeName) {
|
|
201
|
+
const name = storeName ?? this._defaultStoreName;
|
|
202
|
+
return this._stores.get(name) ?? { name, encrypt: true };
|
|
203
|
+
}
|
|
204
|
+
_encrypt(data) {
|
|
205
|
+
const payload = {
|
|
206
|
+
__z: true,
|
|
207
|
+
__t: typeof data,
|
|
208
|
+
__d: data,
|
|
209
|
+
};
|
|
210
|
+
return btoa(encodeURIComponent(JSON.stringify(payload)));
|
|
211
|
+
}
|
|
212
|
+
_decrypt(data) {
|
|
213
|
+
try {
|
|
214
|
+
const decoded = JSON.parse(decodeURIComponent(atob(data)));
|
|
215
|
+
if (!decoded.__z) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
return decoded.__d;
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
_closeConnection() {
|
|
225
|
+
if (this._db) {
|
|
226
|
+
this._db.close();
|
|
227
|
+
this._db = null;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async _reconnect() {
|
|
231
|
+
console.warn('[ZIndexDb] Reconnecting...');
|
|
232
|
+
this._closeConnection();
|
|
233
|
+
this._dbReady = this._initDb();
|
|
234
|
+
await this._dbReady;
|
|
235
|
+
}
|
|
236
|
+
async _withRetry(operation, retries = 3) {
|
|
237
|
+
let lastError;
|
|
238
|
+
for (let i = 0; i < retries; i++) {
|
|
239
|
+
try {
|
|
240
|
+
return await operation();
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
lastError = error;
|
|
244
|
+
if (error instanceof Error && error.name === 'InvalidStateError') {
|
|
245
|
+
await this._reconnect();
|
|
246
|
+
}
|
|
247
|
+
await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 100));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
throw lastError;
|
|
251
|
+
}
|
|
252
|
+
async _initDb() {
|
|
253
|
+
if (this._db) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
return new Promise((resolve, reject) => {
|
|
257
|
+
try {
|
|
258
|
+
const request = indexedDB.open(this._dbName, this._version);
|
|
259
|
+
request.onupgradeneeded = event => {
|
|
260
|
+
const db = event.target.result;
|
|
261
|
+
this._stores.forEach((_, storeName) => {
|
|
262
|
+
if (!db.objectStoreNames.contains(storeName)) {
|
|
263
|
+
db.createObjectStore(storeName);
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
request.onsuccess = event => {
|
|
268
|
+
this._db = event.target.result;
|
|
269
|
+
this._db.onerror = e => console.error('[ZIndexDb] Error:', e.target.error);
|
|
270
|
+
this._db.onclose = () => {
|
|
271
|
+
console.warn('[ZIndexDb] Connection closed unexpectedly');
|
|
272
|
+
this._db = null;
|
|
273
|
+
};
|
|
274
|
+
resolve();
|
|
275
|
+
};
|
|
276
|
+
request.onerror = event => {
|
|
277
|
+
console.error('[ZIndexDb] Open error:', event.target.error);
|
|
278
|
+
reject(event.target.error);
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
reject(error);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Get a value from the store
|
|
288
|
+
* @param key - The key to retrieve
|
|
289
|
+
* @param defaultValue - Default value if key not found
|
|
290
|
+
* @param storeName - Optional store name (uses default if not specified)
|
|
291
|
+
*/
|
|
292
|
+
async get(key, defaultValue, storeName) {
|
|
293
|
+
const storeConfig = this._getStoreConfig(storeName);
|
|
294
|
+
return this._withRetry(async () => {
|
|
295
|
+
await this._dbReady;
|
|
296
|
+
if (!this._db) {
|
|
297
|
+
await this._reconnect();
|
|
298
|
+
if (!this._db) {
|
|
299
|
+
return defaultValue;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
const _key = storeConfig.encrypt !== false ? this._encrypt(key) : key;
|
|
303
|
+
return new Promise(resolve => {
|
|
304
|
+
try {
|
|
305
|
+
const transaction = this._db.transaction([storeConfig.name], this._mode);
|
|
306
|
+
const store = transaction.objectStore(storeConfig.name);
|
|
307
|
+
const request = store.get(_key);
|
|
308
|
+
request.onsuccess = () => {
|
|
309
|
+
try {
|
|
310
|
+
const { result } = request;
|
|
311
|
+
if (!result) {
|
|
312
|
+
resolve(defaultValue);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
const decrypted = storeConfig.encrypt !== false ? this._decrypt(result) : result;
|
|
316
|
+
resolve(decrypted ?? defaultValue);
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
resolve(defaultValue);
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
request.onerror = () => resolve(defaultValue);
|
|
323
|
+
transaction.onerror = () => resolve(defaultValue);
|
|
324
|
+
}
|
|
325
|
+
catch {
|
|
326
|
+
resolve(defaultValue);
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Set a value in the store
|
|
333
|
+
* @param key - The key to set
|
|
334
|
+
* @param value - The value to store
|
|
335
|
+
* @param storeName - Optional store name
|
|
336
|
+
*/
|
|
337
|
+
async set(key, value, storeName) {
|
|
338
|
+
const storeConfig = this._getStoreConfig(storeName);
|
|
339
|
+
return this._withRetry(async () => {
|
|
340
|
+
await this._dbReady;
|
|
341
|
+
if (!this._db) {
|
|
342
|
+
await this._reconnect();
|
|
343
|
+
if (!this._db) {
|
|
344
|
+
throw new Error('Database not initialized');
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
const _key = storeConfig.encrypt !== false ? this._encrypt(key) : key;
|
|
348
|
+
const _value = storeConfig.encrypt !== false ? this._encrypt(value) : value;
|
|
349
|
+
return new Promise((resolve, reject) => {
|
|
350
|
+
try {
|
|
351
|
+
const transaction = this._db.transaction([storeConfig.name], 'readwrite');
|
|
352
|
+
const store = transaction.objectStore(storeConfig.name);
|
|
353
|
+
const request = store.put(_value, _key);
|
|
354
|
+
request.onerror = () => reject(request.error);
|
|
355
|
+
transaction.oncomplete = () => resolve();
|
|
356
|
+
transaction.onerror = () => reject(transaction.error);
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
reject(error);
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Delete a key from the store
|
|
366
|
+
* @param key - Key or array of keys to delete
|
|
367
|
+
* @param storeName - Optional store name
|
|
368
|
+
*/
|
|
369
|
+
async delete(key, storeName) {
|
|
370
|
+
const storeConfig = this._getStoreConfig(storeName);
|
|
371
|
+
return this._withRetry(async () => {
|
|
372
|
+
await this._dbReady;
|
|
373
|
+
if (!this._db) {
|
|
374
|
+
await this._reconnect();
|
|
375
|
+
if (!this._db) {
|
|
376
|
+
throw new Error('Database not initialized');
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
const keys = Array.isArray(key) ? key : [key];
|
|
380
|
+
return new Promise((resolve, reject) => {
|
|
381
|
+
try {
|
|
382
|
+
const transaction = this._db.transaction([storeConfig.name], 'readwrite');
|
|
383
|
+
const store = transaction.objectStore(storeConfig.name);
|
|
384
|
+
keys.forEach(k => {
|
|
385
|
+
const _key = storeConfig.encrypt !== false ? this._encrypt(k) : k;
|
|
386
|
+
store.delete(_key);
|
|
387
|
+
});
|
|
388
|
+
transaction.oncomplete = () => resolve();
|
|
389
|
+
transaction.onerror = () => reject(transaction.error);
|
|
390
|
+
}
|
|
391
|
+
catch (error) {
|
|
392
|
+
reject(error);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Clear all data from a store (respecting protected keys)
|
|
399
|
+
* @param storeName - Optional store name
|
|
400
|
+
*/
|
|
401
|
+
async clear(storeName) {
|
|
402
|
+
const storeConfig = this._getStoreConfig(storeName);
|
|
403
|
+
const protectedKeys = [...this._globalProtectedKeys, ...(storeConfig.protectedKeys ?? [])];
|
|
404
|
+
return this._withRetry(async () => {
|
|
405
|
+
await this._dbReady;
|
|
406
|
+
if (!this._db) {
|
|
407
|
+
await this._reconnect();
|
|
408
|
+
if (!this._db) {
|
|
409
|
+
throw new Error('Database not initialized');
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
if (protectedKeys.length === 0) {
|
|
413
|
+
return new Promise((resolve, reject) => {
|
|
414
|
+
const transaction = this._db.transaction([storeConfig.name], 'readwrite');
|
|
415
|
+
const store = transaction.objectStore(storeConfig.name);
|
|
416
|
+
const request = store.clear();
|
|
417
|
+
request.onsuccess = () => resolve();
|
|
418
|
+
request.onerror = () => reject(request.error);
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
return new Promise((resolve, reject) => {
|
|
422
|
+
try {
|
|
423
|
+
const transaction = this._db.transaction([storeConfig.name], 'readwrite');
|
|
424
|
+
const store = transaction.objectStore(storeConfig.name);
|
|
425
|
+
const keyRequest = store.getAllKeys();
|
|
426
|
+
keyRequest.onsuccess = () => {
|
|
427
|
+
const allKeys = keyRequest.result;
|
|
428
|
+
const encryptedProtected = protectedKeys.map(k => this._encrypt(k));
|
|
429
|
+
allKeys.forEach(k => {
|
|
430
|
+
if (!encryptedProtected.includes(k) && !protectedKeys.includes(k)) {
|
|
431
|
+
try {
|
|
432
|
+
const decrypted = this._decrypt(k);
|
|
433
|
+
if (!protectedKeys.includes(decrypted ?? '')) {
|
|
434
|
+
store.delete(k);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
catch {
|
|
438
|
+
store.delete(k);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
};
|
|
443
|
+
keyRequest.onerror = () => reject(keyRequest.error);
|
|
444
|
+
transaction.oncomplete = () => resolve();
|
|
445
|
+
transaction.onerror = () => reject(transaction.error);
|
|
446
|
+
}
|
|
447
|
+
catch (error) {
|
|
448
|
+
reject(error);
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Get all items from a store
|
|
455
|
+
* @param storeName - Optional store name
|
|
456
|
+
*/
|
|
457
|
+
async getAll(storeName) {
|
|
458
|
+
const storeConfig = this._getStoreConfig(storeName);
|
|
459
|
+
return this._withRetry(async () => {
|
|
460
|
+
await this._dbReady;
|
|
461
|
+
if (!this._db) {
|
|
462
|
+
await this._reconnect();
|
|
463
|
+
if (!this._db) {
|
|
464
|
+
return {};
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return new Promise(resolve => {
|
|
468
|
+
try {
|
|
469
|
+
const transaction = this._db.transaction([storeConfig.name], this._mode);
|
|
470
|
+
const store = transaction.objectStore(storeConfig.name);
|
|
471
|
+
const keysRequest = store.getAllKeys();
|
|
472
|
+
const valuesRequest = store.getAll();
|
|
473
|
+
const results = {};
|
|
474
|
+
keysRequest.onsuccess = () => {
|
|
475
|
+
const keys = keysRequest.result;
|
|
476
|
+
const values = valuesRequest.result;
|
|
477
|
+
keys.forEach((key, index) => {
|
|
478
|
+
if (typeof key === 'string') {
|
|
479
|
+
try {
|
|
480
|
+
const _key = storeConfig.encrypt !== false ? this._decrypt(key) : key;
|
|
481
|
+
const _value = storeConfig.encrypt !== false ? this._decrypt(values[index]) : values[index];
|
|
482
|
+
if (_key && _value !== null) {
|
|
483
|
+
results[_key] = _value;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
catch {
|
|
487
|
+
// Skip invalid entries
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
resolve(results);
|
|
492
|
+
};
|
|
493
|
+
keysRequest.onerror = () => resolve({});
|
|
494
|
+
transaction.onerror = () => resolve({});
|
|
495
|
+
}
|
|
496
|
+
catch {
|
|
497
|
+
resolve({});
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Set multiple items at once
|
|
504
|
+
* @param items - Object with key-value pairs
|
|
505
|
+
* @param storeName - Optional store name
|
|
506
|
+
*/
|
|
507
|
+
async setMultiple(items, storeName) {
|
|
508
|
+
const storeConfig = this._getStoreConfig(storeName);
|
|
509
|
+
const entries = Object.entries(items);
|
|
510
|
+
for (let i = 0; i < entries.length; i += Z_INDEXDB_BATCH_SIZE) {
|
|
511
|
+
const batch = entries.slice(i, i + Z_INDEXDB_BATCH_SIZE);
|
|
512
|
+
await this._withRetry(async () => {
|
|
513
|
+
await this._dbReady;
|
|
514
|
+
if (!this._db) {
|
|
515
|
+
await this._reconnect();
|
|
516
|
+
if (!this._db) {
|
|
517
|
+
throw new Error('Database not initialized');
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return new Promise((resolve, reject) => {
|
|
521
|
+
const transaction = this._db.transaction([storeConfig.name], 'readwrite');
|
|
522
|
+
const store = transaction.objectStore(storeConfig.name);
|
|
523
|
+
batch.forEach(([key, value]) => {
|
|
524
|
+
const _key = storeConfig.encrypt !== false ? this._encrypt(key) : key;
|
|
525
|
+
const _value = storeConfig.encrypt !== false ? this._encrypt(value) : value;
|
|
526
|
+
store.put(_value, _key);
|
|
527
|
+
});
|
|
528
|
+
transaction.oncomplete = () => resolve();
|
|
529
|
+
transaction.onerror = () => reject(transaction.error);
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Get the list of available store names
|
|
536
|
+
*/
|
|
537
|
+
getStoreNames() {
|
|
538
|
+
return Array.from(this._stores.keys());
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Check if a store exists
|
|
542
|
+
* @param storeName - Store name to check
|
|
543
|
+
*/
|
|
544
|
+
hasStore(storeName) {
|
|
545
|
+
return this._stores.has(storeName);
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Add a new store (requires database version upgrade)
|
|
549
|
+
* Note: This will cause database reconnection
|
|
550
|
+
* @param config - Store configuration
|
|
551
|
+
*/
|
|
552
|
+
async addStore(config) {
|
|
553
|
+
if (this._stores.has(config.name)) {
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
this._stores.set(config.name, config);
|
|
557
|
+
this._version += 1;
|
|
558
|
+
this._closeConnection();
|
|
559
|
+
this._dbReady = this._initDb();
|
|
560
|
+
await this._dbReady;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Z-Subject Service
|
|
566
|
+
* A service for managing reactive subjects (Subject and BehaviorSubject)
|
|
567
|
+
*/
|
|
568
|
+
class ZSubjectService {
|
|
569
|
+
_subjects = new Map();
|
|
570
|
+
_behaviorSubjects = new Map();
|
|
571
|
+
/**
|
|
572
|
+
* Emit a value to a subject
|
|
573
|
+
* @param key - Subject identifier
|
|
574
|
+
* @param type - 'subject' for Subject, 'behavior' for BehaviorSubject
|
|
575
|
+
* @param value - Value to emit
|
|
576
|
+
*/
|
|
577
|
+
emit(key, type, value) {
|
|
578
|
+
if (type === 'behavior') {
|
|
579
|
+
const existingSubject = this._behaviorSubjects.get(key);
|
|
580
|
+
if (existingSubject) {
|
|
581
|
+
existingSubject.next(value);
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
const newSubject = new BehaviorSubject(value);
|
|
585
|
+
this._behaviorSubjects.set(key, newSubject);
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
const existingSubject = this._subjects.get(key);
|
|
589
|
+
if (existingSubject) {
|
|
590
|
+
existingSubject.next(value);
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
const newSubject = new Subject();
|
|
594
|
+
this._subjects.set(key, newSubject);
|
|
595
|
+
newSubject.next(value);
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Get an observable for a subject
|
|
599
|
+
* @param key - Subject identifier
|
|
600
|
+
* @param type - 'subject' for Subject, 'behavior' for BehaviorSubject
|
|
601
|
+
* @param initialValue - Initial value for BehaviorSubject (required for new behavior subjects)
|
|
602
|
+
*/
|
|
603
|
+
on$(key, type, initialValue) {
|
|
604
|
+
if (type === 'behavior') {
|
|
605
|
+
const existingSubject = this._behaviorSubjects.get(key);
|
|
606
|
+
if (existingSubject) {
|
|
607
|
+
return existingSubject.asObservable();
|
|
608
|
+
}
|
|
609
|
+
const newSubject = new BehaviorSubject(initialValue);
|
|
610
|
+
this._behaviorSubjects.set(key, newSubject);
|
|
611
|
+
return newSubject.asObservable();
|
|
612
|
+
}
|
|
613
|
+
const existingSubject = this._subjects.get(key);
|
|
614
|
+
if (existingSubject) {
|
|
615
|
+
return existingSubject.asObservable();
|
|
616
|
+
}
|
|
617
|
+
const newSubject = new Subject();
|
|
618
|
+
this._subjects.set(key, newSubject);
|
|
619
|
+
return newSubject.asObservable();
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Get the current value of a BehaviorSubject
|
|
623
|
+
* @param key - Subject identifier
|
|
624
|
+
*/
|
|
625
|
+
getValue(key) {
|
|
626
|
+
const subject = this._behaviorSubjects.get(key);
|
|
627
|
+
if (!subject) {
|
|
628
|
+
return undefined;
|
|
629
|
+
}
|
|
630
|
+
return subject.getValue();
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Complete and remove a subject
|
|
634
|
+
* @param key - Subject identifier
|
|
635
|
+
* @param type - 'subject' for Subject, 'behavior' for BehaviorSubject
|
|
636
|
+
*/
|
|
637
|
+
complete(key, type) {
|
|
638
|
+
if (type === 'behavior') {
|
|
639
|
+
const subject = this._behaviorSubjects.get(key);
|
|
640
|
+
if (!subject) {
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
subject.complete();
|
|
644
|
+
this._behaviorSubjects.delete(key);
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
const subject = this._subjects.get(key);
|
|
648
|
+
if (!subject) {
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
subject.complete();
|
|
652
|
+
this._subjects.delete(key);
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Complete all subjects
|
|
656
|
+
*/
|
|
657
|
+
completeAll() {
|
|
658
|
+
this._subjects.forEach(subject => subject.complete());
|
|
659
|
+
this._behaviorSubjects.forEach(subject => subject.complete());
|
|
660
|
+
this._subjects.clear();
|
|
661
|
+
this._behaviorSubjects.clear();
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Check if a subject exists
|
|
665
|
+
* @param key - Subject identifier
|
|
666
|
+
* @param type - 'subject' for Subject, 'behavior' for BehaviorSubject
|
|
667
|
+
*/
|
|
668
|
+
has(key, type) {
|
|
669
|
+
if (type === 'behavior') {
|
|
670
|
+
return this._behaviorSubjects.has(key);
|
|
671
|
+
}
|
|
672
|
+
return this._subjects.has(key);
|
|
673
|
+
}
|
|
674
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZSubjectService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
675
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZSubjectService, providedIn: 'root' });
|
|
676
|
+
}
|
|
677
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZSubjectService, decorators: [{
|
|
678
|
+
type: Injectable,
|
|
679
|
+
args: [{
|
|
680
|
+
providedIn: 'root',
|
|
681
|
+
}]
|
|
682
|
+
}] });
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Z-Theme Types
|
|
686
|
+
*/
|
|
687
|
+
const Z_DEFAULT_THEME = 'neutral';
|
|
688
|
+
const Z_THEME_CSS_MAP = {
|
|
689
|
+
neutral: '',
|
|
690
|
+
green: 'assets/css/themes/green.css',
|
|
691
|
+
orange: 'assets/css/themes/orange.css',
|
|
692
|
+
violet: 'assets/css/themes/violet.css',
|
|
693
|
+
stone: 'assets/css/themes/stone.css',
|
|
694
|
+
zinc: 'assets/css/themes/zinc.css',
|
|
695
|
+
gray: 'assets/css/themes/gray.css',
|
|
696
|
+
slate: 'assets/css/themes/slate.css',
|
|
697
|
+
hospital: 'assets/css/themes/hospital.css',
|
|
698
|
+
};
|
|
699
|
+
|
|
700
|
+
class ZThemeService {
|
|
701
|
+
_document = inject(DOCUMENT);
|
|
702
|
+
_isDark = signal(false, ...(ngDevMode ? [{ debugName: "_isDark" }] : []));
|
|
703
|
+
_currentTheme = signal(Z_DEFAULT_THEME, ...(ngDevMode ? [{ debugName: "_currentTheme" }] : []));
|
|
704
|
+
_loadedThemes = new Set(['neutral']);
|
|
705
|
+
isDark = this._isDark.asReadonly();
|
|
706
|
+
currentTheme = this._currentTheme.asReadonly();
|
|
707
|
+
setTheme(theme) {
|
|
708
|
+
this._currentTheme.set(theme);
|
|
709
|
+
if (this._loadedThemes.has(theme)) {
|
|
710
|
+
this._document.documentElement.setAttribute('z-theme', theme);
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
if (!Z_THEME_CSS_MAP[theme]) {
|
|
714
|
+
this._document.documentElement.setAttribute('z-theme', theme);
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
this._loadThemeCSS(theme, () => {
|
|
718
|
+
this._document.documentElement.setAttribute('z-theme', theme);
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
toggleDarkMode(isDark) {
|
|
722
|
+
this._isDark.set(isDark);
|
|
723
|
+
if (isDark) {
|
|
724
|
+
this._document.documentElement.classList.add('dark');
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
this._document.documentElement.classList.remove('dark');
|
|
728
|
+
}
|
|
729
|
+
preloadTheme(theme) {
|
|
730
|
+
if (this._loadedThemes.has(theme) || !Z_THEME_CSS_MAP[theme]) {
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
this._loadThemeCSS(theme);
|
|
734
|
+
}
|
|
735
|
+
_loadThemeCSS(theme, onLoad) {
|
|
736
|
+
const cssPath = Z_THEME_CSS_MAP[theme];
|
|
737
|
+
if (!cssPath) {
|
|
738
|
+
onLoad?.();
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
const existingLink = this._document.querySelector(`link[data-theme="${theme}"]`);
|
|
742
|
+
if (existingLink) {
|
|
743
|
+
this._loadedThemes.add(theme);
|
|
744
|
+
onLoad?.();
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
const link = this._document.createElement('link');
|
|
748
|
+
link.rel = 'stylesheet';
|
|
749
|
+
link.href = cssPath;
|
|
750
|
+
link.setAttribute('data-theme', theme);
|
|
751
|
+
link.onload = () => {
|
|
752
|
+
this._loadedThemes.add(theme);
|
|
753
|
+
onLoad?.();
|
|
754
|
+
};
|
|
755
|
+
link.onerror = () => {
|
|
756
|
+
console.error(`Failed to load theme "${theme}" from ${cssPath}`);
|
|
757
|
+
onLoad?.();
|
|
758
|
+
};
|
|
759
|
+
this._document.head.appendChild(link);
|
|
760
|
+
}
|
|
761
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
762
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZThemeService, providedIn: 'root' });
|
|
763
|
+
}
|
|
764
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZThemeService, decorators: [{
|
|
765
|
+
type: Injectable,
|
|
766
|
+
args: [{
|
|
767
|
+
providedIn: 'root',
|
|
768
|
+
}]
|
|
769
|
+
}] });
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Z-Translate Service
|
|
773
|
+
* A translation service with async support and caching
|
|
774
|
+
*/
|
|
775
|
+
const LANG_CACHE_KEY = 'Z_LANGUAGE';
|
|
776
|
+
const DEFAULT_LANG = 'vi';
|
|
777
|
+
class ZTranslateService {
|
|
778
|
+
_translate = inject(TranslateService);
|
|
779
|
+
/** Current language signal */
|
|
780
|
+
currentLang = signal(DEFAULT_LANG, ...(ngDevMode ? [{ debugName: "currentLang" }] : []));
|
|
781
|
+
/** Available languages */
|
|
782
|
+
availableLangs = signal(['vi', 'en'], ...(ngDevMode ? [{ debugName: "availableLangs" }] : []));
|
|
783
|
+
constructor() {
|
|
784
|
+
this._initialize();
|
|
785
|
+
}
|
|
786
|
+
_initialize() {
|
|
787
|
+
const savedLang = ZCacheService.get(LANG_CACHE_KEY) ?? DEFAULT_LANG;
|
|
788
|
+
this._translate.setFallbackLang(DEFAULT_LANG);
|
|
789
|
+
this._translate.use(savedLang);
|
|
790
|
+
this.currentLang.set(savedLang);
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Change the current language
|
|
794
|
+
* @param lang - Language code
|
|
795
|
+
*/
|
|
796
|
+
use(lang) {
|
|
797
|
+
this._translate.use(lang);
|
|
798
|
+
ZCacheService.set(LANG_CACHE_KEY, lang);
|
|
799
|
+
this.currentLang.set(lang);
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Get the current language code
|
|
803
|
+
*/
|
|
804
|
+
getLang() {
|
|
805
|
+
return this._translate.getCurrentLang() ?? DEFAULT_LANG;
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Get translation synchronously (returns key if not found)
|
|
809
|
+
* @param key - Translation key
|
|
810
|
+
* @param params - Interpolation parameters
|
|
811
|
+
*/
|
|
812
|
+
instant(key, params) {
|
|
813
|
+
if (!key) {
|
|
814
|
+
return '';
|
|
815
|
+
}
|
|
816
|
+
return this._translate.instant(key, params);
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Get translation asynchronously
|
|
820
|
+
* @param key - Translation key
|
|
821
|
+
* @param params - Interpolation parameters
|
|
822
|
+
*/
|
|
823
|
+
async get(key, params) {
|
|
824
|
+
if (!key) {
|
|
825
|
+
return '';
|
|
826
|
+
}
|
|
827
|
+
return firstValueFrom(this._translate.get(key, params));
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Get translation as observable
|
|
831
|
+
* @param key - Translation key
|
|
832
|
+
* @param params - Interpolation parameters
|
|
833
|
+
*/
|
|
834
|
+
get$(key, params) {
|
|
835
|
+
return this._translate.get(key, params);
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Stream translations (re-emits on language change)
|
|
839
|
+
* @param key - Translation key
|
|
840
|
+
* @param params - Interpolation parameters
|
|
841
|
+
*/
|
|
842
|
+
stream$(key, params) {
|
|
843
|
+
return this._translate.stream(key, params);
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Get multiple translations at once
|
|
847
|
+
* @param keys - Array of translation keys
|
|
848
|
+
* @param params - Interpolation parameters (applied to all)
|
|
849
|
+
*/
|
|
850
|
+
async getMany(keys, params) {
|
|
851
|
+
const results = {};
|
|
852
|
+
await Promise.all(keys.map(async (key) => {
|
|
853
|
+
results[key] = await this.get(key, params);
|
|
854
|
+
}));
|
|
855
|
+
return results;
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Check if a translation key exists
|
|
859
|
+
* @param key - Translation key
|
|
860
|
+
*/
|
|
861
|
+
has(key) {
|
|
862
|
+
const translation = this._translate.instant(key);
|
|
863
|
+
return translation !== key;
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* Add translations dynamically
|
|
867
|
+
* @param lang - Language code
|
|
868
|
+
* @param translations - Translation object
|
|
869
|
+
* @param shouldMerge - Whether to merge with existing translations
|
|
870
|
+
*/
|
|
871
|
+
setTranslation(lang, translations, shouldMerge = true) {
|
|
872
|
+
this._translate.setTranslation(lang, translations, shouldMerge);
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Get the underlying TranslateService for advanced usage
|
|
876
|
+
*/
|
|
877
|
+
getNgxTranslate() {
|
|
878
|
+
return this._translate;
|
|
879
|
+
}
|
|
880
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTranslateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
881
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTranslateService, providedIn: 'root' });
|
|
882
|
+
}
|
|
883
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTranslateService, decorators: [{
|
|
884
|
+
type: Injectable,
|
|
885
|
+
args: [{
|
|
886
|
+
providedIn: 'root',
|
|
887
|
+
}]
|
|
888
|
+
}], ctorParameters: () => [] });
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* Z-Cache Types
|
|
892
|
+
*/
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Z-Http Types
|
|
896
|
+
*/
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* Z-IndexDB Types
|
|
900
|
+
*/
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* Z-Translate Types
|
|
904
|
+
*/
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* Services types index
|
|
908
|
+
*/
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* Services index
|
|
912
|
+
*/
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* Generated bundle index. Do not edit.
|
|
916
|
+
*/
|
|
917
|
+
|
|
918
|
+
export { ZCacheService, ZIndexDbService, ZSubjectService, ZThemeService, ZTranslateService, Z_DEFAULT_THEME, Z_THEME_CSS_MAP };
|
|
919
|
+
//# sourceMappingURL=shival99-z-ui-services.mjs.map
|