@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.
Files changed (188) hide show
  1. package/dist/{Sidebar-yb115a2w.stx → Sidebar-4hn7vw4n.stx} +18 -0
  2. package/dist/index.js +5 -5
  3. package/package.json +5 -6
  4. package/src/components/CodeBlock.stx +0 -54
  5. package/src/components/Footer.stx +0 -66
  6. package/src/components/Hero.stx +0 -25
  7. package/src/components/Installation.stx +0 -37
  8. package/src/components/index.ts +0 -4
  9. package/src/composables/index.ts +0 -4
  10. package/src/composables/useCopyCode.ts +0 -73
  11. package/src/composables/useDarkMode.ts +0 -100
  12. package/src/composables/useSEO.ts +0 -230
  13. package/src/composables/utilities/index.ts +0 -7
  14. package/src/composables/utilities/useClickOutside.ts +0 -72
  15. package/src/composables/utilities/useFocusTrap.ts +0 -159
  16. package/src/composables/utilities/useKeyboard.ts +0 -124
  17. package/src/composables/utilities/useLocalStorage.ts +0 -132
  18. package/src/composables/utilities/useMediaQuery.ts +0 -118
  19. package/src/index.ts +0 -99
  20. package/src/stx.d.ts +0 -4
  21. package/src/ui/accordion/Accordion.stx +0 -146
  22. package/src/ui/accordion/AccordionItem.stx +0 -37
  23. package/src/ui/accordion/index.ts +0 -47
  24. package/src/ui/audio/Audio.stx +0 -50
  25. package/src/ui/audio/README.md +0 -41
  26. package/src/ui/audio/index.ts +0 -13
  27. package/src/ui/auth/Login.stx +0 -161
  28. package/src/ui/auth/README.md +0 -142
  29. package/src/ui/auth/Signup.stx +0 -159
  30. package/src/ui/auth/TwoFactorChallenge.stx +0 -111
  31. package/src/ui/auth/index.ts +0 -32
  32. package/src/ui/avatar/Avatar.stx +0 -77
  33. package/src/ui/avatar/index.ts +0 -12
  34. package/src/ui/badge/Badge.stx +0 -62
  35. package/src/ui/badge/index.ts +0 -10
  36. package/src/ui/breadcrumb/Breadcrumb.stx +0 -79
  37. package/src/ui/breadcrumb/index.ts +0 -15
  38. package/src/ui/button/Button.stx +0 -74
  39. package/src/ui/button/README.md +0 -80
  40. package/src/ui/button/index.ts +0 -85
  41. package/src/ui/calendar/Calendar.stx +0 -189
  42. package/src/ui/calendar/README.md +0 -254
  43. package/src/ui/calendar/index.ts +0 -14
  44. package/src/ui/card/Card.stx +0 -68
  45. package/src/ui/card/index.ts +0 -13
  46. package/src/ui/checkbox/Checkbox.stx +0 -86
  47. package/src/ui/checkbox/index.ts +0 -16
  48. package/src/ui/combobox/Combobox.stx +0 -96
  49. package/src/ui/combobox/ComboboxButton.stx +0 -25
  50. package/src/ui/combobox/ComboboxInput.stx +0 -32
  51. package/src/ui/combobox/ComboboxOption.stx +0 -49
  52. package/src/ui/combobox/ComboboxOptions.stx +0 -16
  53. package/src/ui/combobox/README.md +0 -150
  54. package/src/ui/combobox/index.ts +0 -40
  55. package/src/ui/command-palette/CommandPalette.stx +0 -81
  56. package/src/ui/command-palette/CommandPaletteItem.stx +0 -27
  57. package/src/ui/command-palette/README.md +0 -42
  58. package/src/ui/command-palette/index.ts +0 -15
  59. package/src/ui/dialog/Dialog.stx +0 -41
  60. package/src/ui/dialog/DialogBackdrop.stx +0 -9
  61. package/src/ui/dialog/DialogDescription.stx +0 -11
  62. package/src/ui/dialog/DialogPanel.stx +0 -16
  63. package/src/ui/dialog/DialogTitle.stx +0 -11
  64. package/src/ui/dialog/README.md +0 -121
  65. package/src/ui/dialog/index.ts +0 -32
  66. package/src/ui/drawer/Drawer.stx +0 -120
  67. package/src/ui/drawer/README.md +0 -90
  68. package/src/ui/drawer/index.ts +0 -9
  69. package/src/ui/dropdown/Dropdown.stx +0 -82
  70. package/src/ui/dropdown/DropdownButton.stx +0 -29
  71. package/src/ui/dropdown/DropdownItem.stx +0 -35
  72. package/src/ui/dropdown/DropdownItems.stx +0 -18
  73. package/src/ui/dropdown/README.md +0 -114
  74. package/src/ui/dropdown/index.ts +0 -29
  75. package/src/ui/form/Form.stx +0 -95
  76. package/src/ui/form/index.ts +0 -67
  77. package/src/ui/heatmap/Heatmap.stx +0 -294
  78. package/src/ui/image/Image.stx +0 -165
  79. package/src/ui/image/README.md +0 -164
  80. package/src/ui/image/index.ts +0 -22
  81. package/src/ui/input/EmailInput.stx +0 -30
  82. package/src/ui/input/NumberInput.stx +0 -126
  83. package/src/ui/input/PasswordInput.stx +0 -143
  84. package/src/ui/input/SearchInput.stx +0 -44
  85. package/src/ui/input/TextInput.stx +0 -132
  86. package/src/ui/input/index.ts +0 -180
  87. package/src/ui/listbox/Listbox.stx +0 -101
  88. package/src/ui/listbox/ListboxButton.stx +0 -28
  89. package/src/ui/listbox/ListboxLabel.stx +0 -11
  90. package/src/ui/listbox/ListboxOption.stx +0 -49
  91. package/src/ui/listbox/ListboxOptions.stx +0 -16
  92. package/src/ui/listbox/README.md +0 -135
  93. package/src/ui/listbox/index.ts +0 -39
  94. package/src/ui/navigator/Navigator.stx +0 -113
  95. package/src/ui/navigator/README.md +0 -352
  96. package/src/ui/navigator/index.ts +0 -20
  97. package/src/ui/notification/Notification.stx +0 -117
  98. package/src/ui/notification/README.md +0 -114
  99. package/src/ui/notification/index.ts +0 -12
  100. package/src/ui/pagination/Pagination.stx +0 -150
  101. package/src/ui/pagination/index.ts +0 -11
  102. package/src/ui/payment/Checkout.stx +0 -31
  103. package/src/ui/payment/DefaultPaymentMethod.stx +0 -62
  104. package/src/ui/payment/PaymentMethods.stx +0 -104
  105. package/src/ui/payment/README.md +0 -345
  106. package/src/ui/payment/SubscriptionCheckout.stx +0 -323
  107. package/src/ui/payment/index.ts +0 -59
  108. package/src/ui/popover/Popover.stx +0 -60
  109. package/src/ui/popover/PopoverButton.stx +0 -29
  110. package/src/ui/popover/PopoverPanel.stx +0 -23
  111. package/src/ui/popover/README.md +0 -91
  112. package/src/ui/popover/index.ts +0 -22
  113. package/src/ui/portal/Portal.stx +0 -57
  114. package/src/ui/portal/index.ts +0 -6
  115. package/src/ui/progress/Progress.stx +0 -131
  116. package/src/ui/progress/index.ts +0 -12
  117. package/src/ui/radio/Radio.stx +0 -77
  118. package/src/ui/radio/index.ts +0 -15
  119. package/src/ui/radio-group/README.md +0 -139
  120. package/src/ui/radio-group/RadioGroup.stx +0 -60
  121. package/src/ui/radio-group/RadioGroupDescription.stx +0 -11
  122. package/src/ui/radio-group/RadioGroupLabel.stx +0 -11
  123. package/src/ui/radio-group/RadioGroupOption.stx +0 -58
  124. package/src/ui/radio-group/index.ts +0 -30
  125. package/src/ui/select/README.md +0 -180
  126. package/src/ui/select/Select.stx +0 -82
  127. package/src/ui/select/index.ts +0 -21
  128. package/src/ui/sidebar/Sidebar.stx +0 -458
  129. package/src/ui/sidebar/SidebarFooter.stx +0 -71
  130. package/src/ui/sidebar/SidebarHeader.stx +0 -103
  131. package/src/ui/sidebar/SidebarItem.stx +0 -86
  132. package/src/ui/sidebar/SidebarSection.stx +0 -83
  133. package/src/ui/sidebar/index.ts +0 -153
  134. package/src/ui/sidebar/themes.ts +0 -240
  135. package/src/ui/skeleton/Skeleton.stx +0 -48
  136. package/src/ui/skeleton/index.ts +0 -11
  137. package/src/ui/spinner/Spinner.stx +0 -109
  138. package/src/ui/spinner/index.ts +0 -9
  139. package/src/ui/stepper/README.md +0 -36
  140. package/src/ui/stepper/Stepper.stx +0 -17
  141. package/src/ui/stepper/StepperStep.stx +0 -57
  142. package/src/ui/stepper/index.ts +0 -19
  143. package/src/ui/storage/README.md +0 -193
  144. package/src/ui/storage/index.ts +0 -347
  145. package/src/ui/switch/README.md +0 -114
  146. package/src/ui/switch/Switch.stx +0 -72
  147. package/src/ui/switch/index.ts +0 -10
  148. package/src/ui/table/README.md +0 -39
  149. package/src/ui/table/Table.stx +0 -14
  150. package/src/ui/table/TableBody.stx +0 -10
  151. package/src/ui/table/TableCell.stx +0 -10
  152. package/src/ui/table/TableHead.stx +0 -10
  153. package/src/ui/table/TableHeader.stx +0 -20
  154. package/src/ui/table/TableRow.stx +0 -11
  155. package/src/ui/table/index.ts +0 -35
  156. package/src/ui/tabs/TabPanel.stx +0 -25
  157. package/src/ui/tabs/Tabs.stx +0 -202
  158. package/src/ui/tabs/index.ts +0 -50
  159. package/src/ui/teleport/Teleport.stx +0 -65
  160. package/src/ui/teleport/index.ts +0 -7
  161. package/src/ui/textarea/Textarea.stx +0 -108
  162. package/src/ui/textarea/index.ts +0 -20
  163. package/src/ui/tooltip/Tooltip.stx +0 -71
  164. package/src/ui/tooltip/index.ts +0 -10
  165. package/src/ui/transition/README.md +0 -30
  166. package/src/ui/transition/Transition.stx +0 -102
  167. package/src/ui/transition/index.ts +0 -25
  168. package/src/ui/video/README.md +0 -47
  169. package/src/ui/video/Video.stx +0 -45
  170. package/src/ui/video/index.ts +0 -16
  171. package/src/ui/virtual-list/VirtualList.stx +0 -67
  172. package/src/ui/virtual-list/index.ts +0 -10
  173. package/src/ui/virtual-table/VirtualTable.stx +0 -103
  174. package/src/ui/virtual-table/index.ts +0 -21
  175. package/src/utils/PROP_VALIDATION.md +0 -458
  176. package/src/utils/THEME_SYSTEM.md +0 -702
  177. package/src/utils/accessibility.ts +0 -841
  178. package/src/utils/animation/easing.ts +0 -368
  179. package/src/utils/animation/helpers.ts +0 -223
  180. package/src/utils/animation/index.ts +0 -5
  181. package/src/utils/animation/keyframes.ts +0 -230
  182. package/src/utils/error-handling.ts +0 -475
  183. package/src/utils/highlighter.ts +0 -93
  184. package/src/utils/i18n.ts +0 -634
  185. package/src/utils/keep-alive.ts +0 -819
  186. package/src/utils/lazy-loading.ts +0 -530
  187. package/src/utils/prop-validation.ts +0 -478
  188. package/src/utils/theme.ts +0 -626
@@ -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
- }
@@ -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
- ```
@@ -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>
@@ -1,10 +0,0 @@
1
- export { default as Switch } from './Switch.stx'
2
-
3
- export interface SwitchProps {
4
- checked?: boolean
5
- disabled?: boolean
6
- size?: 'sm' | 'md' | 'lg'
7
- className?: string
8
- onChange?: (checked: boolean) => void
9
- label?: string
10
- }
@@ -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
@@ -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,10 +0,0 @@
1
- <script server>
2
- export const className = $props.className || ''
3
-
4
- const baseClasses = 'bg-neutral-50 dark:bg-neutral-800'
5
- const headClasses = `${baseClasses} ${className}`.trim()
6
- </script>
7
-
8
- <thead class="{{ headClasses }}">
9
- <slot />
10
- </thead>
@@ -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>
@@ -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
- }