@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,209 @@
1
+ <script>
2
+ export const action = $props.action || ''
3
+ export const method = $props.method || 'POST'
4
+ export const validationSchema = $props.validationSchema || {}
5
+ export const initialValues = $props.initialValues || {}
6
+ export const validateOnChange = $props.validateOnChange ?? true
7
+ export const validateOnBlur = $props.validateOnBlur ?? true
8
+ export const onSubmit = $props.onSubmit
9
+ export const onError = $props.onError
10
+ export const className = $props.className || ''
11
+
12
+ let values = { ...initialValues }
13
+ let errors = {}
14
+ let touched = {}
15
+ let isSubmitting = false
16
+ let isValidating = false
17
+ let submitCount = 0
18
+
19
+ // Validate a single field
20
+ function validateField(name, value) {
21
+ const fieldSchema = validationSchema[name]
22
+ if (!fieldSchema)
23
+ return null
24
+
25
+ // Required validation
26
+ if (fieldSchema.required && !value) {
27
+ return fieldSchema.requiredMessage || `${name} is required`
28
+ }
29
+
30
+ // Min length validation
31
+ if (fieldSchema.minLength && value.length < fieldSchema.minLength) {
32
+ return fieldSchema.minLengthMessage || `${name} must be at least ${fieldSchema.minLength} characters`
33
+ }
34
+
35
+ // Max length validation
36
+ if (fieldSchema.maxLength && value.length > fieldSchema.maxLength) {
37
+ return fieldSchema.maxLengthMessage || `${name} must be at most ${fieldSchema.maxLength} characters`
38
+ }
39
+
40
+ // Pattern validation
41
+ if (fieldSchema.pattern && !fieldSchema.pattern.test(value)) {
42
+ return fieldSchema.patternMessage || `${name} is invalid`
43
+ }
44
+
45
+ // Custom validation
46
+ if (fieldSchema.validate && typeof fieldSchema.validate === 'function') {
47
+ return fieldSchema.validate(value, values)
48
+ }
49
+
50
+ return null
51
+ }
52
+
53
+ // Validate all fields
54
+ function validateForm() {
55
+ const newErrors = {}
56
+ let isValid = true
57
+
58
+ Object.keys(validationSchema).forEach((name) => {
59
+ const error = validateField(name, values[name])
60
+ if (error) {
61
+ newErrors[name] = error
62
+ isValid = false
63
+ }
64
+ })
65
+
66
+ errors = newErrors
67
+ return isValid
68
+ }
69
+
70
+ // Handle field change
71
+ function handleChange(event) {
72
+ const { name, value } = event.target
73
+ values[name] = value
74
+
75
+ if (validateOnChange && touched[name]) {
76
+ const error = validateField(name, value)
77
+ if (error) {
78
+ errors[name] = error
79
+ }
80
+ else {
81
+ delete errors[name]
82
+ }
83
+ }
84
+ }
85
+
86
+ // Handle field blur
87
+ function handleBlur(event) {
88
+ const { name } = event.target
89
+ touched[name] = true
90
+
91
+ if (validateOnBlur) {
92
+ const error = validateField(name, values[name])
93
+ if (error) {
94
+ errors[name] = error
95
+ }
96
+ else {
97
+ delete errors[name]
98
+ }
99
+ }
100
+ }
101
+
102
+ // Handle form submission
103
+ async function handleSubmit(event) {
104
+ event.preventDefault()
105
+ submitCount++
106
+
107
+ // Mark all fields as touched
108
+ Object.keys(validationSchema).forEach((name) => {
109
+ touched[name] = true
110
+ })
111
+
112
+ // Validate
113
+ isValidating = true
114
+ const isValid = validateForm()
115
+ isValidating = false
116
+
117
+ if (!isValid) {
118
+ if (onError) {
119
+ onError(errors)
120
+ }
121
+ return
122
+ }
123
+
124
+ // Submit
125
+ isSubmitting = true
126
+
127
+ try {
128
+ if (onSubmit) {
129
+ await onSubmit(values, { setErrors, setValues, resetForm })
130
+ }
131
+ }
132
+ catch (error) {
133
+ if (onError) {
134
+ onError(error)
135
+ }
136
+ }
137
+ finally {
138
+ isSubmitting = false
139
+ }
140
+ }
141
+
142
+ // Helper functions
143
+ function setErrors(newErrors) {
144
+ errors = { ...errors, ...newErrors }
145
+ }
146
+
147
+ function setValues(newValues) {
148
+ values = { ...values, ...newValues }
149
+ }
150
+
151
+ function resetForm() {
152
+ values = { ...initialValues }
153
+ errors = {}
154
+ touched = {}
155
+ isSubmitting = false
156
+ submitCount = 0
157
+ }
158
+
159
+ function getFieldProps(name) {
160
+ return {
161
+ name,
162
+ value: values[name] || '',
163
+ error: errors[name],
164
+ touched: touched[name],
165
+ onChange: handleChange,
166
+ onBlur: handleBlur,
167
+ }
168
+ }
169
+
170
+ export const formClasses = `space-y-6 ${className}`.trim()
171
+ export const errorClasses = `mt-1 text-sm text-red-600 dark:text-red-400`.trim()
172
+ </script>
173
+
174
+ <form
175
+ class="{{ formClasses }}"
176
+ @if(action)action="{{ action }}"@endif
177
+ method="{{ method }}"
178
+ onsubmit="handleSubmit(event)"
179
+ novalidate
180
+ >
181
+ <slot
182
+ values="{{ values }}"
183
+ errors="{{ errors }}"
184
+ touched="{{ touched }}"
185
+ isSubmitting="{{ isSubmitting }}"
186
+ isValidating="{{ isValidating }}"
187
+ submitCount="{{ submitCount }}"
188
+ getFieldProps="{{ getFieldProps }}"
189
+ setErrors="{{ setErrors }}"
190
+ setValues="{{ setValues }}"
191
+ resetForm="{{ resetForm }}"
192
+ />
193
+ </form>
194
+
195
+ <script>
196
+ // Export context for child components
197
+ if (typeof window !== 'undefined') {
198
+ window.__formContext = {
199
+ values,
200
+ errors,
201
+ touched,
202
+ isSubmitting,
203
+ isValidating,
204
+ getFieldProps,
205
+ handleChange,
206
+ handleBlur,
207
+ }
208
+ }
209
+ </script>
@@ -0,0 +1,67 @@
1
+ import type { PropSchema, ValidationResult } from '../../utils/prop-validation'
2
+ import { createPropValidator, PropTypes } from '../../utils/prop-validation'
3
+
4
+ export { default as Form } from './Form.stx'
5
+
6
+ export interface FieldSchema {
7
+ required?: boolean
8
+ requiredMessage?: string
9
+ minLength?: number
10
+ minLengthMessage?: string
11
+ maxLength?: number
12
+ maxLengthMessage?: string
13
+ pattern?: RegExp
14
+ patternMessage?: string
15
+ validate?: (value: any, values: Record<string, any>) => string | null | undefined
16
+ }
17
+
18
+ export interface ValidationSchema {
19
+ [fieldName: string]: FieldSchema
20
+ }
21
+
22
+ export interface FormHelpers {
23
+ setErrors: (errors: Record<string, string>) => void
24
+ setValues: (values: Record<string, any>) => void
25
+ resetForm: () => void
26
+ }
27
+
28
+ export interface FormProps {
29
+ action?: string
30
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
31
+ validationSchema?: ValidationSchema
32
+ initialValues?: Record<string, any>
33
+ validateOnChange?: boolean
34
+ validateOnBlur?: boolean
35
+ onSubmit?: (values: Record<string, any>, helpers: FormHelpers) => void | Promise<void>
36
+ onError?: (errors: Record<string, string> | Error) => void
37
+ className?: string
38
+ }
39
+
40
+ export interface FieldProps {
41
+ name: string
42
+ value: any
43
+ error?: string
44
+ touched?: boolean
45
+ onChange: (event: Event) => void
46
+ onBlur: (event: Event) => void
47
+ }
48
+
49
+ /**
50
+ * Form prop validation schema
51
+ */
52
+ export const formPropSchema: PropSchema = {
53
+ action: PropTypes.string,
54
+ method: PropTypes.oneOf(['GET', 'POST', 'PUT', 'DELETE', 'PATCH']),
55
+ validationSchema: PropTypes.object,
56
+ initialValues: PropTypes.object,
57
+ validateOnChange: PropTypes.boolean,
58
+ validateOnBlur: PropTypes.boolean,
59
+ onSubmit: PropTypes.func,
60
+ onError: PropTypes.func,
61
+ className: PropTypes.string,
62
+ }
63
+
64
+ /**
65
+ * Validate Form component props
66
+ */
67
+ export const validateFormProps: (props: Record<string, any>) => ValidationResult = createPropValidator('Form', formPropSchema)
@@ -0,0 +1,315 @@
1
+ <script>
2
+ /**
3
+ * Heatmap Component
4
+ *
5
+ * A privacy-compliant heatmap visualization component that displays user interaction data.
6
+ * Can be used to show click heatmaps, movement heatmaps, or scroll depth visualizations.
7
+ *
8
+ * Props:
9
+ * @prop {string} id - Unique identifier for the heatmap container
10
+ * @prop {string} mode - Visualization mode: 'overlay' | 'standalone' | 'thumbnail' (default: 'overlay')
11
+ * @prop {number} width - Width in pixels (default: 100% of container)
12
+ * @prop {number} height - Height in pixels (default: 100% of container)
13
+ * @prop {number} radius - Heat point radius (default: 20)
14
+ * @prop {number} blur - Blur amount for heatmap (default: 15)
15
+ * @prop {number} opacity - Maximum opacity (default: 0.6)
16
+ * @prop {boolean} showLegend - Show color legend (default: true)
17
+ * @prop {boolean} interactive - Enable zoom/pan interactions (default: false)
18
+ * @prop {string} dataSource - Source of data: 'auto' | 'manual' | 'endpoint' (default: 'auto')
19
+ * @prop {string} endpoint - API endpoint for fetching heatmap data
20
+ * @prop {string} colorScheme - Color scheme: 'default' | 'fire' | 'cool' | 'grayscale' (default: 'default')
21
+ */
22
+ const id = props.id || 'stx-heatmap-' + Math.random().toString(36).slice(2, 8)
23
+ const mode = props.mode || 'overlay'
24
+ const width = props.width || null
25
+ const height = props.height || null
26
+ const radius = props.radius || 20
27
+ const blur = props.blur || 15
28
+ const opacity = props.opacity || 0.6
29
+ const showLegend = props.showLegend !== false
30
+ const interactive = props.interactive || false
31
+ const dataSource = props.dataSource || 'auto'
32
+ const endpoint = props.endpoint || ''
33
+ const colorScheme = props.colorScheme || 'default'
34
+
35
+ // Color schemes
36
+ const colorSchemes = {
37
+ default: ['#0000ff', '#00ffff', '#00ff00', '#ffff00', '#ff0000'],
38
+ fire: ['#000000', '#ff0000', '#ffff00', '#ffffff'],
39
+ cool: ['#000033', '#0066ff', '#00ccff', '#ffffff'],
40
+ grayscale: ['#000000', '#444444', '#888888', '#cccccc', '#ffffff']
41
+ }
42
+
43
+ const colors = colorSchemes[colorScheme] || colorSchemes.default
44
+ </script>
45
+
46
+ <div
47
+ id="{{ id }}"
48
+ class="stx-heatmap stx-heatmap--{{ mode }}"
49
+ data-heatmap-radius="{{ radius }}"
50
+ data-heatmap-blur="{{ blur }}"
51
+ data-heatmap-opacity="{{ opacity }}"
52
+ data-heatmap-colors="{{ JSON.stringify(colors) }}"
53
+ data-heatmap-source="{{ dataSource }}"
54
+ @if(endpoint)
55
+ data-heatmap-endpoint="{{ endpoint }}"
56
+ @endif
57
+ @if(width)
58
+ style="width: {{ width }}px; {{ height ? 'height: ' + height + 'px;' : '' }}"
59
+ @endif
60
+ >
61
+ <canvas class="stx-heatmap__canvas"></canvas>
62
+
63
+ @if(showLegend)
64
+ <div class="stx-heatmap__legend">
65
+ <span class="stx-heatmap__legend-label">Low</span>
66
+ <div class="stx-heatmap__legend-gradient" style="background: linear-gradient(to right, {{ colors.join(', ') }})"></div>
67
+ <span class="stx-heatmap__legend-label">High</span>
68
+ </div>
69
+ @endif
70
+
71
+ @if(interactive)
72
+ <div class="stx-heatmap__controls">
73
+ <button type="button" class="stx-heatmap__control" data-action="zoom-in" aria-label="Zoom in">+</button>
74
+ <button type="button" class="stx-heatmap__control" data-action="zoom-out" aria-label="Zoom out">-</button>
75
+ <button type="button" class="stx-heatmap__control" data-action="reset" aria-label="Reset view">Reset</button>
76
+ </div>
77
+ @endif
78
+
79
+ {{ slot }}
80
+ </div>
81
+
82
+ <style>
83
+ .stx-heatmap {
84
+ position: relative;
85
+ overflow: hidden;
86
+ }
87
+
88
+ .stx-heatmap--overlay {
89
+ position: fixed;
90
+ top: 0;
91
+ left: 0;
92
+ width: 100%;
93
+ height: 100%;
94
+ pointer-events: none;
95
+ z-index: 9999;
96
+ }
97
+
98
+ .stx-heatmap--standalone {
99
+ display: block;
100
+ border: 1px solid #e0e0e0;
101
+ border-radius: 8px;
102
+ background: #f9f9f9;
103
+ }
104
+
105
+ .stx-heatmap--thumbnail {
106
+ display: inline-block;
107
+ border: 1px solid #e0e0e0;
108
+ border-radius: 4px;
109
+ background: #f9f9f9;
110
+ }
111
+
112
+ .stx-heatmap__canvas {
113
+ width: 100%;
114
+ height: 100%;
115
+ display: block;
116
+ }
117
+
118
+ .stx-heatmap__legend {
119
+ position: absolute;
120
+ bottom: 10px;
121
+ left: 50%;
122
+ transform: translateX(-50%);
123
+ display: flex;
124
+ align-items: center;
125
+ gap: 8px;
126
+ padding: 6px 12px;
127
+ background: rgba(255, 255, 255, 0.9);
128
+ border-radius: 4px;
129
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
130
+ font-size: 12px;
131
+ pointer-events: auto;
132
+ }
133
+
134
+ .stx-heatmap__legend-gradient {
135
+ width: 100px;
136
+ height: 12px;
137
+ border-radius: 2px;
138
+ }
139
+
140
+ .stx-heatmap__legend-label {
141
+ color: #666;
142
+ }
143
+
144
+ .stx-heatmap__controls {
145
+ position: absolute;
146
+ top: 10px;
147
+ right: 10px;
148
+ display: flex;
149
+ flex-direction: column;
150
+ gap: 4px;
151
+ pointer-events: auto;
152
+ }
153
+
154
+ .stx-heatmap__control {
155
+ width: 32px;
156
+ height: 32px;
157
+ display: flex;
158
+ align-items: center;
159
+ justify-content: center;
160
+ border: 1px solid #e0e0e0;
161
+ border-radius: 4px;
162
+ background: rgba(255, 255, 255, 0.9);
163
+ cursor: pointer;
164
+ font-size: 16px;
165
+ transition: background-color 0.2s;
166
+ }
167
+
168
+ .stx-heatmap__control:hover {
169
+ background: #f0f0f0;
170
+ }
171
+
172
+ .stx-heatmap__control:active {
173
+ background: #e0e0e0;
174
+ }
175
+ </style>
176
+
177
+ <script client>
178
+ (function() {
179
+ const container = document.getElementById('{{ id }}');
180
+ if (!container) return;
181
+
182
+ const canvas = container.querySelector('.stx-heatmap__canvas');
183
+ const ctx = canvas.getContext('2d');
184
+
185
+ const config = {
186
+ radius: parseInt(container.dataset.heatmapRadius) || 20,
187
+ blur: parseInt(container.dataset.heatmapBlur) || 15,
188
+ opacity: parseFloat(container.dataset.heatmapOpacity) || 0.6,
189
+ colors: JSON.parse(container.dataset.heatmapColors || '[]'),
190
+ source: container.dataset.heatmapSource || 'auto',
191
+ endpoint: container.dataset.heatmapEndpoint || ''
192
+ };
193
+
194
+ // Resize canvas to match container
195
+ function resizeCanvas() {
196
+ const rect = container.getBoundingClientRect();
197
+ canvas.width = rect.width || window.innerWidth;
198
+ canvas.height = rect.height || window.innerHeight;
199
+ }
200
+
201
+ // Create color gradient for heatmap
202
+ function createGradient(colors) {
203
+ const gradientCanvas = document.createElement('canvas');
204
+ gradientCanvas.width = 256;
205
+ gradientCanvas.height = 1;
206
+ const gradientCtx = gradientCanvas.getContext('2d');
207
+ const gradient = gradientCtx.createLinearGradient(0, 0, 256, 0);
208
+
209
+ colors.forEach((color, i) => {
210
+ gradient.addColorStop(i / (colors.length - 1), color);
211
+ });
212
+
213
+ gradientCtx.fillStyle = gradient;
214
+ gradientCtx.fillRect(0, 0, 256, 1);
215
+ return gradientCtx.getImageData(0, 0, 256, 1).data;
216
+ }
217
+
218
+ // Render heatmap from data points
219
+ function render(points) {
220
+ resizeCanvas();
221
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
222
+
223
+ // Draw heat points
224
+ points.forEach(function(p) {
225
+ const x = (p.x / 100) * canvas.width;
226
+ const y = (p.y / 100) * canvas.height;
227
+ const intensity = p.type === 'click' ? 1 : 0.3;
228
+
229
+ const gradient = ctx.createRadialGradient(x, y, 0, x, y, config.radius);
230
+ gradient.addColorStop(0, 'rgba(0,0,0,' + intensity + ')');
231
+ gradient.addColorStop(1, 'rgba(0,0,0,0)');
232
+ ctx.fillStyle = gradient;
233
+ ctx.fillRect(x - config.radius, y - config.radius, config.radius * 2, config.radius * 2);
234
+ });
235
+
236
+ // Apply blur
237
+ if (config.blur > 0) {
238
+ ctx.filter = 'blur(' + config.blur + 'px)';
239
+ ctx.drawImage(canvas, 0, 0);
240
+ ctx.filter = 'none';
241
+ }
242
+
243
+ // Colorize
244
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
245
+ const pixels = imageData.data;
246
+ const gradientColors = createGradient(config.colors);
247
+
248
+ for (let i = 0; i < pixels.length; i += 4) {
249
+ const alpha = pixels[i + 3];
250
+ if (alpha > 0) {
251
+ const colorIndex = Math.min(alpha, 255) * 4;
252
+ pixels[i] = gradientColors[colorIndex];
253
+ pixels[i + 1] = gradientColors[colorIndex + 1];
254
+ pixels[i + 2] = gradientColors[colorIndex + 2];
255
+ pixels[i + 3] = Math.round(alpha * config.opacity);
256
+ }
257
+ }
258
+
259
+ ctx.putImageData(imageData, 0, 0);
260
+ }
261
+
262
+ // Load data based on source
263
+ function loadData() {
264
+ if (config.source === 'auto' && window.stxHeatmap) {
265
+ render(window.stxHeatmap.getData().points);
266
+ } else if (config.source === 'endpoint' && config.endpoint) {
267
+ fetch(config.endpoint)
268
+ .then(r => r.json())
269
+ .then(data => render(data.points || data))
270
+ .catch(console.error);
271
+ }
272
+ }
273
+
274
+ // Initialize
275
+ resizeCanvas();
276
+ loadData();
277
+
278
+ // Handle resize
279
+ window.addEventListener('resize', function() {
280
+ resizeCanvas();
281
+ loadData();
282
+ });
283
+
284
+ // Handle interactive controls
285
+ const controls = container.querySelectorAll('.stx-heatmap__control');
286
+ let zoom = 1;
287
+ let panX = 0;
288
+ let panY = 0;
289
+
290
+ controls.forEach(function(btn) {
291
+ btn.addEventListener('click', function() {
292
+ const action = btn.dataset.action;
293
+ if (action === 'zoom-in') {
294
+ zoom = Math.min(zoom * 1.2, 5);
295
+ } else if (action === 'zoom-out') {
296
+ zoom = Math.max(zoom / 1.2, 0.5);
297
+ } else if (action === 'reset') {
298
+ zoom = 1;
299
+ panX = 0;
300
+ panY = 0;
301
+ }
302
+ canvas.style.transform = 'scale(' + zoom + ') translate(' + panX + 'px, ' + panY + 'px)';
303
+ });
304
+ });
305
+
306
+ // Expose API
307
+ container.stxHeatmap = {
308
+ render: render,
309
+ refresh: loadData,
310
+ setData: function(points) { render(points); },
311
+ getCanvas: function() { return canvas; },
312
+ toDataURL: function() { return canvas.toDataURL(); }
313
+ };
314
+ })();
315
+ </script>