forked from npmx-dev/npmx.dev
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathusePreferencesProvider.ts
More file actions
109 lines (97 loc) · 2.49 KB
/
usePreferencesProvider.ts
File metadata and controls
109 lines (97 loc) · 2.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import { createDefu } from 'defu'
/**
* Abstraction for preferences storage
* Currently uses localStorage, designed for future user prefs API
*/
const STORAGE_KEY = 'npmx-list-prefs'
interface StorageProvider<T> {
get: () => T | null
set: (value: T) => void
remove: () => void
}
const defu = createDefu((object, key, value) => {
if (Array.isArray(object[key]) && Array.isArray(value)) {
object[key] = value
return true
}
})
/**
* Creates a localStorage-based storage provider
*/
function createLocalStorageProvider<T>(key: string): StorageProvider<T> {
return {
get: () => {
if (import.meta.server) return null
try {
const stored = localStorage.getItem(key)
if (stored) {
return JSON.parse(stored) as T
}
} catch {
// Corrupted data, remove it
localStorage.removeItem(key)
}
return null
},
set: (value: T) => {
if (import.meta.server) return
try {
localStorage.setItem(key, JSON.stringify(value))
} catch {
// Storage full or other error, fail silently
}
},
remove: () => {
if (import.meta.server) return
localStorage.removeItem(key)
},
}
}
// Future: API-based provider would look like this:
// function createApiStorageProvider<T>(endpoint: string): StorageProvider<T> {
// return {
// get: async () => { /* fetch from API */ },
// set: async (value) => { /* POST to API */ },
// remove: async () => { /* DELETE from API */ },
// }
// }
/**
* Composable for managing preferences storage
* Abstracts the storage mechanism to allow future migration to API-based storage
*
*/
export function usePreferencesProvider<T extends object>(defaultValue: T) {
const provider = createLocalStorageProvider<T>(STORAGE_KEY)
const data = ref<T>(defaultValue)
const isHydrated = shallowRef(false)
// Load from storage on client
onMounted(() => {
const stored = provider.get()
if (stored) {
// Merge stored values with defaults to handle schema evolution
data.value = defu(stored, defaultValue)
}
isHydrated.value = true
})
// Persist changes
function save() {
provider.set(data.value)
}
// Reset to defaults
function reset() {
data.value = { ...defaultValue }
provider.remove()
}
// Update specific keys
function update<K extends keyof T>(key: K, value: T[K]) {
data.value[key] = value
save()
}
return {
data,
isHydrated,
save,
reset,
update,
}
}