@mhmo91/schmancy 0.10.41 → 0.10.43

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 (764) hide show
  1. package/custom-elements.json +291 -179
  2. package/dist/SchmancyElement-CeKrBW2j.cjs +2 -0
  3. package/dist/SchmancyElement-CeKrBW2j.cjs.map +1 -0
  4. package/dist/SchmancyElement-Ob9yGkiG.js +286 -0
  5. package/dist/SchmancyElement-Ob9yGkiG.js.map +1 -0
  6. package/dist/agent/schmancy.agent.js +4057 -4082
  7. package/dist/agent/schmancy.agent.js.map +1 -1
  8. package/dist/agent/schmancy.manifest.json +225 -94
  9. package/dist/animation-CCOIW4wJ.cjs.map +1 -1
  10. package/dist/animation-DCznELuT.js.map +1 -1
  11. package/dist/area-CbajcnmJ.cjs +21 -0
  12. package/dist/area-CbajcnmJ.cjs.map +1 -0
  13. package/dist/{area-BiM7V2ns.js → area-MxLAyWgV.js} +22 -22
  14. package/dist/area-MxLAyWgV.js.map +1 -0
  15. package/dist/area.cjs +1 -1
  16. package/dist/area.js +1 -1
  17. package/dist/{audio-CxO_j__6.js → audio-B_0PGwYC.js} +1 -1
  18. package/dist/audio-B_0PGwYC.js.map +1 -0
  19. package/dist/{audio-xXFfMPCS.cjs → audio-CpwrIaw-.cjs} +1 -1
  20. package/dist/audio-CpwrIaw-.cjs.map +1 -0
  21. package/dist/audio.cjs +1 -1
  22. package/dist/audio.js +2 -2
  23. package/dist/autocomplete-CILzaDB7.cjs +115 -0
  24. package/dist/autocomplete-CILzaDB7.cjs.map +1 -0
  25. package/dist/{autocomplete-DUBY9RtH.js → autocomplete-DV9RxCun.js} +113 -113
  26. package/dist/autocomplete-DV9RxCun.js.map +1 -0
  27. package/dist/autocomplete.cjs +1 -1
  28. package/dist/autocomplete.js +1 -1
  29. package/dist/avatar.cjs +1 -1
  30. package/dist/avatar.cjs.map +1 -1
  31. package/dist/avatar.js +4 -4
  32. package/dist/avatar.js.map +1 -1
  33. package/dist/badge.cjs +1 -1
  34. package/dist/badge.js +1 -1
  35. package/dist/{boat-ScvAima3.js → boat-DoZGgQ0P.js} +6 -8
  36. package/dist/{boat-ScvAima3.js.map → boat-DoZGgQ0P.js.map} +1 -1
  37. package/dist/{boat-BIYaPAHp.cjs → boat-sg0cWO8a.cjs} +3 -5
  38. package/dist/{boat-BIYaPAHp.cjs.map → boat-sg0cWO8a.cjs.map} +1 -1
  39. package/dist/boat.cjs +1 -1
  40. package/dist/boat.js +1 -1
  41. package/dist/breadcrumb.cjs +32 -31
  42. package/dist/breadcrumb.cjs.map +1 -1
  43. package/dist/breadcrumb.js +33 -32
  44. package/dist/breadcrumb.js.map +1 -1
  45. package/dist/busy-Brs-TDh9.cjs +133 -0
  46. package/dist/busy-Brs-TDh9.cjs.map +1 -0
  47. package/dist/busy-g4LoQmhB.js +172 -0
  48. package/dist/busy-g4LoQmhB.js.map +1 -0
  49. package/dist/busy.cjs +1 -1
  50. package/dist/busy.js +1 -1
  51. package/dist/{button-BTpxQ1Kd.cjs → button-Cml67Y_d.cjs} +41 -32
  52. package/dist/button-Cml67Y_d.cjs.map +1 -0
  53. package/dist/{button-D7QHfYf4.js → button-DWANpZfD.js} +43 -34
  54. package/dist/button-DWANpZfD.js.map +1 -0
  55. package/dist/button.cjs +40 -33
  56. package/dist/button.cjs.map +1 -1
  57. package/dist/button.js +42 -35
  58. package/dist/button.js.map +1 -1
  59. package/dist/calendar-2dJrw9pR.cjs +58 -0
  60. package/dist/calendar-2dJrw9pR.cjs.map +1 -0
  61. package/dist/calendar-kCe5LaBa.js +434 -0
  62. package/dist/calendar-kCe5LaBa.js.map +1 -0
  63. package/dist/calendar.cjs +1 -0
  64. package/dist/calendar.js +2 -0
  65. package/dist/{card-DCdtJ5Dy.js → card-CS_hYJbz.js} +136 -136
  66. package/dist/card-CS_hYJbz.js.map +1 -0
  67. package/dist/card-d0KhTnx5.cjs +177 -0
  68. package/dist/card-d0KhTnx5.cjs.map +1 -0
  69. package/dist/card.cjs +1 -1
  70. package/dist/card.js +1 -1
  71. package/dist/{checkbox-DVtyPk7l.js → checkbox-Bjp7kWuE.js} +2 -2
  72. package/dist/{checkbox-DVtyPk7l.js.map → checkbox-Bjp7kWuE.js.map} +1 -1
  73. package/dist/{checkbox-CYGOVPP-.cjs → checkbox-DZ2Wrz7Y.cjs} +1 -1
  74. package/dist/{checkbox-CYGOVPP-.cjs.map → checkbox-DZ2Wrz7Y.cjs.map} +1 -1
  75. package/dist/checkbox.cjs +1 -1
  76. package/dist/checkbox.js +1 -1
  77. package/dist/{chips-DVes-BSz.cjs → chips-BHBVkxsa.cjs} +142 -153
  78. package/dist/chips-BHBVkxsa.cjs.map +1 -0
  79. package/dist/{chips-C5bpgWyf.js → chips-DACeW7YL.js} +127 -138
  80. package/dist/chips-DACeW7YL.js.map +1 -0
  81. package/dist/chips.cjs +1 -1
  82. package/dist/chips.js +2 -2
  83. package/dist/connectivity.cjs +54 -38
  84. package/dist/connectivity.cjs.map +1 -1
  85. package/dist/connectivity.js +56 -40
  86. package/dist/connectivity.js.map +1 -1
  87. package/dist/content-drawer.cjs +1 -1
  88. package/dist/content-drawer.js +1 -1
  89. package/dist/cursor-glow-Ah7VXSj7.js.map +1 -1
  90. package/dist/cursor-glow-Bulq-38P.cjs.map +1 -1
  91. package/dist/date-range-CVAWMdar.cjs +138 -0
  92. package/dist/date-range-CVAWMdar.cjs.map +1 -0
  93. package/dist/date-range-D2NZU5Yg.js +433 -0
  94. package/dist/date-range-D2NZU5Yg.js.map +1 -0
  95. package/dist/{date-range-inline-Dx4Reboo.cjs → date-range-inline-CGM0SPK9.cjs} +2 -4
  96. package/dist/date-range-inline-CGM0SPK9.cjs.map +1 -0
  97. package/dist/{date-range-inline-DPqY9YYf.js → date-range-inline-D6Ozerzw.js} +3 -5
  98. package/dist/date-range-inline-D6Ozerzw.js.map +1 -0
  99. package/dist/date-range-inline.cjs +1 -1
  100. package/dist/date-range-inline.js +1 -1
  101. package/dist/date-range.cjs +1 -1
  102. package/dist/date-range.js +2 -2
  103. package/dist/dayjs.min-CvRZTfam.cjs +1 -0
  104. package/dist/dayjs.min-CvRZTfam.cjs.map +1 -0
  105. package/dist/dayjs.min-DnELezPs.js +296 -0
  106. package/dist/dayjs.min-DnELezPs.js.map +1 -0
  107. package/dist/delay.cjs +1 -1
  108. package/dist/delay.js +3 -3
  109. package/dist/{details-BnleHmYe.js → details-Ct1_GwKr.js} +104 -92
  110. package/dist/details-Ct1_GwKr.js.map +1 -0
  111. package/dist/{details-Bx2jSJxG.cjs → details-D4fVOaj4.cjs} +109 -97
  112. package/dist/details-D4fVOaj4.cjs.map +1 -0
  113. package/dist/details.cjs +1 -1
  114. package/dist/details.js +1 -1
  115. package/dist/{directives-CYf2fAdA.cjs → directives-B2VxfwRL.cjs} +1 -1
  116. package/dist/directives-B2VxfwRL.cjs.map +1 -0
  117. package/dist/{directives-d1rEbW1A.js → directives-Dj8jlv-Q.js} +3 -3
  118. package/dist/directives-Dj8jlv-Q.js.map +1 -0
  119. package/dist/directives.cjs +1 -1
  120. package/dist/directives.js +2 -2
  121. package/dist/discovery.service-CIa3Eeuk.cjs.map +1 -1
  122. package/dist/discovery.service-DZFxtRwW.js.map +1 -1
  123. package/dist/divider-B3v33OnX.cjs +57 -0
  124. package/dist/divider-B3v33OnX.cjs.map +1 -0
  125. package/dist/divider-BJHW3q11.js +89 -0
  126. package/dist/divider-BJHW3q11.js.map +1 -0
  127. package/dist/divider.cjs +1 -1
  128. package/dist/divider.js +1 -1
  129. package/dist/dropdown.cjs +40 -40
  130. package/dist/dropdown.cjs.map +1 -1
  131. package/dist/dropdown.js +41 -41
  132. package/dist/dropdown.js.map +1 -1
  133. package/dist/{expand-DNrWuG_-.js → expand-BT8mOu8Q.js} +103 -97
  134. package/dist/expand-BT8mOu8Q.js.map +1 -0
  135. package/dist/expand-D0YdR9nR.cjs +147 -0
  136. package/dist/expand-D0YdR9nR.cjs.map +1 -0
  137. package/dist/expand.cjs +1 -1
  138. package/dist/expand.js +1 -1
  139. package/dist/fab.cjs +1 -1
  140. package/dist/fab.cjs.map +1 -1
  141. package/dist/fab.js +2 -2
  142. package/dist/fab.js.map +1 -1
  143. package/dist/{float-V7VQKTb8.cjs → float-BPQlDyai.cjs} +1 -1
  144. package/dist/{float-V7VQKTb8.cjs.map → float-BPQlDyai.cjs.map} +1 -1
  145. package/dist/{float-C_CMle0q.js → float-BQcxj3i_.js} +2 -2
  146. package/dist/{float-C_CMle0q.js.map → float-BQcxj3i_.js.map} +1 -1
  147. package/dist/float.cjs +1 -1
  148. package/dist/float.js +1 -1
  149. package/dist/{form-DaaAQd2A.cjs → form-CoWFnClb.cjs} +8 -13
  150. package/dist/form-CoWFnClb.cjs.map +1 -0
  151. package/dist/{form-CMgYSZ3y.js → form-hC2LvCHX.js} +10 -15
  152. package/dist/form-hC2LvCHX.js.map +1 -0
  153. package/dist/form.cjs +12 -13
  154. package/dist/form.cjs.map +1 -1
  155. package/dist/form.js +23 -24
  156. package/dist/form.js.map +1 -1
  157. package/dist/gravity-6pL6CfIr.cjs.map +1 -1
  158. package/dist/gravity-sVK3zGBF.js.map +1 -1
  159. package/dist/handover/agent-runtime-followups.md +1 -1
  160. package/dist/handover/agent-runtime-v1.md +3 -3
  161. package/dist/{hashContent-iRZJJWtE.cjs.map → hashContent--s09Ed_g.cjs.map} +1 -1
  162. package/dist/{hashContent-BqU6v1Xr.js.map → hashContent-CAvrQ56N.js.map} +1 -1
  163. package/dist/icons-BeGKDZ-k.cjs +22 -0
  164. package/dist/icons-BeGKDZ-k.cjs.map +1 -0
  165. package/dist/{icons-mbpHO_73.js → icons-DEJnIxml.js} +20 -22
  166. package/dist/icons-DEJnIxml.js.map +1 -0
  167. package/dist/icons.cjs +1 -1
  168. package/dist/icons.js +1 -1
  169. package/dist/{iframe-88SN5JPu.cjs → iframe-BkSukM9C.cjs} +9 -9
  170. package/dist/iframe-BkSukM9C.cjs.map +1 -0
  171. package/dist/{iframe-U3P1DnQv.js → iframe-V3S-bwEY.js} +10 -10
  172. package/dist/iframe-V3S-bwEY.js.map +1 -0
  173. package/dist/iframe.cjs +1 -1
  174. package/dist/iframe.js +1 -1
  175. package/dist/index.cjs +1 -1
  176. package/dist/index.js +61 -60
  177. package/dist/{input-CPWvGjE4.js → input-CPFCSQld.js} +3 -10
  178. package/dist/input-CPFCSQld.js.map +1 -0
  179. package/dist/{input-BY4Korc5.cjs → input-DSfwfhhj.cjs} +2 -9
  180. package/dist/input-DSfwfhhj.cjs.map +1 -0
  181. package/dist/input-chip-BUuFJUky.cjs +129 -0
  182. package/dist/input-chip-BUuFJUky.cjs.map +1 -0
  183. package/dist/{input-chip-CCZ3i3Sf.js → input-chip-C3a1fwKB.js} +87 -104
  184. package/dist/input-chip-C3a1fwKB.js.map +1 -0
  185. package/dist/input.cjs +1 -1
  186. package/dist/input.js +1 -1
  187. package/dist/json.cjs +5 -1
  188. package/dist/json.cjs.map +1 -1
  189. package/dist/json.js +8 -4
  190. package/dist/json.js.map +1 -1
  191. package/dist/kbd.cjs +28 -28
  192. package/dist/kbd.cjs.map +1 -1
  193. package/dist/kbd.js +29 -29
  194. package/dist/kbd.js.map +1 -1
  195. package/dist/{layout-BhfC26Ks.cjs → layout-CdyHy_oX.cjs} +1 -1
  196. package/dist/layout-CdyHy_oX.cjs.map +1 -0
  197. package/dist/{layout-DC0Npqu7.js → layout-k6fOkpif.js} +1 -1
  198. package/dist/layout-k6fOkpif.js.map +1 -0
  199. package/dist/layout.cjs +26 -26
  200. package/dist/layout.cjs.map +1 -1
  201. package/dist/layout.js +27 -27
  202. package/dist/layout.js.map +1 -1
  203. package/dist/lazy-BaAiIUru.js.map +1 -1
  204. package/dist/lazy-C-7a4FAe.cjs.map +1 -1
  205. package/dist/{lightbox-BSwWvDQc.js → lightbox-DIG0VLOK.js} +8 -14
  206. package/dist/lightbox-DIG0VLOK.js.map +1 -0
  207. package/dist/{lightbox-KrZQH9w9.cjs → lightbox-E5rgd-hu.cjs} +7 -13
  208. package/dist/lightbox-E5rgd-hu.cjs.map +1 -0
  209. package/dist/lightbox.cjs +1 -1
  210. package/dist/lightbox.js +1 -1
  211. package/dist/list-BRz0rDSH.cjs +47 -0
  212. package/dist/list-BRz0rDSH.cjs.map +1 -0
  213. package/dist/{list-BwGtAAfi.js → list-NrOYDPBo.js} +48 -34
  214. package/dist/list-NrOYDPBo.js.map +1 -0
  215. package/dist/list.cjs +1 -1
  216. package/dist/list.js +1 -1
  217. package/dist/magnetic-DKtc4umC.cjs.map +1 -1
  218. package/dist/magnetic-DaOOv5Dz.js.map +1 -1
  219. package/dist/{menu-DX8d96x-.js → menu-B1Ei9SVj.js} +12 -12
  220. package/dist/menu-B1Ei9SVj.js.map +1 -0
  221. package/dist/{menu-jT_yAk5V.cjs → menu-Duvl66Nl.cjs} +10 -10
  222. package/dist/menu-Duvl66Nl.cjs.map +1 -0
  223. package/dist/menu.cjs +1 -1
  224. package/dist/menu.js +1 -1
  225. package/dist/{mixins-XGVIOvKt.cjs → mixins-Cjn20BQH.cjs} +42 -170
  226. package/dist/mixins-Cjn20BQH.cjs.map +1 -0
  227. package/dist/{mixins-COeG4DiX.js → mixins-q4KAL8Xr.js} +43 -177
  228. package/dist/mixins-q4KAL8Xr.js.map +1 -0
  229. package/dist/mixins.cjs +1 -1
  230. package/dist/mixins.js +2 -2
  231. package/dist/nav-drawer.cjs +1 -1
  232. package/dist/nav-drawer.js +1 -1
  233. package/dist/navigation-bar.cjs +1 -1
  234. package/dist/navigation-bar.js +1 -1
  235. package/dist/navigation-rail.cjs +2 -4
  236. package/dist/navigation-rail.cjs.map +1 -1
  237. package/dist/navigation-rail.js +3 -5
  238. package/dist/navigation-rail.js.map +1 -1
  239. package/dist/{notification-CAJVpLne.js → notification-COhUhUCr.js} +5 -7
  240. package/dist/notification-COhUhUCr.js.map +1 -0
  241. package/dist/{notification-DO3VXceY.cjs → notification-DsdA_MJe.cjs} +2 -4
  242. package/dist/notification-DsdA_MJe.cjs.map +1 -0
  243. package/dist/notification.cjs +1 -1
  244. package/dist/notification.js +1 -1
  245. package/dist/{option-JISY0wZJ.js → option-BIzgTbXz.js} +20 -20
  246. package/dist/option-BIzgTbXz.js.map +1 -0
  247. package/dist/option-C5hhqR2z.cjs +43 -0
  248. package/dist/option-C5hhqR2z.cjs.map +1 -0
  249. package/dist/option.cjs +1 -1
  250. package/dist/option.js +1 -1
  251. package/dist/{overlay-B1jVf-ge.cjs → overlay-BzgF8P7i.cjs} +48 -41
  252. package/dist/overlay-BzgF8P7i.cjs.map +1 -0
  253. package/dist/{overlay-CT-tMHDX.js → overlay-LoRRemny.js} +52 -45
  254. package/dist/overlay-LoRRemny.js.map +1 -0
  255. package/dist/overlay-stack-Bdr9lOqi.cjs.map +1 -1
  256. package/dist/overlay-stack-D2rgxQLh.js.map +1 -1
  257. package/dist/overlay.cjs +1 -1
  258. package/dist/{overlay.confirm-body-Dn-Zgogx.js → overlay.confirm-body-D0b1MoCw.js} +30 -45
  259. package/dist/overlay.confirm-body-D0b1MoCw.js.map +1 -0
  260. package/dist/{overlay.confirm-body-mYDYoJL8.cjs → overlay.confirm-body-DCneq73Z.cjs} +22 -37
  261. package/dist/overlay.confirm-body-DCneq73Z.cjs.map +1 -0
  262. package/dist/overlay.js +3 -3
  263. package/dist/{overlay.service-yqTOyLlr.js → overlay.service-BuUeti6X.js} +2 -2
  264. package/dist/overlay.service-BuUeti6X.js.map +1 -0
  265. package/dist/{overlay.service-BQmva9GY.cjs → overlay.service-MMTiW2T3.cjs} +1 -1
  266. package/dist/overlay.service-MMTiW2T3.cjs.map +1 -0
  267. package/dist/{progress-CGWozq_n.js → progress-D7n3SKAO.js} +50 -50
  268. package/dist/progress-D7n3SKAO.js.map +1 -0
  269. package/dist/progress-DsCnFsH5.cjs +51 -0
  270. package/dist/progress-DsCnFsH5.cjs.map +1 -0
  271. package/dist/progress.cjs +1 -1
  272. package/dist/progress.js +1 -1
  273. package/dist/{radio-group-DoSX5D2V.cjs → radio-group-CaAjg9UV.cjs} +1 -1
  274. package/dist/radio-group-CaAjg9UV.cjs.map +1 -0
  275. package/dist/{radio-group-CXkq6qAF.js → radio-group-Drpl6Pl8.js} +2 -2
  276. package/dist/radio-group-Drpl6Pl8.js.map +1 -0
  277. package/dist/radio-group.cjs +1 -1
  278. package/dist/radio-group.js +1 -1
  279. package/dist/range.cjs +4 -2
  280. package/dist/range.cjs.map +1 -1
  281. package/dist/range.js +5 -3
  282. package/dist/range.js.map +1 -1
  283. package/dist/reduced-motion-D-L12p7G.js.map +1 -1
  284. package/dist/reduced-motion-Ds-HjMzn.cjs.map +1 -1
  285. package/dist/{rxjs-utils-Csnks202.cjs.map → rxjs-utils-4P2v57ke.cjs.map} +1 -1
  286. package/dist/{rxjs-utils-d-ivVN84.js.map → rxjs-utils-JMFdgQSl.js.map} +1 -1
  287. package/dist/rxjs-utils.cjs +1 -1
  288. package/dist/rxjs-utils.js +1 -1
  289. package/dist/search-DPKoC-dT.cjs.map +1 -1
  290. package/dist/search-MvIBA93K.js.map +1 -1
  291. package/dist/{select-CU90i50_.js → select-CTXkrrVZ.js} +13 -13
  292. package/dist/select-CTXkrrVZ.js.map +1 -0
  293. package/dist/select-DcLcpPCh.cjs +56 -0
  294. package/dist/select-DcLcpPCh.cjs.map +1 -0
  295. package/dist/select.cjs +1 -1
  296. package/dist/select.js +1 -1
  297. package/dist/skeleton.cjs +37 -33
  298. package/dist/skeleton.cjs.map +1 -1
  299. package/dist/skeleton.js +38 -34
  300. package/dist/skeleton.js.map +1 -1
  301. package/dist/skills/schmancy/surface.md +79 -39
  302. package/dist/skills/surface.md +79 -39
  303. package/dist/slider.cjs +31 -31
  304. package/dist/slider.cjs.map +1 -1
  305. package/dist/slider.js +32 -32
  306. package/dist/slider.js.map +1 -1
  307. package/dist/{sound.service-m8WjOhjn.js → sound.service-AJwuk3yr.js} +1 -1
  308. package/dist/sound.service-AJwuk3yr.js.map +1 -0
  309. package/dist/{sound.service-Qhr8nCeG.cjs → sound.service-CVsxhQkX.cjs} +1 -1
  310. package/dist/sound.service-CVsxhQkX.cjs.map +1 -0
  311. package/dist/{splash-screen-Cs3dbPN3.js → splash-screen-DANfqvlo.js} +23 -26
  312. package/dist/splash-screen-DANfqvlo.js.map +1 -0
  313. package/dist/splash-screen-K74cgU6S.cjs +38 -0
  314. package/dist/splash-screen-K74cgU6S.cjs.map +1 -0
  315. package/dist/splash-screen.cjs +1 -1
  316. package/dist/splash-screen.js +1 -1
  317. package/dist/{src-CCVbLLgC.js → src-DAtcPmCb.js} +295 -389
  318. package/dist/src-DAtcPmCb.js.map +1 -0
  319. package/dist/src-DuRvYagm.cjs +237 -0
  320. package/dist/src-DuRvYagm.cjs.map +1 -0
  321. package/dist/{state--x58-AuK.cjs → state-BWQiqN6I.cjs} +1 -1
  322. package/dist/state-BWQiqN6I.cjs.map +1 -0
  323. package/dist/{state-QSwQ61sA.js → state-DBA_gzJO.js} +1 -1
  324. package/dist/state-DBA_gzJO.js.map +1 -0
  325. package/dist/state.cjs +1 -1
  326. package/dist/state.js +2 -2
  327. package/dist/steps.cjs +20 -12
  328. package/dist/steps.cjs.map +1 -1
  329. package/dist/steps.js +21 -13
  330. package/dist/steps.js.map +1 -1
  331. package/dist/surface-COBvWWFb.cjs +7 -0
  332. package/dist/surface-COBvWWFb.cjs.map +1 -0
  333. package/dist/{surface-cqMsHJHM.js → surface-DXk1X1tL.js} +9 -9
  334. package/dist/surface-DXk1X1tL.js.map +1 -0
  335. package/dist/surface.cjs +1 -1
  336. package/dist/surface.js +1 -1
  337. package/dist/switch.cjs +13 -4
  338. package/dist/switch.cjs.map +1 -1
  339. package/dist/switch.js +14 -5
  340. package/dist/switch.js.map +1 -1
  341. package/dist/table.cjs +4 -10
  342. package/dist/table.cjs.map +1 -1
  343. package/dist/table.js +5 -11
  344. package/dist/table.js.map +1 -1
  345. package/dist/{tabs-Ib0Mh__1.js → tabs-BYhFWnsx.js} +7 -7
  346. package/dist/tabs-BYhFWnsx.js.map +1 -0
  347. package/dist/{tabs-Dk9UDWpq.cjs → tabs-CJwB0fr0.cjs} +6 -6
  348. package/dist/tabs-CJwB0fr0.cjs.map +1 -0
  349. package/dist/tabs.cjs +1 -1
  350. package/dist/tabs.js +1 -1
  351. package/dist/teleport.cjs +1 -1
  352. package/dist/teleport.js +1 -1
  353. package/dist/{textarea-CcRsw08B.js → textarea-BjDx1w2g.js} +37 -41
  354. package/dist/textarea-BjDx1w2g.js.map +1 -0
  355. package/dist/{textarea-Cntd9tfV.cjs → textarea-CGgznhd6.cjs} +36 -40
  356. package/dist/textarea-CGgznhd6.cjs.map +1 -0
  357. package/dist/textarea.cjs +1 -1
  358. package/dist/textarea.js +1 -1
  359. package/dist/{theme-wwFbvp5e.cjs → theme-BVul7lHS.cjs} +6 -6
  360. package/dist/{theme-wwFbvp5e.cjs.map → theme-BVul7lHS.cjs.map} +1 -1
  361. package/dist/{theme-CKYXG0le.js → theme-Dvm5J8nh.js} +11 -11
  362. package/dist/{theme-CKYXG0le.js.map → theme-Dvm5J8nh.js.map} +1 -1
  363. package/dist/{theme-button-iLqT56KA.js → theme-button-Bko5ohFP.js} +2 -2
  364. package/dist/{theme-button-iLqT56KA.js.map → theme-button-Bko5ohFP.js.map} +1 -1
  365. package/dist/{theme-button-DE9Lrl7m.cjs → theme-button-YLY7zR1c.cjs} +1 -1
  366. package/dist/{theme-button-DE9Lrl7m.cjs.map → theme-button-YLY7zR1c.cjs.map} +1 -1
  367. package/dist/theme-button.cjs +1 -1
  368. package/dist/theme-button.js +1 -1
  369. package/dist/theme.cjs +1 -1
  370. package/dist/{theme.interface-CSt7JUBD.cjs.map → theme.interface-B-qxDsZQ.cjs.map} +1 -1
  371. package/dist/{theme.interface-odQEpZZH.js.map → theme.interface-B7caS5cg.js.map} +1 -1
  372. package/dist/theme.js +4 -4
  373. package/dist/{theme.service-5RjyR7Sy.js → theme.service-D94nm7Bf.js} +1 -1
  374. package/dist/theme.service-D94nm7Bf.js.map +1 -0
  375. package/dist/{theme.service-DA6KY52G.cjs → theme.service-h2fXQq7x.cjs} +1 -1
  376. package/dist/theme.service-h2fXQq7x.cjs.map +1 -0
  377. package/dist/tooltip.cjs.map +1 -1
  378. package/dist/tooltip.js.map +1 -1
  379. package/dist/tree.cjs +14 -14
  380. package/dist/tree.cjs.map +1 -1
  381. package/dist/tree.js +15 -15
  382. package/dist/tree.js.map +1 -1
  383. package/dist/types.cjs +1 -1
  384. package/dist/types.cjs.map +1 -1
  385. package/dist/types.js +19 -2
  386. package/dist/types.js.map +1 -1
  387. package/dist/typewriter.cjs.map +1 -1
  388. package/dist/typewriter.js.map +1 -1
  389. package/dist/typography-ByF2k5yW.js +358 -0
  390. package/dist/typography-ByF2k5yW.js.map +1 -0
  391. package/dist/typography-Cfav17it.cjs +282 -0
  392. package/dist/typography-Cfav17it.cjs.map +1 -0
  393. package/dist/typography.cjs +1 -1
  394. package/dist/typography.js +1 -1
  395. package/dist/utils-DIXndz6Q.cjs.map +1 -0
  396. package/dist/utils-dSPH7Oh9.js.map +1 -0
  397. package/dist/utils.cjs +1 -1
  398. package/dist/utils.js +1 -1
  399. package/dist/visually-hidden.cjs +13 -13
  400. package/dist/visually-hidden.cjs.map +1 -1
  401. package/dist/visually-hidden.js +14 -14
  402. package/dist/visually-hidden.js.map +1 -1
  403. package/dist/window-Br1OmpL-.cjs +67 -0
  404. package/dist/window-Br1OmpL-.cjs.map +1 -0
  405. package/dist/{window-Db5ZYY6t.js → window-CCmN4but.js} +28 -20
  406. package/dist/window-CCmN4but.js.map +1 -0
  407. package/dist/window.cjs +1 -1
  408. package/dist/window.js +1 -1
  409. package/package.json +1 -1
  410. package/skills/schmancy/surface.md +79 -39
  411. package/src/area/area.component.ts +249 -163
  412. package/src/area/area.service.test.ts +994 -1006
  413. package/src/area/area.service.ts +76 -72
  414. package/src/area/index.ts +7 -7
  415. package/src/area/lazy.ts +39 -42
  416. package/src/area/route.component.ts +54 -52
  417. package/src/area/router.types.ts +7 -7
  418. package/src/audio/emotional-sounds.ts +880 -801
  419. package/src/audio/sound.service.ts +26 -5
  420. package/src/avatar/avatar.ts +4 -4
  421. package/src/badge/badge.ts +60 -209
  422. package/src/badge/index.ts +1 -1
  423. package/src/boat/boat.ts +2 -4
  424. package/src/breadcrumb/breadcrumb.ts +37 -32
  425. package/src/busy/busy.ts +15 -12
  426. package/src/busy/index.ts +2 -2
  427. package/src/busy/spinner.ts +132 -119
  428. package/src/button/button.test.ts +5 -1
  429. package/src/button/button.ts +113 -72
  430. package/src/button/icon-button.ts +64 -46
  431. package/src/button/index.ts +2 -2
  432. package/src/calendar/calendar.test.ts +504 -0
  433. package/src/calendar/calendar.ts +587 -0
  434. package/src/calendar/index.ts +2 -0
  435. package/src/card/actions.ts +11 -9
  436. package/src/card/card.ts +79 -77
  437. package/src/card/content.ts +8 -6
  438. package/src/card/index.ts +4 -4
  439. package/src/card/media.ts +50 -50
  440. package/src/chips/assist-chip.ts +1 -1
  441. package/src/chips/chips.ts +1 -1
  442. package/src/chips/filter-chip.ts +1 -1
  443. package/src/chips/index.ts +1 -1
  444. package/src/chips/input-chip.ts +1 -1
  445. package/src/chips/suggestion-chip.ts +1 -1
  446. package/src/connectivity/connectivity-status.ts +91 -65
  447. package/src/content-drawer/drawer.service.ts +47 -36
  448. package/src/content-drawer/drawer.ts +31 -18
  449. package/src/content-drawer/index.ts +5 -5
  450. package/src/content-drawer/main.ts +9 -7
  451. package/src/content-drawer/sheet.ts +8 -6
  452. package/src/date-range-inline/date-range-inline.ts +523 -522
  453. package/src/date-range-inline/index.ts +2 -2
  454. package/src/delay/index.ts +1 -1
  455. package/src/details/details.ts +119 -105
  456. package/src/details/index.ts +1 -1
  457. package/src/directives/ai-badge.ts +1 -5
  458. package/src/directives/animate-text.ts +43 -31
  459. package/src/directives/art/effects/howl.ts +1 -4
  460. package/src/directives/art/effects/samwa.ts +11 -5
  461. package/src/directives/art/effects/snow.ts +1 -2
  462. package/src/directives/battery.ts +21 -12
  463. package/src/directives/confirm-click.ts +9 -17
  464. package/src/directives/cursor-glow.ts +1 -1
  465. package/src/directives/cycle-text.ts +7 -21
  466. package/src/directives/drag.ts +50 -45
  467. package/src/directives/fyi.ts +1 -6
  468. package/src/directives/gravity.ts +10 -8
  469. package/src/directives/hummingbird.ts +256 -107
  470. package/src/directives/index.ts +29 -29
  471. package/src/directives/intersect.ts +11 -11
  472. package/src/directives/layout.ts +25 -36
  473. package/src/directives/liquid.ts +4 -10
  474. package/src/directives/living-border.ts +7 -5
  475. package/src/directives/long-press.ts +2 -1
  476. package/src/directives/magnetic.ts +7 -6
  477. package/src/directives/nebula.ts +34 -28
  478. package/src/directives/overflow-within.ts +18 -20
  479. package/src/directives/reduced-motion.ts +9 -9
  480. package/src/directives/reveal.ts +193 -195
  481. package/src/directives/ripple.ts +9 -7
  482. package/src/directives/urgent.ts +3 -1
  483. package/src/directives/working-snake.ts +1 -1
  484. package/src/discovery/discovery.service.ts +198 -210
  485. package/src/discovery/index.ts +1 -1
  486. package/src/divider/divider.ts +49 -47
  487. package/src/divider/index.ts +1 -1
  488. package/src/dropdown/dropdown-content.ts +42 -40
  489. package/src/dropdown/index.ts +2 -2
  490. package/src/expand/expand-root.component.ts +70 -61
  491. package/src/expand/expand.component.ts +48 -43
  492. package/src/fab/fab.test.ts +1 -2
  493. package/src/fab/fab.ts +2 -4
  494. package/src/form/fields/autocomplete/autocomplete.scss +6 -2
  495. package/src/form/fields/autocomplete/autocomplete.ts +712 -724
  496. package/src/form/fields/autocomplete/index.ts +1 -1
  497. package/src/form/fields/checkbox/checkbox.ts +1 -4
  498. package/src/form/fields/chips/assist-chip.ts +69 -72
  499. package/src/form/fields/chips/chips.ts +14 -15
  500. package/src/form/fields/chips/filter-chip.ts +36 -32
  501. package/src/form/fields/chips/index.ts +5 -5
  502. package/src/form/fields/chips/input-chip.ts +130 -142
  503. package/src/form/fields/chips/suggestion-chip.ts +69 -72
  504. package/src/form/fields/date-range/date-range-dialog.ts +141 -194
  505. package/src/form/fields/date-range/date-range-helpers.ts +63 -64
  506. package/src/form/fields/date-range/date-range.test.ts +359 -122
  507. package/src/form/fields/date-range/date-range.ts +319 -395
  508. package/src/form/fields/date-range/index.ts +2 -2
  509. package/src/form/fields/input/index.ts +8 -8
  510. package/src/form/fields/input/input.scss +30 -26
  511. package/src/form/fields/input/input.test.ts +4 -1
  512. package/src/form/fields/input/input.ts +4 -20
  513. package/src/form/fields/radio-group/index.ts +2 -2
  514. package/src/form/fields/radio-group/radio-button.ts +8 -8
  515. package/src/form/fields/radio-group/radio-group.ts +4 -1
  516. package/src/form/fields/range/range.ts +3 -1
  517. package/src/form/fields/select/index.ts +2 -2
  518. package/src/form/fields/select/select.ts +51 -45
  519. package/src/form/fields/switch/switch.ts +13 -7
  520. package/src/form/fields/textarea/index.ts +1 -1
  521. package/src/form/fields/textarea/textarea.ts +44 -49
  522. package/src/form/form-summary.ts +15 -14
  523. package/src/form/form.test.ts +0 -1
  524. package/src/form/form.ts +11 -23
  525. package/src/icons/icon.ts +25 -25
  526. package/src/icons/index.ts +1 -1
  527. package/src/iframe/iframe.ts +11 -9
  528. package/src/index.ts +1 -0
  529. package/src/json/json.ts +9 -2
  530. package/src/kbd/kbd.ts +30 -28
  531. package/src/layout/scroll/index.ts +1 -1
  532. package/src/layout/scroll/scroll.ts +54 -54
  533. package/src/lightbox/lightbox-service.ts +27 -18
  534. package/src/lightbox/lightbox.directive.ts +2 -1
  535. package/src/lightbox/lightbox.ts +21 -38
  536. package/src/list/index.ts +3 -3
  537. package/src/list/list-item.ts +32 -26
  538. package/src/list/list.ts +13 -8
  539. package/src/menu/index.ts +2 -2
  540. package/src/menu/menu-item.ts +7 -5
  541. package/src/menu/menu.ts +8 -6
  542. package/src/nav-drawer/appbar.ts +9 -7
  543. package/src/nav-drawer/content.ts +10 -8
  544. package/src/nav-drawer/drawer.ts +29 -25
  545. package/src/nav-drawer/index.ts +6 -6
  546. package/src/navigation-bar/index.ts +2 -2
  547. package/src/navigation-bar/navigation-bar-item.ts +127 -118
  548. package/src/navigation-bar/navigation-bar.ts +103 -91
  549. package/src/navigation-rail/index.ts +2 -2
  550. package/src/navigation-rail/navigation-rail.ts +21 -22
  551. package/src/notification/index.ts +6 -6
  552. package/src/notification/notification-service.ts +1 -2
  553. package/src/notification/notification.scss +5 -1
  554. package/src/notification/notification.ts +1 -3
  555. package/src/notification/notify.ts +204 -207
  556. package/src/option/index.ts +1 -1
  557. package/src/option/option.ts +26 -25
  558. package/src/overlay/overlay.animations.ts +4 -14
  559. package/src/overlay/overlay.component.ts +110 -131
  560. package/src/overlay/overlay.confirm-body.ts +26 -48
  561. package/src/overlay/overlay.gestures.ts +8 -10
  562. package/src/overlay/overlay.layout.ts +1 -4
  563. package/src/overlay/overlay.positioning.ts +4 -15
  564. package/src/overlay/overlay.service.ts +9 -24
  565. package/src/overlay/overlay.stack.test.ts +4 -1
  566. package/src/overlay/overlay.stack.ts +4 -4
  567. package/src/overlay/overlay.types.ts +11 -20
  568. package/src/progress/index.ts +1 -1
  569. package/src/progress/progress.ts +135 -133
  570. package/src/rxjs-utils/index.ts +6 -6
  571. package/src/rxjs-utils/waitForElement.ts +20 -20
  572. package/src/rxjs-utils/waitForElementAll.ts +21 -21
  573. package/src/rxjs-utils/waitForElements.ts +27 -27
  574. package/src/rxjs-utils/waitForElementsAll.ts +27 -29
  575. package/src/rxjs-utils/waitUntil.ts +7 -12
  576. package/src/skeleton/skeleton.ts +39 -33
  577. package/src/slider/index.ts +2 -2
  578. package/src/slider/slide.ts +14 -12
  579. package/src/slider/slider.ts +24 -22
  580. package/src/splash-screen/index.ts +1 -1
  581. package/src/splash-screen/splash-screen.ts +26 -27
  582. package/src/state/active-host.ts +4 -5
  583. package/src/state/index.ts +34 -53
  584. package/src/state/persist.ts +14 -11
  585. package/src/state/schmancy-context.ts +88 -88
  586. package/src/state/state.test-d.ts +3 -13
  587. package/src/state/state.test.ts +1 -4
  588. package/src/steps/index.ts +3 -3
  589. package/src/steps/schmancy-step.ts +41 -31
  590. package/src/steps/schmancy-steps.ts +7 -5
  591. package/src/surface/index.ts +1 -1
  592. package/src/surface/surface.styles.ts +55 -191
  593. package/src/surface/surface.ts +18 -16
  594. package/src/table/index.ts +2 -2
  595. package/src/table/row.ts +1 -4
  596. package/src/table/table.ts +2 -5
  597. package/src/tabs/index.ts +2 -2
  598. package/src/tabs/tabs-group.ts +8 -6
  599. package/src/teleport/index.ts +2 -2
  600. package/src/test-utils/a11y.ts +1 -3
  601. package/src/theme/index.ts +17 -17
  602. package/src/theme/theme-audio-player.ts +18 -16
  603. package/src/theme/theme-controller-boat.ts +1 -1
  604. package/src/theme/theme-controller.ts +36 -32
  605. package/src/theme/theme.component.ts +5 -9
  606. package/src/theme/theme.events.ts +1 -1
  607. package/src/theme/theme.format.ts +7 -7
  608. package/src/theme/theme.service.ts +453 -468
  609. package/src/theme/theme.style.css +78 -23
  610. package/src/theme-button/index.ts +1 -1
  611. package/src/tooltip/tooltip.directive.ts +1 -1
  612. package/src/tree/index.ts +1 -1
  613. package/src/tree/tree.ts +24 -16
  614. package/src/types/surface.ts +38 -44
  615. package/src/typewriter/typewriter.directive.ts +30 -39
  616. package/src/typography/typography.ts +289 -269
  617. package/src/utils/animation.ts +2 -8
  618. package/src/utils/index.ts +6 -6
  619. package/src/utils/number.ts +480 -517
  620. package/src/utils/overlay-stack.ts +1 -3
  621. package/src/utils/search.ts +5 -9
  622. package/src/visually-hidden/visually-hidden.ts +15 -13
  623. package/src/window/window-manager.ts +6 -1
  624. package/src/window/window-position.ts +7 -5
  625. package/src/window/window.ts +146 -114
  626. package/types/mixins/surface.mixin.d.ts +8 -14
  627. package/types/src/badge/badge.d.ts +15 -48
  628. package/types/src/calendar/calendar.d.ts +108 -0
  629. package/types/src/calendar/calendar.test.d.ts +1 -0
  630. package/types/src/calendar/index.d.ts +2 -0
  631. package/types/src/directives/cursor-glow.d.ts +1 -1
  632. package/types/src/directives/hummingbird.d.ts +3 -3
  633. package/types/src/form/fields/date-range/date-range-dialog.d.ts +24 -28
  634. package/types/src/form/fields/date-range/date-range.d.ts +35 -66
  635. package/types/src/form/fields/date-range/date-range.test.d.ts +1 -0
  636. package/types/src/form/fields/date-range/index.d.ts +1 -1
  637. package/types/src/index.d.ts +1 -0
  638. package/types/src/surface/surface.d.ts +9 -7
  639. package/types/src/surface/surface.styles.d.ts +15 -20
  640. package/types/src/types/surface.d.ts +17 -22
  641. package/types/src/utils/number.d.ts +1 -1
  642. package/types/src/window/window.d.ts +0 -1
  643. package/dist/SchmancyElement-D9WA9FP9.cjs +0 -2
  644. package/dist/SchmancyElement-D9WA9FP9.cjs.map +0 -1
  645. package/dist/SchmancyElement-OWgz9ePG.js +0 -286
  646. package/dist/SchmancyElement-OWgz9ePG.js.map +0 -1
  647. package/dist/area-BiM7V2ns.js.map +0 -1
  648. package/dist/area-C7XjCoet.cjs +0 -21
  649. package/dist/area-C7XjCoet.cjs.map +0 -1
  650. package/dist/audio-CxO_j__6.js.map +0 -1
  651. package/dist/audio-xXFfMPCS.cjs.map +0 -1
  652. package/dist/autocomplete-DD7Hd59N.cjs +0 -115
  653. package/dist/autocomplete-DD7Hd59N.cjs.map +0 -1
  654. package/dist/autocomplete-DUBY9RtH.js.map +0 -1
  655. package/dist/busy-BjsO3y2A.js +0 -173
  656. package/dist/busy-BjsO3y2A.js.map +0 -1
  657. package/dist/busy-UbCGkTAi.cjs +0 -134
  658. package/dist/busy-UbCGkTAi.cjs.map +0 -1
  659. package/dist/button-BTpxQ1Kd.cjs.map +0 -1
  660. package/dist/button-D7QHfYf4.js.map +0 -1
  661. package/dist/card-DCdtJ5Dy.js.map +0 -1
  662. package/dist/card-rprhCYIC.cjs +0 -177
  663. package/dist/card-rprhCYIC.cjs.map +0 -1
  664. package/dist/chips-C5bpgWyf.js.map +0 -1
  665. package/dist/chips-DVes-BSz.cjs.map +0 -1
  666. package/dist/date-range-DDUuBlJ6.cjs +0 -142
  667. package/dist/date-range-DDUuBlJ6.cjs.map +0 -1
  668. package/dist/date-range-IPlbrhwW.js +0 -966
  669. package/dist/date-range-IPlbrhwW.js.map +0 -1
  670. package/dist/date-range-inline-DPqY9YYf.js.map +0 -1
  671. package/dist/date-range-inline-Dx4Reboo.cjs.map +0 -1
  672. package/dist/details-BnleHmYe.js.map +0 -1
  673. package/dist/details-Bx2jSJxG.cjs.map +0 -1
  674. package/dist/directives-CYf2fAdA.cjs.map +0 -1
  675. package/dist/directives-d1rEbW1A.js.map +0 -1
  676. package/dist/divider-CimQJVr3.cjs +0 -57
  677. package/dist/divider-CimQJVr3.cjs.map +0 -1
  678. package/dist/divider-Cr-rx3vA.js +0 -89
  679. package/dist/divider-Cr-rx3vA.js.map +0 -1
  680. package/dist/expand-DNrWuG_-.js.map +0 -1
  681. package/dist/expand-_cp8oBjp.cjs +0 -141
  682. package/dist/expand-_cp8oBjp.cjs.map +0 -1
  683. package/dist/form-CMgYSZ3y.js.map +0 -1
  684. package/dist/form-DaaAQd2A.cjs.map +0 -1
  685. package/dist/icons-C2RkSXjP.cjs +0 -24
  686. package/dist/icons-C2RkSXjP.cjs.map +0 -1
  687. package/dist/icons-mbpHO_73.js.map +0 -1
  688. package/dist/iframe-88SN5JPu.cjs.map +0 -1
  689. package/dist/iframe-U3P1DnQv.js.map +0 -1
  690. package/dist/input-BY4Korc5.cjs.map +0 -1
  691. package/dist/input-CPWvGjE4.js.map +0 -1
  692. package/dist/input-chip-CCZ3i3Sf.js.map +0 -1
  693. package/dist/input-chip-kytMdbaM.cjs +0 -146
  694. package/dist/input-chip-kytMdbaM.cjs.map +0 -1
  695. package/dist/layout-BhfC26Ks.cjs.map +0 -1
  696. package/dist/layout-DC0Npqu7.js.map +0 -1
  697. package/dist/lightbox-BSwWvDQc.js.map +0 -1
  698. package/dist/lightbox-KrZQH9w9.cjs.map +0 -1
  699. package/dist/list-BwGtAAfi.js.map +0 -1
  700. package/dist/list-DIs02A3d.cjs +0 -40
  701. package/dist/list-DIs02A3d.cjs.map +0 -1
  702. package/dist/menu-DX8d96x-.js.map +0 -1
  703. package/dist/menu-jT_yAk5V.cjs.map +0 -1
  704. package/dist/mixins-COeG4DiX.js.map +0 -1
  705. package/dist/mixins-XGVIOvKt.cjs.map +0 -1
  706. package/dist/notification-CAJVpLne.js.map +0 -1
  707. package/dist/notification-DO3VXceY.cjs.map +0 -1
  708. package/dist/option-BNo1Zs-l.cjs +0 -43
  709. package/dist/option-BNo1Zs-l.cjs.map +0 -1
  710. package/dist/option-JISY0wZJ.js.map +0 -1
  711. package/dist/overlay-B1jVf-ge.cjs.map +0 -1
  712. package/dist/overlay-CT-tMHDX.js.map +0 -1
  713. package/dist/overlay.confirm-body-Dn-Zgogx.js.map +0 -1
  714. package/dist/overlay.confirm-body-mYDYoJL8.cjs.map +0 -1
  715. package/dist/overlay.service-BQmva9GY.cjs.map +0 -1
  716. package/dist/overlay.service-yqTOyLlr.js.map +0 -1
  717. package/dist/progress-CGWozq_n.js.map +0 -1
  718. package/dist/progress-DOVJhsR0.cjs +0 -51
  719. package/dist/progress-DOVJhsR0.cjs.map +0 -1
  720. package/dist/radio-group-CXkq6qAF.js.map +0 -1
  721. package/dist/radio-group-DoSX5D2V.cjs.map +0 -1
  722. package/dist/select-CObZenqg.cjs +0 -56
  723. package/dist/select-CObZenqg.cjs.map +0 -1
  724. package/dist/select-CU90i50_.js.map +0 -1
  725. package/dist/sound.service-Qhr8nCeG.cjs.map +0 -1
  726. package/dist/sound.service-m8WjOhjn.js.map +0 -1
  727. package/dist/splash-screen-Ca6Ew8p6.cjs +0 -41
  728. package/dist/splash-screen-Ca6Ew8p6.cjs.map +0 -1
  729. package/dist/splash-screen-Cs3dbPN3.js.map +0 -1
  730. package/dist/src-CCVbLLgC.js.map +0 -1
  731. package/dist/src-CpftzdZV.cjs +0 -264
  732. package/dist/src-CpftzdZV.cjs.map +0 -1
  733. package/dist/state--x58-AuK.cjs.map +0 -1
  734. package/dist/state-QSwQ61sA.js.map +0 -1
  735. package/dist/surface-bTjOiq8n.cjs +0 -7
  736. package/dist/surface-bTjOiq8n.cjs.map +0 -1
  737. package/dist/surface-cqMsHJHM.js.map +0 -1
  738. package/dist/tabs-Dk9UDWpq.cjs.map +0 -1
  739. package/dist/tabs-Ib0Mh__1.js.map +0 -1
  740. package/dist/textarea-CcRsw08B.js.map +0 -1
  741. package/dist/textarea-Cntd9tfV.cjs.map +0 -1
  742. package/dist/theme.service-5RjyR7Sy.js.map +0 -1
  743. package/dist/theme.service-DA6KY52G.cjs.map +0 -1
  744. package/dist/typography-DeEYdMhW.js +0 -358
  745. package/dist/typography-DeEYdMhW.js.map +0 -1
  746. package/dist/typography-Tm7wSaB2.cjs +0 -282
  747. package/dist/typography-Tm7wSaB2.cjs.map +0 -1
  748. package/dist/utils-DTa3QHxk.cjs.map +0 -1
  749. package/dist/utils-H8wNknWC.js.map +0 -1
  750. package/dist/window-Db5ZYY6t.js.map +0 -1
  751. package/dist/window-oQqx5xqQ.cjs +0 -59
  752. package/dist/window-oQqx5xqQ.cjs.map +0 -1
  753. package/src/form/fields/date-range/date-range-presets.ts +0 -220
  754. package/src/form/fields/date-range/date-utils.ts +0 -58
  755. package/types/src/form/fields/date-range/date-range-presets.d.ts +0 -18
  756. package/types/src/form/fields/date-range/date-utils.d.ts +0 -15
  757. /package/dist/{hashContent-iRZJJWtE.cjs → hashContent--s09Ed_g.cjs} +0 -0
  758. /package/dist/{hashContent-BqU6v1Xr.js → hashContent-CAvrQ56N.js} +0 -0
  759. /package/dist/{rxjs-utils-Csnks202.cjs → rxjs-utils-4P2v57ke.cjs} +0 -0
  760. /package/dist/{rxjs-utils-d-ivVN84.js → rxjs-utils-JMFdgQSl.js} +0 -0
  761. /package/dist/{theme.interface-CSt7JUBD.cjs → theme.interface-B-qxDsZQ.cjs} +0 -0
  762. /package/dist/{theme.interface-odQEpZZH.js → theme.interface-B7caS5cg.js} +0 -0
  763. /package/dist/{utils-DTa3QHxk.cjs → utils-DIXndz6Q.cjs} +0 -0
  764. /package/dist/{utils-H8wNknWC.js → utils-dSPH7Oh9.js} +0 -0
@@ -8,18 +8,8 @@ import { ifDefined } from 'lit/directives/if-defined.js'
8
8
  import { createRef, ref } from 'lit/directives/ref.js'
9
9
  import { repeat } from 'lit/directives/repeat.js'
10
10
  import { when } from 'lit/directives/when.js'
11
- import {
12
- BehaviorSubject,
13
- combineLatest,
14
- fromEvent,
15
- timer
16
- } from 'rxjs'
17
- import {
18
- debounceTime,
19
- distinctUntilChanged,
20
- takeUntil,
21
- tap
22
- } from 'rxjs/operators'
11
+ import { BehaviorSubject, combineLatest, fromEvent, timer } from 'rxjs'
12
+ import { debounceTime, distinctUntilChanged, takeUntil, tap } from 'rxjs/operators'
23
13
  import style from './autocomplete.scss?inline'
24
14
 
25
15
  // Import the similarity function (or include it inline)
@@ -28,13 +18,13 @@ import { similarity } from '../../../utils/search'
28
18
  import '../../../chips/input-chip'
29
19
 
30
20
  export type SchmancyAutocompleteChangeEvent = CustomEvent<{
31
- value: string | string[]
32
- values?: string[]
21
+ value: string | string[]
22
+ values?: string[]
33
23
  }>
34
24
 
35
25
  interface FilteredOption {
36
- option: SchmancyOption
37
- score: number
26
+ option: SchmancyOption
27
+ score: number
38
28
  }
39
29
 
40
30
  /**
@@ -50,715 +40,713 @@ interface FilteredOption {
50
40
  */
51
41
  @customElement('schmancy-autocomplete')
52
42
  export default class SchmancyAutocomplete extends SchmancyFormField(unsafeCSS(style)) {
53
-
54
- // `formAssociated`, `internals`, `attachInternals`, `name`, `label`,
55
- // `required`, `disabled`, `error`, `validationMessage`, `hint`, `id`,
56
- // `validateOn`, `touched/dirty/submitted`, `markTouched/markSubmitted`,
57
- // `setCustomValidity`, `formDisabledCallback`, FIELD_CONNECT_EVENT dispatch
58
- // — all from the mixin.
59
-
60
- /** Override mixin's resetForm with autocomplete-specific subject reset. */
61
- override resetForm(): void {
62
- if (this.multi) {
63
- this.selectedValues$.next([])
64
- } else {
65
- this.selectedValue$.next('')
66
- }
67
- this.inputValue = ''
68
- this.inputValue$.next('')
69
- super.resetForm()
70
- }
71
-
72
- // Track whether value/values have been explicitly set
73
- valueSet: boolean = false
74
- valuesSet: boolean = false
75
-
76
- // Autocomplete-specific properties only `name`, `label`, `required`,
77
- // `error`, `validationMessage` come from the mixin.
78
- @property({ type: String }) placeholder = ''
79
- @property({ type: String }) maxHeight = '300px'
80
- @property({ type: Boolean }) multi = false
81
- @property({ type: String }) description = ''
82
- @property({ type: String, reflect: true }) size: InputSize = 'md'
83
- @property({ type: String }) autocomplete = 'off'
84
- @property({ type: Number }) debounceMs = 200
85
- @property({ type: Number }) similarityThreshold = 0.3 // Minimum similarity score to show option
86
-
87
- private readonly a11yId = `schmancy-autocomplete-${Math.random().toString(36).slice(2, 10)}`
88
-
89
- // Values property for multi-select mode
90
- @property({ type: Array })
91
- get values() {
92
- return [...this.selectedValues$.value]
93
- }
94
- set values(vals: string[]) {
95
- this.valuesSet = true
96
- this.selectedValues$.next(Array.isArray(vals) ? [...vals] : [])
97
- }
98
-
99
- // Value property narrowed override of the mixin's wide value union.
100
- @property({ type: String, reflect: true })
101
- override get value(): string {
102
- return this.multi
103
- ? this.selectedValues$.value.join(',')
104
- : this.selectedValue$.value
105
- }
106
- override set value(val: string) {
107
- this.valueSet = true
108
- if (this.multi) {
109
- const newValues = val ? val.split(',').map(v => v.trim()).filter(Boolean) : []
110
- const currentValues = this.selectedValues$.value
111
- // Only update if values actually changed
112
- if (JSON.stringify(newValues) !== JSON.stringify(currentValues)) {
113
- this.selectedValues$.next(newValues)
114
- }
115
- } else {
116
- // Only update if value actually changed
117
- if (val !== this.selectedValue$.value) {
118
- this.selectedValue$.next(val)
119
- // Update the input display when value is set
120
- this.updateInputDisplay()
121
- }
122
- }
123
- }
124
-
125
- // State
126
- @state() private open = false
127
- @state() private inputValue = ''
128
- @state() private visibleOptionsCount = 0
129
- @state() private hasResults = true
130
-
131
- // DOM references
132
- @query('#options') listbox!: HTMLUListElement
133
- @query('sch-input') input!: SchmancyInput
134
- @queryAssignedElements({ flatten: true }) private options!: SchmancyOption[]
135
- private inputElementRef = createRef<HTMLInputElement>()
136
-
137
- // RxJS Subjects - only what we actually need
138
- private selectedValue$ = new BehaviorSubject<string>('')
139
- private selectedValues$ = new BehaviorSubject<string[]>([])
140
- private inputValue$ = new BehaviorSubject<string>('')
141
-
142
- override connectedCallback() {
143
- // FIELD_CONNECT_EVENT is dispatched by the mixin's connectedCallback.
144
- super.connectedCallback()
145
- this.setupAutocompleteLogic()
146
- this.setupDocumentClickHandler()
147
- }
148
-
149
- private setupAutocompleteLogic() {
150
- // Sync selection state
151
- combineLatest([
152
- this.selectedValue$,
153
- this.selectedValues$
154
- ]).pipe(
155
- tap(([selectedValue, selectedValues]) => {
156
- this.updateOptionSelection(selectedValue, selectedValues)
157
- // Keep ElementInternals form value in sync with selection (single and multi).
158
- const formValue = this.multi ? selectedValues.join(',') : selectedValue
159
- this.internals?.setFormValue(formValue || null)
160
- if (this.required) {
161
- const missing = this.multi ? selectedValues.length === 0 : !selectedValue
162
- this.internals?.setValidity(
163
- missing ? { valueMissing: true } : {},
164
- missing ? this.validationMessage || 'Please select an option.' : undefined,
165
- )
166
- }
167
- }),
168
- takeUntil(this.disconnecting)
169
- ).subscribe()
170
-
171
- // Filter options based on input
172
- this.inputValue$.pipe(
173
- distinctUntilChanged(),
174
- debounceTime(this.debounceMs),
175
- tap(searchTerm => {
176
- if (this.open) {
177
- this.filterOptions(searchTerm)
178
- }
179
- }),
180
- takeUntil(this.disconnecting)
181
- ).subscribe()
182
- }
183
-
184
- private setupOptionHandlers() {
185
- this.options.forEach((option, index) => {
186
- option.setAttribute('role', 'option')
187
- option.tabIndex = -1
188
- if (!option.id) {
189
- option.id = `${this.id}-option-${index}`
190
- }
191
- // Idempotent: slotchange may fire repeatedly for the same option nodes,
192
- // and addEventListener doesn't replace prior handlers like onfoo= did.
193
- if (option.dataset.schmancyAutocompleteHandlers === 'attached') return
194
- option.dataset.schmancyAutocompleteHandlers = 'attached'
195
-
196
- // Prevent blur handler from interfering with option selection
197
- fromEvent<MouseEvent>(option, 'mousedown')
198
- .pipe(takeUntil(this.disconnecting))
199
- .subscribe(e => e.preventDefault())
200
-
201
- // Handle the actual selection
202
- fromEvent<MouseEvent>(option, 'click')
203
- .pipe(takeUntil(this.disconnecting))
204
- .subscribe(e => {
205
- e.stopPropagation()
206
- this.selectOption(option)
207
- })
208
- })
209
- }
210
-
211
- private updateOptionSelection(selectedValue: string, selectedValues: string[]) {
212
- this.options.forEach(option => {
213
- option.selected = this.multi
214
- ? selectedValues.includes(option.value)
215
- : option.value === selectedValue
216
- option.setAttribute('aria-selected', String(option.selected))
217
- })
218
- }
219
-
220
- private filterOptions(searchTerm: string) {
221
- const term = searchTerm.trim()
222
-
223
- if (!term) {
224
- // Show all options if no search term
225
- this.options.forEach(option => {
226
- option.hidden = false
227
- option.style.order = '0'
228
- })
229
- this.visibleOptionsCount = this.options.length
230
- this.hasResults = true
231
- } else {
232
- // Calculate similarity scores for all options
233
- const scoredOptions: FilteredOption[] = this.options.map(option => {
234
- const optionLabel = option.label || option.textContent || ''
235
- const optionValue = option.value
236
-
237
- const labelScore = similarity(term, optionLabel)
238
- const valueScore = similarity(term, optionValue)
239
- const score = Math.max(labelScore * 1.1, valueScore)
240
-
241
- return { option, score }
242
- })
243
-
244
- // Sort by score (highest first)
245
- scoredOptions.sort((a, b) => b.score - a.score)
246
-
247
- // Apply visibility and ordering
248
- let visibleCount = 0
249
- scoredOptions.forEach((item, index) => {
250
- const { option, score } = item
251
-
252
- if (score < this.similarityThreshold) {
253
- option.hidden = true
254
- } else {
255
- option.hidden = false
256
- visibleCount++
257
- option.style.order = String(index)
258
- }
259
- })
260
-
261
- this.visibleOptionsCount = visibleCount
262
- this.hasResults = visibleCount > 0
263
- }
264
-
265
- this.announceToScreenReader(
266
- this.visibleOptionsCount > 0
267
- ? `${this.visibleOptionsCount} option${this.visibleOptionsCount === 1 ? '' : 's'} available.`
268
- : 'No results found.'
269
- )
270
- }
271
-
272
- private openDropdown() {
273
- this.open = true
274
- // Reset filters based on current input value when dropdown opens
275
- this.filterOptions(this.inputValue)
276
- }
277
-
278
- private selectOption(option: SchmancyOption) {
279
- if (this.multi) {
280
- const currentValues = this.selectedValues$.value
281
- const index = currentValues.indexOf(option.value)
282
- const newValues = index > -1
283
- ? currentValues.filter(v => v !== option.value)
284
- : [...currentValues, option.value]
285
-
286
- this.selectedValues$.next(newValues)
287
- this.announceToScreenReader(
288
- newValues.length > 0
289
- ? `Selected: ${this.getSelectedLabels().join(', ')}`
290
- : 'No options selected'
291
- )
292
- this.fireChangeEvent()
293
- } else {
294
- // Update value first
295
- this.selectedValue$.next(option.value)
296
-
297
- // Close dropdown IMMEDIATELY to prevent blur handler from firing
298
- this.open = false
299
-
300
- // Now fire event with the NEW value
301
- this.fireChangeEvent()
302
-
303
- // Update UI
304
- this.inputValue = option.label || option.textContent || ''
305
- this.inputValue$.next(this.inputValue)
306
-
307
- this.announceToScreenReader(`Selected: ${option.label || option.textContent}`)
308
- }
309
- }
310
-
311
- private setupDocumentClickHandler() {
312
- fromEvent<MouseEvent>(document, 'click')
313
- .pipe(takeUntil(this.disconnecting))
314
- .subscribe(e => {
315
- if (!this.open) return
316
- const path = e.composedPath()
317
- if (!path.includes(this) && !this.options.some(opt => path.includes(opt))) {
318
- this.open = false
319
- this.updateInputDisplay()
320
- }
321
- })
322
- }
323
-
324
-
325
- private updateInputDisplay() {
326
- // For multi-select, we don't update input display since chips show the selections
327
- if (this.multi) return
328
-
329
- const selectedValue = this.selectedValue$.value
330
- const option = this.options.find(opt => opt.value === selectedValue)
331
- this.inputValue = option ? option.label || option.textContent || '' : ''
332
- this.inputValue$.next(this.inputValue)
333
-
334
- if (this.inputElementRef.value) {
335
- this.inputElementRef.value.value = this.inputValue
336
- }
337
- }
338
-
339
- private getSelectedLabels(): string[] {
340
- return this.options
341
- .filter(option =>
342
- this.multi
343
- ? this.selectedValues$.value.includes(option.value)
344
- : option.value === this.selectedValue$.value
345
- )
346
- .map(option => option.label || option.textContent || '')
347
- }
348
-
349
- private announceToScreenReader(message: string) {
350
- const liveRegion = this.shadowRoot?.querySelector('#live-status')
351
- if (liveRegion) {
352
- liveRegion.textContent = message
353
- }
354
- }
355
-
356
- private fireChangeEvent() {
357
- const detail: SchmancyAutocompleteChangeEvent['detail'] = {
358
- value: this.value,
359
- }
360
-
361
- if (this.multi) {
362
- detail.values = [...this.selectedValues$.value]
363
- }
364
-
365
- this.dispatchEvent(
366
- new CustomEvent<SchmancyAutocompleteChangeEvent['detail']>('change', {
367
- detail,
368
- bubbles: true,
369
- composed: true,
370
- })
371
- )
372
- }
373
-
374
- public checkValidity(): boolean {
375
- if (!this.required) return true
376
- return this.multi
377
- ? this.selectedValues$.value.length > 0
378
- : Boolean(this.selectedValue$.value)
379
- }
380
-
381
- public reportValidity(): boolean {
382
- if (this.inputElementRef.value) {
383
- return this.inputElementRef.value.reportValidity()
384
- }
385
- return this.checkValidity()
386
- }
387
-
388
- firstUpdated() {
389
- this.setupOptionHandlers()
390
-
391
- // Sync initial value with display after options are available
392
- this.updateInputDisplay()
393
-
394
- // Update options when slot changes
395
- const slot = this.shadowRoot?.querySelector('slot')
396
- slot?.addEventListener('slotchange', () => {
397
- this.setupOptionHandlers()
398
- this.updateOptionSelection(this.selectedValue$.value, this.selectedValues$.value)
399
- })
400
- }
401
-
402
- private handleChipRemove(value: string) {
403
- const currentValues = this.selectedValues$.value
404
- const newValues = currentValues.filter(v => v !== value)
405
- this.selectedValues$.next(newValues)
406
- this.fireChangeEvent()
407
- this.announceToScreenReader(`Removed: ${this.getChipLabel(value)}`)
408
- }
409
-
410
- private getChipLabel(value: string): string {
411
- const option = this.options.find(opt => opt.value === value)
412
- return option ? option.label || option.textContent || value : value
413
- }
414
-
415
- private focusTextInput() {
416
- if (this.inputElementRef.value) {
417
- this.inputElementRef.value.focus()
418
- }
419
- }
420
-
421
- render() {
422
- const descriptionId = `${this.id}-desc`
423
-
424
- // Get size-based styling to match Schmancy input
425
- const getSizeStyles = () => {
426
- switch (this.size) {
427
- case 'sm':
428
- return {
429
- height: 'min-h-[40px]',
430
- padding: 'px-2',
431
- fontSize: 'text-sm', // 14px
432
- labelSize: 'text-sm'
433
- }
434
- case 'lg':
435
- return {
436
- height: 'min-h-[60px]',
437
- padding: 'px-5',
438
- fontSize: 'text-lg', // 18px
439
- labelSize: 'text-lg'
440
- }
441
- case 'md':
442
- default:
443
- return {
444
- height: 'min-h-[50px]',
445
- padding: 'px-4',
446
- fontSize: 'text-base', // 16px
447
- labelSize: 'text-base'
448
- }
449
- }
450
- }
451
-
452
- const { height, padding, fontSize, labelSize } = getSizeStyles()
453
-
454
- return html`
455
- <div class="relative">
456
- <!-- Screen reader live region -->
457
- <div id="live-status" role="status" aria-live="polite" class="sr-only"></div>
458
-
459
- <!-- Description -->
460
- ${this.description ? html`<div id="${descriptionId}" class="sr-only">${this.description}</div>` : ''}
461
-
462
- <!-- Custom input wrapper for Gmail-style chip input -->
463
- <slot name="trigger">
464
- ${when(this.multi,
465
- () => html`
466
- <!-- Custom multi-select input with inline chips -->
467
- <div class="relative">
468
- ${when(this.label, () => html`
469
- <label class="${classMap({
470
- 'block mb-1 font-medium': true,
471
- 'text-primary-default': !this.error,
472
- 'text-error-default': this.error,
473
- [labelSize]: true
474
- })}">
475
- ${this.label}${this.required ? html`<span class="text-error-default ml-1">*</span>` : ''}
476
- </label>
477
- `)}
478
- <div
479
- class="${classMap({
480
- 'flex flex-wrap items-center gap-1': true,
481
- [height]: true,
482
- [padding]: true,
483
- 'block w-full min-w-0 rounded-[8px] border-0': true,
484
- 'bg-surface-highest text-surface-on': true,
485
- 'ring-0 ring-inset focus-within:ring-1 focus-within:ring-inset': true,
486
- 'ring-secondary-default focus-within:ring-secondary-default': !this.error,
487
- 'ring-error-default focus-within:ring-error-default': this.error,
488
- 'cursor-text transition-colors duration-200': true
489
- })}"
490
- @click=${() => this.focusTextInput()}
491
- role="combobox"
492
- aria-autocomplete="list"
493
- aria-haspopup="listbox"
494
- aria-controls="options"
495
- aria-expanded=${this.open}
496
- >
497
- <!-- Render chips inline -->
498
- ${repeat(
499
- this.selectedValues$.value,
500
- value => value,
501
- value => html`
502
- <schmancy-input-chip
503
- .value=${value}
504
- @remove=${(e: CustomEvent) => this.handleChipRemove(e.detail.value)}
505
- class="shrink-0 my-0.5"
506
- >
507
- ${this.getChipLabel(value)}
508
- </schmancy-input-chip>
509
- `
510
- )}
511
-
512
- <!-- Text input for typing -->
513
- <input
514
- ${ref(this.inputElementRef)}
515
- id="autocomplete-input"
516
- type="text"
517
- class="flex-1 min-w-[120px] py-1 bg-transparent border-none outline-none ${fontSize} font-medium text-surface-on placeholder:text-muted"
518
- name=${this.name || this.label?.toLowerCase().replace(/\s+/g, '-') || ''}
519
- .placeholder=${this.selectedValues$.value.length > 0 ? 'Add more...' : this.placeholder}
520
- .value=${this.inputValue}
521
- .autocomplete=${this.autocomplete}
522
- aria-invalid=${this.error ? 'true' : 'false'}
523
- aria-required=${this.required ? 'true' : 'false'}
524
- aria-describedby=${this.error && this.validationMessage ? `${this.a11yId}-err` : nothing}
525
- aria-label=${!this.label && this.placeholder ? this.placeholder : nothing}
526
- @input=${(e: Event) => {
527
- const value = (e.target as HTMLInputElement).value
528
- this.inputValue = value
529
- this.inputValue$.next(value)
530
- }}
531
- @focus=${(e: FocusEvent) => {
532
- e.stopPropagation()
533
- // Clear input on focus for new searches
534
- this.inputValue = ''
535
- this.inputValue$.next('')
536
- this.openDropdown()
537
- }}
538
- @keydown=${(e: KeyboardEvent) => {
539
- this.handleKeyDown(e)
540
- }}
541
- @blur=${() => {
542
- this.handleAutoSelectOnBlur()
543
- }}
544
- />
545
- </div>
546
-
547
- <!-- Validation message -->
548
- ${when(this.error && this.validationMessage, () => html`
549
- <div id="${this.a11yId}-err" class="mt-1 text-sm text-error-default" role="alert">
550
- ${this.validationMessage}
551
- </div>
552
- `)}
553
- </div>
554
- `,
555
- () => html`
556
- <!-- Regular single-select input -->
557
- <schmancy-input
558
- .size=${this.size}
559
- ${ref(this.inputElementRef)}
560
- id="autocomplete-input"
561
- class="w-full"
562
- .name=${this.name || this.label?.toLowerCase().replace(/\s+/g, '-') || ''}
563
- .label=${this.label}
564
- .placeholder=${this.placeholder}
565
- .required=${this.required}
566
- .value=${this.inputValue}
567
- type="text"
568
- autocomplete=${this.autocomplete}
569
- clickable
570
- role="combobox"
571
- aria-autocomplete="list"
572
- aria-haspopup="listbox"
573
- aria-controls="options"
574
- aria-expanded=${this.open}
575
- aria-describedby=${ifDefined(this.description ? descriptionId : undefined)}
576
- @input=${(e: Event) => {
577
- const value = (e.target as HTMLInputElement).value
578
- this.inputValue = value
579
- this.inputValue$.next(value)
580
- }}
581
- @focus=${(e: FocusEvent) => {
582
- e.stopPropagation()
583
- this.openDropdown()
584
- }}
585
- @click=${(e: MouseEvent) => {
586
- e.stopPropagation()
587
- this.openDropdown()
588
- }}
589
- @keydown=${(e: KeyboardEvent) => {
590
- this.handleKeyDown(e)
591
- }}
592
- @blur=${() => {
593
- this.handleAutoSelectOnBlur()
594
- }}
595
- >
596
- </schmancy-input>
597
- `
598
- )}
599
- </slot>
600
-
601
- <!-- Options dropdown -->
602
- <ul
603
- id="options"
604
- class=${classMap({
605
- 'absolute': true,
606
- 'z-[1000]': true,
607
- 'mt-1': true,
608
- 'w-full': true,
609
- 'rounded-md': true,
610
- 'shadow-md': true,
611
- 'overflow-auto': true,
612
- 'min-w-full': true,
613
- 'bg-surface-low': true,
614
- 'flex': true,
615
- 'flex-col': true, // Enable flexbox for ordering
616
- })}
617
- role="listbox"
618
- aria-multiselectable=${this.multi ? 'true' : 'false'}
619
- aria-label=${`${this.label || 'Options'} dropdown`}
620
- ?hidden=${!this.open}
621
- style="max-height: ${this.maxHeight}; display: ${this.open ? 'flex' : 'none'};"
622
- @slotchange=${() => {
623
- this.setupOptionHandlers()
624
- }}
625
- >
626
- <slot></slot>
627
- ${!this.hasResults ? html`
628
- <li class="px-3 py-2 text-sm text-muted">No results found</li>
629
- ` : ''}
630
- </ul>
631
- </div>
632
- `
633
- }
634
-
635
- private handleAutoSelectOnBlur() {
636
- // Only auto-select in single-select mode and when dropdown is open with a search term
637
- if (this.multi || !this.open || !this.inputValue.trim()) {
638
- return
639
- }
640
-
641
- const searchTerm = this.inputValue.trim()
642
-
643
- // Find the best matching option using the same similarity logic as filtering
644
- let bestMatch: SchmancyOption | null = null
645
- let bestScore = 0
646
-
647
- this.options.forEach(option => {
648
- // Skip hidden options
649
- if (option.hidden) return
650
-
651
- // Get text to search in (prioritize label, then textContent, then value)
652
- const optionLabel = option.label || option.textContent || ''
653
- const optionValue = option.value
654
-
655
- // Calculate similarity scores for both label and value
656
- const labelScore = similarity(searchTerm, optionLabel)
657
- const valueScore = similarity(searchTerm, optionValue)
658
-
659
- // Use the higher score (prioritizing label matches)
660
- const score = Math.max(labelScore * 1.1, valueScore) // Slight boost for label matches
661
-
662
- // Keep track of best match that meets threshold
663
- if (score > bestScore && score >= this.similarityThreshold) {
664
- bestScore = score
665
- bestMatch = option
666
- }
667
- })
668
-
669
- // Auto-select the best match if found
670
- if (bestMatch) {
671
- // Silently update the selected value without firing change event
672
- this.selectedValue$.next(bestMatch.value)
673
- this.inputValue = bestMatch.label || bestMatch.textContent || ''
674
- this.inputValue$.next(this.inputValue)
675
- this.open = false
676
- }
677
- }
678
-
679
- private handleKeyDown(event: KeyboardEvent) {
680
- const isOpen = this.open
681
- const selectedValues = this.selectedValues$.value
682
-
683
- // Handle backspace to remove last chip in multi-select when input is empty
684
- if (this.multi && event.key === 'Backspace' && !this.inputValue && selectedValues.length > 0 && !isOpen) {
685
- event.preventDefault()
686
- const lastValue = selectedValues[selectedValues.length - 1]
687
- this.handleChipRemove(lastValue)
688
- return
689
- }
690
-
691
- if (!isOpen && (event.key === 'ArrowDown' || event.key === 'Enter')) {
692
- event.preventDefault()
693
- this.openDropdown()
694
-
695
- timer(10)
696
- .pipe(takeUntil(this.disconnecting))
697
- .subscribe(() => {
698
- const firstVisible = this.options.find(opt => !opt.hidden)
699
- firstVisible?.focus()
700
- })
701
- return
702
- }
703
-
704
- if (!isOpen) return
705
-
706
- const visibleOptions = this.options.filter(opt => !opt.hidden)
707
- .toSorted((a, b) => parseInt(a.style.order || '0') - parseInt(b.style.order || '0'))
708
-
709
- const focusedOption = visibleOptions.find(opt => opt === document.activeElement)
710
- const currentIndex = focusedOption ? visibleOptions.indexOf(focusedOption) : -1
711
-
712
- switch (event.key) {
713
- case 'Escape':
714
- event.preventDefault()
715
- this.open = false
716
- this.updateInputDisplay()
717
- this.inputElementRef.value?.focus()
718
- break
719
-
720
- case 'Tab':
721
- this.open = false
722
- this.updateInputDisplay()
723
- break
724
-
725
- case 'ArrowDown':
726
- event.preventDefault()
727
- const nextIndex = currentIndex < visibleOptions.length - 1 ? currentIndex + 1 : 0
728
- visibleOptions[nextIndex]?.focus()
729
- break
730
-
731
- case 'ArrowUp':
732
- event.preventDefault()
733
- const prevIndex = currentIndex > 0 ? currentIndex - 1 : visibleOptions.length - 1
734
- visibleOptions[prevIndex]?.focus()
735
- break
736
-
737
- case 'Home':
738
- event.preventDefault()
739
- visibleOptions[0]?.focus()
740
- break
741
-
742
- case 'End':
743
- event.preventDefault()
744
- visibleOptions[visibleOptions.length - 1]?.focus()
745
- break
746
-
747
- case 'Enter':
748
- case ' ':
749
- if (focusedOption) {
750
- event.preventDefault()
751
- this.selectOption(focusedOption)
752
- }
753
- break
754
- }
755
- }
43
+ // `formAssociated`, `internals`, `attachInternals`, `name`, `label`,
44
+ // `required`, `disabled`, `error`, `validationMessage`, `hint`, `id`,
45
+ // `validateOn`, `touched/dirty/submitted`, `markTouched/markSubmitted`,
46
+ // `setCustomValidity`, `formDisabledCallback`, FIELD_CONNECT_EVENT dispatch
47
+ // all from the mixin.
48
+
49
+ /** Override mixin's resetForm with autocomplete-specific subject reset. */
50
+ override resetForm(): void {
51
+ if (this.multi) {
52
+ this.selectedValues$.next([])
53
+ } else {
54
+ this.selectedValue$.next('')
55
+ }
56
+ this.inputValue = ''
57
+ this.inputValue$.next('')
58
+ super.resetForm()
59
+ }
60
+
61
+ // Track whether value/values have been explicitly set
62
+ valueSet: boolean = false
63
+ valuesSet: boolean = false
64
+
65
+ // Autocomplete-specific properties only — `name`, `label`, `required`,
66
+ // `error`, `validationMessage` come from the mixin.
67
+ @property({ type: String }) placeholder = ''
68
+ @property({ type: String }) maxHeight = '300px'
69
+ @property({ type: Boolean }) multi = false
70
+ @property({ type: String }) description = ''
71
+ @property({ type: String, reflect: true }) size: InputSize = 'md'
72
+ @property({ type: String }) autocomplete = 'off'
73
+ @property({ type: Number }) debounceMs = 200
74
+ @property({ type: Number }) similarityThreshold = 0.3 // Minimum similarity score to show option
75
+
76
+ private readonly a11yId = `schmancy-autocomplete-${Math.random().toString(36).slice(2, 10)}`
77
+
78
+ // Values property for multi-select mode
79
+ @property({ type: Array })
80
+ get values() {
81
+ return [...this.selectedValues$.value]
82
+ }
83
+ set values(vals: string[]) {
84
+ this.valuesSet = true
85
+ this.selectedValues$.next(Array.isArray(vals) ? [...vals] : [])
86
+ }
87
+
88
+ // Value property — narrowed override of the mixin's wide value union.
89
+ @property({ type: String, reflect: true })
90
+ override get value(): string {
91
+ return this.multi ? this.selectedValues$.value.join(',') : this.selectedValue$.value
92
+ }
93
+ override set value(val: string) {
94
+ this.valueSet = true
95
+ if (this.multi) {
96
+ const newValues = val
97
+ ? val
98
+ .split(',')
99
+ .map(v => v.trim())
100
+ .filter(Boolean)
101
+ : []
102
+ const currentValues = this.selectedValues$.value
103
+ // Only update if values actually changed
104
+ if (JSON.stringify(newValues) !== JSON.stringify(currentValues)) {
105
+ this.selectedValues$.next(newValues)
106
+ }
107
+ } else {
108
+ // Only update if value actually changed
109
+ if (val !== this.selectedValue$.value) {
110
+ this.selectedValue$.next(val)
111
+ // Update the input display when value is set
112
+ this.updateInputDisplay()
113
+ }
114
+ }
115
+ }
116
+
117
+ // State
118
+ @state() private open = false
119
+ @state() private inputValue = ''
120
+ @state() private visibleOptionsCount = 0
121
+ @state() private hasResults = true
122
+
123
+ // DOM references
124
+ @query('#options') listbox!: HTMLUListElement
125
+ @query('sch-input') input!: SchmancyInput
126
+ @queryAssignedElements({ flatten: true }) private options!: SchmancyOption[]
127
+ private inputElementRef = createRef<HTMLInputElement>()
128
+
129
+ // RxJS Subjects - only what we actually need
130
+ private selectedValue$ = new BehaviorSubject<string>('')
131
+ private selectedValues$ = new BehaviorSubject<string[]>([])
132
+ private inputValue$ = new BehaviorSubject<string>('')
133
+
134
+ override connectedCallback() {
135
+ // FIELD_CONNECT_EVENT is dispatched by the mixin's connectedCallback.
136
+ super.connectedCallback()
137
+ this.setupAutocompleteLogic()
138
+ this.setupDocumentClickHandler()
139
+ }
140
+
141
+ private setupAutocompleteLogic() {
142
+ // Sync selection state
143
+ combineLatest([this.selectedValue$, this.selectedValues$])
144
+ .pipe(
145
+ tap(([selectedValue, selectedValues]) => {
146
+ this.updateOptionSelection(selectedValue, selectedValues)
147
+ // Keep ElementInternals form value in sync with selection (single and multi).
148
+ const formValue = this.multi ? selectedValues.join(',') : selectedValue
149
+ this.internals?.setFormValue(formValue || null)
150
+ if (this.required) {
151
+ const missing = this.multi ? selectedValues.length === 0 : !selectedValue
152
+ this.internals?.setValidity(
153
+ missing ? { valueMissing: true } : {},
154
+ missing ? this.validationMessage || 'Please select an option.' : undefined,
155
+ )
156
+ }
157
+ }),
158
+ takeUntil(this.disconnecting),
159
+ )
160
+ .subscribe()
161
+
162
+ // Filter options based on input
163
+ this.inputValue$
164
+ .pipe(
165
+ distinctUntilChanged(),
166
+ debounceTime(this.debounceMs),
167
+ tap(searchTerm => {
168
+ if (this.open) {
169
+ this.filterOptions(searchTerm)
170
+ }
171
+ }),
172
+ takeUntil(this.disconnecting),
173
+ )
174
+ .subscribe()
175
+ }
176
+
177
+ private setupOptionHandlers() {
178
+ this.options.forEach((option, index) => {
179
+ option.setAttribute('role', 'option')
180
+ option.tabIndex = -1
181
+ if (!option.id) {
182
+ option.id = `${this.id}-option-${index}`
183
+ }
184
+ // Idempotent: slotchange may fire repeatedly for the same option nodes,
185
+ // and addEventListener doesn't replace prior handlers like onfoo= did.
186
+ if (option.dataset.schmancyAutocompleteHandlers === 'attached') return
187
+ option.dataset.schmancyAutocompleteHandlers = 'attached'
188
+
189
+ // Prevent blur handler from interfering with option selection
190
+ fromEvent<MouseEvent>(option, 'mousedown')
191
+ .pipe(takeUntil(this.disconnecting))
192
+ .subscribe(e => e.preventDefault())
193
+
194
+ // Handle the actual selection
195
+ fromEvent<MouseEvent>(option, 'click')
196
+ .pipe(takeUntil(this.disconnecting))
197
+ .subscribe(e => {
198
+ e.stopPropagation()
199
+ this.selectOption(option)
200
+ })
201
+ })
202
+ }
203
+
204
+ private updateOptionSelection(selectedValue: string, selectedValues: string[]) {
205
+ this.options.forEach(option => {
206
+ option.selected = this.multi ? selectedValues.includes(option.value) : option.value === selectedValue
207
+ option.setAttribute('aria-selected', String(option.selected))
208
+ })
209
+ }
210
+
211
+ private filterOptions(searchTerm: string) {
212
+ const term = searchTerm.trim()
213
+
214
+ if (!term) {
215
+ // Show all options if no search term
216
+ this.options.forEach(option => {
217
+ option.hidden = false
218
+ option.style.order = '0'
219
+ })
220
+ this.visibleOptionsCount = this.options.length
221
+ this.hasResults = true
222
+ } else {
223
+ // Calculate similarity scores for all options
224
+ const scoredOptions: FilteredOption[] = this.options.map(option => {
225
+ const optionLabel = option.label || option.textContent || ''
226
+ const optionValue = option.value
227
+
228
+ const labelScore = similarity(term, optionLabel)
229
+ const valueScore = similarity(term, optionValue)
230
+ const score = Math.max(labelScore * 1.1, valueScore)
231
+
232
+ return { option, score }
233
+ })
234
+
235
+ // Sort by score (highest first)
236
+ scoredOptions.sort((a, b) => b.score - a.score)
237
+
238
+ // Apply visibility and ordering
239
+ let visibleCount = 0
240
+ scoredOptions.forEach((item, index) => {
241
+ const { option, score } = item
242
+
243
+ if (score < this.similarityThreshold) {
244
+ option.hidden = true
245
+ } else {
246
+ option.hidden = false
247
+ visibleCount++
248
+ option.style.order = String(index)
249
+ }
250
+ })
251
+
252
+ this.visibleOptionsCount = visibleCount
253
+ this.hasResults = visibleCount > 0
254
+ }
255
+
256
+ this.announceToScreenReader(
257
+ this.visibleOptionsCount > 0
258
+ ? `${this.visibleOptionsCount} option${this.visibleOptionsCount === 1 ? '' : 's'} available.`
259
+ : 'No results found.',
260
+ )
261
+ }
262
+
263
+ private openDropdown() {
264
+ this.open = true
265
+ // Reset filters based on current input value when dropdown opens
266
+ this.filterOptions(this.inputValue)
267
+ }
268
+
269
+ private selectOption(option: SchmancyOption) {
270
+ if (this.multi) {
271
+ const currentValues = this.selectedValues$.value
272
+ const index = currentValues.indexOf(option.value)
273
+ const newValues = index > -1 ? currentValues.filter(v => v !== option.value) : [...currentValues, option.value]
274
+
275
+ this.selectedValues$.next(newValues)
276
+ this.announceToScreenReader(
277
+ newValues.length > 0 ? `Selected: ${this.getSelectedLabels().join(', ')}` : 'No options selected',
278
+ )
279
+ this.fireChangeEvent()
280
+ } else {
281
+ // Update value first
282
+ this.selectedValue$.next(option.value)
283
+
284
+ // Close dropdown IMMEDIATELY to prevent blur handler from firing
285
+ this.open = false
286
+
287
+ // Now fire event with the NEW value
288
+ this.fireChangeEvent()
289
+
290
+ // Update UI
291
+ this.inputValue = option.label || option.textContent || ''
292
+ this.inputValue$.next(this.inputValue)
293
+
294
+ this.announceToScreenReader(`Selected: ${option.label || option.textContent}`)
295
+ }
296
+ }
297
+
298
+ private setupDocumentClickHandler() {
299
+ fromEvent<MouseEvent>(document, 'click')
300
+ .pipe(takeUntil(this.disconnecting))
301
+ .subscribe(e => {
302
+ if (!this.open) return
303
+ const path = e.composedPath()
304
+ if (!path.includes(this) && !this.options.some(opt => path.includes(opt))) {
305
+ this.open = false
306
+ this.updateInputDisplay()
307
+ }
308
+ })
309
+ }
310
+
311
+ private updateInputDisplay() {
312
+ // For multi-select, we don't update input display since chips show the selections
313
+ if (this.multi) return
314
+
315
+ const selectedValue = this.selectedValue$.value
316
+ const option = this.options.find(opt => opt.value === selectedValue)
317
+ this.inputValue = option ? option.label || option.textContent || '' : ''
318
+ this.inputValue$.next(this.inputValue)
319
+
320
+ if (this.inputElementRef.value) {
321
+ this.inputElementRef.value.value = this.inputValue
322
+ }
323
+ }
324
+
325
+ private getSelectedLabels(): string[] {
326
+ return this.options
327
+ .filter(option =>
328
+ this.multi ? this.selectedValues$.value.includes(option.value) : option.value === this.selectedValue$.value,
329
+ )
330
+ .map(option => option.label || option.textContent || '')
331
+ }
332
+
333
+ private announceToScreenReader(message: string) {
334
+ const liveRegion = this.shadowRoot?.querySelector('#live-status')
335
+ if (liveRegion) {
336
+ liveRegion.textContent = message
337
+ }
338
+ }
339
+
340
+ private fireChangeEvent() {
341
+ const detail: SchmancyAutocompleteChangeEvent['detail'] = {
342
+ value: this.value,
343
+ }
344
+
345
+ if (this.multi) {
346
+ detail.values = [...this.selectedValues$.value]
347
+ }
348
+
349
+ this.dispatchEvent(
350
+ new CustomEvent<SchmancyAutocompleteChangeEvent['detail']>('change', {
351
+ detail,
352
+ bubbles: true,
353
+ composed: true,
354
+ }),
355
+ )
356
+ }
357
+
358
+ public checkValidity(): boolean {
359
+ if (!this.required) return true
360
+ return this.multi ? this.selectedValues$.value.length > 0 : Boolean(this.selectedValue$.value)
361
+ }
362
+
363
+ public reportValidity(): boolean {
364
+ if (this.inputElementRef.value) {
365
+ return this.inputElementRef.value.reportValidity()
366
+ }
367
+ return this.checkValidity()
368
+ }
369
+
370
+ firstUpdated() {
371
+ this.setupOptionHandlers()
372
+
373
+ // Sync initial value with display after options are available
374
+ this.updateInputDisplay()
375
+
376
+ // Update options when slot changes
377
+ const slot = this.shadowRoot?.querySelector('slot')
378
+ slot?.addEventListener('slotchange', () => {
379
+ this.setupOptionHandlers()
380
+ this.updateOptionSelection(this.selectedValue$.value, this.selectedValues$.value)
381
+ })
382
+ }
383
+
384
+ private handleChipRemove(value: string) {
385
+ const currentValues = this.selectedValues$.value
386
+ const newValues = currentValues.filter(v => v !== value)
387
+ this.selectedValues$.next(newValues)
388
+ this.fireChangeEvent()
389
+ this.announceToScreenReader(`Removed: ${this.getChipLabel(value)}`)
390
+ }
391
+
392
+ private getChipLabel(value: string): string {
393
+ const option = this.options.find(opt => opt.value === value)
394
+ return option ? option.label || option.textContent || value : value
395
+ }
396
+
397
+ private focusTextInput() {
398
+ if (this.inputElementRef.value) {
399
+ this.inputElementRef.value.focus()
400
+ }
401
+ }
402
+
403
+ render() {
404
+ const descriptionId = `${this.id}-desc`
405
+
406
+ // Get size-based styling to match Schmancy input
407
+ const getSizeStyles = () => {
408
+ switch (this.size) {
409
+ case 'sm':
410
+ return {
411
+ height: 'min-h-[40px]',
412
+ padding: 'px-2',
413
+ fontSize: 'text-sm', // 14px
414
+ labelSize: 'text-sm',
415
+ }
416
+ case 'lg':
417
+ return {
418
+ height: 'min-h-[60px]',
419
+ padding: 'px-5',
420
+ fontSize: 'text-lg', // 18px
421
+ labelSize: 'text-lg',
422
+ }
423
+ case 'md':
424
+ default:
425
+ return {
426
+ height: 'min-h-[50px]',
427
+ padding: 'px-4',
428
+ fontSize: 'text-base', // 16px
429
+ labelSize: 'text-base',
430
+ }
431
+ }
432
+ }
433
+
434
+ const { height, padding, fontSize, labelSize } = getSizeStyles()
435
+
436
+ return html`
437
+ <div class="relative">
438
+ <!-- Screen reader live region -->
439
+ <div id="live-status" role="status" aria-live="polite" class="sr-only"></div>
440
+
441
+ <!-- Description -->
442
+ ${this.description ? html`<div id="${descriptionId}" class="sr-only">${this.description}</div>` : ''}
443
+
444
+ <!-- Custom input wrapper for Gmail-style chip input -->
445
+ <slot name="trigger">
446
+ ${when(
447
+ this.multi,
448
+ () => html`
449
+ <!-- Custom multi-select input with inline chips -->
450
+ <div class="relative">
451
+ ${when(
452
+ this.label,
453
+ () => html`
454
+ <label
455
+ class="${classMap({
456
+ 'block mb-1 font-medium': true,
457
+ 'text-primary-default': !this.error,
458
+ 'text-error-default': this.error,
459
+ [labelSize]: true,
460
+ })}"
461
+ >
462
+ ${this.label}${this.required ? html`<span class="text-error-default ml-1">*</span>` : ''}
463
+ </label>
464
+ `,
465
+ )}
466
+ <div
467
+ class="${classMap({
468
+ 'flex flex-wrap items-center gap-1': true,
469
+ [height]: true,
470
+ [padding]: true,
471
+ 'block w-full min-w-0 rounded-[8px] border-0': true,
472
+ 'bg-surface-highest text-surface-on': true,
473
+ 'ring-0 ring-inset focus-within:ring-1 focus-within:ring-inset': true,
474
+ 'ring-secondary-default focus-within:ring-secondary-default': !this.error,
475
+ 'ring-error-default focus-within:ring-error-default': this.error,
476
+ 'cursor-text transition-colors duration-200': true,
477
+ })}"
478
+ @click=${() => this.focusTextInput()}
479
+ role="combobox"
480
+ aria-autocomplete="list"
481
+ aria-haspopup="listbox"
482
+ aria-controls="options"
483
+ aria-expanded=${this.open}
484
+ >
485
+ <!-- Render chips inline -->
486
+ ${repeat(
487
+ this.selectedValues$.value,
488
+ value => value,
489
+ value => html`
490
+ <schmancy-input-chip
491
+ .value=${value}
492
+ @remove=${(e: CustomEvent) => this.handleChipRemove(e.detail.value)}
493
+ class="shrink-0 my-0.5"
494
+ >
495
+ ${this.getChipLabel(value)}
496
+ </schmancy-input-chip>
497
+ `,
498
+ )}
499
+
500
+ <!-- Text input for typing -->
501
+ <input
502
+ ${ref(this.inputElementRef)}
503
+ id="autocomplete-input"
504
+ type="text"
505
+ class="flex-1 min-w-[120px] py-1 bg-transparent border-none outline-none ${fontSize} font-medium text-surface-on placeholder:text-muted"
506
+ name=${this.name || this.label?.toLowerCase().replace(/\s+/g, '-') || ''}
507
+ .placeholder=${this.selectedValues$.value.length > 0 ? 'Add more...' : this.placeholder}
508
+ .value=${this.inputValue}
509
+ .autocomplete=${this.autocomplete}
510
+ aria-invalid=${this.error ? 'true' : 'false'}
511
+ aria-required=${this.required ? 'true' : 'false'}
512
+ aria-describedby=${this.error && this.validationMessage ? `${this.a11yId}-err` : nothing}
513
+ aria-label=${!this.label && this.placeholder ? this.placeholder : nothing}
514
+ @input=${(e: Event) => {
515
+ const value = (e.target as HTMLInputElement).value
516
+ this.inputValue = value
517
+ this.inputValue$.next(value)
518
+ }}
519
+ @focus=${(e: FocusEvent) => {
520
+ e.stopPropagation()
521
+ // Clear input on focus for new searches
522
+ this.inputValue = ''
523
+ this.inputValue$.next('')
524
+ this.openDropdown()
525
+ }}
526
+ @keydown=${(e: KeyboardEvent) => {
527
+ this.handleKeyDown(e)
528
+ }}
529
+ @blur=${() => {
530
+ this.handleAutoSelectOnBlur()
531
+ }}
532
+ />
533
+ </div>
534
+
535
+ <!-- Validation message -->
536
+ ${when(
537
+ this.error && this.validationMessage,
538
+ () => html`
539
+ <div id="${this.a11yId}-err" class="mt-1 text-sm text-error-default" role="alert">
540
+ ${this.validationMessage}
541
+ </div>
542
+ `,
543
+ )}
544
+ </div>
545
+ `,
546
+ () => html`
547
+ <!-- Regular single-select input -->
548
+ <schmancy-input
549
+ .size=${this.size}
550
+ ${ref(this.inputElementRef)}
551
+ id="autocomplete-input"
552
+ class="w-full"
553
+ .name=${this.name || this.label?.toLowerCase().replace(/\s+/g, '-') || ''}
554
+ .label=${this.label}
555
+ .placeholder=${this.placeholder}
556
+ .required=${this.required}
557
+ .value=${this.inputValue}
558
+ type="text"
559
+ autocomplete=${this.autocomplete}
560
+ clickable
561
+ role="combobox"
562
+ aria-autocomplete="list"
563
+ aria-haspopup="listbox"
564
+ aria-controls="options"
565
+ aria-expanded=${this.open}
566
+ aria-describedby=${ifDefined(this.description ? descriptionId : undefined)}
567
+ @input=${(e: Event) => {
568
+ const value = (e.target as HTMLInputElement).value
569
+ this.inputValue = value
570
+ this.inputValue$.next(value)
571
+ }}
572
+ @focus=${(e: FocusEvent) => {
573
+ e.stopPropagation()
574
+ this.openDropdown()
575
+ }}
576
+ @click=${(e: MouseEvent) => {
577
+ e.stopPropagation()
578
+ this.openDropdown()
579
+ }}
580
+ @keydown=${(e: KeyboardEvent) => {
581
+ this.handleKeyDown(e)
582
+ }}
583
+ @blur=${() => {
584
+ this.handleAutoSelectOnBlur()
585
+ }}
586
+ >
587
+ </schmancy-input>
588
+ `,
589
+ )}
590
+ </slot>
591
+
592
+ <!-- Options dropdown -->
593
+ <ul
594
+ id="options"
595
+ class=${classMap({
596
+ absolute: true,
597
+ 'z-[1000]': true,
598
+ 'mt-1': true,
599
+ 'w-full': true,
600
+ 'rounded-md': true,
601
+ 'shadow-md': true,
602
+ 'overflow-auto': true,
603
+ 'min-w-full': true,
604
+ 'bg-surface-low': true,
605
+ flex: true,
606
+ 'flex-col': true, // Enable flexbox for ordering
607
+ })}
608
+ role="listbox"
609
+ aria-multiselectable=${this.multi ? 'true' : 'false'}
610
+ aria-label=${`${this.label || 'Options'} dropdown`}
611
+ ?hidden=${!this.open}
612
+ style="max-height: ${this.maxHeight}; display: ${this.open ? 'flex' : 'none'};"
613
+ @slotchange=${() => {
614
+ this.setupOptionHandlers()
615
+ }}
616
+ >
617
+ <slot></slot>
618
+ ${!this.hasResults ? html` <li class="px-3 py-2 text-sm text-muted">No results found</li> ` : ''}
619
+ </ul>
620
+ </div>
621
+ `
622
+ }
623
+
624
+ private handleAutoSelectOnBlur() {
625
+ // Only auto-select in single-select mode and when dropdown is open with a search term
626
+ if (this.multi || !this.open || !this.inputValue.trim()) {
627
+ return
628
+ }
629
+
630
+ const searchTerm = this.inputValue.trim()
631
+
632
+ // Find the best matching option using the same similarity logic as filtering
633
+ let bestMatch: SchmancyOption | null = null
634
+ let bestScore = 0
635
+
636
+ this.options.forEach(option => {
637
+ // Skip hidden options
638
+ if (option.hidden) return
639
+
640
+ // Get text to search in (prioritize label, then textContent, then value)
641
+ const optionLabel = option.label || option.textContent || ''
642
+ const optionValue = option.value
643
+
644
+ // Calculate similarity scores for both label and value
645
+ const labelScore = similarity(searchTerm, optionLabel)
646
+ const valueScore = similarity(searchTerm, optionValue)
647
+
648
+ // Use the higher score (prioritizing label matches)
649
+ const score = Math.max(labelScore * 1.1, valueScore) // Slight boost for label matches
650
+
651
+ // Keep track of best match that meets threshold
652
+ if (score > bestScore && score >= this.similarityThreshold) {
653
+ bestScore = score
654
+ bestMatch = option
655
+ }
656
+ })
657
+
658
+ // Auto-select the best match if found
659
+ if (bestMatch) {
660
+ // Silently update the selected value without firing change event
661
+ this.selectedValue$.next(bestMatch.value)
662
+ this.inputValue = bestMatch.label || bestMatch.textContent || ''
663
+ this.inputValue$.next(this.inputValue)
664
+ this.open = false
665
+ }
666
+ }
667
+
668
+ private handleKeyDown(event: KeyboardEvent) {
669
+ const isOpen = this.open
670
+ const selectedValues = this.selectedValues$.value
671
+
672
+ // Handle backspace to remove last chip in multi-select when input is empty
673
+ if (this.multi && event.key === 'Backspace' && !this.inputValue && selectedValues.length > 0 && !isOpen) {
674
+ event.preventDefault()
675
+ const lastValue = selectedValues[selectedValues.length - 1]
676
+ this.handleChipRemove(lastValue)
677
+ return
678
+ }
679
+
680
+ if (!isOpen && (event.key === 'ArrowDown' || event.key === 'Enter')) {
681
+ event.preventDefault()
682
+ this.openDropdown()
683
+
684
+ timer(10)
685
+ .pipe(takeUntil(this.disconnecting))
686
+ .subscribe(() => {
687
+ const firstVisible = this.options.find(opt => !opt.hidden)
688
+ firstVisible?.focus()
689
+ })
690
+ return
691
+ }
692
+
693
+ if (!isOpen) return
694
+
695
+ const visibleOptions = this.options
696
+ .filter(opt => !opt.hidden)
697
+ .toSorted((a, b) => parseInt(a.style.order || '0') - parseInt(b.style.order || '0'))
698
+
699
+ const focusedOption = visibleOptions.find(opt => opt === document.activeElement)
700
+ const currentIndex = focusedOption ? visibleOptions.indexOf(focusedOption) : -1
701
+
702
+ switch (event.key) {
703
+ case 'Escape':
704
+ event.preventDefault()
705
+ this.open = false
706
+ this.updateInputDisplay()
707
+ this.inputElementRef.value?.focus()
708
+ break
709
+
710
+ case 'Tab':
711
+ this.open = false
712
+ this.updateInputDisplay()
713
+ break
714
+
715
+ case 'ArrowDown':
716
+ event.preventDefault()
717
+ const nextIndex = currentIndex < visibleOptions.length - 1 ? currentIndex + 1 : 0
718
+ visibleOptions[nextIndex]?.focus()
719
+ break
720
+
721
+ case 'ArrowUp':
722
+ event.preventDefault()
723
+ const prevIndex = currentIndex > 0 ? currentIndex - 1 : visibleOptions.length - 1
724
+ visibleOptions[prevIndex]?.focus()
725
+ break
726
+
727
+ case 'Home':
728
+ event.preventDefault()
729
+ visibleOptions[0]?.focus()
730
+ break
731
+
732
+ case 'End':
733
+ event.preventDefault()
734
+ visibleOptions[visibleOptions.length - 1]?.focus()
735
+ break
736
+
737
+ case 'Enter':
738
+ case ' ':
739
+ if (focusedOption) {
740
+ event.preventDefault()
741
+ this.selectOption(focusedOption)
742
+ }
743
+ break
744
+ }
745
+ }
756
746
  }
757
747
 
758
-
759
-
760
748
  declare global {
761
- interface HTMLElementTagNameMap {
762
- 'schmancy-autocomplete': SchmancyAutocomplete
763
- }
749
+ interface HTMLElementTagNameMap {
750
+ 'schmancy-autocomplete': SchmancyAutocomplete
751
+ }
764
752
  }