@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
@@ -1,1006 +1,994 @@
1
- // src/area/area.service.test.ts
2
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
3
- import { area } from './area.service'
4
- import type { ActiveRoute } from './router.types'
5
-
6
- // Create a test-specific area service class for testing private methods
7
- class TestAreaService {
8
- public prettyURL = false
9
- public enableHistoryMode = true
10
-
11
- // Copy the createCleanURL method for direct testing
12
- createCleanURL(areas: Record<string, ActiveRoute>, clearQueryParams?: string[] | boolean | null): string {
13
- // Get the current base path (everything except the last segment which might be encoded state)
14
- const currentPath = location.pathname
15
- const pathSegments = currentPath.split('/')
16
- let basePath = '/'
17
-
18
- // Check if the last segment is encoded state (contains { or %7B)
19
- const lastSegment = pathSegments[pathSegments.length - 1]
20
- if (lastSegment && (lastSegment.includes('{') || lastSegment.includes('%7B'))) {
21
- // Remove the encoded state segment to get the base path
22
- pathSegments.pop()
23
- basePath = pathSegments.join('/') || '/'
24
- } else {
25
- // Keep the current path as base path
26
- basePath = currentPath
27
- }
28
-
29
- // Ensure base path ends properly
30
- if (basePath !== '/' && !basePath.endsWith('/')) {
31
- basePath += '/'
32
- }
33
-
34
- // Handle query parameters
35
- let queryString = ''
36
-
37
- if (clearQueryParams !== true) {
38
- // Get current query params
39
- const urlParams = new URLSearchParams(location.search)
40
-
41
- // Clear specific params if provided
42
- if (Array.isArray(clearQueryParams)) {
43
- clearQueryParams.forEach(param => urlParams.delete(param))
44
- }
45
-
46
- // Convert back to string
47
- queryString = urlParams.toString()
48
- queryString = queryString ? `?${queryString}` : ''
49
- }
50
- // If clearQueryParams === true, queryString remains empty (all params cleared)
51
-
52
- if (this.prettyURL) {
53
- // Create pretty URLs - customize this based on your routing needs
54
- const mainArea = areas.main
55
- if (mainArea) {
56
- let path = basePath === '/' ? `/${mainArea.component}` : `${basePath}${mainArea.component}`
57
-
58
- // Add simple params to URL
59
- const searchParams = new URLSearchParams(queryString)
60
- if (mainArea.params) {
61
- Object.entries(mainArea.params).forEach(([key, value]) => {
62
- if (typeof value === 'string' || typeof value === 'number') {
63
- searchParams.set(key, String(value))
64
- }
65
- })
66
- }
67
-
68
- const query = searchParams.toString()
69
- return path + (query ? `?${query}` : '')
70
- }
71
- }
72
-
73
- // Fallback to encoded state in URL (original behavior)
74
- try {
75
- // Clean up empty objects before encoding
76
- type CleanRoute = Omit<ActiveRoute, 'area'>
77
- const cleanedAreas: Record<string, CleanRoute> = {}
78
- Object.entries(areas).forEach(([areaName, route]) => {
79
- const cleanRoute: CleanRoute = { component: route.component }
80
-
81
- // Only include state if it has content
82
- if (route.state && Object.keys(route.state).length > 0) {
83
- cleanRoute.state = route.state
84
- }
85
-
86
- // Only include params if it has content
87
- if (route.params && Object.keys(route.params).length > 0) {
88
- cleanRoute.params = route.params
89
- }
90
-
91
- // Only include props if it has content
92
- if (route.props && Object.keys(route.props).length > 0) {
93
- cleanRoute.props = route.props
94
- }
95
-
96
- cleanedAreas[areaName] = cleanRoute
97
- })
98
-
99
- // If cleanedAreas is empty, preserve the base path
100
- if (Object.keys(cleanedAreas).length === 0) {
101
- const cleanBasePath = basePath === '/' ? '' : basePath.replace(/\/$/, '')
102
- return `${cleanBasePath}${queryString}`
103
- }
104
-
105
- const encoded = encodeURIComponent(JSON.stringify(cleanedAreas))
106
- const cleanBasePath = basePath === '/' ? '' : basePath.replace(/\/$/, '')
107
- return `${cleanBasePath}/${encoded}${queryString}`
108
- } catch (error) {
109
- console.error('Failed to encode URL state:', error)
110
- return location.pathname
111
- }
112
- }
113
- }
114
-
115
- // Mock global objects
116
- const mockHistory = {
117
- state: {},
118
- pushState: vi.fn(),
119
- replaceState: vi.fn(),
120
- }
121
-
122
- const mockLocation = {
123
- pathname: '/',
124
- search: '',
125
- hash: '',
126
- }
127
-
128
- const mockWindow = {
129
- history: mockHistory,
130
- location: mockLocation,
131
- addEventListener: vi.fn(),
132
- dispatchEvent: vi.fn(),
133
- removeEventListener: vi.fn(),
134
- }
135
-
136
- // Setup global mocks
137
- beforeEach(() => {
138
- vi.clearAllMocks()
139
-
140
- // Reset history and location state
141
- mockHistory.state = {}
142
- mockLocation.pathname = '/'
143
- mockLocation.search = ''
144
- mockLocation.hash = ''
145
-
146
- // Mock global objects
147
- global.history = mockHistory as any
148
- global.location = mockLocation as any
149
- global.window = mockWindow as any
150
-
151
- // Clear any existing areas
152
- area.clear()
153
- })
154
-
155
- afterEach(() => {
156
- vi.clearAllMocks()
157
- area.clear()
158
- })
159
-
160
- describe('AreaService - Base Path Preservation Tests', () => {
161
- let testService: TestAreaService
162
-
163
- beforeEach(() => {
164
- testService = new TestAreaService()
165
- })
166
-
167
- describe('createCleanURL base path preservation', () => {
168
- it('should maintain base path when navigating from /demo/', () => {
169
- mockLocation.pathname = '/demo/'
170
-
171
- const result = testService.createCleanURL({})
172
-
173
- expect(result).toBe('/demo')
174
- })
175
-
176
- it('should maintain base path when navigating from /demo/area/', () => {
177
- mockLocation.pathname = '/demo/area/'
178
-
179
- const result = testService.createCleanURL({})
180
-
181
- expect(result).toBe('/demo/area')
182
- })
183
-
184
- it('should maintain base path when navigating from /app/admin/', () => {
185
- mockLocation.pathname = '/app/admin/'
186
-
187
- const result = testService.createCleanURL({})
188
-
189
- expect(result).toBe('/app/admin')
190
- })
191
-
192
- it('should work correctly from root /', () => {
193
- mockLocation.pathname = '/'
194
-
195
- const result = testService.createCleanURL({})
196
-
197
- expect(result).toBe('')
198
- })
199
-
200
- it('should preserve nested paths like /demo/area/', () => {
201
- mockLocation.pathname = '/demo/area/sub'
202
-
203
- const result = testService.createCleanURL({})
204
-
205
- expect(result).toBe('/demo/area/sub')
206
- })
207
-
208
- it('should handle deep nested paths', () => {
209
- mockLocation.pathname = '/demo/app/users/profile/settings'
210
-
211
- const result = testService.createCleanURL({})
212
-
213
- expect(result).toBe('/demo/app/users/profile/settings')
214
- })
215
-
216
- it('should detect and remove encoded state from /demo/', () => {
217
- mockLocation.pathname = '/demo/%7B%22main%22%3A%7B%22component%22%3A%22test%22%7D%7D'
218
-
219
- const result = testService.createCleanURL({})
220
-
221
- expect(result).toBe('/demo')
222
- })
223
-
224
- it('should detect and remove encoded state from nested paths', () => {
225
- mockLocation.pathname = '/demo/area/page/%7B%22sidebar%22%3A%7B%22component%22%3A%22info%22%7D%7D'
226
-
227
- const result = testService.createCleanURL({})
228
-
229
- expect(result).toBe('/demo/area/page')
230
- })
231
-
232
- it('should handle encoded state with unencoded brackets', () => {
233
- mockLocation.pathname = '/demo/{"main":{"component":"test"}}'
234
-
235
- const result = testService.createCleanURL({})
236
-
237
- expect(result).toBe('/demo')
238
- })
239
-
240
- it('should preserve base path when removing multiple encoded segments', () => {
241
- mockLocation.pathname = '/demo/area/%7B%22main%22%3A%7B%22component%22%3A%22test%22%7D%2C%22sidebar%22%3A%7B%22component%22%3A%22info%22%7D%7D'
242
-
243
- const result = testService.createCleanURL({})
244
-
245
- expect(result).toBe('/demo/area')
246
- })
247
- })
248
-
249
- describe('encoded state URL generation', () => {
250
- it('should add encoded state to clean URLs (/demo/ becomes /demo/encoded)', () => {
251
- mockLocation.pathname = '/demo/'
252
-
253
- const areas = {
254
- main: {
255
- component: 'test-component',
256
- state: { page: 1 },
257
- params: {},
258
- props: {},
259
- area: 'main'
260
- }
261
- }
262
-
263
- const result = testService.createCleanURL(areas)
264
-
265
- expect(result).toMatch(/^\/demo\//)
266
- expect(result).toMatch(/%7B.*%7D$/)
267
-
268
- // Verify the encoded content
269
- const encodedPart = result.split('/').pop()!
270
- const decoded = JSON.parse(decodeURIComponent(encodedPart))
271
- expect(decoded.main.component).toBe('test-component')
272
- expect(decoded.main.state).toEqual({ page: 1 })
273
- })
274
-
275
- it('should handle removing encoded state (/demo/encoded back to /demo/)', () => {
276
- mockLocation.pathname = '/demo/%7B%22main%22%3A%7B%22component%22%3A%22test%22%2C%22state%22%3A%7B%22page%22%3A1%7D%7D%7D'
277
-
278
- const result = testService.createCleanURL({})
279
-
280
- expect(result).toBe('/demo')
281
- })
282
-
283
- it('should handle multiple area states correctly', () => {
284
- mockLocation.pathname = '/demo/area/'
285
-
286
- const areas = {
287
- main: {
288
- component: 'main-component',
289
- state: { tab: 'home' },
290
- params: { id: '123' },
291
- props: {},
292
- area: 'main'
293
- },
294
- sidebar: {
295
- component: 'sidebar-component',
296
- state: { expanded: true },
297
- params: {},
298
- props: { theme: 'dark' },
299
- area: 'sidebar'
300
- }
301
- }
302
-
303
- const result = testService.createCleanURL(areas)
304
-
305
- expect(result).toMatch(/^\/demo\/area\//)
306
-
307
- // Decode and verify content
308
- const encodedPart = result.split('/').pop()!
309
- const decoded = JSON.parse(decodeURIComponent(encodedPart))
310
- expect(decoded).toHaveProperty('main')
311
- expect(decoded).toHaveProperty('sidebar')
312
- expect(decoded.main.state).toEqual({ tab: 'home' })
313
- expect(decoded.sidebar.state).toEqual({ expanded: true })
314
- })
315
-
316
- it('should not add empty state to encoded URLs', () => {
317
- mockLocation.pathname = '/demo/'
318
-
319
- const areas = {
320
- main: {
321
- component: 'test-component',
322
- state: {},
323
- params: {},
324
- props: {},
325
- area: 'main'
326
- }
327
- }
328
-
329
- const result = testService.createCleanURL(areas)
330
-
331
- const encodedPart = result.split('/').pop()!
332
- const decoded = JSON.parse(decodeURIComponent(encodedPart))
333
- expect(decoded.main).toEqual({ component: 'test-component' })
334
- expect(decoded.main).not.toHaveProperty('state')
335
- expect(decoded.main).not.toHaveProperty('params')
336
- expect(decoded.main).not.toHaveProperty('props')
337
- })
338
- })
339
- })
340
-
341
- describe('AreaService - Clear Method Tests', () => {
342
- beforeEach(() => {
343
- area.enableHistoryMode = true
344
- })
345
-
346
- it('should preserve /demo/ path when clearing areas', () => {
347
- mockLocation.pathname = '/demo/%7B%22main%22%3A%7B%22component%22%3A%22test%22%7D%7D'
348
-
349
- area.clear()
350
-
351
- expect(mockHistory.replaceState).toHaveBeenCalledWith(
352
- { schmancyAreas: {} },
353
- '',
354
- '/demo'
355
- )
356
- })
357
-
358
- it('should preserve root / when clearing from root with encoded state', () => {
359
- mockLocation.pathname = '/%7B%22main%22%3A%7B%22component%22%3A%22home%22%7D%7D'
360
-
361
- area.clear()
362
-
363
- expect(mockHistory.replaceState).toHaveBeenCalledWith(
364
- { schmancyAreas: {} },
365
- '',
366
- ''
367
- )
368
- })
369
-
370
- it('should preserve nested paths when clearing', () => {
371
- mockLocation.pathname = '/demo/app/admin/%7B%22sidebar%22%3A%7B%22component%22%3A%22nav%22%7D%7D'
372
-
373
- area.clear()
374
-
375
- expect(mockHistory.replaceState).toHaveBeenCalledWith(
376
- { schmancyAreas: {} },
377
- '',
378
- '/demo/app/admin'
379
- )
380
- })
381
-
382
- it('should remove all area states but keep base path', () => {
383
- mockLocation.pathname = '/demo/area/'
384
-
385
- // Set up some areas directly in the current map (simulating existing state)
386
- area.current.set('main', {
387
- component: 'main-component',
388
- area: 'main',
389
- state: { data: 'test' },
390
- params: {},
391
- props: {}
392
- })
393
-
394
- area.current.set('sidebar', {
395
- component: 'sidebar-component',
396
- area: 'sidebar',
397
- state: { expanded: true },
398
- params: {},
399
- props: {}
400
- })
401
-
402
- expect(area.current.size).toBe(2)
403
-
404
- area.clear()
405
-
406
- // All areas should be removed
407
- expect(area.current.size).toBe(0)
408
- expect(area.getActiveAreas()).toEqual([])
409
-
410
- // History should be updated with clean base path
411
- expect(mockHistory.replaceState).toHaveBeenCalled()
412
- const lastCall = mockHistory.replaceState.mock.calls[mockHistory.replaceState.mock.calls.length - 1]
413
- expect(lastCall[0]).toEqual({ schmancyAreas: {} })
414
- expect(lastCall[2]).toBe('/demo/area')
415
- })
416
-
417
- it('should call createCleanURL with empty object', () => {
418
- mockLocation.pathname = '/demo/area/%7B%22main%22%3A%7B%22component%22%3A%22test%22%7D%7D'
419
-
420
- // Spy on the private createCleanURL method
421
- const spy = vi.spyOn(area as any, 'createCleanURL')
422
-
423
- area.clear()
424
-
425
- expect(spy).toHaveBeenCalledWith({})
426
- })
427
-
428
- it('should clear history state properly', () => {
429
- mockLocation.pathname = '/demo/area/'
430
-
431
- // Set initial history state
432
- mockHistory.state = {
433
- schmancyAreas: {
434
- main: { component: 'test-component' }
435
- },
436
- otherData: 'should-be-preserved'
437
- }
438
-
439
- area.clear()
440
-
441
- expect(mockHistory.replaceState).toHaveBeenCalledWith(
442
- { schmancyAreas: {} },
443
- '',
444
- '/demo/area'
445
- )
446
- })
447
- })
448
-
449
- describe('AreaService - Pop Method Tests', () => {
450
- beforeEach(() => {
451
- area.enableHistoryMode = true
452
- })
453
-
454
- it('should remove only specified area from URL', () => {
455
- mockLocation.pathname = '/demo/area/'
456
-
457
- // Set up multiple areas
458
- area.current.set('main', {
459
- component: 'main-component',
460
- area: 'main',
461
- state: { data: 'main' },
462
- params: {},
463
- props: {}
464
- })
465
-
466
- area.current.set('sidebar', {
467
- component: 'sidebar-component',
468
- area: 'sidebar',
469
- state: { expanded: true },
470
- params: {},
471
- props: {}
472
- })
473
-
474
- expect(area.current.size).toBe(2)
475
-
476
- area.pop('sidebar')
477
-
478
- // Only sidebar should be removed
479
- expect(area.current.size).toBe(1)
480
- expect(area.current.has('main')).toBe(true)
481
- expect(area.current.has('sidebar')).toBe(false)
482
-
483
- // History should be updated
484
- expect(mockHistory.replaceState).toHaveBeenCalled()
485
- const lastCall = mockHistory.replaceState.mock.calls[mockHistory.replaceState.mock.calls.length - 1]
486
- expect(lastCall[2]).toMatch(/^\/demo\/area/)
487
- })
488
-
489
- it('should preserve base path when popping last area', () => {
490
- mockLocation.pathname = '/demo/area/%7B%22main%22%3A%7B%22component%22%3A%22test%22%7D%7D'
491
-
492
- // Set up single area
493
- area.current.set('main', {
494
- component: 'main-component',
495
- area: 'main',
496
- state: {},
497
- params: {},
498
- props: {}
499
- })
500
-
501
- area.pop('main')
502
-
503
- expect(area.current.size).toBe(0)
504
-
505
- // Base path should be preserved
506
- expect(mockHistory.replaceState).toHaveBeenCalled()
507
- const lastCall = mockHistory.replaceState.mock.calls[mockHistory.replaceState.mock.calls.length - 1]
508
- expect(lastCall[2]).toBe('/demo/area')
509
- })
510
-
511
- it('should update history correctly when popping areas', () => {
512
- mockLocation.pathname = '/demo/area/'
513
-
514
- // Set up areas and history state
515
- area.current.set('main', {
516
- component: 'main-component',
517
- area: 'main',
518
- state: { page: 1 },
519
- params: {},
520
- props: {}
521
- })
522
-
523
- area.current.set('sidebar', {
524
- component: 'sidebar-component',
525
- area: 'sidebar',
526
- state: { expanded: true },
527
- params: {},
528
- props: {}
529
- })
530
-
531
- // Set up initial history state to match current areas
532
- mockHistory.state = {
533
- schmancyAreas: {
534
- main: { component: 'main-component', state: { page: 1 } },
535
- sidebar: { component: 'sidebar-component', state: { expanded: true } }
536
- }
537
- }
538
-
539
- area.pop('sidebar')
540
-
541
- // Check that history state was updated correctly
542
- expect(mockHistory.replaceState).toHaveBeenCalled()
543
- const [newState] = mockHistory.replaceState.mock.calls[mockHistory.replaceState.mock.calls.length - 1]
544
-
545
- // The newState should have the remaining area (main) but not the popped one (sidebar)
546
- expect(newState.schmancyAreas).toHaveProperty('main')
547
- expect(newState.schmancyAreas).not.toHaveProperty('sidebar')
548
- })
549
-
550
- it('should handle popping non-existent area gracefully', () => {
551
- mockLocation.pathname = '/demo/area/'
552
-
553
- area.current.set('main', {
554
- component: 'main-component',
555
- area: 'main',
556
- state: {},
557
- params: {},
558
- props: {}
559
- })
560
-
561
- expect(() => area.pop('nonexistent')).not.toThrow()
562
- expect(area.current.size).toBe(1) // main area should still exist
563
- })
564
- })
565
-
566
- describe('AreaService - Pretty URL Mode Tests', () => {
567
- let testService: TestAreaService
568
-
569
- beforeEach(() => {
570
- testService = new TestAreaService()
571
- testService.prettyURL = true
572
- })
573
-
574
- it('should work with base paths in pretty URL mode', () => {
575
- mockLocation.pathname = '/demo/app/'
576
-
577
- const areas = {
578
- main: {
579
- component: 'dashboard',
580
- params: { view: 'analytics' },
581
- state: {},
582
- props: {},
583
- area: 'main'
584
- }
585
- }
586
-
587
- const result = testService.createCleanURL(areas)
588
-
589
- expect(result).toBe('/demo/app/dashboard?view=analytics')
590
- })
591
-
592
- it('should add pretty URL parameters correctly', () => {
593
- mockLocation.pathname = '/admin/users/'
594
-
595
- const areas = {
596
- main: {
597
- component: 'user-list',
598
- params: {
599
- page: 2,
600
- limit: 50,
601
- sort: 'name'
602
- },
603
- state: {},
604
- props: {},
605
- area: 'main'
606
- }
607
- }
608
-
609
- const result = testService.createCleanURL(areas)
610
-
611
- expect(result).toBe('/admin/users/user-list?page=2&limit=50&sort=name')
612
- })
613
-
614
- it('should switch between pretty and encoded modes', () => {
615
- mockLocation.pathname = '/demo/area/'
616
-
617
- const areas = {
618
- main: {
619
- component: 'test-component',
620
- params: { id: '123' },
621
- state: {},
622
- props: {},
623
- area: 'main'
624
- }
625
- }
626
-
627
- // Pretty mode
628
- testService.prettyURL = true
629
- const prettyResult = testService.createCleanURL(areas)
630
- expect(prettyResult).toBe('/demo/area/test-component?id=123')
631
-
632
- // Encoded mode
633
- testService.prettyURL = false
634
- const encodedResult = testService.createCleanURL(areas)
635
- expect(encodedResult).toMatch(/^\/demo\/area\//)
636
- expect(encodedResult).toMatch(/%7B.*%7D$/)
637
- })
638
-
639
- it('should fallback to encoded for non-main areas in pretty mode', () => {
640
- mockLocation.pathname = '/demo/area/'
641
-
642
- const areas = {
643
- sidebar: {
644
- component: 'navigation',
645
- state: { expanded: true },
646
- params: {},
647
- props: {},
648
- area: 'sidebar'
649
- }
650
- }
651
-
652
- const result = testService.createCleanURL(areas)
653
-
654
- // Should fallback to encoded URL for non-main areas
655
- expect(result).toMatch(/^\/demo\/area\//)
656
- expect(result).toMatch(/%7B.*%7D$/)
657
- })
658
- })
659
-
660
- describe('AreaService - Query Parameter Tests', () => {
661
- let testService: TestAreaService
662
-
663
- beforeEach(() => {
664
- testService = new TestAreaService()
665
- })
666
-
667
- it('should preserve query parameters during navigation', () => {
668
- mockLocation.pathname = '/demo/area/'
669
- mockLocation.search = '?theme=dark&lang=en'
670
-
671
- const result = testService.createCleanURL({})
672
-
673
- expect(result).toBe('/demo/area?theme=dark&lang=en')
674
- })
675
-
676
- it('should work with clearQueryParams option', () => {
677
- mockLocation.pathname = '/demo/area/'
678
- mockLocation.search = '?theme=dark&lang=en&temp=remove'
679
-
680
- const result = testService.createCleanURL({}, ['temp'])
681
-
682
- expect(result).toBe('/demo/area?theme=dark&lang=en')
683
- expect(result).not.toMatch(/temp=remove/)
684
- })
685
-
686
- it('should clear all query params when clearQueryParams is true', () => {
687
- mockLocation.pathname = '/demo/area/'
688
- mockLocation.search = '?theme=dark&lang=en&temp=remove'
689
-
690
- const result = testService.createCleanURL({}, true)
691
-
692
- expect(result).toBe('/demo/area')
693
- })
694
-
695
- it('should handle query params with encoded URLs', () => {
696
- mockLocation.pathname = '/demo/area/'
697
- mockLocation.search = '?theme=dark'
698
-
699
- const areas = {
700
- main: {
701
- component: 'test-component',
702
- state: { page: 1 },
703
- params: {},
704
- props: {},
705
- area: 'main'
706
- }
707
- }
708
-
709
- const result = testService.createCleanURL(areas)
710
-
711
- expect(result).toMatch(/theme=dark/)
712
- expect(result).toMatch(/%7B.*%7D/)
713
- })
714
-
715
- it('should preserve URL-encoded query parameters', () => {
716
- mockLocation.pathname = '/demo/area/'
717
- mockLocation.search = '?search=hello%20world&special=%26%3D%3F'
718
-
719
- const result = testService.createCleanURL({})
720
-
721
- expect(result).toMatch(/search=/)
722
- expect(result).toMatch(/special=/)
723
- })
724
- })
725
-
726
- describe('AreaService - Edge Cases', () => {
727
- let testService: TestAreaService
728
-
729
- beforeEach(() => {
730
- testService = new TestAreaService()
731
- })
732
-
733
- it('should handle URLs with special characters in base path', () => {
734
- mockLocation.pathname = '/démö/área-especial/tëst/'
735
-
736
- const result = testService.createCleanURL({})
737
-
738
- expect(result).toBe('/démö/área-especial/tëst')
739
- })
740
-
741
- it('should handle very long base paths', () => {
742
- const longPath = '/very/long/nested/path/with/many/segments/that/goes/on/and/on/and/on'
743
- mockLocation.pathname = longPath + '/'
744
-
745
- const result = testService.createCleanURL({})
746
-
747
- expect(result).toBe(longPath)
748
- })
749
-
750
- it('should handle URLs with existing encoded segments that are not state', () => {
751
- mockLocation.pathname = '/demo/encoded%20path/area/'
752
-
753
- const result = testService.createCleanURL({})
754
-
755
- expect(result).toBe('/demo/encoded%20path/area')
756
- })
757
-
758
- it('should handle malformed encoded state gracefully', () => {
759
- mockLocation.pathname = '/demo/area/%7B%22invalid%22%3A'
760
-
761
- expect(() => testService.createCleanURL({})).not.toThrow()
762
- const result = testService.createCleanURL({})
763
- expect(result).toBe('/demo/area')
764
- })
765
-
766
- it('should handle empty path segments', () => {
767
- mockLocation.pathname = '/demo//area//'
768
-
769
- const result = testService.createCleanURL({})
770
-
771
- // The createCleanURL method handles empty segments by preserving them but removes trailing slash
772
- expect(result).toBe('/demo//area/')
773
- })
774
-
775
- it('should handle concurrent navigation calls', async () => {
776
- mockLocation.pathname = '/demo/area/'
777
-
778
- // Simulate multiple rapid navigation calls by directly setting areas
779
- // (since push() is async and goes through request pipeline)
780
- const promises = []
781
- for (let i = 0; i < 10; i++) {
782
- promises.push(new Promise<void>((resolve) => {
783
- setTimeout(() => {
784
- area.current.set(`area${i}`, {
785
- area: `area${i}`,
786
- component: `component${i}`,
787
- state: { index: i },
788
- params: {},
789
- props: {}
790
- })
791
- resolve()
792
- }, Math.random() * 10)
793
- }))
794
- }
795
-
796
- await Promise.all(promises)
797
-
798
- // All areas should be present
799
- expect(area.current.size).toBe(10)
800
- })
801
-
802
- it('should handle createCleanURL with null/undefined areas', () => {
803
- mockLocation.pathname = '/demo/area/'
804
-
805
- // Test with null and undefined values
806
- const areas = {
807
- main: {
808
- component: 'test-component',
809
- state: null as any,
810
- params: undefined as any,
811
- props: {},
812
- area: 'main'
813
- }
814
- }
815
-
816
- expect(() => testService.createCleanURL(areas)).not.toThrow()
817
- const result = testService.createCleanURL(areas)
818
- expect(result).toMatch(/^\/demo\/area\//)
819
- })
820
-
821
- it('should handle extremely large state objects', () => {
822
- mockLocation.pathname = '/demo/area/'
823
-
824
- // Create a large state object
825
- const largeState: Record<string, string> = {}
826
- for (let i = 0; i < 1000; i++) {
827
- largeState[`key${i}`] = `value${i}`.repeat(10)
828
- }
829
-
830
- const areas = {
831
- main: {
832
- component: 'test-component',
833
- state: largeState,
834
- params: {},
835
- props: {},
836
- area: 'main'
837
- }
838
- }
839
-
840
- expect(() => testService.createCleanURL(areas)).not.toThrow()
841
- const result = testService.createCleanURL(areas)
842
- expect(result).toMatch(/^\/demo\/area\//)
843
- })
844
- })
845
-
846
- describe('AreaService - Integration Tests', () => {
847
- beforeEach(() => {
848
- area.enableHistoryMode = true
849
- })
850
-
851
- it('should maintain functionality after multiple operations', () => {
852
- mockLocation.pathname = '/demo/app/'
853
-
854
- // Add main area directly to current map
855
- area.current.set('main', {
856
- area: 'main',
857
- component: 'dashboard',
858
- state: { view: 'analytics' },
859
- params: { period: 'monthly' },
860
- props: { title: 'Dashboard' }
861
- })
862
-
863
- expect(area.current.size).toBe(1)
864
- expect(area.hasArea('main')).toBe(true)
865
-
866
- // Add sidebar area directly to current map
867
- area.current.set('sidebar', {
868
- area: 'sidebar',
869
- component: 'navigation',
870
- state: { expanded: true },
871
- params: {},
872
- props: {}
873
- })
874
-
875
- expect(area.current.size).toBe(2)
876
-
877
- // Pop sidebar
878
- area.pop('sidebar')
879
-
880
- expect(area.current.size).toBe(1)
881
- expect(area.hasArea('main')).toBe(true)
882
- expect(area.hasArea('sidebar')).toBe(false)
883
-
884
- // Clear all
885
- area.clear()
886
-
887
- expect(area.current.size).toBe(0)
888
- expect(area.getActiveAreas()).toEqual([])
889
- })
890
-
891
- it('should handle rapid state changes correctly', () => {
892
- mockLocation.pathname = '/demo/area/'
893
-
894
- // Test that multiple rapid changes to the same area work correctly
895
- area.current.set('main', {
896
- area: 'main',
897
- component: 'test',
898
- state: { step: 1 },
899
- params: {},
900
- props: {}
901
- })
902
-
903
- expect(area.getRoute('main')?.state).toEqual({ step: 1 })
904
-
905
- // Update the state
906
- area.current.set('main', {
907
- area: 'main',
908
- component: 'test',
909
- state: { step: 2 },
910
- params: {},
911
- props: {}
912
- })
913
-
914
- expect(area.getRoute('main')?.state).toEqual({ step: 2 })
915
-
916
- // Final update
917
- area.current.set('main', {
918
- area: 'main',
919
- component: 'test',
920
- state: { final: true },
921
- params: {},
922
- props: {}
923
- })
924
-
925
- expect(area.getRoute('main')?.state).toEqual({ final: true })
926
- })
927
-
928
- it('should preserve functionality across history mode changes', () => {
929
- mockLocation.pathname = '/demo/area/'
930
-
931
- // Enable history mode and directly set an area (simulating push)
932
- area.enableHistoryMode = true
933
- area.current.set('main', {
934
- area: 'main',
935
- component: 'test',
936
- state: {},
937
- params: {},
938
- props: {}
939
- })
940
-
941
- // Call updateBrowserHistory directly to test history behavior
942
- area.updateBrowserHistory('main', {
943
- area: 'main',
944
- component: 'test',
945
- state: {},
946
- params: {},
947
- props: {}
948
- })
949
-
950
- expect(mockHistory.pushState).toHaveBeenCalled()
951
-
952
- // Disable history mode
953
- area.enableHistoryMode = false
954
- area.current.set('sidebar', {
955
- area: 'sidebar',
956
- component: 'nav',
957
- state: {},
958
- params: {},
959
- props: {}
960
- })
961
-
962
- // Should still work but not update history
963
- expect(area.current.size).toBe(2)
964
- })
965
-
966
- it('should maintain RxJS stream subscriptions correctly', () => {
967
- mockLocation.pathname = '/demo/area/'
968
-
969
- // Test that the RxJS observables work correctly
970
- area.current.set('main', {
971
- area: 'main',
972
- component: 'test',
973
- state: { initial: true },
974
- params: { id: '123' },
975
- props: { title: 'Test' }
976
- })
977
-
978
- // Test synchronous access to current route
979
- const route = area.getRoute('main')
980
- expect(route).toBeDefined()
981
- expect(route?.state).toEqual({ initial: true })
982
- expect(route?.params).toEqual({ id: '123' })
983
- expect(route?.props).toEqual({ title: 'Test' })
984
-
985
- // Test that hasArea works
986
- expect(area.hasArea('main')).toBe(true)
987
- expect(area.hasArea('nonexistent')).toBe(false)
988
-
989
- // Test getActiveAreas
990
- expect(area.getActiveAreas()).toEqual(['main'])
991
-
992
- // Update state and verify
993
- area.current.set('main', {
994
- area: 'main',
995
- component: 'test',
996
- state: { updated: true },
997
- params: { id: '456' },
998
- props: { title: 'Updated' }
999
- })
1000
-
1001
- const updatedRoute = area.getRoute('main')
1002
- expect(updatedRoute?.state).toEqual({ updated: true })
1003
- expect(updatedRoute?.params).toEqual({ id: '456' })
1004
- expect(updatedRoute?.props).toEqual({ title: 'Updated' })
1005
- })
1006
- })
1
+ // src/area/area.service.test.ts
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
3
+ import { area } from './area.service'
4
+ import type { ActiveRoute } from './router.types'
5
+
6
+ // Create a test-specific area service class for testing private methods
7
+ class TestAreaService {
8
+ public prettyURL = false
9
+ public enableHistoryMode = true
10
+
11
+ // Copy the createCleanURL method for direct testing
12
+ createCleanURL(areas: Record<string, ActiveRoute>, clearQueryParams?: string[] | boolean | null): string {
13
+ // Get the current base path (everything except the last segment which might be encoded state)
14
+ const currentPath = location.pathname
15
+ const pathSegments = currentPath.split('/')
16
+ let basePath = '/'
17
+
18
+ // Check if the last segment is encoded state (contains { or %7B)
19
+ const lastSegment = pathSegments[pathSegments.length - 1]
20
+ if (lastSegment && (lastSegment.includes('{') || lastSegment.includes('%7B'))) {
21
+ // Remove the encoded state segment to get the base path
22
+ pathSegments.pop()
23
+ basePath = pathSegments.join('/') || '/'
24
+ } else {
25
+ // Keep the current path as base path
26
+ basePath = currentPath
27
+ }
28
+
29
+ // Ensure base path ends properly
30
+ if (basePath !== '/' && !basePath.endsWith('/')) {
31
+ basePath += '/'
32
+ }
33
+
34
+ // Handle query parameters
35
+ let queryString = ''
36
+
37
+ if (clearQueryParams !== true) {
38
+ // Get current query params
39
+ const urlParams = new URLSearchParams(location.search)
40
+
41
+ // Clear specific params if provided
42
+ if (Array.isArray(clearQueryParams)) {
43
+ clearQueryParams.forEach(param => urlParams.delete(param))
44
+ }
45
+
46
+ // Convert back to string
47
+ queryString = urlParams.toString()
48
+ queryString = queryString ? `?${queryString}` : ''
49
+ }
50
+ // If clearQueryParams === true, queryString remains empty (all params cleared)
51
+
52
+ if (this.prettyURL) {
53
+ // Create pretty URLs - customize this based on your routing needs
54
+ const mainArea = areas.main
55
+ if (mainArea) {
56
+ let path = basePath === '/' ? `/${mainArea.component}` : `${basePath}${mainArea.component}`
57
+
58
+ // Add simple params to URL
59
+ const searchParams = new URLSearchParams(queryString)
60
+ if (mainArea.params) {
61
+ Object.entries(mainArea.params).forEach(([key, value]) => {
62
+ if (typeof value === 'string' || typeof value === 'number') {
63
+ searchParams.set(key, String(value))
64
+ }
65
+ })
66
+ }
67
+
68
+ const query = searchParams.toString()
69
+ return path + (query ? `?${query}` : '')
70
+ }
71
+ }
72
+
73
+ // Fallback to encoded state in URL (original behavior)
74
+ try {
75
+ // Clean up empty objects before encoding
76
+ type CleanRoute = Omit<ActiveRoute, 'area'>
77
+ const cleanedAreas: Record<string, CleanRoute> = {}
78
+ Object.entries(areas).forEach(([areaName, route]) => {
79
+ const cleanRoute: CleanRoute = { component: route.component }
80
+
81
+ // Only include state if it has content
82
+ if (route.state && Object.keys(route.state).length > 0) {
83
+ cleanRoute.state = route.state
84
+ }
85
+
86
+ // Only include params if it has content
87
+ if (route.params && Object.keys(route.params).length > 0) {
88
+ cleanRoute.params = route.params
89
+ }
90
+
91
+ // Only include props if it has content
92
+ if (route.props && Object.keys(route.props).length > 0) {
93
+ cleanRoute.props = route.props
94
+ }
95
+
96
+ cleanedAreas[areaName] = cleanRoute
97
+ })
98
+
99
+ // If cleanedAreas is empty, preserve the base path
100
+ if (Object.keys(cleanedAreas).length === 0) {
101
+ const cleanBasePath = basePath === '/' ? '' : basePath.replace(/\/$/, '')
102
+ return `${cleanBasePath}${queryString}`
103
+ }
104
+
105
+ const encoded = encodeURIComponent(JSON.stringify(cleanedAreas))
106
+ const cleanBasePath = basePath === '/' ? '' : basePath.replace(/\/$/, '')
107
+ return `${cleanBasePath}/${encoded}${queryString}`
108
+ } catch (error) {
109
+ console.error('Failed to encode URL state:', error)
110
+ return location.pathname
111
+ }
112
+ }
113
+ }
114
+
115
+ // Mock global objects
116
+ const mockHistory = {
117
+ state: {},
118
+ pushState: vi.fn(),
119
+ replaceState: vi.fn(),
120
+ }
121
+
122
+ const mockLocation = {
123
+ pathname: '/',
124
+ search: '',
125
+ hash: '',
126
+ }
127
+
128
+ const mockWindow = {
129
+ history: mockHistory,
130
+ location: mockLocation,
131
+ addEventListener: vi.fn(),
132
+ dispatchEvent: vi.fn(),
133
+ removeEventListener: vi.fn(),
134
+ }
135
+
136
+ // Setup global mocks
137
+ beforeEach(() => {
138
+ vi.clearAllMocks()
139
+
140
+ // Reset history and location state
141
+ mockHistory.state = {}
142
+ mockLocation.pathname = '/'
143
+ mockLocation.search = ''
144
+ mockLocation.hash = ''
145
+
146
+ // Mock global objects
147
+ global.history = mockHistory as any
148
+ global.location = mockLocation as any
149
+ global.window = mockWindow as any
150
+
151
+ // Clear any existing areas
152
+ area.clear()
153
+ })
154
+
155
+ afterEach(() => {
156
+ vi.clearAllMocks()
157
+ area.clear()
158
+ })
159
+
160
+ describe('AreaService - Base Path Preservation Tests', () => {
161
+ let testService: TestAreaService
162
+
163
+ beforeEach(() => {
164
+ testService = new TestAreaService()
165
+ })
166
+
167
+ describe('createCleanURL base path preservation', () => {
168
+ it('should maintain base path when navigating from /demo/', () => {
169
+ mockLocation.pathname = '/demo/'
170
+
171
+ const result = testService.createCleanURL({})
172
+
173
+ expect(result).toBe('/demo')
174
+ })
175
+
176
+ it('should maintain base path when navigating from /demo/area/', () => {
177
+ mockLocation.pathname = '/demo/area/'
178
+
179
+ const result = testService.createCleanURL({})
180
+
181
+ expect(result).toBe('/demo/area')
182
+ })
183
+
184
+ it('should maintain base path when navigating from /app/admin/', () => {
185
+ mockLocation.pathname = '/app/admin/'
186
+
187
+ const result = testService.createCleanURL({})
188
+
189
+ expect(result).toBe('/app/admin')
190
+ })
191
+
192
+ it('should work correctly from root /', () => {
193
+ mockLocation.pathname = '/'
194
+
195
+ const result = testService.createCleanURL({})
196
+
197
+ expect(result).toBe('')
198
+ })
199
+
200
+ it('should preserve nested paths like /demo/area/', () => {
201
+ mockLocation.pathname = '/demo/area/sub'
202
+
203
+ const result = testService.createCleanURL({})
204
+
205
+ expect(result).toBe('/demo/area/sub')
206
+ })
207
+
208
+ it('should handle deep nested paths', () => {
209
+ mockLocation.pathname = '/demo/app/users/profile/settings'
210
+
211
+ const result = testService.createCleanURL({})
212
+
213
+ expect(result).toBe('/demo/app/users/profile/settings')
214
+ })
215
+
216
+ it('should detect and remove encoded state from /demo/', () => {
217
+ mockLocation.pathname = '/demo/%7B%22main%22%3A%7B%22component%22%3A%22test%22%7D%7D'
218
+
219
+ const result = testService.createCleanURL({})
220
+
221
+ expect(result).toBe('/demo')
222
+ })
223
+
224
+ it('should detect and remove encoded state from nested paths', () => {
225
+ mockLocation.pathname = '/demo/area/page/%7B%22sidebar%22%3A%7B%22component%22%3A%22info%22%7D%7D'
226
+
227
+ const result = testService.createCleanURL({})
228
+
229
+ expect(result).toBe('/demo/area/page')
230
+ })
231
+
232
+ it('should handle encoded state with unencoded brackets', () => {
233
+ mockLocation.pathname = '/demo/{"main":{"component":"test"}}'
234
+
235
+ const result = testService.createCleanURL({})
236
+
237
+ expect(result).toBe('/demo')
238
+ })
239
+
240
+ it('should preserve base path when removing multiple encoded segments', () => {
241
+ mockLocation.pathname =
242
+ '/demo/area/%7B%22main%22%3A%7B%22component%22%3A%22test%22%7D%2C%22sidebar%22%3A%7B%22component%22%3A%22info%22%7D%7D'
243
+
244
+ const result = testService.createCleanURL({})
245
+
246
+ expect(result).toBe('/demo/area')
247
+ })
248
+ })
249
+
250
+ describe('encoded state URL generation', () => {
251
+ it('should add encoded state to clean URLs (/demo/ becomes /demo/encoded)', () => {
252
+ mockLocation.pathname = '/demo/'
253
+
254
+ const areas = {
255
+ main: {
256
+ component: 'test-component',
257
+ state: { page: 1 },
258
+ params: {},
259
+ props: {},
260
+ area: 'main',
261
+ },
262
+ }
263
+
264
+ const result = testService.createCleanURL(areas)
265
+
266
+ expect(result).toMatch(/^\/demo\//)
267
+ expect(result).toMatch(/%7B.*%7D$/)
268
+
269
+ // Verify the encoded content
270
+ const encodedPart = result.split('/').pop()!
271
+ const decoded = JSON.parse(decodeURIComponent(encodedPart))
272
+ expect(decoded.main.component).toBe('test-component')
273
+ expect(decoded.main.state).toEqual({ page: 1 })
274
+ })
275
+
276
+ it('should handle removing encoded state (/demo/encoded back to /demo/)', () => {
277
+ mockLocation.pathname =
278
+ '/demo/%7B%22main%22%3A%7B%22component%22%3A%22test%22%2C%22state%22%3A%7B%22page%22%3A1%7D%7D%7D'
279
+
280
+ const result = testService.createCleanURL({})
281
+
282
+ expect(result).toBe('/demo')
283
+ })
284
+
285
+ it('should handle multiple area states correctly', () => {
286
+ mockLocation.pathname = '/demo/area/'
287
+
288
+ const areas = {
289
+ main: {
290
+ component: 'main-component',
291
+ state: { tab: 'home' },
292
+ params: { id: '123' },
293
+ props: {},
294
+ area: 'main',
295
+ },
296
+ sidebar: {
297
+ component: 'sidebar-component',
298
+ state: { expanded: true },
299
+ params: {},
300
+ props: { theme: 'dark' },
301
+ area: 'sidebar',
302
+ },
303
+ }
304
+
305
+ const result = testService.createCleanURL(areas)
306
+
307
+ expect(result).toMatch(/^\/demo\/area\//)
308
+
309
+ // Decode and verify content
310
+ const encodedPart = result.split('/').pop()!
311
+ const decoded = JSON.parse(decodeURIComponent(encodedPart))
312
+ expect(decoded).toHaveProperty('main')
313
+ expect(decoded).toHaveProperty('sidebar')
314
+ expect(decoded.main.state).toEqual({ tab: 'home' })
315
+ expect(decoded.sidebar.state).toEqual({ expanded: true })
316
+ })
317
+
318
+ it('should not add empty state to encoded URLs', () => {
319
+ mockLocation.pathname = '/demo/'
320
+
321
+ const areas = {
322
+ main: {
323
+ component: 'test-component',
324
+ state: {},
325
+ params: {},
326
+ props: {},
327
+ area: 'main',
328
+ },
329
+ }
330
+
331
+ const result = testService.createCleanURL(areas)
332
+
333
+ const encodedPart = result.split('/').pop()!
334
+ const decoded = JSON.parse(decodeURIComponent(encodedPart))
335
+ expect(decoded.main).toEqual({ component: 'test-component' })
336
+ expect(decoded.main).not.toHaveProperty('state')
337
+ expect(decoded.main).not.toHaveProperty('params')
338
+ expect(decoded.main).not.toHaveProperty('props')
339
+ })
340
+ })
341
+ })
342
+
343
+ describe('AreaService - Clear Method Tests', () => {
344
+ beforeEach(() => {
345
+ area.enableHistoryMode = true
346
+ })
347
+
348
+ it('should preserve /demo/ path when clearing areas', () => {
349
+ mockLocation.pathname = '/demo/%7B%22main%22%3A%7B%22component%22%3A%22test%22%7D%7D'
350
+
351
+ area.clear()
352
+
353
+ expect(mockHistory.replaceState).toHaveBeenCalledWith({ schmancyAreas: {} }, '', '/demo')
354
+ })
355
+
356
+ it('should preserve root / when clearing from root with encoded state', () => {
357
+ mockLocation.pathname = '/%7B%22main%22%3A%7B%22component%22%3A%22home%22%7D%7D'
358
+
359
+ area.clear()
360
+
361
+ expect(mockHistory.replaceState).toHaveBeenCalledWith({ schmancyAreas: {} }, '', '')
362
+ })
363
+
364
+ it('should preserve nested paths when clearing', () => {
365
+ mockLocation.pathname = '/demo/app/admin/%7B%22sidebar%22%3A%7B%22component%22%3A%22nav%22%7D%7D'
366
+
367
+ area.clear()
368
+
369
+ expect(mockHistory.replaceState).toHaveBeenCalledWith({ schmancyAreas: {} }, '', '/demo/app/admin')
370
+ })
371
+
372
+ it('should remove all area states but keep base path', () => {
373
+ mockLocation.pathname = '/demo/area/'
374
+
375
+ // Set up some areas directly in the current map (simulating existing state)
376
+ area.current.set('main', {
377
+ component: 'main-component',
378
+ area: 'main',
379
+ state: { data: 'test' },
380
+ params: {},
381
+ props: {},
382
+ })
383
+
384
+ area.current.set('sidebar', {
385
+ component: 'sidebar-component',
386
+ area: 'sidebar',
387
+ state: { expanded: true },
388
+ params: {},
389
+ props: {},
390
+ })
391
+
392
+ expect(area.current.size).toBe(2)
393
+
394
+ area.clear()
395
+
396
+ // All areas should be removed
397
+ expect(area.current.size).toBe(0)
398
+ expect(area.getActiveAreas()).toEqual([])
399
+
400
+ // History should be updated with clean base path
401
+ expect(mockHistory.replaceState).toHaveBeenCalled()
402
+ const lastCall = mockHistory.replaceState.mock.calls[mockHistory.replaceState.mock.calls.length - 1]
403
+ expect(lastCall[0]).toEqual({ schmancyAreas: {} })
404
+ expect(lastCall[2]).toBe('/demo/area')
405
+ })
406
+
407
+ it('should call createCleanURL with empty object', () => {
408
+ mockLocation.pathname = '/demo/area/%7B%22main%22%3A%7B%22component%22%3A%22test%22%7D%7D'
409
+
410
+ // Spy on the private createCleanURL method
411
+ const spy = vi.spyOn(area as any, 'createCleanURL')
412
+
413
+ area.clear()
414
+
415
+ expect(spy).toHaveBeenCalledWith({})
416
+ })
417
+
418
+ it('should clear history state properly', () => {
419
+ mockLocation.pathname = '/demo/area/'
420
+
421
+ // Set initial history state
422
+ mockHistory.state = {
423
+ schmancyAreas: {
424
+ main: { component: 'test-component' },
425
+ },
426
+ otherData: 'should-be-preserved',
427
+ }
428
+
429
+ area.clear()
430
+
431
+ expect(mockHistory.replaceState).toHaveBeenCalledWith({ schmancyAreas: {} }, '', '/demo/area')
432
+ })
433
+ })
434
+
435
+ describe('AreaService - Pop Method Tests', () => {
436
+ beforeEach(() => {
437
+ area.enableHistoryMode = true
438
+ })
439
+
440
+ it('should remove only specified area from URL', () => {
441
+ mockLocation.pathname = '/demo/area/'
442
+
443
+ // Set up multiple areas
444
+ area.current.set('main', {
445
+ component: 'main-component',
446
+ area: 'main',
447
+ state: { data: 'main' },
448
+ params: {},
449
+ props: {},
450
+ })
451
+
452
+ area.current.set('sidebar', {
453
+ component: 'sidebar-component',
454
+ area: 'sidebar',
455
+ state: { expanded: true },
456
+ params: {},
457
+ props: {},
458
+ })
459
+
460
+ expect(area.current.size).toBe(2)
461
+
462
+ area.pop('sidebar')
463
+
464
+ // Only sidebar should be removed
465
+ expect(area.current.size).toBe(1)
466
+ expect(area.current.has('main')).toBe(true)
467
+ expect(area.current.has('sidebar')).toBe(false)
468
+
469
+ // History should be updated
470
+ expect(mockHistory.replaceState).toHaveBeenCalled()
471
+ const lastCall = mockHistory.replaceState.mock.calls[mockHistory.replaceState.mock.calls.length - 1]
472
+ expect(lastCall[2]).toMatch(/^\/demo\/area/)
473
+ })
474
+
475
+ it('should preserve base path when popping last area', () => {
476
+ mockLocation.pathname = '/demo/area/%7B%22main%22%3A%7B%22component%22%3A%22test%22%7D%7D'
477
+
478
+ // Set up single area
479
+ area.current.set('main', {
480
+ component: 'main-component',
481
+ area: 'main',
482
+ state: {},
483
+ params: {},
484
+ props: {},
485
+ })
486
+
487
+ area.pop('main')
488
+
489
+ expect(area.current.size).toBe(0)
490
+
491
+ // Base path should be preserved
492
+ expect(mockHistory.replaceState).toHaveBeenCalled()
493
+ const lastCall = mockHistory.replaceState.mock.calls[mockHistory.replaceState.mock.calls.length - 1]
494
+ expect(lastCall[2]).toBe('/demo/area')
495
+ })
496
+
497
+ it('should update history correctly when popping areas', () => {
498
+ mockLocation.pathname = '/demo/area/'
499
+
500
+ // Set up areas and history state
501
+ area.current.set('main', {
502
+ component: 'main-component',
503
+ area: 'main',
504
+ state: { page: 1 },
505
+ params: {},
506
+ props: {},
507
+ })
508
+
509
+ area.current.set('sidebar', {
510
+ component: 'sidebar-component',
511
+ area: 'sidebar',
512
+ state: { expanded: true },
513
+ params: {},
514
+ props: {},
515
+ })
516
+
517
+ // Set up initial history state to match current areas
518
+ mockHistory.state = {
519
+ schmancyAreas: {
520
+ main: { component: 'main-component', state: { page: 1 } },
521
+ sidebar: { component: 'sidebar-component', state: { expanded: true } },
522
+ },
523
+ }
524
+
525
+ area.pop('sidebar')
526
+
527
+ // Check that history state was updated correctly
528
+ expect(mockHistory.replaceState).toHaveBeenCalled()
529
+ const [newState] = mockHistory.replaceState.mock.calls[mockHistory.replaceState.mock.calls.length - 1]
530
+
531
+ // The newState should have the remaining area (main) but not the popped one (sidebar)
532
+ expect(newState.schmancyAreas).toHaveProperty('main')
533
+ expect(newState.schmancyAreas).not.toHaveProperty('sidebar')
534
+ })
535
+
536
+ it('should handle popping non-existent area gracefully', () => {
537
+ mockLocation.pathname = '/demo/area/'
538
+
539
+ area.current.set('main', {
540
+ component: 'main-component',
541
+ area: 'main',
542
+ state: {},
543
+ params: {},
544
+ props: {},
545
+ })
546
+
547
+ expect(() => area.pop('nonexistent')).not.toThrow()
548
+ expect(area.current.size).toBe(1) // main area should still exist
549
+ })
550
+ })
551
+
552
+ describe('AreaService - Pretty URL Mode Tests', () => {
553
+ let testService: TestAreaService
554
+
555
+ beforeEach(() => {
556
+ testService = new TestAreaService()
557
+ testService.prettyURL = true
558
+ })
559
+
560
+ it('should work with base paths in pretty URL mode', () => {
561
+ mockLocation.pathname = '/demo/app/'
562
+
563
+ const areas = {
564
+ main: {
565
+ component: 'dashboard',
566
+ params: { view: 'analytics' },
567
+ state: {},
568
+ props: {},
569
+ area: 'main',
570
+ },
571
+ }
572
+
573
+ const result = testService.createCleanURL(areas)
574
+
575
+ expect(result).toBe('/demo/app/dashboard?view=analytics')
576
+ })
577
+
578
+ it('should add pretty URL parameters correctly', () => {
579
+ mockLocation.pathname = '/admin/users/'
580
+
581
+ const areas = {
582
+ main: {
583
+ component: 'user-list',
584
+ params: {
585
+ page: 2,
586
+ limit: 50,
587
+ sort: 'name',
588
+ },
589
+ state: {},
590
+ props: {},
591
+ area: 'main',
592
+ },
593
+ }
594
+
595
+ const result = testService.createCleanURL(areas)
596
+
597
+ expect(result).toBe('/admin/users/user-list?page=2&limit=50&sort=name')
598
+ })
599
+
600
+ it('should switch between pretty and encoded modes', () => {
601
+ mockLocation.pathname = '/demo/area/'
602
+
603
+ const areas = {
604
+ main: {
605
+ component: 'test-component',
606
+ params: { id: '123' },
607
+ state: {},
608
+ props: {},
609
+ area: 'main',
610
+ },
611
+ }
612
+
613
+ // Pretty mode
614
+ testService.prettyURL = true
615
+ const prettyResult = testService.createCleanURL(areas)
616
+ expect(prettyResult).toBe('/demo/area/test-component?id=123')
617
+
618
+ // Encoded mode
619
+ testService.prettyURL = false
620
+ const encodedResult = testService.createCleanURL(areas)
621
+ expect(encodedResult).toMatch(/^\/demo\/area\//)
622
+ expect(encodedResult).toMatch(/%7B.*%7D$/)
623
+ })
624
+
625
+ it('should fallback to encoded for non-main areas in pretty mode', () => {
626
+ mockLocation.pathname = '/demo/area/'
627
+
628
+ const areas = {
629
+ sidebar: {
630
+ component: 'navigation',
631
+ state: { expanded: true },
632
+ params: {},
633
+ props: {},
634
+ area: 'sidebar',
635
+ },
636
+ }
637
+
638
+ const result = testService.createCleanURL(areas)
639
+
640
+ // Should fallback to encoded URL for non-main areas
641
+ expect(result).toMatch(/^\/demo\/area\//)
642
+ expect(result).toMatch(/%7B.*%7D$/)
643
+ })
644
+ })
645
+
646
+ describe('AreaService - Query Parameter Tests', () => {
647
+ let testService: TestAreaService
648
+
649
+ beforeEach(() => {
650
+ testService = new TestAreaService()
651
+ })
652
+
653
+ it('should preserve query parameters during navigation', () => {
654
+ mockLocation.pathname = '/demo/area/'
655
+ mockLocation.search = '?theme=dark&lang=en'
656
+
657
+ const result = testService.createCleanURL({})
658
+
659
+ expect(result).toBe('/demo/area?theme=dark&lang=en')
660
+ })
661
+
662
+ it('should work with clearQueryParams option', () => {
663
+ mockLocation.pathname = '/demo/area/'
664
+ mockLocation.search = '?theme=dark&lang=en&temp=remove'
665
+
666
+ const result = testService.createCleanURL({}, ['temp'])
667
+
668
+ expect(result).toBe('/demo/area?theme=dark&lang=en')
669
+ expect(result).not.toMatch(/temp=remove/)
670
+ })
671
+
672
+ it('should clear all query params when clearQueryParams is true', () => {
673
+ mockLocation.pathname = '/demo/area/'
674
+ mockLocation.search = '?theme=dark&lang=en&temp=remove'
675
+
676
+ const result = testService.createCleanURL({}, true)
677
+
678
+ expect(result).toBe('/demo/area')
679
+ })
680
+
681
+ it('should handle query params with encoded URLs', () => {
682
+ mockLocation.pathname = '/demo/area/'
683
+ mockLocation.search = '?theme=dark'
684
+
685
+ const areas = {
686
+ main: {
687
+ component: 'test-component',
688
+ state: { page: 1 },
689
+ params: {},
690
+ props: {},
691
+ area: 'main',
692
+ },
693
+ }
694
+
695
+ const result = testService.createCleanURL(areas)
696
+
697
+ expect(result).toMatch(/theme=dark/)
698
+ expect(result).toMatch(/%7B.*%7D/)
699
+ })
700
+
701
+ it('should preserve URL-encoded query parameters', () => {
702
+ mockLocation.pathname = '/demo/area/'
703
+ mockLocation.search = '?search=hello%20world&special=%26%3D%3F'
704
+
705
+ const result = testService.createCleanURL({})
706
+
707
+ expect(result).toMatch(/search=/)
708
+ expect(result).toMatch(/special=/)
709
+ })
710
+ })
711
+
712
+ describe('AreaService - Edge Cases', () => {
713
+ let testService: TestAreaService
714
+
715
+ beforeEach(() => {
716
+ testService = new TestAreaService()
717
+ })
718
+
719
+ it('should handle URLs with special characters in base path', () => {
720
+ mockLocation.pathname = '/démö/área-especial/tëst/'
721
+
722
+ const result = testService.createCleanURL({})
723
+
724
+ expect(result).toBe('/démö/área-especial/tëst')
725
+ })
726
+
727
+ it('should handle very long base paths', () => {
728
+ const longPath = '/very/long/nested/path/with/many/segments/that/goes/on/and/on/and/on'
729
+ mockLocation.pathname = longPath + '/'
730
+
731
+ const result = testService.createCleanURL({})
732
+
733
+ expect(result).toBe(longPath)
734
+ })
735
+
736
+ it('should handle URLs with existing encoded segments that are not state', () => {
737
+ mockLocation.pathname = '/demo/encoded%20path/area/'
738
+
739
+ const result = testService.createCleanURL({})
740
+
741
+ expect(result).toBe('/demo/encoded%20path/area')
742
+ })
743
+
744
+ it('should handle malformed encoded state gracefully', () => {
745
+ mockLocation.pathname = '/demo/area/%7B%22invalid%22%3A'
746
+
747
+ expect(() => testService.createCleanURL({})).not.toThrow()
748
+ const result = testService.createCleanURL({})
749
+ expect(result).toBe('/demo/area')
750
+ })
751
+
752
+ it('should handle empty path segments', () => {
753
+ mockLocation.pathname = '/demo//area//'
754
+
755
+ const result = testService.createCleanURL({})
756
+
757
+ // The createCleanURL method handles empty segments by preserving them but removes trailing slash
758
+ expect(result).toBe('/demo//area/')
759
+ })
760
+
761
+ it('should handle concurrent navigation calls', async () => {
762
+ mockLocation.pathname = '/demo/area/'
763
+
764
+ // Simulate multiple rapid navigation calls by directly setting areas
765
+ // (since push() is async and goes through request pipeline)
766
+ const promises = []
767
+ for (let i = 0; i < 10; i++) {
768
+ promises.push(
769
+ new Promise<void>(resolve => {
770
+ setTimeout(() => {
771
+ area.current.set(`area${i}`, {
772
+ area: `area${i}`,
773
+ component: `component${i}`,
774
+ state: { index: i },
775
+ params: {},
776
+ props: {},
777
+ })
778
+ resolve()
779
+ }, Math.random() * 10)
780
+ }),
781
+ )
782
+ }
783
+
784
+ await Promise.all(promises)
785
+
786
+ // All areas should be present
787
+ expect(area.current.size).toBe(10)
788
+ })
789
+
790
+ it('should handle createCleanURL with null/undefined areas', () => {
791
+ mockLocation.pathname = '/demo/area/'
792
+
793
+ // Test with null and undefined values
794
+ const areas = {
795
+ main: {
796
+ component: 'test-component',
797
+ state: null as any,
798
+ params: undefined as any,
799
+ props: {},
800
+ area: 'main',
801
+ },
802
+ }
803
+
804
+ expect(() => testService.createCleanURL(areas)).not.toThrow()
805
+ const result = testService.createCleanURL(areas)
806
+ expect(result).toMatch(/^\/demo\/area\//)
807
+ })
808
+
809
+ it('should handle extremely large state objects', () => {
810
+ mockLocation.pathname = '/demo/area/'
811
+
812
+ // Create a large state object
813
+ const largeState: Record<string, string> = {}
814
+ for (let i = 0; i < 1000; i++) {
815
+ largeState[`key${i}`] = `value${i}`.repeat(10)
816
+ }
817
+
818
+ const areas = {
819
+ main: {
820
+ component: 'test-component',
821
+ state: largeState,
822
+ params: {},
823
+ props: {},
824
+ area: 'main',
825
+ },
826
+ }
827
+
828
+ expect(() => testService.createCleanURL(areas)).not.toThrow()
829
+ const result = testService.createCleanURL(areas)
830
+ expect(result).toMatch(/^\/demo\/area\//)
831
+ })
832
+ })
833
+
834
+ describe('AreaService - Integration Tests', () => {
835
+ beforeEach(() => {
836
+ area.enableHistoryMode = true
837
+ })
838
+
839
+ it('should maintain functionality after multiple operations', () => {
840
+ mockLocation.pathname = '/demo/app/'
841
+
842
+ // Add main area directly to current map
843
+ area.current.set('main', {
844
+ area: 'main',
845
+ component: 'dashboard',
846
+ state: { view: 'analytics' },
847
+ params: { period: 'monthly' },
848
+ props: { title: 'Dashboard' },
849
+ })
850
+
851
+ expect(area.current.size).toBe(1)
852
+ expect(area.hasArea('main')).toBe(true)
853
+
854
+ // Add sidebar area directly to current map
855
+ area.current.set('sidebar', {
856
+ area: 'sidebar',
857
+ component: 'navigation',
858
+ state: { expanded: true },
859
+ params: {},
860
+ props: {},
861
+ })
862
+
863
+ expect(area.current.size).toBe(2)
864
+
865
+ // Pop sidebar
866
+ area.pop('sidebar')
867
+
868
+ expect(area.current.size).toBe(1)
869
+ expect(area.hasArea('main')).toBe(true)
870
+ expect(area.hasArea('sidebar')).toBe(false)
871
+
872
+ // Clear all
873
+ area.clear()
874
+
875
+ expect(area.current.size).toBe(0)
876
+ expect(area.getActiveAreas()).toEqual([])
877
+ })
878
+
879
+ it('should handle rapid state changes correctly', () => {
880
+ mockLocation.pathname = '/demo/area/'
881
+
882
+ // Test that multiple rapid changes to the same area work correctly
883
+ area.current.set('main', {
884
+ area: 'main',
885
+ component: 'test',
886
+ state: { step: 1 },
887
+ params: {},
888
+ props: {},
889
+ })
890
+
891
+ expect(area.getRoute('main')?.state).toEqual({ step: 1 })
892
+
893
+ // Update the state
894
+ area.current.set('main', {
895
+ area: 'main',
896
+ component: 'test',
897
+ state: { step: 2 },
898
+ params: {},
899
+ props: {},
900
+ })
901
+
902
+ expect(area.getRoute('main')?.state).toEqual({ step: 2 })
903
+
904
+ // Final update
905
+ area.current.set('main', {
906
+ area: 'main',
907
+ component: 'test',
908
+ state: { final: true },
909
+ params: {},
910
+ props: {},
911
+ })
912
+
913
+ expect(area.getRoute('main')?.state).toEqual({ final: true })
914
+ })
915
+
916
+ it('should preserve functionality across history mode changes', () => {
917
+ mockLocation.pathname = '/demo/area/'
918
+
919
+ // Enable history mode and directly set an area (simulating push)
920
+ area.enableHistoryMode = true
921
+ area.current.set('main', {
922
+ area: 'main',
923
+ component: 'test',
924
+ state: {},
925
+ params: {},
926
+ props: {},
927
+ })
928
+
929
+ // Call updateBrowserHistory directly to test history behavior
930
+ area.updateBrowserHistory('main', {
931
+ area: 'main',
932
+ component: 'test',
933
+ state: {},
934
+ params: {},
935
+ props: {},
936
+ })
937
+
938
+ expect(mockHistory.pushState).toHaveBeenCalled()
939
+
940
+ // Disable history mode
941
+ area.enableHistoryMode = false
942
+ area.current.set('sidebar', {
943
+ area: 'sidebar',
944
+ component: 'nav',
945
+ state: {},
946
+ params: {},
947
+ props: {},
948
+ })
949
+
950
+ // Should still work but not update history
951
+ expect(area.current.size).toBe(2)
952
+ })
953
+
954
+ it('should maintain RxJS stream subscriptions correctly', () => {
955
+ mockLocation.pathname = '/demo/area/'
956
+
957
+ // Test that the RxJS observables work correctly
958
+ area.current.set('main', {
959
+ area: 'main',
960
+ component: 'test',
961
+ state: { initial: true },
962
+ params: { id: '123' },
963
+ props: { title: 'Test' },
964
+ })
965
+
966
+ // Test synchronous access to current route
967
+ const route = area.getRoute('main')
968
+ expect(route).toBeDefined()
969
+ expect(route?.state).toEqual({ initial: true })
970
+ expect(route?.params).toEqual({ id: '123' })
971
+ expect(route?.props).toEqual({ title: 'Test' })
972
+
973
+ // Test that hasArea works
974
+ expect(area.hasArea('main')).toBe(true)
975
+ expect(area.hasArea('nonexistent')).toBe(false)
976
+
977
+ // Test getActiveAreas
978
+ expect(area.getActiveAreas()).toEqual(['main'])
979
+
980
+ // Update state and verify
981
+ area.current.set('main', {
982
+ area: 'main',
983
+ component: 'test',
984
+ state: { updated: true },
985
+ params: { id: '456' },
986
+ props: { title: 'Updated' },
987
+ })
988
+
989
+ const updatedRoute = area.getRoute('main')
990
+ expect(updatedRoute?.state).toEqual({ updated: true })
991
+ expect(updatedRoute?.params).toEqual({ id: '456' })
992
+ expect(updatedRoute?.props).toEqual({ title: 'Updated' })
993
+ })
994
+ })