@stacksjs/components 0.2.92 → 0.2.94
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/{Sidebar-yb115a2w.stx → Sidebar-4hn7vw4n.stx} +18 -0
- package/dist/index.js +5 -5
- package/package.json +5 -6
- package/src/components/CodeBlock.stx +0 -54
- package/src/components/Footer.stx +0 -66
- package/src/components/Hero.stx +0 -25
- package/src/components/Installation.stx +0 -37
- package/src/components/index.ts +0 -4
- package/src/composables/index.ts +0 -4
- package/src/composables/useCopyCode.ts +0 -73
- package/src/composables/useDarkMode.ts +0 -100
- package/src/composables/useSEO.ts +0 -230
- package/src/composables/utilities/index.ts +0 -7
- package/src/composables/utilities/useClickOutside.ts +0 -72
- package/src/composables/utilities/useFocusTrap.ts +0 -159
- package/src/composables/utilities/useKeyboard.ts +0 -124
- package/src/composables/utilities/useLocalStorage.ts +0 -132
- package/src/composables/utilities/useMediaQuery.ts +0 -118
- package/src/index.ts +0 -99
- package/src/stx.d.ts +0 -4
- package/src/ui/accordion/Accordion.stx +0 -146
- package/src/ui/accordion/AccordionItem.stx +0 -37
- package/src/ui/accordion/index.ts +0 -47
- package/src/ui/audio/Audio.stx +0 -50
- package/src/ui/audio/README.md +0 -41
- package/src/ui/audio/index.ts +0 -13
- package/src/ui/auth/Login.stx +0 -161
- package/src/ui/auth/README.md +0 -142
- package/src/ui/auth/Signup.stx +0 -159
- package/src/ui/auth/TwoFactorChallenge.stx +0 -111
- package/src/ui/auth/index.ts +0 -32
- package/src/ui/avatar/Avatar.stx +0 -77
- package/src/ui/avatar/index.ts +0 -12
- package/src/ui/badge/Badge.stx +0 -62
- package/src/ui/badge/index.ts +0 -10
- package/src/ui/breadcrumb/Breadcrumb.stx +0 -79
- package/src/ui/breadcrumb/index.ts +0 -15
- package/src/ui/button/Button.stx +0 -74
- package/src/ui/button/README.md +0 -80
- package/src/ui/button/index.ts +0 -85
- package/src/ui/calendar/Calendar.stx +0 -189
- package/src/ui/calendar/README.md +0 -254
- package/src/ui/calendar/index.ts +0 -14
- package/src/ui/card/Card.stx +0 -68
- package/src/ui/card/index.ts +0 -13
- package/src/ui/checkbox/Checkbox.stx +0 -86
- package/src/ui/checkbox/index.ts +0 -16
- package/src/ui/combobox/Combobox.stx +0 -96
- package/src/ui/combobox/ComboboxButton.stx +0 -25
- package/src/ui/combobox/ComboboxInput.stx +0 -32
- package/src/ui/combobox/ComboboxOption.stx +0 -49
- package/src/ui/combobox/ComboboxOptions.stx +0 -16
- package/src/ui/combobox/README.md +0 -150
- package/src/ui/combobox/index.ts +0 -40
- package/src/ui/command-palette/CommandPalette.stx +0 -81
- package/src/ui/command-palette/CommandPaletteItem.stx +0 -27
- package/src/ui/command-palette/README.md +0 -42
- package/src/ui/command-palette/index.ts +0 -15
- package/src/ui/dialog/Dialog.stx +0 -41
- package/src/ui/dialog/DialogBackdrop.stx +0 -9
- package/src/ui/dialog/DialogDescription.stx +0 -11
- package/src/ui/dialog/DialogPanel.stx +0 -16
- package/src/ui/dialog/DialogTitle.stx +0 -11
- package/src/ui/dialog/README.md +0 -121
- package/src/ui/dialog/index.ts +0 -32
- package/src/ui/drawer/Drawer.stx +0 -120
- package/src/ui/drawer/README.md +0 -90
- package/src/ui/drawer/index.ts +0 -9
- package/src/ui/dropdown/Dropdown.stx +0 -82
- package/src/ui/dropdown/DropdownButton.stx +0 -29
- package/src/ui/dropdown/DropdownItem.stx +0 -35
- package/src/ui/dropdown/DropdownItems.stx +0 -18
- package/src/ui/dropdown/README.md +0 -114
- package/src/ui/dropdown/index.ts +0 -29
- package/src/ui/form/Form.stx +0 -95
- package/src/ui/form/index.ts +0 -67
- package/src/ui/heatmap/Heatmap.stx +0 -294
- package/src/ui/image/Image.stx +0 -165
- package/src/ui/image/README.md +0 -164
- package/src/ui/image/index.ts +0 -22
- package/src/ui/input/EmailInput.stx +0 -30
- package/src/ui/input/NumberInput.stx +0 -126
- package/src/ui/input/PasswordInput.stx +0 -143
- package/src/ui/input/SearchInput.stx +0 -44
- package/src/ui/input/TextInput.stx +0 -132
- package/src/ui/input/index.ts +0 -180
- package/src/ui/listbox/Listbox.stx +0 -101
- package/src/ui/listbox/ListboxButton.stx +0 -28
- package/src/ui/listbox/ListboxLabel.stx +0 -11
- package/src/ui/listbox/ListboxOption.stx +0 -49
- package/src/ui/listbox/ListboxOptions.stx +0 -16
- package/src/ui/listbox/README.md +0 -135
- package/src/ui/listbox/index.ts +0 -39
- package/src/ui/navigator/Navigator.stx +0 -113
- package/src/ui/navigator/README.md +0 -352
- package/src/ui/navigator/index.ts +0 -20
- package/src/ui/notification/Notification.stx +0 -117
- package/src/ui/notification/README.md +0 -114
- package/src/ui/notification/index.ts +0 -12
- package/src/ui/pagination/Pagination.stx +0 -150
- package/src/ui/pagination/index.ts +0 -11
- package/src/ui/payment/Checkout.stx +0 -31
- package/src/ui/payment/DefaultPaymentMethod.stx +0 -62
- package/src/ui/payment/PaymentMethods.stx +0 -104
- package/src/ui/payment/README.md +0 -345
- package/src/ui/payment/SubscriptionCheckout.stx +0 -323
- package/src/ui/payment/index.ts +0 -59
- package/src/ui/popover/Popover.stx +0 -60
- package/src/ui/popover/PopoverButton.stx +0 -29
- package/src/ui/popover/PopoverPanel.stx +0 -23
- package/src/ui/popover/README.md +0 -91
- package/src/ui/popover/index.ts +0 -22
- package/src/ui/portal/Portal.stx +0 -57
- package/src/ui/portal/index.ts +0 -6
- package/src/ui/progress/Progress.stx +0 -131
- package/src/ui/progress/index.ts +0 -12
- package/src/ui/radio/Radio.stx +0 -77
- package/src/ui/radio/index.ts +0 -15
- package/src/ui/radio-group/README.md +0 -139
- package/src/ui/radio-group/RadioGroup.stx +0 -60
- package/src/ui/radio-group/RadioGroupDescription.stx +0 -11
- package/src/ui/radio-group/RadioGroupLabel.stx +0 -11
- package/src/ui/radio-group/RadioGroupOption.stx +0 -58
- package/src/ui/radio-group/index.ts +0 -30
- package/src/ui/select/README.md +0 -180
- package/src/ui/select/Select.stx +0 -82
- package/src/ui/select/index.ts +0 -21
- package/src/ui/sidebar/Sidebar.stx +0 -458
- package/src/ui/sidebar/SidebarFooter.stx +0 -71
- package/src/ui/sidebar/SidebarHeader.stx +0 -103
- package/src/ui/sidebar/SidebarItem.stx +0 -86
- package/src/ui/sidebar/SidebarSection.stx +0 -83
- package/src/ui/sidebar/index.ts +0 -153
- package/src/ui/sidebar/themes.ts +0 -240
- package/src/ui/skeleton/Skeleton.stx +0 -48
- package/src/ui/skeleton/index.ts +0 -11
- package/src/ui/spinner/Spinner.stx +0 -109
- package/src/ui/spinner/index.ts +0 -9
- package/src/ui/stepper/README.md +0 -36
- package/src/ui/stepper/Stepper.stx +0 -17
- package/src/ui/stepper/StepperStep.stx +0 -57
- package/src/ui/stepper/index.ts +0 -19
- package/src/ui/storage/README.md +0 -193
- package/src/ui/storage/index.ts +0 -347
- package/src/ui/switch/README.md +0 -114
- package/src/ui/switch/Switch.stx +0 -72
- package/src/ui/switch/index.ts +0 -10
- package/src/ui/table/README.md +0 -39
- package/src/ui/table/Table.stx +0 -14
- package/src/ui/table/TableBody.stx +0 -10
- package/src/ui/table/TableCell.stx +0 -10
- package/src/ui/table/TableHead.stx +0 -10
- package/src/ui/table/TableHeader.stx +0 -20
- package/src/ui/table/TableRow.stx +0 -11
- package/src/ui/table/index.ts +0 -35
- package/src/ui/tabs/TabPanel.stx +0 -25
- package/src/ui/tabs/Tabs.stx +0 -202
- package/src/ui/tabs/index.ts +0 -50
- package/src/ui/teleport/Teleport.stx +0 -65
- package/src/ui/teleport/index.ts +0 -7
- package/src/ui/textarea/Textarea.stx +0 -108
- package/src/ui/textarea/index.ts +0 -20
- package/src/ui/tooltip/Tooltip.stx +0 -71
- package/src/ui/tooltip/index.ts +0 -10
- package/src/ui/transition/README.md +0 -30
- package/src/ui/transition/Transition.stx +0 -102
- package/src/ui/transition/index.ts +0 -25
- package/src/ui/video/README.md +0 -47
- package/src/ui/video/Video.stx +0 -45
- package/src/ui/video/index.ts +0 -16
- package/src/ui/virtual-list/VirtualList.stx +0 -67
- package/src/ui/virtual-list/index.ts +0 -10
- package/src/ui/virtual-table/VirtualTable.stx +0 -103
- package/src/ui/virtual-table/index.ts +0 -21
- package/src/utils/PROP_VALIDATION.md +0 -458
- package/src/utils/THEME_SYSTEM.md +0 -702
- package/src/utils/accessibility.ts +0 -841
- package/src/utils/animation/easing.ts +0 -368
- package/src/utils/animation/helpers.ts +0 -223
- package/src/utils/animation/index.ts +0 -5
- package/src/utils/animation/keyframes.ts +0 -230
- package/src/utils/error-handling.ts +0 -475
- package/src/utils/highlighter.ts +0 -93
- package/src/utils/i18n.ts +0 -634
- package/src/utils/keep-alive.ts +0 -819
- package/src/utils/lazy-loading.ts +0 -530
- package/src/utils/prop-validation.ts +0 -478
- package/src/utils/theme.ts +0 -626
package/src/ui/storage/index.ts
DELETED
|
@@ -1,347 +0,0 @@
|
|
|
1
|
-
// Storage utilities for localStorage and sessionStorage
|
|
2
|
-
|
|
3
|
-
export interface StorageOptions {
|
|
4
|
-
expire?: number // Expiration in milliseconds
|
|
5
|
-
namespace?: string // Prefix for keys
|
|
6
|
-
encrypt?: boolean // Encrypt data (basic XOR encryption)
|
|
7
|
-
compress?: boolean // Compress large objects
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
type StorageWatchCallback = (_newValue: any, _oldValue: any, _key: string) => void
|
|
11
|
-
|
|
12
|
-
export class StorageManager {
|
|
13
|
-
private storage: globalThis.Storage
|
|
14
|
-
private namespace: string
|
|
15
|
-
private watchers: Map<string, Set<StorageWatchCallback>> = new Map()
|
|
16
|
-
private encryptionKey = 'stx-storage-key' // Simple encryption key
|
|
17
|
-
|
|
18
|
-
constructor(type: 'local' | 'session' = 'local', namespace = '') {
|
|
19
|
-
this.storage = type === 'local' ? globalThis.localStorage : globalThis.sessionStorage
|
|
20
|
-
this.namespace = namespace
|
|
21
|
-
|
|
22
|
-
// Listen for storage events from other tabs/windows
|
|
23
|
-
if (typeof window !== 'undefined') {
|
|
24
|
-
window.addEventListener('storage', this.handleStorageEvent.bind(this))
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
private handleStorageEvent(event: StorageEvent): void {
|
|
29
|
-
if (!event.key)
|
|
30
|
-
return
|
|
31
|
-
|
|
32
|
-
const key = this.namespace && event.key.startsWith(`${this.namespace}:`)
|
|
33
|
-
? event.key.slice(this.namespace.length + 1)
|
|
34
|
-
: event.key
|
|
35
|
-
|
|
36
|
-
const callbacks = this.watchers.get(key)
|
|
37
|
-
if (callbacks) {
|
|
38
|
-
const oldValue = event.oldValue ? JSON.parse(event.oldValue).value : null
|
|
39
|
-
const newValue = event.newValue ? JSON.parse(event.newValue).value : null
|
|
40
|
-
callbacks.forEach(callback => callback(newValue, oldValue, key))
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
private encrypt(data: string): string {
|
|
45
|
-
// Simple XOR encryption (not cryptographically secure, just obfuscation)
|
|
46
|
-
let result = ''
|
|
47
|
-
for (let i = 0; i < data.length; i++) {
|
|
48
|
-
result += String.fromCharCode(data.charCodeAt(i) ^ this.encryptionKey.charCodeAt(i % this.encryptionKey.length))
|
|
49
|
-
}
|
|
50
|
-
return btoa(result) // Base64 encode
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
private decrypt(data: string): string {
|
|
54
|
-
try {
|
|
55
|
-
const decoded = atob(data) // Base64 decode
|
|
56
|
-
let result = ''
|
|
57
|
-
for (let i = 0; i < decoded.length; i++) {
|
|
58
|
-
result += String.fromCharCode(decoded.charCodeAt(i) ^ this.encryptionKey.charCodeAt(i % this.encryptionKey.length))
|
|
59
|
-
}
|
|
60
|
-
return result
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
return data // Return as-is if decryption fails
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
private compress(data: string): string {
|
|
68
|
-
// Simple LZ-based compression for large strings
|
|
69
|
-
if (data.length < 100)
|
|
70
|
-
return data // Don't compress small strings
|
|
71
|
-
|
|
72
|
-
const dict: Record<string, number> = {}
|
|
73
|
-
let dictSize = 256
|
|
74
|
-
const result: number[] = []
|
|
75
|
-
let w = ''
|
|
76
|
-
|
|
77
|
-
for (let i = 0; i < data.length; i++) {
|
|
78
|
-
const c = data.charAt(i)
|
|
79
|
-
const wc = w + c
|
|
80
|
-
|
|
81
|
-
if (dict[wc] !== undefined) {
|
|
82
|
-
w = wc
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
result.push(w.length > 1 ? dict[w] : w.charCodeAt(0))
|
|
86
|
-
dict[wc] = dictSize++
|
|
87
|
-
w = c
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (w !== '') {
|
|
92
|
-
result.push(w.length > 1 ? dict[w] : w.charCodeAt(0))
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return JSON.stringify(result)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
private decompress(data: string): string {
|
|
99
|
-
try {
|
|
100
|
-
const compressed = JSON.parse(data)
|
|
101
|
-
if (!Array.isArray(compressed))
|
|
102
|
-
return data
|
|
103
|
-
|
|
104
|
-
const dict: Record<number, string> = {}
|
|
105
|
-
let dictSize = 256
|
|
106
|
-
let w = String.fromCharCode(compressed[0])
|
|
107
|
-
let result = w
|
|
108
|
-
|
|
109
|
-
for (let i = 1; i < compressed.length; i++) {
|
|
110
|
-
const k = compressed[i]
|
|
111
|
-
let entry: string
|
|
112
|
-
|
|
113
|
-
if (dict[k] !== undefined) {
|
|
114
|
-
entry = dict[k]
|
|
115
|
-
}
|
|
116
|
-
else if (k === dictSize) {
|
|
117
|
-
entry = w + w.charAt(0)
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
return data // Decompression failed
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
result += entry
|
|
124
|
-
dict[dictSize++] = w + entry.charAt(0)
|
|
125
|
-
w = entry
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return result
|
|
129
|
-
}
|
|
130
|
-
catch {
|
|
131
|
-
return data // Return as-is if decompression fails
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
private getKey(key: string): string {
|
|
136
|
-
return this.namespace ? `${this.namespace}:${key}` : key
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
set(key: string, value: any, options?: StorageOptions): void {
|
|
140
|
-
const fullKey = this.getKey(key)
|
|
141
|
-
const oldValue = this.get(key)
|
|
142
|
-
|
|
143
|
-
const data = {
|
|
144
|
-
value,
|
|
145
|
-
timestamp: Date.now(),
|
|
146
|
-
expire: options?.expire,
|
|
147
|
-
encrypted: options?.encrypt || false,
|
|
148
|
-
compressed: options?.compress || false,
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
try {
|
|
152
|
-
let serialized = JSON.stringify(data)
|
|
153
|
-
|
|
154
|
-
// Compress if enabled
|
|
155
|
-
if (options?.compress) {
|
|
156
|
-
serialized = this.compress(serialized)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Encrypt if enabled
|
|
160
|
-
if (options?.encrypt) {
|
|
161
|
-
serialized = this.encrypt(serialized)
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
this.storage.setItem(fullKey, serialized)
|
|
165
|
-
|
|
166
|
-
// Notify watchers
|
|
167
|
-
this.notifyWatchers(key, value, oldValue)
|
|
168
|
-
}
|
|
169
|
-
catch (error) {
|
|
170
|
-
console.error('Storage set error:', error)
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
get<T = any>(key: string): T | null {
|
|
175
|
-
const fullKey = this.getKey(key)
|
|
176
|
-
|
|
177
|
-
try {
|
|
178
|
-
let item = this.storage.getItem(fullKey)
|
|
179
|
-
if (!item)
|
|
180
|
-
return null
|
|
181
|
-
|
|
182
|
-
// Try to decrypt if it looks encrypted (base64)
|
|
183
|
-
if (item.match(/^[A-Z0-9+/]+=*$/i)) {
|
|
184
|
-
try {
|
|
185
|
-
item = this.decrypt(item)
|
|
186
|
-
}
|
|
187
|
-
catch {
|
|
188
|
-
// Not encrypted, continue
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Try to decompress if it looks compressed (starts with [)
|
|
193
|
-
if (item.startsWith('[')) {
|
|
194
|
-
try {
|
|
195
|
-
item = this.decompress(item)
|
|
196
|
-
}
|
|
197
|
-
catch {
|
|
198
|
-
// Not compressed, continue
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const data = JSON.parse(item)
|
|
203
|
-
|
|
204
|
-
// Check expiration
|
|
205
|
-
if (data.expire && Date.now() - data.timestamp > data.expire) {
|
|
206
|
-
this.remove(key)
|
|
207
|
-
return null
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return data.value as T
|
|
211
|
-
}
|
|
212
|
-
catch (error) {
|
|
213
|
-
console.error('Storage get error:', error)
|
|
214
|
-
return null
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
watch(key: string, callback: StorageWatchCallback): () => void {
|
|
219
|
-
if (!this.watchers.has(key)) {
|
|
220
|
-
this.watchers.set(key, new Set())
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
this.watchers.get(key)!.add(callback)
|
|
224
|
-
|
|
225
|
-
// Return unwatch function
|
|
226
|
-
return () => {
|
|
227
|
-
const callbacks = this.watchers.get(key)
|
|
228
|
-
if (callbacks) {
|
|
229
|
-
callbacks.delete(callback)
|
|
230
|
-
if (callbacks.size === 0) {
|
|
231
|
-
this.watchers.delete(key)
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
private notifyWatchers(key: string, newValue: any, oldValue: any): void {
|
|
238
|
-
const callbacks = this.watchers.get(key)
|
|
239
|
-
if (callbacks) {
|
|
240
|
-
callbacks.forEach(callback => callback(newValue, oldValue, key))
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
remove(key: string): void {
|
|
245
|
-
const fullKey = this.getKey(key)
|
|
246
|
-
const oldValue = this.get(key)
|
|
247
|
-
this.storage.removeItem(fullKey)
|
|
248
|
-
|
|
249
|
-
// Notify watchers
|
|
250
|
-
this.notifyWatchers(key, null, oldValue)
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
clear(): void {
|
|
254
|
-
if (this.namespace) {
|
|
255
|
-
// Clear only namespaced items
|
|
256
|
-
const keys = this.keys()
|
|
257
|
-
keys.forEach(key => this.remove(key))
|
|
258
|
-
}
|
|
259
|
-
else {
|
|
260
|
-
this.storage.clear()
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
has(key: string): boolean {
|
|
265
|
-
return this.get(key) !== null
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
keys(): string[] {
|
|
269
|
-
const allKeys = Object.keys(this.storage)
|
|
270
|
-
|
|
271
|
-
if (this.namespace) {
|
|
272
|
-
const prefix = `${this.namespace}:`
|
|
273
|
-
return allKeys
|
|
274
|
-
.filter(key => key.startsWith(prefix))
|
|
275
|
-
.map(key => key.slice(prefix.length))
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return allKeys
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
size(): number {
|
|
282
|
-
return this.keys().length
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// Get storage quota information (if available)
|
|
286
|
-
getQuota(): { usage: number, quota: number, percentage: number } | null {
|
|
287
|
-
if ('storage' in navigator && 'estimate' in navigator.storage) {
|
|
288
|
-
// Use async/await pattern but return promise
|
|
289
|
-
return null // This would need to be async, but keeping sync API
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Fallback: estimate based on current data
|
|
293
|
-
try {
|
|
294
|
-
let totalSize = 0
|
|
295
|
-
for (let i = 0; i < this.storage.length; i++) {
|
|
296
|
-
const key = this.storage.key(i)
|
|
297
|
-
if (key) {
|
|
298
|
-
const value = this.storage.getItem(key) || ''
|
|
299
|
-
totalSize += key.length + value.length
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Typical localStorage quota is 5-10MB
|
|
304
|
-
const estimatedQuota = 5 * 1024 * 1024 // 5MB
|
|
305
|
-
return {
|
|
306
|
-
usage: totalSize * 2, // Multiply by 2 for UTF-16 encoding
|
|
307
|
-
quota: estimatedQuota,
|
|
308
|
-
percentage: (totalSize * 2 / estimatedQuota) * 100,
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
catch {
|
|
312
|
-
return null
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// Batch operations
|
|
317
|
-
setMany(items: Record<string, any>, options?: StorageOptions): void {
|
|
318
|
-
Object.entries(items).forEach(([key, value]) => {
|
|
319
|
-
this.set(key, value, options)
|
|
320
|
-
})
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
getMany<T = any>(keys: string[]): Record<string, T | null> {
|
|
324
|
-
const result: Record<string, T | null> = {}
|
|
325
|
-
keys.forEach((key) => {
|
|
326
|
-
result[key] = this.get<T>(key)
|
|
327
|
-
})
|
|
328
|
-
return result
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
removeMany(keys: string[]): void {
|
|
332
|
-
keys.forEach(key => this.remove(key))
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Convenience exports
|
|
337
|
-
export const storage: {
|
|
338
|
-
local: StorageManager
|
|
339
|
-
session: StorageManager
|
|
340
|
-
} = {
|
|
341
|
-
local: new StorageManager('local'),
|
|
342
|
-
session: new StorageManager('session'),
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
export function createStorage(type: 'local' | 'session' = 'local', namespace?: string): StorageManager {
|
|
346
|
-
return new StorageManager(type, namespace)
|
|
347
|
-
}
|
package/src/ui/switch/README.md
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
# Switch Component
|
|
2
|
-
|
|
3
|
-
A modern toggle switch component built with STX and headwind utility classes, inspired by Headless UI.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
bun add @stacksjs/components
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Usage
|
|
12
|
-
|
|
13
|
-
```stx
|
|
14
|
-
<Switch checked label="Enable notifications" />
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Props
|
|
18
|
-
|
|
19
|
-
| Prop | Type | Default | Description |
|
|
20
|
-
|------|------|---------|-------------|
|
|
21
|
-
| `checked` | `boolean` | `false` | Whether the switch is checked |
|
|
22
|
-
| `disabled` | `boolean` | `false` | Disable the switch |
|
|
23
|
-
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Switch size |
|
|
24
|
-
| `className` | `string` | `''` | Additional CSS classes |
|
|
25
|
-
| `onChange` | `(checked: boolean) => void` | - | Change handler |
|
|
26
|
-
| `label` | `string` | `''` | Optional label text |
|
|
27
|
-
|
|
28
|
-
## Examples
|
|
29
|
-
|
|
30
|
-
### Basic Switch
|
|
31
|
-
|
|
32
|
-
```stx
|
|
33
|
-
<script server>
|
|
34
|
-
let enabled = false
|
|
35
|
-
|
|
36
|
-
function handleChange(checked) {
|
|
37
|
-
enabled = checked
|
|
38
|
-
console.log('Switch is now:', checked)
|
|
39
|
-
}
|
|
40
|
-
</script>
|
|
41
|
-
|
|
42
|
-
<Switch :checked="enabled" :onChange="handleChange" />
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### With Label
|
|
46
|
-
|
|
47
|
-
```stx
|
|
48
|
-
<Switch checked label="Enable notifications" :onChange="handleChange" />
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Sizes
|
|
52
|
-
|
|
53
|
-
```stx
|
|
54
|
-
<Switch size="sm" checked />
|
|
55
|
-
|
|
56
|
-
<Switch size="md" checked />
|
|
57
|
-
|
|
58
|
-
<Switch size="lg" checked />
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### Disabled State
|
|
62
|
-
|
|
63
|
-
```stx
|
|
64
|
-
<Switch checked disabled label="Cannot toggle" />
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### Custom Styling
|
|
68
|
-
|
|
69
|
-
```stx
|
|
70
|
-
<Switch checked className="my-4" />
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Accessibility
|
|
74
|
-
|
|
75
|
-
The Switch component follows WAI-ARIA best practices:
|
|
76
|
-
|
|
77
|
-
- Proper `role="switch"` attribute
|
|
78
|
-
- `aria-checked` reflects the current state
|
|
79
|
-
- `aria-disabled` when disabled
|
|
80
|
-
- Screen reader text via `sr-only` class
|
|
81
|
-
- Keyboard accessible (can be toggled with Space/Enter)
|
|
82
|
-
- Focus visible ring for keyboard navigation
|
|
83
|
-
|
|
84
|
-
## Features
|
|
85
|
-
|
|
86
|
-
- Smooth animations and transitions
|
|
87
|
-
- Three size options (sm, md, lg)
|
|
88
|
-
- Dark mode support
|
|
89
|
-
- Disabled state
|
|
90
|
-
- Optional label
|
|
91
|
-
- Accessible by default
|
|
92
|
-
- Headwind utility classes for easy customization
|
|
93
|
-
- Inspired by Headless UI design
|
|
94
|
-
|
|
95
|
-
## Styling
|
|
96
|
-
|
|
97
|
-
The component uses headwind utility classes for styling. You can customize it by:
|
|
98
|
-
|
|
99
|
-
1. Passing additional classes via `className` prop
|
|
100
|
-
2. Modifying the component source
|
|
101
|
-
3. Using headwind configuration to adjust the design system
|
|
102
|
-
|
|
103
|
-
### Color Customization
|
|
104
|
-
|
|
105
|
-
To customize colors, you can:
|
|
106
|
-
|
|
107
|
-
- Override the checked state background color in your headwind config
|
|
108
|
-
- Pass custom classes via `className` prop
|
|
109
|
-
- Modify the component's source code
|
|
110
|
-
|
|
111
|
-
Example:
|
|
112
|
-
```stx
|
|
113
|
-
<Switch checked className="!bg-green-500" />
|
|
114
|
-
```
|
package/src/ui/switch/Switch.stx
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
<script server>
|
|
2
|
-
export const checked = $props.checked || false
|
|
3
|
-
export const disabled = $props.disabled || false
|
|
4
|
-
export const size = $props.size || 'md'
|
|
5
|
-
export const className = $props.className || ''
|
|
6
|
-
export const label = $props.label || ''
|
|
7
|
-
|
|
8
|
-
const sizeConfig = {
|
|
9
|
-
sm: { track: 'h-5 w-9', thumb: 'h-4 w-4', translate: 'translate-x-4' },
|
|
10
|
-
md: { track: 'h-[38px] w-[74px]', thumb: 'h-[34px] w-[34px]', translate: 'translate-x-9' },
|
|
11
|
-
lg: { track: 'h-12 w-24', thumb: 'h-11 w-11', translate: 'translate-x-12' },
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const config = sizeConfig[size]
|
|
15
|
-
|
|
16
|
-
export const trackBaseClasses = 'relative inline-flex shrink-0 cursor-pointer border-2 border-transparent rounded-full transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-neutral-300 dark:focus-visible:ring-offset-neutral-700'
|
|
17
|
-
export const trackDisabledClasses = disabled ? 'opacity-50 cursor-not-allowed pointer-events-none' : ''
|
|
18
|
-
export const thumbBaseClasses = 'pointer-events-none inline-block transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out'
|
|
19
|
-
</script>
|
|
20
|
-
|
|
21
|
-
<script client>
|
|
22
|
-
const emit = defineEmits()
|
|
23
|
-
const isDisabled = {{ disabled }}
|
|
24
|
-
const config = {{ config }}
|
|
25
|
-
const trackBase = {{ trackBaseClasses }}
|
|
26
|
-
const trackDisabled = {{ trackDisabledClasses }}
|
|
27
|
-
const thumbBase = {{ thumbBaseClasses }}
|
|
28
|
-
const extraClass = {{ className }}
|
|
29
|
-
// Reactive prop binding so the parent can drive `checked` via signals.
|
|
30
|
-
// See stacksjs/stx#1704.
|
|
31
|
-
const isChecked = useReactiveProp('checked', {{ checked }})
|
|
32
|
-
|
|
33
|
-
function toggle() {
|
|
34
|
-
if (isDisabled) return
|
|
35
|
-
isChecked.set(!isChecked())
|
|
36
|
-
emit('change', isChecked())
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function trackClasses() {
|
|
40
|
-
const stateClass = isChecked()
|
|
41
|
-
? 'bg-blue-500 dark:bg-blue-600'
|
|
42
|
-
: 'bg-neutral-400 dark:bg-neutral-600'
|
|
43
|
-
return `${trackBase} ${stateClass} ${trackDisabled} ${config.track} ${extraClass}`.trim()
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function thumbClasses() {
|
|
47
|
-
const pos = isChecked() ? config.translate : 'translate-x-0'
|
|
48
|
-
return `${thumbBase} ${pos} ${config.thumb}`.trim()
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
defineExpose({ isChecked, toggle })
|
|
52
|
-
</script>
|
|
53
|
-
|
|
54
|
-
<label class="inline-flex items-center gap-3 cursor-pointer">
|
|
55
|
-
@if(label)
|
|
56
|
-
<span class="text-sm font-medium text-neutral-700 dark:text-neutral-300">
|
|
57
|
-
{{ label }}
|
|
58
|
-
</span>
|
|
59
|
-
@endif
|
|
60
|
-
|
|
61
|
-
<button
|
|
62
|
-
type="button"
|
|
63
|
-
role="switch"
|
|
64
|
-
:aria-checked="isChecked() ? 'true' : 'false'"
|
|
65
|
-
:class="trackClasses()"
|
|
66
|
-
@if(disabled)disabled aria-disabled="true"@endif
|
|
67
|
-
@click="toggle()"
|
|
68
|
-
>
|
|
69
|
-
<span class="sr-only">@if(label){{ label }}@else Toggle switch@endif</span>
|
|
70
|
-
<span aria-hidden="true" :class="thumbClasses()"></span>
|
|
71
|
-
</button>
|
|
72
|
-
</label>
|
package/src/ui/switch/index.ts
DELETED
package/src/ui/table/README.md
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# Table Component
|
|
2
|
-
|
|
3
|
-
Responsive table component with sorting and styling options.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
bun add @stacksjs/components
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Usage
|
|
12
|
-
|
|
13
|
-
```stx
|
|
14
|
-
<Table>
|
|
15
|
-
<TableHead>
|
|
16
|
-
<TableRow>
|
|
17
|
-
<TableHeader>Name</TableHeader>
|
|
18
|
-
<TableHeader>Email</TableHeader>
|
|
19
|
-
<TableHeader>Role</TableHeader>
|
|
20
|
-
</TableRow>
|
|
21
|
-
</TableHead>
|
|
22
|
-
|
|
23
|
-
<TableBody>
|
|
24
|
-
<TableRow>
|
|
25
|
-
<TableCell>John Doe</TableCell>
|
|
26
|
-
<TableCell>john@example.com</TableCell>
|
|
27
|
-
<TableCell>Admin</TableCell>
|
|
28
|
-
</TableRow>
|
|
29
|
-
</TableBody>
|
|
30
|
-
</Table>
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Features
|
|
34
|
-
|
|
35
|
-
- Responsive design
|
|
36
|
-
- Sortable columns
|
|
37
|
-
- Striped rows
|
|
38
|
-
- Hover effects
|
|
39
|
-
- Dark mode support
|
package/src/ui/table/Table.stx
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<script server>
|
|
2
|
-
export const className = $props.className || ''
|
|
3
|
-
export const striped = $props.striped || false
|
|
4
|
-
export const hoverable = $props.hoverable !== false
|
|
5
|
-
|
|
6
|
-
const baseClasses = 'min-w-full divide-y divide-neutral-200 dark:divide-neutral-700'
|
|
7
|
-
const tableClasses = `${baseClasses} ${className}`.trim()
|
|
8
|
-
</script>
|
|
9
|
-
|
|
10
|
-
<div class="overflow-x-auto">
|
|
11
|
-
<table class="{{ tableClasses }}">
|
|
12
|
-
<slot :striped="striped" :hoverable="hoverable" />
|
|
13
|
-
</table>
|
|
14
|
-
</div>
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
<script server>
|
|
2
|
-
export const className = $props.className || ''
|
|
3
|
-
|
|
4
|
-
const baseClasses = 'bg-white divide-y divide-neutral-200 dark:bg-neutral-900 dark:divide-neutral-700'
|
|
5
|
-
const bodyClasses = `${baseClasses} ${className}`.trim()
|
|
6
|
-
</script>
|
|
7
|
-
|
|
8
|
-
<tbody class="{{ bodyClasses }}">
|
|
9
|
-
<slot />
|
|
10
|
-
</tbody>
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
<script server>
|
|
2
|
-
export const className = $props.className || ''
|
|
3
|
-
|
|
4
|
-
const baseClasses = 'px-6 py-4 whitespace-nowrap text-sm text-neutral-900 dark:text-neutral-100'
|
|
5
|
-
const cellClasses = `${baseClasses} ${className}`.trim()
|
|
6
|
-
</script>
|
|
7
|
-
|
|
8
|
-
<td class="{{ cellClasses }}">
|
|
9
|
-
<slot />
|
|
10
|
-
</td>
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
<script server>
|
|
2
|
-
export const className = $props.className || ''
|
|
3
|
-
export const sortable = $props.sortable || false
|
|
4
|
-
|
|
5
|
-
const baseClasses = 'px-6 py-3 text-left text-xs font-medium text-neutral-500 uppercase tracking-wider dark:text-neutral-400'
|
|
6
|
-
const sortableClasses = sortable ? 'cursor-pointer hover:text-neutral-700 dark:hover:text-neutral-200' : ''
|
|
7
|
-
export const headerClasses = `${baseClasses} ${sortableClasses} ${className}`.trim()
|
|
8
|
-
</script>
|
|
9
|
-
|
|
10
|
-
<script client>
|
|
11
|
-
const emit = defineEmits()
|
|
12
|
-
|
|
13
|
-
function onClick() {
|
|
14
|
-
emit('click')
|
|
15
|
-
}
|
|
16
|
-
</script>
|
|
17
|
-
|
|
18
|
-
<th class="{{ headerClasses }}" @if(sortable)@click="onClick()"@endif>
|
|
19
|
-
<slot />
|
|
20
|
-
</th>
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
<script server>
|
|
2
|
-
export const className = $props.className || ''
|
|
3
|
-
export const hoverable = $props.hoverable !== false
|
|
4
|
-
|
|
5
|
-
const hoverClasses = hoverable ? 'hover:bg-neutral-50 dark:hover:bg-neutral-800' : ''
|
|
6
|
-
const rowClasses = `${hoverClasses} ${className}`.trim()
|
|
7
|
-
</script>
|
|
8
|
-
|
|
9
|
-
<tr class="{{ rowClasses }}">
|
|
10
|
-
<slot />
|
|
11
|
-
</tr>
|
package/src/ui/table/index.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
export { default as Table } from './Table.stx'
|
|
2
|
-
export { default as TableBody } from './TableBody.stx'
|
|
3
|
-
export { default as TableCell } from './TableCell.stx'
|
|
4
|
-
export { default as TableHead } from './TableHead.stx'
|
|
5
|
-
export { default as TableHeader } from './TableHeader.stx'
|
|
6
|
-
export { default as TableRow } from './TableRow.stx'
|
|
7
|
-
|
|
8
|
-
export interface TableProps {
|
|
9
|
-
className?: string
|
|
10
|
-
striped?: boolean
|
|
11
|
-
hoverable?: boolean
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface TableHeadProps {
|
|
15
|
-
className?: string
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface TableBodyProps {
|
|
19
|
-
className?: string
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface TableRowProps {
|
|
23
|
-
className?: string
|
|
24
|
-
hoverable?: boolean
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface TableHeaderProps {
|
|
28
|
-
className?: string
|
|
29
|
-
sortable?: boolean
|
|
30
|
-
onClick?: () => void
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface TableCellProps {
|
|
34
|
-
className?: string
|
|
35
|
-
}
|