@stacksjs/components 0.2.35 → 0.2.54

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 +5 -5
  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
@@ -5,99 +5,84 @@ export const siblingCount = $props.siblingCount || 1
5
5
  export const showFirstLast = $props.showFirstLast ?? true
6
6
  export const showPrevNext = $props.showPrevNext ?? true
7
7
  export const className = $props.className || ''
8
- export const onChange = $props.onChange
9
8
 
10
- // Generate page numbers to display
11
- function generatePageNumbers() {
12
- const pages = []
13
- const totalNumbers = siblingCount * 2 + 3 // siblings on each side + current + first + last
14
- const totalBlocks = totalNumbers + 2 // + 2 for ellipsis
15
-
16
- if (totalPages <= totalBlocks) {
17
- // Show all pages
18
- for (let i = 1; i <= totalPages; i++) {
19
- pages.push(i)
20
- }
21
- } else {
22
- const leftSiblingIndex = Math.max(currentPage - siblingCount, 1)
23
- const rightSiblingIndex = Math.min(currentPage + siblingCount, totalPages)
9
+ export const paginationClasses = `flex items-center gap-1 ${className}`.trim()
10
+ export const buttonClasses = `px-3 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-white dark:disabled:hover:bg-gray-800 transition-colors duration-150`.trim()
11
+ export const activeButtonClasses = `px-3 py-2 text-sm font-medium text-white bg-blue-600 dark:bg-blue-500 border border-blue-600 dark:border-blue-500 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900`.trim()
12
+ export const ellipsisClasses = `px-3 py-2 text-sm font-medium text-gray-400 dark:text-gray-600`.trim()
13
+ export const iconClasses = `w-5 h-5`.trim()
14
+ </script>
24
15
 
25
- const showLeftEllipsis = leftSiblingIndex > 2
26
- const showRightEllipsis = rightSiblingIndex < totalPages - 1
16
+ <script client>
17
+ const emit = defineEmits()
18
+ const totalPages = {{ totalPages }}
19
+ const siblingCount = {{ siblingCount }}
20
+ const page = state({{ currentPage }})
27
21
 
28
- if (!showLeftEllipsis && showRightEllipsis) {
29
- // Show left pages + ellipsis + last
30
- const leftCount = 3 + 2 * siblingCount
31
- for (let i = 1; i <= leftCount; i++) {
32
- pages.push(i)
33
- }
34
- pages.push('ellipsis-right')
35
- pages.push(totalPages)
36
- } else if (showLeftEllipsis && !showRightEllipsis) {
37
- // Show first + ellipsis + right pages
38
- pages.push(1)
39
- pages.push('ellipsis-left')
40
- const rightCount = 3 + 2 * siblingCount
41
- for (let i = totalPages - rightCount + 1; i <= totalPages; i++) {
42
- pages.push(i)
43
- }
44
- } else {
45
- // Show first + ellipsis + middle + ellipsis + last
46
- pages.push(1)
47
- pages.push('ellipsis-left')
48
- for (let i = leftSiblingIndex; i <= rightSiblingIndex; i++) {
49
- pages.push(i)
50
- }
51
- pages.push('ellipsis-right')
52
- pages.push(totalPages)
53
- }
22
+ function goToPage(target) {
23
+ if (target >= 1 && target <= totalPages && target !== page()) {
24
+ page.set(target)
25
+ emit('change', target)
54
26
  }
55
-
56
- return pages
57
27
  }
58
28
 
59
- const pageNumbers = generatePageNumbers()
29
+ function goToPrevious() { goToPage(page() - 1) }
30
+ function goToNext() { goToPage(page() + 1) }
31
+ function goToFirst() { goToPage(1) }
32
+ function goToLast() { goToPage(totalPages) }
60
33
 
61
- function goToPage(page) {
62
- if (page >= 1 && page <= totalPages && page !== currentPage) {
63
- if (onChange) {
64
- onChange(page)
65
- }
66
- }
67
- }
34
+ const pageNumbers = derived(() => {
35
+ const cur = page()
36
+ const pages = []
37
+ const totalNumbers = siblingCount * 2 + 3
38
+ const totalBlocks = totalNumbers + 2
68
39
 
69
- function goToPrevious() {
70
- goToPage(currentPage - 1)
71
- }
40
+ if (totalPages <= totalBlocks) {
41
+ for (let i = 1; i <= totalPages; i++) pages.push(i)
42
+ return pages
43
+ }
72
44
 
73
- function goToNext() {
74
- goToPage(currentPage + 1)
75
- }
45
+ const leftSibling = Math.max(cur - siblingCount, 1)
46
+ const rightSibling = Math.min(cur + siblingCount, totalPages)
47
+ const showLeftEllipsis = leftSibling > 2
48
+ const showRightEllipsis = rightSibling < totalPages - 1
76
49
 
77
- function goToFirst() {
78
- goToPage(1)
79
- }
50
+ if (!showLeftEllipsis && showRightEllipsis) {
51
+ const leftCount = 3 + 2 * siblingCount
52
+ for (let i = 1; i <= leftCount; i++) pages.push(i)
53
+ pages.push('ellipsis-right')
54
+ pages.push(totalPages)
55
+ }
56
+ else if (showLeftEllipsis && !showRightEllipsis) {
57
+ pages.push(1)
58
+ pages.push('ellipsis-left')
59
+ const rightCount = 3 + 2 * siblingCount
60
+ for (let i = totalPages - rightCount + 1; i <= totalPages; i++) pages.push(i)
61
+ }
62
+ else {
63
+ pages.push(1)
64
+ pages.push('ellipsis-left')
65
+ for (let i = leftSibling; i <= rightSibling; i++) pages.push(i)
66
+ pages.push('ellipsis-right')
67
+ pages.push(totalPages)
68
+ }
69
+ return pages
70
+ })
80
71
 
81
- function goToLast() {
82
- goToPage(totalPages)
83
- }
72
+ const isFirstPage = derived(() => page() === 1)
73
+ const isLastPage = derived(() => page() === totalPages)
84
74
 
85
- export const paginationClasses = `flex items-center gap-1 ${className}`.trim()
86
- export const buttonClasses = `px-3 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-white dark:disabled:hover:bg-gray-800 transition-colors duration-150`.trim()
87
- export const activeButtonClasses = `px-3 py-2 text-sm font-medium text-white bg-blue-600 dark:bg-blue-500 border border-blue-600 dark:border-blue-500 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900`.trim()
88
- export const ellipsisClasses = `px-3 py-2 text-sm font-medium text-gray-400 dark:text-gray-600`.trim()
89
- export const iconClasses = `w-5 h-5`.trim()
75
+ defineExpose({ page, goToPage })
90
76
  </script>
91
77
 
92
78
  <nav class="{{ paginationClasses }}" role="navigation" aria-label="Pagination">
93
- <!-- First page button -->
94
79
  @if(showFirstLast)
95
80
  <button
96
81
  type="button"
97
82
  @click="goToFirst()"
98
83
  class="{{ buttonClasses }}"
99
84
  aria-label="Go to first page"
100
- disabled="{{ currentPage === 1 }}"
85
+ :disabled="isFirstPage()"
101
86
  >
102
87
  <svg class="{{ iconClasses }}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
103
88
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 19l-7-7 7-7m8 14l-7-7 7-7" />
@@ -105,14 +90,13 @@ export const iconClasses = `w-5 h-5`.trim()
105
90
  </button>
106
91
  @endif
107
92
 
108
- <!-- Previous button -->
109
93
  @if(showPrevNext)
110
94
  <button
111
95
  type="button"
112
96
  @click="goToPrevious()"
113
97
  class="{{ buttonClasses }}"
114
98
  aria-label="Go to previous page"
115
- disabled="{{ currentPage === 1 }}"
99
+ :disabled="isFirstPage()"
116
100
  >
117
101
  <svg class="{{ iconClasses }}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
118
102
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
@@ -120,45 +104,27 @@ export const iconClasses = `w-5 h-5`.trim()
120
104
  </button>
121
105
  @endif
122
106
 
123
- <!-- Page numbers -->
124
- @foreach(page in pageNumbers)
125
- @if(typeof page === 'string')
126
- <!-- Ellipsis -->
127
- <span class="{{ ellipsisClasses }}" aria-hidden="true">...</span>
128
- @else
129
- @if(page === currentPage)
130
- <!-- Current page -->
131
- <button
132
- type="button"
133
- class="{{ activeButtonClasses }}"
134
- aria-label="Page {{ page }}"
135
- aria-current="page"
136
- disabled
137
- >
138
- {{ page }}
139
- </button>
140
- @else
141
- <!-- Other pages -->
142
- <button
143
- type="button"
144
- @click="goToPage({{ page }})"
145
- class="{{ buttonClasses }}"
146
- aria-label="Go to page {{ page }}"
147
- >
148
- {{ page }}
149
- </button>
150
- @endif
151
- @endif
152
- @endforeach
107
+ <span :for="p in pageNumbers()" :key="p" class="contents">
108
+ <span :if="typeof p === 'string'" class="{{ ellipsisClasses }}" aria-hidden="true">...</span>
109
+ <button
110
+ :if="typeof p !== 'string'"
111
+ type="button"
112
+ :class="p === page() ? '{{ activeButtonClasses }}' : '{{ buttonClasses }}'"
113
+ :aria-label="`Go to page ${p}`"
114
+ :aria-current="p === page() ? 'page' : null"
115
+ :disabled="p === page()"
116
+ @click="goToPage(p)"
117
+ x-text="p"
118
+ ></button>
119
+ </span>
153
120
 
154
- <!-- Next button -->
155
121
  @if(showPrevNext)
156
122
  <button
157
123
  type="button"
158
124
  @click="goToNext()"
159
125
  class="{{ buttonClasses }}"
160
126
  aria-label="Go to next page"
161
- disabled="{{ currentPage === totalPages }}"
127
+ :disabled="isLastPage()"
162
128
  >
163
129
  <svg class="{{ iconClasses }}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
164
130
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
@@ -166,14 +132,13 @@ export const iconClasses = `w-5 h-5`.trim()
166
132
  </button>
167
133
  @endif
168
134
 
169
- <!-- Last page button -->
170
135
  @if(showFirstLast)
171
136
  <button
172
137
  type="button"
173
138
  @click="goToLast()"
174
139
  class="{{ buttonClasses }}"
175
140
  aria-label="Go to last page"
176
- disabled="{{ currentPage === totalPages }}"
141
+ :disabled="isLastPage()"
177
142
  >
178
143
  <svg class="{{ iconClasses }}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
179
144
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 5l7 7-7 7M5 5l7 7-7 7" />
@@ -0,0 +1,142 @@
1
+ <script server>
2
+ export const value = $props.value || ''
3
+ export const placeholder = $props.placeholder || 'Enter your password'
4
+ export const disabled = $props.disabled || false
5
+ export const readonly = $props.readonly || false
6
+ export const size = $props.size || 'md'
7
+ export const error = $props.error || false
8
+ export const label = $props.label || ''
9
+ export const helperText = $props.helperText || ''
10
+ export const required = $props.required || false
11
+ export const showStrength = $props.showStrength || false
12
+ export const className = $props.className || ''
13
+ export const inputClassName = $props.inputClassName || ''
14
+
15
+ 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' }
16
+ const iconSizeClasses = { sm: 'h-4 w-4', md: 'h-5 w-5', lg: 'h-6 w-6' }
17
+
18
+ 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 pr-10'
19
+ const stateClasses = error
20
+ ? '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'
21
+ : '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'
22
+ const disabledClasses = disabled
23
+ ? 'opacity-50 cursor-not-allowed bg-gray-50 dark:bg-gray-800'
24
+ : readonly
25
+ ? 'bg-gray-50 dark:bg-gray-800 cursor-default'
26
+ : 'bg-white dark:bg-blue-gray-700'
27
+
28
+ export const inputClasses = `${baseClasses} ${sizeClasses[size]} ${stateClasses} ${disabledClasses} ${inputClassName}`.trim()
29
+ export const labelClasses = `block text-sm font-medium leading-6 mb-2 ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-900 dark:text-gray-100'}`.trim()
30
+ export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'}`.trim()
31
+ export const iconClasses = iconSizeClasses[size]
32
+ </script>
33
+
34
+ <script client>
35
+ const emit = defineEmits()
36
+ const showPassword = state(false)
37
+ const inputValue = state({{ value }})
38
+
39
+ function onInput(event) {
40
+ inputValue.set(event.target.value)
41
+ emit('input', inputValue())
42
+ }
43
+
44
+ function toggleVisibility() {
45
+ showPassword.set(!showPassword())
46
+ }
47
+
48
+ function calcStrength(pw) {
49
+ if (!pw) return 0
50
+ let s = 0
51
+ if (pw.length >= 8) s += 25
52
+ if (pw.length >= 12) s += 25
53
+ if (/[a-z]/.test(pw) && /[A-Z]/.test(pw)) s += 25
54
+ if (/\d/.test(pw)) s += 15
55
+ if (/[^a-zA-Z\d]/.test(pw)) s += 10
56
+ return Math.min(s, 100)
57
+ }
58
+
59
+ const strength = derived(() => calcStrength(inputValue()))
60
+ const strengthText = derived(() => {
61
+ const s = strength()
62
+ return s < 40 ? 'Weak' : s < 70 ? 'Medium' : 'Strong'
63
+ })
64
+ const strengthColor = derived(() => {
65
+ const s = strength()
66
+ return s < 40 ? 'bg-red-500' : s < 70 ? 'bg-yellow-500' : 'bg-green-500'
67
+ })
68
+ const strengthLabelColor = derived(() => {
69
+ const s = strength()
70
+ return s < 40 ? 'text-red-600' : s < 70 ? 'text-yellow-600' : 'text-green-600'
71
+ })
72
+ const inputType = derived(() => showPassword() ? 'text' : 'password')
73
+ const toggleAriaLabel = derived(() => showPassword() ? 'Hide password' : 'Show password')
74
+
75
+ defineExpose({ value: inputValue })
76
+ </script>
77
+
78
+ <div class="w-full {{ className }}">
79
+ @if(label)
80
+ <label for="password-input" class="{{ labelClasses }}">
81
+ {{ label }}
82
+ @if(required)
83
+ <span class="text-red-500 ml-1">*</span>
84
+ @endif
85
+ </label>
86
+ @endif
87
+
88
+ <div class="relative">
89
+ <input
90
+ :type="inputType()"
91
+ id="password-input"
92
+ class="{{ inputClasses }}"
93
+ :value="inputValue()"
94
+ placeholder="{{ placeholder }}"
95
+ @if(disabled)disabled@endif
96
+ @if(readonly)readonly@endif
97
+ @if(required)required@endif
98
+ @input="onInput($event)"
99
+ :aria-invalid="{{ error ? 'true' : 'false' }}"
100
+ @if(helperText)aria-describedby="helper-text"@endif
101
+ />
102
+
103
+ <div class="absolute inset-y-0 right-0 flex items-center pr-3">
104
+ <button
105
+ type="button"
106
+ @click="toggleVisibility()"
107
+ class="text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 focus:outline-none"
108
+ :aria-label="toggleAriaLabel()"
109
+ >
110
+ <svg :show="showPassword()" class="{{ iconClasses }}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
111
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" />
112
+ </svg>
113
+ <svg :show="!showPassword()" class="{{ iconClasses }}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
114
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
115
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
116
+ </svg>
117
+ </button>
118
+ </div>
119
+ </div>
120
+
121
+ @if(showStrength)
122
+ <div :show="inputValue().length > 0" class="mt-2">
123
+ <div class="flex items-center justify-between mb-1">
124
+ <span class="text-xs text-gray-600 dark:text-gray-400">Password strength</span>
125
+ <span class="text-xs font-medium" :class="strengthLabelColor()" x-text="strengthText()"></span>
126
+ </div>
127
+ <div class="w-full h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
128
+ <div
129
+ class="h-full transition-all duration-300 rounded-full"
130
+ :class="strengthColor()"
131
+ :style="`width: ${strength()}%`"
132
+ ></div>
133
+ </div>
134
+ </div>
135
+ @endif
136
+
137
+ @if(helperText)
138
+ <p id="helper-text" class="{{ helperClasses }}">
139
+ {{ helperText }}
140
+ </p>
141
+ @endif
142
+ </div>
@@ -1,12 +1,11 @@
1
1
  <script server>
2
2
  export const paymentMethods = $props.paymentMethods || []
3
- export const onDeletePaymentMethod = $props.onDeletePaymentMethod
4
- export const onMakeDefault = $props.onMakeDefault
5
3
  export const isLoading = $props.isLoading || false
4
+ export const showMakeDefault = $props.showMakeDefault ?? true
5
+ export const showDelete = $props.showDelete ?? true
6
6
  export const className = $props.className || ''
7
7
 
8
- // Card brand icon mapping
9
- const brandIcons = {
8
+ export const brandIcons = {
10
9
  visa: '💳',
11
10
  mastercard: '💳',
12
11
  amex: '💳',
@@ -17,9 +16,22 @@ const brandIcons = {
17
16
  export const containerClasses = `mt-4 ${className}`.trim()
18
17
  </script>
19
18
 
19
+ <script client>
20
+ const emit = defineEmits()
21
+
22
+ function onMakeDefault(methodId) {
23
+ emit('makeDefault', methodId)
24
+ }
25
+
26
+ function onDelete(methodId) {
27
+ if (confirm('Are you sure you want to delete this payment method?')) {
28
+ emit('deletePaymentMethod', methodId)
29
+ }
30
+ }
31
+ </script>
32
+
20
33
  <div class="{{ containerClasses }}">
21
34
  @if(isLoading)
22
- <!-- Loading state -->
23
35
  <div class="animate-pulse">
24
36
  <div class="h-24 bg-gray-200 dark:bg-gray-700 rounded-lg"></div>
25
37
  </div>
@@ -31,12 +43,10 @@ export const containerClasses = `mt-4 ${className}`.trim()
31
43
  <div class="w-full p-3">
32
44
  <div class="flex items-center justify-between">
33
45
  <div class="flex items-center space-x-4">
34
- <!-- Card Brand Icon -->
35
46
  <div class="flex-shrink-0 h-10 w-10 flex items-center justify-center bg-gray-100 dark:bg-gray-700 rounded-md">
36
47
  <span class="text-2xl">{{ brandIcons[method.brand.toLowerCase()] || brandIcons.default }}</span>
37
48
  </div>
38
49
 
39
- <!-- Card Details -->
40
50
  <div>
41
51
  <h2 class="text-sm text-gray-600 dark:text-gray-300">
42
52
  {{ method.brand }} •••• {{ method.last_four }}
@@ -48,12 +58,11 @@ export const containerClasses = `mt-4 ${className}`.trim()
48
58
  </div>
49
59
 
50
60
  <div class="flex justify-end space-x-4">
51
- <!-- Make Default Button -->
52
- @if(onMakeDefault)
61
+ @if(showMakeDefault)
53
62
  <button
54
63
  type="button"
55
64
  class="border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-blue-gray-700 px-2 py-1 text-sm text-gray-700 dark:text-gray-300 font-semibold shadow-sm hover:bg-gray-50 dark:hover:bg-blue-gray-600 focus-visible:outline-2 focus-visible:outline-blue-600 focus-visible:outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed"
56
- @click="handleMakeDefault('{{ method.id }}')"
65
+ @click="onMakeDefault('{{ method.id }}')"
57
66
  title="Make default"
58
67
  >
59
68
  <svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@@ -62,12 +71,11 @@ export const containerClasses = `mt-4 ${className}`.trim()
62
71
  </button>
63
72
  @endif
64
73
 
65
- <!-- Delete Button -->
66
- @if(onDeletePaymentMethod)
74
+ @if(showDelete)
67
75
  <button
68
76
  type="button"
69
77
  class="border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-blue-gray-700 px-2 py-1 text-sm text-red-600 dark:text-red-400 font-semibold shadow-sm hover:bg-red-50 dark:hover:bg-red-900/20 focus-visible:outline-2 focus-visible:outline-red-600 focus-visible:outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed"
70
- @click="handleDelete('{{ method.id }}')"
78
+ @click="onDelete('{{ method.id }}')"
71
79
  title="Delete payment method"
72
80
  >
73
81
  <svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@@ -82,7 +90,6 @@ export const containerClasses = `mt-4 ${className}`.trim()
82
90
  @endforeach
83
91
  </ul>
84
92
  @else
85
- <!-- Empty state -->
86
93
  <div class="text-center py-12 bg-white dark:bg-blue-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
87
94
  <svg class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
88
95
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" />
@@ -95,19 +102,3 @@ export const containerClasses = `mt-4 ${className}`.trim()
95
102
  @endif
96
103
  @endif
97
104
  </div>
98
-
99
- <script>
100
- function handleMakeDefault(methodId) {
101
- if (onMakeDefault) {
102
- onMakeDefault(methodId)
103
- }
104
- }
105
-
106
- function handleDelete(methodId) {
107
- if (onDeletePaymentMethod) {
108
- if (confirm('Are you sure you want to delete this payment method?')) {
109
- onDeletePaymentMethod(methodId)
110
- }
111
- }
112
- }
113
- </script>
@@ -0,0 +1,60 @@
1
+ <script server>
2
+ export const className = $props.className || ''
3
+ export const as = $props.as || 'div'
4
+
5
+ const baseClasses = 'relative'
6
+ export const popoverClasses = `${baseClasses} ${className}`.trim()
7
+ export const popoverInstanceId = `stx-pop-${Math.random().toString(36).slice(2, 11)}`
8
+ </script>
9
+
10
+ <script client>
11
+ const emit = defineEmits()
12
+ const popoverInstanceId = {{ popoverInstanceId }}
13
+ const isOpen = state(false)
14
+ let rootEl = null
15
+ let outsideHandler = null
16
+
17
+ function open() { if (!isOpen()) { isOpen.set(true); emit('change', true) } }
18
+ function close() { if (isOpen()) { isOpen.set(false); emit('change', false) } }
19
+ function toggle() { isOpen() ? close() : open() }
20
+
21
+ function onToggle(event) { event.stopPropagation(); toggle() }
22
+ function onClose(event) { event.stopPropagation(); close() }
23
+ function onOpen(event) { event.stopPropagation(); open() }
24
+
25
+ onMount(() => {
26
+ rootEl = document.querySelector(`[data-popover-id="${popoverInstanceId}"]`)
27
+ outsideHandler = (event) => {
28
+ if (!isOpen()) return
29
+ if (rootEl && !rootEl.contains(event.target)) close()
30
+ }
31
+ document.addEventListener('click', outsideHandler, true)
32
+ })
33
+
34
+ onDestroy(() => {
35
+ if (outsideHandler) document.removeEventListener('click', outsideHandler, true)
36
+ })
37
+
38
+ const dataState = derived(() => isOpen() ? 'open' : 'closed')
39
+
40
+ defineExpose({ isOpen, toggle, open, close })
41
+ </script>
42
+
43
+ <component
44
+ :is="{{ as }}"
45
+ class="{{ popoverClasses }}"
46
+ data-stx-popover
47
+ data-popover-id="{{ popoverInstanceId }}"
48
+ :data-state="dataState()"
49
+ @stx:popover-toggle="onToggle($event)"
50
+ @stx:popover-open="onOpen($event)"
51
+ @stx:popover-close="onClose($event)"
52
+ >
53
+ <slot />
54
+ </component>
55
+
56
+ <style>
57
+ [data-stx-popover][data-state="closed"] [data-stx-popover-panel] {
58
+ display: none;
59
+ }
60
+ </style>
@@ -2,22 +2,28 @@
2
2
  export const className = $props.className || ''
3
3
  export const as = $props.as || 'button'
4
4
  export const disabled = $props.disabled || false
5
- export const onClick = $props.onClick
6
5
 
7
6
  const baseClasses = 'inline-flex items-center gap-2 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75'
8
- const buttonClasses = `${baseClasses} ${className}`.trim()
7
+ export const buttonClasses = `${baseClasses} ${className}`.trim()
8
+ </script>
9
+
10
+ <script client>
11
+ const emit = defineEmits()
12
+ const isDisabled = {{ disabled }}
9
13
 
10
- export function handleClick(e) {
11
- if (disabled) return
12
- onClick?.(e)
14
+ function onClick(event) {
15
+ if (isDisabled) return
16
+ emit('click', event)
17
+ event.currentTarget.dispatchEvent(new CustomEvent('stx:popover-toggle', { bubbles: true }))
13
18
  }
14
19
  </script>
15
20
 
16
21
  <component
17
22
  :is="{{ as }}"
18
23
  class="{{ buttonClasses }}"
24
+ data-stx-popover-button
19
25
  @if(disabled)disabled aria-disabled="true"@endif
20
- @click="handleClick"
26
+ @click="onClick($event)"
21
27
  >
22
28
  <slot />
23
29
  </component>
@@ -1,23 +1,23 @@
1
1
  <script server>
2
2
  export const className = $props.className || ''
3
3
  export const as = $props.as || 'div'
4
- export const static_ = $props.static || false
5
4
  export const position = $props.position || 'bottom'
6
5
 
7
6
  const positionClasses = {
8
- 'top': 'bottom-full mb-2',
9
- 'bottom': 'top-full mt-2',
10
- 'left': 'right-full mr-2',
11
- 'right': 'left-full ml-2',
7
+ top: 'bottom-full mb-2',
8
+ bottom: 'top-full mt-2',
9
+ left: 'right-full mr-2',
10
+ right: 'left-full ml-2',
12
11
  }
13
12
 
14
13
  const baseClasses = 'absolute z-10 rounded-lg bg-white dark:bg-neutral-800 p-4 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none'
15
- const panelClasses = `${baseClasses} ${positionClasses[position]} ${className}`.trim()
14
+ export const panelClasses = `${baseClasses} ${positionClasses[position]} ${className}`.trim()
16
15
  </script>
17
16
 
18
17
  <component
19
18
  :is="{{ as }}"
20
19
  class="{{ panelClasses }}"
20
+ data-stx-popover-panel
21
21
  >
22
22
  <slot />
23
23
  </component>