@stacksjs/components 0.2.27 → 0.2.53

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 (237) hide show
  1. package/dist/Accordion-19x8q4rj.stx +88 -0
  2. package/dist/{Audio-vdmg1258.stx → Audio-hjywk3qc.stx} +4 -4
  3. package/dist/{Avatar-365cvrh4.stx → Avatar-h3jmwcq0.stx} +23 -34
  4. package/dist/{Badge-8m3qete4.stx → Badge-k8dy3d53.stx} +11 -19
  5. package/dist/{Button-3tt4vsqt.stx → Button-1vknasy3.stx} +18 -33
  6. package/dist/Calendar-reny0et7.stx +189 -0
  7. package/dist/{Card-6wdbexqr.stx → Card-c1g1fd1w.stx} +27 -33
  8. package/dist/{Checkbox-p3q3e3nf.stx → Checkbox-zgdmy3p5.stx} +36 -44
  9. package/dist/{Checkout-e4037tyy.stx → Checkout-agt0ngpw.stx} +8 -16
  10. package/dist/Combobox-fdkr6cdq.stx +96 -0
  11. package/dist/{ComboboxButton-8gxwkhdx.stx → ComboboxButton-g76stp1e.stx} +14 -2
  12. package/dist/{ComboboxInput-mk99z65n.stx → ComboboxInput-z1293bbh.stx} +12 -5
  13. package/dist/ComboboxOption-hmxt3gte.stx +49 -0
  14. package/dist/{ListboxOptions-8kqvfp7z.stx → ComboboxOptions-sq5360ae.stx} +2 -2
  15. package/dist/CommandPalette-36mxf05w.stx +81 -0
  16. package/dist/CommandPaletteItem-mvk6q7a2.stx +27 -0
  17. package/dist/Dialog-hczpexs4.stx +37 -0
  18. package/dist/{Drawer-p7m351bn.stx → Drawer-xhevp5cw.stx} +29 -49
  19. package/dist/Dropdown-vvhemtc0.stx +82 -0
  20. package/dist/{DropdownButton-9w8zkfwj.stx → DropdownButton-wcz67dxy.stx} +12 -6
  21. package/dist/DropdownItem-c7rz08e5.stx +35 -0
  22. package/dist/{DropdownItems-aygjpxkx.stx → DropdownItems-1sxnd5qn.stx} +2 -2
  23. package/dist/EmailInput-vak3kkmk.stx +30 -0
  24. package/dist/Form-mas630hy.stx +95 -0
  25. package/dist/{Image-jhkm6w1f.stx → Image-yhxcvgzs.stx} +68 -100
  26. package/dist/Listbox-q49tnr90.stx +101 -0
  27. package/dist/{ListboxButton-spwx7513.stx → ListboxButton-keb8gm0q.stx} +12 -1
  28. package/dist/ListboxOption-e0dtv1gc.stx +49 -0
  29. package/dist/{ComboboxOptions-6b5ywc5x.stx → ListboxOptions-ejkatcfg.stx} +7 -3
  30. package/dist/{Login-rx9gyqhx.stx → Login-gepemsth.stx} +17 -19
  31. package/dist/Navigator-rk2a5wvk.stx +113 -0
  32. package/dist/Notification-tb9b525s.stx +78 -0
  33. package/dist/{NumberInput-nzpkx1ry.stx → NumberInput-drnvtqej.stx} +40 -53
  34. package/dist/{Pagination-h61eq390.stx → Pagination-n3d31cns.stx} +73 -108
  35. package/dist/PasswordInput-n02x6053.stx +142 -0
  36. package/dist/{PaymentMethods-j42jsk5x.stx → PaymentMethods-x4vrx9yb.stx} +21 -30
  37. package/dist/Popover-et2q44m7.stx +60 -0
  38. package/dist/{PopoverButton-fnaqc77g.stx → PopoverButton-mffzc9pr.stx} +12 -6
  39. package/dist/{PopoverPanel-y0vtf5w9.stx → PopoverPanel-054yh38h.stx} +6 -6
  40. package/dist/{Portal-s2q37yp0.stx → Portal-1vf14s4x.stx} +20 -37
  41. package/dist/{Progress-vmdkm06p.stx → Progress-yxcntr4x.stx} +36 -48
  42. package/dist/{Radio-04bs3d21.stx → Radio-cgjv2hb2.stx} +29 -34
  43. package/dist/RadioGroup-qxmnm3xm.stx +60 -0
  44. package/dist/RadioGroupOption-s1d6r221.stx +58 -0
  45. package/dist/SearchInput-pdx7agn0.stx +44 -0
  46. package/dist/{Select-g4mmvfv8.stx → Select-28y0pg83.stx} +23 -31
  47. package/dist/Sidebar-73vc78as.stx +236 -0
  48. package/dist/SidebarFooter-zsfsgcxb.stx +109 -0
  49. package/dist/SidebarHeader-7v174a9c.stx +122 -0
  50. package/dist/SidebarItem-3y3e8g2t.stx +92 -0
  51. package/dist/SidebarSection-z5ey6awe.stx +113 -0
  52. package/dist/{Signup-c4dyv6f3.stx → Signup-hxm5hwkr.stx} +26 -28
  53. package/dist/{StepperStep-en4r00zv.stx → StepperStep-wxgembat.stx} +9 -5
  54. package/dist/{SubscriptionCheckout-gawjzf3q.stx → SubscriptionCheckout-zy2ycrmw.stx} +99 -129
  55. package/dist/Switch-r5t5w04w.stx +70 -0
  56. package/dist/{TableHeader-ydys9rzz.stx → TableHeader-0eyzbqpz.stx} +10 -3
  57. package/dist/Tabs-bzvvn4ke.stx +126 -0
  58. package/dist/Teleport-tsx4bt4n.stx +65 -0
  59. package/dist/{TextInput-k2kfkgy1.stx → TextInput-kf084763.stx} +42 -59
  60. package/dist/{Textarea-szad57dz.stx → Textarea-xg4rr2xz.stx} +42 -54
  61. package/dist/{Tooltip-em7t551n.stx → Tooltip-m0tqd72e.stx} +34 -40
  62. package/dist/Transition-7smenaz8.stx +102 -0
  63. package/dist/{TwoFactorChallenge-an9yvxvq.stx → TwoFactorChallenge-y5m3nk79.stx} +49 -63
  64. package/dist/{Video-crxva65p.stx → Video-84j4pv6h.stx} +4 -4
  65. package/dist/VirtualList-fa7v5a3h.stx +67 -0
  66. package/dist/VirtualTable-f9crbfjr.stx +103 -0
  67. package/dist/components/CodeBlock.stx +1 -6
  68. package/dist/index.js +13 -13
  69. package/dist/ui/accordion/Accordion.stx +1 -3
  70. package/dist/ui/auth/Login.stx +0 -1
  71. package/dist/ui/auth/Signup.stx +0 -1
  72. package/dist/ui/auth/TwoFactorChallenge.stx +1 -3
  73. package/dist/ui/avatar/Avatar.stx +0 -2
  74. package/dist/ui/badge/Badge.stx +0 -1
  75. package/dist/ui/button/Button.stx +1 -2
  76. package/dist/ui/calendar/Calendar.stx +12 -2
  77. package/dist/ui/card/Card.stx +0 -2
  78. package/dist/ui/checkbox/Checkbox.stx +2 -2
  79. package/dist/ui/combobox/Combobox.stx +2 -1
  80. package/dist/ui/combobox/ComboboxButton.stx +1 -0
  81. package/dist/ui/combobox/ComboboxInput.stx +1 -4
  82. package/dist/ui/combobox/ComboboxOption.stx +2 -9
  83. package/dist/ui/combobox/ComboboxOptions.stx +1 -1
  84. package/dist/ui/command-palette/CommandPalette.stx +3 -6
  85. package/dist/ui/command-palette/CommandPaletteItem.stx +2 -7
  86. package/dist/ui/dialog/Dialog.stx +1 -3
  87. package/dist/ui/drawer/Drawer.stx +1 -3
  88. package/dist/ui/dropdown/Dropdown.stx +2 -5
  89. package/dist/ui/dropdown/DropdownButton.stx +1 -3
  90. package/dist/ui/dropdown/DropdownItem.stx +2 -7
  91. package/dist/ui/dropdown/DropdownItems.stx +1 -1
  92. package/dist/ui/form/Form.stx +0 -5
  93. package/dist/ui/image/Image.stx +1 -6
  94. package/dist/ui/input/EmailInput.stx +1 -1
  95. package/dist/ui/input/NumberInput.stx +0 -1
  96. package/dist/ui/input/PasswordInput.stx +1 -4
  97. package/dist/ui/input/SearchInput.stx +2 -2
  98. package/dist/ui/input/TextInput.stx +1 -2
  99. package/dist/ui/listbox/Listbox.stx +2 -1
  100. package/dist/ui/listbox/ListboxButton.stx +1 -0
  101. package/dist/ui/listbox/ListboxOption.stx +2 -9
  102. package/dist/ui/listbox/ListboxOptions.stx +1 -1
  103. package/dist/ui/navigator/Navigator.stx +4 -4
  104. package/dist/ui/notification/Notification.stx +1 -5
  105. package/dist/ui/pagination/Pagination.stx +1 -2
  106. package/dist/ui/payment/Checkout.stx +0 -4
  107. package/dist/ui/payment/PaymentMethods.stx +15 -2
  108. package/dist/ui/payment/SubscriptionCheckout.stx +3 -184
  109. package/dist/ui/popover/Popover.stx +2 -5
  110. package/dist/ui/popover/PopoverButton.stx +1 -3
  111. package/dist/ui/popover/PopoverPanel.stx +1 -1
  112. package/dist/ui/portal/Portal.stx +1 -0
  113. package/dist/ui/progress/Progress.stx +6 -1
  114. package/dist/ui/radio/Radio.stx +1 -1
  115. package/dist/ui/radio-group/RadioGroup.stx +2 -1
  116. package/dist/ui/radio-group/RadioGroupOption.stx +2 -9
  117. package/dist/ui/select/Select.stx +1 -1
  118. package/dist/ui/sidebar/Sidebar.stx +9 -26
  119. package/dist/ui/sidebar/SidebarFooter.stx +6 -21
  120. package/dist/ui/sidebar/SidebarHeader.stx +7 -18
  121. package/dist/ui/sidebar/SidebarItem.stx +4 -20
  122. package/dist/ui/sidebar/SidebarSection.stx +5 -18
  123. package/dist/ui/sidebar/index.d.ts +13 -1
  124. package/dist/ui/stepper/StepperStep.stx +1 -5
  125. package/dist/ui/switch/Switch.stx +4 -10
  126. package/dist/ui/table/TableHeader.stx +1 -1
  127. package/dist/ui/tabs/Tabs.stx +0 -1
  128. package/dist/ui/teleport/Teleport.stx +1 -0
  129. package/dist/ui/textarea/Textarea.stx +1 -1
  130. package/dist/ui/tooltip/Tooltip.stx +1 -2
  131. package/dist/ui/transition/Transition.stx +9 -16
  132. package/dist/ui/virtual-list/VirtualList.stx +2 -4
  133. package/dist/ui/virtual-table/VirtualTable.stx +1 -1
  134. package/package.json +3 -3
  135. package/src/components/CodeBlock.stx +26 -29
  136. package/src/components/Footer.stx +1 -1
  137. package/src/components/Hero.stx +1 -1
  138. package/src/components/Installation.stx +5 -7
  139. package/src/ui/accordion/Accordion.stx +48 -68
  140. package/src/ui/audio/Audio.stx +4 -4
  141. package/src/ui/auth/Login.stx +17 -19
  142. package/src/ui/auth/Signup.stx +26 -28
  143. package/src/ui/auth/TwoFactorChallenge.stx +49 -63
  144. package/src/ui/avatar/Avatar.stx +23 -34
  145. package/src/ui/badge/Badge.stx +11 -19
  146. package/src/ui/button/Button.stx +18 -33
  147. package/src/ui/calendar/Calendar.stx +93 -136
  148. package/src/ui/card/Card.stx +27 -33
  149. package/src/ui/checkbox/Checkbox.stx +36 -44
  150. package/src/ui/combobox/Combobox.stx +87 -4
  151. package/src/ui/combobox/ComboboxButton.stx +14 -2
  152. package/src/ui/combobox/ComboboxInput.stx +12 -5
  153. package/src/ui/combobox/ComboboxOption.stx +29 -32
  154. package/src/ui/combobox/ComboboxOptions.stx +7 -3
  155. package/src/ui/command-palette/CommandPalette.stx +70 -28
  156. package/src/ui/command-palette/CommandPaletteItem.stx +15 -20
  157. package/src/ui/dialog/Dialog.stx +28 -10
  158. package/src/ui/drawer/Drawer.stx +29 -49
  159. package/src/ui/dropdown/Dropdown.stx +69 -11
  160. package/src/ui/dropdown/DropdownButton.stx +12 -6
  161. package/src/ui/dropdown/DropdownItem.stx +18 -26
  162. package/src/ui/dropdown/DropdownItems.stx +2 -2
  163. package/src/ui/form/Form.stx +50 -164
  164. package/src/ui/heatmap/Heatmap.stx +16 -37
  165. package/src/ui/image/Image.stx +68 -100
  166. package/src/ui/input/EmailInput.stx +16 -27
  167. package/src/ui/input/NumberInput.stx +40 -53
  168. package/src/ui/input/PasswordInput.stx +64 -77
  169. package/src/ui/input/SearchInput.stx +27 -46
  170. package/src/ui/input/TextInput.stx +42 -59
  171. package/src/ui/listbox/Listbox.stx +91 -4
  172. package/src/ui/listbox/ListboxButton.stx +12 -1
  173. package/src/ui/listbox/ListboxOption.stx +30 -38
  174. package/src/ui/listbox/ListboxOptions.stx +2 -2
  175. package/src/ui/navigator/Navigator.stx +52 -77
  176. package/src/ui/notification/Notification.stx +46 -39
  177. package/src/ui/pagination/Pagination.stx +73 -108
  178. package/src/ui/payment/Checkout.stx +8 -16
  179. package/src/ui/payment/PaymentMethods.stx +21 -30
  180. package/src/ui/payment/SubscriptionCheckout.stx +99 -129
  181. package/src/ui/popover/Popover.stx +50 -14
  182. package/src/ui/popover/PopoverButton.stx +12 -6
  183. package/src/ui/popover/PopoverPanel.stx +6 -6
  184. package/src/ui/portal/Portal.stx +20 -37
  185. package/src/ui/progress/Progress.stx +36 -48
  186. package/src/ui/radio/Radio.stx +29 -34
  187. package/src/ui/radio-group/RadioGroup.stx +51 -4
  188. package/src/ui/radio-group/RadioGroupOption.stx +37 -30
  189. package/src/ui/select/Select.stx +23 -31
  190. package/src/ui/sidebar/Sidebar.stx +136 -188
  191. package/src/ui/sidebar/SidebarFooter.stx +49 -35
  192. package/src/ui/sidebar/SidebarHeader.stx +52 -30
  193. package/src/ui/sidebar/SidebarItem.stx +52 -78
  194. package/src/ui/sidebar/SidebarSection.stx +56 -51
  195. package/src/ui/sidebar/index.ts +21 -1
  196. package/src/ui/stepper/StepperStep.stx +9 -5
  197. package/src/ui/switch/Switch.stx +38 -42
  198. package/src/ui/table/TableHeader.stx +10 -3
  199. package/src/ui/tabs/Tabs.stx +68 -87
  200. package/src/ui/teleport/Teleport.stx +23 -59
  201. package/src/ui/textarea/Textarea.stx +42 -54
  202. package/src/ui/tooltip/Tooltip.stx +34 -40
  203. package/src/ui/transition/Transition.stx +76 -148
  204. package/src/ui/video/Video.stx +4 -4
  205. package/src/ui/virtual-list/VirtualList.stx +45 -61
  206. package/src/ui/virtual-table/VirtualTable.stx +62 -72
  207. package/dist/Accordion-sbfra0mz.stx +0 -108
  208. package/dist/Calendar-58nnkdh8.stx +0 -232
  209. package/dist/Combobox-th5ebw62.stx +0 -13
  210. package/dist/ComboboxOption-an9zyan6.stx +0 -52
  211. package/dist/CommandPalette-kwmdf2np.stx +0 -39
  212. package/dist/CommandPaletteItem-zx2fbpts.stx +0 -32
  213. package/dist/Dialog-rgmj8q2j.stx +0 -19
  214. package/dist/Dropdown-jz4jy0n3.stx +0 -24
  215. package/dist/DropdownItem-wagdspb8.stx +0 -43
  216. package/dist/EmailInput-ajsp9vw6.stx +0 -41
  217. package/dist/Form-1mdmx9b6.stx +0 -209
  218. package/dist/Listbox-pqqh72g8.stx +0 -14
  219. package/dist/ListboxOption-7nxzaexq.stx +0 -57
  220. package/dist/Navigator-aay8bjab.stx +0 -138
  221. package/dist/Notification-gk33gv7b.stx +0 -71
  222. package/dist/PasswordInput-zv4hm43j.stx +0 -155
  223. package/dist/Popover-469gbt6r.stx +0 -24
  224. package/dist/RadioGroup-bvb1mrhd.stx +0 -13
  225. package/dist/RadioGroupOption-cpdwgnhw.stx +0 -51
  226. package/dist/SearchInput-2521m475.stx +0 -63
  227. package/dist/Sidebar-40ckbzh8.stx +0 -288
  228. package/dist/SidebarFooter-hnth56mg.stx +0 -95
  229. package/dist/SidebarHeader-3wrte2p2.stx +0 -100
  230. package/dist/SidebarItem-zqtvvzpc.stx +0 -118
  231. package/dist/SidebarSection-3etwnbjr.stx +0 -108
  232. package/dist/Switch-036j9vm1.stx +0 -74
  233. package/dist/Tabs-svh721m0.stx +0 -145
  234. package/dist/Teleport-qfch3w0b.stx +0 -101
  235. package/dist/Transition-0hmr6zxb.stx +0 -174
  236. package/dist/VirtualList-cndhjejh.stx +0 -83
  237. package/dist/VirtualTable-ng0b48yp.stx +0 -113
@@ -2,73 +2,56 @@
2
2
  export const target = $props.target || 'body'
3
3
  export const disabled = $props.disabled || false
4
4
 
5
- let portalContainer = null
6
- let mounted = false
5
+ export const portalId = `portal-content-${Math.random().toString(36).slice(2, 11)}`
6
+ </script>
7
7
 
8
- // Create portal container
9
- function createPortal() {
10
- if (typeof window === 'undefined' || disabled)
11
- return
8
+ <script client>
9
+ const target = {{ target }}
10
+ const isDisabled = {{ disabled }}
11
+ const portalId = {{ portalId }}
12
12
 
13
- // Get target element
14
- const targetElement = typeof target === 'string'
15
- ? document.querySelector(target)
16
- : target
13
+ let portalContainer = null
17
14
 
15
+ function createPortal() {
16
+ if (isDisabled) return
17
+ const targetElement = typeof target === 'string' ? document.querySelector(target) : target
18
18
  if (!targetElement) {
19
19
  console.warn(`Portal target "${target}" not found`)
20
20
  return
21
21
  }
22
-
23
- // Create portal container
24
22
  portalContainer = document.createElement('div')
25
23
  portalContainer.setAttribute('data-portal', 'true')
26
-
27
- // Move portal content to target
28
- const portalContent = document.getElementById('portal-content')
24
+ const portalContent = document.getElementById(portalId)
29
25
  if (portalContent) {
30
26
  while (portalContent.firstChild) {
31
27
  portalContainer.appendChild(portalContent.firstChild)
32
28
  }
33
29
  }
34
-
35
30
  targetElement.appendChild(portalContainer)
36
- mounted = true
37
31
  }
38
32
 
39
- // Cleanup portal
40
33
  function cleanupPortal() {
41
34
  if (portalContainer && portalContainer.parentNode) {
42
35
  portalContainer.parentNode.removeChild(portalContainer)
43
36
  portalContainer = null
44
- mounted = false
45
37
  }
46
38
  }
47
39
 
48
- // Initialize portal on mount
49
- if (typeof window !== 'undefined') {
50
- window.addEventListener('DOMContentLoaded', () => {
51
- setTimeout(createPortal, 0)
52
- })
53
-
54
- // Cleanup on page unload
40
+ onMount(() => {
41
+ setTimeout(createPortal, 0)
55
42
  window.addEventListener('beforeunload', cleanupPortal)
56
- }
43
+ })
44
+
45
+ onDestroy(() => {
46
+ cleanupPortal()
47
+ window.removeEventListener('beforeunload', cleanupPortal)
48
+ })
57
49
  </script>
58
50
 
59
51
  @if(disabled)
60
- <!-- Render in place if disabled -->
61
52
  <slot />
62
53
  @else
63
- <!-- Hidden container that will be moved to portal -->
64
- <div id="portal-content" style="display: none;">
54
+ <div id="{{ portalId }}" style="display: none;">
65
55
  <slot />
66
56
  </div>
67
57
  @endif
68
-
69
- <script>
70
- // Trigger portal creation after render
71
- if (typeof window !== 'undefined' && document.readyState === 'complete') {
72
- setTimeout(createPortal, 0)
73
- }
74
- </script>
@@ -8,24 +8,8 @@ export const showLabel = $props.showLabel ?? false
8
8
  export const indeterminate = $props.indeterminate || false
9
9
  export const className = $props.className || ''
10
10
 
11
- // Calculate percentage
12
- const percentage = Math.min(Math.max((value / max) * 100, 0), 100)
13
-
14
- // Size classes for linear
15
- const linearSizeClasses = {
16
- sm: 'h-1',
17
- md: 'h-2',
18
- lg: 'h-3',
19
- }
20
-
21
- // Size classes for circular
22
- const circularSizeClasses = {
23
- sm: 'w-12 h-12',
24
- md: 'w-16 h-16',
25
- lg: 'w-24 h-24',
26
- }
27
-
28
- // Color classes
11
+ const linearSizeClasses = { sm: 'h-1', md: 'h-2', lg: 'h-3' }
12
+ const circularSizeClasses = { sm: 'w-12 h-12', md: 'w-16 h-16', lg: 'w-24 h-24' }
29
13
  const colorClasses = {
30
14
  primary: 'bg-blue-600 dark:bg-blue-500',
31
15
  secondary: 'bg-purple-600 dark:bg-purple-500',
@@ -35,12 +19,10 @@ const colorClasses = {
35
19
  info: 'bg-cyan-600 dark:bg-cyan-500',
36
20
  }
37
21
 
38
- // Circular SVG values
39
- const circularSize = size === 'sm' ? 48 : size === 'md' ? 64 : 96
40
- const strokeWidth = size === 'sm' ? 4 : size === 'md' ? 6 : 8
41
- const radius = (circularSize - strokeWidth) / 2
42
- const circumference = 2 * Math.PI * radius
43
- const strokeDashoffset = indeterminate ? 0 : circumference - (percentage / 100) * circumference
22
+ export const circularSize = size === 'sm' ? 48 : size === 'md' ? 64 : 96
23
+ export const strokeWidth = size === 'sm' ? 4 : size === 'md' ? 6 : 8
24
+ export const radius = (circularSize - strokeWidth) / 2
25
+ export const circumference = 2 * Math.PI * radius
44
26
 
45
27
  export const containerClasses = variant === 'linear'
46
28
  ? `w-full ${className}`.trim()
@@ -48,51 +30,64 @@ export const containerClasses = variant === 'linear'
48
30
 
49
31
  export const linearTrackClasses = `w-full ${linearSizeClasses[size]} bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden`.trim()
50
32
  export const linearBarClasses = `h-full ${colorClasses[color]} transition-all duration-300 ease-in-out ${indeterminate ? 'animate-progress-indeterminate' : ''}`.trim()
33
+ export const circularSizeClass = circularSizeClasses[size]
51
34
  export const labelClasses = `text-sm font-medium text-gray-700 dark:text-gray-300 mb-2`.trim()
52
35
  export const circularTrackColor = 'stroke-gray-200 dark:stroke-gray-700'
53
- export const circularBarColor = color === 'primary' ? 'stroke-blue-600 dark:stroke-blue-500' :
54
- color === 'secondary' ? 'stroke-purple-600 dark:stroke-purple-500' :
55
- color === 'success' ? 'stroke-green-600 dark:stroke-green-500' :
56
- color === 'warning' ? 'stroke-yellow-600 dark:stroke-yellow-500' :
57
- color === 'danger' ? 'stroke-red-600 dark:stroke-red-500' :
58
- 'stroke-cyan-600 dark:stroke-cyan-500'
36
+ export const circularBarColor = color === 'primary' ? 'stroke-blue-600 dark:stroke-blue-500'
37
+ : color === 'secondary' ? 'stroke-purple-600 dark:stroke-purple-500'
38
+ : color === 'success' ? 'stroke-green-600 dark:stroke-green-500'
39
+ : color === 'warning' ? 'stroke-yellow-600 dark:stroke-yellow-500'
40
+ : color === 'danger' ? 'stroke-red-600 dark:stroke-red-500'
41
+ : 'stroke-cyan-600 dark:stroke-cyan-500'
42
+ </script>
43
+
44
+ <script client>
45
+ const max = {{ max }}
46
+ const indeterminate = {{ indeterminate }}
47
+ const circumference = {{ circumference }}
48
+ const value = state({{ value }})
49
+
50
+ const percentage = derived(() => Math.min(Math.max((value() / max) * 100, 0), 100))
51
+ const percentLabel = derived(() => `${Math.round(percentage())}%`)
52
+ const linearWidth = derived(() => indeterminate ? '30%' : `${percentage()}%`)
53
+ const dashOffset = derived(() => indeterminate ? 0 : circumference - (percentage() / 100) * circumference)
54
+
55
+ defineExpose({ value, setValue: (v) => value.set(v) })
59
56
  </script>
60
57
 
61
58
  @if(variant === 'linear')
62
59
  <div class="{{ containerClasses }}">
63
60
  @if(showLabel)
64
61
  <div class="flex items-center justify-between mb-2">
65
- <span class="{{ labelClasses }}">{{ Math.round(percentage) }}%</span>
62
+ <span class="{{ labelClasses }}" x-text="percentLabel()"></span>
66
63
  </div>
67
64
  @endif
68
65
 
69
66
  <div
70
67
  class="{{ linearTrackClasses }}"
71
68
  role="progressbar"
72
- aria-valuenow="{{ value }}"
69
+ :aria-valuenow="value()"
73
70
  aria-valuemin="0"
74
71
  aria-valuemax="{{ max }}"
75
72
  >
76
73
  <div
77
74
  class="{{ linearBarClasses }}"
78
- style="width: {{ indeterminate ? '30%' : percentage + '%' }}"
75
+ :style="`width: ${linearWidth()}`"
79
76
  ></div>
80
77
  </div>
81
78
  </div>
82
79
  @else
83
- <!-- Circular Progress -->
84
80
  <div class="{{ containerClasses }}">
85
- <div class="{{ circularSizeClasses[size] }} relative">
81
+ <div class="{{ circularSizeClass }} relative">
86
82
  <svg
87
83
  class="transform -rotate-90"
88
84
  width="{{ circularSize }}"
89
85
  height="{{ circularSize }}"
90
86
  role="progressbar"
91
- aria-valuenow="{{ value }}"
87
+ :aria-valuenow="value()"
92
88
  aria-valuemin="0"
93
89
  aria-valuemax="{{ max }}"
94
90
  >
95
- <!-- Background circle -->
96
91
  <circle
97
92
  class="{{ circularTrackColor }}"
98
93
  cx="{{ circularSize / 2 }}"
@@ -101,7 +96,6 @@ export const circularBarColor = color === 'primary' ? 'stroke-blue-600 dark:stro
101
96
  stroke-width="{{ strokeWidth }}"
102
97
  fill="none"
103
98
  />
104
- <!-- Progress circle -->
105
99
  <circle
106
100
  class="{{ circularBarColor }} transition-all duration-300 ease-in-out {{ indeterminate ? 'animate-spin' : '' }}"
107
101
  cx="{{ circularSize / 2 }}"
@@ -110,16 +104,14 @@ export const circularBarColor = color === 'primary' ? 'stroke-blue-600 dark:stro
110
104
  stroke-width="{{ strokeWidth }}"
111
105
  fill="none"
112
106
  stroke-dasharray="{{ circumference }}"
113
- stroke-dashoffset="{{ strokeDashoffset }}"
107
+ :stroke-dashoffset="dashOffset()"
114
108
  stroke-linecap="round"
115
109
  />
116
110
  </svg>
117
111
 
118
112
  @if(showLabel)
119
113
  <div class="absolute inset-0 flex items-center justify-center">
120
- <span class="text-sm font-semibold text-gray-700 dark:text-gray-300">
121
- {{ Math.round(percentage) }}%
122
- </span>
114
+ <span class="text-sm font-semibold text-gray-700 dark:text-gray-300" x-text="percentLabel()"></span>
123
115
  </div>
124
116
  @endif
125
117
  </div>
@@ -128,12 +120,8 @@ export const circularBarColor = color === 'primary' ? 'stroke-blue-600 dark:stro
128
120
 
129
121
  <style>
130
122
  @keyframes progress-indeterminate {
131
- 0% {
132
- transform: translateX(-100%);
133
- }
134
- 100% {
135
- transform: translateX(400%);
136
- }
123
+ 0% { transform: translateX(-100%); }
124
+ 100% { transform: translateX(400%); }
137
125
  }
138
126
 
139
127
  .animate-progress-indeterminate {
@@ -1,6 +1,5 @@
1
1
  <script server>
2
2
  export const checked = $props.checked || false
3
- export const onChange = $props.onChange || (() => {})
4
3
  export const disabled = $props.disabled || false
5
4
  export const size = $props.size || 'md'
6
5
  export const label = $props.label || ''
@@ -11,42 +10,48 @@ export const value = $props.value || ''
11
10
  export const name = $props.name || ''
12
11
  export const className = $props.className || ''
13
12
 
14
- // Size classes
15
- const sizeClasses = {
16
- sm: 'h-4 w-4',
17
- md: 'h-5 w-5',
18
- lg: 'h-6 w-6',
19
- }
20
-
21
- // Base classes for radio
13
+ const sizeClasses = { sm: 'h-4 w-4', md: 'h-5 w-5', lg: 'h-6 w-6' }
22
14
  const baseClasses = 'border-gray-300 dark:border-gray-600 text-indigo-600 dark:text-indigo-500 focus:ring-indigo-600 dark:focus:ring-indigo-500 focus:ring-2 focus:ring-offset-2 dark:focus:ring-offset-gray-900 transition-colors'
23
-
24
- const stateClasses = error
25
- ? 'border-red-300 dark:border-red-600 text-red-600 dark:text-red-500 focus:ring-red-500 dark:focus:ring-red-400'
26
- : ''
27
-
28
- const disabledClasses = disabled
29
- ? 'opacity-50 cursor-not-allowed'
30
- : 'cursor-pointer'
15
+ const stateClasses = error ? 'border-red-300 dark:border-red-600 text-red-600 dark:text-red-500 focus:ring-red-500 dark:focus:ring-red-400' : ''
16
+ const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'
31
17
 
32
18
  export const radioClasses = `${baseClasses} ${sizeClasses[size]} ${stateClasses} ${disabledClasses}`.trim()
33
19
  export const labelClasses = `text-sm font-medium ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-900 dark:text-gray-100'} ${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`.trim()
34
20
  export const descriptionClasses = `text-sm ${error ? 'text-red-500 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'}`.trim()
21
+ export const inputId = `radio-${value || label}`
22
+ </script>
23
+
24
+ <script client>
25
+ const emit = defineEmits()
26
+ const isDisabled = {{ disabled }}
27
+ const radioValue = {{ value }}
28
+ const isChecked = state({{ checked }})
29
+
30
+ function onInputChange(event) {
31
+ if (isDisabled) {
32
+ event.target.checked = isChecked()
33
+ return
34
+ }
35
+ isChecked.set(event.target.checked)
36
+ emit('change', { value: radioValue, checked: isChecked() })
37
+ }
38
+
39
+ defineExpose({ isChecked })
35
40
  </script>
36
41
 
37
42
  <div class="relative flex items-start {{ className }}">
38
43
  <div class="flex h-6 items-center">
39
44
  <input
40
- id="radio-{{ value || label }}"
45
+ id="{{ inputId }}"
41
46
  type="radio"
42
47
  name="{{ name }}"
43
48
  value="{{ value }}"
44
- checked="{{ checked }}"
45
- disabled="{{ disabled }}"
46
- required="{{ required }}"
49
+ :checked="isChecked()"
50
+ @if(disabled)disabled@endif
51
+ @if(required)required@endif
47
52
  class="{{ radioClasses }}"
48
- onchange="handleChange(event)"
49
- aria-invalid="{{ error ? 'true' : 'false' }}"
53
+ @change="onInputChange($event)"
54
+ :aria-invalid="{{ error ? 'true' : 'false' }}"
50
55
  @if(description)aria-describedby="radio-description"@endif
51
56
  />
52
57
  </div>
@@ -54,7 +59,7 @@ export const descriptionClasses = `text-sm ${error ? 'text-red-500 dark:text-red
54
59
  @if(label || description)
55
60
  <div class="ml-3 text-sm leading-6">
56
61
  @if(label)
57
- <label for="radio-{{ value || label }}" class="{{ labelClasses }}">
62
+ <label for="{{ inputId }}" class="{{ labelClasses }}">
58
63
  {{ label }}
59
64
  @if(required)
60
65
  <span class="text-red-500 ml-1">*</span>
@@ -69,13 +74,3 @@ export const descriptionClasses = `text-sm ${error ? 'text-red-500 dark:text-red
69
74
  </div>
70
75
  @endif
71
76
  </div>
72
-
73
- <script>
74
- function handleChange(event) {
75
- const isChecked = event.target.checked
76
-
77
- if (!disabled && onChange) {
78
- onChange(value, isChecked)
79
- }
80
- }
81
- </script>
@@ -0,0 +1,60 @@
1
+ <script server>
2
+ export const value = $props.value
3
+ export const className = $props.className || ''
4
+ export const as = $props.as || 'div'
5
+
6
+ const baseClasses = 'w-full'
7
+ export const groupClasses = `${baseClasses} ${className}`.trim()
8
+ export const radioGroupInstanceId = `stx-rg-${Math.random().toString(36).slice(2, 11)}`
9
+ </script>
10
+
11
+ <script client>
12
+ const emit = defineEmits()
13
+ const radioGroupInstanceId = {{ radioGroupInstanceId }}
14
+ const initialValue = {{ value === undefined ? 'null' : value }}
15
+ const selected = state(initialValue)
16
+ let rootEl = null
17
+
18
+ function selectValue(v) {
19
+ selected.set(v)
20
+ syncOptionStates()
21
+ emit('change', v)
22
+ }
23
+
24
+ function syncOptionStates() {
25
+ if (!rootEl) return
26
+ const options = rootEl.querySelectorAll('[data-stx-radio-option]')
27
+ for (const opt of options) {
28
+ const raw = opt.dataset.optionValue
29
+ let v
30
+ try { v = JSON.parse(raw ?? 'null') }
31
+ catch { v = raw }
32
+ const checked = selected() === v
33
+ opt.setAttribute('aria-checked', checked ? 'true' : 'false')
34
+ opt.setAttribute('data-checked', checked ? 'true' : 'false')
35
+ }
36
+ }
37
+
38
+ function onSelect(event) {
39
+ event.stopPropagation()
40
+ selectValue(event.detail)
41
+ }
42
+
43
+ onMount(() => {
44
+ rootEl = document.querySelector(`[data-radio-group-id="${radioGroupInstanceId}"]`)
45
+ syncOptionStates()
46
+ })
47
+
48
+ defineExpose({ selected, selectValue })
49
+ </script>
50
+
51
+ <component
52
+ :is="{{ as }}"
53
+ class="{{ groupClasses }}"
54
+ data-stx-radio-group
55
+ data-radio-group-id="{{ radioGroupInstanceId }}"
56
+ role="radiogroup"
57
+ @stx:radio-select="onSelect($event)"
58
+ >
59
+ <slot />
60
+ </component>
@@ -0,0 +1,58 @@
1
+ <script server>
2
+ export const value = $props.value
3
+ export const disabled = $props.disabled || false
4
+ export const className = $props.className || ''
5
+ export const as = $props.as || 'div'
6
+
7
+ const baseClasses = 'relative flex cursor-pointer rounded-lg px-5 py-4 shadow-md focus:outline-none transition-all bg-white dark:bg-neutral-800 text-neutral-900 dark:text-neutral-100 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-60 focus-visible:ring-offset-2 focus-visible:ring-offset-blue-300 dark:focus-visible:ring-offset-blue-700'
8
+ const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed' : ''
9
+ export const optionClasses = `${baseClasses} ${disabledClasses} ${className}`.trim()
10
+ export const valueJson = JSON.stringify(value === undefined ? null : value)
11
+ </script>
12
+
13
+ <script client>
14
+ const isDisabled = {{ disabled }}
15
+ const optionValue = {{ value === undefined ? 'null' : value }}
16
+
17
+ function onClick(event) {
18
+ if (isDisabled) return
19
+ event.currentTarget.dispatchEvent(
20
+ new CustomEvent('stx:radio-select', { bubbles: true, detail: optionValue }),
21
+ )
22
+ }
23
+
24
+ function onKey(event) {
25
+ if (isDisabled) return
26
+ if (event.key === ' ' || event.key === 'Enter') {
27
+ event.preventDefault()
28
+ event.currentTarget.dispatchEvent(
29
+ new CustomEvent('stx:radio-select', { bubbles: true, detail: optionValue }),
30
+ )
31
+ }
32
+ }
33
+ </script>
34
+
35
+ <component
36
+ :is="{{ as }}"
37
+ class="{{ optionClasses }}"
38
+ data-stx-radio-option
39
+ data-option-value="{{ valueJson }}"
40
+ role="radio"
41
+ aria-checked="false"
42
+ @if(disabled)aria-disabled="true"@endif
43
+ tabindex="{{ disabled ? '-1' : '0' }}"
44
+ @click="onClick($event)"
45
+ @keydown="onKey($event)"
46
+ >
47
+ <slot :disabled="disabled" />
48
+ </component>
49
+
50
+ <style>
51
+ [data-stx-radio-option][data-checked="true"] {
52
+ background-color: rgb(30 58 138 / 0.75);
53
+ color: white;
54
+ }
55
+ .dark [data-stx-radio-option][data-checked="true"] {
56
+ background-color: rgb(30 64 175 / 1);
57
+ }
58
+ </style>
@@ -0,0 +1,44 @@
1
+ <script server>
2
+ export const value = $props.value || ''
3
+ export const placeholder = $props.placeholder || 'Search...'
4
+ export const disabled = $props.disabled || false
5
+ export const size = $props.size || 'md'
6
+ export const error = $props.error || false
7
+ export const label = $props.label || ''
8
+ export const helperText = $props.helperText || ''
9
+ export const loading = $props.loading || false
10
+ export const className = $props.className || ''
11
+
12
+ export const searchIcon = '<svg fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /></svg>'
13
+ export const spinnerIcon = '<svg class="animate-spin" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>'
14
+ export const rightIcon = loading ? spinnerIcon : ''
15
+ </script>
16
+
17
+ <script client>
18
+ const emit = defineEmits()
19
+ let searchTimeout = null
20
+
21
+ function onSearchInput(event) {
22
+ const target = event.target
23
+ const value = target?.value ?? event.detail
24
+ if (searchTimeout) clearTimeout(searchTimeout)
25
+ searchTimeout = setTimeout(() => emit('search', value), 300)
26
+ }
27
+ </script>
28
+
29
+ <div @input="onSearchInput($event)">
30
+ <TextInput
31
+ :value="value"
32
+ :placeholder="placeholder"
33
+ :disabled="disabled"
34
+ :size="size"
35
+ :error="error"
36
+ :label="label"
37
+ :helperText="helperText"
38
+ :leftIcon="searchIcon"
39
+ :rightIcon="rightIcon"
40
+ :clearable="!loading"
41
+ :className="className"
42
+ inputClassName="pr-20"
43
+ />
44
+ </div>
@@ -1,6 +1,5 @@
1
1
  <script server>
2
2
  export const value = $props.value || ''
3
- export const onChange = $props.onChange || (() => {})
4
3
  export const options = $props.options || []
5
4
  export const placeholder = $props.placeholder || 'Select an option'
6
5
  export const disabled = $props.disabled || false
@@ -11,20 +10,11 @@ export const helperText = $props.helperText || ''
11
10
  export const required = $props.required || false
12
11
  export const className = $props.className || ''
13
12
 
14
- // Size classes
15
- const sizeClasses = {
16
- sm: 'px-3 py-1.5 text-sm h-8',
17
- md: 'px-4 py-2 text-base h-10',
18
- lg: 'px-5 py-3 text-lg h-12',
19
- }
20
-
21
- // Base and state classes
13
+ const sizeClasses = { sm: 'px-3 py-1.5 text-sm h-8', md: 'px-4 py-2 text-base h-10', lg: 'px-5 py-3 text-lg h-12' }
22
14
  const baseClasses = 'block w-full border-0 rounded-md shadow-sm ring-1 ring-inset transition-colors duration-200 focus:ring-2 focus:ring-inset dark:bg-blue-gray-700 dark:text-gray-100'
23
-
24
15
  const stateClasses = error
25
16
  ? 'ring-red-300 dark:ring-red-600 text-red-900 dark:text-red-300 placeholder-red-300 dark:placeholder-red-500 focus:ring-red-500 dark:focus:ring-red-400'
26
17
  : 'ring-gray-300 dark:ring-gray-600 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:ring-indigo-600 dark:focus:ring-indigo-400'
27
-
28
18
  const disabledClasses = disabled
29
19
  ? 'opacity-50 cursor-not-allowed bg-gray-50 dark:bg-gray-800'
30
20
  : 'bg-white dark:bg-blue-gray-700 cursor-pointer hover:ring-gray-400 dark:hover:ring-gray-500'
@@ -32,11 +22,24 @@ const disabledClasses = disabled
32
22
  export const selectClasses = `${baseClasses} ${sizeClasses[size]} ${stateClasses} ${disabledClasses} ${className}`.trim()
33
23
  export const labelClasses = `block text-sm font-medium leading-6 ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-900 dark:text-gray-100'}`.trim()
34
24
  export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'}`.trim()
25
+ export const selectId = `select-${value}`
26
+ </script>
27
+
28
+ <script client>
29
+ const emit = defineEmits()
30
+ const selectedValue = state({{ value }})
31
+
32
+ function onChange(event) {
33
+ selectedValue.set(event.target.value)
34
+ emit('change', selectedValue())
35
+ }
36
+
37
+ defineExpose({ value: selectedValue })
35
38
  </script>
36
39
 
37
40
  <div class="w-full">
38
41
  @if(label)
39
- <label for="select-{{ value }}" class="{{ labelClasses }}">
42
+ <label for="{{ selectId }}" class="{{ labelClasses }}">
40
43
  {{ label }}
41
44
  @if(required)
42
45
  <span class="text-red-500 ml-1">*</span>
@@ -46,24 +49,23 @@ export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red
46
49
 
47
50
  <div class="{{ label ? 'mt-2' : '' }}">
48
51
  <select
49
- id="select-{{ value }}"
52
+ id="{{ selectId }}"
50
53
  class="{{ selectClasses }}"
51
- value="{{ value }}"
52
- disabled="{{ disabled }}"
53
- required="{{ required }}"
54
- onchange="handleChange(event)"
55
- aria-invalid="{{ error ? 'true' : 'false' }}"
54
+ :value="selectedValue()"
55
+ @if(disabled)disabled@endif
56
+ @if(required)required@endif
57
+ @change="onChange($event)"
58
+ :aria-invalid="{{ error ? 'true' : 'false' }}"
56
59
  aria-describedby="{{ helperText ? 'helper-text' : '' }}"
57
60
  >
58
61
  @if(placeholder)
59
- <option value="" disabled selected>{{ placeholder }}</option>
62
+ <option value="" disabled>{{ placeholder }}</option>
60
63
  @endif
61
64
 
62
65
  @foreach(option in options)
63
66
  <option
64
67
  value="{{ option.value }}"
65
- selected="{{ option.value === value }}"
66
- disabled="{{ option.disabled }}"
68
+ @if(option.disabled)disabled@endif
67
69
  >
68
70
  {{ option.label }}
69
71
  </option>
@@ -77,13 +79,3 @@ export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red
77
79
  </p>
78
80
  @endif
79
81
  </div>
80
-
81
- <script>
82
- function handleChange(event) {
83
- const newValue = event.target.value
84
-
85
- if (onChange) {
86
- onChange(newValue)
87
- }
88
- }
89
- </script>