@mhmo91/schmancy 0.10.15 → 0.10.16

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 (541) hide show
  1. package/custom-elements.json +2554 -3086
  2. package/dist/active-host-BP0zy_Y9.js +63 -0
  3. package/dist/{active-host-CvNYoprt.js.map → active-host-BP0zy_Y9.js.map} +1 -1
  4. package/dist/active-host-jH3iloCR.cjs +1 -0
  5. package/dist/{active-host-CcIa2tmW.cjs.map → active-host-jH3iloCR.cjs.map} +1 -1
  6. package/dist/agent/schmancy.agent.js +2579 -2385
  7. package/dist/agent/schmancy.agent.js.map +1 -1
  8. package/dist/agent/schmancy.manifest.json +971 -1189
  9. package/dist/{animation-CO_Csq84.cjs.map → animation-CCOIW4wJ.cjs.map} +1 -1
  10. package/dist/{animation-BK-8BwY8.js.map → animation-DCznELuT.js.map} +1 -1
  11. package/dist/{area-C_kgZZhN.js → area-ChxsDTu_.js} +2 -2
  12. package/dist/{area-C_kgZZhN.js.map → area-ChxsDTu_.js.map} +1 -1
  13. package/dist/{area-DFPtKzWy.cjs → area-Qt6yUnuA.cjs} +3 -3
  14. package/dist/{area-DFPtKzWy.cjs.map → area-Qt6yUnuA.cjs.map} +1 -1
  15. package/dist/area.cjs +1 -1
  16. package/dist/area.js +2 -2
  17. package/dist/{audio-CluX8Qpq.cjs → audio-D-TZzpXF.cjs} +1 -1
  18. package/dist/{audio-CluX8Qpq.cjs.map → audio-D-TZzpXF.cjs.map} +1 -1
  19. package/dist/{audio-DcXphulJ.js → audio-DS43uoRA.js} +1 -1
  20. package/dist/{audio-DcXphulJ.js.map → audio-DS43uoRA.js.map} +1 -1
  21. package/dist/audio.cjs +1 -1
  22. package/dist/audio.js +2 -2
  23. package/dist/{autocomplete-DWSuwSRS.js → autocomplete-CXvUjMD-.js} +46 -71
  24. package/dist/autocomplete-CXvUjMD-.js.map +1 -0
  25. package/dist/autocomplete-Ck2zbdF9.cjs +115 -0
  26. package/dist/autocomplete-Ck2zbdF9.cjs.map +1 -0
  27. package/dist/autocomplete.cjs +1 -1
  28. package/dist/autocomplete.js +1 -1
  29. package/dist/avatar.cjs +2 -2
  30. package/dist/avatar.cjs.map +1 -1
  31. package/dist/avatar.js +3 -3
  32. package/dist/badge.cjs +1 -1
  33. package/dist/badge.js +1 -1
  34. package/dist/{boat-CZma2ojF.js → boat-Bj0wVcZi.js} +5 -5
  35. package/dist/{boat-CZma2ojF.js.map → boat-Bj0wVcZi.js.map} +1 -1
  36. package/dist/{boat-Dy6cc3hB.cjs → boat-DpFkILFF.cjs} +2 -2
  37. package/dist/{boat-Dy6cc3hB.cjs.map → boat-DpFkILFF.cjs.map} +1 -1
  38. package/dist/boat.cjs +1 -1
  39. package/dist/boat.js +1 -1
  40. package/dist/breadcrumb.cjs +3 -3
  41. package/dist/breadcrumb.cjs.map +1 -1
  42. package/dist/breadcrumb.js +2 -2
  43. package/dist/{busy-DCsqryvq.cjs → busy-CtcnclA3.cjs} +3 -3
  44. package/dist/{busy-DCsqryvq.cjs.map → busy-CtcnclA3.cjs.map} +1 -1
  45. package/dist/{busy-DeV2ByMw.js → busy-CyZSBnZP.js} +2 -2
  46. package/dist/{busy-DeV2ByMw.js.map → busy-CyZSBnZP.js.map} +1 -1
  47. package/dist/busy.cjs +1 -1
  48. package/dist/busy.js +1 -1
  49. package/dist/button.cjs +4 -4
  50. package/dist/button.cjs.map +1 -1
  51. package/dist/button.js +19 -4
  52. package/dist/button.js.map +1 -1
  53. package/dist/{card--GgSX4X5.cjs → card-Cl6jp1yX.cjs} +5 -5
  54. package/dist/{card--GgSX4X5.cjs.map → card-Cl6jp1yX.cjs.map} +1 -1
  55. package/dist/{card-BTTsHzJJ.js → card-nYZCKmOO.js} +3 -3
  56. package/dist/{card-BTTsHzJJ.js.map → card-nYZCKmOO.js.map} +1 -1
  57. package/dist/card.cjs +1 -1
  58. package/dist/card.js +1 -1
  59. package/dist/{checkbox-NNReP9s_.cjs → checkbox-BeNo0ZGt.cjs} +4 -4
  60. package/dist/{checkbox-Cj5j-ppk.js.map → checkbox-BeNo0ZGt.cjs.map} +1 -1
  61. package/dist/{checkbox-Cj5j-ppk.js → checkbox-DiUrZiyc.js} +17 -30
  62. package/dist/checkbox-DiUrZiyc.js.map +1 -0
  63. package/dist/checkbox.cjs +1 -1
  64. package/dist/checkbox.js +1 -1
  65. package/dist/{chips-CP-CbfoZ.js → chips-CfPFXv7Z.js} +5 -5
  66. package/dist/{chips-CP-CbfoZ.js.map → chips-CfPFXv7Z.js.map} +1 -1
  67. package/dist/{chips-iporOXxK.cjs → chips-DK6m-VCM.cjs} +5 -5
  68. package/dist/{chips-iporOXxK.cjs.map → chips-DK6m-VCM.cjs.map} +1 -1
  69. package/dist/chips.cjs +1 -1
  70. package/dist/chips.js +2 -2
  71. package/dist/connectivity.cjs +2 -2
  72. package/dist/connectivity.cjs.map +1 -1
  73. package/dist/connectivity.js +3 -3
  74. package/dist/content-drawer.cjs +1 -1
  75. package/dist/content-drawer.js +1 -1
  76. package/dist/{context-DJTJnSK4.js.map → context-6oXCZmZN.js.map} +1 -1
  77. package/dist/{context-BpCETidA.cjs.map → context-CRZeiCqq.cjs.map} +1 -1
  78. package/dist/{cursor-glow-Bulq-38P.cjs → cursor-glow-C8LgCxpI.cjs} +1 -1
  79. package/dist/{cursor-glow-Bulq-38P.cjs.map → cursor-glow-C8LgCxpI.cjs.map} +1 -1
  80. package/dist/{cursor-glow-Ah7VXSj7.js → cursor-glow-Cs2XLDB9.js} +1 -1
  81. package/dist/{cursor-glow-Ah7VXSj7.js.map → cursor-glow-Cs2XLDB9.js.map} +1 -1
  82. package/dist/date-range-DA6anfcF.cjs +131 -0
  83. package/dist/date-range-DA6anfcF.cjs.map +1 -0
  84. package/dist/{date-range-CgNujP8r.js → date-range-DjlF2u7o.js} +124 -89
  85. package/dist/date-range-DjlF2u7o.js.map +1 -0
  86. package/dist/date-range-inline-BfYK795W.cjs +43 -0
  87. package/dist/{date-range-inline-D4IjOOO0.cjs.map → date-range-inline-BfYK795W.cjs.map} +1 -1
  88. package/dist/{date-range-inline-C2PXX_GY.js → date-range-inline-n7y_H6PJ.js} +2 -2
  89. package/dist/{date-range-inline-C2PXX_GY.js.map → date-range-inline-n7y_H6PJ.js.map} +1 -1
  90. package/dist/date-range-inline.cjs +1 -1
  91. package/dist/date-range-inline.js +1 -1
  92. package/dist/date-range.cjs +1 -1
  93. package/dist/date-range.js +1 -1
  94. package/dist/delay.cjs +2 -2
  95. package/dist/delay.cjs.map +1 -1
  96. package/dist/delay.js +3 -3
  97. package/dist/{details-DT2b3xOn.cjs → details-BdAVsLl-.cjs} +2 -2
  98. package/dist/{details-DT2b3xOn.cjs.map → details-BdAVsLl-.cjs.map} +1 -1
  99. package/dist/{details-VjaNwtfd.js → details-CS_ToAOj.js} +6 -6
  100. package/dist/{details-VjaNwtfd.js.map → details-CS_ToAOj.js.map} +1 -1
  101. package/dist/details.cjs +1 -1
  102. package/dist/details.js +1 -1
  103. package/dist/directives.cjs +1 -1
  104. package/dist/directives.js +5 -5
  105. package/dist/{divider-BMO8pzEO.js → divider-COLK0RbT.js} +2 -2
  106. package/dist/{divider-BMO8pzEO.js.map → divider-COLK0RbT.js.map} +1 -1
  107. package/dist/{divider-BW33TZ-X.cjs → divider-CvWAnvdO.cjs} +2 -2
  108. package/dist/{divider-BW33TZ-X.cjs.map → divider-CvWAnvdO.cjs.map} +1 -1
  109. package/dist/divider.cjs +1 -1
  110. package/dist/divider.js +1 -1
  111. package/dist/dropdown.cjs +3 -3
  112. package/dist/dropdown.cjs.map +1 -1
  113. package/dist/dropdown.js +2 -2
  114. package/dist/{expand-DbELKKOt.js → expand-D9LzmpoV.js} +5 -5
  115. package/dist/{expand-DbELKKOt.js.map → expand-D9LzmpoV.js.map} +1 -1
  116. package/dist/{expand-_f5EUKWB.cjs → expand-r2sATPUJ.cjs} +3 -3
  117. package/dist/{expand-_f5EUKWB.cjs.map → expand-r2sATPUJ.cjs.map} +1 -1
  118. package/dist/expand.cjs +1 -1
  119. package/dist/expand.js +1 -1
  120. package/dist/float-2nHYuBx-.cjs +1 -0
  121. package/dist/{float-CKmd-0-t.cjs.map → float-2nHYuBx-.cjs.map} +1 -1
  122. package/dist/{float-B6RBb2dN.js → float-BWy39CXr.js} +2 -2
  123. package/dist/{float-B6RBb2dN.js.map → float-BWy39CXr.js.map} +1 -1
  124. package/dist/float.cjs +1 -1
  125. package/dist/float.js +1 -1
  126. package/dist/form-DhjedCWm.js +258 -0
  127. package/dist/form-DhjedCWm.js.map +1 -0
  128. package/dist/form-g5c70rac.cjs +42 -0
  129. package/dist/form-g5c70rac.cjs.map +1 -0
  130. package/dist/form.cjs +1 -1
  131. package/dist/form.js +2 -2
  132. package/dist/handover/agent-runtime-followups.md +1 -1
  133. package/dist/handover/agent-runtime-v1.md +3 -3
  134. package/dist/{hashContent-Bobsobip.cjs.map → hashContent-Ck6laKlk.cjs.map} +1 -1
  135. package/dist/{hashContent-BU6jl5ih.js.map → hashContent-dJrI-9sc.js.map} +1 -1
  136. package/dist/{icons-r-S17M8U.cjs → icons-1HIENBco.cjs} +2 -2
  137. package/dist/{icons-r-S17M8U.cjs.map → icons-1HIENBco.cjs.map} +1 -1
  138. package/dist/{icons-CoDo95Cu.js → icons-3y0kr1aB.js} +3 -3
  139. package/dist/{icons-CoDo95Cu.js.map → icons-3y0kr1aB.js.map} +1 -1
  140. package/dist/icons.cjs +1 -1
  141. package/dist/icons.js +1 -1
  142. package/dist/{iframe-P9c_qg1-.cjs → iframe-CjqYuZG5.cjs} +2 -2
  143. package/dist/{iframe-P9c_qg1-.cjs.map → iframe-CjqYuZG5.cjs.map} +1 -1
  144. package/dist/{iframe-k4oI-TIj.js → iframe-Z5gTK-gd.js} +2 -2
  145. package/dist/{iframe-k4oI-TIj.js.map → iframe-Z5gTK-gd.js.map} +1 -1
  146. package/dist/iframe.cjs +1 -1
  147. package/dist/iframe.js +1 -1
  148. package/dist/index.cjs +1 -1
  149. package/dist/index.js +60 -60
  150. package/dist/{input-D95GjINh.js → input-B-fw6f_r.js} +103 -104
  151. package/dist/input-B-fw6f_r.js.map +1 -0
  152. package/dist/input-BtcIhu0Q.cjs +52 -0
  153. package/dist/input-BtcIhu0Q.cjs.map +1 -0
  154. package/dist/{input-chip-DpC_XEKN.js → input-chip-CtQ0pH5b.js} +2 -2
  155. package/dist/{input-chip-DpC_XEKN.js.map → input-chip-CtQ0pH5b.js.map} +1 -1
  156. package/dist/{input-chip-D0ZXqTt5.cjs → input-chip-DZktYohr.cjs} +2 -2
  157. package/dist/{input-chip-D0ZXqTt5.cjs.map → input-chip-DZktYohr.cjs.map} +1 -1
  158. package/dist/input.cjs +1 -1
  159. package/dist/input.js +1 -1
  160. package/dist/json.cjs +2 -2
  161. package/dist/json.cjs.map +1 -1
  162. package/dist/json.js +3 -3
  163. package/dist/kbd.cjs +2 -2
  164. package/dist/kbd.cjs.map +1 -1
  165. package/dist/kbd.js +2 -2
  166. package/dist/{layout-CXPNsUIo.js → layout-BH28sKGc.js} +1 -1
  167. package/dist/{layout-CXPNsUIo.js.map → layout-BH28sKGc.js.map} +1 -1
  168. package/dist/{layout-Zhe7wSZ_.cjs → layout-Delq-QvR.cjs} +1 -1
  169. package/dist/{layout-Zhe7wSZ_.cjs.map → layout-Delq-QvR.cjs.map} +1 -1
  170. package/dist/layout.cjs +1 -1
  171. package/dist/layout.js +1 -1
  172. package/dist/{lazy-Dq9mRRjT.cjs.map → lazy-CayEFyC3.cjs.map} +1 -1
  173. package/dist/{lazy-B0ia54tT.js.map → lazy-D-bO2r4m.js.map} +1 -1
  174. package/dist/{lightbox-C-yHeoK0.cjs → lightbox-BHTZOn8K.cjs} +3 -3
  175. package/dist/{lightbox-C-yHeoK0.cjs.map → lightbox-BHTZOn8K.cjs.map} +1 -1
  176. package/dist/{lightbox-CovQtmyn.js → lightbox-BL3LWp-P.js} +9 -9
  177. package/dist/{lightbox-CovQtmyn.js.map → lightbox-BL3LWp-P.js.map} +1 -1
  178. package/dist/lightbox.cjs +1 -1
  179. package/dist/lightbox.js +1 -1
  180. package/dist/{list-CAijuky4.cjs → list-CHYa5VGY.cjs} +3 -3
  181. package/dist/{list-CAijuky4.cjs.map → list-CHYa5VGY.cjs.map} +1 -1
  182. package/dist/{list-C1pR9vhu.js → list-DLJL1JQj.js} +2 -2
  183. package/dist/{list-C1pR9vhu.js.map → list-DLJL1JQj.js.map} +1 -1
  184. package/dist/list.cjs +1 -1
  185. package/dist/list.js +1 -1
  186. package/dist/{magnetic-BJgB1dVi.cjs → magnetic-Bgh7aHHI.cjs} +1 -1
  187. package/dist/{magnetic-BJgB1dVi.cjs.map → magnetic-Bgh7aHHI.cjs.map} +1 -1
  188. package/dist/{magnetic-YwCNvtbB.js → magnetic-DxvoEz8_.js} +2 -2
  189. package/dist/{magnetic-YwCNvtbB.js.map → magnetic-DxvoEz8_.js.map} +1 -1
  190. package/dist/{menu-B59vZv9n.js → menu-BNq93w6X.js} +3 -3
  191. package/dist/{menu-B59vZv9n.js.map → menu-BNq93w6X.js.map} +1 -1
  192. package/dist/{menu-BaHO3Cip.cjs → menu-DAikvkeV.cjs} +3 -3
  193. package/dist/{menu-BaHO3Cip.cjs.map → menu-DAikvkeV.cjs.map} +1 -1
  194. package/dist/menu.cjs +1 -1
  195. package/dist/menu.js +1 -1
  196. package/dist/mixins-BOOu6q2n.cjs +298 -0
  197. package/dist/mixins-BOOu6q2n.cjs.map +1 -0
  198. package/dist/mixins-BWb9_e1s.js +680 -0
  199. package/dist/mixins-BWb9_e1s.js.map +1 -0
  200. package/dist/mixins.cjs +1 -1
  201. package/dist/mixins.js +2 -2
  202. package/dist/nav-drawer.cjs +1 -1
  203. package/dist/nav-drawer.js +1 -1
  204. package/dist/navigation-bar.cjs +1 -1
  205. package/dist/navigation-bar.js +1 -1
  206. package/dist/navigation-rail.cjs +3 -3
  207. package/dist/navigation-rail.cjs.map +1 -1
  208. package/dist/navigation-rail.js +2 -2
  209. package/dist/{notification-BeLoVa47.js → notification-CUmb9c3Y.js} +4 -4
  210. package/dist/{notification-BeLoVa47.js.map → notification-CUmb9c3Y.js.map} +1 -1
  211. package/dist/notification-Dy2azMyt.cjs +23 -0
  212. package/dist/{notification-BC9nG8Sr.cjs.map → notification-Dy2azMyt.cjs.map} +1 -1
  213. package/dist/notification.cjs +1 -1
  214. package/dist/notification.js +1 -1
  215. package/dist/{option-BWF4GBp-.cjs → option-CDgIKifG.cjs} +2 -2
  216. package/dist/{option-BWF4GBp-.cjs.map → option-CDgIKifG.cjs.map} +1 -1
  217. package/dist/{option-UvlSAcC4.js → option-DFvQ551b.js} +2 -2
  218. package/dist/{option-UvlSAcC4.js.map → option-DFvQ551b.js.map} +1 -1
  219. package/dist/option.cjs +1 -1
  220. package/dist/option.js +1 -1
  221. package/dist/{overlay-stack-DCDS17uj.js.map → overlay-stack-BR4iYivO.js.map} +1 -1
  222. package/dist/{overlay-stack-DPIe_aYv.cjs.map → overlay-stack-Dk0xETTy.cjs.map} +1 -1
  223. package/dist/overlay.cjs +2 -2
  224. package/dist/overlay.cjs.map +1 -1
  225. package/dist/{overlay.confirm-body-URtE1gI3.cjs → overlay.confirm-body-BkhNvr0c.cjs} +2 -2
  226. package/dist/{overlay.confirm-body-URtE1gI3.cjs.map → overlay.confirm-body-BkhNvr0c.cjs.map} +1 -1
  227. package/dist/{overlay.confirm-body-9W0B5QGv.js → overlay.confirm-body-uFp-0Zfh.js} +2 -2
  228. package/dist/{overlay.confirm-body-9W0B5QGv.js.map → overlay.confirm-body-uFp-0Zfh.js.map} +1 -1
  229. package/dist/overlay.js +8 -8
  230. package/dist/{overlay.service-DnZTcKyJ.cjs → overlay.service-1YWfUD2S.cjs} +1 -1
  231. package/dist/{overlay.service-DnZTcKyJ.cjs.map → overlay.service-1YWfUD2S.cjs.map} +1 -1
  232. package/dist/{overlay.service-CVqs2Gu1.js → overlay.service-BcF12kGb.js} +2 -2
  233. package/dist/{overlay.service-CVqs2Gu1.js.map → overlay.service-BcF12kGb.js.map} +1 -1
  234. package/dist/page.cjs +2 -2
  235. package/dist/page.cjs.map +1 -1
  236. package/dist/page.js +5 -5
  237. package/dist/{progress-CwzwY8Oe.cjs → progress-C02sWkmE.cjs} +2 -2
  238. package/dist/{progress-CwzwY8Oe.cjs.map → progress-C02sWkmE.cjs.map} +1 -1
  239. package/dist/{progress-C29Uw-WJ.js → progress-bLbGRuQ1.js} +2 -2
  240. package/dist/{progress-C29Uw-WJ.js.map → progress-bLbGRuQ1.js.map} +1 -1
  241. package/dist/progress.cjs +1 -1
  242. package/dist/progress.js +1 -1
  243. package/dist/radio-group-BA-jRct5.cjs +40 -0
  244. package/dist/radio-group-BA-jRct5.cjs.map +1 -0
  245. package/dist/{radio-group-CW8airhZ.js → radio-group-DA4eIGCj.js} +4 -4
  246. package/dist/radio-group-DA4eIGCj.js.map +1 -0
  247. package/dist/radio-group.cjs +1 -1
  248. package/dist/radio-group.js +1 -1
  249. package/dist/range.cjs +6 -4
  250. package/dist/range.cjs.map +1 -1
  251. package/dist/range.js +19 -15
  252. package/dist/range.js.map +1 -1
  253. package/dist/{reduced-motion-D-L12p7G.js.map → reduced-motion-D7LqTUMn.js.map} +1 -1
  254. package/dist/{reduced-motion-Ds-HjMzn.cjs.map → reduced-motion-Dzfp_w5x.cjs.map} +1 -1
  255. package/dist/{rxjs-utils-CVeJQ9KG.js.map → rxjs-utils-D9U4MW0Q.js.map} +1 -1
  256. package/dist/{rxjs-utils-DCUHg_Ml.cjs.map → rxjs-utils-kWPShgKu.cjs.map} +1 -1
  257. package/dist/rxjs-utils.cjs +1 -1
  258. package/dist/rxjs-utils.js +1 -1
  259. package/dist/{scroll-BotoGcMU.js → scroll-CG5up5oy.js} +2 -2
  260. package/dist/{scroll-BotoGcMU.js.map → scroll-CG5up5oy.js.map} +1 -1
  261. package/dist/{scroll-CmhmUebp.cjs → scroll-D8vBF_gY.cjs} +2 -2
  262. package/dist/{scroll-CmhmUebp.cjs.map → scroll-D8vBF_gY.cjs.map} +1 -1
  263. package/dist/{search-BLCRsxIC.cjs.map → search-DPKoC-dT.cjs.map} +1 -1
  264. package/dist/{search-BTz7-Rev.js.map → search-MvIBA93K.js.map} +1 -1
  265. package/dist/{select-Dbn-CImU.js → select-BrK1BJoU.js} +52 -73
  266. package/dist/select-BrK1BJoU.js.map +1 -0
  267. package/dist/select-Dh2j7Qc-.cjs +56 -0
  268. package/dist/select-Dh2j7Qc-.cjs.map +1 -0
  269. package/dist/select.cjs +1 -1
  270. package/dist/select.js +1 -1
  271. package/dist/skeleton.cjs +2 -2
  272. package/dist/skeleton.cjs.map +1 -1
  273. package/dist/skeleton.js +2 -2
  274. package/dist/skills/autocomplete.md +16 -3
  275. package/dist/skills/button.md +19 -0
  276. package/dist/skills/checkbox.md +19 -0
  277. package/dist/skills/date-range.md +19 -0
  278. package/dist/skills/form-ux-rules.md +55 -0
  279. package/dist/skills/form.md +121 -25
  280. package/dist/skills/input.md +19 -4
  281. package/dist/skills/range.md +15 -1
  282. package/dist/skills/schmancy/autocomplete.md +16 -3
  283. package/dist/skills/schmancy/button.md +19 -0
  284. package/dist/skills/schmancy/checkbox.md +19 -0
  285. package/dist/skills/schmancy/date-range.md +19 -0
  286. package/dist/skills/schmancy/form-ux-rules.md +55 -0
  287. package/dist/skills/schmancy/form.md +121 -25
  288. package/dist/skills/schmancy/input.md +19 -4
  289. package/dist/skills/schmancy/range.md +15 -1
  290. package/dist/skills/schmancy/select.md +13 -1
  291. package/dist/skills/schmancy/switch.md +21 -2
  292. package/dist/skills/schmancy/textarea.md +13 -0
  293. package/dist/skills/select.md +13 -1
  294. package/dist/skills/switch.md +21 -2
  295. package/dist/skills/textarea.md +13 -0
  296. package/dist/slider.cjs +3 -3
  297. package/dist/slider.cjs.map +1 -1
  298. package/dist/slider.js +2 -2
  299. package/dist/{sound.service-kKfsN0m-.js → sound.service-BIN2W7Rv.js} +1 -1
  300. package/dist/{sound.service-kKfsN0m-.js.map → sound.service-BIN2W7Rv.js.map} +1 -1
  301. package/dist/{sound.service-BGs6m0Cm.cjs → sound.service-DyY78ukR.cjs} +1 -1
  302. package/dist/{sound.service-BGs6m0Cm.cjs.map → sound.service-DyY78ukR.cjs.map} +1 -1
  303. package/dist/{splash-screen-DtkjCJYo.js → splash-screen-BcjjJSlK.js} +2 -2
  304. package/dist/{splash-screen-DtkjCJYo.js.map → splash-screen-BcjjJSlK.js.map} +1 -1
  305. package/dist/{splash-screen-DlQUv-kV.cjs → splash-screen-Kr1sPtME.cjs} +2 -2
  306. package/dist/{splash-screen-DlQUv-kV.cjs.map → splash-screen-Kr1sPtME.cjs.map} +1 -1
  307. package/dist/splash-screen.cjs +1 -1
  308. package/dist/splash-screen.js +1 -1
  309. package/dist/{src-DEUjlTsX.cjs → src-B2-CU8fu.cjs} +11 -11
  310. package/dist/{src-DEUjlTsX.cjs.map → src-B2-CU8fu.cjs.map} +1 -1
  311. package/dist/{src-D6e0adHi.js → src-DvywUq7l.js} +38 -38
  312. package/dist/{src-D6e0adHi.js.map → src-DvywUq7l.js.map} +1 -1
  313. package/dist/state-avic94Ft.cjs +1 -0
  314. package/dist/{state-DNdCPITt.cjs.map → state-avic94Ft.cjs.map} +1 -1
  315. package/dist/{state-BusMG6sM.js → state-nm8yzMPp.js} +1 -2
  316. package/dist/{state-BusMG6sM.js.map → state-nm8yzMPp.js.map} +1 -1
  317. package/dist/state.cjs +1 -1
  318. package/dist/state.js +2 -2
  319. package/dist/steps.cjs +3 -3
  320. package/dist/steps.cjs.map +1 -1
  321. package/dist/steps.js +2 -2
  322. package/dist/{surface-A82O1kgu.js → surface-BtMMHKol.js} +2 -2
  323. package/dist/{surface-A82O1kgu.js.map → surface-BtMMHKol.js.map} +1 -1
  324. package/dist/surface-CgXeKdGL.cjs +7 -0
  325. package/dist/{surface-BpppoNXN.cjs.map → surface-CgXeKdGL.cjs.map} +1 -1
  326. package/dist/surface.cjs +1 -1
  327. package/dist/surface.js +1 -1
  328. package/dist/switch.cjs +3 -3
  329. package/dist/switch.cjs.map +1 -1
  330. package/dist/switch.js +27 -43
  331. package/dist/switch.js.map +1 -1
  332. package/dist/table.cjs +3 -3
  333. package/dist/table.cjs.map +1 -1
  334. package/dist/table.js +2 -2
  335. package/dist/{tabs-cVHHd1dY.js → tabs-CikPr7by.js} +2 -2
  336. package/dist/{tabs-cVHHd1dY.js.map → tabs-CikPr7by.js.map} +1 -1
  337. package/dist/{tabs-TO3UiBsm.cjs → tabs-CitVls3_.cjs} +2 -2
  338. package/dist/{tabs-TO3UiBsm.cjs.map → tabs-CitVls3_.cjs.map} +1 -1
  339. package/dist/tabs.cjs +1 -1
  340. package/dist/tabs.js +1 -1
  341. package/dist/teleport.cjs +1 -1
  342. package/dist/teleport.js +1 -1
  343. package/dist/textarea-CqV1wvmB.cjs +43 -0
  344. package/dist/textarea-CqV1wvmB.cjs.map +1 -0
  345. package/dist/textarea-DVkwQSis.js +186 -0
  346. package/dist/textarea-DVkwQSis.js.map +1 -0
  347. package/dist/textarea.cjs +1 -1
  348. package/dist/textarea.js +1 -1
  349. package/dist/{theme-CT408FqH.js → theme-BIWS4TOW.js} +9 -9
  350. package/dist/{theme-CT408FqH.js.map → theme-BIWS4TOW.js.map} +1 -1
  351. package/dist/theme-DMgjiKda.cjs +181 -0
  352. package/dist/{theme-CpuF3D3q.cjs.map → theme-DMgjiKda.cjs.map} +1 -1
  353. package/dist/{theme-button-pTb5-Wxx.js → theme-button-DC_shZ_7.js} +2 -2
  354. package/dist/{theme-button-pTb5-Wxx.js.map → theme-button-DC_shZ_7.js.map} +1 -1
  355. package/dist/theme-button-ENKa3TPT.cjs +8 -0
  356. package/dist/{theme-button-B6Xf-EiH.cjs.map → theme-button-ENKa3TPT.cjs.map} +1 -1
  357. package/dist/theme-button.cjs +1 -1
  358. package/dist/theme-button.js +1 -1
  359. package/dist/theme.cjs +1 -1
  360. package/dist/{theme.interface-B9TjbSBF.js.map → theme.interface-C8OHheXg.js.map} +1 -1
  361. package/dist/{theme.interface-BujperTo.cjs.map → theme.interface-CYo4UpWK.cjs.map} +1 -1
  362. package/dist/theme.js +4 -4
  363. package/dist/{theme.service-DIUo1mBP.js → theme.service-BOWIT_5k.js} +1 -1
  364. package/dist/{theme.service-DIUo1mBP.js.map → theme.service-BOWIT_5k.js.map} +1 -1
  365. package/dist/{theme.service-Cfk88qHK.cjs → theme.service-DkdH1t60.cjs} +1 -1
  366. package/dist/{theme.service-Cfk88qHK.cjs.map → theme.service-DkdH1t60.cjs.map} +1 -1
  367. package/dist/tree.cjs +2 -2
  368. package/dist/tree.cjs.map +1 -1
  369. package/dist/tree.js +2 -2
  370. package/dist/typography.cjs +2 -2
  371. package/dist/typography.cjs.map +1 -1
  372. package/dist/typography.js +2 -2
  373. package/dist/{utils-kND2Z9Xg.js → utils-Cj_nRRyx.js} +2 -2
  374. package/dist/{utils-kND2Z9Xg.js.map → utils-Cj_nRRyx.js.map} +1 -1
  375. package/dist/{utils-Dt5PpmaQ.cjs → utils-D2QUu4-g.cjs} +1 -1
  376. package/dist/{utils-Dt5PpmaQ.cjs.map → utils-D2QUu4-g.cjs.map} +1 -1
  377. package/dist/utils.cjs +1 -1
  378. package/dist/utils.js +4 -4
  379. package/dist/visually-hidden.cjs +2 -2
  380. package/dist/visually-hidden.cjs.map +1 -1
  381. package/dist/visually-hidden.js +2 -2
  382. package/dist/{window-CuBcOxbc.js → window-BTecgE_U.js} +7 -7
  383. package/dist/{window-CuBcOxbc.js.map → window-BTecgE_U.js.map} +1 -1
  384. package/dist/{window-CSKvv4Ts.cjs → window-DGydMS0g.cjs} +2 -2
  385. package/dist/{window-CSKvv4Ts.cjs.map → window-DGydMS0g.cjs.map} +1 -1
  386. package/dist/window.cjs +1 -1
  387. package/dist/window.js +1 -1
  388. package/package.json +1 -1
  389. package/skills/schmancy/autocomplete.md +16 -3
  390. package/skills/schmancy/button.md +19 -0
  391. package/skills/schmancy/checkbox.md +19 -0
  392. package/skills/schmancy/date-range.md +19 -0
  393. package/skills/schmancy/form-ux-rules.md +55 -0
  394. package/skills/schmancy/form.md +121 -25
  395. package/skills/schmancy/input.md +19 -4
  396. package/skills/schmancy/range.md +15 -1
  397. package/skills/schmancy/select.md +13 -1
  398. package/skills/schmancy/switch.md +21 -2
  399. package/skills/schmancy/textarea.md +13 -0
  400. package/src/button/button.test.ts +122 -0
  401. package/src/button/button.ts +36 -0
  402. package/src/{autocomplete → form/fields/autocomplete}/autocomplete.ts +48 -75
  403. package/src/{checkbox → form/fields/checkbox}/checkbox.test.ts +1 -1
  404. package/src/form/fields/checkbox/checkbox.ts +126 -0
  405. package/src/form/fields/date-range/date-range.test.ts +102 -0
  406. package/src/{date-range → form/fields/date-range}/date-range.ts +90 -7
  407. package/src/form/fields/input/input.test.ts +201 -0
  408. package/src/{input → form/fields/input}/input.ts +153 -238
  409. package/src/{radio-group → form/fields/radio-group}/radio-button.ts +1 -1
  410. package/src/{radio-group → form/fields/radio-group}/radio-group.ts +1 -1
  411. package/src/form/fields/range/range.test.ts +90 -0
  412. package/src/{range → form/fields/range}/range.ts +34 -13
  413. package/src/{select → form/fields/select}/select.ts +77 -108
  414. package/src/{switch → form/fields/switch}/switch.test.ts +1 -1
  415. package/src/{switch → form/fields/switch}/switch.ts +71 -51
  416. package/src/form/fields/textarea/textarea.test.ts +54 -0
  417. package/src/{textarea → form/fields/textarea}/textarea.ts +33 -72
  418. package/src/form/form-state.ts +31 -0
  419. package/src/form/form-summary.test.ts +105 -0
  420. package/src/form/form-summary.ts +171 -0
  421. package/src/form/form.test.ts +218 -35
  422. package/src/form/form.ts +330 -99
  423. package/src/form/index.ts +2 -0
  424. package/src/index.ts +9 -9
  425. package/types/mixins/formField.mixin.d.ts +90 -0
  426. package/types/src/button/button.d.ts +9 -0
  427. package/types/src/button/button.test.d.ts +3 -0
  428. package/types/src/{autocomplete → form/fields/autocomplete}/autocomplete.d.ts +6 -15
  429. package/types/src/form/fields/checkbox/checkbox.d.ts +47 -0
  430. package/types/src/{date-range → form/fields/date-range}/date-range.d.ts +22 -4
  431. package/types/src/form/fields/date-range/date-range.test.d.ts +1 -0
  432. package/types/src/{input → form/fields/input}/input.d.ts +20 -45
  433. package/types/src/form/fields/input/input.test.d.ts +1 -0
  434. package/types/src/{radio-group → form/fields/radio-group}/radio-button.d.ts +1 -1
  435. package/types/src/{radio-group → form/fields/radio-group}/radio-group.d.ts +1 -1
  436. package/types/src/form/fields/range/range.d.ts +28 -0
  437. package/types/src/form/fields/range/range.test.d.ts +1 -0
  438. package/types/src/{select → form/fields/select}/select.d.ts +23 -24
  439. package/types/src/form/fields/switch/switch.d.ts +57 -0
  440. package/types/src/{textarea → form/fields/textarea}/textarea.d.ts +6 -39
  441. package/types/src/form/fields/textarea/textarea.test.d.ts +1 -0
  442. package/types/src/form/form-state.d.ts +22 -0
  443. package/types/src/form/form-summary.d.ts +42 -0
  444. package/types/src/form/form-summary.test.d.ts +4 -0
  445. package/types/src/form/form.d.ts +79 -34
  446. package/types/src/form/form.test.d.ts +2 -2
  447. package/types/src/form/index.d.ts +2 -0
  448. package/types/src/index.d.ts +9 -9
  449. package/dist/active-host-CcIa2tmW.cjs +0 -1
  450. package/dist/active-host-CvNYoprt.js +0 -57
  451. package/dist/autocomplete-DWSuwSRS.js.map +0 -1
  452. package/dist/autocomplete-iCJOia-q.cjs +0 -115
  453. package/dist/autocomplete-iCJOia-q.cjs.map +0 -1
  454. package/dist/checkbox-NNReP9s_.cjs.map +0 -1
  455. package/dist/date-range-CaOxwZDq.cjs +0 -131
  456. package/dist/date-range-CaOxwZDq.cjs.map +0 -1
  457. package/dist/date-range-CgNujP8r.js.map +0 -1
  458. package/dist/date-range-inline-D4IjOOO0.cjs +0 -43
  459. package/dist/decorate-23nYs4Le.js +0 -7
  460. package/dist/decorate-DpFmy0nm.cjs +0 -1
  461. package/dist/float-CKmd-0-t.cjs +0 -1
  462. package/dist/form-CFvwnfuJ.js +0 -68
  463. package/dist/form-CFvwnfuJ.js.map +0 -1
  464. package/dist/form-Ceijw1aA.cjs +0 -1
  465. package/dist/form-Ceijw1aA.cjs.map +0 -1
  466. package/dist/input-D95GjINh.js.map +0 -1
  467. package/dist/input-D9s4jDAb.cjs +0 -51
  468. package/dist/input-D9s4jDAb.cjs.map +0 -1
  469. package/dist/mixins-BV0w2yIE.js +0 -627
  470. package/dist/mixins-BV0w2yIE.js.map +0 -1
  471. package/dist/mixins-DvAYa-F7.cjs +0 -298
  472. package/dist/mixins-DvAYa-F7.cjs.map +0 -1
  473. package/dist/notification-BC9nG8Sr.cjs +0 -23
  474. package/dist/radio-group-ByMD6Lsj.cjs +0 -40
  475. package/dist/radio-group-ByMD6Lsj.cjs.map +0 -1
  476. package/dist/radio-group-CW8airhZ.js.map +0 -1
  477. package/dist/select-BdBThja4.cjs +0 -56
  478. package/dist/select-BdBThja4.cjs.map +0 -1
  479. package/dist/select-Dbn-CImU.js.map +0 -1
  480. package/dist/state-DNdCPITt.cjs +0 -1
  481. package/dist/surface-BpppoNXN.cjs +0 -7
  482. package/dist/textarea-B9dy-yec.js +0 -211
  483. package/dist/textarea-B9dy-yec.js.map +0 -1
  484. package/dist/textarea-DFY0Flgv.cjs +0 -39
  485. package/dist/textarea-DFY0Flgv.cjs.map +0 -1
  486. package/dist/theme-CpuF3D3q.cjs +0 -181
  487. package/dist/theme-button-B6Xf-EiH.cjs +0 -8
  488. package/src/checkbox/checkbox.ts +0 -162
  489. package/types/src/checkbox/checkbox.d.ts +0 -71
  490. package/types/src/range/range.d.ts +0 -25
  491. package/types/src/switch/switch.d.ts +0 -53
  492. /package/dist/{animation-CO_Csq84.cjs → animation-CCOIW4wJ.cjs} +0 -0
  493. /package/dist/{animation-BK-8BwY8.js → animation-DCznELuT.js} +0 -0
  494. /package/dist/{context-DJTJnSK4.js → context-6oXCZmZN.js} +0 -0
  495. /package/dist/{context-BpCETidA.cjs → context-CRZeiCqq.cjs} +0 -0
  496. /package/dist/{hashContent-Bobsobip.cjs → hashContent-Ck6laKlk.cjs} +0 -0
  497. /package/dist/{hashContent-BU6jl5ih.js → hashContent-dJrI-9sc.js} +0 -0
  498. /package/dist/{lazy-Dq9mRRjT.cjs → lazy-CayEFyC3.cjs} +0 -0
  499. /package/dist/{lazy-B0ia54tT.js → lazy-D-bO2r4m.js} +0 -0
  500. /package/dist/{overlay-stack-DCDS17uj.js → overlay-stack-BR4iYivO.js} +0 -0
  501. /package/dist/{overlay-stack-DPIe_aYv.cjs → overlay-stack-Dk0xETTy.cjs} +0 -0
  502. /package/dist/{reduced-motion-D-L12p7G.js → reduced-motion-D7LqTUMn.js} +0 -0
  503. /package/dist/{reduced-motion-Ds-HjMzn.cjs → reduced-motion-Dzfp_w5x.cjs} +0 -0
  504. /package/dist/{rxjs-utils-CVeJQ9KG.js → rxjs-utils-D9U4MW0Q.js} +0 -0
  505. /package/dist/{rxjs-utils-DCUHg_Ml.cjs → rxjs-utils-kWPShgKu.cjs} +0 -0
  506. /package/dist/{search-BLCRsxIC.cjs → search-DPKoC-dT.cjs} +0 -0
  507. /package/dist/{search-BTz7-Rev.js → search-MvIBA93K.js} +0 -0
  508. /package/dist/{theme.interface-B9TjbSBF.js → theme.interface-C8OHheXg.js} +0 -0
  509. /package/dist/{theme.interface-BujperTo.cjs → theme.interface-CYo4UpWK.cjs} +0 -0
  510. /package/src/{autocomplete → form/fields/autocomplete}/autocomplete.scss +0 -0
  511. /package/src/{autocomplete → form/fields/autocomplete}/index.ts +0 -0
  512. /package/src/{checkbox → form/fields/checkbox}/index.ts +0 -0
  513. /package/src/{date-range → form/fields/date-range}/date-range-dialog.ts +0 -0
  514. /package/src/{date-range → form/fields/date-range}/date-range-helpers.ts +0 -0
  515. /package/src/{date-range → form/fields/date-range}/date-range-presets.ts +0 -0
  516. /package/src/{date-range → form/fields/date-range}/date-utils.ts +0 -0
  517. /package/src/{date-range → form/fields/date-range}/index.ts +0 -0
  518. /package/src/{input → form/fields/input}/index.ts +0 -0
  519. /package/src/{input → form/fields/input}/input.scss +0 -0
  520. /package/src/{radio-group → form/fields/radio-group}/index.ts +0 -0
  521. /package/src/{radio-group → form/fields/radio-group}/radio-group.scss +0 -0
  522. /package/src/{range → form/fields/range}/index.ts +0 -0
  523. /package/src/{select → form/fields/select}/index.ts +0 -0
  524. /package/src/{switch → form/fields/switch}/index.ts +0 -0
  525. /package/src/{textarea → form/fields/textarea}/index.ts +0 -0
  526. /package/src/{textarea → form/fields/textarea}/textarea.scss +0 -0
  527. /package/types/src/{autocomplete → form/fields/autocomplete}/index.d.ts +0 -0
  528. /package/types/src/{checkbox → form/fields/checkbox}/checkbox.test.d.ts +0 -0
  529. /package/types/src/{checkbox → form/fields/checkbox}/index.d.ts +0 -0
  530. /package/types/src/{date-range → form/fields/date-range}/date-range-dialog.d.ts +0 -0
  531. /package/types/src/{date-range → form/fields/date-range}/date-range-helpers.d.ts +0 -0
  532. /package/types/src/{date-range → form/fields/date-range}/date-range-presets.d.ts +0 -0
  533. /package/types/src/{date-range → form/fields/date-range}/date-utils.d.ts +0 -0
  534. /package/types/src/{date-range → form/fields/date-range}/index.d.ts +0 -0
  535. /package/types/src/{input → form/fields/input}/index.d.ts +0 -0
  536. /package/types/src/{radio-group → form/fields/radio-group}/index.d.ts +0 -0
  537. /package/types/src/{range → form/fields/range}/index.d.ts +0 -0
  538. /package/types/src/{select → form/fields/select}/index.d.ts +0 -0
  539. /package/types/src/{switch → form/fields/switch}/index.d.ts +0 -0
  540. /package/types/src/{switch → form/fields/switch}/switch.test.d.ts +0 -0
  541. /package/types/src/{textarea → form/fields/textarea}/index.d.ts +0 -0
package/src/form/form.ts CHANGED
@@ -1,146 +1,377 @@
1
- import { customElement } from 'lit/decorators.js'
2
-
3
1
  /**
4
- * A thin ergonomic wrapper around a native `<form>` element. Its children are
5
- * reparented into a `<form>` element in light DOM on connection, so:
6
- *
7
- * - Form-associated custom elements (FACE) resolve their `internals.form`
8
- * correctly via native DOM ancestry.
9
- * - `new FormData(form)` collects values from every FACE + native control
10
- * without any manual walking.
11
- * - `form.reset()` triggers `formResetCallback()` on every FACE.
12
- * - `form.reportValidity()` runs native validation UI.
13
- * - `<button type="submit">` and `<schmancy-button type="submit">` both
14
- * submit the form via the native submitter pipeline.
2
+ * `<schmancy-form>` schmancy form owner with isolated submit state.
15
3
  *
16
- * This component exists only to translate the native `submit` / `reset`
17
- * events into the Schmancy event shape (`detail: FormData`). All heavy
18
- * lifting is the platform's.
4
+ * Architecture:
5
+ * - Extends `SchmancyElement` (shadow DOM, RxJS + `disconnecting` conventions).
6
+ * - Renders content inside `<schmancy-context .provides=${[formSubmitState]}>`
7
+ * so each form instance gets an isolated copy of the submit state via the
8
+ * schmancy state library — no `@lit/context` plumbing in user code.
9
+ * - Inner `<form novalidate>` is the native-semantics trigger for Enter-key
10
+ * submit and `type=submit` button activation.
11
+ * - Fields self-register via `FIELD_CONNECT_EVENT` (composed event from
12
+ * `FormFieldMixin.connectedCallback`). Stale refs (from disconnected
13
+ * fields) are filtered by `isConnected` at iteration time.
19
14
  *
20
- * @element schmancy-form
21
- * @fires submit - `CustomEvent<FormData>` emitted when the form is submitted.
22
- * @fires reset - Emitted after the underlying form resets.
15
+ * Schema seam — pass any `{ parse(input): T }` object (zod, valibot, ArkType,
16
+ * etc.) via the `schema` property to get typed `submit` event detail.
23
17
  */
18
+
19
+ import { html } from 'lit'
20
+ import { customElement, property, state } from 'lit/decorators.js'
21
+ import { fromEvent, takeUntil } from 'rxjs'
22
+ import { SchmancyElement } from '../../mixins'
23
+ import { FIELD_CONNECT_EVENT, type IFormFieldMixin } from '../../mixins/formField.mixin'
24
+ import { formSubmitState, type FormSubmitState, type FormError } from './form-state'
25
+
26
+ /** Structural type matching zod, valibot, ArkType, etc. — any schema with `.parse()`. */
27
+ export interface ParseSchema<T = unknown> {
28
+ parse(input: unknown): T
29
+ }
30
+
31
+ const isButton = (node: EventTarget): node is HTMLElement => {
32
+ if (!(node instanceof HTMLElement)) return false
33
+ return node.tagName === 'BUTTON' || node.tagName === 'SCHMANCY-BUTTON'
34
+ }
35
+
36
+ /** Submit event detail. `data` is typed when `schema` is set. */
37
+ export type SchmancyFormSubmitDetail<T = Record<string, FormDataEntryValue>> = {
38
+ data: T
39
+ formData: FormData
40
+ /**
41
+ * Register a promise that gates the form's success/error state. If unused,
42
+ * the form synchronously flips to `success` after dispatch. If used,
43
+ * `success`/`error` reflect the promise's outcome.
44
+ */
45
+ until(p: Promise<unknown>): void
46
+ }
47
+
24
48
  @customElement('schmancy-form')
25
- export default class SchmancyForm extends HTMLElement {
49
+ export default class SchmancyForm<TSchema extends ParseSchema | undefined = undefined>
50
+ extends SchmancyElement {
26
51
  public static readonly tagName: string = 'schmancy-form'
27
52
 
28
- private _form: HTMLFormElement | null = null
29
- private _wrapped = false
53
+ /**
54
+ * Optional schema for parsing FormData on submit. Anything with a
55
+ * `.parse(input)` method works (zod, valibot, ArkType). When set, the
56
+ * `submit` event's `detail.data` is typed `z.infer<TSchema>`.
57
+ */
58
+ @property({ attribute: false })
59
+ schema?: TSchema
30
60
 
31
- /** ElementInternals for `:state(invalid)` / `:state(submitting)` broadcasting. */
32
- private readonly _internals: ElementInternals | undefined = (() => {
33
- try { return this.attachInternals() } catch { return undefined }
34
- })()
61
+ /** Skip built-in browser constraint validation. Mirrors `<form novalidate>`. */
62
+ @property({ type: Boolean })
63
+ novalidate: boolean = true
35
64
 
36
- /** Skip built-in constraint validation on submit. Mirrors `<form novalidate>`. */
37
- get novalidate(): boolean {
38
- return this.hasAttribute('novalidate')
39
- }
40
- set novalidate(value: boolean) {
41
- if (value) this.setAttribute('novalidate', '')
42
- else this.removeAttribute('novalidate')
43
- }
65
+ private _fields = new Set<IFormFieldMixin>()
66
+ private _submitting = false
44
67
 
45
- connectedCallback(): void {
46
- this.ensureForm()
47
- }
68
+ /**
69
+ * Local mirror of the submit-state status — drives the inline live region
70
+ * synchronously. Independent of the schmancy-state library's resolution
71
+ * chain (which has known cross-await fallback semantics) so the AT-facing
72
+ * announcement is always correct for this form instance.
73
+ */
74
+ @state() private _liveStatus: 'idle' | 'submitting' | 'success' | 'error' = 'idle'
75
+ @state() private _liveError: string = ''
76
+ private _internals: ElementInternals | undefined = (() => {
77
+ try {
78
+ return this.attachInternals()
79
+ } catch {
80
+ return undefined
81
+ }
82
+ })()
48
83
 
49
- disconnectedCallback(): void {
50
- if (this._form) {
51
- this._form.removeEventListener('submit', this._onSubmit)
52
- this._form.removeEventListener('reset', this._onReset)
84
+ override connectedCallback(): void {
85
+ super.connectedCallback()
86
+
87
+ // Forbid nested <schmancy-form>.
88
+ if (this.parentElement?.closest('schmancy-form')) {
89
+ console.error('[schmancy-form] nested <schmancy-form> is not supported')
90
+ return
53
91
  }
92
+
93
+ // Field registry — composed event from FormFieldMixin.connectedCallback.
94
+ fromEvent<CustomEvent<IFormFieldMixin>>(this, FIELD_CONNECT_EVENT)
95
+ .pipe(takeUntil(this.disconnecting))
96
+ .subscribe(e => this._fields.add(e.detail))
97
+
98
+ // Submit-trigger interception — slotted descendants live in light DOM
99
+ // while the inner <form> is in shadow DOM, so native form-association
100
+ // across the shadow boundary doesn't work for `<button type=submit>`.
101
+ // Capture clicks on type=submit buttons (native + schmancy-button) and
102
+ // Enter keys on registered fields, then call the inner form's
103
+ // requestSubmit() which fires the same native submit event handler
104
+ // chain that a directly-associated button would have triggered.
105
+ fromEvent<MouseEvent>(this, 'click')
106
+ .pipe(takeUntil(this.disconnecting))
107
+ .subscribe(e => this._maybeRequestSubmit(e))
108
+ fromEvent<KeyboardEvent>(this, 'keydown')
109
+ .pipe(takeUntil(this.disconnecting))
110
+ .subscribe(e => {
111
+ if (e.key !== 'Enter' || e.shiftKey) return
112
+ // Skip Enter inside <textarea> (newline) and contenteditable.
113
+ const target = e.target as HTMLElement | null
114
+ if (target?.tagName === 'TEXTAREA') return
115
+ if (target?.isContentEditable) return
116
+ this._maybeRequestSubmit(e)
117
+ })
54
118
  }
55
119
 
56
- /**
57
- * On first connection, create the internal light-DOM `<form>` and move
58
- * existing children into it. Re-entry is a no-op.
59
- */
60
- private ensureForm(): void {
61
- if (this._wrapped) return
62
-
63
- // Respect an explicit consumer-supplied wrapping <form>.
64
- const existing = Array.from(this.children).find(c => c instanceof HTMLFormElement) as
65
- | HTMLFormElement
66
- | undefined
67
-
68
- const form = existing ?? document.createElement('form')
69
- form.noValidate = true
70
-
71
- if (!existing) {
72
- // Snapshot children because appending mutates `this.childNodes`.
73
- const children = Array.from(this.childNodes)
74
- for (const node of children) form.appendChild(node)
75
- this.appendChild(form)
120
+ private _maybeRequestSubmit(e: Event): void {
121
+ // On click: trigger only when the target is a type=submit button.
122
+ // On keydown(Enter): always trigger if focus is on a registered field.
123
+ // type=reset is handled separately in _maybeReset.
124
+ if (e.type === 'click') {
125
+ const path = e.composedPath()
126
+ const resetBtn = path.find(
127
+ node => isButton(node) && node.getAttribute('type') === 'reset',
128
+ )
129
+ if (resetBtn) {
130
+ e.preventDefault()
131
+ const form = this.shadowRoot?.querySelector('form')
132
+ form?.reset()
133
+ return
134
+ }
135
+ const submitBtn = path.find(
136
+ node => isButton(node) && node.getAttribute('type') === 'submit',
137
+ )
138
+ if (!submitBtn) return
139
+ e.preventDefault()
76
140
  }
141
+ const form = this.shadowRoot?.querySelector('form')
142
+ form?.requestSubmit()
143
+ }
77
144
 
78
- form.addEventListener('submit', this._onSubmit)
79
- form.addEventListener('reset', this._onReset)
80
-
81
- this._form = form
82
- this._wrapped = true
145
+ /** Active fields — drops stale refs from disconnected nodes. */
146
+ private get _activeFields(): IFormFieldMixin[] {
147
+ return [...this._fields].filter(f => f.isConnected)
83
148
  }
84
149
 
85
- private _onSubmit = (e: SubmitEvent): void => {
86
- // Prevent the default navigation AND stop the native submit from
87
- // bubbling past this wrapper — otherwise consumers listening for
88
- // `submit` on <schmancy-form> would see the native event plus our
89
- // CustomEvent (two fires per submission).
150
+ private async _onSubmit(e: SubmitEvent): Promise<void> {
90
151
  e.preventDefault()
91
152
  e.stopPropagation()
92
- if (!this.novalidate && !this._form!.reportValidity()) {
93
- this._internals?.states.add('invalid')
153
+ if (this._submitting) return
154
+
155
+ // If any field has an async validator pending, wait for them all to
156
+ // settle before deciding validity. This is the hard-block model: the
157
+ // user clicks submit, the form holds (aria-busy="true"), waits for
158
+ // validators, then proceeds with the truth they reported.
159
+ const pendingValidators = this._activeFields.filter(f => f.isValidating)
160
+ if (pendingValidators.length > 0) {
161
+ this._broadcastStatus('submitting')
162
+ await new Promise<void>(resolve => {
163
+ const tick = () => {
164
+ if (this._activeFields.every(f => !f.isValidating)) resolve()
165
+ else requestAnimationFrame(tick)
166
+ }
167
+ tick()
168
+ })
169
+ }
170
+
171
+ // Phase 4 — submit forces error display on every field.
172
+ this._activeFields.forEach(f => f.markSubmitted())
173
+
174
+ // Validate; success path entered ONLY if all valid.
175
+ const allValid = this._activeFields.every(f => f.checkValidity())
176
+ const fs = formSubmitState.value
177
+ if (!allValid) {
178
+ formSubmitState.set({
179
+ ...fs,
180
+ status: 'error',
181
+ error: { message: 'Validation failed' },
182
+ })
183
+ this._broadcastStatus('error', 'Validation failed. Please correct the highlighted fields.')
184
+ const firstInvalid = this._activeFields.find(f => f.error) as unknown as
185
+ | HTMLElement
186
+ | undefined
187
+ firstInvalid?.focus()
94
188
  return
95
189
  }
96
- this._internals?.states.delete('invalid')
97
- this._internals?.states.add('submitting')
190
+
191
+ this._submitting = true
192
+ formSubmitState.set({
193
+ ...fs,
194
+ status: 'submitting',
195
+ error: null,
196
+ submitCount: fs.submitCount + 1,
197
+ })
198
+ this._broadcastStatus('submitting')
199
+
200
+ // Build payload from the registered fields' contracted toFormEntries().
201
+ const formData = new FormData()
202
+ for (const field of this._activeFields) {
203
+ for (const [k, v] of field.toFormEntries()) formData.append(k, v)
204
+ }
205
+ const raw = Object.fromEntries(formData)
206
+ const data = this.schema ? this.schema.parse(raw) : raw
207
+
208
+ // Awaitable submit — consumers register promises via e.detail.until(p).
209
+ const pending: Promise<unknown>[] = []
210
+ this.dispatchEvent(
211
+ new CustomEvent<SchmancyFormSubmitDetail<unknown>>('submit', {
212
+ detail: {
213
+ data,
214
+ formData,
215
+ until: (p: Promise<unknown>) => pending.push(p),
216
+ },
217
+ }),
218
+ )
219
+
98
220
  try {
99
- this.dispatchEvent(
100
- new CustomEvent('submit', {
101
- detail: new FormData(this._form!),
102
- }),
103
- )
221
+ await Promise.all(pending)
222
+ formSubmitState.set({
223
+ ...formSubmitState.value,
224
+ status: 'success',
225
+ error: null,
226
+ })
227
+ this._broadcastStatus('success')
228
+ } catch (err) {
229
+ const message = err instanceof Error ? err.message : String(err)
230
+ formSubmitState.set({
231
+ ...formSubmitState.value,
232
+ status: 'error',
233
+ error: { message },
234
+ })
235
+ this._broadcastStatus('error', message)
104
236
  } finally {
105
- this._internals?.states.delete('submitting')
237
+ this._submitting = false
106
238
  }
107
239
  }
108
240
 
109
- private _onReset = (e: Event): void => {
241
+ private _onReset(e: Event): void {
110
242
  e.stopPropagation()
111
- this._internals?.states.delete('invalid')
243
+ this._activeFields.forEach(f => f.resetForm())
244
+ formSubmitState.set({ status: 'idle', error: null, submitCount: 0 })
245
+ this._broadcastStatus('idle')
112
246
  this.dispatchEvent(new CustomEvent('reset'))
113
247
  }
114
248
 
249
+ private _broadcastStatus(status: FormSubmitState['status'], errorMessage?: string): void {
250
+ this._liveStatus = status
251
+ this._liveError = errorMessage ?? ''
252
+ const states = this._internals?.states
253
+ if (states) {
254
+ for (const s of ['submitting', 'success', 'error', 'idle']) states.delete(s)
255
+ states.add(status)
256
+ }
257
+ // aria-busy on the host while submitting (WCAG 2.2 AA — disabled buttons
258
+ // drop from tab order; keep them focusable, signal busy via aria).
259
+ if (status === 'submitting') this.setAttribute('aria-busy', 'true')
260
+ else this.removeAttribute('aria-busy')
261
+ // Public formstate event — external state stores subscribe without
262
+ // having to consume formSubmitState directly.
263
+ this.dispatchEvent(
264
+ new CustomEvent<FormSubmitState>('formstate', {
265
+ detail: { ...formSubmitState.value },
266
+ bubbles: true,
267
+ composed: true,
268
+ }),
269
+ )
270
+ }
271
+
272
+ /**
273
+ * Server-side error mapping (RHF `setError(name, ...)`-equivalent).
274
+ * Sets `setCustomValidity(message)` on the matching field and ensures the
275
+ * error displays by marking it submitted.
276
+ */
277
+ public setFieldError(name: string, message: string): boolean {
278
+ const field = this._activeFields.find(f => f.name === name)
279
+ if (!field) return false
280
+ field.setCustomValidity(message)
281
+ field.markSubmitted()
282
+ return true
283
+ }
284
+
285
+ /**
286
+ * Top-of-form error (RHF `setError('root.serverError', ...)`-equivalent).
287
+ * Flips form status to 'error' with a structured `FormError`.
288
+ */
289
+ public setFormError(message: string, code?: string): void {
290
+ formSubmitState.set({
291
+ ...formSubmitState.value,
292
+ status: 'error',
293
+ error: { message, code } as FormError,
294
+ })
295
+ this._broadcastStatus('error', message)
296
+ }
297
+
298
+ /**
299
+ * Clear the `submitted` flag on every registered field without resetting
300
+ * their values. Wizard pattern: stepping back from a later step should not
301
+ * leave the earlier step's fields in aggressive "show all errors" mode
302
+ * (which `submitted = true` triggers via the `_shouldShowError()` gate).
303
+ *
304
+ * Pristine fields with `validateOn: 'dirty'` go quiet again. Fields the
305
+ * user actually dirtied keep showing their errors (correct UX — those are
306
+ * still genuine mistakes the user can see).
307
+ */
308
+ public clearSubmitted(): void {
309
+ this._activeFields.forEach(f => f.clearSubmitted())
310
+ formSubmitState.set({
311
+ ...formSubmitState.value,
312
+ status: 'idle',
313
+ error: null,
314
+ })
315
+ this._broadcastStatus('idle')
316
+ }
317
+
115
318
  /** Programmatically submit via the native submitter pipeline. */
116
319
  public submit(): boolean {
117
- if (!this._form) return false
118
- if (!this.novalidate && !this._form.reportValidity()) return false
119
- this._form.requestSubmit()
320
+ const form = this.shadowRoot?.querySelector('form')
321
+ if (!form) return false
322
+ form.requestSubmit()
120
323
  return true
121
324
  }
122
325
 
123
326
  /** Programmatically reset via native `form.reset()`. */
124
327
  public reset(): void {
125
- this._form?.reset()
328
+ const form = this.shadowRoot?.querySelector('form')
329
+ form?.reset()
126
330
  }
127
331
 
128
332
  public reportValidity(): boolean {
129
- return this._form?.reportValidity() ?? true
333
+ return this._activeFields.every(f => f.reportValidity())
130
334
  }
131
335
 
132
336
  public checkValidity(): boolean {
133
- return this._form?.checkValidity() ?? true
337
+ return this._activeFields.every(f => f.checkValidity())
134
338
  }
135
339
 
136
- /** Snapshot of current form values. Equivalent to `new FormData(this.form)`. */
340
+ /** Snapshot of current form values from the registered fields. */
137
341
  public getFormData(): FormData {
138
- return this._form ? new FormData(this._form) : new FormData()
342
+ const formData = new FormData()
343
+ for (const field of this._activeFields) {
344
+ for (const [k, v] of field.toFormEntries()) formData.append(k, v)
345
+ }
346
+ return formData
139
347
  }
140
348
 
141
- /** The underlying `<form>` element (escape hatch for advanced integration). */
142
- public get form(): HTMLFormElement | null {
143
- return this._form
349
+ render() {
350
+ return html`
351
+ <schmancy-context .provides=${[formSubmitState]}>
352
+ <form
353
+ ?novalidate=${this.novalidate}
354
+ @submit=${this._onSubmit}
355
+ @reset=${this._onReset}
356
+ >
357
+ <slot></slot>
358
+ </form>
359
+ <!--
360
+ Form-level live region — assistive tech announces server-side
361
+ form errors (validation summary, network failure, server reject)
362
+ here. Visually hidden via the .sr-only convention; consumers
363
+ render their own visible banner from formSubmitState if they
364
+ want one. Empty content while idle/submitting/success — only
365
+ error states populate the region. WCAG 4.1.3 (Status Messages).
366
+ -->
367
+ <div
368
+ role="status"
369
+ aria-live="assertive"
370
+ aria-atomic="true"
371
+ class="sr-only"
372
+ >${this._liveStatus === 'error' ? this._liveError : ''}</div>
373
+ </schmancy-context>
374
+ `
144
375
  }
145
376
  }
146
377
 
@@ -150,10 +381,9 @@ declare global {
150
381
  }
151
382
  }
152
383
 
153
- // === Retained type surface ===
154
- // These interfaces were part of the old collection-based engine. They're kept
155
- // exported because downstream code may still import them as documentation.
156
- // The new implementation no longer uses them internally.
384
+ // Retained type surfaces — kept exported because downstream code may import
385
+ // them as documentation. The new implementation no longer uses them
386
+ // internally.
157
387
 
158
388
  export interface FormElement extends HTMLElement {
159
389
  name?: string
@@ -173,6 +403,7 @@ export interface ValidatableFormElement extends FormElement {
173
403
  }
174
404
 
175
405
  export interface FormEventMap {
176
- submit: CustomEvent<FormData>
406
+ submit: CustomEvent<SchmancyFormSubmitDetail<unknown>>
177
407
  reset: CustomEvent
408
+ formstate: CustomEvent<FormSubmitState>
178
409
  }
package/src/form/index.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  export * from './form'
2
2
  export { default as SchmancyForm } from './form'
3
+ export * from './form-state'
4
+ export * from './form-summary'
package/src/index.ts CHANGED
@@ -1,16 +1,16 @@
1
1
  export * from './area';
2
2
  export * from './audio';
3
- export * from './autocomplete';
3
+ export * from './form/fields/autocomplete';
4
4
  export * from './badge';
5
5
  export * from './boat';
6
6
  export * from './busy';
7
7
  export * from './button';
8
8
  export * from './card';
9
- export * from './checkbox';
9
+ export * from './form/fields/checkbox';
10
10
  export * from './chips';
11
11
  export * from './connectivity';
12
12
  export * from './content-drawer';
13
- export * from './date-range';
13
+ export * from './form/fields/date-range';
14
14
  export * from './date-range-inline';
15
15
  export * from './delay';
16
16
  export * from './details';
@@ -24,7 +24,7 @@ export * from './window';
24
24
  export * from './form';
25
25
  export * from './icons';
26
26
  export * from './iframe';
27
- export * from './input';
27
+ export * from './form/fields/input';
28
28
  export * from './json';
29
29
  export * from './layout';
30
30
  export * from './lightbox';
@@ -37,10 +37,10 @@ export * from './notification';
37
37
  export * from './option';
38
38
  export * from './page';
39
39
  export * from './progress';
40
- export * from './radio-group';
41
- export * from './range';
40
+ export * from './form/fields/radio-group';
41
+ export * from './form/fields/range';
42
42
  export * from './rxjs-utils';
43
- export * from './select';
43
+ export * from './form/fields/select';
44
44
  export * from './slider';
45
45
  export * from './state';
46
46
  export * from './steps';
@@ -48,7 +48,7 @@ export * from './surface';
48
48
  export * from './table';
49
49
  export * from './tabs';
50
50
  export * from './teleport';
51
- export * from './textarea';
51
+ export * from './form/fields/textarea';
52
52
  export * from './theme';
53
53
  export * from './theme-button';
54
54
  export * from './tooltip';
@@ -62,5 +62,5 @@ export * from './breadcrumb';
62
62
  export * from './kbd';
63
63
  export * from './skeleton';
64
64
  export * from './splash-screen';
65
- export * from './switch';
65
+ export * from './form/fields/switch';
66
66
  export * from './visually-hidden';
@@ -8,6 +8,38 @@ import { ITailwindElementMixin } from './tailwind.mixin';
8
8
  * the global registry so detection works across module realms/bundles.
9
9
  */
10
10
  export declare const SCHMANCY_FORM_FIELD: unique symbol;
11
+ /**
12
+ * Composed event a field dispatches in `connectedCallback`. `<schmancy-form>`
13
+ * subscribes via `fromEvent(this, FIELD_CONNECT_EVENT)` and adds the field to
14
+ * its registry. No matching disconnect event — composed events from a
15
+ * disconnected node have an empty composed path; cleanup is via `isConnected`
16
+ * filter at iteration time.
17
+ */
18
+ export declare const FIELD_CONNECT_EVENT = "schmancy:field:connect";
19
+ /**
20
+ * Validation modes.
21
+ * - `dirty` (default per Revolute) — show errors only after value diverged.
22
+ * - `touched` — show errors after first blur, even if value unchanged.
23
+ * - `always` — show errors immediately (live search).
24
+ * - `submitted` — hold all errors until submit (wizard step).
25
+ * - `length` — fire validation only when `value.length` reaches `maxlength`
26
+ * (Stripe pattern for predictable-length fields: ZIP, phone, card number).
27
+ */
28
+ export type ValidateOn = 'always' | 'touched' | 'dirty' | 'submitted' | 'length';
29
+ /**
30
+ * Per-field error-message override map. Keys are `ValidityState` flag names;
31
+ * values are the message to display when that flag is set. Unset flags fall
32
+ * back to the mixin's hardcoded defaults (English).
33
+ *
34
+ * Use for i18n or domain-specific copy:
35
+ * ```ts
36
+ * <schmancy-input
37
+ * .errorMessages=${{ valueMissing: 'Adresse e-mail requise', typeMismatch: 'Format e-mail invalide' }}
38
+ * type="email" required
39
+ * ></schmancy-input>
40
+ * ```
41
+ */
42
+ export type ErrorMessages = Partial<Record<keyof ValidityState, string>>;
11
43
  /**
12
44
  * Interface defining the properties and methods that the FormFieldMixin adds.
13
45
  */
@@ -22,10 +54,68 @@ export interface IFormFieldMixin extends Element {
22
54
  validationMessage: string;
23
55
  hint?: string;
24
56
  id: string;
57
+ /** Set to `true` once the user has blurred the field at least once. */
58
+ touched: boolean;
59
+ /** `true` when the current value differs from the captured default. */
60
+ dirty: boolean;
61
+ /** Inverse of `dirty`. */
62
+ pristine: boolean;
63
+ /** Set to `true` by `<schmancy-form>` on submit (forces error display). */
64
+ submitted: boolean;
65
+ /** Validation mode — controls when errors display. Default `'dirty'`. */
66
+ validateOn: ValidateOn;
67
+ /**
68
+ * Per-field error-message override map (i18n hook). See `ErrorMessages`.
69
+ * Unset keys fall back to the mixin's hardcoded English defaults.
70
+ */
71
+ errorMessages?: ErrorMessages;
72
+ /**
73
+ * `ElementInternals` instance attached by the mixin. Exposed so subclasses
74
+ * with non-standard validity semantics (date-range, multi-select) can
75
+ * surface platform `ValidityStateFlags` directly via
76
+ * `internals.setValidity({ valueMissing: true })` rather than rolling their
77
+ * own `attachInternals` call.
78
+ */
79
+ internals: ElementInternals | undefined;
25
80
  form: HTMLFormElement | null;
26
81
  checkValidity(): boolean;
27
82
  reportValidity(): boolean;
28
83
  setCustomValidity(message: string): void;
84
+ /** Mark the field as touched (component should call on blur). */
85
+ markTouched(): void;
86
+ /** Mark the field as submitted (called by `<schmancy-form>` on submit). */
87
+ markSubmitted(): void;
88
+ /**
89
+ * Clear the `submitted` flag without resetting value/touched/error.
90
+ * Used by wizards: stepping back from step N to step N-1 should not
91
+ * leave step N-1's fields in aggressive "show all errors" mode.
92
+ */
93
+ clearSubmitted(): void;
94
+ /**
95
+ * `true` while an async validator is in flight. Broadcast as
96
+ * `:state(validating)`. `<schmancy-form>` blocks submit until every
97
+ * registered field's `isValidating` is back to `false`.
98
+ */
99
+ isValidating: boolean;
100
+ /**
101
+ * Run an async validator. While the promise is pending, `isValidating` is
102
+ * `true` and `:state(validating)` is broadcast. On resolve, the returned
103
+ * string is passed to `setCustomValidity` — empty string clears any
104
+ * existing custom error; non-empty marks the field invalid with that
105
+ * message.
106
+ *
107
+ * Submitting the form while a validator is pending is queued — the form
108
+ * waits for `Promise.all(pending validators)` to settle before proceeding.
109
+ */
110
+ runAsyncValidator(fn: () => Promise<string>): Promise<void>;
111
+ /**
112
+ * Whether the gate for showing validation errors is open right now. Exposed
113
+ * so subclasses with custom error-display channels (e.g. select renders
114
+ * errors on a child input) can keep their gate consistent with the mixin's.
115
+ * Subclasses should not override this — extend the truth table by changing
116
+ * `validateOn` instead.
117
+ */
118
+ _shouldShowError(): boolean;
29
119
  toFormEntries(): Array<[string, FormDataEntryValue]>;
30
120
  resetForm(): void;
31
121
  emitChange(detail: any): void;