@katlux/providers 0.1.0-beta.0 → 0.1.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +5496 -0
- package/dist/index.d.cts +1861 -0
- package/dist/index.d.mts +1861 -0
- package/dist/index.d.ts +1861 -0
- package/dist/index.mjs +5475 -0
- package/package.json +7 -3
- package/build.config.ts +0 -4
- package/services/CacheProvider/ACacheProvider.ts +0 -13
- package/services/CacheProvider/CApplicationCache.ts +0 -123
- package/services/CacheProvider/CCookieCache.ts +0 -212
- package/services/CacheProvider/CIndexedDBCache.ts +0 -312
- package/services/CacheProvider/CLocalStorageCache.ts +0 -232
- package/services/CacheProvider/CMemoryCache.ts +0 -185
- package/services/CacheProvider/CSessionCache.ts +0 -378
- package/services/CacheProvider/CacheProviderFactory.ts +0 -22
- package/services/DataProvider/AAPIDataProvider.ts +0 -24
- package/services/DataProvider/ADataProvider.ts +0 -126
- package/services/DataProvider/CAPIFlatClientDataProvider.ts +0 -199
- package/services/DataProvider/CAPIFlatServerDataProvider.ts +0 -77
- package/services/DataProvider/CAPITreeClientDataProvider.ts +0 -205
- package/services/DataProvider/CAPITreeServerDataProvider.ts +0 -98
- package/services/DataProvider/CFlatClientDataProvider.ts +0 -52
- package/services/DataProvider/CFlatServerDataProvider.ts +0 -104
- package/services/DataProvider/CTreeClientDataProvider.ts +0 -335
- package/services/DataProvider/CTreeServerDataProvider.ts +0 -207
- package/services/RequestProvider/RequestProvider.ts +0 -165
- package/services/RequestProvider/serverCache.ts +0 -24
- package/services/index.ts +0 -19
- package/services/types.ts +0 -172
- package/src/index.ts +0 -1
|
@@ -1,378 +0,0 @@
|
|
|
1
|
-
import { ACacheProvider } from './ACacheProvider'
|
|
2
|
-
import type { ICacheEntry } from '../types'
|
|
3
|
-
import { CCookieCache } from './CCookieCache'
|
|
4
|
-
|
|
5
|
-
export class CSessionCache extends ACacheProvider {
|
|
6
|
-
private static instance: CSessionCache | null = null
|
|
7
|
-
|
|
8
|
-
public static getInstance(): CSessionCache {
|
|
9
|
-
if (!this.instance) {
|
|
10
|
-
this.instance = new CSessionCache()
|
|
11
|
-
}
|
|
12
|
-
return this.instance
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Use global symbol to share storage across module instances (API vs App)
|
|
16
|
-
private static get storage(): Map<string, Map<string, ICacheEntry<any>>> {
|
|
17
|
-
const globalKey = Symbol.for('Katlux_CSessionCache_Storage')
|
|
18
|
-
const _global = globalThis as any
|
|
19
|
-
if (!_global[globalKey]) {
|
|
20
|
-
_global[globalKey] = new Map<string, Map<string, ICacheEntry<any>>>()
|
|
21
|
-
}
|
|
22
|
-
return _global[globalKey]
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
public static getSnapshot(): Map<string, Map<string, ICacheEntry<any>>> {
|
|
26
|
-
return this.storage
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
private sessionIdCache: ACacheProvider | null = null
|
|
30
|
-
private sessionId: string | null = null
|
|
31
|
-
private readonly SESSION_ID_KEY = '_session_id'
|
|
32
|
-
|
|
33
|
-
private constructor() {
|
|
34
|
-
super()
|
|
35
|
-
// Use cookie cache for storing sessionId on client via Singleton
|
|
36
|
-
this.sessionIdCache = CCookieCache.getInstance()
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Generate a UUID v4 sessionId
|
|
41
|
-
*/
|
|
42
|
-
private generateSessionId(): string {
|
|
43
|
-
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
44
|
-
const r = Math.random() * 16 | 0
|
|
45
|
-
const v = c === 'x' ? r : (r & 0x3 | 0x8)
|
|
46
|
-
return v.toString(16)
|
|
47
|
-
})
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Get or create sessionId
|
|
52
|
-
*/
|
|
53
|
-
private async getSessionId(nuxtApp?: any): Promise<string> {
|
|
54
|
-
if (this.sessionId) {
|
|
55
|
-
return this.sessionId
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (!this.sessionIdCache) {
|
|
59
|
-
return this.generateSessionId()
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Try to get existing sessionId from cookie
|
|
63
|
-
const existingId = await this.sessionIdCache.get<string>(this.SESSION_ID_KEY, nuxtApp)
|
|
64
|
-
|
|
65
|
-
if (existingId) {
|
|
66
|
-
this.sessionId = existingId
|
|
67
|
-
return existingId
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Generate new sessionId
|
|
71
|
-
const newId = this.generateSessionId()
|
|
72
|
-
this.sessionId = newId
|
|
73
|
-
|
|
74
|
-
// Store in cookie (1 year lifetime)
|
|
75
|
-
if (this.sessionIdCache) {
|
|
76
|
-
await this.sessionIdCache.set(this.SESSION_ID_KEY, newId, 365 * 24 * 60 * 60 * 1000, nuxtApp)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return newId
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async getKeyList(nuxtApp?: any): Promise<Array<string>> {
|
|
83
|
-
const prefix = 'cache:'
|
|
84
|
-
|
|
85
|
-
// Client: Use browser sessionStorage
|
|
86
|
-
if (import.meta.client) {
|
|
87
|
-
return Object.keys(sessionStorage)
|
|
88
|
-
.filter(key => key.startsWith(prefix))
|
|
89
|
-
.map(key => key.substring(prefix.length))
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Server: Use session storage (Map)
|
|
93
|
-
const sessionId = await this.getSessionId(nuxtApp)
|
|
94
|
-
const sessionData = CSessionCache.storage.get(sessionId)
|
|
95
|
-
if (!sessionData) {
|
|
96
|
-
return []
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return Array.from(sessionData.keys())
|
|
100
|
-
.filter(key => key.startsWith(prefix))
|
|
101
|
-
.map(key => key.substring(prefix.length))
|
|
102
|
-
}
|
|
103
|
-
async get<T>(key: string, nuxtApp?: any): Promise<T | null> {
|
|
104
|
-
const prefixedKey = 'cache:' + key
|
|
105
|
-
|
|
106
|
-
// Client: Use browser sessionStorage
|
|
107
|
-
if (import.meta.client) {
|
|
108
|
-
try {
|
|
109
|
-
const stored = sessionStorage.getItem(prefixedKey)
|
|
110
|
-
if (stored) {
|
|
111
|
-
const entry: ICacheEntry<T> = JSON.parse(stored)
|
|
112
|
-
if (Date.now() - entry.timestamp <= entry.lifetime) {
|
|
113
|
-
return entry.data as T
|
|
114
|
-
} else {
|
|
115
|
-
sessionStorage.removeItem(prefixedKey)
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
} catch (e) {
|
|
119
|
-
// Serialisation error
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Check SSR Payload as fallback/hydration
|
|
123
|
-
try {
|
|
124
|
-
const payloadData = (window as any).__NUXT__?.payload?.data
|
|
125
|
-
if (payloadData && payloadData[prefixedKey]) {
|
|
126
|
-
const entry = payloadData[prefixedKey] as ICacheEntry<T>
|
|
127
|
-
if (Date.now() - entry.timestamp <= entry.lifetime) {
|
|
128
|
-
// Hydrate into sessionStorage
|
|
129
|
-
window.sessionStorage.setItem(prefixedKey, JSON.stringify(entry))
|
|
130
|
-
return entry.data
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
} catch (e) {
|
|
134
|
-
// Silent fail
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return null
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Server: Retrieve from session storage (Map)
|
|
141
|
-
if (import.meta.server) {
|
|
142
|
-
const sessionId = await this.getSessionId(nuxtApp)
|
|
143
|
-
const sessionData = CSessionCache.storage.get(sessionId)
|
|
144
|
-
if (!sessionData) {
|
|
145
|
-
return null
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const entry = sessionData.get(prefixedKey)
|
|
149
|
-
if (!entry) {
|
|
150
|
-
return null
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Check expiration
|
|
154
|
-
if (Date.now() - entry.timestamp <= entry.lifetime) {
|
|
155
|
-
return entry.data as T
|
|
156
|
-
} else {
|
|
157
|
-
// Remove expired entry
|
|
158
|
-
sessionData.delete(prefixedKey)
|
|
159
|
-
return null
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return null
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
async set<T>(key: string, data: T, lifetime: number, nuxtApp?: any): Promise<void> {
|
|
167
|
-
const prefixedKey = 'cache:' + key
|
|
168
|
-
const entry: ICacheEntry<T> = {
|
|
169
|
-
data,
|
|
170
|
-
timestamp: Date.now(),
|
|
171
|
-
lifetime
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Client: Store in browser sessionStorage
|
|
175
|
-
if (import.meta.client) {
|
|
176
|
-
try {
|
|
177
|
-
sessionStorage.setItem(prefixedKey, JSON.stringify(entry))
|
|
178
|
-
} catch (e) {
|
|
179
|
-
// Quota exceeded
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Server: Store in session storage (Map) AND Payload for hydration
|
|
184
|
-
if (import.meta.server) {
|
|
185
|
-
try {
|
|
186
|
-
const sessionId = await this.getSessionId(nuxtApp)
|
|
187
|
-
|
|
188
|
-
// 1. Session Storage
|
|
189
|
-
let sessionData = CSessionCache.storage.get(sessionId)
|
|
190
|
-
if (!sessionData) {
|
|
191
|
-
sessionData = new Map<string, ICacheEntry<any>>()
|
|
192
|
-
CSessionCache.storage.set(sessionId, sessionData)
|
|
193
|
-
}
|
|
194
|
-
sessionData.set(prefixedKey, entry)
|
|
195
|
-
|
|
196
|
-
// 2. Nuxt Payload (for hydration into client's sessionStorage)
|
|
197
|
-
if (nuxtApp) {
|
|
198
|
-
const payload = (nuxtApp as any).payload?.data || (nuxtApp as any).ssrContext?.payload?.data
|
|
199
|
-
if (payload) {
|
|
200
|
-
payload[prefixedKey] = entry
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
} catch (e) {
|
|
204
|
-
// Silent fail
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
async remove(key: string, nuxtApp?: any): Promise<void> {
|
|
210
|
-
const prefixedKey = 'cache:' + key
|
|
211
|
-
|
|
212
|
-
if (import.meta.client) {
|
|
213
|
-
sessionStorage.removeItem(prefixedKey)
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (import.meta.server) {
|
|
217
|
-
try {
|
|
218
|
-
// 1. Session storage
|
|
219
|
-
const sessionId = await this.getSessionId(nuxtApp)
|
|
220
|
-
const sessionData = CSessionCache.storage.get(sessionId)
|
|
221
|
-
if (sessionData) {
|
|
222
|
-
sessionData.delete(prefixedKey)
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// 2. Payload cleanup
|
|
226
|
-
if (nuxtApp) {
|
|
227
|
-
const payload = (nuxtApp as any).payload?.data || (nuxtApp as any).ssrContext?.payload?.data
|
|
228
|
-
if (payload && payload[prefixedKey]) {
|
|
229
|
-
delete payload[prefixedKey]
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
} catch (e) {
|
|
233
|
-
// Silent fail
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
async removeByPrefix(prefix: string, nuxtApp?: any): Promise<void> {
|
|
239
|
-
const fullPrefix = 'cache:' + prefix
|
|
240
|
-
|
|
241
|
-
// Client: Iterate sessionStorage
|
|
242
|
-
if (import.meta.client) {
|
|
243
|
-
const keysToDelete: string[] = []
|
|
244
|
-
for (let i = 0; i < sessionStorage.length; i++) {
|
|
245
|
-
const key = sessionStorage.key(i)
|
|
246
|
-
if (key && key.startsWith(fullPrefix)) {
|
|
247
|
-
keysToDelete.push(key)
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
for (const key of keysToDelete) {
|
|
251
|
-
sessionStorage.removeItem(key)
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Server: Iterate session map
|
|
256
|
-
if (import.meta.server) {
|
|
257
|
-
try {
|
|
258
|
-
const sessionId = await this.getSessionId(nuxtApp)
|
|
259
|
-
const sessionData = CSessionCache.storage.get(sessionId)
|
|
260
|
-
|
|
261
|
-
if (sessionData) {
|
|
262
|
-
const keysToDelete: string[] = []
|
|
263
|
-
for (const key of sessionData.keys()) {
|
|
264
|
-
if (key.startsWith(fullPrefix)) {
|
|
265
|
-
keysToDelete.push(key)
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
for (const key of keysToDelete) {
|
|
269
|
-
sessionData.delete(key)
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Payload cleanup
|
|
274
|
-
if (nuxtApp) {
|
|
275
|
-
const payload = (nuxtApp as any).payload?.data || (nuxtApp as any).ssrContext?.payload?.data
|
|
276
|
-
if (payload) {
|
|
277
|
-
for (const key in payload) {
|
|
278
|
-
if (key.startsWith(fullPrefix)) {
|
|
279
|
-
delete payload[key]
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
} catch (e) {
|
|
285
|
-
// Silent fail
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
async getRemainingTime(key: string, nuxtApp?: any): Promise<number | null> {
|
|
291
|
-
const prefixedKey = 'cache:' + key
|
|
292
|
-
|
|
293
|
-
if (import.meta.client) {
|
|
294
|
-
const entryStr = sessionStorage.getItem(prefixedKey)
|
|
295
|
-
if (!entryStr) return null
|
|
296
|
-
try {
|
|
297
|
-
const entry: ICacheEntry<any> = JSON.parse(entryStr)
|
|
298
|
-
return Math.max(0, (entry.timestamp + entry.lifetime) - Date.now())
|
|
299
|
-
} catch (e) {
|
|
300
|
-
return null
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
if (import.meta.server) {
|
|
305
|
-
const sessionId = await this.getSessionId(nuxtApp)
|
|
306
|
-
const sessionData = CSessionCache.storage.get(sessionId)
|
|
307
|
-
if (!sessionData) return null
|
|
308
|
-
|
|
309
|
-
const entry = sessionData.get(prefixedKey)
|
|
310
|
-
if (!entry) return null
|
|
311
|
-
|
|
312
|
-
return Math.max(0, (entry.timestamp + entry.lifetime) - Date.now())
|
|
313
|
-
}
|
|
314
|
-
return null
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
async cleanupExpired(): Promise<void> {
|
|
318
|
-
const prefix = 'cache:'
|
|
319
|
-
const now = Date.now()
|
|
320
|
-
|
|
321
|
-
if (import.meta.client) {
|
|
322
|
-
const keysToDelete: string[] = []
|
|
323
|
-
for (let i = 0; i < sessionStorage.length; i++) {
|
|
324
|
-
const key = sessionStorage.key(i)
|
|
325
|
-
if (key && key.startsWith(prefix)) {
|
|
326
|
-
const stored = sessionStorage.getItem(key)
|
|
327
|
-
if (stored) {
|
|
328
|
-
try {
|
|
329
|
-
const entry: ICacheEntry<any> = JSON.parse(stored)
|
|
330
|
-
if (now - entry.timestamp > entry.lifetime) {
|
|
331
|
-
keysToDelete.push(key)
|
|
332
|
-
}
|
|
333
|
-
} catch (e) {
|
|
334
|
-
keysToDelete.push(key)
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
for (const key of keysToDelete) {
|
|
340
|
-
sessionStorage.removeItem(key)
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
if (import.meta.server) {
|
|
345
|
-
const sessionsToDelete: string[] = []
|
|
346
|
-
|
|
347
|
-
// Iterate through all sessions
|
|
348
|
-
for (const [sessionId, sessionData] of CSessionCache.storage.entries()) {
|
|
349
|
-
const keysToDelete: string[] = []
|
|
350
|
-
|
|
351
|
-
// Check each entry in the session
|
|
352
|
-
for (const [key, entry] of sessionData.entries()) {
|
|
353
|
-
// Only cleanup entries for this cache key
|
|
354
|
-
if (key.startsWith(prefix)) {
|
|
355
|
-
if (now - entry.timestamp > entry.lifetime) {
|
|
356
|
-
keysToDelete.push(key)
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Remove expired entries
|
|
362
|
-
for (const key of keysToDelete) {
|
|
363
|
-
sessionData.delete(key)
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// If session is empty, mark for deletion
|
|
367
|
-
if (sessionData.size === 0) {
|
|
368
|
-
sessionsToDelete.push(sessionId)
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// Remove empty sessions
|
|
373
|
-
for (const sessionId of sessionsToDelete) {
|
|
374
|
-
CSessionCache.storage.delete(sessionId)
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { ECacheStrategy } from '../types'
|
|
2
|
-
import { ACacheProvider } from './ACacheProvider'
|
|
3
|
-
import { CApplicationCache } from './CApplicationCache'
|
|
4
|
-
import { CMemoryCache } from './CMemoryCache'
|
|
5
|
-
import { CLocalStorageCache } from './CLocalStorageCache'
|
|
6
|
-
import { CIndexedDBCache } from './CIndexedDBCache'
|
|
7
|
-
import { CCookieCache } from './CCookieCache'
|
|
8
|
-
import { CSessionCache } from './CSessionCache'
|
|
9
|
-
|
|
10
|
-
export class CacheProviderFactory {
|
|
11
|
-
static getProvider(strategy: ECacheStrategy): ACacheProvider | null {
|
|
12
|
-
switch (strategy) {
|
|
13
|
-
case ECacheStrategy.Application: return CApplicationCache.getInstance()
|
|
14
|
-
case ECacheStrategy.Session: return CSessionCache.getInstance()
|
|
15
|
-
case ECacheStrategy.Memory: return CMemoryCache.getInstance()
|
|
16
|
-
case ECacheStrategy.LocalStorage: return CLocalStorageCache.getInstance()
|
|
17
|
-
case ECacheStrategy.IndexedDB: return CIndexedDBCache.getInstance()
|
|
18
|
-
case ECacheStrategy.Cookie: return CCookieCache.getInstance()
|
|
19
|
-
default: return null
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { ref, type Ref } from 'vue'
|
|
2
|
-
import { ECacheStrategy } from '../types';
|
|
3
|
-
import type { IDataProviderOptions } from '../types';
|
|
4
|
-
import { RequestProvider } from '../RequestProvider/RequestProvider';
|
|
5
|
-
|
|
6
|
-
export abstract class AAPIDataProvider {
|
|
7
|
-
|
|
8
|
-
apiUrl: Ref<string> = ref("")
|
|
9
|
-
cacheStrategy: Ref<ECacheStrategy | null> = ref(null)
|
|
10
|
-
cacheLifetime: Ref<number> = ref(60 * 1000)
|
|
11
|
-
requestProvider: RequestProvider = new RequestProvider()
|
|
12
|
-
refreshOnMutation: Ref<boolean | undefined> = ref(undefined)
|
|
13
|
-
|
|
14
|
-
constructor(options?: IDataProviderOptions) {
|
|
15
|
-
if (options?.cacheStrategy) this.cacheStrategy.value = options.cacheStrategy
|
|
16
|
-
if (options?.cacheLifetime) this.cacheLifetime.value = options.cacheLifetime
|
|
17
|
-
if (options?.refreshOnMutation !== undefined) this.refreshOnMutation.value = options.refreshOnMutation
|
|
18
|
-
}
|
|
19
|
-
abstract setAPIUrl(url: string): void
|
|
20
|
-
abstract create(item: any): Promise<void>
|
|
21
|
-
abstract update(item: any): Promise<void>
|
|
22
|
-
abstract delete(items: any[]): Promise<void>
|
|
23
|
-
}
|
|
24
|
-
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { ref, type Ref } from 'vue'
|
|
2
|
-
import type { TDataRow, IDataFilter, IDataSort, IDataResult, IKDatatableAction, IDataProviderOptions } from '../types'
|
|
3
|
-
import { useDebounce } from '@katlux/toolkit/composables/useDebounce'
|
|
4
|
-
|
|
5
|
-
// Note: In an isomorphic package, we must handle Nuxt auto-imports carefully.
|
|
6
|
-
// Standard Vue ref and watch are explicitly imported.
|
|
7
|
-
import { watch } from 'vue'
|
|
8
|
-
const APP_MODULE = '#app';
|
|
9
|
-
// DataProvider abstract class
|
|
10
|
-
export abstract class ADataProvider {
|
|
11
|
-
filter: Ref<IDataFilter | null> = ref(null)
|
|
12
|
-
sortList: Ref<IDataSort[]> = ref([])
|
|
13
|
-
currentPage: Ref<number> = ref(1)
|
|
14
|
-
pageSize: Ref<number> = ref(10)
|
|
15
|
-
pageData: Ref<TDataRow[]> = ref([])
|
|
16
|
-
rowCount: Ref<number> = ref(0)
|
|
17
|
-
loading: Ref<Boolean> = ref(false)
|
|
18
|
-
SSR: Ref<Boolean> = ref(false)
|
|
19
|
-
deduplicate: Ref<Boolean> = ref(true)
|
|
20
|
-
nuxtApp: any = null
|
|
21
|
-
urlPageParam: Ref<string> = ref('')
|
|
22
|
-
selectedRows: Ref<any[]> = ref([])
|
|
23
|
-
selectAll: Ref<boolean> = ref(false)
|
|
24
|
-
|
|
25
|
-
constructor(options?: IDataProviderOptions) {
|
|
26
|
-
if (options?.nuxtApp) {
|
|
27
|
-
this.nuxtApp = options.nuxtApp
|
|
28
|
-
} else if (import.meta.server) {
|
|
29
|
-
try {
|
|
30
|
-
import(/* @vite-ignore */ APP_MODULE).then(appModule => {
|
|
31
|
-
this.nuxtApp = appModule.useNuxtApp()
|
|
32
|
-
}).catch(() => { })
|
|
33
|
-
} catch (e) {
|
|
34
|
-
// Outside nuxt context
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (options?.filter) {
|
|
39
|
-
this.filter.value = options.filter
|
|
40
|
-
}
|
|
41
|
-
if (options?.sortList) {
|
|
42
|
-
this.sortList.value = options.sortList
|
|
43
|
-
}
|
|
44
|
-
if (options?.pageSize) {
|
|
45
|
-
this.pageSize.value = options.pageSize
|
|
46
|
-
}
|
|
47
|
-
if (options?.currentPage) {
|
|
48
|
-
this.currentPage.value = options.currentPage
|
|
49
|
-
}
|
|
50
|
-
if (options?.SSR) {
|
|
51
|
-
this.SSR.value = options.SSR
|
|
52
|
-
}
|
|
53
|
-
if (options?.deduplicate !== undefined) {
|
|
54
|
-
this.deduplicate.value = options.deduplicate
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// URL-based pagination: read initial page from URL
|
|
58
|
-
if (options?.urlPageParam) {
|
|
59
|
-
this.urlPageParam.value = options.urlPageParam
|
|
60
|
-
try {
|
|
61
|
-
import(/* @vite-ignore */ APP_MODULE).then(appModule => {
|
|
62
|
-
const route = appModule.useRoute()
|
|
63
|
-
const pageFromUrl = route.query[options.urlPageParam!]
|
|
64
|
-
if (pageFromUrl) {
|
|
65
|
-
const parsedPage = parseInt(pageFromUrl as string, 10)
|
|
66
|
-
if (!isNaN(parsedPage) && parsedPage > 0) {
|
|
67
|
-
this.currentPage.value = parsedPage
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}).catch(() => { })
|
|
71
|
-
} catch (e) {
|
|
72
|
-
// useRoute not available (outside Vue context)
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Watch for changes in currentPage and pageSize to automatically reload data
|
|
77
|
-
watch(this.currentPage, (newPage) => {
|
|
78
|
-
this.loadPageData()
|
|
79
|
-
|
|
80
|
-
// URL-based pagination: update URL when page changes (client-side only)
|
|
81
|
-
if (import.meta.client && this.urlPageParam.value) {
|
|
82
|
-
try {
|
|
83
|
-
import(/* @vite-ignore */ APP_MODULE).then(appModule => {
|
|
84
|
-
const router = appModule.useRouter()
|
|
85
|
-
const currentQuery = { ...router.currentRoute.value.query }
|
|
86
|
-
if (newPage === 1) {
|
|
87
|
-
delete currentQuery[this.urlPageParam.value]
|
|
88
|
-
} else {
|
|
89
|
-
currentQuery[this.urlPageParam.value] = String(newPage)
|
|
90
|
-
}
|
|
91
|
-
router.replace({ query: currentQuery })
|
|
92
|
-
}).catch(() => { })
|
|
93
|
-
} catch (e) {
|
|
94
|
-
// useRouter not available
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
watch(this.pageSize, () => {
|
|
100
|
-
this.currentPage.value = 1
|
|
101
|
-
this.loadPageData()
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
watch(this.filter, useDebounce(() => {
|
|
105
|
-
this.currentPage.value = 1
|
|
106
|
-
this.loadPageData()
|
|
107
|
-
}, 300))
|
|
108
|
-
watch(this.sortList, () => {
|
|
109
|
-
this.currentPage.value = 1
|
|
110
|
-
this.loadPageData()
|
|
111
|
-
})
|
|
112
|
-
}
|
|
113
|
-
setFilter(filter: IDataFilter | null) {
|
|
114
|
-
this.filter.value = filter
|
|
115
|
-
}
|
|
116
|
-
setSortList(sortList: IDataSort[]) {
|
|
117
|
-
this.sortList.value = sortList
|
|
118
|
-
}
|
|
119
|
-
setCurrentPage(currentPage: number) {
|
|
120
|
-
this.currentPage.value = currentPage
|
|
121
|
-
}
|
|
122
|
-
setPageSize(pageSize: number) {
|
|
123
|
-
this.pageSize.value = pageSize
|
|
124
|
-
}
|
|
125
|
-
abstract loadPageData(): void
|
|
126
|
-
}
|