@stacksjs/components 0.2.35 → 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
@@ -8,9 +8,7 @@ export const fullWidth = $props.fullWidth || false
8
8
  export const leftIcon = $props.leftIcon || ''
9
9
  export const rightIcon = $props.rightIcon || ''
10
10
  export const className = $props.className || ''
11
- export const onClick = $props.onClick
12
11
 
13
- // Variant styles using headwind
14
12
  const variantClasses = {
15
13
  primary: 'bg-blue-500 hover:bg-blue-600 text-white shadow-md hover:shadow-lg dark:bg-blue-600 dark:hover:bg-blue-700',
16
14
  secondary: 'bg-neutral-200 hover:bg-neutral-300 text-neutral-900 dark:bg-neutral-700 dark:hover:bg-neutral-600 dark:text-white',
@@ -19,7 +17,6 @@ const variantClasses = {
19
17
  danger: 'bg-red-500 hover:bg-red-600 text-white shadow-md hover:shadow-lg dark:bg-red-600 dark:hover:bg-red-700',
20
18
  }
21
19
 
22
- // Size styles using headwind
23
20
  const sizeClasses = {
24
21
  xs: 'px-2.5 py-1 text-xs h-7 gap-1',
25
22
  sm: 'px-3 py-1.5 text-sm h-8 gap-1.5',
@@ -28,62 +25,50 @@ const sizeClasses = {
28
25
  xl: 'px-8 py-4 text-xl h-14 gap-3',
29
26
  }
30
27
 
31
- // Icon size styles
32
- const iconSizeClasses = {
33
- xs: 'h-3 w-3',
34
- sm: 'h-4 w-4',
35
- md: 'h-5 w-5',
36
- lg: 'h-6 w-6',
37
- xl: 'h-7 w-7',
38
- }
39
-
40
- // Base classes
28
+ const iconSizeClasses = { xs: 'h-3 w-3', sm: 'h-4 w-4', md: 'h-5 w-5', lg: 'h-6 w-6', xl: 'h-7 w-7' }
41
29
  const baseClasses = 'inline-flex items-center justify-center font-semibold rounded-md transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none'
42
-
43
- // Width classes
44
30
  const widthClasses = fullWidth ? 'w-full' : ''
31
+ export const isDisabled = disabled || loading
45
32
 
46
- // Loading state disables interactions
47
- const isDisabled = disabled || loading
48
-
49
- // Combine classes
50
33
  export const buttonClasses = `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${widthClasses} ${className}`.trim()
51
34
  export const iconClasses = iconSizeClasses[size]
52
35
  export const spinnerClasses = `animate-spin ${iconClasses}`.trim()
53
36
  </script>
54
37
 
38
+ <script client>
39
+ const emit = defineEmits()
40
+ const disabledNow = {{ isDisabled }}
41
+
42
+ function onClick(event) {
43
+ if (disabledNow) {
44
+ event.preventDefault()
45
+ return
46
+ }
47
+ emit('click', event)
48
+ }
49
+ </script>
50
+
55
51
  <button
56
52
  type="{{ type }}"
57
53
  class="{{ buttonClasses }}"
58
54
  @if(isDisabled)disabled@endif
59
- @if(onClick)@click="handleClick(event)"@endif
55
+ @click="onClick($event)"
60
56
  aria-busy="{{ loading ? 'true' : 'false' }}"
61
57
  >
62
58
  @if(loading)
63
- <!-- Loading spinner -->
64
59
  <svg class="{{ spinnerClasses }}" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
65
60
  <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
66
61
  <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>
67
62
  </svg>
68
63
  @else
69
64
  @if(leftIcon)
70
- <!-- Left icon -->
71
- <span class="{{ iconClasses }}" innerHTML="{!! leftIcon !!}"></span>
65
+ <span class="{{ iconClasses }}">{!! leftIcon !!}</span>
72
66
  @endif
73
67
  @endif
74
68
 
75
69
  <slot />
76
70
 
77
71
  @if(!loading && rightIcon)
78
- <!-- Right icon -->
79
- <span class="{{ iconClasses }}" innerHTML="{!! rightIcon !!}"></span>
72
+ <span class="{{ iconClasses }}">{!! rightIcon !!}</span>
80
73
  @endif
81
74
  </button>
82
-
83
- <script>
84
- function handleClick(event) {
85
- if (!isDisabled && onClick) {
86
- onClick(event)
87
- }
88
- }
89
- </script>
@@ -1,171 +1,137 @@
1
1
  <script server>
2
- // Calendar Component Props
3
- export const value = $props.value || new Date()
4
- export const onChange = $props.onChange
5
- export const minDate = $props.minDate
6
- export const maxDate = $props.maxDate
2
+ export const value = $props.value || null
3
+ export const minDate = $props.minDate || null
4
+ export const maxDate = $props.maxDate || null
7
5
  export const disabledDates = $props.disabledDates || []
8
6
  export const locale = $props.locale || 'en-US'
9
- export const firstDayOfWeek = $props.firstDayOfWeek || 0 // 0 = Sunday, 1 = Monday
7
+ export const firstDayOfWeek = $props.firstDayOfWeek || 0
10
8
  export const showToday = $props.showToday !== false
11
9
  export const className = $props.className || ''
12
10
  export const highlightToday = $props.highlightToday !== false
13
11
 
14
- // State
15
- let currentMonth = value ? new Date(value) : new Date()
16
- let selectedDate = value ? new Date(value) : null
17
- const today = new Date()
12
+ const baseClasses = 'bg-white dark:bg-gray-800 rounded-lg shadow-lg p-4 w-full max-w-sm'
13
+ export const calendarClasses = `${baseClasses} ${className}`.trim()
14
+ export const headerClasses = 'flex items-center justify-between mb-4'
15
+ export const monthYearClasses = 'text-lg font-semibold text-gray-900 dark:text-gray-100'
16
+ export const buttonClasses = 'p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md transition-colors'
17
+ export const weekdaysClasses = 'grid grid-cols-7 gap-1 mb-2'
18
+ export const weekdayClasses = 'text-center text-xs font-medium text-gray-600 dark:text-gray-400 py-2'
19
+ export const daysGridClasses = 'grid grid-cols-7 gap-1'
20
+
21
+ const valueIso = value ? new Date(value).toISOString() : null
22
+ export const initialValueIso = valueIso
23
+ const minIso = minDate ? new Date(minDate).toISOString() : null
24
+ const maxIso = maxDate ? new Date(maxDate).toISOString() : null
25
+ export const minIsoValue = minIso
26
+ export const maxIsoValue = maxIso
27
+ export const disabledIsoList = disabledDates.map(d => new Date(d).toISOString())
28
+ </script>
18
29
 
19
- // Helper functions
20
- function getDaysInMonth(date) {
21
- return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate()
22
- }
30
+ <script client>
31
+ const emit = defineEmits()
32
+ const locale = {{ locale }}
33
+ const firstDayOfWeek = {{ firstDayOfWeek }}
34
+ const highlightToday = {{ highlightToday }}
35
+ const minDate = {{ minIsoValue === null ? 'null' : minIsoValue }} ? new Date({{ minIsoValue === null ? 'null' : minIsoValue }}) : null
36
+ const maxDate = {{ maxIsoValue === null ? 'null' : maxIsoValue }} ? new Date({{ maxIsoValue === null ? 'null' : maxIsoValue }}) : null
37
+ const disabledDates = ({{ disabledIsoList }}).map(s => new Date(s))
38
+ const initialValue = {{ initialValueIso === null ? 'null' : initialValueIso }} ? new Date({{ initialValueIso === null ? 'null' : initialValueIso }}) : null
23
39
 
24
- function getFirstDayOfMonth(date) {
25
- return new Date(date.getFullYear(), date.getMonth(), 1).getDay()
26
- }
40
+ const today = new Date()
41
+ const currentMonth = state(initialValue ? new Date(initialValue) : new Date())
42
+ const selectedDate = state(initialValue)
27
43
 
28
- function isSameDay(date1, date2) {
29
- if (!date1 || !date2) return false
30
- return date1.getFullYear() === date2.getFullYear() &&
31
- date1.getMonth() === date2.getMonth() &&
32
- date1.getDate() === date2.getDate()
44
+ function isSameDay(d1, d2) {
45
+ if (!d1 || !d2) return false
46
+ return d1.getFullYear() === d2.getFullYear() && d1.getMonth() === d2.getMonth() && d1.getDate() === d2.getDate()
33
47
  }
34
48
 
35
- function isToday(date) {
36
- return isSameDay(date, today)
37
- }
49
+ function isToday(d) { return isSameDay(d, today) }
38
50
 
39
- function isDisabled(date) {
40
- if (minDate && date < minDate) return true
41
- if (maxDate && date > maxDate) return true
42
- return disabledDates.some(d => isSameDay(d, date))
51
+ function isDisabled(d) {
52
+ if (minDate && d < minDate) return true
53
+ if (maxDate && d > maxDate) return true
54
+ return disabledDates.some(x => isSameDay(x, d))
43
55
  }
44
56
 
45
- function getMonthName(date) {
46
- return date.toLocaleDateString(locale, { month: 'long' })
47
- }
57
+ function getDaysInMonth(d) { return new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate() }
58
+ function getFirstDayOfMonth(d) { return new Date(d.getFullYear(), d.getMonth(), 1).getDay() }
48
59
 
49
- function getYear(date) {
50
- return date.getFullYear()
51
- }
60
+ const monthYearLabel = derived(() => {
61
+ const m = currentMonth()
62
+ return `${m.toLocaleDateString(locale, { month: 'long' })} ${m.getFullYear()}`
63
+ })
52
64
 
53
- function getDayNames() {
65
+ const dayNames = (() => {
54
66
  const days = []
55
- const baseDate = new Date(2024, 0, firstDayOfWeek) // Jan 2024 started on Monday
67
+ const baseDate = new Date(2024, 0, firstDayOfWeek)
56
68
  for (let i = 0; i < 7; i++) {
57
69
  const day = new Date(baseDate)
58
70
  day.setDate(baseDate.getDate() + i)
59
71
  days.push(day.toLocaleDateString(locale, { weekday: 'short' }))
60
72
  }
61
73
  return days
62
- }
74
+ })()
63
75
 
64
- function getCalendarDays() {
65
- const daysInMonth = getDaysInMonth(currentMonth)
66
- const firstDay = (getFirstDayOfMonth(currentMonth) - firstDayOfWeek + 7) % 7
76
+ const calendarDays = derived(() => {
77
+ const month = currentMonth()
78
+ const daysInMonth = getDaysInMonth(month)
79
+ const firstDay = (getFirstDayOfMonth(month) - firstDayOfWeek + 7) % 7
67
80
  const days = []
68
-
69
- // Previous month days
70
- const prevMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1)
71
- const daysInPrevMonth = getDaysInMonth(prevMonth)
81
+ const prev = new Date(month.getFullYear(), month.getMonth() - 1, 1)
82
+ const daysInPrev = getDaysInMonth(prev)
72
83
  for (let i = firstDay - 1; i >= 0; i--) {
73
- days.push({
74
- date: new Date(prevMonth.getFullYear(), prevMonth.getMonth(), daysInPrevMonth - i),
75
- isCurrentMonth: false,
76
- })
84
+ days.push({ date: new Date(prev.getFullYear(), prev.getMonth(), daysInPrev - i), isCurrentMonth: false })
77
85
  }
78
-
79
- // Current month days
80
86
  for (let i = 1; i <= daysInMonth; i++) {
81
- days.push({
82
- date: new Date(currentMonth.getFullYear(), currentMonth.getMonth(), i),
83
- isCurrentMonth: true,
84
- })
87
+ days.push({ date: new Date(month.getFullYear(), month.getMonth(), i), isCurrentMonth: true })
85
88
  }
86
-
87
- // Next month days to fill the grid
88
- const remainingDays = 42 - days.length // 6 rows * 7 days
89
- const nextMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1)
90
- for (let i = 1; i <= remainingDays; i++) {
91
- days.push({
92
- date: new Date(nextMonth.getFullYear(), nextMonth.getMonth(), i),
93
- isCurrentMonth: false,
94
- })
89
+ const remaining = 42 - days.length
90
+ const next = new Date(month.getFullYear(), month.getMonth() + 1, 1)
91
+ for (let i = 1; i <= remaining; i++) {
92
+ days.push({ date: new Date(next.getFullYear(), next.getMonth(), i), isCurrentMonth: false })
95
93
  }
96
-
97
94
  return days
98
- }
95
+ })
99
96
 
100
97
  function previousMonth() {
101
- currentMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1)
98
+ const m = currentMonth()
99
+ currentMonth.set(new Date(m.getFullYear(), m.getMonth() - 1, 1))
102
100
  }
103
101
 
104
- function nextMonth() {
105
- currentMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1)
102
+ function nextMonthFn() {
103
+ const m = currentMonth()
104
+ currentMonth.set(new Date(m.getFullYear(), m.getMonth() + 1, 1))
106
105
  }
107
106
 
108
107
  function selectDate(date) {
109
108
  if (isDisabled(date)) return
110
- selectedDate = date
111
- onChange?.(date)
109
+ selectedDate.set(date)
110
+ emit('change', date)
112
111
  }
113
112
 
114
113
  function goToToday() {
115
- currentMonth = new Date(today)
114
+ currentMonth.set(new Date(today))
116
115
  selectDate(today)
117
116
  }
118
117
 
119
- // Base classes
120
- const baseClasses = 'bg-white dark:bg-gray-800 rounded-lg shadow-lg p-4 w-full max-w-sm'
121
- const calendarClasses = `${baseClasses} ${className}`.trim()
122
-
123
- const headerClasses = 'flex items-center justify-between mb-4'
124
- const monthYearClasses = 'text-lg font-semibold text-gray-900 dark:text-gray-100'
125
- const buttonClasses = 'p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md transition-colors'
126
-
127
- const weekdaysClasses = 'grid grid-cols-7 gap-1 mb-2'
128
- const weekdayClasses = 'text-center text-xs font-medium text-gray-600 dark:text-gray-400 py-2'
129
-
130
- const daysGridClasses = 'grid grid-cols-7 gap-1'
131
-
132
- function getDayClasses(day) {
118
+ function dayClasses(day) {
133
119
  const base = 'flex items-center justify-center h-10 w-10 rounded-md text-sm transition-colors'
134
- const isSelected = isSameDay(day.date, selectedDate)
120
+ const isSelected = isSameDay(day.date, selectedDate())
135
121
  const isTodayDate = isToday(day.date)
136
122
  const disabled = isDisabled(day.date)
137
-
138
- let classes = base
139
-
140
- if (!day.isCurrentMonth) {
141
- classes += ' text-gray-400 dark:text-gray-600'
142
- } else {
143
- classes += ' text-gray-900 dark:text-gray-100'
144
- }
145
-
146
- if (disabled) {
147
- classes += ' opacity-50 cursor-not-allowed'
148
- } else {
149
- classes += ' cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700'
150
- }
151
-
152
- if (isSelected) {
153
- classes += ' bg-blue-600 text-white hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600'
154
- }
155
-
156
- if (isTodayDate && highlightToday && !isSelected) {
157
- classes += ' border-2 border-blue-600 dark:border-blue-400'
158
- }
159
-
160
- return classes
123
+ let cls = base
124
+ cls += day.isCurrentMonth ? ' text-gray-900 dark:text-gray-100' : ' text-gray-400 dark:text-gray-600'
125
+ cls += disabled ? ' opacity-50 cursor-not-allowed' : ' cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700'
126
+ if (isSelected) cls += ' bg-blue-600 text-white hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600'
127
+ if (isTodayDate && highlightToday && !isSelected) cls += ' border-2 border-blue-600 dark:border-blue-400'
128
+ return cls
161
129
  }
162
130
 
163
- const dayNames = getDayNames()
164
- const calendarDays = getCalendarDays()
131
+ defineExpose({ selectedDate, currentMonth, selectDate })
165
132
  </script>
166
133
 
167
134
  <div class="{{ calendarClasses }}">
168
- <!-- Header -->
169
135
  <div class="{{ headerClasses }}">
170
136
  <button
171
137
  type="button"
@@ -178,14 +144,12 @@ const calendarDays = getCalendarDays()
178
144
  </svg>
179
145
  </button>
180
146
 
181
- <div class="{{ monthYearClasses }}">
182
- {{ getMonthName(currentMonth) }} {{ getYear(currentMonth) }}
183
- </div>
147
+ <div class="{{ monthYearClasses }}" x-text="monthYearLabel()"></div>
184
148
 
185
149
  <button
186
150
  type="button"
187
151
  class="{{ buttonClasses }}"
188
- @click="nextMonth()"
152
+ @click="nextMonthFn()"
189
153
  aria-label="Next month"
190
154
  >
191
155
  <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -194,28 +158,21 @@ const calendarDays = getCalendarDays()
194
158
  </button>
195
159
  </div>
196
160
 
197
- <!-- Weekday names -->
198
161
  <div class="{{ weekdaysClasses }}">
199
- @foreach(dayNames as day)
200
- <div class="{{ weekdayClasses }}">{{ day }}</div>
201
- @endforeach
162
+ <div :for="d in dayNames" :key="d" class="{{ weekdayClasses }}" x-text="d"></div>
202
163
  </div>
203
164
 
204
- <!-- Calendar grid -->
205
165
  <div class="{{ daysGridClasses }}">
206
- @foreach(calendarDays as day)
207
- <button
208
- type="button"
209
- class="{{ getDayClasses(day) }}"
210
- @click="selectDate(day.date)"
211
- aria-label="{{ day.date.toLocaleDateString(locale) }}"
212
- @if(isDisabled(day.date))
213
- disabled
214
- @endif
215
- >
216
- {{ day.date.getDate() }}
217
- </button>
218
- @endforeach
166
+ <button
167
+ :for="day in calendarDays()"
168
+ :key="day.date.toISOString()"
169
+ type="button"
170
+ :class="dayClasses(day)"
171
+ @click="selectDate(day.date)"
172
+ :aria-label="day.date.toLocaleDateString(locale)"
173
+ :disabled="isDisabled(day.date)"
174
+ x-text="day.date.getDate()"
175
+ ></button>
219
176
  </div>
220
177
 
221
178
  @if(showToday)
@@ -5,11 +5,8 @@ export const hover = $props.hover || false
5
5
  export const clickable = $props.clickable || false
6
6
  export const image = $props.image || ''
7
7
  export const imageAlt = $props.imageAlt || ''
8
- export const imagePosition = $props.imagePosition || 'top'
9
8
  export const className = $props.className || ''
10
- export const onClick = $props.onClick
11
9
 
12
- // Variant classes
13
10
  const variantClasses = {
14
11
  default: 'bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700',
15
12
  outlined: 'bg-transparent border-2 border-gray-300 dark:border-gray-600',
@@ -17,18 +14,8 @@ const variantClasses = {
17
14
  flat: 'bg-gray-50 dark:bg-gray-900 border-0',
18
15
  }
19
16
 
20
- // Padding classes
21
- const paddingClasses = {
22
- none: 'p-0',
23
- sm: 'p-3',
24
- default: 'p-6',
25
- lg: 'p-8',
26
- }
27
-
28
- // Hover effect classes
17
+ const paddingClasses = { none: 'p-0', sm: 'p-3', default: 'p-6', lg: 'p-8' }
29
18
  const hoverClasses = hover ? 'transition-all duration-200 hover:shadow-lg hover:-translate-y-0.5' : ''
30
-
31
- // Clickable classes
32
19
  const clickableClasses = clickable ? 'cursor-pointer' : ''
33
20
 
34
21
  export const cardClasses = `rounded-lg overflow-hidden ${variantClasses[variant]} ${paddingClasses[image ? 'none' : padding]} ${hoverClasses} ${clickableClasses} ${className}`.trim()
@@ -36,12 +23,35 @@ export const imageClasses = `w-full h-48 object-cover`.trim()
36
23
  export const contentClasses = `${image ? paddingClasses[padding] : ''}`.trim()
37
24
  </script>
38
25
 
39
- @if(onClick)
40
- <div class="{{ cardClasses }}" @click="handleClick(event)" role="button" tabindex="0" onkeydown="handleKeyDown(event)">
26
+ <script client>
27
+ const emit = defineEmits()
28
+ const isClickable = {{ clickable }}
29
+
30
+ function onClick(event) {
31
+ if (!isClickable) return
32
+ emit('click', event)
33
+ }
34
+
35
+ function onKey(event) {
36
+ if (!isClickable) return
37
+ if (event.key === 'Enter' || event.key === ' ') {
38
+ event.preventDefault()
39
+ emit('click', event)
40
+ }
41
+ }
42
+ </script>
43
+
44
+ @if(clickable)
45
+ <div
46
+ class="{{ cardClasses }}"
47
+ @click="onClick($event)"
48
+ @keydown="onKey($event)"
49
+ role="button"
50
+ tabindex="0"
51
+ >
41
52
  @if(image)
42
53
  <img src="{{ image }}" alt="{{ imageAlt }}" class="{{ imageClasses }}" />
43
54
  @endif
44
-
45
55
  <div class="{{ contentClasses }}">
46
56
  <slot />
47
57
  </div>
@@ -51,24 +61,8 @@ export const contentClasses = `${image ? paddingClasses[padding] : ''}`.trim()
51
61
  @if(image)
52
62
  <img src="{{ image }}" alt="{{ imageAlt }}" class="{{ imageClasses }}" />
53
63
  @endif
54
-
55
64
  <div class="{{ contentClasses }}">
56
65
  <slot />
57
66
  </div>
58
67
  </div>
59
68
  @endif
60
-
61
- <script>
62
- function handleClick(event) {
63
- if (clickable && onClick) {
64
- onClick(event)
65
- }
66
- }
67
-
68
- function handleKeyDown(event) {
69
- if ((event.key === 'Enter' || event.key === ' ') && clickable && onClick) {
70
- event.preventDefault()
71
- onClick(event)
72
- }
73
- }
74
- </script>
@@ -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 indeterminate = $props.indeterminate || false
6
5
  export const size = $props.size || 'md'
@@ -12,42 +11,55 @@ export const value = $props.value || ''
12
11
  export const name = $props.name || ''
13
12
  export const className = $props.className || ''
14
13
 
15
- // Size classes
16
- const sizeClasses = {
17
- sm: 'h-4 w-4',
18
- md: 'h-5 w-5',
19
- lg: 'h-6 w-6',
20
- }
21
-
22
- // Base classes for checkbox
14
+ const sizeClasses = { sm: 'h-4 w-4', md: 'h-5 w-5', lg: 'h-6 w-6' }
23
15
  const baseClasses = 'rounded 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'
24
-
25
- const stateClasses = error
26
- ? 'border-red-300 dark:border-red-600 text-red-600 dark:text-red-500 focus:ring-red-500 dark:focus:ring-red-400'
27
- : ''
28
-
29
- const disabledClasses = disabled
30
- ? 'opacity-50 cursor-not-allowed'
31
- : 'cursor-pointer'
16
+ 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' : ''
17
+ const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'
32
18
 
33
19
  export const checkboxClasses = `${baseClasses} ${sizeClasses[size]} ${stateClasses} ${disabledClasses}`.trim()
34
20
  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()
35
21
  export const descriptionClasses = `text-sm ${error ? 'text-red-500 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'}`.trim()
22
+ export const inputId = `checkbox-${value || label}`
23
+ </script>
24
+
25
+ <script client>
26
+ const emit = defineEmits()
27
+ const isDisabled = {{ disabled }}
28
+ const isIndeterminate = {{ indeterminate }}
29
+ const isChecked = state({{ checked }})
30
+
31
+ function onInputChange(event) {
32
+ if (isDisabled) {
33
+ event.target.checked = isChecked()
34
+ return
35
+ }
36
+ isChecked.set(event.target.checked)
37
+ emit('change', isChecked())
38
+ }
39
+
40
+ onMount(() => {
41
+ if (isIndeterminate) {
42
+ const el = document.getElementById({{ inputId }})
43
+ if (el) el.indeterminate = true
44
+ }
45
+ })
46
+
47
+ defineExpose({ isChecked })
36
48
  </script>
37
49
 
38
50
  <div class="relative flex items-start {{ className }}">
39
51
  <div class="flex h-6 items-center">
40
52
  <input
41
- id="checkbox-{{ value || label }}"
53
+ id="{{ inputId }}"
42
54
  type="checkbox"
43
55
  name="{{ name }}"
44
56
  value="{{ value }}"
45
- checked="{{ checked }}"
46
- disabled="{{ disabled }}"
47
- required="{{ required }}"
57
+ :checked="isChecked()"
58
+ @if(disabled)disabled@endif
59
+ @if(required)required@endif
48
60
  class="{{ checkboxClasses }}"
49
- onchange="handleChange(event)"
50
- aria-invalid="{{ error ? 'true' : 'false' }}"
61
+ @change="onInputChange($event)"
62
+ :aria-invalid="{{ error ? 'true' : 'false' }}"
51
63
  @if(description)aria-describedby="checkbox-description"@endif
52
64
  />
53
65
  </div>
@@ -55,7 +67,7 @@ export const descriptionClasses = `text-sm ${error ? 'text-red-500 dark:text-red
55
67
  @if(label || description)
56
68
  <div class="ml-3 text-sm leading-6">
57
69
  @if(label)
58
- <label for="checkbox-{{ value || label }}" class="{{ labelClasses }}">
70
+ <label for="{{ inputId }}" class="{{ labelClasses }}">
59
71
  {{ label }}
60
72
  @if(required)
61
73
  <span class="text-red-500 ml-1">*</span>
@@ -70,23 +82,3 @@ export const descriptionClasses = `text-sm ${error ? 'text-red-500 dark:text-red
70
82
  </div>
71
83
  @endif
72
84
  </div>
73
-
74
- <script>
75
- function handleChange(event) {
76
- const isChecked = event.target.checked
77
-
78
- if (!disabled && onChange) {
79
- onChange(isChecked)
80
- }
81
- }
82
-
83
- // Handle indeterminate state
84
- if (indeterminate && typeof window !== 'undefined') {
85
- window.addEventListener('DOMContentLoaded', () => {
86
- const checkbox = document.getElementById('checkbox-{{ value || label }}')
87
- if (checkbox) {
88
- checkbox.indeterminate = true
89
- }
90
- })
91
- }
92
- </script>