@mhmo91/schmancy 0.10.34 → 0.10.36

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 (414) hide show
  1. package/custom-elements.json +1144 -1101
  2. package/dist/SchmancyElement-BBzRWB1w.cjs +2 -0
  3. package/dist/SchmancyElement-BBzRWB1w.cjs.map +1 -0
  4. package/dist/SchmancyElement-C3CpdNsi.js +284 -0
  5. package/dist/SchmancyElement-C3CpdNsi.js.map +1 -0
  6. package/dist/agent/schmancy.agent.js +3414 -3263
  7. package/dist/agent/schmancy.agent.js.map +1 -1
  8. package/dist/agent/schmancy.manifest.json +335 -276
  9. package/dist/{area-DviXdbDx.js → area-BA96mwFY.js} +2 -2
  10. package/dist/{area-DviXdbDx.js.map → area-BA96mwFY.js.map} +1 -1
  11. package/dist/{area-CTSTgjlx.cjs → area-DtyQDdOF.cjs} +1 -1
  12. package/dist/{area-CTSTgjlx.cjs.map → area-DtyQDdOF.cjs.map} +1 -1
  13. package/dist/area.cjs +1 -1
  14. package/dist/area.js +1 -1
  15. package/dist/{audio-Q9oB_cQR.cjs → audio-B_jT9Xr7.cjs} +1 -1
  16. package/dist/{audio-Q9oB_cQR.cjs.map → audio-B_jT9Xr7.cjs.map} +1 -1
  17. package/dist/{audio-DFYoaw0M.js → audio-D52h1jAT.js} +1 -1
  18. package/dist/{audio-DFYoaw0M.js.map → audio-D52h1jAT.js.map} +1 -1
  19. package/dist/audio.cjs +1 -1
  20. package/dist/audio.js +2 -2
  21. package/dist/{autocomplete-DmLXJr7C.cjs → autocomplete-Bts5Jwwr.cjs} +1 -1
  22. package/dist/{autocomplete-DmLXJr7C.cjs.map → autocomplete-Bts5Jwwr.cjs.map} +1 -1
  23. package/dist/{autocomplete-BDvuma6D.js → autocomplete-CI4QJXAN.js} +3 -3
  24. package/dist/{autocomplete-BDvuma6D.js.map → autocomplete-CI4QJXAN.js.map} +1 -1
  25. package/dist/autocomplete.cjs +1 -1
  26. package/dist/autocomplete.js +1 -1
  27. package/dist/avatar.cjs +1 -1
  28. package/dist/avatar.js +3 -3
  29. package/dist/badge.cjs +1 -1
  30. package/dist/badge.js +1 -1
  31. package/dist/boat-I4B1UNMc.cjs +34 -0
  32. package/dist/boat-I4B1UNMc.cjs.map +1 -0
  33. package/dist/boat-_N1x5U_3.js +191 -0
  34. package/dist/boat-_N1x5U_3.js.map +1 -0
  35. package/dist/boat.cjs +1 -1
  36. package/dist/boat.js +1 -1
  37. package/dist/breadcrumb.cjs +1 -1
  38. package/dist/breadcrumb.js +2 -2
  39. package/dist/{busy-CgzZbGfx.cjs → busy-BIUonyPk.cjs} +1 -1
  40. package/dist/{busy-CgzZbGfx.cjs.map → busy-BIUonyPk.cjs.map} +1 -1
  41. package/dist/{busy-DgQ4ux5N.js → busy-CCB3qKnh.js} +2 -2
  42. package/dist/{busy-DgQ4ux5N.js.map → busy-CCB3qKnh.js.map} +1 -1
  43. package/dist/busy.cjs +1 -1
  44. package/dist/busy.js +1 -1
  45. package/dist/{button-qbN1muQ0.js → button-C89bPnHt.js} +2 -2
  46. package/dist/{button-qbN1muQ0.js.map → button-C89bPnHt.js.map} +1 -1
  47. package/dist/{button-DFvR1iXX.cjs → button-CkwQH-g3.cjs} +1 -1
  48. package/dist/{button-DFvR1iXX.cjs.map → button-CkwQH-g3.cjs.map} +1 -1
  49. package/dist/button.cjs +4 -4
  50. package/dist/button.cjs.map +1 -1
  51. package/dist/button.js +16 -22
  52. package/dist/button.js.map +1 -1
  53. package/dist/{card-D_GlwZ5q.cjs → card-BO93_oxQ.cjs} +1 -1
  54. package/dist/{card-D_GlwZ5q.cjs.map → card-BO93_oxQ.cjs.map} +1 -1
  55. package/dist/{card-DAbr-7Vy.js → card-CFsCgJKZ.js} +2 -2
  56. package/dist/{card-DAbr-7Vy.js.map → card-CFsCgJKZ.js.map} +1 -1
  57. package/dist/card.cjs +1 -1
  58. package/dist/card.js +1 -1
  59. package/dist/{checkbox-BNORaxMF.js → checkbox-Bh7q0djq.js} +2 -2
  60. package/dist/{checkbox-BNORaxMF.js.map → checkbox-Bh7q0djq.js.map} +1 -1
  61. package/dist/{checkbox-BUY_uc_r.cjs → checkbox-CFUBUFtW.cjs} +1 -1
  62. package/dist/{checkbox-BUY_uc_r.cjs.map → checkbox-CFUBUFtW.cjs.map} +1 -1
  63. package/dist/checkbox.cjs +1 -1
  64. package/dist/checkbox.js +1 -1
  65. package/dist/{chips-Dg6Lk6BT.js → chips-6YaoRmeG.js} +145 -122
  66. package/dist/chips-6YaoRmeG.js.map +1 -0
  67. package/dist/{chips-CXZ4dJCK.cjs → chips-BfzpsyV1.cjs} +44 -33
  68. package/dist/chips-BfzpsyV1.cjs.map +1 -0
  69. package/dist/chips.cjs +1 -1
  70. package/dist/chips.js +2 -2
  71. package/dist/connectivity.cjs +1 -1
  72. package/dist/connectivity.js +3 -3
  73. package/dist/content-drawer.cjs +1 -1
  74. package/dist/content-drawer.js +1 -1
  75. package/dist/{date-range-BU6WX7d5.js → date-range-CEo_Kjqw.js} +129 -137
  76. package/dist/date-range-CEo_Kjqw.js.map +1 -0
  77. package/dist/{date-range-C-_be3_E.cjs → date-range-N-A249O9.cjs} +25 -19
  78. package/dist/date-range-N-A249O9.cjs.map +1 -0
  79. package/dist/{date-range-inline-7o7xtVIu.js → date-range-inline-BwialV9j.js} +2 -2
  80. package/dist/{date-range-inline-7o7xtVIu.js.map → date-range-inline-BwialV9j.js.map} +1 -1
  81. package/dist/{date-range-inline-DJtUmHKF.cjs → date-range-inline-DFopysWF.cjs} +1 -1
  82. package/dist/{date-range-inline-DJtUmHKF.cjs.map → date-range-inline-DFopysWF.cjs.map} +1 -1
  83. package/dist/date-range-inline.cjs +1 -1
  84. package/dist/date-range-inline.js +1 -1
  85. package/dist/date-range.cjs +1 -1
  86. package/dist/date-range.js +1 -1
  87. package/dist/delay.cjs +1 -1
  88. package/dist/delay.js +2 -2
  89. package/dist/{details-Bs0MyyvF.cjs → details-BLRPV8sY.cjs} +1 -1
  90. package/dist/{details-Bs0MyyvF.cjs.map → details-BLRPV8sY.cjs.map} +1 -1
  91. package/dist/{details-EfbDPVEo.js → details-GtpfI2hA.js} +2 -2
  92. package/dist/{details-EfbDPVEo.js.map → details-GtpfI2hA.js.map} +1 -1
  93. package/dist/details.cjs +1 -1
  94. package/dist/details.js +1 -1
  95. package/dist/{directives-zi1Mm2er.js → directives-C2dXgpCY.js} +13 -6
  96. package/dist/directives-C2dXgpCY.js.map +1 -0
  97. package/dist/{directives-fLwDj6b0.cjs → directives-CvYGSW_a.cjs} +2 -2
  98. package/dist/directives-CvYGSW_a.cjs.map +1 -0
  99. package/dist/directives.cjs +1 -1
  100. package/dist/directives.js +2 -2
  101. package/dist/{divider-CEPfrIwe.js → divider-D0pGX2VB.js} +2 -2
  102. package/dist/{divider-CEPfrIwe.js.map → divider-D0pGX2VB.js.map} +1 -1
  103. package/dist/{divider-CdIsWZrM.cjs → divider-rNsWCvMi.cjs} +1 -1
  104. package/dist/{divider-CdIsWZrM.cjs.map → divider-rNsWCvMi.cjs.map} +1 -1
  105. package/dist/divider.cjs +1 -1
  106. package/dist/divider.js +1 -1
  107. package/dist/dropdown.cjs +1 -1
  108. package/dist/dropdown.js +2 -2
  109. package/dist/{expand-g1vqqUp1.js → expand-FcKAzJta.js} +3 -3
  110. package/dist/{expand-g1vqqUp1.js.map → expand-FcKAzJta.js.map} +1 -1
  111. package/dist/{expand--at1k3qo.cjs → expand-tffQHGbZ.cjs} +1 -1
  112. package/dist/{expand--at1k3qo.cjs.map → expand-tffQHGbZ.cjs.map} +1 -1
  113. package/dist/expand.cjs +1 -1
  114. package/dist/expand.js +1 -1
  115. package/dist/fab.cjs +77 -0
  116. package/dist/fab.cjs.map +1 -0
  117. package/dist/fab.js +151 -0
  118. package/dist/fab.js.map +1 -0
  119. package/dist/{float-P9HukAm-.cjs → float-CDjgxWyy.cjs} +1 -1
  120. package/dist/{float-P9HukAm-.cjs.map → float-CDjgxWyy.cjs.map} +1 -1
  121. package/dist/{float-DxVzgI9o.js → float-CQ1WEp3M.js} +2 -2
  122. package/dist/{float-DxVzgI9o.js.map → float-CQ1WEp3M.js.map} +1 -1
  123. package/dist/float.cjs +1 -1
  124. package/dist/float.js +1 -1
  125. package/dist/{form-CqLaozHp.js → form-H24puioV.js} +3 -3
  126. package/dist/{form-CqLaozHp.js.map → form-H24puioV.js.map} +1 -1
  127. package/dist/{form-ByYhXe1p.cjs → form-VYhbbir3.cjs} +1 -1
  128. package/dist/{form-ByYhXe1p.cjs.map → form-VYhbbir3.cjs.map} +1 -1
  129. package/dist/form.cjs +6 -6
  130. package/dist/form.cjs.map +1 -1
  131. package/dist/form.js +25 -23
  132. package/dist/form.js.map +1 -1
  133. package/dist/handover/agent-runtime-followups.md +1 -1
  134. package/dist/handover/agent-runtime-v1.md +3 -3
  135. package/dist/{icons-DYtiRU5V.cjs → icons-B_a1HStW.cjs} +1 -1
  136. package/dist/{icons-DYtiRU5V.cjs.map → icons-B_a1HStW.cjs.map} +1 -1
  137. package/dist/{icons-CkphcMp6.js → icons-DBxfN91B.js} +2 -2
  138. package/dist/{icons-CkphcMp6.js.map → icons-DBxfN91B.js.map} +1 -1
  139. package/dist/icons.cjs +1 -1
  140. package/dist/icons.js +1 -1
  141. package/dist/{iframe-CjqJksl8.js → iframe-BDVElN8z.js} +2 -2
  142. package/dist/{iframe-CjqJksl8.js.map → iframe-BDVElN8z.js.map} +1 -1
  143. package/dist/{iframe-C3trkP8q.cjs → iframe-CG-z9qev.cjs} +1 -1
  144. package/dist/{iframe-C3trkP8q.cjs.map → iframe-CG-z9qev.cjs.map} +1 -1
  145. package/dist/iframe.cjs +1 -1
  146. package/dist/iframe.js +1 -1
  147. package/dist/index.cjs +1 -1
  148. package/dist/index.js +57 -56
  149. package/dist/{input-DuavpwNL.cjs → input-B14Nn6xD.cjs} +1 -1
  150. package/dist/{input-DuavpwNL.cjs.map → input-B14Nn6xD.cjs.map} +1 -1
  151. package/dist/{input-CG51zDVh.js → input-Bt_o4sYo.js} +2 -2
  152. package/dist/{input-CG51zDVh.js.map → input-Bt_o4sYo.js.map} +1 -1
  153. package/dist/{input-chip-C6Lq1927.js → input-chip-DEqO0DXc.js} +2 -2
  154. package/dist/input-chip-DEqO0DXc.js.map +1 -0
  155. package/dist/{input-chip-57tgNXKT.cjs → input-chip-ugYu9Fn9.cjs} +1 -1
  156. package/dist/input-chip-ugYu9Fn9.cjs.map +1 -0
  157. package/dist/input.cjs +1 -1
  158. package/dist/input.js +1 -1
  159. package/dist/json.cjs +1 -1
  160. package/dist/json.js +3 -3
  161. package/dist/kbd.cjs +1 -1
  162. package/dist/kbd.js +2 -2
  163. package/dist/{layout-D4IOwx7p.js → layout-BJ_43VrH.js} +1 -1
  164. package/dist/{layout-D4IOwx7p.js.map → layout-BJ_43VrH.js.map} +1 -1
  165. package/dist/{layout-6ipbiWTl.cjs → layout-DF9ZaQ-b.cjs} +1 -1
  166. package/dist/{layout-6ipbiWTl.cjs.map → layout-DF9ZaQ-b.cjs.map} +1 -1
  167. package/dist/layout.cjs +1 -1
  168. package/dist/layout.js +2 -2
  169. package/dist/{lightbox-H8pVWGMX.cjs → lightbox-B47Zoqv-.cjs} +1 -1
  170. package/dist/{lightbox-H8pVWGMX.cjs.map → lightbox-B47Zoqv-.cjs.map} +1 -1
  171. package/dist/{lightbox-CsyO2XSr.js → lightbox-ZmuoBBFT.js} +2 -2
  172. package/dist/{lightbox-CsyO2XSr.js.map → lightbox-ZmuoBBFT.js.map} +1 -1
  173. package/dist/lightbox.cjs +1 -1
  174. package/dist/lightbox.js +1 -1
  175. package/dist/{list-BAwH0pQW.js → list-C47xzld_.js} +2 -2
  176. package/dist/{list-BAwH0pQW.js.map → list-C47xzld_.js.map} +1 -1
  177. package/dist/{list-Bs9m8kw7.cjs → list-CaSWrlG2.cjs} +1 -1
  178. package/dist/{list-Bs9m8kw7.cjs.map → list-CaSWrlG2.cjs.map} +1 -1
  179. package/dist/list.cjs +1 -1
  180. package/dist/list.js +1 -1
  181. package/dist/{menu-tQVARVaC.js → menu-8RObM6Ie.js} +3 -3
  182. package/dist/{menu-tQVARVaC.js.map → menu-8RObM6Ie.js.map} +1 -1
  183. package/dist/{menu-BMcGzj1h.cjs → menu-Jpsy85SX.cjs} +1 -1
  184. package/dist/{menu-BMcGzj1h.cjs.map → menu-Jpsy85SX.cjs.map} +1 -1
  185. package/dist/menu.cjs +1 -1
  186. package/dist/menu.js +1 -1
  187. package/dist/{mixins-CGXSzZc7.cjs → mixins-DPdzC9ZH.cjs} +1 -1
  188. package/dist/{mixins-CGXSzZc7.cjs.map → mixins-DPdzC9ZH.cjs.map} +1 -1
  189. package/dist/{mixins-Bp0wIHg2.js → mixins-DTzfFVyv.js} +1 -1
  190. package/dist/{mixins-Bp0wIHg2.js.map → mixins-DTzfFVyv.js.map} +1 -1
  191. package/dist/mixins.cjs +1 -1
  192. package/dist/mixins.js +2 -2
  193. package/dist/nav-drawer.cjs +1 -1
  194. package/dist/nav-drawer.js +1 -1
  195. package/dist/navigation-bar.cjs +1 -1
  196. package/dist/navigation-bar.js +1 -1
  197. package/dist/navigation-rail.cjs +1 -1
  198. package/dist/navigation-rail.js +2 -2
  199. package/dist/{notification-D1tX2nx5.js → notification-Ccktcj9H.js} +4 -4
  200. package/dist/{notification-D1tX2nx5.js.map → notification-Ccktcj9H.js.map} +1 -1
  201. package/dist/{notification-Bz00zdpV.cjs → notification-DSkB-sn0.cjs} +1 -1
  202. package/dist/{notification-Bz00zdpV.cjs.map → notification-DSkB-sn0.cjs.map} +1 -1
  203. package/dist/notification.cjs +1 -1
  204. package/dist/notification.js +1 -1
  205. package/dist/{option-BnybLEDO.cjs → option-0aNiVB3Q.cjs} +1 -1
  206. package/dist/{option-BnybLEDO.cjs.map → option-0aNiVB3Q.cjs.map} +1 -1
  207. package/dist/{option-BpGV8Apj.js → option-CkMxwBqU.js} +2 -2
  208. package/dist/{option-BpGV8Apj.js.map → option-CkMxwBqU.js.map} +1 -1
  209. package/dist/option.cjs +1 -1
  210. package/dist/option.js +1 -1
  211. package/dist/{overlay-UQR2Dy3u.cjs → overlay-BS-ta-zq.cjs} +5 -5
  212. package/dist/overlay-BS-ta-zq.cjs.map +1 -0
  213. package/dist/{overlay-BpNhd74N.js → overlay-H3Wt_dgQ.js} +108 -108
  214. package/dist/overlay-H3Wt_dgQ.js.map +1 -0
  215. package/dist/overlay.cjs +1 -1
  216. package/dist/{overlay.confirm-body-CVDtVk5X.cjs → overlay.confirm-body-CR9xaqOE.cjs} +1 -1
  217. package/dist/{overlay.confirm-body-CVDtVk5X.cjs.map → overlay.confirm-body-CR9xaqOE.cjs.map} +1 -1
  218. package/dist/{overlay.confirm-body-BHcXu5Wk.js → overlay.confirm-body-Dxn_wNm3.js} +6 -6
  219. package/dist/{overlay.confirm-body-BHcXu5Wk.js.map → overlay.confirm-body-Dxn_wNm3.js.map} +1 -1
  220. package/dist/overlay.js +3 -3
  221. package/dist/{overlay.service-DTE6NwIM.js → overlay.service-C46kOtUi.js} +2 -2
  222. package/dist/{overlay.service-DTE6NwIM.js.map → overlay.service-C46kOtUi.js.map} +1 -1
  223. package/dist/{overlay.service-C8RsQzgM.cjs → overlay.service-DEj3rfRr.cjs} +1 -1
  224. package/dist/{overlay.service-C8RsQzgM.cjs.map → overlay.service-DEj3rfRr.cjs.map} +1 -1
  225. package/dist/{progress-CAKsxp29.js → progress-BK7gSq8j.js} +2 -2
  226. package/dist/{progress-CAKsxp29.js.map → progress-BK7gSq8j.js.map} +1 -1
  227. package/dist/{progress-gbIALDRs.cjs → progress-zs18GR6C.cjs} +1 -1
  228. package/dist/{progress-gbIALDRs.cjs.map → progress-zs18GR6C.cjs.map} +1 -1
  229. package/dist/progress.cjs +1 -1
  230. package/dist/progress.js +1 -1
  231. package/dist/{radio-group-otyvZvUk.js → radio-group-1HCpzRUB.js} +2 -2
  232. package/dist/{radio-group-otyvZvUk.js.map → radio-group-1HCpzRUB.js.map} +1 -1
  233. package/dist/{radio-group-CfJ5DtI4.cjs → radio-group-DbYlyPc-.cjs} +1 -1
  234. package/dist/{radio-group-CfJ5DtI4.cjs.map → radio-group-DbYlyPc-.cjs.map} +1 -1
  235. package/dist/radio-group.cjs +1 -1
  236. package/dist/radio-group.js +1 -1
  237. package/dist/range.cjs +1 -1
  238. package/dist/range.js +2 -2
  239. package/dist/{select-81jniVTs.cjs → select-B-SSmUDe.cjs} +1 -1
  240. package/dist/{select-81jniVTs.cjs.map → select-B-SSmUDe.cjs.map} +1 -1
  241. package/dist/{select-9vXx1fhr.js → select-CEyhNtZ2.js} +3 -3
  242. package/dist/{select-9vXx1fhr.js.map → select-CEyhNtZ2.js.map} +1 -1
  243. package/dist/select.cjs +1 -1
  244. package/dist/select.js +1 -1
  245. package/dist/skeleton.cjs +1 -1
  246. package/dist/skeleton.js +2 -2
  247. package/dist/skills/INDEX.md +1 -1
  248. package/dist/skills/SKILL.md +6 -7
  249. package/dist/skills/boat.md +21 -15
  250. package/dist/skills/fab.md +75 -0
  251. package/dist/skills/schmancy/INDEX.md +1 -1
  252. package/dist/skills/schmancy/SKILL.md +6 -7
  253. package/dist/skills/schmancy/boat.md +21 -15
  254. package/dist/skills/schmancy/fab.md +75 -0
  255. package/dist/skills/schmancy/theme.md +1 -1
  256. package/dist/skills/theme.md +1 -1
  257. package/dist/slider.cjs +1 -1
  258. package/dist/slider.js +2 -2
  259. package/dist/{sound.service-D3ZSq1Kj.js → sound.service-DO4SmUUT.js} +1 -1
  260. package/dist/{sound.service-D3ZSq1Kj.js.map → sound.service-DO4SmUUT.js.map} +1 -1
  261. package/dist/{sound.service-CmIw63aM.cjs → sound.service-G_8GV_6L.cjs} +1 -1
  262. package/dist/{sound.service-CmIw63aM.cjs.map → sound.service-G_8GV_6L.cjs.map} +1 -1
  263. package/dist/{splash-screen-BOjrmGLk.js → splash-screen-B1mM4_xz.js} +2 -2
  264. package/dist/{splash-screen-BOjrmGLk.js.map → splash-screen-B1mM4_xz.js.map} +1 -1
  265. package/dist/{splash-screen-C5KAWXvA.cjs → splash-screen-cbz4bxjB.cjs} +1 -1
  266. package/dist/{splash-screen-C5KAWXvA.cjs.map → splash-screen-cbz4bxjB.cjs.map} +1 -1
  267. package/dist/splash-screen.cjs +1 -1
  268. package/dist/splash-screen.js +1 -1
  269. package/dist/{src-Bm1vop0l.cjs → src-czeiuT1m.cjs} +1 -1
  270. package/dist/{src-Bm1vop0l.cjs.map → src-czeiuT1m.cjs.map} +1 -1
  271. package/dist/{src-DoOhMBtI.js → src-tncsWsTY.js} +36 -35
  272. package/dist/{src-DoOhMBtI.js.map → src-tncsWsTY.js.map} +1 -1
  273. package/dist/{state-Cex3rmx2.cjs → state-Cx0aoL5e.cjs} +1 -1
  274. package/dist/{state-Cex3rmx2.cjs.map → state-Cx0aoL5e.cjs.map} +1 -1
  275. package/dist/{state-CWBRTSvE.js → state-DJDp3N7J.js} +1 -1
  276. package/dist/{state-CWBRTSvE.js.map → state-DJDp3N7J.js.map} +1 -1
  277. package/dist/state.cjs +1 -1
  278. package/dist/state.js +2 -2
  279. package/dist/steps.cjs +1 -1
  280. package/dist/steps.js +2 -2
  281. package/dist/{surface-PfiejLuw.cjs → surface-C3cxTcJD.cjs} +1 -1
  282. package/dist/{surface-PfiejLuw.cjs.map → surface-C3cxTcJD.cjs.map} +1 -1
  283. package/dist/{surface-9S5scTsD.js → surface-CYBl8_a3.js} +2 -2
  284. package/dist/{surface-9S5scTsD.js.map → surface-CYBl8_a3.js.map} +1 -1
  285. package/dist/surface.cjs +1 -1
  286. package/dist/surface.js +1 -1
  287. package/dist/switch.cjs +1 -1
  288. package/dist/switch.js +2 -2
  289. package/dist/table.cjs +1 -1
  290. package/dist/table.js +2 -2
  291. package/dist/{tabs-BBOjAmgG.js → tabs-DHy93Q3N.js} +2 -2
  292. package/dist/{tabs-BBOjAmgG.js.map → tabs-DHy93Q3N.js.map} +1 -1
  293. package/dist/{tabs-uYvb1P06.cjs → tabs-ORQ_Zd43.cjs} +1 -1
  294. package/dist/{tabs-uYvb1P06.cjs.map → tabs-ORQ_Zd43.cjs.map} +1 -1
  295. package/dist/tabs.cjs +1 -1
  296. package/dist/tabs.js +1 -1
  297. package/dist/teleport.cjs +1 -1
  298. package/dist/teleport.js +1 -1
  299. package/dist/{textarea-YPHX4g7Y.cjs → textarea-CEUaDURR.cjs} +1 -1
  300. package/dist/{textarea-YPHX4g7Y.cjs.map → textarea-CEUaDURR.cjs.map} +1 -1
  301. package/dist/{textarea-QzSj8Dkl.js → textarea-DHIMt-ly.js} +2 -2
  302. package/dist/{textarea-QzSj8Dkl.js.map → textarea-DHIMt-ly.js.map} +1 -1
  303. package/dist/textarea.cjs +1 -1
  304. package/dist/textarea.js +1 -1
  305. package/dist/theme-CJpjkqHr.cjs +181 -0
  306. package/dist/{theme-iKUaS9JB.cjs.map → theme-CJpjkqHr.cjs.map} +1 -1
  307. package/dist/{theme-C2Mp-VGt.js → theme-CgI9PRco.js} +6 -5
  308. package/dist/{theme-C2Mp-VGt.js.map → theme-CgI9PRco.js.map} +1 -1
  309. package/dist/{theme-button-CJmhxfMe.cjs → theme-button--FuBkuVr.cjs} +1 -1
  310. package/dist/{theme-button-CJmhxfMe.cjs.map → theme-button--FuBkuVr.cjs.map} +1 -1
  311. package/dist/{theme-button-DGWAXhzd.js → theme-button-D-FXb3oO.js} +2 -2
  312. package/dist/{theme-button-DGWAXhzd.js.map → theme-button-D-FXb3oO.js.map} +1 -1
  313. package/dist/theme-button.cjs +1 -1
  314. package/dist/theme-button.js +1 -1
  315. package/dist/theme.cjs +1 -1
  316. package/dist/theme.js +3 -3
  317. package/dist/{theme.service-hc4N-1Oz.js → theme.service-BfTK1Wtl.js} +1 -1
  318. package/dist/{theme.service-hc4N-1Oz.js.map → theme.service-BfTK1Wtl.js.map} +1 -1
  319. package/dist/{theme.service-p61RsJBF.cjs → theme.service-Dg7LO0Qz.cjs} +1 -1
  320. package/dist/{theme.service-p61RsJBF.cjs.map → theme.service-Dg7LO0Qz.cjs.map} +1 -1
  321. package/dist/tree.cjs +1 -1
  322. package/dist/tree.js +2 -2
  323. package/dist/{typography-DwV0sqht.js → typography-BEGLfHwz.js} +2 -2
  324. package/dist/{typography-DwV0sqht.js.map → typography-BEGLfHwz.js.map} +1 -1
  325. package/dist/{typography-Bdt8RlX2.cjs → typography-CxA3sx9B.cjs} +1 -1
  326. package/dist/{typography-Bdt8RlX2.cjs.map → typography-CxA3sx9B.cjs.map} +1 -1
  327. package/dist/typography.cjs +1 -1
  328. package/dist/typography.js +1 -1
  329. package/dist/visually-hidden.cjs +1 -1
  330. package/dist/visually-hidden.js +2 -2
  331. package/dist/{window-n4jN60B_.js → window-B_n4P9az.js} +3 -3
  332. package/dist/{window-n4jN60B_.js.map → window-B_n4P9az.js.map} +1 -1
  333. package/dist/{window-D2WfvNng.cjs → window-Vl1u1-EG.cjs} +1 -1
  334. package/dist/{window-D2WfvNng.cjs.map → window-Vl1u1-EG.cjs.map} +1 -1
  335. package/dist/window.cjs +1 -1
  336. package/dist/window.js +1 -1
  337. package/package.json +1 -1
  338. package/skills/schmancy/INDEX.md +1 -1
  339. package/skills/schmancy/SKILL.md +6 -7
  340. package/skills/schmancy/boat.md +21 -15
  341. package/skills/schmancy/fab.md +75 -0
  342. package/skills/schmancy/theme.md +1 -1
  343. package/src/CLAUDE.md +22 -16
  344. package/src/boat/boat.test.ts +130 -0
  345. package/src/boat/boat.ts +192 -186
  346. package/src/button/icon-button.ts +18 -30
  347. package/src/chips/assist-chip.ts +2 -263
  348. package/src/chips/chips.ts +2 -259
  349. package/src/chips/filter-chip.ts +2 -255
  350. package/src/chips/index.ts +2 -5
  351. package/src/chips/input-chip.ts +2 -413
  352. package/src/chips/suggestion-chip.ts +2 -266
  353. package/src/directives/fill.ts +28 -5
  354. package/src/fab/fab.test.ts +101 -0
  355. package/src/fab/fab.ts +226 -0
  356. package/src/fab/index.ts +1 -0
  357. package/src/form/fields/chips/assist-chip.ts +263 -0
  358. package/src/form/fields/chips/chips.ts +234 -0
  359. package/src/form/fields/chips/filter-chip.ts +255 -0
  360. package/src/form/fields/chips/index.ts +5 -0
  361. package/src/form/fields/chips/input-chip.ts +413 -0
  362. package/src/form/fields/chips/suggestion-chip.ts +266 -0
  363. package/src/form/fields/date-range/date-range.test.ts +44 -0
  364. package/src/form/fields/date-range/date-range.ts +103 -97
  365. package/src/form/fields/index.ts +1 -0
  366. package/src/index.ts +2 -1
  367. package/src/overlay/overlay.animations.ts +2 -3
  368. package/src/overlay/overlay.component.ts +8 -4
  369. package/src/overlay/overlay.types.ts +14 -2
  370. package/src/theme/theme.component.ts +1 -0
  371. package/src/theme/theme.style.css +3 -0
  372. package/types/src/boat/boat.d.ts +40 -25
  373. package/types/src/boat/boat.test.d.ts +2 -0
  374. package/types/src/button/icon-button.d.ts +3 -2
  375. package/types/src/chips/assist-chip.d.ts +1 -47
  376. package/types/src/chips/chips.d.ts +1 -43
  377. package/types/src/chips/filter-chip.d.ts +1 -67
  378. package/types/src/chips/index.d.ts +1 -5
  379. package/types/src/chips/input-chip.d.ts +1 -82
  380. package/types/src/chips/suggestion-chip.d.ts +1 -52
  381. package/types/src/fab/fab.d.ts +80 -0
  382. package/types/src/fab/fab.test.d.ts +2 -0
  383. package/types/src/fab/index.d.ts +1 -0
  384. package/types/src/form/fields/chips/assist-chip.d.ts +47 -0
  385. package/types/src/form/fields/chips/chips.d.ts +35 -0
  386. package/types/src/form/fields/chips/filter-chip.d.ts +67 -0
  387. package/types/src/form/fields/chips/index.d.ts +5 -0
  388. package/types/src/form/fields/chips/input-chip.d.ts +82 -0
  389. package/types/src/form/fields/chips/suggestion-chip.d.ts +52 -0
  390. package/types/src/form/fields/date-range/date-range.d.ts +5 -10
  391. package/types/src/form/fields/index.d.ts +1 -0
  392. package/types/src/index.d.ts +2 -1
  393. package/types/src/overlay/overlay.component.d.ts +1 -0
  394. package/types/src/overlay/overlay.types.d.ts +12 -2
  395. package/types/src/theme/theme.component.d.ts +1 -0
  396. package/dist/SchmancyElement-CA0Wqt8m.js +0 -284
  397. package/dist/SchmancyElement-CA0Wqt8m.js.map +0 -1
  398. package/dist/SchmancyElement-CYIif26I.cjs +0 -2
  399. package/dist/SchmancyElement-CYIif26I.cjs.map +0 -1
  400. package/dist/boat-BgpWcLnV.cjs +0 -38
  401. package/dist/boat-BgpWcLnV.cjs.map +0 -1
  402. package/dist/boat-Y5UMiJCV.js +0 -216
  403. package/dist/boat-Y5UMiJCV.js.map +0 -1
  404. package/dist/chips-CXZ4dJCK.cjs.map +0 -1
  405. package/dist/chips-Dg6Lk6BT.js.map +0 -1
  406. package/dist/date-range-BU6WX7d5.js.map +0 -1
  407. package/dist/date-range-C-_be3_E.cjs.map +0 -1
  408. package/dist/directives-fLwDj6b0.cjs.map +0 -1
  409. package/dist/directives-zi1Mm2er.js.map +0 -1
  410. package/dist/input-chip-57tgNXKT.cjs.map +0 -1
  411. package/dist/input-chip-C6Lq1927.js.map +0 -1
  412. package/dist/overlay-BpNhd74N.js.map +0 -1
  413. package/dist/overlay-UQR2Dy3u.cjs.map +0 -1
  414. package/dist/theme-iKUaS9JB.cjs +0 -181
@@ -1,266 +1,2 @@
1
- import { SchmancyElement } from '@mixins/index'
2
- import { css, html, LitElement } from 'lit'
3
- import { customElement, property, state } from 'lit/decorators.js'
4
- import { classMap } from 'lit/directives/class-map.js'
5
- import { BehaviorSubject, combineLatest } from 'rxjs'
6
- import { takeUntil } from 'rxjs/operators'
7
-
8
- /**
9
- * Suggestion chip component - provides contextual recommendations to users
10
- *
11
- * IMPORTANT: Suggestion chips do NOT have a selected state. They are designed to
12
- * provide suggestions and recommendations that trigger actions when clicked.
13
- * Unlike filter chips, they cannot be toggled on/off.
14
- *
15
- * Pure Schmancy implementation with Tailwind CSS and RxJS state management
16
- */
17
- @customElement('schmancy-suggestion-chip')
18
- export class SchmancySuggestionChip extends SchmancyElement {
19
- static styles = [css`
20
- :host {
21
- display: inline-block;
22
- outline: none;
23
- width: fit-content;
24
- min-width: fit-content;
25
- }
26
-
27
- :host([disabled]) {
28
- pointer-events: none;
29
- }
30
-
31
- .ripple {
32
- position: absolute;
33
- border-radius: 50%;
34
- transform: scale(0);
35
- animation: ripple 600ms linear;
36
- background-color: rgba(0, 0, 0, 0.08);
37
- pointer-events: none;
38
- }
39
-
40
- @keyframes ripple {
41
- to {
42
- transform: scale(4);
43
- opacity: 0;
44
- }
45
- }
46
-
47
- /* State layer for M3 hover/focus/pressed states */
48
- .state-layer {
49
- position: absolute;
50
- inset: 0;
51
- border-radius: inherit;
52
- pointer-events: none;
53
- background-color: currentColor;
54
- opacity: 0;
55
- transition: opacity 200ms ease;
56
- }
57
-
58
- :host(:not([disabled])) button:hover .state-layer {
59
- opacity: 0.08;
60
- }
61
-
62
- :host(:not([disabled])) button:focus-visible .state-layer {
63
- opacity: 0.1;
64
- }
65
-
66
- :host(:not([disabled])) button:active .state-layer {
67
- opacity: 0.1;
68
- }
69
- `];
70
- /** Value identifier for the chip */
71
- @property({ reflect: true }) value = ''
72
-
73
- /** Optional icon name (Material Symbols) */
74
- @property({ reflect: true }) icon = ''
75
-
76
- /** Optional href for navigation */
77
- @property({ reflect: true }) href = ''
78
-
79
- /** Target for navigation (e.g., '_blank') */
80
- @property({ reflect: true }) target = ''
81
-
82
- /** Disable the chip */
83
- @property({ type: Boolean, reflect: true }) disabled = false
84
-
85
- /** Elevated style variant - flat by default per M3 spec */
86
- @property({ type: Boolean, reflect: true }) elevated = false
87
-
88
- // RxJS state streams
89
- private hover$ = new BehaviorSubject<boolean>(false)
90
- private pressed$ = new BehaviorSubject<boolean>(false)
91
- private focused$ = new BehaviorSubject<boolean>(false)
92
-
93
- // UI state - only ripples needed for rendering
94
- @state() private ripples: Array<{ x: number; y: number; id: number }> = []
95
-
96
- protected static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true }
97
-
98
- private nextRippleId = 0
99
-
100
- connectedCallback() {
101
- super.connectedCallback()
102
-
103
- // Stream management for future extensions
104
- // Currently, states are handled directly in event handlers
105
- // This pipeline can be extended for more complex state interactions
106
- combineLatest([
107
- this.hover$,
108
- this.pressed$,
109
- this.focused$
110
- ]).pipe(
111
- // States are managed through event handlers directly
112
- // This pipeline is kept for potential future state combinations
113
- takeUntil(this.disconnecting)
114
- ).subscribe()
115
- }
116
-
117
- private handleClick = (e: MouseEvent) => {
118
- if (this.disabled) return
119
-
120
- // Add ripple effect
121
- const button = this.shadowRoot?.querySelector('button')
122
- if (button) {
123
- const rect = button.getBoundingClientRect()
124
- const x = e.clientX - rect.left
125
- const y = e.clientY - rect.top
126
- const id = this.nextRippleId++
127
-
128
- this.ripples = [...this.ripples, { x, y, id }]
129
-
130
- // Remove ripple after animation
131
- setTimeout(() => {
132
- this.ripples = this.ripples.filter(r => r.id !== id)
133
- }, 600)
134
- }
135
-
136
- // Navigate if href is provided
137
- if (this.href) {
138
- if (this.target === '_blank') {
139
- window.open(this.href, '_blank')
140
- } else {
141
- window.location.href = this.href
142
- }
143
- }
144
-
145
- // Dispatch action event
146
- this.dispatchEvent(new CustomEvent('action', {
147
- detail: { value: this.value },
148
- bubbles: true,
149
- composed: true
150
- }))
151
- }
152
-
153
- private handleKeyDown = (e: KeyboardEvent) => {
154
- if (this.disabled) return
155
-
156
- if (e.key === 'Enter' || e.key === ' ') {
157
- e.preventDefault()
158
- this.pressed$.next(true)
159
-
160
- // Simulate click
161
- const clickEvent = new MouseEvent('click', {
162
- bubbles: true,
163
- cancelable: true,
164
- clientX: 0,
165
- clientY: 0
166
- })
167
- this.handleClick(clickEvent)
168
-
169
- setTimeout(() => this.pressed$.next(false), 100)
170
- }
171
- }
172
-
173
- private handleFocus = () => {
174
- this.focused$.next(true)
175
- }
176
-
177
- private handleBlur = () => {
178
- this.focused$.next(false)
179
- }
180
-
181
- protected render(): unknown {
182
- const hasIcon = !!this.icon;
183
-
184
- const classes = {
185
- 'relative': true,
186
- 'inline-flex': true,
187
- 'items-center': true,
188
- 'gap-2': true,
189
- 'h-8': true, // M3: 32px height
190
- 'min-h-[32px]': true,
191
- 'rounded-full': true,
192
- 'cursor-pointer': !this.disabled,
193
- 'transition-all': true,
194
- 'duration-200': true,
195
- 'select-none': true,
196
- 'overflow-hidden': true,
197
-
198
- // M3 Padding: 8px with icon, 16px without (leading), 16px trailing
199
- 'pl-2': hasIcon, // 8px with icon
200
- 'pl-4': !hasIcon, // 16px without icon
201
- 'pr-4': true, // 16px trailing
202
-
203
- // M3 Colors for suggestion chips
204
- 'bg-surface-containerLow': true,
205
- 'text-surface-onVariant': true,
206
-
207
- // Suggestion chips are flat by default (no elevation per M3)
208
-
209
- // Focus state
210
- 'focus-visible:outline': !this.disabled,
211
- 'focus-visible:outline-2': !this.disabled,
212
- 'focus-visible:outline-primary': !this.disabled,
213
- 'focus-visible:outline-offset-2': !this.disabled,
214
-
215
- // Disabled
216
- 'opacity-38': this.disabled, // M3 disabled opacity
217
- 'cursor-not-allowed': this.disabled
218
- }
219
-
220
- return html`
221
- <button
222
- type="button"
223
- class=${classMap(classes)}
224
- ?disabled=${this.disabled}
225
- @click=${this.handleClick}
226
- @keydown=${this.handleKeyDown}
227
- @mouseenter=${() => this.hover$.next(true)}
228
- @mouseleave=${() => this.hover$.next(false)}
229
- @mousedown=${() => this.pressed$.next(true)}
230
- @mouseup=${() => this.pressed$.next(false)}
231
- @focus=${this.handleFocus}
232
- @blur=${this.handleBlur}
233
- tabindex=${this.disabled ? '-1' : '0'}
234
- role="button"
235
- aria-disabled=${this.disabled}
236
- aria-label=${this.value}
237
- >
238
- ${this.icon ? html`
239
- <schmancy-icon class="text-[18px] shrink-0">${this.icon}</schmancy-icon>
240
- ` : ''}
241
- <span class="text-sm font-medium leading-5">
242
- <slot></slot>
243
- </span>
244
-
245
- <!-- Ripple effects -->
246
- ${this.ripples.map(ripple => html`
247
- <span
248
- class="ripple"
249
- style="left: ${ripple.x}px; top: ${ripple.y}px;"
250
- ></span>
251
- `)}
252
-
253
- <!-- State layer for M3 hover/focus/pressed states -->
254
- <div class="state-layer"></div>
255
- </button>
256
- `
257
- }
258
- }
259
-
260
- declare global {
261
- interface HTMLElementTagNameMap {
262
- 'schmancy-suggestion-chip': SchmancySuggestionChip
263
- }
264
- }
265
-
266
- export type SuggestionChipActionEvent = { value: string }
1
+ // Moved to src/form/fields/chips/ this is a backward-compat re-export shim.
2
+ export * from '../form/fields/chips/suggestion-chip';
@@ -1,7 +1,7 @@
1
1
  import { AsyncDirective, directive } from 'lit/async-directive.js'
2
2
  import { ElementPart, PartType } from 'lit/directive.js'
3
3
  import { EMPTY, Subject, animationFrameScheduler, combineLatest, fromEvent, merge, timer } from 'rxjs'
4
- import { auditTime, filter, startWith, switchMap, takeUntil, tap } from 'rxjs/operators'
4
+ import { auditTime, distinctUntilChanged, filter, map, startWith, switchMap, takeUntil, tap } from 'rxjs/operators'
5
5
  import { theme } from '../theme/theme.service'
6
6
  import { fromResizeObserver } from './layout'
7
7
 
@@ -50,6 +50,15 @@ class Fill extends AsyncDirective {
50
50
  : EMPTY,
51
51
  fromEvent(window, 'orientationchange'),
52
52
  fromEvent(document, 'focusout', { passive: true }).pipe(switchMap(() => timer(100))),
53
+ // Observe `el` itself: this is the post-layout settle trigger
54
+ // (its initial callback fires once `rect.top/left` resolve) and
55
+ // the only DOM trigger for a top-level fill element whose
56
+ // `el.parentElement` is null. It is loop-safe ONLY because of
57
+ // the `distinctUntilChanged` gate below: write → RO fires →
58
+ // recompute → identical value → gate suppresses → no write →
59
+ // RO quiet (converges in ≤2 frames). The ~20s overlay hang was
60
+ // the *missing gate*, not the observation — without it every
61
+ // write re-fired the observer forever.
53
62
  fromResizeObserver(el),
54
63
  el.parentElement ? fromResizeObserver(el.parentElement) : EMPTY,
55
64
  ).pipe(auditTime(0, animationFrameScheduler), startWith(null)),
@@ -61,14 +70,28 @@ class Fill extends AsyncDirective {
61
70
  const vv = window.visualViewport
62
71
  return vv ? Math.abs(vv.scale - 1) <= 0.01 : true
63
72
  }),
64
- tap(([, bottomOffset, isFullscreen]) => {
73
+ map(([, bottomOffset, isFullscreen]) => {
65
74
  const vv = window.visualViewport
66
75
  const rect = el.getBoundingClientRect()
76
+ return {
77
+ height: `${Math.max(0, (vv?.height ?? window.innerHeight) - rect.top)}px`,
78
+ width: `${Math.max(0, (vv?.width ?? window.innerWidth) - rect.left)}px`,
79
+ paddingBottom: `${isFullscreen ? 0 : bottomOffset}px`,
80
+ }
81
+ }),
82
+ // Idempotent gate: once geometry is stable the computed values
83
+ // stop changing, so no DOM write happens and no resize is
84
+ // emitted — the parent observer (and any ancestor's, e.g. the
85
+ // overlay's) goes quiet instead of ping-ponging.
86
+ distinctUntilChanged(
87
+ (a, b) => a.height === b.height && a.width === b.width && a.paddingBottom === b.paddingBottom,
88
+ ),
89
+ tap(({ height, width, paddingBottom }) => {
67
90
  const s = el.style
68
91
  s.boxSizing = 'border-box'
69
- s.height = `${Math.max(0, (vv?.height ?? window.innerHeight) - rect.top)}px`
70
- s.width = `${Math.max(0, (vv?.width ?? window.innerWidth) - rect.left)}px`
71
- s.paddingBottom = `${isFullscreen ? 0 : bottomOffset}px`
92
+ s.height = height
93
+ s.width = width
94
+ s.paddingBottom = paddingBottom
72
95
  s.minHeight = '0'
73
96
  s.minWidth = '0'
74
97
  s.overflow = 'hidden'
@@ -0,0 +1,101 @@
1
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest'
2
+ import './fab'
3
+ import '../icons/icon'
4
+ import { expectNoA11yViolations } from '../test-utils/a11y'
5
+ import type { SchmancyFab } from './fab'
6
+
7
+ const nextUpdate = () => new Promise(r => requestAnimationFrame(() => r(null)))
8
+
9
+ const base = (fab: SchmancyFab) =>
10
+ fab.shadowRoot!.querySelector('[part="base"]') as HTMLButtonElement
11
+
12
+ describe('schmancy-fab — M3 contract', () => {
13
+ let host: HTMLDivElement
14
+
15
+ beforeEach(() => {
16
+ host = document.createElement('div')
17
+ document.body.appendChild(host)
18
+ })
19
+
20
+ afterEach(() => {
21
+ host.remove()
22
+ })
23
+
24
+ it('renders a non-disablable native button (M3: a FAB is never disabled)', async () => {
25
+ host.innerHTML = `<schmancy-fab aria-label="Compose"><schmancy-icon>edit</schmancy-icon></schmancy-fab>`
26
+ const fab = host.querySelector('schmancy-fab') as SchmancyFab
27
+ await nextUpdate()
28
+ const btn = base(fab)
29
+ expect(btn.tagName).toBe('BUTTON')
30
+ expect(btn.getAttribute('type')).toBe('button')
31
+ expect('disabled' in fab).toBe(false)
32
+ })
33
+
34
+ it('defaults to surface / medium: surface-containerHigh container, primary on-colour, 16dp shape', async () => {
35
+ host.innerHTML = `<schmancy-fab aria-label="Compose"><schmancy-icon>edit</schmancy-icon></schmancy-fab>`
36
+ const fab = host.querySelector('schmancy-fab') as SchmancyFab
37
+ await nextUpdate()
38
+ expect(fab.variant).toBe('surface')
39
+ expect(fab.size).toBe('medium')
40
+ expect(fab.extended).toBe(false)
41
+ const cls = base(fab).classList
42
+ expect(cls.contains('bg-surface-containerHigh')).toBe(true)
43
+ expect(cls.contains('text-primary-default')).toBe(true)
44
+ expect(cls.contains('size-14')).toBe(true)
45
+ expect(getComputedStyle(fab).borderTopLeftRadius).toBe('16px')
46
+ })
47
+
48
+ it.each([
49
+ ['primary', 'bg-primary-container', 'text-primary-onContainer'],
50
+ ['secondary', 'bg-secondary-container', 'text-secondary-onContainer'],
51
+ ['tertiary', 'bg-tertiary-container', 'text-tertiary-onContainer'],
52
+ ] as const)('maps the %s variant to its M3 container + on-container colour pair', async (variant, bg, fg) => {
53
+ host.innerHTML = `<schmancy-fab variant="${variant}" aria-label="x"><schmancy-icon>add</schmancy-icon></schmancy-fab>`
54
+ const fab = host.querySelector('schmancy-fab') as SchmancyFab
55
+ await nextUpdate()
56
+ const cls = base(fab).classList
57
+ expect(cls.contains(bg)).toBe(true)
58
+ expect(cls.contains(fg)).toBe(true)
59
+ })
60
+
61
+ it('applies the M3 size geometry: small 12dp/40, large 28dp/36 icon', async () => {
62
+ host.innerHTML = `<schmancy-fab size="small" aria-label="x"><schmancy-icon>add</schmancy-icon></schmancy-fab>`
63
+ let fab = host.querySelector('schmancy-fab') as SchmancyFab
64
+ await nextUpdate()
65
+ expect(base(fab).classList.contains('size-10')).toBe(true)
66
+ expect(getComputedStyle(fab).borderTopLeftRadius).toBe('12px')
67
+
68
+ host.innerHTML = `<schmancy-fab size="large" aria-label="x"><schmancy-icon>add</schmancy-icon></schmancy-fab>`
69
+ fab = host.querySelector('schmancy-fab') as SchmancyFab
70
+ await nextUpdate()
71
+ expect(base(fab).classList.contains('size-24')).toBe(true)
72
+ expect(getComputedStyle(fab).borderTopLeftRadius).toBe('28px')
73
+ })
74
+
75
+ it('derives the extended form from a non-empty label and renders the label', async () => {
76
+ host.innerHTML = `<schmancy-fab label="Compose" variant="primary"><schmancy-icon>edit</schmancy-icon></schmancy-fab>`
77
+ const fab = host.querySelector('schmancy-fab') as SchmancyFab
78
+ await nextUpdate()
79
+ expect(fab.extended).toBe(true)
80
+ expect(fab.hasAttribute('extended')).toBe(true)
81
+ const btn = base(fab)
82
+ expect(btn.classList.contains('size-14')).toBe(false)
83
+ expect(btn.classList.contains('ps-4')).toBe(true)
84
+ expect(btn.classList.contains('pe-5')).toBe(true)
85
+ expect(btn.textContent).toContain('Compose')
86
+ })
87
+
88
+ it('reflects lowered and hides the icon slot from AT when a name is present', async () => {
89
+ host.innerHTML = `<schmancy-fab lowered label="New"><schmancy-icon>edit</schmancy-icon></schmancy-fab>`
90
+ const fab = host.querySelector('schmancy-fab') as SchmancyFab
91
+ await nextUpdate()
92
+ expect(fab.hasAttribute('lowered')).toBe(true)
93
+ expect(fab.shadowRoot!.querySelector('slot')!.getAttribute('aria-hidden')).toBe('true')
94
+ })
95
+
96
+ it('has no a11y violations as an icon-only FAB with an accessible name', async () => {
97
+ host.innerHTML = `<schmancy-fab aria-label="Compose message"><schmancy-icon>edit</schmancy-icon></schmancy-fab>`
98
+ await nextUpdate()
99
+ await expectNoA11yViolations(host)
100
+ })
101
+ })
package/src/fab/fab.ts ADDED
@@ -0,0 +1,226 @@
1
+ import { SchmancyElement } from '@mixins/index'
2
+ import { css, html, LitElement } from 'lit'
3
+ import { customElement, property, query } from 'lit/decorators.js'
4
+ import { ifDefined } from 'lit/directives/if-defined.js'
5
+ import { when } from 'lit/directives/when.js'
6
+ import { magnetic } from '../directives/magnetic'
7
+
8
+ export type FabVariant = 'surface' | 'primary' | 'secondary' | 'tertiary'
9
+ export type FabSize = 'small' | 'medium' | 'large'
10
+
11
+ /**
12
+ * Material 3 Floating Action Button.
13
+ *
14
+ * Mirrors the `md-fab` reference implementation
15
+ * (github.com/material-components/material-web) and the M3 spec
16
+ * (m3.material.io/components/floating-action-button): four colour
17
+ * variants, three container sizes, an extended form (set `label`),
18
+ * and a `lowered` elevation register. Per M3 guidance a FAB is never
19
+ * disabled, so there is no `disabled` property.
20
+ *
21
+ * Sizing, shape, icon size and elevation are M3 design tokens expressed
22
+ * as `:host` rules (the same token-driven model as `md-fab`'s SCSS);
23
+ * colour roles and the state layer are schmancy theme utilities. The
24
+ * host shrink-wraps the inner button and clips its rounded shape.
25
+ *
26
+ * @element schmancy-fab
27
+ * @slot - The icon (typically `<schmancy-icon>`).
28
+ * @csspart base - The underlying native `<button>`.
29
+ */
30
+ @customElement('schmancy-fab')
31
+ export class SchmancyFab extends SchmancyElement {
32
+ static styles = [
33
+ css`
34
+ :host {
35
+ /* M3 FAB (medium) — 16dp shape, 24dp icon, resting level 3 */
36
+ display: inline-flex;
37
+ position: relative;
38
+ touch-action: manipulation;
39
+ overflow: hidden;
40
+ border-radius: 16px;
41
+ --_icon: 24px;
42
+ --_elevation: var(--schmancy-sys-elevation-3);
43
+ --_elevation-hover: var(--schmancy-sys-elevation-4);
44
+ box-shadow: var(--_elevation);
45
+ transition:
46
+ box-shadow 280ms cubic-bezier(0.34, 1.56, 0.64, 1),
47
+ transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
48
+ }
49
+ /* M3 small FAB — 12dp shape */
50
+ :host([size='small']) {
51
+ border-radius: 12px;
52
+ }
53
+ /* M3 large FAB — 28dp shape, 36dp icon */
54
+ :host([size='large']) {
55
+ border-radius: 28px;
56
+ --_icon: 36px;
57
+ }
58
+ /* Extended FAB always uses the 16dp shape / 24dp icon */
59
+ :host([extended]) {
60
+ border-radius: 16px;
61
+ --_icon: 24px;
62
+ }
63
+ /* M3 lowered register — resting level 1, hover level 2 */
64
+ :host([lowered]) {
65
+ --_elevation: var(--schmancy-sys-elevation-1);
66
+ --_elevation-hover: var(--schmancy-sys-elevation-2);
67
+ }
68
+ :host(:hover) {
69
+ box-shadow: var(--_elevation-hover);
70
+ }
71
+ :host(:active) {
72
+ /* M3 pressed elevation == resting; schmancy spring press */
73
+ box-shadow: var(--_elevation);
74
+ transform: scale(0.96);
75
+ transition-duration: 100ms;
76
+ }
77
+ ::slotted(*) {
78
+ font-size: var(--_icon);
79
+ width: var(--_icon);
80
+ height: var(--_icon);
81
+ }
82
+ :host([extended]) ::slotted(*) {
83
+ margin-inline-end: 12px;
84
+ }
85
+ @media (prefers-reduced-motion: reduce) {
86
+ :host {
87
+ transition: none;
88
+ }
89
+ :host(:active) {
90
+ transform: none;
91
+ }
92
+ }
93
+ :host *,
94
+ * {
95
+ touch-action: manipulation;
96
+ }
97
+ `,
98
+ ]
99
+
100
+ protected static shadowRootOptions = {
101
+ ...LitElement.shadowRootOptions,
102
+ mode: 'open',
103
+ delegatesFocus: true,
104
+ }
105
+
106
+ @query('[part="base"]', true)
107
+ private nativeElement!: HTMLElement
108
+
109
+ private ariaLabelValue!: string
110
+
111
+ /**
112
+ * Colour variant. M3 maps each to a container + on-container role.
113
+ * @attr
114
+ * @default 'surface'
115
+ */
116
+ @property({ reflect: true, type: String })
117
+ public variant: FabVariant = 'surface'
118
+
119
+ /**
120
+ * Container size. Ignored while extended (extended is always 56dp tall).
121
+ * @attr
122
+ * @default 'medium'
123
+ */
124
+ @property({ reflect: true, type: String })
125
+ public size: FabSize = 'medium'
126
+
127
+ /**
128
+ * Extended-FAB label. A non-empty value switches the FAB to its
129
+ * extended form (icon + text), exactly as `md-fab` derives extended
130
+ * from a truthy label.
131
+ * @attr
132
+ */
133
+ @property({ type: String })
134
+ public label = ''
135
+
136
+ /**
137
+ * Reflected mirror of "label is non-empty" so `:host([extended])`
138
+ * can drive the extended geometry. Synced from `label`; read it,
139
+ * don't set it.
140
+ * @attr
141
+ */
142
+ @property({ type: Boolean, reflect: true })
143
+ public extended = false
144
+
145
+ /**
146
+ * Lowers the FAB's elevation register (M3 lowered FAB).
147
+ * @attr
148
+ * @default false
149
+ */
150
+ @property({ type: Boolean, reflect: true })
151
+ public lowered = false
152
+
153
+ public override set ariaLabel(value: string) {
154
+ const oldVal = this.ariaLabelValue
155
+ this.ariaLabelValue = value
156
+ if (this.hasAttribute('aria-label')) {
157
+ this.removeAttribute('aria-label')
158
+ }
159
+ this.requestUpdate('ariaLabel', oldVal)
160
+ }
161
+
162
+ @property({ attribute: 'aria-label' })
163
+ public override get ariaLabel() {
164
+ return this.ariaLabelValue
165
+ }
166
+
167
+ /** Sets focus on the FAB. */
168
+ public override focus(options?: FocusOptions) {
169
+ this.nativeElement.focus(options)
170
+ }
171
+
172
+ /** Removes focus from the FAB. */
173
+ public override blur() {
174
+ this.nativeElement.blur()
175
+ }
176
+
177
+ protected willUpdate(changed: Map<string, unknown>) {
178
+ if (changed.has('label')) this.extended = this.label.length > 0
179
+ }
180
+
181
+ render() {
182
+ const classes = {
183
+ 'relative z-0 flex items-center justify-center overflow-hidden border-0 cursor-pointer outline-hidden focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 outline-secondary-default':
184
+ true,
185
+ // M3 container — fixed square per size, content-width when extended
186
+ 'size-10': !this.extended && this.size === 'small',
187
+ 'size-14': !this.extended && this.size === 'medium',
188
+ 'size-24': !this.extended && this.size === 'large',
189
+ 'h-14 ps-4 pe-5 gap-3 text-sm font-medium leading-5': this.extended,
190
+ // M3 container + on-container colour roles per variant
191
+ 'bg-surface-containerHigh text-primary-default': this.variant === 'surface',
192
+ 'bg-primary-container text-primary-onContainer': this.variant === 'primary',
193
+ 'bg-secondary-container text-secondary-onContainer': this.variant === 'secondary',
194
+ 'bg-tertiary-container text-tertiary-onContainer': this.variant === 'tertiary',
195
+ }
196
+
197
+ const stateLayerClasses = {
198
+ 'absolute inset-0 z-0 opacity-0 transition-opacity duration-150 hover:opacity-8 focus-visible:opacity-10 active:opacity-10':
199
+ true,
200
+ 'bg-primary-default': this.variant === 'surface',
201
+ 'bg-primary-onContainer': this.variant === 'primary',
202
+ 'bg-secondary-onContainer': this.variant === 'secondary',
203
+ 'bg-tertiary-onContainer': this.variant === 'tertiary',
204
+ }
205
+
206
+ return html`
207
+ <button
208
+ ${magnetic({ strength: 3, radius: 60 })}
209
+ part="base"
210
+ type="button"
211
+ aria-label=${ifDefined(this.ariaLabel)}
212
+ class="${this.classMap(classes)}"
213
+ >
214
+ <div class="${this.classMap(stateLayerClasses)}"></div>
215
+ <slot aria-hidden=${ifDefined(this.ariaLabel || this.label ? 'true' : undefined)}></slot>
216
+ ${when(this.extended, () => html`<span class="relative z-0">${this.label}</span>`)}
217
+ </button>
218
+ `
219
+ }
220
+ }
221
+
222
+ declare global {
223
+ interface HTMLElementTagNameMap {
224
+ 'schmancy-fab': SchmancyFab
225
+ }
226
+ }
@@ -0,0 +1 @@
1
+ export * from './fab'