@stacksjs/components 0.2.3

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 (342) hide show
  1. package/README.md +477 -0
  2. package/dist/Accordion-x2gjedap.stx +108 -0
  3. package/dist/Audio-0fk6p9tn.stx +50 -0
  4. package/dist/Avatar-5m0v0s4x.stx +88 -0
  5. package/dist/Badge-ynes82vv.stx +70 -0
  6. package/dist/Breadcrumb-h9n427sk.stx +79 -0
  7. package/dist/Button-7xtrzn14.stx +89 -0
  8. package/dist/Calendar-egvx1pma.stx +232 -0
  9. package/dist/Card-r9w7vy1a.stx +74 -0
  10. package/dist/Checkbox-m8v46pmx.stx +92 -0
  11. package/dist/Checkout-vcnnr176.stx +39 -0
  12. package/dist/CodeBlock-mwex82cm.stx +57 -0
  13. package/dist/Combobox-npb08vkp.stx +13 -0
  14. package/dist/ComboboxButton-vcdyq4zw.stx +13 -0
  15. package/dist/ComboboxInput-c9d2p02m.stx +25 -0
  16. package/dist/ComboboxOption-7d0h9ash.stx +52 -0
  17. package/dist/ComboboxOptions-4dtqc0qa.stx +12 -0
  18. package/dist/CommandPalette-ah00bf8v.stx +39 -0
  19. package/dist/CommandPaletteItem-2yx0gvtj.stx +32 -0
  20. package/dist/DefaultPaymentMethod-emw5fg8s.stx +62 -0
  21. package/dist/Dialog-1w4rz7x0.stx +19 -0
  22. package/dist/DialogBackdrop-qbm3y5ab.stx +9 -0
  23. package/dist/DialogDescription-01w5j74b.stx +11 -0
  24. package/dist/DialogPanel-brsszrrs.stx +16 -0
  25. package/dist/DialogTitle-3rxhwg21.stx +11 -0
  26. package/dist/Drawer-yfaz8q7f.stx +137 -0
  27. package/dist/Dropdown-ng7nx556.stx +24 -0
  28. package/dist/DropdownButton-vc4zpyrb.stx +23 -0
  29. package/dist/DropdownItem-z491y23c.stx +43 -0
  30. package/dist/DropdownItems-7ykf29sj.stx +18 -0
  31. package/dist/EmailInput-bfshpj92.stx +41 -0
  32. package/dist/Footer-xdaf02nr.stx +66 -0
  33. package/dist/Form-z4f7w6r1.stx +209 -0
  34. package/dist/Hero-9vvp7pxf.stx +25 -0
  35. package/dist/Image-ktk0451c.stx +197 -0
  36. package/dist/Installation-33bj4rm7.stx +39 -0
  37. package/dist/Listbox-xcejcmyc.stx +14 -0
  38. package/dist/ListboxButton-nx5kmw19.stx +17 -0
  39. package/dist/ListboxLabel-aky8a73n.stx +11 -0
  40. package/dist/ListboxOption-6h4hsdz5.stx +57 -0
  41. package/dist/ListboxOptions-2sacdtm5.stx +16 -0
  42. package/dist/Login-s2jkvd9y.stx +163 -0
  43. package/dist/Navigator-tqqdgdkw.stx +138 -0
  44. package/dist/Notification-sry8egea.stx +71 -0
  45. package/dist/NumberInput-zm93ddfw.stx +138 -0
  46. package/dist/Pagination-yza9v4qw.stx +183 -0
  47. package/dist/PasswordInput-4xnndp5y.stx +155 -0
  48. package/dist/PaymentMethods-jxba6kek.stx +113 -0
  49. package/dist/Popover-scbqatjq.stx +24 -0
  50. package/dist/PopoverButton-rbxhzj4k.stx +23 -0
  51. package/dist/PopoverPanel-9ng1rm6e.stx +23 -0
  52. package/dist/Portal-j111yvqp.stx +74 -0
  53. package/dist/Progress-gbmw3akr.stx +142 -0
  54. package/dist/Radio-1n9qamv1.stx +81 -0
  55. package/dist/RadioGroup-12dw2vhk.stx +13 -0
  56. package/dist/RadioGroupDescription-bjm3mdht.stx +11 -0
  57. package/dist/RadioGroupLabel-aky8a73n.stx +11 -0
  58. package/dist/RadioGroupOption-9pcxgwmd.stx +51 -0
  59. package/dist/SearchInput-g240e2nq.stx +63 -0
  60. package/dist/Select-p9f94b20.stx +89 -0
  61. package/dist/Sidebar-gwxw8q38.stx +288 -0
  62. package/dist/SidebarFooter-887kxret.stx +95 -0
  63. package/dist/SidebarHeader-qjeyx0hv.stx +100 -0
  64. package/dist/SidebarItem-4dpwtmb4.stx +118 -0
  65. package/dist/SidebarSection-kh92fk4m.stx +108 -0
  66. package/dist/Signup-3nxq5zag.stx +161 -0
  67. package/dist/Skeleton-g0nx1njm.stx +48 -0
  68. package/dist/Spinner-w4zj58xp.stx +109 -0
  69. package/dist/Stepper-rwc5actp.stx +17 -0
  70. package/dist/StepperStep-h0k657nm.stx +53 -0
  71. package/dist/SubscriptionCheckout-4cbxdg7w.stx +353 -0
  72. package/dist/Switch-kjq8qd2p.stx +74 -0
  73. package/dist/Table-59j9qwp0.stx +14 -0
  74. package/dist/TableBody-reka2gwv.stx +10 -0
  75. package/dist/TableCell-j7nh4sgr.stx +10 -0
  76. package/dist/TableHead-ecbrwpqm.stx +10 -0
  77. package/dist/TableHeader-jn2shwsv.stx +13 -0
  78. package/dist/TableRow-xz8rrxhc.stx +11 -0
  79. package/dist/Tabs-kb6ex6m7.stx +145 -0
  80. package/dist/Teleport-819cg9zm.stx +101 -0
  81. package/dist/TextInput-zx9kgr79.stx +146 -0
  82. package/dist/Textarea-5xvmrb0z.stx +119 -0
  83. package/dist/Tooltip-5ehqsy49.stx +76 -0
  84. package/dist/Transition-2j39s044.stx +174 -0
  85. package/dist/TwoFactorChallenge-2q4gg89m.stx +125 -0
  86. package/dist/Video-6aq4shh4.stx +45 -0
  87. package/dist/VirtualList-weh5qtm1.stx +83 -0
  88. package/dist/VirtualTable-59a1eskc.stx +113 -0
  89. package/dist/components/index.d.ts +4 -0
  90. package/dist/composables/index.d.ts +4 -0
  91. package/dist/composables/index.js +3 -0
  92. package/dist/composables/useCopyCode.d.ts +19 -0
  93. package/dist/composables/useDarkMode.d.ts +25 -0
  94. package/dist/composables/useSEO.d.ts +36 -0
  95. package/dist/composables/utilities/index.d.ts +5 -0
  96. package/dist/composables/utilities/useClickOutside.d.ts +28 -0
  97. package/dist/composables/utilities/useFocusTrap.d.ts +34 -0
  98. package/dist/composables/utilities/useKeyboard.d.ts +55 -0
  99. package/dist/composables/utilities/useLocalStorage.d.ts +33 -0
  100. package/dist/composables/utilities/useMediaQuery.d.ts +64 -0
  101. package/dist/index.d.ts +86 -0
  102. package/dist/index.js +19 -0
  103. package/dist/ui/accordion/index.d.ts +12 -0
  104. package/dist/ui/audio/index.d.ts +12 -0
  105. package/dist/ui/auth/index.d.ts +29 -0
  106. package/dist/ui/avatar/index.d.ts +11 -0
  107. package/dist/ui/badge/index.d.ts +9 -0
  108. package/dist/ui/breadcrumb/index.d.ts +13 -0
  109. package/dist/ui/button/index.d.ts +45 -0
  110. package/dist/ui/calendar/index.d.ts +13 -0
  111. package/dist/ui/card/index.d.ts +12 -0
  112. package/dist/ui/checkbox/index.d.ts +15 -0
  113. package/dist/ui/combobox/index.d.ts +35 -0
  114. package/dist/ui/command-palette/index.d.ts +13 -0
  115. package/dist/ui/dialog/index.d.ts +27 -0
  116. package/dist/ui/drawer/index.d.ts +8 -0
  117. package/dist/ui/dropdown/index.d.ts +25 -0
  118. package/dist/ui/form/index.d.ts +48 -0
  119. package/dist/ui/image/index.d.ts +21 -0
  120. package/dist/ui/input/index.d.ts +73 -0
  121. package/dist/ui/listbox/index.d.ts +34 -0
  122. package/dist/ui/navigator/index.d.ts +18 -0
  123. package/dist/ui/notification/index.d.ts +11 -0
  124. package/dist/ui/pagination/index.d.ts +10 -0
  125. package/dist/ui/payment/index.d.ts +53 -0
  126. package/dist/ui/popover/index.d.ts +19 -0
  127. package/dist/ui/portal/index.d.ts +5 -0
  128. package/dist/ui/progress/index.d.ts +11 -0
  129. package/dist/ui/radio/index.d.ts +14 -0
  130. package/dist/ui/radio-group/index.d.ts +26 -0
  131. package/dist/ui/select/index.d.ts +19 -0
  132. package/dist/ui/sidebar/index.d.ts +129 -0
  133. package/dist/ui/skeleton/index.d.ts +10 -0
  134. package/dist/ui/spinner/index.d.ts +8 -0
  135. package/dist/ui/stepper/index.d.ts +17 -0
  136. package/dist/ui/storage/index.d.ts +40 -0
  137. package/dist/ui/switch/index.d.ts +9 -0
  138. package/dist/ui/table/index.d.ts +29 -0
  139. package/dist/ui/tabs/index.d.ts +14 -0
  140. package/dist/ui/teleport/index.d.ts +6 -0
  141. package/dist/ui/textarea/index.d.ts +19 -0
  142. package/dist/ui/tooltip/index.d.ts +9 -0
  143. package/dist/ui/transition/index.d.ts +24 -0
  144. package/dist/ui/video/index.d.ts +15 -0
  145. package/dist/ui/virtual-list/index.d.ts +9 -0
  146. package/dist/ui/virtual-table/index.d.ts +19 -0
  147. package/dist/utils/accessibility.d.ts +285 -0
  148. package/dist/utils/animation/easing.d.ts +112 -0
  149. package/dist/utils/animation/helpers.d.ts +119 -0
  150. package/dist/utils/animation/index.d.ts +3 -0
  151. package/dist/utils/animation/keyframes.d.ts +334 -0
  152. package/dist/utils/error-handling.d.ts +182 -0
  153. package/dist/utils/highlighter.d.ts +27 -0
  154. package/dist/utils/highlighter.js +2 -0
  155. package/dist/utils/i18n.d.ts +245 -0
  156. package/dist/utils/keep-alive.d.ts +171 -0
  157. package/dist/utils/lazy-loading.d.ts +174 -0
  158. package/dist/utils/prop-validation.d.ts +106 -0
  159. package/dist/utils/theme.d.ts +234 -0
  160. package/package.json +69 -0
  161. package/src/components/CodeBlock.stx +57 -0
  162. package/src/components/Footer.stx +66 -0
  163. package/src/components/Hero.stx +25 -0
  164. package/src/components/Installation.stx +39 -0
  165. package/src/components/index.ts +4 -0
  166. package/src/composables/index.ts +4 -0
  167. package/src/composables/useCopyCode.ts +73 -0
  168. package/src/composables/useDarkMode.ts +100 -0
  169. package/src/composables/useSEO.ts +229 -0
  170. package/src/composables/utilities/index.ts +7 -0
  171. package/src/composables/utilities/useClickOutside.ts +72 -0
  172. package/src/composables/utilities/useFocusTrap.ts +159 -0
  173. package/src/composables/utilities/useKeyboard.ts +124 -0
  174. package/src/composables/utilities/useLocalStorage.ts +132 -0
  175. package/src/composables/utilities/useMediaQuery.ts +118 -0
  176. package/src/index.ts +99 -0
  177. package/src/stx.d.ts +4 -0
  178. package/src/ui/accordion/Accordion.stx +108 -0
  179. package/src/ui/accordion/index.ts +14 -0
  180. package/src/ui/audio/Audio.stx +50 -0
  181. package/src/ui/audio/README.md +42 -0
  182. package/src/ui/audio/index.ts +13 -0
  183. package/src/ui/auth/Login.stx +163 -0
  184. package/src/ui/auth/README.md +145 -0
  185. package/src/ui/auth/Signup.stx +161 -0
  186. package/src/ui/auth/TwoFactorChallenge.stx +125 -0
  187. package/src/ui/auth/index.ts +32 -0
  188. package/src/ui/avatar/Avatar.stx +88 -0
  189. package/src/ui/avatar/index.ts +12 -0
  190. package/src/ui/badge/Badge.stx +70 -0
  191. package/src/ui/badge/index.ts +10 -0
  192. package/src/ui/breadcrumb/Breadcrumb.stx +79 -0
  193. package/src/ui/breadcrumb/index.ts +15 -0
  194. package/src/ui/button/Button.stx +89 -0
  195. package/src/ui/button/README.md +100 -0
  196. package/src/ui/button/index.ts +85 -0
  197. package/src/ui/calendar/Calendar.stx +232 -0
  198. package/src/ui/calendar/README.md +254 -0
  199. package/src/ui/calendar/index.ts +14 -0
  200. package/src/ui/card/Card.stx +74 -0
  201. package/src/ui/card/index.ts +13 -0
  202. package/src/ui/checkbox/Checkbox.stx +92 -0
  203. package/src/ui/checkbox/index.ts +16 -0
  204. package/src/ui/combobox/Combobox.stx +13 -0
  205. package/src/ui/combobox/ComboboxButton.stx +13 -0
  206. package/src/ui/combobox/ComboboxInput.stx +25 -0
  207. package/src/ui/combobox/ComboboxOption.stx +52 -0
  208. package/src/ui/combobox/ComboboxOptions.stx +12 -0
  209. package/src/ui/combobox/README.md +154 -0
  210. package/src/ui/combobox/index.ts +40 -0
  211. package/src/ui/command-palette/CommandPalette.stx +39 -0
  212. package/src/ui/command-palette/CommandPaletteItem.stx +32 -0
  213. package/src/ui/command-palette/README.md +42 -0
  214. package/src/ui/command-palette/index.ts +15 -0
  215. package/src/ui/dialog/Dialog.stx +19 -0
  216. package/src/ui/dialog/DialogBackdrop.stx +9 -0
  217. package/src/ui/dialog/DialogDescription.stx +11 -0
  218. package/src/ui/dialog/DialogPanel.stx +16 -0
  219. package/src/ui/dialog/DialogTitle.stx +11 -0
  220. package/src/ui/dialog/README.md +133 -0
  221. package/src/ui/dialog/index.ts +32 -0
  222. package/src/ui/drawer/Drawer.stx +137 -0
  223. package/src/ui/drawer/README.md +110 -0
  224. package/src/ui/drawer/index.ts +9 -0
  225. package/src/ui/dropdown/Dropdown.stx +24 -0
  226. package/src/ui/dropdown/DropdownButton.stx +23 -0
  227. package/src/ui/dropdown/DropdownItem.stx +43 -0
  228. package/src/ui/dropdown/DropdownItems.stx +18 -0
  229. package/src/ui/dropdown/README.md +142 -0
  230. package/src/ui/dropdown/index.ts +29 -0
  231. package/src/ui/form/Form.stx +209 -0
  232. package/src/ui/form/index.ts +67 -0
  233. package/src/ui/heatmap/Heatmap.stx +315 -0
  234. package/src/ui/image/Image.stx +197 -0
  235. package/src/ui/image/README.md +171 -0
  236. package/src/ui/image/index.ts +22 -0
  237. package/src/ui/input/EmailInput.stx +41 -0
  238. package/src/ui/input/NumberInput.stx +138 -0
  239. package/src/ui/input/PasswordInput.stx +155 -0
  240. package/src/ui/input/SearchInput.stx +63 -0
  241. package/src/ui/input/TextInput.stx +146 -0
  242. package/src/ui/input/index.ts +180 -0
  243. package/src/ui/listbox/Listbox.stx +14 -0
  244. package/src/ui/listbox/ListboxButton.stx +17 -0
  245. package/src/ui/listbox/ListboxLabel.stx +11 -0
  246. package/src/ui/listbox/ListboxOption.stx +57 -0
  247. package/src/ui/listbox/ListboxOptions.stx +16 -0
  248. package/src/ui/listbox/README.md +143 -0
  249. package/src/ui/listbox/index.ts +39 -0
  250. package/src/ui/navigator/Navigator.stx +138 -0
  251. package/src/ui/navigator/README.md +352 -0
  252. package/src/ui/navigator/index.ts +20 -0
  253. package/src/ui/notification/Notification.stx +71 -0
  254. package/src/ui/notification/README.md +118 -0
  255. package/src/ui/notification/index.ts +12 -0
  256. package/src/ui/pagination/Pagination.stx +183 -0
  257. package/src/ui/pagination/index.ts +11 -0
  258. package/src/ui/payment/Checkout.stx +39 -0
  259. package/src/ui/payment/DefaultPaymentMethod.stx +62 -0
  260. package/src/ui/payment/PaymentMethods.stx +113 -0
  261. package/src/ui/payment/README.md +427 -0
  262. package/src/ui/payment/SubscriptionCheckout.stx +353 -0
  263. package/src/ui/payment/index.ts +59 -0
  264. package/src/ui/popover/Popover.stx +24 -0
  265. package/src/ui/popover/PopoverButton.stx +23 -0
  266. package/src/ui/popover/PopoverPanel.stx +23 -0
  267. package/src/ui/popover/README.md +95 -0
  268. package/src/ui/popover/index.ts +22 -0
  269. package/src/ui/portal/Portal.stx +74 -0
  270. package/src/ui/portal/index.ts +6 -0
  271. package/src/ui/progress/Progress.stx +142 -0
  272. package/src/ui/progress/index.ts +12 -0
  273. package/src/ui/radio/Radio.stx +81 -0
  274. package/src/ui/radio/index.ts +15 -0
  275. package/src/ui/radio-group/README.md +145 -0
  276. package/src/ui/radio-group/RadioGroup.stx +13 -0
  277. package/src/ui/radio-group/RadioGroupDescription.stx +11 -0
  278. package/src/ui/radio-group/RadioGroupLabel.stx +11 -0
  279. package/src/ui/radio-group/RadioGroupOption.stx +51 -0
  280. package/src/ui/radio-group/index.ts +30 -0
  281. package/src/ui/select/README.md +204 -0
  282. package/src/ui/select/Select.stx +89 -0
  283. package/src/ui/select/index.ts +21 -0
  284. package/src/ui/sidebar/Sidebar.stx +288 -0
  285. package/src/ui/sidebar/SidebarFooter.stx +95 -0
  286. package/src/ui/sidebar/SidebarHeader.stx +100 -0
  287. package/src/ui/sidebar/SidebarItem.stx +118 -0
  288. package/src/ui/sidebar/SidebarSection.stx +108 -0
  289. package/src/ui/sidebar/index.ts +175 -0
  290. package/src/ui/skeleton/Skeleton.stx +48 -0
  291. package/src/ui/skeleton/index.ts +11 -0
  292. package/src/ui/spinner/Spinner.stx +109 -0
  293. package/src/ui/spinner/index.ts +9 -0
  294. package/src/ui/stepper/README.md +36 -0
  295. package/src/ui/stepper/Stepper.stx +17 -0
  296. package/src/ui/stepper/StepperStep.stx +53 -0
  297. package/src/ui/stepper/index.ts +19 -0
  298. package/src/ui/storage/README.md +193 -0
  299. package/src/ui/storage/index.ts +347 -0
  300. package/src/ui/switch/README.md +133 -0
  301. package/src/ui/switch/Switch.stx +74 -0
  302. package/src/ui/switch/index.ts +10 -0
  303. package/src/ui/table/README.md +39 -0
  304. package/src/ui/table/Table.stx +14 -0
  305. package/src/ui/table/TableBody.stx +10 -0
  306. package/src/ui/table/TableCell.stx +10 -0
  307. package/src/ui/table/TableHead.stx +10 -0
  308. package/src/ui/table/TableHeader.stx +13 -0
  309. package/src/ui/table/TableRow.stx +11 -0
  310. package/src/ui/table/index.ts +35 -0
  311. package/src/ui/tabs/Tabs.stx +145 -0
  312. package/src/ui/tabs/index.ts +16 -0
  313. package/src/ui/teleport/Teleport.stx +101 -0
  314. package/src/ui/teleport/index.ts +7 -0
  315. package/src/ui/textarea/Textarea.stx +119 -0
  316. package/src/ui/textarea/index.ts +20 -0
  317. package/src/ui/tooltip/Tooltip.stx +76 -0
  318. package/src/ui/tooltip/index.ts +10 -0
  319. package/src/ui/transition/README.md +30 -0
  320. package/src/ui/transition/Transition.stx +174 -0
  321. package/src/ui/transition/index.ts +25 -0
  322. package/src/ui/video/README.md +48 -0
  323. package/src/ui/video/Video.stx +45 -0
  324. package/src/ui/video/index.ts +16 -0
  325. package/src/ui/virtual-list/VirtualList.stx +83 -0
  326. package/src/ui/virtual-list/index.ts +10 -0
  327. package/src/ui/virtual-table/VirtualTable.stx +113 -0
  328. package/src/ui/virtual-table/index.ts +21 -0
  329. package/src/utils/PROP_VALIDATION.md +458 -0
  330. package/src/utils/THEME_SYSTEM.md +702 -0
  331. package/src/utils/accessibility.ts +841 -0
  332. package/src/utils/animation/easing.ts +368 -0
  333. package/src/utils/animation/helpers.ts +223 -0
  334. package/src/utils/animation/index.ts +5 -0
  335. package/src/utils/animation/keyframes.ts +230 -0
  336. package/src/utils/error-handling.ts +473 -0
  337. package/src/utils/highlighter.ts +93 -0
  338. package/src/utils/i18n.ts +634 -0
  339. package/src/utils/keep-alive.ts +818 -0
  340. package/src/utils/lazy-loading.ts +530 -0
  341. package/src/utils/prop-validation.ts +478 -0
  342. package/src/utils/theme.ts +626 -0
@@ -0,0 +1,113 @@
1
+ <script>
2
+ export const columns = $props.columns || []
3
+ export const data = $props.data || []
4
+ export const rowHeight = $props.rowHeight || 48
5
+ export const height = $props.height || 600
6
+ export const headerHeight = $props.headerHeight || 56
7
+ export const overscan = $props.overscan || 5
8
+ export const striped = $props.striped || false
9
+ export const hoverable = $props.hoverable ?? true
10
+ export const className = $props.className || ''
11
+
12
+ // Calculate visible range
13
+ let scrollTop = 0
14
+ const visibleCount = Math.ceil((height - headerHeight) / rowHeight)
15
+ const totalHeight = data.length * rowHeight
16
+
17
+ function getVisibleRange() {
18
+ const startIndex = Math.max(0, Math.floor(scrollTop / rowHeight) - overscan)
19
+ const endIndex = Math.min(data.length - 1, Math.floor((scrollTop + height - headerHeight) / rowHeight) + overscan)
20
+ return { startIndex, endIndex }
21
+ }
22
+
23
+ const { startIndex, endIndex } = getVisibleRange()
24
+ const visibleRows = data.slice(startIndex, endIndex + 1)
25
+ const offsetY = startIndex * rowHeight
26
+
27
+ function handleScroll(event) {
28
+ const target = event.target
29
+ scrollTop = target.scrollTop
30
+ }
31
+
32
+ // Get cell value
33
+ function getCellValue(row, column) {
34
+ if (column.render) {
35
+ return column.render(row[column.key], row)
36
+ }
37
+ return row[column.key]
38
+ }
39
+
40
+ export const containerClasses = `border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden ${className}`.trim()
41
+ export const headerClasses = `bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 flex`.trim()
42
+ export const headerCellClasses = `px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider`.trim()
43
+ export const bodyClasses = `overflow-auto bg-white dark:bg-gray-900`.trim()
44
+ export const innerClasses = `relative`.trim()
45
+ export const rowClasses = `absolute left-0 right-0 flex border-b border-gray-200 dark:border-gray-700 ${hoverable ? 'hover:bg-gray-50 dark:hover:bg-gray-800' : ''}`.trim()
46
+ export const cellClasses = `px-6 py-4 text-sm text-gray-900 dark:text-gray-100`.trim()
47
+ </script>
48
+
49
+ <div class="{{ containerClasses }}">
50
+ <!-- Header -->
51
+ <div class="{{ headerClasses }}" style="height: {{ headerHeight }}px">
52
+ @foreach(column in columns)
53
+ <div
54
+ class="{{ headerCellClasses }}"
55
+ style="flex: {{ column.width || 1 }}; min-width: {{ column.minWidth || 'auto' }}"
56
+ >
57
+ {{ column.label || column.key }}
58
+ </div>
59
+ @endforeach
60
+ </div>
61
+
62
+ <!-- Body -->
63
+ <div
64
+ class="{{ bodyClasses }}"
65
+ style="height: {{ height - headerHeight }}px"
66
+ onscroll="handleScroll(event)"
67
+ >
68
+ <div
69
+ class="{{ innerClasses }}"
70
+ style="height: {{ totalHeight }}px"
71
+ >
72
+ @foreach(row in visibleRows)
73
+ <div
74
+ class="{{ rowClasses }} {{ striped && ($loop.index % 2 === 1) ? 'bg-gray-50 dark:bg-gray-800' : '' }}"
75
+ style="top: {{ (startIndex + $loop.index) * rowHeight }}px; height: {{ rowHeight }}px"
76
+ data-index="{{ startIndex + $loop.index }}"
77
+ >
78
+ @foreach(column in columns)
79
+ <div
80
+ class="{{ cellClasses }}"
81
+ style="flex: {{ column.width || 1 }}; min-width: {{ column.minWidth || 'auto' }}"
82
+ >
83
+ {!! getCellValue(row, column) !!}
84
+ </div>
85
+ @endforeach
86
+ </div>
87
+ @endforeach
88
+ </div>
89
+ </div>
90
+ </div>
91
+
92
+ <script>
93
+ // Re-render on scroll (simplified reactivity)
94
+ if (typeof window !== 'undefined') {
95
+ let ticking = false
96
+
97
+ function updateVisibleRows() {
98
+ const range = getVisibleRange()
99
+ // Trigger re-render
100
+ ticking = false
101
+ }
102
+
103
+ const bodyContainer = document.querySelector('.overflow-auto')
104
+ if (bodyContainer) {
105
+ bodyContainer.addEventListener('scroll', () => {
106
+ if (!ticking) {
107
+ window.requestAnimationFrame(updateVisibleRows)
108
+ ticking = true
109
+ }
110
+ })
111
+ }
112
+ }
113
+ </script>
@@ -0,0 +1,21 @@
1
+ export { default as VirtualTable } from './VirtualTable.stx'
2
+
3
+ export interface VirtualTableColumn<T = any> {
4
+ key: string
5
+ label?: string
6
+ width?: number
7
+ minWidth?: string
8
+ render?: (value: any, row: T) => string
9
+ }
10
+
11
+ export interface VirtualTableProps<T = any> {
12
+ columns: VirtualTableColumn<T>[]
13
+ data: T[]
14
+ rowHeight?: number
15
+ height?: number
16
+ headerHeight?: number
17
+ overscan?: number
18
+ striped?: boolean
19
+ hoverable?: boolean
20
+ className?: string
21
+ }
@@ -0,0 +1,458 @@
1
+ # Prop Validation System
2
+
3
+ A comprehensive runtime prop validation system for component development with clear, actionable error messages.
4
+
5
+ ## Features
6
+
7
+ - **Development-only warnings** - Zero runtime cost in production
8
+ - **TypeScript integration** - Works alongside TypeScript for compile-time checks
9
+ - **Clear error messages** - Actionable warnings with specific values and expected types
10
+ - **Extensive validators** - 20+ built-in validators for common use cases
11
+ - **Composable schemas** - Build complex validation schemas from simple validators
12
+ - **Custom validators** - Easy to create custom validation logic
13
+
14
+ ## Installation
15
+
16
+ The prop validation utilities are included in `@stacksjs/components`:
17
+
18
+ ```ts
19
+ import { validateProps, PropTypes } from '@stacksjs/components'
20
+ ```
21
+
22
+ ## Basic Usage
23
+
24
+ ### Define a Schema
25
+
26
+ ```ts
27
+ import { PropTypes } from '@stacksjs/components'
28
+
29
+ const buttonPropSchema = {
30
+ variant: PropTypes.oneOf(['primary', 'secondary', 'outline']),
31
+ size: PropTypes.oneOf(['sm', 'md', 'lg']),
32
+ disabled: PropTypes.boolean,
33
+ onClick: PropTypes.func.required, // Mark as required
34
+ }
35
+ ```
36
+
37
+ ### Validate Props
38
+
39
+ ```ts
40
+ import { validateProps } from '@stacksjs/components'
41
+
42
+ function Button(props) {
43
+ // Validate props (only warns in development)
44
+ validateProps('Button', props, buttonPropSchema)
45
+
46
+ // ... rest of component
47
+ }
48
+ ```
49
+
50
+ ### Using createPropValidator
51
+
52
+ For reusable validators:
53
+
54
+ ```ts
55
+ import { createPropValidator, PropTypes } from '@stacksjs/components'
56
+
57
+ const validateButtonProps = createPropValidator('Button', {
58
+ variant: PropTypes.oneOf(['primary', 'secondary', 'outline']),
59
+ size: PropTypes.oneOf(['sm', 'md', 'lg']),
60
+ disabled: PropTypes.boolean,
61
+ onClick: PropTypes.func.required,
62
+ })
63
+
64
+ // In component
65
+ function Button(props) {
66
+ validateButtonProps(props)
67
+ // ... rest of component
68
+ }
69
+ ```
70
+
71
+ ## Available Validators
72
+
73
+ ### Primitive Types
74
+
75
+ ```ts
76
+ PropTypes.string // String type
77
+ PropTypes.number // Number type (excludes NaN)
78
+ PropTypes.boolean // Boolean type
79
+ PropTypes.func // Function type
80
+ PropTypes.object // Object type (excludes arrays and null)
81
+ PropTypes.array // Array type
82
+ PropTypes.any // Any type (always passes)
83
+ ```
84
+
85
+ ### Enum Validators
86
+
87
+ ```ts
88
+ // Value must be one of the specified options
89
+ PropTypes.oneOf(['sm', 'md', 'lg'])
90
+ PropTypes.oneOf([1, 2, 3])
91
+
92
+ // Value must match one of the specified types
93
+ PropTypes.oneOfType([
94
+ PropTypes.string,
95
+ PropTypes.number
96
+ ])
97
+ ```
98
+
99
+ ### Array Validators
100
+
101
+ ```ts
102
+ // Array of specific type
103
+ PropTypes.arrayOf(PropTypes.string)
104
+ PropTypes.arrayOf(PropTypes.number)
105
+
106
+ // Array of objects with specific shape
107
+ PropTypes.arrayOf(
108
+ PropTypes.shape({
109
+ id: PropTypes.number.required,
110
+ name: PropTypes.string.required
111
+ })
112
+ )
113
+ ```
114
+
115
+ ### Object Validators
116
+
117
+ ```ts
118
+ // Object with specific shape
119
+ PropTypes.shape({
120
+ name: PropTypes.string.required,
121
+ age: PropTypes.number,
122
+ email: PropTypes.email,
123
+ address: PropTypes.shape({
124
+ street: PropTypes.string,
125
+ city: PropTypes.string.required
126
+ })
127
+ })
128
+
129
+ // Instance of a class
130
+ PropTypes.instanceOf(Date)
131
+ PropTypes.instanceOf(HTMLElement)
132
+ ```
133
+
134
+ ### Number Validators
135
+
136
+ ```ts
137
+ // Minimum value
138
+ PropTypes.min(0)
139
+
140
+ // Maximum value
141
+ PropTypes.max(100)
142
+
143
+ // Range
144
+ PropTypes.range(0, 100)
145
+ ```
146
+
147
+ ### String Validators
148
+
149
+ ```ts
150
+ // Pattern matching
151
+ PropTypes.pattern(/^[A-Z]{3}$/)
152
+
153
+ // Email validation
154
+ PropTypes.email
155
+
156
+ // URL validation
157
+ PropTypes.url
158
+ ```
159
+
160
+ ### Custom Validators
161
+
162
+ ```ts
163
+ // Custom validation function
164
+ PropTypes.custom(
165
+ (value) => value > 0 && value < 100,
166
+ 'Value must be between 0 and 100'
167
+ )
168
+
169
+ // Custom with dynamic error message
170
+ PropTypes.custom(
171
+ (value, propName, componentName) => typeof value === 'string' && value.length > 5,
172
+ (value, propName, componentName) =>
173
+ `Prop \`${propName}\` in \`${componentName}\` must be a string longer than 5 characters`
174
+ )
175
+ ```
176
+
177
+ ## Required Props
178
+
179
+ Mark any validator as required:
180
+
181
+ ```ts
182
+ const schema = {
183
+ name: PropTypes.string.required,
184
+ age: PropTypes.number.required,
185
+ email: PropTypes.email.required,
186
+ onClick: PropTypes.func.required
187
+ }
188
+ ```
189
+
190
+ ## Complex Examples
191
+
192
+ ### Form Input Validation
193
+
194
+ ```ts
195
+ const inputPropSchema = {
196
+ value: PropTypes.string,
197
+ onChange: PropTypes.func.required,
198
+ placeholder: PropTypes.string,
199
+ disabled: PropTypes.boolean,
200
+ size: PropTypes.oneOf(['sm', 'md', 'lg']),
201
+ error: PropTypes.boolean,
202
+ label: PropTypes.string,
203
+ helperText: PropTypes.string,
204
+ required: PropTypes.boolean,
205
+ maxLength: PropTypes.range(1, 1000),
206
+ pattern: PropTypes.instanceOf(RegExp)
207
+ }
208
+ ```
209
+
210
+ ### User Object Validation
211
+
212
+ ```ts
213
+ const userPropSchema = {
214
+ user: PropTypes.shape({
215
+ id: PropTypes.number.required,
216
+ name: PropTypes.string.required,
217
+ email: PropTypes.email.required,
218
+ age: PropTypes.range(0, 150),
219
+ address: PropTypes.shape({
220
+ street: PropTypes.string,
221
+ city: PropTypes.string.required,
222
+ zipCode: PropTypes.pattern(/^\d{5}$/)
223
+ }),
224
+ roles: PropTypes.arrayOf(
225
+ PropTypes.oneOf(['admin', 'user', 'guest'])
226
+ )
227
+ }).required
228
+ }
229
+ ```
230
+
231
+ ### Complex Component Props
232
+
233
+ ```ts
234
+ const dataPropSchema = {
235
+ items: PropTypes.arrayOf(
236
+ PropTypes.shape({
237
+ id: PropTypes.oneOfType([
238
+ PropTypes.string,
239
+ PropTypes.number
240
+ ]).required,
241
+ label: PropTypes.string.required,
242
+ value: PropTypes.any,
243
+ disabled: PropTypes.boolean,
244
+ metadata: PropTypes.object
245
+ })
246
+ ).required,
247
+ selectedId: PropTypes.oneOfType([
248
+ PropTypes.string,
249
+ PropTypes.number
250
+ ]),
251
+ onSelect: PropTypes.func.required,
252
+ multiSelect: PropTypes.boolean,
253
+ maxSelections: PropTypes.custom(
254
+ (value) => typeof value === 'number' && value > 0,
255
+ 'maxSelections must be a positive number'
256
+ )
257
+ }
258
+ ```
259
+
260
+ ## Validation Options
261
+
262
+ ```ts
263
+ validateProps('Component', props, schema, {
264
+ // Throw error instead of warning (default: false)
265
+ throwOnError: true,
266
+
267
+ // Disable console warnings (default: true in dev)
268
+ logWarnings: false
269
+ })
270
+ ```
271
+
272
+ ## Helper Functions
273
+
274
+ ### assertProp
275
+
276
+ Assert a value matches a validator (throws on failure):
277
+
278
+ ```ts
279
+ import { assertProp, PropTypes } from '@stacksjs/components'
280
+
281
+ function processUser(user: unknown) {
282
+ // Throws if user doesn't match shape
283
+ assertProp(user, PropTypes.shape({
284
+ id: PropTypes.number.required,
285
+ name: PropTypes.string.required
286
+ }), 'user')
287
+
288
+ // TypeScript now knows user has the correct shape
289
+ console.log(user.id, user.name)
290
+ }
291
+ ```
292
+
293
+ ### warnProp
294
+
295
+ Warn if prop doesn't match validator (non-throwing):
296
+
297
+ ```ts
298
+ import { warnProp, PropTypes } from '@stacksjs/components'
299
+
300
+ function logValue(value: unknown) {
301
+ // Only warns in development, doesn't throw
302
+ warnProp(value, PropTypes.string, 'value')
303
+
304
+ console.log(value)
305
+ }
306
+ ```
307
+
308
+ ## Error Messages
309
+
310
+ The validation system provides clear, actionable error messages:
311
+
312
+ ```
313
+ ⚠️ Prop validation failed for `Button`
314
+ Invalid prop `variant` of value `large` supplied to `Button`, expected one of ["primary", "secondary", "outline", "ghost", "danger"].
315
+ Required prop `onClick` was not specified in `Button`.
316
+ ```
317
+
318
+ ## Production Behavior
319
+
320
+ - All validation is **completely skipped in production** builds
321
+ - Zero runtime overhead
322
+ - No console warnings
323
+ - `validateProps()`, `assertProp()`, and `warnProp()` become no-ops
324
+
325
+ ## TypeScript Integration
326
+
327
+ Prop validation works alongside TypeScript:
328
+
329
+ ```ts
330
+ interface ButtonProps {
331
+ variant?: 'primary' | 'secondary' | 'outline'
332
+ size?: 'sm' | 'md' | 'lg'
333
+ disabled?: boolean
334
+ onClick: (event: Event) => void
335
+ }
336
+
337
+ const buttonPropSchema = {
338
+ variant: PropTypes.oneOf(['primary', 'secondary', 'outline']),
339
+ size: PropTypes.oneOf(['sm', 'md', 'lg']),
340
+ disabled: PropTypes.boolean,
341
+ onClick: PropTypes.func.required
342
+ }
343
+
344
+ function Button(props: ButtonProps) {
345
+ // TypeScript provides compile-time checking
346
+ // PropTypes provides runtime checking in development
347
+ validateProps('Button', props, buttonPropSchema)
348
+
349
+ // ...
350
+ }
351
+ ```
352
+
353
+ ## Best Practices
354
+
355
+ ### 1. Define schemas separately
356
+
357
+ ```ts
358
+ // schemas/button.ts
359
+ export const buttonPropSchema = {
360
+ variant: PropTypes.oneOf(['primary', 'secondary']),
361
+ size: PropTypes.oneOf(['sm', 'md', 'lg']),
362
+ onClick: PropTypes.func.required
363
+ }
364
+
365
+ // components/Button.ts
366
+ import { buttonPropSchema } from '../schemas/button'
367
+ ```
368
+
369
+ ### 2. Reuse base schemas
370
+
371
+ ```ts
372
+ const baseInputSchema = {
373
+ value: PropTypes.string,
374
+ onChange: PropTypes.func,
375
+ disabled: PropTypes.boolean,
376
+ size: PropTypes.oneOf(['sm', 'md', 'lg'])
377
+ }
378
+
379
+ const textInputSchema = {
380
+ ...baseInputSchema,
381
+ maxLength: PropTypes.number,
382
+ placeholder: PropTypes.string
383
+ }
384
+
385
+ const numberInputSchema = {
386
+ ...baseInputSchema,
387
+ min: PropTypes.number,
388
+ max: PropTypes.number,
389
+ step: PropTypes.number
390
+ }
391
+ ```
392
+
393
+ ### 3. Create component-specific validators
394
+
395
+ ```ts
396
+ // ui/button/index.ts
397
+ import { createPropValidator, PropTypes } from '@stacksjs/components'
398
+
399
+ export const validateButtonProps = createPropValidator('Button', {
400
+ variant: PropTypes.oneOf(['primary', 'secondary', 'outline']),
401
+ size: PropTypes.oneOf(['sm', 'md', 'lg']),
402
+ onClick: PropTypes.func.required
403
+ })
404
+
405
+ // Export for use in other files
406
+ export { validateButtonProps }
407
+ ```
408
+
409
+ ### 4. Use custom validators for complex logic
410
+
411
+ ```ts
412
+ const isPowerOfTwo = PropTypes.custom(
413
+ (value) => typeof value === 'number' && (value & (value - 1)) === 0,
414
+ 'Value must be a power of 2'
415
+ )
416
+
417
+ const isEven = PropTypes.custom(
418
+ (value) => typeof value === 'number' && value % 2 === 0,
419
+ 'Value must be an even number'
420
+ )
421
+ ```
422
+
423
+ ## Migration from React PropTypes
424
+
425
+ If you're familiar with React's PropTypes, this system follows a similar API:
426
+
427
+ ```ts
428
+ // React PropTypes
429
+ Component.propTypes = {
430
+ name: PropTypes.string.isRequired,
431
+ age: PropTypes.number,
432
+ onClick: PropTypes.func.isRequired
433
+ }
434
+
435
+ // @stacksjs/components
436
+ const componentPropSchema = {
437
+ name: PropTypes.string.required,
438
+ age: PropTypes.number,
439
+ onClick: PropTypes.func.required
440
+ }
441
+ validateProps('Component', props, componentPropSchema)
442
+ ```
443
+
444
+ Key differences:
445
+ - Use `.required` instead of `.isRequired`
446
+ - Validation is a separate function call, not a static property
447
+ - Validation only runs in development mode
448
+
449
+ ## Examples in the Library
450
+
451
+ Several components in the library already use prop validation:
452
+
453
+ - **Button** - `src/ui/button/index.ts`
454
+ - **TextInput** - `src/ui/input/index.ts`
455
+ - **NumberInput** - `src/ui/input/index.ts`
456
+ - **Form** - `src/ui/form/index.ts`
457
+
458
+ Check these files for real-world examples of prop validation in action.