@pithyjs/ui-kit 0.1.0-beta.0

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 (417) hide show
  1. package/LICENSE +21 -0
  2. package/dist/animation/animate-in.d.ts +35 -0
  3. package/dist/animation/animate-in.d.ts.map +1 -0
  4. package/dist/animation/animate-in.js +71 -0
  5. package/dist/animation/animate-in.js.map +1 -0
  6. package/dist/atoms/actions/_icon-control.scss +53 -0
  7. package/dist/atoms/actions/button/Button.d.ts +93 -0
  8. package/dist/atoms/actions/button/Button.d.ts.map +1 -0
  9. package/dist/atoms/actions/button/Button.js +332 -0
  10. package/dist/atoms/actions/button/Button.js.map +1 -0
  11. package/dist/atoms/actions/button/button.scss +342 -0
  12. package/dist/atoms/actions/icon-button/IconButton.d.ts +47 -0
  13. package/dist/atoms/actions/icon-button/IconButton.d.ts.map +1 -0
  14. package/dist/atoms/actions/icon-button/IconButton.js +81 -0
  15. package/dist/atoms/actions/icon-button/IconButton.js.map +1 -0
  16. package/dist/atoms/actions/icon-button/icon-button.scss +20 -0
  17. package/dist/atoms/actions/icon-group/IconGroup.d.ts +60 -0
  18. package/dist/atoms/actions/icon-group/IconGroup.d.ts.map +1 -0
  19. package/dist/atoms/actions/icon-group/IconGroup.js +191 -0
  20. package/dist/atoms/actions/icon-group/IconGroup.js.map +1 -0
  21. package/dist/atoms/actions/icon-group/icon-group.scss +36 -0
  22. package/dist/atoms/actions/segmented-control/SegmentedControl.d.ts +49 -0
  23. package/dist/atoms/actions/segmented-control/SegmentedControl.d.ts.map +1 -0
  24. package/dist/atoms/actions/segmented-control/SegmentedControl.js +107 -0
  25. package/dist/atoms/actions/segmented-control/SegmentedControl.js.map +1 -0
  26. package/dist/atoms/actions/segmented-control/segmented-control.scss +90 -0
  27. package/dist/atoms/content/_mixins.scss +79 -0
  28. package/dist/atoms/content/action-tile/ActionTile.d.ts +27 -0
  29. package/dist/atoms/content/action-tile/ActionTile.d.ts.map +1 -0
  30. package/dist/atoms/content/action-tile/ActionTile.js +98 -0
  31. package/dist/atoms/content/action-tile/ActionTile.js.map +1 -0
  32. package/dist/atoms/content/action-tile/action-tile.scss +150 -0
  33. package/dist/atoms/content/badge/Badge.d.ts +57 -0
  34. package/dist/atoms/content/badge/Badge.d.ts.map +1 -0
  35. package/dist/atoms/content/badge/Badge.js +177 -0
  36. package/dist/atoms/content/badge/Badge.js.map +1 -0
  37. package/dist/atoms/content/badge/badge.scss +198 -0
  38. package/dist/atoms/content/heading/Heading.d.ts +53 -0
  39. package/dist/atoms/content/heading/Heading.d.ts.map +1 -0
  40. package/dist/atoms/content/heading/Heading.js +142 -0
  41. package/dist/atoms/content/heading/Heading.js.map +1 -0
  42. package/dist/atoms/content/heading/heading.scss +43 -0
  43. package/dist/atoms/content/icon/Icon.d.ts +68 -0
  44. package/dist/atoms/content/icon/Icon.d.ts.map +1 -0
  45. package/dist/atoms/content/icon/Icon.js +137 -0
  46. package/dist/atoms/content/icon/Icon.js.map +1 -0
  47. package/dist/atoms/content/icon/icon.scss +66 -0
  48. package/dist/atoms/content/icon/resolveIconSvg.d.ts +21 -0
  49. package/dist/atoms/content/icon/resolveIconSvg.d.ts.map +1 -0
  50. package/dist/atoms/content/icon/resolveIconSvg.js +73 -0
  51. package/dist/atoms/content/icon/resolveIconSvg.js.map +1 -0
  52. package/dist/atoms/content/image/Image.d.ts +81 -0
  53. package/dist/atoms/content/image/Image.d.ts.map +1 -0
  54. package/dist/atoms/content/image/Image.js +293 -0
  55. package/dist/atoms/content/image/Image.js.map +1 -0
  56. package/dist/atoms/content/image/image.scss +77 -0
  57. package/dist/atoms/content/text/Text.d.ts +58 -0
  58. package/dist/atoms/content/text/Text.d.ts.map +1 -0
  59. package/dist/atoms/content/text/Text.js +158 -0
  60. package/dist/atoms/content/text/Text.js.map +1 -0
  61. package/dist/atoms/content/text/text.scss +98 -0
  62. package/dist/atoms/content/tile/Tile.d.ts +66 -0
  63. package/dist/atoms/content/tile/Tile.d.ts.map +1 -0
  64. package/dist/atoms/content/tile/Tile.js +125 -0
  65. package/dist/atoms/content/tile/Tile.js.map +1 -0
  66. package/dist/atoms/content/tile/tile.scss +122 -0
  67. package/dist/atoms/feedback/tooltip/Tooltip.d.ts +44 -0
  68. package/dist/atoms/feedback/tooltip/Tooltip.d.ts.map +1 -0
  69. package/dist/atoms/feedback/tooltip/Tooltip.js +121 -0
  70. package/dist/atoms/feedback/tooltip/Tooltip.js.map +1 -0
  71. package/dist/atoms/feedback/tooltip/tooltip.scss +96 -0
  72. package/dist/atoms/forms/checkbox/Checkbox.d.ts +49 -0
  73. package/dist/atoms/forms/checkbox/Checkbox.d.ts.map +1 -0
  74. package/dist/atoms/forms/checkbox/Checkbox.js +269 -0
  75. package/dist/atoms/forms/checkbox/Checkbox.js.map +1 -0
  76. package/dist/atoms/forms/checkbox/CheckboxGroup.d.ts +50 -0
  77. package/dist/atoms/forms/checkbox/CheckboxGroup.d.ts.map +1 -0
  78. package/dist/atoms/forms/checkbox/CheckboxGroup.js +132 -0
  79. package/dist/atoms/forms/checkbox/CheckboxGroup.js.map +1 -0
  80. package/dist/atoms/forms/checkbox/checkbox.scss +506 -0
  81. package/dist/atoms/forms/file-picker/FilePicker.d.ts +23 -0
  82. package/dist/atoms/forms/file-picker/FilePicker.d.ts.map +1 -0
  83. package/dist/atoms/forms/file-picker/FilePicker.js +103 -0
  84. package/dist/atoms/forms/file-picker/FilePicker.js.map +1 -0
  85. package/dist/atoms/forms/file-picker/file-picker.scss +84 -0
  86. package/dist/atoms/forms/form-field/FormField.d.ts +39 -0
  87. package/dist/atoms/forms/form-field/FormField.d.ts.map +1 -0
  88. package/dist/atoms/forms/form-field/FormField.js +139 -0
  89. package/dist/atoms/forms/form-field/FormField.js.map +1 -0
  90. package/dist/atoms/forms/form-field/form-field.scss +46 -0
  91. package/dist/atoms/forms/input/Input.d.ts +81 -0
  92. package/dist/atoms/forms/input/Input.d.ts.map +1 -0
  93. package/dist/atoms/forms/input/Input.js +417 -0
  94. package/dist/atoms/forms/input/Input.js.map +1 -0
  95. package/dist/atoms/forms/input/input.scss +547 -0
  96. package/dist/atoms/forms/radio/Radio.d.ts +54 -0
  97. package/dist/atoms/forms/radio/Radio.d.ts.map +1 -0
  98. package/dist/atoms/forms/radio/Radio.js +278 -0
  99. package/dist/atoms/forms/radio/Radio.js.map +1 -0
  100. package/dist/atoms/forms/radio/RadioGroup.d.ts +54 -0
  101. package/dist/atoms/forms/radio/RadioGroup.d.ts.map +1 -0
  102. package/dist/atoms/forms/radio/RadioGroup.js +137 -0
  103. package/dist/atoms/forms/radio/RadioGroup.js.map +1 -0
  104. package/dist/atoms/forms/radio/radio.scss +459 -0
  105. package/dist/atoms/forms/select/Select.d.ts +57 -0
  106. package/dist/atoms/forms/select/Select.d.ts.map +1 -0
  107. package/dist/atoms/forms/select/Select.js +457 -0
  108. package/dist/atoms/forms/select/Select.js.map +1 -0
  109. package/dist/atoms/forms/select/select-dropdown.d.ts +49 -0
  110. package/dist/atoms/forms/select/select-dropdown.d.ts.map +1 -0
  111. package/dist/atoms/forms/select/select-dropdown.js +305 -0
  112. package/dist/atoms/forms/select/select-dropdown.js.map +1 -0
  113. package/dist/atoms/forms/select/select-keyboard.d.ts +14 -0
  114. package/dist/atoms/forms/select/select-keyboard.d.ts.map +1 -0
  115. package/dist/atoms/forms/select/select-keyboard.js +182 -0
  116. package/dist/atoms/forms/select/select-keyboard.js.map +1 -0
  117. package/dist/atoms/forms/select/select.scss +630 -0
  118. package/dist/atoms/forms/textarea/Textarea.d.ts +69 -0
  119. package/dist/atoms/forms/textarea/Textarea.d.ts.map +1 -0
  120. package/dist/atoms/forms/textarea/Textarea.js +394 -0
  121. package/dist/atoms/forms/textarea/Textarea.js.map +1 -0
  122. package/dist/atoms/forms/textarea/textarea.scss +486 -0
  123. package/dist/atoms/forms/toggle/Toggle.d.ts +47 -0
  124. package/dist/atoms/forms/toggle/Toggle.d.ts.map +1 -0
  125. package/dist/atoms/forms/toggle/Toggle.js +243 -0
  126. package/dist/atoms/forms/toggle/Toggle.js.map +1 -0
  127. package/dist/atoms/forms/toggle/toggle.scss +561 -0
  128. package/dist/compiler.d.ts +7 -0
  129. package/dist/compiler.d.ts.map +1 -0
  130. package/dist/compiler.js +18 -0
  131. package/dist/compiler.js.map +1 -0
  132. package/dist/icons/index.d.ts +14 -0
  133. package/dist/icons/index.d.ts.map +1 -0
  134. package/dist/icons/index.js +14 -0
  135. package/dist/icons/index.js.map +1 -0
  136. package/dist/icons/registry.d.ts +65 -0
  137. package/dist/icons/registry.d.ts.map +1 -0
  138. package/dist/icons/registry.js +126 -0
  139. package/dist/icons/registry.js.map +1 -0
  140. package/dist/index.d.ts +55 -0
  141. package/dist/index.d.ts.map +1 -0
  142. package/dist/index.js +64 -0
  143. package/dist/index.js.map +1 -0
  144. package/dist/layout/Grid.d.ts +78 -0
  145. package/dist/layout/Grid.d.ts.map +1 -0
  146. package/dist/layout/Grid.js +272 -0
  147. package/dist/layout/Grid.js.map +1 -0
  148. package/dist/layout/GridItem.d.ts +52 -0
  149. package/dist/layout/GridItem.d.ts.map +1 -0
  150. package/dist/layout/GridItem.js +99 -0
  151. package/dist/layout/GridItem.js.map +1 -0
  152. package/dist/layout/PageSection.d.ts +43 -0
  153. package/dist/layout/PageSection.d.ts.map +1 -0
  154. package/dist/layout/PageSection.js +75 -0
  155. package/dist/layout/PageSection.js.map +1 -0
  156. package/dist/layout/SmartContainer.d.ts +66 -0
  157. package/dist/layout/SmartContainer.d.ts.map +1 -0
  158. package/dist/layout/SmartContainer.js +194 -0
  159. package/dist/layout/SmartContainer.js.map +1 -0
  160. package/dist/layout/Stack.d.ts +65 -0
  161. package/dist/layout/Stack.d.ts.map +1 -0
  162. package/dist/layout/Stack.js +114 -0
  163. package/dist/layout/Stack.js.map +1 -0
  164. package/dist/organisms/content/Card.d.ts +93 -0
  165. package/dist/organisms/content/Card.d.ts.map +1 -0
  166. package/dist/organisms/content/Card.js +353 -0
  167. package/dist/organisms/content/Card.js.map +1 -0
  168. package/dist/organisms/content/_card-horizontal.scss +88 -0
  169. package/dist/organisms/content/_card-link.scss +49 -0
  170. package/dist/organisms/content/_card-overlay.scss +91 -0
  171. package/dist/organisms/content/_card-root.scss +99 -0
  172. package/dist/organisms/content/_card-slots.scss +143 -0
  173. package/dist/organisms/content/_card-states.scss +166 -0
  174. package/dist/organisms/content/_card-tokens.scss +22 -0
  175. package/dist/organisms/content/_card-variants.scss +31 -0
  176. package/dist/organisms/content/card.scss +15 -0
  177. package/dist/organisms/feedback/Modal.d.ts +39 -0
  178. package/dist/organisms/feedback/Modal.d.ts.map +1 -0
  179. package/dist/organisms/feedback/Modal.js +120 -0
  180. package/dist/organisms/feedback/Modal.js.map +1 -0
  181. package/dist/organisms/feedback/Popover.d.ts +30 -0
  182. package/dist/organisms/feedback/Popover.d.ts.map +1 -0
  183. package/dist/organisms/feedback/Popover.js +151 -0
  184. package/dist/organisms/feedback/Popover.js.map +1 -0
  185. package/dist/organisms/feedback/modal.scss +66 -0
  186. package/dist/organisms/feedback/popover.scss +27 -0
  187. package/dist/organisms/forms/DynamicForm.d.ts +118 -0
  188. package/dist/organisms/forms/DynamicForm.d.ts.map +1 -0
  189. package/dist/organisms/forms/DynamicForm.js +546 -0
  190. package/dist/organisms/forms/DynamicForm.js.map +1 -0
  191. package/dist/organisms/forms/Form.d.ts +62 -0
  192. package/dist/organisms/forms/Form.d.ts.map +1 -0
  193. package/dist/organisms/forms/Form.js +131 -0
  194. package/dist/organisms/forms/Form.js.map +1 -0
  195. package/dist/organisms/forms/dynamic-form.scss +91 -0
  196. package/dist/organisms/forms/form-helpers.d.ts +21 -0
  197. package/dist/organisms/forms/form-helpers.d.ts.map +1 -0
  198. package/dist/organisms/forms/form-helpers.js +57 -0
  199. package/dist/organisms/forms/form-helpers.js.map +1 -0
  200. package/dist/organisms/forms/form-state.d.ts +66 -0
  201. package/dist/organisms/forms/form-state.d.ts.map +1 -0
  202. package/dist/organisms/forms/form-state.js +156 -0
  203. package/dist/organisms/forms/form-state.js.map +1 -0
  204. package/dist/organisms/forms/form.scss +23 -0
  205. package/dist/organisms/forms/validation.d.ts +32 -0
  206. package/dist/organisms/forms/validation.d.ts.map +1 -0
  207. package/dist/organisms/forms/validation.js +118 -0
  208. package/dist/organisms/forms/validation.js.map +1 -0
  209. package/dist/organisms/navigation/Navbar.d.ts +66 -0
  210. package/dist/organisms/navigation/Navbar.d.ts.map +1 -0
  211. package/dist/organisms/navigation/Navbar.js +881 -0
  212. package/dist/organisms/navigation/Navbar.js.map +1 -0
  213. package/dist/organisms/navigation/_navbar-announcement.scss +62 -0
  214. package/dist/organisms/navigation/_navbar-brand.scss +69 -0
  215. package/dist/organisms/navigation/_navbar-container.scss +37 -0
  216. package/dist/organisms/navigation/_navbar-desktop.scss +116 -0
  217. package/dist/organisms/navigation/_navbar-drawer.scss +97 -0
  218. package/dist/organisms/navigation/_navbar-links.scss +143 -0
  219. package/dist/organisms/navigation/_navbar-menu.scss +193 -0
  220. package/dist/organisms/navigation/_navbar-root.scss +107 -0
  221. package/dist/organisms/navigation/_navbar-search.scss +83 -0
  222. package/dist/organisms/navigation/_navbar-toggle.scss +91 -0
  223. package/dist/organisms/navigation/_navbar-tokens.scss +15 -0
  224. package/dist/organisms/navigation/navbar.scss +17 -0
  225. package/dist/registry/categories.d.ts +6 -0
  226. package/dist/registry/categories.d.ts.map +1 -0
  227. package/dist/registry/categories.js +13 -0
  228. package/dist/registry/categories.js.map +1 -0
  229. package/dist/registry/components.d.ts +5 -0
  230. package/dist/registry/components.d.ts.map +1 -0
  231. package/dist/registry/components.js +95 -0
  232. package/dist/registry/components.js.map +1 -0
  233. package/dist/registry/index.d.ts +59 -0
  234. package/dist/registry/index.d.ts.map +1 -0
  235. package/dist/registry/index.js +79 -0
  236. package/dist/registry/index.js.map +1 -0
  237. package/dist/registry/levels.d.ts +6 -0
  238. package/dist/registry/levels.d.ts.map +1 -0
  239. package/dist/registry/levels.js +9 -0
  240. package/dist/registry/levels.js.map +1 -0
  241. package/dist/registry/registry-meta-types.d.ts +67 -0
  242. package/dist/registry/registry-meta-types.d.ts.map +1 -0
  243. package/dist/registry/registry-meta-types.js +6 -0
  244. package/dist/registry/registry-meta-types.js.map +1 -0
  245. package/dist/registry/registry-meta.d.ts +5 -0
  246. package/dist/registry/registry-meta.d.ts.map +1 -0
  247. package/dist/registry/registry-meta.js +731 -0
  248. package/dist/registry/registry-meta.js.map +1 -0
  249. package/dist/registry/renderer.d.ts +69 -0
  250. package/dist/registry/renderer.d.ts.map +1 -0
  251. package/dist/registry/renderer.js +311 -0
  252. package/dist/registry/renderer.js.map +1 -0
  253. package/dist/sections/_section-base.scss +204 -0
  254. package/dist/sections/contact/ContactSection.d.ts +55 -0
  255. package/dist/sections/contact/ContactSection.d.ts.map +1 -0
  256. package/dist/sections/contact/ContactSection.js +142 -0
  257. package/dist/sections/contact/ContactSection.js.map +1 -0
  258. package/dist/sections/contact/contact-section.scss +142 -0
  259. package/dist/sections/cta/CTASection.d.ts +52 -0
  260. package/dist/sections/cta/CTASection.d.ts.map +1 -0
  261. package/dist/sections/cta/CTASection.js +130 -0
  262. package/dist/sections/cta/CTASection.js.map +1 -0
  263. package/dist/sections/cta/cta-section.scss +131 -0
  264. package/dist/sections/entrance.d.ts +29 -0
  265. package/dist/sections/entrance.d.ts.map +1 -0
  266. package/dist/sections/entrance.js +69 -0
  267. package/dist/sections/entrance.js.map +1 -0
  268. package/dist/sections/features/FeaturesSection.d.ts +54 -0
  269. package/dist/sections/features/FeaturesSection.d.ts.map +1 -0
  270. package/dist/sections/features/FeaturesSection.js +139 -0
  271. package/dist/sections/features/FeaturesSection.js.map +1 -0
  272. package/dist/sections/features/features-section.scss +140 -0
  273. package/dist/sections/footer/FooterSection.d.ts +53 -0
  274. package/dist/sections/footer/FooterSection.d.ts.map +1 -0
  275. package/dist/sections/footer/FooterSection.js +147 -0
  276. package/dist/sections/footer/FooterSection.js.map +1 -0
  277. package/dist/sections/footer/footer-section.scss +131 -0
  278. package/dist/sections/hero/HeroSection.d.ts +66 -0
  279. package/dist/sections/hero/HeroSection.d.ts.map +1 -0
  280. package/dist/sections/hero/HeroSection.js +156 -0
  281. package/dist/sections/hero/HeroSection.js.map +1 -0
  282. package/dist/sections/hero/hero-section.scss +211 -0
  283. package/dist/shared/_form-animations.scss +283 -0
  284. package/dist/shared/_media-fit.scss +63 -0
  285. package/dist/shared/_mixins.scss +69 -0
  286. package/dist/shared/_tokens.scss +116 -0
  287. package/dist/shared/badge.d.ts +15 -0
  288. package/dist/shared/badge.d.ts.map +1 -0
  289. package/dist/shared/badge.js +15 -0
  290. package/dist/shared/badge.js.map +1 -0
  291. package/dist/shared/button.d.ts +22 -0
  292. package/dist/shared/button.d.ts.map +1 -0
  293. package/dist/shared/button.js +22 -0
  294. package/dist/shared/button.js.map +1 -0
  295. package/dist/shared/card.d.ts +21 -0
  296. package/dist/shared/card.d.ts.map +1 -0
  297. package/dist/shared/card.js +38 -0
  298. package/dist/shared/card.js.map +1 -0
  299. package/dist/shared/checkbox.d.ts +10 -0
  300. package/dist/shared/checkbox.d.ts.map +1 -0
  301. package/dist/shared/checkbox.js +28 -0
  302. package/dist/shared/checkbox.js.map +1 -0
  303. package/dist/shared/clickable.d.ts +34 -0
  304. package/dist/shared/clickable.d.ts.map +1 -0
  305. package/dist/shared/clickable.js +54 -0
  306. package/dist/shared/clickable.js.map +1 -0
  307. package/dist/shared/common.d.ts +31 -0
  308. package/dist/shared/common.d.ts.map +1 -0
  309. package/dist/shared/common.js +92 -0
  310. package/dist/shared/common.js.map +1 -0
  311. package/dist/shared/entrance-config.d.ts +46 -0
  312. package/dist/shared/entrance-config.d.ts.map +1 -0
  313. package/dist/shared/entrance-config.js +54 -0
  314. package/dist/shared/entrance-config.js.map +1 -0
  315. package/dist/shared/focusable.d.ts +8 -0
  316. package/dist/shared/focusable.d.ts.map +1 -0
  317. package/dist/shared/focusable.js +17 -0
  318. package/dist/shared/focusable.js.map +1 -0
  319. package/dist/shared/form-elements.d.ts +57 -0
  320. package/dist/shared/form-elements.d.ts.map +1 -0
  321. package/dist/shared/form-elements.js +95 -0
  322. package/dist/shared/form-elements.js.map +1 -0
  323. package/dist/shared/form-state.d.ts +52 -0
  324. package/dist/shared/form-state.d.ts.map +1 -0
  325. package/dist/shared/form-state.js +151 -0
  326. package/dist/shared/form-state.js.map +1 -0
  327. package/dist/shared/grid.d.ts +21 -0
  328. package/dist/shared/grid.d.ts.map +1 -0
  329. package/dist/shared/grid.js +187 -0
  330. package/dist/shared/grid.js.map +1 -0
  331. package/dist/shared/icon-slot.d.ts +22 -0
  332. package/dist/shared/icon-slot.d.ts.map +1 -0
  333. package/dist/shared/icon-slot.js +38 -0
  334. package/dist/shared/icon-slot.js.map +1 -0
  335. package/dist/shared/icon.d.ts +17 -0
  336. package/dist/shared/icon.d.ts.map +1 -0
  337. package/dist/shared/icon.js +18 -0
  338. package/dist/shared/icon.js.map +1 -0
  339. package/dist/shared/icons.d.ts +19 -0
  340. package/dist/shared/icons.d.ts.map +1 -0
  341. package/dist/shared/icons.js +22 -0
  342. package/dist/shared/icons.js.map +1 -0
  343. package/dist/shared/image.d.ts +35 -0
  344. package/dist/shared/image.d.ts.map +1 -0
  345. package/dist/shared/image.js +46 -0
  346. package/dist/shared/image.js.map +1 -0
  347. package/dist/shared/input.d.ts +12 -0
  348. package/dist/shared/input.d.ts.map +1 -0
  349. package/dist/shared/input.js +29 -0
  350. package/dist/shared/input.js.map +1 -0
  351. package/dist/shared/radio.d.ts +10 -0
  352. package/dist/shared/radio.d.ts.map +1 -0
  353. package/dist/shared/radio.js +29 -0
  354. package/dist/shared/radio.js.map +1 -0
  355. package/dist/shared/reconcile-list.d.ts +37 -0
  356. package/dist/shared/reconcile-list.d.ts.map +1 -0
  357. package/dist/shared/reconcile-list.js +95 -0
  358. package/dist/shared/reconcile-list.js.map +1 -0
  359. package/dist/shared/section.d.ts +23 -0
  360. package/dist/shared/section.d.ts.map +1 -0
  361. package/dist/shared/section.js +65 -0
  362. package/dist/shared/section.js.map +1 -0
  363. package/dist/shared/segmented.d.ts +17 -0
  364. package/dist/shared/segmented.d.ts.map +1 -0
  365. package/dist/shared/segmented.js +53 -0
  366. package/dist/shared/segmented.js.map +1 -0
  367. package/dist/shared/select.d.ts +18 -0
  368. package/dist/shared/select.d.ts.map +1 -0
  369. package/dist/shared/select.js +14 -0
  370. package/dist/shared/select.js.map +1 -0
  371. package/dist/shared/slots.d.ts +11 -0
  372. package/dist/shared/slots.d.ts.map +1 -0
  373. package/dist/shared/slots.js +33 -0
  374. package/dist/shared/slots.js.map +1 -0
  375. package/dist/shared/smart-container.d.ts +19 -0
  376. package/dist/shared/smart-container.d.ts.map +1 -0
  377. package/dist/shared/smart-container.js +29 -0
  378. package/dist/shared/smart-container.js.map +1 -0
  379. package/dist/shared/text.d.ts +29 -0
  380. package/dist/shared/text.d.ts.map +1 -0
  381. package/dist/shared/text.js +72 -0
  382. package/dist/shared/text.js.map +1 -0
  383. package/dist/shared/textarea.d.ts +6 -0
  384. package/dist/shared/textarea.d.ts.map +1 -0
  385. package/dist/shared/textarea.js +18 -0
  386. package/dist/shared/textarea.js.map +1 -0
  387. package/dist/shared/toggle.d.ts +6 -0
  388. package/dist/shared/toggle.d.ts.map +1 -0
  389. package/dist/shared/toggle.js +19 -0
  390. package/dist/shared/toggle.js.map +1 -0
  391. package/dist/shared/types.d.ts +41 -0
  392. package/dist/shared/types.d.ts.map +1 -0
  393. package/dist/shared/types.js +20 -0
  394. package/dist/shared/types.js.map +1 -0
  395. package/dist/structural/ErrorBoundary.d.ts +18 -0
  396. package/dist/structural/ErrorBoundary.d.ts.map +1 -0
  397. package/dist/structural/ErrorBoundary.js +119 -0
  398. package/dist/structural/ErrorBoundary.js.map +1 -0
  399. package/dist/structural/For.d.ts +37 -0
  400. package/dist/structural/For.d.ts.map +1 -0
  401. package/dist/structural/For.js +129 -0
  402. package/dist/structural/For.js.map +1 -0
  403. package/dist/structural/Lazy.d.ts +43 -0
  404. package/dist/structural/Lazy.d.ts.map +1 -0
  405. package/dist/structural/Lazy.js +47 -0
  406. package/dist/structural/Lazy.js.map +1 -0
  407. package/dist/structural/NavLink.d.ts +45 -0
  408. package/dist/structural/NavLink.d.ts.map +1 -0
  409. package/dist/structural/NavLink.js +37 -0
  410. package/dist/structural/NavLink.js.map +1 -0
  411. package/dist/structural/Show.d.ts +35 -0
  412. package/dist/structural/Show.d.ts.map +1 -0
  413. package/dist/structural/Show.js +34 -0
  414. package/dist/structural/Show.js.map +1 -0
  415. package/dist/structural/nav-link.scss +54 -0
  416. package/dist/styles/index.scss +58 -0
  417. package/package.json +64 -0
@@ -0,0 +1,131 @@
1
+ /** @codex
2
+ {
3
+ "id": "pithy.ui-kit.form",
4
+ "title": "<Form> Organism",
5
+ "category": "feature"
6
+ }
7
+ */
8
+ import { renderChildren } from '@pithyjs/core';
9
+ import { captureSlot } from '../../shared/slots';
10
+ import { createFormState } from './form-state';
11
+ import { createFormElement, attachSubmittingEffect, handleValidationFailure, } from './form-helpers';
12
+ // ---------------------------------------------------------------------------
13
+ // Component
14
+ // ---------------------------------------------------------------------------
15
+ /**
16
+ * Semantic `<form>` organism with signal-based state engine.
17
+ *
18
+ * Creates a `<form novalidate>` element, manages submit lifecycle
19
+ * (validate → submit → complete), and exposes `state` on the return
20
+ * object for manual wiring to child form atoms.
21
+ *
22
+ * Children are appended by the caller — Form does not render fields
23
+ * itself. For config-driven field generation, use `<DynamicForm>`.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const form = Form({
28
+ * initialValues: { email: "" },
29
+ * validationRules: { email: [{ type: "required", message: "Required" }] },
30
+ * onSubmit: (values) => console.log(values),
31
+ * });
32
+ * const input = Input({
33
+ * label: "Email",
34
+ * value: form.state.fieldValue("email"),
35
+ * error: form.state.fieldHasError("email"),
36
+ * errorMessage: form.state.fieldErrorMessage("email"),
37
+ * onInput: (val) => form.state.setValue("email", val),
38
+ * onBlur: () => form.state.setTouched("email"),
39
+ * });
40
+ * form.el.appendChild(input.el);
41
+ * ```
42
+ */
43
+ /** @codexApi {"parent":"pithy.ui-kit.form","name":"Form","stability":"stable","signature":"(props: FormProps) => FormResult"} */
44
+ export function Form({ onSubmit, initialValues = undefined, validateOn = undefined, validationRules = undefined, ariaLabel = undefined, ariaLabelledBy = undefined, }) {
45
+ const disposers = [];
46
+ // -- State engine ---------------------------------------------------------
47
+ const state = createFormState({
48
+ initialValues,
49
+ validateOn,
50
+ validationRules,
51
+ });
52
+ // -- DOM ------------------------------------------------------------------
53
+ const { form, statusEl } = createFormElement();
54
+ if (ariaLabel)
55
+ form.setAttribute('aria-label', ariaLabel);
56
+ if (ariaLabelledBy)
57
+ form.setAttribute('aria-labelledby', ariaLabelledBy);
58
+ // -- Reactive submitting state --------------------------------------------
59
+ disposers.push(attachSubmittingEffect(form, state));
60
+ // Fields the consumer has declared either via initialValues or validationRules.
61
+ const trackedFields = new Set([
62
+ ...Object.keys(initialValues ?? {}),
63
+ ...Object.keys(validationRules ?? {}),
64
+ ]);
65
+ // Autofill safety net — see DynamicForm.syncDomToState for the full rationale.
66
+ const syncDomToState = () => {
67
+ const captured = [];
68
+ const inputs = form.querySelectorAll('input[name], textarea[name], select[name]');
69
+ for (const el of inputs) {
70
+ if (el instanceof HTMLInputElement &&
71
+ (el.type === 'checkbox' || el.type === 'radio')) {
72
+ continue;
73
+ }
74
+ const name = el.name;
75
+ if (!trackedFields.has(name))
76
+ continue;
77
+ captured.push([name, el.value]);
78
+ }
79
+ for (const [name, value] of captured) {
80
+ if (value !== state.values()[name]) {
81
+ state.setValue(name, value);
82
+ }
83
+ }
84
+ };
85
+ // -- Submit handler -------------------------------------------------------
86
+ const handleSubmit = async (e) => {
87
+ e.preventDefault();
88
+ if (state.isSubmitting())
89
+ return;
90
+ syncDomToState();
91
+ const valid = state.validateAll();
92
+ if (!valid) {
93
+ handleValidationFailure(form, state, statusEl);
94
+ return;
95
+ }
96
+ statusEl.textContent = '';
97
+ state.isSubmitting.set(true);
98
+ try {
99
+ // Shallow (top-level) copy — new outer object, so consumers can't
100
+ // reassign properties on our internal values map. Nested references
101
+ // would still be shared, but every built-in FieldConfig type produces
102
+ // a primitive value (string/number/boolean), so there's nothing
103
+ // nested to share. Revisit if complex field types are introduced.
104
+ await onSubmit({ ...state.values() });
105
+ }
106
+ catch (err) {
107
+ if (import.meta.env?.DEV) {
108
+ console.error('[PithyJS] Form onSubmit threw:', err);
109
+ }
110
+ }
111
+ finally {
112
+ state.isSubmitting.set(false);
113
+ }
114
+ };
115
+ form.addEventListener('submit', handleSubmit);
116
+ disposers.push(() => form.removeEventListener('submit', handleSubmit));
117
+ // -- Content projection (template children) -------------------------------
118
+ const { el: childFragment, destroy: destroyChildren } = renderChildren();
119
+ captureSlot(form, 'default');
120
+ form.appendChild(childFragment);
121
+ disposers.push(destroyChildren);
122
+ // -- Destroy --------------------------------------------------------------
123
+ function destroy() {
124
+ for (const dispose of disposers)
125
+ dispose();
126
+ disposers.length = 0;
127
+ form.replaceChildren();
128
+ }
129
+ return { el: form, destroy, state };
130
+ }
131
+ //# sourceMappingURL=Form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Form.js","sourceRoot":"","sources":["../../../src/organisms/forms/Form.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ @use 'sass:map';
2
+ @use '@pithyjs/design-system/a11y/styles/mixins' as a11y;
3
+ @use '@pithyjs/design-system/spacing/styles/tokens' as spacing;
4
+ @use '../../shared/tokens' as shared;
5
+
6
+ .ui-dynamic-form-grid {
7
+ display: grid;
8
+ gap: var(--space-md);
9
+ grid-template-columns: 1fr;
10
+ min-inline-size: 0;
11
+ }
12
+
13
+ .ui-dynamic-form {
14
+ container-type: inline-size;
15
+ }
16
+
17
+ // Max of the fluid --space-md range — the runtime gap can be larger than
18
+ // the static default, so using `max` keeps the $min-column floor honored.
19
+ $_max-gap: map.get(map.get(spacing.$spacing-fluid, md), max);
20
+
21
+ // N columns activate when the container fits N × $min-column + (N−1) gaps.
22
+ @for $n from 2 through 4 {
23
+ @container (min-inline-size: #{shared.$min-column * $n + $_max-gap * ($n - 1)}) {
24
+ .ui-dynamic-form-grid[data-columns='#{$n}'] {
25
+ grid-template-columns: repeat($n, 1fr);
26
+ }
27
+ }
28
+ }
29
+
30
+ .ui-dynamic-form-field {
31
+ min-inline-size: 0;
32
+
33
+ &[data-span='full'] {
34
+ grid-column: 1 / -1;
35
+ }
36
+ }
37
+
38
+ // Numeric spans activate only when multi-column layout is active.
39
+ @container (min-inline-size: #{shared.$min-column * 2 + $_max-gap}) {
40
+ @for $n from 2 through 4 {
41
+ .ui-dynamic-form-field[data-span='#{$n}'] {
42
+ grid-column: span $n;
43
+ }
44
+ }
45
+ }
46
+
47
+ // -- Collapsible field (dependsOn) — 0fr→1fr height animation --------------
48
+ .ui-dynamic-form-collapsible {
49
+ border: 0;
50
+ padding: 0;
51
+ margin: 0;
52
+ min-inline-size: 0;
53
+
54
+ display: grid;
55
+ grid-template-rows: 0fr;
56
+ opacity: 0;
57
+ overflow: hidden;
58
+
59
+ @include a11y.motion-safe {
60
+ transition:
61
+ grid-template-rows var(--motion-duration-normal)
62
+ var(--motion-easing-standard),
63
+ opacity var(--motion-duration-normal) var(--motion-easing-standard);
64
+ }
65
+
66
+ > * {
67
+ min-block-size: 0;
68
+ }
69
+
70
+ &-open {
71
+ grid-template-rows: 1fr;
72
+ opacity: 1;
73
+ }
74
+ }
75
+
76
+ .ui-dynamic-form-check {
77
+ display: inline-flex;
78
+ align-items: center;
79
+ inline-size: var(--icon-size-sm);
80
+ block-size: var(--icon-size-sm);
81
+ vertical-align: middle;
82
+ margin-inline-end: var(--space-xs);
83
+ }
84
+
85
+ .ui-dynamic-form-actions {
86
+ grid-column: 1 / -1;
87
+ display: flex;
88
+ justify-content: flex-end;
89
+ gap: var(--space-sm);
90
+ padding-block-start: var(--space-sm);
91
+ }
@@ -0,0 +1,21 @@
1
+ /** @codex { "id": "pithy.ui-kit.form-helpers", "title": "Form DOM Helpers", "category": "feature" } */
2
+ import type { FormState } from './form-state';
3
+ /**
4
+ * Create a `<form novalidate>` element with an aria-live status region.
5
+ * Returns both the form and status elements for further configuration.
6
+ */
7
+ export declare function createFormElement(...classes: string[]): {
8
+ form: HTMLFormElement;
9
+ statusEl: HTMLDivElement;
10
+ };
11
+ /**
12
+ * Sync `data-submitting` and `aria-busy` attributes on the form element
13
+ * reactively from `state.isSubmitting`. Returns an effect disposer.
14
+ */
15
+ export declare function attachSubmittingEffect(form: HTMLFormElement, state: FormState): () => void;
16
+ /**
17
+ * Handle validation failure: announce error count via status region
18
+ * and focus the first invalid field. Returns false (for use as early return).
19
+ */
20
+ export declare function handleValidationFailure(form: HTMLFormElement, state: FormState, statusEl: HTMLElement): false;
21
+ //# sourceMappingURL=form-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-helpers.d.ts","sourceRoot":"","sources":["../../../src/organisms/forms/form-helpers.ts"],"names":[],"mappings":"AAAA,uGAAuG;AAWvG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG;IACvD,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,EAAE,cAAc,CAAC;CAC1B,CAYA;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,eAAe,EACrB,KAAK,EAAE,SAAS,GACf,MAAM,IAAI,CAWZ;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,eAAe,EACrB,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,WAAW,GACpB,KAAK,CAgBP"}
@@ -0,0 +1,57 @@
1
+ /** @codex { "id": "pithy.ui-kit.form-helpers", "title": "Form DOM Helpers", "category": "feature" } */
2
+ /**
3
+ * Shared DOM helpers for Form and DynamicForm organisms.
4
+ * Extracts common setup patterns to avoid duplication.
5
+ *
6
+ * @internal — not part of public API. Exports are consumed only by
7
+ * Form.ts and DynamicForm.ts within this package.
8
+ */
9
+ import { effect } from '@pithyjs/signals';
10
+ /**
11
+ * Create a `<form novalidate>` element with an aria-live status region.
12
+ * Returns both the form and status elements for further configuration.
13
+ */
14
+ export function createFormElement(...classes) {
15
+ const form = document.createElement('form');
16
+ form.noValidate = true;
17
+ form.classList.add('ui-form', ...classes);
18
+ const statusEl = document.createElement('div');
19
+ statusEl.setAttribute('role', 'status');
20
+ statusEl.setAttribute('aria-live', 'polite');
21
+ statusEl.classList.add('u-sr-only');
22
+ form.appendChild(statusEl);
23
+ return { form, statusEl };
24
+ }
25
+ /**
26
+ * Sync `data-submitting` and `aria-busy` attributes on the form element
27
+ * reactively from `state.isSubmitting`. Returns an effect disposer.
28
+ */
29
+ export function attachSubmittingEffect(form, state) {
30
+ return effect(() => {
31
+ const submitting = state.isSubmitting();
32
+ if (submitting) {
33
+ form.setAttribute('data-submitting', '');
34
+ form.setAttribute('aria-busy', 'true');
35
+ }
36
+ else {
37
+ form.removeAttribute('data-submitting');
38
+ form.removeAttribute('aria-busy');
39
+ }
40
+ });
41
+ }
42
+ /**
43
+ * Handle validation failure: announce error count via status region
44
+ * and focus the first invalid field. Returns false (for use as early return).
45
+ */
46
+ export function handleValidationFailure(form, state, statusEl) {
47
+ const errorCount = Object.values(state.errors()).reduce((sum, msgs) => sum + msgs.length, 0);
48
+ statusEl.textContent = `Form has ${errorCount} ${errorCount === 1 ? 'error' : 'errors'}. Please correct and try again.`;
49
+ const FOCUSABLE = "input, select, textarea, button, [tabindex]:not([tabindex='-1'])";
50
+ const firstInvalid = form.querySelector("[aria-invalid='true']");
51
+ const target = firstInvalid?.matches(FOCUSABLE)
52
+ ? firstInvalid
53
+ : firstInvalid?.querySelector(FOCUSABLE);
54
+ target?.focus();
55
+ return false;
56
+ }
57
+ //# sourceMappingURL=form-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-helpers.js","sourceRoot":"","sources":["../../../src/organisms/forms/form-helpers.ts"],"names":[],"mappings":"AAAA,uGAAuG;AAEvG;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAG1C;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAG,OAAiB;IAIpD,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACvB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,OAAO,CAAC,CAAC;IAE1C,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC/C,QAAQ,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACxC,QAAQ,CAAC,YAAY,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAC7C,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACpC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IAE3B,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAqB,EACrB,KAAgB;IAEhB,OAAO,MAAM,CAAC,GAAG,EAAE;QACjB,MAAM,UAAU,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC;QACxC,IAAI,UAAU,EAAE,CAAC;YACf,IAAI,CAAC,YAAY,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;YACzC,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,eAAe,CAAC,iBAAiB,CAAC,CAAC;YACxC,IAAI,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;QACpC,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,IAAqB,EACrB,KAAgB,EAChB,QAAqB;IAErB,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CACrD,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,EAChC,CAAC,CACF,CAAC;IACF,QAAQ,CAAC,WAAW,GAAG,YAAY,UAAU,IAAI,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,iCAAiC,CAAC;IAExH,MAAM,SAAS,GACb,kEAAkE,CAAC;IACrE,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,CAAc,uBAAuB,CAAC,CAAC;IAC9E,MAAM,MAAM,GAAG,YAAY,EAAE,OAAO,CAAC,SAAS,CAAC;QAC7C,CAAC,CAAC,YAAY;QACd,CAAC,CAAC,YAAY,EAAE,aAAa,CAAc,SAAS,CAAC,CAAC;IACxD,MAAM,EAAE,KAAK,EAAE,CAAC;IAEhB,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,66 @@
1
+ /** @codex { "id": "pithy.ui-kit.form-state-engine", "title": "Form State Engine", "category": "feature" } */
2
+ /**
3
+ * Signal-based form state engine.
4
+ * Pure logic — no DOM dependencies. Used by both <Form> and <DynamicForm>.
5
+ */
6
+ import { type Signal } from '@pithyjs/signals';
7
+ import { type ValidationRule } from './validation';
8
+ /** When to run field validation. */
9
+ export type ValidateOn = 'change' | 'blur' | 'submit';
10
+ /** @codexApi {"parent":"pithy.ui-kit.form-state-engine","name":"FormState","stability":"stable","signature":"interface FormState"} */
11
+ export interface FormState {
12
+ /** Current field values */
13
+ values: Signal<Record<string, unknown>>;
14
+ /** Per-field error message arrays. Empty object = no errors. */
15
+ errors: Signal<Record<string, string[]>>;
16
+ /** Which fields the user has interacted with */
17
+ touched: Signal<Record<string, boolean>>;
18
+ /**
19
+ * Which fields have been edited at least once (matches React Hook Form's
20
+ * `dirtyFields` semantics — sticky after first edit, only cleared by `reset()`).
21
+ * Powers the "type-then-clear-then-blur shows required error" UX.
22
+ */
23
+ dirty: Signal<Record<string, boolean>>;
24
+ /** Derived: true when any field has been edited at least once */
25
+ isDirty: () => boolean;
26
+ /** Derived: true when errors object has no keys with non-empty arrays */
27
+ isValid: () => boolean;
28
+ /** True while the onSubmit async handler is executing */
29
+ isSubmitting: Signal<boolean>;
30
+ /** Set a single field value. Triggers validation if validateOn is "change". */
31
+ setValue(field: string, value: unknown): void;
32
+ /** Manually set errors for a field. Pass empty array to clear. */
33
+ setError(field: string, messages: string[]): void;
34
+ /** Mark a field as touched. Triggers validation if validateOn is "blur". */
35
+ setTouched(field: string): void;
36
+ /** Reset all state to initialValues, clear errors and touched. */
37
+ reset(): void;
38
+ /** Signal accessor for a single field's value — plugs into atom value prop */
39
+ fieldValue(field: string): () => unknown;
40
+ /** Signal accessor: is this field in error state? — plugs into atom error prop */
41
+ fieldHasError(field: string): () => boolean;
42
+ /** Signal accessor: first error message — plugs into atom errorMessage prop */
43
+ fieldErrorMessage(field: string): () => string;
44
+ /** Validate a single field against its rules. Returns error messages. */
45
+ validateField(field: string): string[];
46
+ /** Validate all registered fields. Sets errors signal. Returns true if all valid. */
47
+ validateAll(): boolean;
48
+ }
49
+ /** @codexApi {"parent":"pithy.ui-kit.form-state-engine","name":"FormStateOptions","stability":"stable","signature":"interface FormStateOptions"} */
50
+ export interface FormStateOptions {
51
+ /** Initial field values (default: {}) */
52
+ initialValues?: Record<string, unknown>;
53
+ /** When to run validation (default: "submit") */
54
+ validateOn?: ValidateOn;
55
+ /** Validation rules keyed by field name */
56
+ validationRules?: Record<string, ValidationRule[]>;
57
+ }
58
+ /**
59
+ * Create a reactive form state engine.
60
+ *
61
+ * Returns a FormState object with signal-based values, errors, touched,
62
+ * and field accessors that are directly compatible with form atom props.
63
+ */
64
+ /** @codexApi {"parent":"pithy.ui-kit.form-state-engine","name":"createFormState","stability":"stable","signature":"(options?: FormStateOptions) => FormState"} */
65
+ export declare function createFormState(options?: FormStateOptions): FormState;
66
+ //# sourceMappingURL=form-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-state.d.ts","sourceRoot":"","sources":["../../../src/organisms/forms/form-state.ts"],"names":[],"mappings":"AAAA,6GAA6G;AAE7G;;;GAGG;AAEH,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AAMtB,oCAAoC;AACpC,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEtD,sIAAsI;AACtI,MAAM,WAAW,SAAS;IACxB,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxC,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IACzC,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACzC;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACvC,iEAAiE;IACjE,OAAO,EAAE,MAAM,OAAO,CAAC;IACvB,yEAAyE;IACzE,OAAO,EAAE,MAAM,OAAO,CAAC;IACvB,yDAAyD;IACzD,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAE9B,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9C,kEAAkE;IAClE,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAClD,4EAA4E;IAC5E,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,kEAAkE;IAClE,KAAK,IAAI,IAAI,CAAC;IAEd,8EAA8E;IAC9E,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,OAAO,CAAC;IACzC,kFAAkF;IAClF,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,OAAO,CAAC;IAC5C,+EAA+E;IAC/E,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,MAAM,CAAC;IAE/C,yEAAyE;IACzE,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACvC,qFAAqF;IACrF,WAAW,IAAI,OAAO,CAAC;CACxB;AAED,oJAAoJ;AACpJ,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,iDAAiD;IACjD,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,2CAA2C;IAC3C,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC;CACpD;AAMD;;;;;GAKG;AACH,kKAAkK;AAClK,wBAAgB,eAAe,CAAC,OAAO,GAAE,gBAAqB,GAAG,SAAS,CA6JzE"}
@@ -0,0 +1,156 @@
1
+ /** @codex { "id": "pithy.ui-kit.form-state-engine", "title": "Form State Engine", "category": "feature" } */
2
+ /**
3
+ * Signal-based form state engine.
4
+ * Pure logic — no DOM dependencies. Used by both <Form> and <DynamicForm>.
5
+ */
6
+ import { signal, batch } from '@pithyjs/signals';
7
+ import { validateField as runValidation, } from './validation';
8
+ // ---------------------------------------------------------------------------
9
+ // Factory
10
+ // ---------------------------------------------------------------------------
11
+ /**
12
+ * Create a reactive form state engine.
13
+ *
14
+ * Returns a FormState object with signal-based values, errors, touched,
15
+ * and field accessors that are directly compatible with form atom props.
16
+ */
17
+ /** @codexApi {"parent":"pithy.ui-kit.form-state-engine","name":"createFormState","stability":"stable","signature":"(options?: FormStateOptions) => FormState"} */
18
+ export function createFormState(options = {}) {
19
+ const { initialValues: rawInitialValues = {}, validateOn = 'submit', validationRules = {}, } = options;
20
+ // Snapshot initialValues so caller mutations after createFormState() don't
21
+ // change reset() / dirty comparison behavior.
22
+ const initialValues = { ...rawInitialValues };
23
+ // -- Core signals ---------------------------------------------------------
24
+ const values = signal({ ...initialValues });
25
+ const errors = signal({});
26
+ const touched = signal({});
27
+ const dirty = signal({});
28
+ const isSubmitting = signal(false);
29
+ // Derived as plain functions (not computed()) — @pithyjs/signals' computed
30
+ // doesn't return a disposer, so using it would leak an internal effect
31
+ // for every createFormState() call. As plain functions these are still
32
+ // reactive when called inside an effect (they read errors()/dirty() which
33
+ // tracks the dependency normally).
34
+ const isValid = () => {
35
+ const errs = errors();
36
+ return Object.keys(errs).every(key => errs[key].length === 0);
37
+ };
38
+ const isDirty = () => {
39
+ const d = dirty();
40
+ return Object.keys(d).some(key => d[key]);
41
+ };
42
+ // -- Internal validation --------------------------------------------------
43
+ function runFieldValidation(field) {
44
+ const rules = validationRules[field];
45
+ if (!rules || rules.length === 0)
46
+ return [];
47
+ return runValidation(values()[field], rules, values());
48
+ }
49
+ function applyFieldErrors(field, msgs) {
50
+ errors.set(prev => {
51
+ const next = { ...prev };
52
+ if (msgs.length === 0) {
53
+ delete next[field];
54
+ }
55
+ else {
56
+ next[field] = msgs;
57
+ }
58
+ return next;
59
+ });
60
+ }
61
+ // -- Mutations ------------------------------------------------------------
62
+ function setValue(field, value) {
63
+ values.set(prev => ({ ...prev, [field]: value }));
64
+ // `dirty` tracks "has been edited at least once" (not "currently differs
65
+ // from initial"). This matches React Hook Form's `dirtyFields` semantics
66
+ // and powers the "type-then-clear-then-blur shows required error" UX:
67
+ // a user who edits a field and reverts it should still see validation
68
+ // on blur, otherwise required-but-cleared fields look fine.
69
+ // The flag is set on first edit and only reset by `reset()`.
70
+ if (value !== initialValues[field]) {
71
+ dirty.set(prev => ({ ...prev, [field]: true }));
72
+ }
73
+ // Always re-validate on change if the field already has errors (eager clear)
74
+ // Otherwise only validate on change if that's the configured mode
75
+ const hasExistingError = errors()[field]?.length > 0;
76
+ if (validateOn === 'change' || hasExistingError) {
77
+ applyFieldErrors(field, runFieldValidation(field));
78
+ }
79
+ }
80
+ function setError(field, messages) {
81
+ applyFieldErrors(field, messages);
82
+ }
83
+ function setTouched(field) {
84
+ touched.set(prev => ({ ...prev, [field]: true }));
85
+ if (validateOn === 'blur' && dirty()[field]) {
86
+ applyFieldErrors(field, runFieldValidation(field));
87
+ }
88
+ }
89
+ function reset() {
90
+ batch(() => {
91
+ values.set({ ...initialValues });
92
+ errors.set({});
93
+ touched.set({});
94
+ dirty.set({});
95
+ isSubmitting.set(false);
96
+ });
97
+ }
98
+ // -- Field accessors (signal-compatible for atom props) -------------------
99
+ function fieldValue(field) {
100
+ return () => values()[field];
101
+ }
102
+ function fieldHasError(field) {
103
+ return () => {
104
+ const fieldErrors = errors()[field];
105
+ return !!fieldErrors && fieldErrors.length > 0;
106
+ };
107
+ }
108
+ function fieldErrorMessage(field) {
109
+ return () => {
110
+ const fieldErrors = errors()[field];
111
+ return fieldErrors && fieldErrors.length > 0 ? fieldErrors[0] : '';
112
+ };
113
+ }
114
+ // -- Validation -----------------------------------------------------------
115
+ function validateFieldMethod(field) {
116
+ const msgs = runFieldValidation(field);
117
+ applyFieldErrors(field, msgs);
118
+ return msgs;
119
+ }
120
+ function validateAll() {
121
+ const allErrors = {};
122
+ const currentValues = values();
123
+ for (const field of Object.keys(validationRules)) {
124
+ const rules = validationRules[field];
125
+ if (!rules || rules.length === 0)
126
+ continue;
127
+ const msgs = runValidation(currentValues[field], rules, currentValues);
128
+ if (msgs.length > 0) {
129
+ allErrors[field] = msgs;
130
+ }
131
+ }
132
+ // Replace all errors with freshly computed validation results.
133
+ // Manual errors set via setError() are cleared — validateAll() is a full reset.
134
+ errors.set(allErrors);
135
+ return Object.keys(allErrors).length === 0;
136
+ }
137
+ return {
138
+ values,
139
+ errors,
140
+ touched,
141
+ dirty,
142
+ isDirty,
143
+ isValid,
144
+ isSubmitting,
145
+ setValue,
146
+ setError,
147
+ setTouched,
148
+ reset,
149
+ fieldValue,
150
+ fieldHasError,
151
+ fieldErrorMessage,
152
+ validateField: validateFieldMethod,
153
+ validateAll,
154
+ };
155
+ }
156
+ //# sourceMappingURL=form-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-state.js","sourceRoot":"","sources":["../../../src/organisms/forms/form-state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ @use '@pithyjs/design-system/a11y/styles/mixins' as a11y;
2
+
3
+ .ui-form {
4
+ // Minimal base — child atoms handle their own styling.
5
+ // Form only manages the submitting overlay state.
6
+
7
+ &[data-submitting] {
8
+ opacity: var(--state-disabled-opacity);
9
+ pointer-events: none;
10
+
11
+ @include a11y.motion-safe {
12
+ transition: opacity var(--motion-duration-normal)
13
+ var(--motion-easing-standard);
14
+ }
15
+ }
16
+
17
+ // Forced-colors: make submitting state visible via system color
18
+ @include a11y.forced-colors {
19
+ &[data-submitting] {
20
+ color: GrayText;
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,32 @@
1
+ /** @codex { "id": "pithy.ui-kit.validation", "title": "Form Validation Engine", "category": "feature" } */
2
+ /**
3
+ * Pure validation functions for the form system.
4
+ * No DOM dependencies — can be tested and used in isolation.
5
+ */
6
+ /** @codexApi {"parent":"pithy.ui-kit.validation","name":"ValidationRule","stability":"stable","signature":"interface ValidationRule"} */
7
+ export interface ValidationRule {
8
+ /** Built-in rule type or "custom" for user-defined validators */
9
+ type: 'required' | 'minLength' | 'maxLength' | 'min' | 'max' | 'pattern' | 'email' | 'url' | 'custom';
10
+ /** Rule parameter: number for length/min/max, string or RegExp source for pattern */
11
+ value?: unknown;
12
+ /** Error message shown when the rule fails */
13
+ message: string;
14
+ /** Custom validator function (only used when type is "custom") */
15
+ validator?: (value: unknown, allValues: Record<string, unknown>) => string | null;
16
+ }
17
+ /**
18
+ * Validate a single value against one rule.
19
+ * Returns the error message string if the rule fails, or `null` if it passes.
20
+ *
21
+ * Non-required rules are skipped for empty values (standard HTML5 behavior):
22
+ * an empty optional field is valid; the "required" rule handles emptiness.
23
+ */
24
+ /** @codexApi {"parent":"pithy.ui-kit.validation","name":"checkRule","stability":"stable","signature":"(rule: ValidationRule, value: unknown, allValues: Record<string, unknown>) => string | null"} */
25
+ export declare function checkRule(rule: ValidationRule, value: unknown, allValues: Record<string, unknown>): string | null;
26
+ /**
27
+ * Validate a single value against all its rules.
28
+ * Returns an array of error messages (empty array = valid).
29
+ */
30
+ /** @codexApi {"parent":"pithy.ui-kit.validation","name":"validateField","stability":"stable","signature":"(value: unknown, rules: ValidationRule[], allValues: Record<string, unknown>) => string[]"} */
31
+ export declare function validateField(value: unknown, rules: ValidationRule[], allValues: Record<string, unknown>): string[];
32
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/organisms/forms/validation.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAE3G;;;GAGG;AAMH,yIAAyI;AACzI,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,IAAI,EACA,UAAU,GACV,WAAW,GACX,WAAW,GACX,KAAK,GACL,KAAK,GACL,SAAS,GACT,OAAO,GACP,KAAK,GACL,QAAQ,CAAC;IACb,qFAAqF;IACrF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,SAAS,CAAC,EAAE,CACV,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC/B,MAAM,GAAG,IAAI,CAAC;CACpB;AAiBD;;;;;;GAMG;AACH,uMAAuM;AACvM,wBAAgB,SAAS,CACvB,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,MAAM,GAAG,IAAI,CAkFf;AAED;;;GAGG;AACH,yMAAyM;AACzM,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,cAAc,EAAE,EACvB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,MAAM,EAAE,CAOV"}